Contour Identification of an Image using C# and EmguCV

Finding Contours can be useful in creating masks for images and to segment and extract features from an image. Its used by Meteorologists to understand weather maps and for analysis and forecasts. The following post describes how contours could be identified in an image.

Implementation would be based on EmguCV the popular .NET ported version of OpenCV. Even though OpenCV is built in C++, EmguCV provides a wrapper which manages to invoke the same OpenCV libraries.By use of the EmguCV wrapper it provides all the necessary functions as well as data types of OpenCV in a .NET compliant library making it easier to implement.

Setting up the Project

  1. To start off with after launching a visual studio editor, create a new C# windows forms application.
  2. Next using Nuget Package manager find the EmguCV package.
  3. Nuget EmguCV Package

    Nuget EmguCV Package

    This EmguCV package consists of a light version of OpenCV (15MB) the actual OpenCV version is >100MBs larger. For the purpose of this demonstration the Nuget version is sufficient enough.
    If you prefer having the complete package to start off with download and install EmguCV from here

  4. If you notice by adding the Nuget package all of the .NET EmguCV Wrapper DLL have been included into the new project. Next we need to reference the native DLLs to the build path so that when running the application it would find the correct DLLs
  5. Right click on the project title in the solutions window and select add existing item, and select all the opencv DLLs located under the packages\opencv folder, and select Add as Link
    Linking Native DLLs

    Linking Native DLLs

Once these DLLs are referenced in the project as a link, select all the DLLs from the solution explorer and from the properties panel select Copy to output directory

Copy to output directory

Copy to output directory

Once all of the above have completed, your ready to go with EmguCV.

Implementing the Application

Application overview

Before contour extraction its important to filter an image so that unwanted noise and details are removed. Even though are eyes aren’t sensitive enough to find fine grained details of an image, cameras and image sensing devices are capable of it. This on the other hand becomes problematic since subtle pixel variations can detect as contour edges of an image.

The application could be divided into three main parts for simplicity.

  1. Capturing a Camera stream off of a webcam.
  2. Simple thresholding of the image to filter out noise
  3. Contour extraction

The Application Demo


The video demonstrates the usage of the application and how it detects separate contours, based on the threshold value of the image. Also notice how the contour edge changes when the same image inverts causing the contour lines to inset into the area.

User Interface

FindContours - Design

FindContours – Design

The form interface features buttons to start the camera capture, additional components are for the threshold slider, inverting the captured image, also two image viewers are used to display the colored image and the grayscale image. A timer control is used to stream the images from the camera to extract contours.

Implementation

The event bindings of the UI have been listed below. The Time tick function would tick every 100 milliseconds and poll for a new camera frame. This captured frame would be passed to the FindContours class and it would process and send the output images.

frmContours.cs

 /// <summary>
    /// Main form to interact with EmguCV for contour processing.
    /// </summary>
    public partial class frmContours : Form
    {
        Emgu.CV.Capture c;
        FindContours processor = new FindContours();
        Bitmap colorImage;

        /// <summary>
        /// Initializes the initial tracker value to th tracker label.
        /// </summary>
        public frmContours()
        {
            InitializeComponent();
            lblThresholdValue.Text = trackbarThreshold.Value.ToString();
        }

        /// <summary>
        /// Updates the threshold label when the tracker changes.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void trackbarThreshold_Scroll(object sender, EventArgs e)
        {
            lblThresholdValue.Text = trackbarThreshold.Value.ToString();
        }

        /// <summary>
        /// Starts the camera capture.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnCapture_Click(object sender, EventArgs e)
        {
            if (c == null)
            {
                c = new Emgu.CV.Capture();
            }
            CameraStreamCapture.Enabled = true;

        }

        /// <summary>
        /// Stops the Camera capture.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnStopCapture_Click(object sender, EventArgs e)
        {
            CameraStreamCapture.Enabled = false;
            if (c != null)
            {
                c.Dispose();
                c = null;
            }

        }
        /// <summary>
        /// Start capturing from the camera stream.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CameraStreamCapture_Tick(object sender, EventArgs e)
        {
            colorImage = c.QueryFrame().ToBitmap();
            Bitmap color;
            Bitmap gray;
            processor.IdentifyContours(colorImage, trackbarThreshold.Value, chkBoxInvert.Checked, out gray, out color);
            pictBoxColor.Image = color;
            pictBoxGray.Image = gray;
        }

        /// <summary>
        /// Stop the camera and release resources when the form is closing
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void frmContours_FormClosing(object sender, FormClosingEventArgs e)
        {
            CameraStreamCapture.Enabled = false;
            c = null;
        }
    }

