C# OpenCvSharp corn kernel counting

Effect

Project

Code

using OpenCvSharp;
using System;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
 
namespace OpenCvSharp_Demo
{
    public partial class frmMain : Form
    {
        public frmMain()
        {
            InitializeComponent();
        }
 
        string fileFilter = "*.*|*.bmp;*.jpg;*.jpeg;*.tiff;*.tiff;*.png";
        string image_path = "";
 
        DateTime dt1 = DateTime.Now;
        DateTime dt2 = DateTime.Now;
 
        Mat image;
        Mat result_image;
 
        StringBuilder sb = new StringBuilder();
 
        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = fileFilter;
            if (ofd.ShowDialog() != DialogResult.OK) return;
 
            pictureBox1.Image = null;
            pictureBox2.Image = null;
            textBox1.Text = "";
 
            image_path = ofd.FileName;
            pictureBox1.Image = new Bitmap(image_path);
            image = new Mat(image_path);
        }
 
        private void Form1_Load(object sender, EventArgs e)
        {
            //test
            image_path = "test_img/1.jpg";
            image = new Mat(image_path);
            pictureBox1.Image = new Bitmap(image_path);
        }
 
        private void button2_Click(object sender, EventArgs e)
        {
            if (image_path == "")
            {
                return;
            }
            textBox1.Text = "Detecting, please wait...";
            pictureBox2.Image = null;
            Application.DoEvents();
 
            result_image = image.Clone();
 
            //Binarization operation
            Mat grayimg = new Mat();
            Cv2.CvtColor(image, grayimg, ColorConversionCodes.BGR2GRAY);
            Mat BinaryImg = new Mat();
            Cv2.Threshold(grayimg, BinaryImg, 240, 255, ThresholdTypes.Binary);
            //Cv2.ImShow("Binarization", BinaryImg);
 
            //corrosion
            Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(15, 15));
            Mat morhImage = new Mat();
            Cv2.Dilate(BinaryImg, morhImage, kernel, null, 2);
            //Cv2.ImShow("morphology", morhImage);
 
            //Distance transformation: used for the distance between each non-zero point in the binary image and its nearest zero point. The brighter point on the distance transformation image represents the farther the distance between this point and the zero point.
            Mat dist = new Mat();
            Cv2.BitwiseNot(morhImage, morhImage);
            /*
            In OpenCV, the function distanceTransform() is used to calculate the distance between each non-zero pixel in the image and its nearest zero pixel.
            The output is to save the distance information between each non-zero point and the nearest zero point. The brighter the point on the image, the farther away it is from the zero point.
            use:
            Based on this property of distance transformation, simple operations can be used to refine the outline of characters and find the centroid (center) of the object.
            */
            /*
            Images processed by distance transformation are usually binary images, and binary images actually divide the image into two parts, namely the background and the object. The object is usually also called the foreground target.
            Usually we set the grayscale value of the foreground target to 255 (i.e. white), and the grayscale value of the background to 0 (i.e. black).
            Therefore, the non-zero pixels in the definition are the foreground targets, and the zero pixels are the background.
            So the farther the pixel point in the foreground target in the image is from the background, the greater the distance. If we replace the pixel value with this distance value, the brighter the point in the newly generated image will be.
            */
            //User: user-defined
            //L1: Manhattan distance
            //L2: Euclidean distance
            //C: chessboard distance
            Cv2.DistanceTransform(morhImage, dist, DistanceTypes.L1, DistanceTransformMasks.Mask3);
            Cv2.Normalize(dist, dist, 0, 1.0, NormTypes.MinMax); //The range is between 0~1
            //Cv2.ImShow("distance", dist);
 
            //morphological processing
            Mat MorphImg = new Mat();
            dist.ConvertTo(MorphImg, MatType.CV_8U);
            Cv2.Threshold(MorphImg, MorphImg, 0.99, 255, ThresholdTypes.Binary); //The upper image pixel value is between 0~1
            kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(7, 3), new OpenCvSharp.Point(-1, -1));
            Cv2.MorphologyEx(MorphImg, MorphImg, MorphTypes.Open, kernel); //Open operation
            //Cv2.ImShow("t-distance", MorphImg);
 
            //Find the outline area of the seed
            OpenCvSharp.Point[][] contours;
            HierarchyIndex[] hierarchly;
            Cv2.FindContours(MorphImg, out contours, out hierarchly, RetrievalModes.External, ContourApproximationModes.ApproxSimple, new OpenCvSharp.Point(0, 0));
            Mat markers = Mat.Zeros(image.Size(), MatType.CV_8UC3);
            int x, y, w, h;
            Rect rect;
            for (int i = 0; i < contours.Length; i + + )
            {
                // Cv2.DrawContours(markers, contours, i, Scalar.RandomColor(), 2, LineTypes.Link8, hierarchly);
                rect = Cv2.BoundingRect(contours[i]);
                x = rect.X;
                y = rect.Y;
                w = rect.Width;
                h = rect.Height;
                Cv2.Circle(result_image, x + w / 2, y + h / 2, 20, new Scalar(0, 0, 255), -1);
                if (i >= 9)
                {
                    Cv2.PutText(result_image, (i + 1).ToString(), new OpenCvSharp.Point(x + w / 2 - 18, y + h / 2 + 8), HersheyFonts.HersheySimplex, 0.8, new Scalar(0, 255 , 0), 2);
                }
                else
                {
                    Cv2.PutText(result_image, (i + 1).ToString(), new OpenCvSharp.Point(x + w / 2 - 8, y + h / 2 + 8), HersheyFonts.HersheySimplex, 0.8, new Scalar(0, 255 , 0), 2);
                }
            }
 
            textBox1.Text = "number of corns: " + contours.Length;
            pictureBox2.Image = new Bitmap(result_image.ToMemoryStream());
 
 
        }
 
        private void pictureBox2_DoubleClick(object sender, EventArgs e)
        {
            Common.ShowNormalImg(pictureBox2.Image);
        }
 
        private void pictureBox1_DoubleClick(object sender, EventArgs e)
        {
            Common.ShowNormalImg(pictureBox1.Image);
        }
    }
}

Download

Demo download