1、说明

1、CvInvoke.ConvexHull()函数: 寻找二维点集的凸包;
hull : VectorOfPoint类型或者VectorOfInt类型(返回凸包的点或凸包点的索引);
points : 二维点集:VectorOfPoint类型
2、ConvexHull()函数与DrawContours()函数配合,可以用来检测物体是否存在缺陷(应用);
3、convexityDefects()函数说明:计算轮廓的凸性缺陷,可以用来做手势识别

contour : 单个轮廓,数据类型: VectorOfPoint
convexHull : 凸包索引或指针,C#中只能为索引,VectorOfInt 类型(ConvexHull()函数计算出来的)
convexityDefects :  输出结果,凸性缺陷检测结果,个人觉得应该是vector<Vec4i>类型,EmguCV中可以
使用VectorOfVectorOfInt 类或Mat 类或 VectorOfRect 类来存储该结果;依次存储起始点索引、结束点索引、
最远点索引、以及最远点到凸包的距离(像素单位):必须除去256(具有8个小数位),以得到正确的距离值。

OpenCV说明:


EmguCV中说明:

2、 二维点集的凸包

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Emgu.CV;
using Emgu.Util;
using Emgu.CV.Structure;
using Emgu.CV.CvEnum;
using Emgu.CV.Util;
using System.Drawing;
namespace lesson23
{
   
    class Program
    {
   
        static void Main(string[] args)
        {
   
            ///点集的凸包
            //Mat src = Mat.Zeros(500, 500, DepthType.Cv8U, 3); //创建黑色图像

            Mat src = new Mat(500, 500, DepthType.Cv8U, 3);
            src.SetTo(new MCvScalar(0));


            Random random = new Random();
            VectorOfPoint vPoints1 = new VectorOfPoint(); //存储随机点
            VectorOfPoint hull = new VectorOfPoint();  //存储凸包点

            while (true)
            {
   
                hull.Clear(); //清除上一次数据
                src.SetTo(new MCvScalar(0));
                int count = random.Next(3, 60);
                for (int i = 0; i < count;i++)  //创建随机点集
                {
   
                    Point[] pt = new Point[1];
                    pt[0].X = random.Next(src.Cols / 5, src.Cols * 4 / 5);
                    pt[0].Y = random.Next(src.Rows / 5, src.Rows * 4 / 5);
                    //在源图像上显示随机点集
                    CvInvoke.Circle(src, pt[0], 3, 
                        new MCvScalar(random.Next(0, 255), random.Next(0, 255), random.Next(0, 255)),-1);
                    vPoints1.Push(pt);
                }
                CvInvoke.Imshow("src", src);
                CvInvoke.ConvexHull(vPoints1, hull, false, true);
                for(int i = 0; i < hull.Size;i++)       //绘制凸包(闭合曲线)
                {
   
                    CvInvoke.Line(src, hull[i], hull[(i + 1) % hull.Size], new MCvScalar(255, 0, 0), 2);
                }
                CvInvoke.Imshow("result", src);

                CvInvoke.WaitKey(0);
            }
        }
    }
}


3、轮廓的凸包

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Emgu.CV;
using Emgu.Util;
using Emgu.CV.Structure;
using Emgu.CV.CvEnum;
using Emgu.CV.Util;
using System.Drawing;

namespace lesson23_1
{
   
    class Program
    {
   
        static void Main(string[] args)
        {
   
            ///绘制每个轮廓的凸包
            Mat src = CvInvoke.Imread("22.jpg");
            Mat dst = src.Clone();
            Mat gray_img = new Mat();
            CvInvoke.CvtColor(src, gray_img, ColorConversion.Bgr2Gray);  //转换为灰度图

            CvInvoke.Threshold(gray_img, gray_img, 100, 255, ThresholdType.BinaryInv); //转换为灰度图

            VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
            VectorOfRect hierarchy = new VectorOfRect();   //轮廓层次结构
            //发现轮廓
            CvInvoke.FindContours(gray_img, contours, hierarchy, RetrType.Tree, ChainApproxMethod.ChainApproxNone);
            //绘制所有轮廓
            Random random = new Random();
            MCvScalar color1 = new MCvScalar(random.Next(0, 255), random.Next(0, 255), random.Next(0, 255));
            //CvInvoke.DrawContours(dst, contours, -1, color1, 2);

            VectorOfInt hull = new VectorOfInt();   //存储凸包点索引
            //VectorOfPoint hull = new VectorOfPoint(); //也可以直接存储凸包点
            for(int i = 0; i < contours.Size; i++)  //绘制凸包
            {
   
                CvInvoke.ConvexHull(contours[i], hull);
                MCvScalar color2 = new MCvScalar(random.Next(0, 255), random.Next(0, 255), random.Next(0, 255));

                for (int j = 0; j < hull.Size; j++)
                {
   
                    if (j != hull.Size - 1)
                        CvInvoke.Line(dst, contours[i][hull[j]], contours[i][hull[j + 1]], color2, 2);
                    else
                        CvInvoke.Line(dst, contours[i][j], contours[i][0], color2, 2);
                }
            }
            CvInvoke.Imshow("result", dst);

            CvInvoke.WaitKey(0);
        }
    }
}


