Detect Basic Shape in Image with WPF EmguCV

Second tutorial that I have tried was how to detect circles, rectangles and triangles in an image. Like the previous program, I will use WPF and C# for the following program. When you start new project, do not ever forget to do basic setting for the project.

Basic Setting:

  • Copy paste the library (opencv_ffmpeg310_64.dll, opencv_core220.dll, opencv_imgproc220.dll) into Debug folder inside your project folder.
  • Add existing item into the solution explorer in the Visual Studio. Add 3 libraries mentioned above.
  • Copy paste cvextern.dll also into our project folder.
  • Change the Advanced Properties into “Copy Always”

  • Add new references into References. Add Emgu.CV.UI, Emgu.CV.World and Add Emgu. Util. If needed, I also add System.Drawing into the references.
  • Add existing class provided by EmguCV: BitmapSourceConverter.cs
  • Change the Build Properties into x64 setting. Please refer to previous tutorial.
  • Declare the using statement before begin writing your code.
using Emgu.CV.Structure;
using Emgu.CV;
using Emgu.Util;
using Emgu.CV.CvEnum;
using Microsoft.Win32;
using Emgu.CV.Util;

Start Program:

Now We can start writing the algorithm. First of all, take care of the GUI first. I will put the output image into Image Control in WPF. the XAML code as follow:

<Grid>
  <Grid.ColumnDefinitions>
   <ColumnDefinition />
   <ColumnDefinition />
  </Grid.ColumnDefinitions>
  <Grid.RowDefinitions>
   <RowDefinition Height="*"/>
   <RowDefinition Height="*"/>
   <RowDefinition Height="auto"/>
  </Grid.RowDefinitions>
 <Image x:Name="myImage" Margin="5" />
 <Image Grid.Column="1" x:Name="myGreyImage" Margin="5"/>
 <Image Grid.Row="1" x:Name="detectCircle" Margin="5"/>
 <Image Grid.Row="1" Grid.Column="1" x:Name="detectRectangle" Margin="5"/>
 <Button Grid.Column="1" Grid.Row="2" x:Name="testButton" Content="Load Image" Click="testButton_Click" HorizontalAlignment="Right" VerticalAlignment="Bottom" Height="44" Width="92"/>
</Grid>

I create two methods,first for detecting circles and second for detecting rectangles and triangles. Below are the code:

Detect and Draw detected circles:

private static Image<Bgr, byte> detectCircle(Image<Bgr, byte> image)
{
Image<Gray, byte> imageGrey = image.Convert<Gray, byte>();

double cannyThreshold = 180.0;
double circleAccumulatorThreshold = 120;
CircleF[] circles = CvInvoke.HoughCircles(imageGrey, HoughType.Gradient,
2.0, 20.0, cannyThreshold,
circleAccumulatorThreshold, 5);

//canny and edge detection
double cannyThresholdLinking = 120.0;
UMat cannyEdges = new UMat();
CvInvoke.Canny(imageGrey, cannyEdges, cannyThreshold, cannyThresholdLinking);

LineSegment2D[] lines = CvInvoke.HoughLinesP(cannyEdges, 1, Math.PI / 45.0,
20, 30, 10);
Image<Bgr, byte> circleImage = image.CopyBlank();
foreach (CircleF circle in circles)
circleImage.Draw(circle, new Bgr(255, 0, 0), 5);

return circleImage;
}

Detect and Draw detected rectangles and triangles:

 

 private static Image<Bgr, byte> detectRectTri(Image<Bgr, byte> image)
 {
 Image<Gray, byte> imageGrey = image.Convert<Gray, byte>();

 List<Triangle2DF> triangleList = new List<Triangle2DF>();
 List<RotatedRect> boxList = new List<RotatedRect>();

 UMat cannyEdges = new UMat();
 double cannyThreshold = 180.0;
 double cannyThresholdLinking = 120.0;
 CvInvoke.Canny(imageGrey, cannyEdges, cannyThreshold, cannyThresholdLinking);

 
 using (VectorOfVectorOfPoint countours = new VectorOfVectorOfPoint())
 {

 CvInvoke.FindContours(cannyEdges, countours, null, RetrType.List,
 ChainApproxMethod.ChainApproxSimple);
 int count = countours.Size;
 for (int i = 0; i < count; i++)
 {
 using (VectorOfPoint kontur = countours[i])
 using (VectorOfPoint approxContour = new VectorOfPoint())
 {
 CvInvoke.ApproxPolyDP(kontur, approxContour, CvInvoke.ArcLength(kontur, true) * 0.05, true);
 if (CvInvoke.ContourArea(approxContour, false) > 250) //only consider contours with area greater than 250
 {
 if (approxContour.Size == 3) //the countour has 3 vertices. it is triangle
 {
 System.Drawing.Point[] pts = approxContour.ToArray();
 triangleList.Add(new Triangle2DF(pts[0], pts[1], pts[2]));
 }
 else if (approxContour.Size == 4) //rectangle
 {
 //determine if allthe 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+i)% edges.Length].GetExteriorAngleDegree(edges[j]));
 if (angle < 80 || angle > 100)
 {
 isRectangle = false;
 break;
 }
 
 }
 if (isRectangle) boxList.Add(CvInvoke.MinAreaRect(approxContour));
 }
 }
 }
 }
 }

 Image<Bgr, byte> triRectImage = image.CopyBlank();
 foreach (Triangle2DF triangle in triangleList)
 triRectImage.Draw(triangle, new Bgr(0, 255, 0), 5);
 foreach (RotatedRect box in boxList)
 triRectImage.Draw(box, new Bgr(0, 0, 255), 5);

 return triRectImage;
 }

Button Event Handler:

 private void Button_Click(object sender, RoutedEventArgs e)
        {
            OpenFileDialog openPic = new OpenFileDialog();
            if (openPic.ShowDialog() == true)
            {
                Image<Bgr, byte> gambar = new Image<Bgr,byte>(openPic.FileName);
                originalImage.Source = Emgu.CV.WPF.BitmapSourceConvert.ToBitmapSource(gambar);

                Image<Bgr,byte> bunder = detectCircle(gambar);
              lingkaranImage.Source = Emgu.CV.WPF.BitmapSourceConvert.ToBitmapSource(bunder);

                Image<Bgr, byte> kotak = detectRectTri(gambar);
                kotakSegitigaImage.Source = Emgu.CV.WPF.BitmapSourceConvert.ToBitmapSource(kotak);
            }

        }

Then below was the result:

References: 1

Leave a Reply

%d bloggers like this: