Search code examples
c#image-processingemgucvedge-detectioncanny-operator

How to find coordinate of contour centroid in C#


I am doing image processing so that I am finding contours in the image. What I need is the centroid pixel number of the found contour in the image. To find the pixel number I am using the code given below. After finding the pixel number I want to show it in the text boxes as x and y coordinates. But the code is not working. Please help me. What is wrong?

                VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
                CvInvoke.FindContours(cannyImage, contours, null, Emgu.CV.CvEnum.RetrType.External, Emgu.CV.CvEnum.ChainApproxMethod.ChainApproxSimple);
                    var cannyOut = cannyImage.ToImage<Bgr, byte>();
                    //CvInvoke.DrawContours(cannyOut, contours, 2, new MCvScalar(255, 0, 0),2);

                    VectorOfPoint approx = new VectorOfPoint();

                    Dictionary<int, double> shapes = new Dictionary<int, double>();

                    for (int i = 0; i < contours.Size; i++)
                    {
                        approx.Clear();
                        double perimeter = CvInvoke.ArcLength(contours[i], true);
                        CvInvoke.ApproxPolyDP(contours[i], approx, 0.04 * perimeter, true);
                        double area = CvInvoke.ContourArea(contours[i]);

                        if (approx.Size > 4)
                        {
                            shapes.Add(i, area);
                        }
                    }
                    if (shapes.Count > 0)
                    {
                        var sortedShapes = (from item in shapes
                                            orderby item.Value ascending
                                            select item).ToList();

                        for (int i = 0; i < sortedShapes.Count; i++)
                        {
                            
                            CvInvoke.DrawContours(cannyOut, contours, sortedShapes[i].Key, new MCvScalar(255, 0, 0), 2);
                            var moments = CvInvoke.Moments(contours[sortedShapes[i].Key]);
                            int x = (int)(moments.M10 / moments.M00);
                            int y = (int)(moments.M01 / moments.M00);

                            CvInvoke.PutText(cannyOut, (i + 1).ToString(), new Point(x, y), Emgu.CV.CvEnum.FontFace.HersheyTriplex, 1.0,
                                new MCvScalar(255, 0, 0), 2);
                            //CvInvoke.PutText(cannyOut, sortedShapes[i].Value.ToString(), new Point(x, y - 30), Emgu.CV.CvEnum.FontFace.HersheyTriplex, 1.0,
                            //    new MCvScalar(255, 0, 0), 2);

                            textBox1.Text = x.ToString();
                            textBox2.Text = y.ToString();
                        }

                    }

Solution

  • To find the centroid of a shape you need to split it into many triangles first.

    Then for each triangle with vertices A, B, C you do the summation weighted by the area of the triangle just as so

    static void Main(string[] args)
    {
        var shape = new List<Triangle>();
        // fill shape with triangles
    
        float area = 0f;
        Vector2 centroid = Vector2.Zero;
    
        foreach (var triangle in shape)
        {
            float trig_area = triangle.Area;
            Vector2 trig_cen = triangle.Centroid;
    
            area += trig_area;
            centroid += trig_area * trig_cen;
        }
        centroid /= area;
    
    }
    

    For reference, a 2D triangle has the following properties

    public readonly struct Triangle
    {
    
        public Triangle(Vector2 a, Vector2 b, Vector2 c) : this()
        {
            A = a;
            B = b;
            C = c;
        }
    
        public Vector2 A { get; }
        public Vector2 B { get; }
        public Vector2 C { get; }
    
        public float Area { get => (Cross(A, B) + Cross(B, C) + Cross(C, A)) / 2; }
        public Vector2 Centroid { get => (A + B + C) / 3; }
    
        // helper function
        static float Cross(Vector2 a, Vector2 b) => a.X * b.Y - a.Y * b.X;
    }