c# - 操作图像和更新图片框存在一些问题

标签 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));
}

最佳答案

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

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

编辑:

void MyThreadFunction( )
{
    Bitmap localThreadImage;
    ...

    Image copyForPictureBox = localThreadImage.Clone( ) as Image;

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

    ....
}

因此,我们的想法是在线程上创建一个位图,该位图只能由该线程(即您的后台工作线程)访问。当您想要更新 PictureBox 中的图像时,您可以使用 BeginInvoke 调用 GUI 线程(这不会阻塞您的工作线程)将 Bitmap 的副本传递到 PictureBox

关于c# - 操作图像和更新图片框存在一些问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5496830/

相关文章:

c# - Socket.EndReceive 工作流程

c# - 在长时间操作期间抽取 Windows 消息?

javascript - 使用 JavaScript 对象构造函数更改文本颜色

android - SQLite 连接和锁定

c++ - 为什么锁会起作用?

c# - WPF ListView 大数据时性能很差

javascript - c# mvc nreco HtmlToPdfConverter 问题将 html 文档转换为 pdf

Java将对象的变量插入数组

JavaScript 或 Node.js 意外地对对象进行排序

c# - 我一直都在做错锁吗?