23-凸包ConvexHull(EmguCV学习)
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);
}