2017-10-06 11 views
0

La fonction que je veux est de permettre à l'utilisateur d'utiliser la souris pour dessiner les contours de plusieurs objets sur l'image. Afficher les contours sur l'image et pouvoir exporter les données de contours. J'utilise cyotek.imagebox ici https://github.com/cyotek/Cyotek.Windows.Forms.ImageBox, un contrôle personnalisé pour l'affichage de l'image. Voici mon approche.dessiner des contours avec la souris sur Winform C# avec Cyotek.ImageBox

private List<List<Point>> contours = new List<List<Point>>(); //a list to store all contours 

private List<Point> contour_temp= new List<Point>(); //a list to store the contour the user is drawing 

private bool mousedown // a flag that change when mouse up/mouse down event is triggered 

private void imageBox_MouseDown(object sender, MouseEventArgs e) 
{ 
    mousedown = true; 
} 

private void imageBox_MouseUp(object sender, MouseEventArgs e) 
{ 
    mousedown = false; 
} 

private void imageBox_MouseMove(object sender, MouseEventArgs e) 
{ 
    //drawing occurs when mouse is down, a contour is finish drawing when mouse is up 
    // when a contour finish drawing. it will be added to the contours list and the contour temp will be clear 

    if (draw_on) // a flag for this function to be active 
    { 
     if (contour_temp.Count > 0) 
     { 
      if (mousedown) 
      { 
       if (imageBox.IsPointInImage(e.Location)) //function cyotek.imagebox provides , it translate the mouse location to pixel location 
       { 
        Point p = this.imageBox.PointToImage(e.Location); 
        if (p != contour_temp[contour_temp.Count - 1]) 
         contour_temp.Add(p); 
       } 
      } 
      else 
      { 
       if (contour_temp.Count > 2) 
       { 
        contours.Add(contour_temp); 
        contour_temp.Clear(); 
       } 
      } 
     } 
     else 
     { 
      if (mousedown) 
      { 
       contour_temp = new List<Point>(); 
       Point p = this.imageBox.PointToImage(e.Location); 
       contour_temp.Add(p); 
      } 
     } 
     imageBox.Invalidate(); 
    } 
} 

private void imageBox_Paint(object sender, PaintEventArgs e) 
{ 
    //at the painting function, always paint all the contours stored 
    //if mouse is down, print the temporary contour the user is currently drawing 
    foreach (List<Point> contour in contours) 
    { 
     Point p0 = contour[0]; 
     foreach (Point p1 in contour) 
     { 
      e.Graphics.DrawLine(new Pen(Color.Red), imageBox.GetOffsetPoint(p0), imageBox.GetOffsetPoint(p1));//Cyotek.ImageBox provided, get the correct point even after zooming 
      p0 = p1; 
     } //draw all contours 

    } 

    if(draw_on && mousedown && contour_temp.Count>0) 
    { 

     Point p0 = contour_temp[0]; 
     foreach (Point p1 in contour_temp) 
     { 
      e.Graphics.DrawLine(new Pen(Color.Green), imageBox.GetOffsetPoint(p0), imageBox.GetOffsetPoint(p1)); 
      p0 = p1; 
     }// draw the contour user is drawing 

    } 
    } 

La partie dessiner contour_temp fonctionne correctement. Mais le programme plante quand un contour a fini de dessiner, en lançant System.ArgumentOutOfRangeException sur 'mscorlib.dll', Après quelques tests, je découvre que la chose dessiner tous les contours dans la fonction de peinture est en quelque sorte fausse. J'ai fait quelques recherches sur l'exception, en disant que l'index de la liste est hors limite. Mais comment cela est-il arrivé quand j'utilise "foreach"?

Répondre

0

Le problème est dû à ces lignes ici:

contours.Add(contour_temp); 
contour_temp.Clear(); 

Dans le premier cas, vous ajoutez la liste des points temporaires contour_temp à votre liste principale contours, mais comme List<T> est un reference type que vous êtes ne pas ajouter une copie des points, mais une référence à la liste - que vous effacez, ce qui fait que contours ne contient réellement que des listes vides. Mais votre routine de peinture s'attend à ce que ces listes contiennent toujours au moins un point, d'où le ArgumentOutOfRangeException lorsque vous essayez de peindre lorsque vous essayez d'obtenir le premier élément dans une liste où aucun élément n'existe.

Je fixe votre programme de démonstration en changeant la première ligne à

contours.Add(new List<Point>(contour_temp)); 

Cela va créer une copie de la liste, préservant ainsi les points. J'ai testé votre programme de démonstration avec cette correction et cela a bien fonctionné.

Espérons que cela aide. Et merci d'utiliser ImageBox!

+0

Merci. Maintenant, cela fonctionne parfaitement! –

+0

Et encore merci pour le contrôle de l'imagebox pratique. En passant, j'ai posé une autre question sur le forum Cyotek à propos de l'imagebox. Pourriez-vous jeter un coup d'oeil? –