4、缺陷检测

 static void Main(string[] args)
 {
   
     //缺陷检测
     Mat srcImg = CvInvoke.Imread("12.png");
     Mat result = srcImg.Clone();
     Mat gray_src = new Mat();
     Mat img1 = new Mat(srcImg.Size, DepthType.Cv8U, 1);
     Mat img2 = img1.Clone();

     CvInvoke.Imshow("input", srcImg);
     CvInvoke.CvtColor(srcImg, gray_src, ColorConversion.Bgr2Gray);
     CvInvoke.Threshold(gray_src, gray_src, 100, 255, ThresholdType.Binary);

     VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
     VectorOfRect hierarchy = new VectorOfRect();
     CvInvoke.FindContours(gray_src, contours, hierarchy, RetrType.Tree, ChainApproxMethod.ChainApproxNone);
     CvInvoke.DrawContours(img1, contours, -1, new MCvScalar(255), 2);  //创建轮廓掩码
     CvInvoke.MedianBlur(img1, img1, 5);
     CvInvoke.Imshow("contours mask", img1);
     
     VectorOfPoint hull = new VectorOfPoint();
     for(int i = 0; i < contours.Size; i++)          //绘制凸包
     {
   
         CvInvoke.ConvexHull(contours[i], hull);  //计算凸包
         for (int j = 0; j < hull.Size;j++)
         {
   
             CvInvoke.Line(img2, hull[j], hull[(j + 1) % hull.Size], new MCvScalar(255), 2);
         }
     }
     CvInvoke.MedianBlur(img2, img2, 5);
     CvInvoke.Imshow("convex hull", img2);
     Mat diff = new Mat();
     CvInvoke.AbsDiff(img1, img2, diff);
     CvInvoke.Imshow("Diff", diff);
     //输出判断缺陷结果 :NG(Not Good)、OK
     VectorOfVectorOfPoint contours2 = new VectorOfVectorOfPoint();
     VectorOfRect hierarchy2 = new VectorOfRect();

     CvInvoke.FindContours(diff, contours2, hierarchy2, RetrType.Tree, ChainApproxMethod.ChainApproxNone);
     CvInvoke.DrawContours(result, contours2, -1, new MCvScalar(0, 0, 255), 2);      //在原图像上绘制出轮廓
     if (contours2.Size > 0)      //说明存在缺陷
     {
   
         CvInvoke.PutText(result, "NG", new Point(80, 100), FontFace.HersheyDuplex, 0.8, new MCvScalar(0, 0, 255), 2);
     }
     else
         CvInvoke.PutText(result, "OK", new Point(80, 100), FontFace.HersheyDuplex, 0.8, new MCvScalar(0, 255, 0), 2);
     CvInvoke.Imshow("result", result);

     CvInvoke.WaitKey(0);
 }


5、轮廓凸缺陷(ConvexityDefects())

static void Main(string[] args)
      {
   
           //凸包缺陷检测
           Mat srcImg = CvInvoke.Imread("mask.jpg");
           Mat dst = srcImg.Clone();
           CvInvoke.Imshow("input", srcImg);
           Mat grayImg = new Mat();

           CvInvoke.CvtColor(srcImg, grayImg, ColorConversion.Bgr2Gray);
           CvInvoke.Threshold(grayImg, grayImg, 100, 255, ThresholdType.Binary);

           VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
           VectorOfRect hierarchy = new VectorOfRect();

           CvInvoke.FindContours(grayImg, contours, hierarchy, RetrType.External, ChainApproxMethod.ChainApproxNone);
           CvInvoke.DrawContours(dst, contours, -1, new MCvScalar(255, 0, 0), 2);

           for(int i = 0; i < contours.Size;i++)
           {
   
               VectorOfInt hull = new VectorOfInt();
               CvInvoke.ConvexHull(contours[i], hull);    //计算凸包 
               for (int j = 0; j < hull.Size; j++)          //绘制凸包 
               {
   
                   CvInvoke.Circle(dst, contours[i][hull[j]], 5, new MCvScalar(0, 255, 0), -1);
                   CvInvoke.Line(dst, contours[i][hull[j]], contours[i][hull[(j + 1) % hull.Size]],
                               new MCvScalar(0, 255, 255), 2);
               }

               Mat defects = new Mat();
               CvInvoke.ConvexityDefects(contours[i], hull, defects);          //凸包缺陷检测 ,hull必须为索引
               Console.WriteLine("{0}", defects);
               if(!defects.IsEmpty)            //中文!与英文!不同
               {
   
                   //1行4列
                   using (Matrix<int> m = new Matrix<int>(defects.Rows, defects.Cols, defects.NumberOfChannels))
                   {
   
                       defects.CopyTo(m);   //刚开始使用Int16(Short)数据类型的矩阵,将矩阵中数据截断导致结果错误!!
                       for(int j = 0; j < m.Rows;j++)
                       {
   
                           int startIdx = m.Data[j, 0];  //起始点在轮廓上的索引
                           int endIdx = m.Data[j, 1];
                           int farthestIdx = m.Data[j, 2];
                           double distance = m.Data[j, 3] / 256.0;  //距离

                           Point startPoint = contours[i][startIdx];
                           Point endPoint = contours[i][endIdx];
                           Point farPoint = contours[i][farthestIdx];

                           Console.WriteLine("distance = {0}.", distance);
                           CvInvoke.Line(dst, startPoint, endPoint, new MCvScalar(128, 128, 0), 2);
                           CvInvoke.Circle(dst, farPoint, 5, new MCvScalar(0, 0, 255), -1);  //红色点为距离凸包最远点
                       }
                   }
               }
           }
           CvInvoke.Imshow("result", dst);
           CvInvoke.WaitKey(0);            
       }