Search code examples
c#opencvui-automationemgucv

OpenCV FindContours does not find the correct rectangle


Using CvInvoke.Canny and CvInvoke.FindContours I'm trying to find the rectangle containing the item name (Schematic: Maple Desk). This rectangle is shown in the image below:

Wanted rectangle

I'm able to find a lot of rectangles but I'm not able to get this one. Tried a lot of different thresholds for Canny but to no effect. The following image shows all rectangles I currently get:

Processed image

Any ideas how to tackle this? Do I need to use other thresholds or another approach? I already experimented using grayscale and blurring but that didn't give better result. I added my current source below and the original image I'm using is this:

Original image

public Mat ProcessImage(Mat img)
{
 UMat filter = new UMat();
 UMat cannyEdges = new UMat();
 Mat rectangleImage = new Mat(img.Size, DepthType.Cv8U, 3);

 //Convert the image to grayscale and filter out the noise
 //CvInvoke.CvtColor(img, filter, ColorConversion.Bgr2Gray);

 //Remove noise
 //CvInvoke.GaussianBlur(filter, filter, new System.Drawing.Size(3, 3), 1);

 // Canny and edge detection
 double cannyThreshold = 1.0; //180.0
 double cannyThresholdLinking = 1.0; //120.0
 //CvInvoke.Canny(filter, cannyEdges, cannyThreshold, cannyThresholdLinking);
 CvInvoke.Canny(img, cannyEdges, cannyThreshold, cannyThresholdLinking);

 // Find rectangles
 List<RotatedRect> rectangleList = new List<RotatedRect>();
 using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
 {
   CvInvoke.FindContours(cannyEdges, contours, null, RetrType.List, ChainApproxMethod.ChainApproxSimple);
   int count = contours.Size;
   for (int i = 0; i < count; i++)
   {
     using (VectorOfPoint contour = contours[i])
     using (VectorOfPoint approxContour = new VectorOfPoint())
     {
       CvInvoke.ApproxPolyDP(contour, approxContour, CvInvoke.ArcLength(contour, true) * 0.05, true);
       // Only consider contours with area greater than 250
       if (CvInvoke.ContourArea(approxContour, false) > 250)
       {
         // The contour has 4 vertices.
         if (approxContour.Size == 4)
         {
           // Determine if all the angles in the contour are within [80, 100] degree
           bool isRectangle = true;
           System.Drawing.Point[] pts = approxContour.ToArray();
           LineSegment2D[] edges = Emgu.CV.PointCollection.PolyLine(pts, true);

           for (int j = 0; j < edges.Length; j++)
           {
             double angle = Math.Abs(edges[(j + 1) % edges.Length].GetExteriorAngleDegree(edges[j]));
             if (angle < 80 || angle > 100)
             {
               isRectangle = false;
               break;
             }
           }

           if (isRectangle) rectangleList.Add(CvInvoke.MinAreaRect(approxContour));
         }
       }
     }
   }
 }

 // Draw rectangles
 foreach (RotatedRect rectangle in rectangleList)
 {
   CvInvoke.Polylines(rectangleImage, Array.ConvertAll(rectangle.GetVertices(), System.Drawing.Point.Round), true,
       new Bgr(Color.DarkOrange).MCvScalar, 2);
 }

 //Drawing a light gray frame around the image
 CvInvoke.Rectangle(rectangleImage,
     new Rectangle(System.Drawing.Point.Empty,
         new System.Drawing.Size(rectangleImage.Width - 1, rectangleImage.Height - 1)),
     new MCvScalar(120, 120, 120));
 //Draw the labels
 CvInvoke.PutText(rectangleImage, "Rectangles", new System.Drawing.Point(20, 20),
     FontFace.HersheyDuplex, 0.5, new MCvScalar(120, 120, 120));

 Mat result = new Mat();
 CvInvoke.VConcat(new Mat[] { img, rectangleImage }, result);
 return result;
}

Edit 1: After some more fine tuning with the following thresholds for Canny

  • cannyThreshold 100
  • cannyThresholdLinking 400

And using a minimum size for all ContourAreas of 10000 I can get the following result:

enter image description here

Edit 2: For those interested, solved using the current detection, no changes were needed. Used the 2 detected rectangles in the screenshot above to get the location of the missing rectangle containing the item name.

Result can be found here: https://github.com/josdemmers/NewWorldCompanion


Solution

  • For those interested, solved using the current detection, no changes were needed. Used the 2 detected rectangles in the screenshot above to get the location of the missing rectangle containing the item name.

    Result can be found here: https://github.com/josdemmers/NewWorldCompanion