操作图像和更新pictureBox有一些问题

时间:2011-03-31 08:00:57

标签: c# object locking multithreading

我无法完全符合我想在标题中说的内容,它太长了。好的,这是一个多线程的应用程序。我的应用程序所做的是查看图片,找到图片的边缘,并从边缘找到该对象的形状。当它找到形状时,它会不断更新图像,以便我们可以获得某种形象。我创建了一段非常短暂的(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 个答案:

答案 0 :(得分:3)

您似乎在与GUI线程分开的线程上运行shapes位图。当您移动窗口时,将运行OnPaint例程,该例程也将访问图像。

解决此问题需要做的是在工作线程中的单独位图上运行,然后使用窗体上的Invoke将其副本传递给GUI线程。这样你就可以保证只有一个线程一次访问图片框图像。

编辑:

void MyThreadFunction( )
{
    Bitmap localThreadImage;
    ...

    Image copyForPictureBox = localThreadImage.Clone( ) as Image;

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

    ....
}

所以我的想法是你在线程上创建一个Bitmap,该线程只能被该线程访问(即你的后台工作者线程)。当你想要更新PictureBox中的图像时,使用BeginInvoke(不阻止你的工作线程)调用GUI线程,传递{的副本{1}} Bitmap

答案 1 :(得分:0)

仅在应用程序中的某一点锁定形状对象无法完成任何操作。您还可以使用此位图绘制到窗口,我的猜测是您没有锁定它以进行绘制。您也可以将其锁定在OnPaint中,或者使用不同的位图进行操作和显示。