У манипулирования изображением и обновления pictureBox есть некоторые проблемы - PullRequest
2 голосов
/ 31 марта 2011

Я не смог бы точно вписать то, что хотел сказать в названии, это было бы слишком долго.Хорошо, это многопоточное приложение.То, что делает мое приложение, это смотрит на изображение, находит края изображения и находит форму этого объекта по краям.Хотя он находит форму, он постоянно обновляет изображение, чтобы мы могли получить какое-то визуальное представление.Я создал очень короткое (40 секунд) видео, демонстрирующее проблему: http://phstudios.com/projects/Programming/MultiThreadIssue/

Как видите, все работает нормально, пока я не сдвину окно.Это всегда так.Я запускал программу несколько раз, не двигая окна, и она работала нормально.Тем не менее, в ту минуту, когда я перемещаю окно, появляется эта ошибка.Как видите, я блокирую конкретное изображение, с которым я хотел бы работать.Формы OnPaint перекрывают это как-то?Можно ли как-нибудь это исправить?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Drawing.Imaging;

namespace LineRecognition
{
    public enum Shape
    {
        Unknown,
        Quadrilateral,
        Circle,
        Triangle
    }
    public partial class Form1 : Form
    {
        Bitmap image, original, shapes;

        List<Point> outlines;

        Shape shape;

        ShapeDetection detector;
        public Form1()
        {
            InitializeComponent();
            edgeDetection.WorkerReportsProgress = true;
            shapeDetection.WorkerReportsProgress = true;
            shape = Shape.Unknown;
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            original = new Bitmap("photo1.png");
            image = new Bitmap("photo1.png");
            shapes = new Bitmap(image.Width, image.Height);
            pictureBox1.Image = (Image)original;
        }

        private void findLines_Click(object sender, EventArgs e)
        {
            if (edgeDetection.IsBusy != true)
            {
                lblStatus.Text = "Finding Edges";
                edgeDetection.RunWorkerAsync();
            }
        }

        private void justTheOutlines(Bitmap image, List<Point> pixels, BackgroundWorker worker)
        {
            lock (image)
            {
                for (int i = 0; i < pixels.Count; i++)
                {
                    image.SetPixel(pixels[i].X, pixels[i].Y, Color.Red);
                    worker.ReportProgress((int)((float)i * 100 / (float)pixels.Count));
                }
            }
        }

        private List<Point> outlineLines(Bitmap image, BackgroundWorker worker)
        {
            int w = image.Width;
            int h = image.Height;

            int alpha = 800000;

            List<Point> changes = new List<Point>();

            lock (image)
            {

                for (int i = 0; i < w; i++)
                {
                    for (int j = 0; j < h; j++)
                    {
                        Color selected = image.GetPixel(i, j);
                        Color nextRight = selected;
                        Color nextDown = selected;

                        if (i < w - 1)
                            nextRight = image.GetPixel(i + 1, j);

                        if (j < h - 1)
                            nextDown = image.GetPixel(i, j + 1);

                        int iSelected = selected.ToArgb();
                        int iNextRight = nextRight.ToArgb();
                        int iNextDown = nextDown.ToArgb();

                        if (Math.Abs(iSelected - iNextRight) > alpha)
                        {
                            if (iSelected < iNextRight)
                            {
                                Point p = new Point(i, j);
                                if(!ContainsPoint(changes, p)) changes.Add(p);
                            }
                            else
                            {
                                Point p = new Point(i + 1, j);
                                if (!ContainsPoint(changes, p)) changes.Add(p);
                            }
                        }

                        if (Math.Abs(iSelected - iNextDown) > alpha)
                        {
                            if (iSelected < iNextDown)
                            {
                                Point p = new Point(i, j);
                                if (!ContainsPoint(changes, p)) changes.Add(p);
                            }
                            else
                            {
                                Point p = new Point(i, j + 1);
                                if (!ContainsPoint(changes, p)) changes.Add(p);
                            }
                        }

                        image.SetPixel(i, j, Color.White);
                    }
                    worker.ReportProgress((int)(((float)i / (float)w) * 100));
                }
            }

            return changes;
        }

        private bool ContainsPoint(List<Point> changes, Point p)
        {
            foreach (Point n in changes)
            {
                if (n.Equals(p)) return true;
            }
            return false;
        }

        private void edgeDetection_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;
            outlines = outlineLines(image, worker);
            justTheOutlines(image, outlines, worker);
            pictureBox2.Image = (Image)image;
            Thread.Sleep(100);
            image.Save("photo-lines.jpg");
        }