The find contours class performs the contour extraction. It takes a color image captured from the camera, converts it to grayscale and then uses the grayscale version. The FindContours method allows to get a list of contours which could be iterated. When extracting contours its possible to provide a CHAIN_APPROX_METHOD which specifies how contour point sequences are chained together to create a single connected approximated polygon.

We then iterate through the contour list and then use the ApproxPoly function to find the contour which helps us to identify the bounding rectangle and the surrounding scope and area of the identified contour.

In order to filter out noise here we only draw contours with a width greater than 20 pixels.

finally the contour is been drawn in blue and the bounding rectangle of it is drawn in green. The resulting images are send out of the method call as output parameters.

FindContours.cs

 /// <summary>
    /// Class responsible for extracting out the contours of an image.
    /// </summary>
    class FindContours
    {
        /// <summary>
        /// Method used to process the image and set the output result images.
        /// </summary>
        /// <param name="colorImage">Source color image.</param>
        /// <param name="thresholdValue">Value used for thresholding.</param>
        /// <param name="processedGray">Resulting gray image.</param>
        /// <param name="processedColor">Resulting color image.</param>
        public void IdentifyContours(Bitmap colorImage,int thresholdValue,bool invert,out Bitmap processedGray,out Bitmap processedColor)
        {

            #region Conversion To grayscale
            Image<Gray, byte> grayImage = new Image<Gray, byte>(colorImage);
            Image<Bgr, byte> color = new Image<Bgr, byte>(colorImage);
            
            #endregion
            

            #region  Image normalization and inversion (if required)
            grayImage = grayImage.ThresholdBinary(new Gray(thresholdValue), new Gray(255));
            if (invert)
            {
                grayImage._Not();
            }  
            #endregion
            
            #region Extracting the Contours
            using (MemStorage storage = new MemStorage())
            {

                for (Contour<Point> contours = grayImage.FindContours(Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_TREE, storage); contours != null; contours = contours.HNext)
                {

                    Contour<Point> currentContour = contours.ApproxPoly(contours.Perimeter * 0.015, storage);
                    if (currentContour.BoundingRectangle.Width > 20)
                    {
                        CvInvoke.cvDrawContours(color, contours, new MCvScalar(255), new MCvScalar(255), -1, 2, Emgu.CV.CvEnum.LINE_TYPE.EIGHT_CONNECTED, new Point(0, 0));
                        color.Draw(currentContour.BoundingRectangle, new Bgr(0, 255, 0), 1);
                    }
                }

            } 
            #endregion
            

            #region Asigning output
            processedColor = color.ToBitmap();
            processedGray = grayImage.ToBitmap();
            #endregion
        }
    }

This Example project could be downloaded from the URL below.
Find Contours Source Code
** Note that the Nuget Packages have been removed to reduce the file size **

Hope the post would provide a basic insight as to how image processing is done using the popular OpenCV library in C# .NET.

Lasitha Ishan Petthawadu.

Advertisements

10 thoughts on “Contour Identification of an Image using C# and EmguCV

  1. Pingback: Contour Identification of an Image using C# and EmguCV | Ragnarok Connection

  2. Pingback: Contour Identification of an Picture utilizing C# and EmguCV | Ragnarok Connection

    • You need to configure emgucv first in your project.
      There are some simple steps which you can follow
      1) Download latest emgucv
      2) Set envoirement variable
      3) Add refrence to emgucv /bin/ and dlls
      4) Add existing items in project from emgucv/bin/x86 (add according to your need for example you need to specify vs version for it)
      5) Build again and run . let me know if there is any problem

      • grayImage.FindContours(Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_TREE, storage);

        The findContour Function always have red line as it’s not found , I’m sure that i added all references .. so what is the solution?!

      • Hi Adel,
        Please try to see if the correct Using statements have been added to the top of the class, if so please check the reference type of the grayImage variable.
        You could also use the Object explorer and see if the references you added have the reference type referred by grayImage within it to see if you have the correct DLLs referred. Happy Coding!! Cheers!

  3. Hi. I like to take a photo of a paper document, then convert it to a scanned document – smaller, cleaner and black & White.
    I will basically send the photo (.jpeg) to server and have server scan it automatically into another .jpeg file.
    Can this code be used to do what I want, or there is more to it?
    thanks. Kimya

  4. hy Emgu CV is changed very much now can u please upload latest way of doing it ? i am getting errors and can not do this

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s