2011-03-31 3 views
1

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

Как вы можете видеть, все работает нормально до минуты, когда я перемещаю окно. Это всегда так. Я несколько раз запускал программу, не перемещая окно, и все было нормально. Тем не менее, в тот момент, когда я перехожу к окну, это приведет к ошибке. Как вы видите, я блокирую конкретный образ, с которым мне хотелось бы работать. Являются ли формы 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; 
     } 
    } 
} 

Update

Что сказал Ник, прежде работал хорошо. Я добавил это к моему событию 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)); 
} 
+0

Если это не будет хорошо работать как комментарий, есть очень хороший шанс, что вы должны просто обновить свой вопрос. Я пошел вперед и объединил ваш ответ. –

ответ

1

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

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

Edit:

void MyThreadFunction() 
{ 
    Bitmap localThreadImage; 
    ... 

    Image copyForPictureBox = localThreadImage.Clone() as Image; 

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

    .... 
} 

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

+0

Как вы предлагаете использовать метод invoke? – Nathaniel

+0

Если я использую pictureBox, он говорит, что перекрестные потоковые вызовы запрещены. Будет ли это в моем событии CircleChange? – Nathaniel

0

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

+0

Блокировка метода OnPaint никогда не является хорошей идеей. Это может привести к замораживанию графического интерфейса. – Nick

+0

Это правильно. Моя точка зрения была больше, чтобы показать, почему «lock (forms)» не влияет на код. Использование вызовов для изменения отображаемого изображения является лучшим решением здесь. – Thalur

Смежные вопросы