        private void edgeDetection_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            algorithmProgress.Value = e.ProgressPercentage;
        }

        private void edgeDetection_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            algorithmProgress.Value = 0;
            findLines.Enabled = false;
            determineShape.Enabled = true;
            lblStatus.Text = "";
        }

        private void determineShape_Click(object sender, EventArgs e)
        {
            if (shapeDetection.IsBusy != true)
            {
                pictureBox1.Image = (Image)image;
                lblStatus.Text = "Running Shape Detection:  Circle -> Quadrilateral";
                shapeDetection.RunWorkerAsync();
            }
        }

        private void ShapeDetection_DoWork(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;
            detector = new ShapeDetection(outlines, 40, 10);
            detector.Worker = worker;
            detector.circleChange += new ShapeDetection.CircleChangeEventHandler(circleChange);
            if (detector.IsCircle())
            {
                MessageBox.Show("Object is a circle");
                shape = Shape.Circle;
            }
            else if (detector.IsQuadrilateral())
            {
                MessageBox.Show("Object is a quadrilateral", "Number of edges:  " + detector.Summits);
                shape = Shape.Quadrilateral;
            }
            else
            {
                int sides = detector.Summits.Count;
                if (sides == 3)
                { 
                    MessageBox.Show("Object is a triangle");
                    shape = Shape.Triangle;
                }
                else
                {
                    MessageBox.Show("Number of edges:  " + detector.Summits.Count, "Unknown");
                }
            }

            BitmapDrawing.DrawLines(detector.Summits, shapes);
            BitmapDrawing.DrawSummits(detector.Summits, shapes);
            pictureBox2.Image = (Image)shapes;
            Thread.Sleep(100);
        }

        private void ShapeDetection_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (detector != null)
            {
                lblSummits.Text += detector.Summits.Count;
                lblType.Text += shape.ToString();
                determineShape.Enabled = false;
                lblStatus.Text = "";
            }
        }

        void circleChange(object sender, CircleChangeEventArgs e)
        {
            lock (shapes)
            {
                Point p = detector.visited[detector.visited.Count - 1];
                shapes.SetPixel(p.X, p.Y, Color.Blue);
                pictureBox2.Image = (Image)shapes;
                Thread.Sleep(10);
                detector.Worker.ReportProgress((int)(e.percent * 100));
            }
        }

        private void shapeDetection_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            algorithmProgress.Value = e.ProgressPercentage;
        }
    }
}

Обновление

То, что Ник сказал раньше, работало нормально.Я добавил это к своему событию CircleChange, и оно работает.Может кто-нибудь объяснить, почему вызов заставляет его работать вместо установки picturebox2.Image к изображению фигур?Я имею в виду, я называю это после того, как я вызываю setpixel, так что я должен закончить изменение изображения правильно?

void circleChange(object sender, CircleChangeEventArgs e)
{
    Point p = detector.visited[detector.visited.Count - 1];
    shapes.SetPixel(p.X, p.Y, Color.Blue);
    Image copyForPictureBox = shapes.Clone() as Image;
    BeginInvoke(new Action(() => pictureBox2.Image = copyForPictureBox));
    Thread.Sleep(15);
    detector.Worker.ReportProgress((int)(e.percent * 100));
}

Ответы [ 2 ]

3 голосов
/ 31 марта 2011

Похоже, что вы работаете с shapes растровым изображением в потоке, отдельном от потока GUI.Когда вы перемещаете окно, будет запущена подпрограмма OnPaint, которая также получит доступ к изображению.

Чтобы решить эту проблему, вам нужно обработать отдельное растровое изображение в рабочем потоке, а затем передать копиюэто в поток GUI, используя Invoke на форме.Таким образом, вы гарантированно получите доступ только к одному потоку изображения за один раз.

Редактировать:

void MyThreadFunction( )
{
    Bitmap localThreadImage;
    ...

    Image copyForPictureBox = localThreadImage.Clone( ) as Image;

    BeginInvoke( new Action( () => pictureBox.Image = copyForPictureBox ) );

    ....
}

Итак, идея заключается в том, что вы создаете Bitmap напоток, к которому когда-либо обращался только этот поток (т.е. ваш фоновый рабочий поток).И когда вы попадаете в точку, когда вы хотите обновить изображение в PictureBox, вы вызываете поток GUI, используя BeginInvoke (который не блокирует ваш рабочий поток), передавая копию Bitmap вPictureBox.

0 голосов
/ 31 марта 2011

Блокировка объекта фигур только в одной точке вашего приложения ничего не дает. Вы также используете это растровое изображение для рисования в окне, и я предполагаю, что вы не блокируете его для рисования. Вы также можете заблокировать его в OnPaint или использовать другое растровое изображение для манипуляции и отображения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...