C#+OpenCV基础(十六)_识别圆、直线、斑点、轮廓
一、简易图像
1、识别直线(霍夫直线变换)
/// <summary>
/// 检测直线并标注(霍夫直线变换)
/// 需要优化
/// </summary>
/// <param name="mat">图片</param>
/// <param name="resultMat">结果图片</param>
/// <returns>直线数据</returns>
public static LineSegmentPoint[] LineDetector_HoughLines(Mat mat, out Mat resultMat)
{
resultMat = new Mat();
mat.CopyTo(resultMat);
// 转化为灰度图
Mat gray = new Mat();
Cv2.CvtColor(mat, gray, ColorConversionCodes.BGR2GRAY);
// 膨胀1次(图型添加轮廓)
var kenal3 = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(1, 1));
Cv2.Dilate(gray, gray, kenal3);
// 膨胀Cv2.Dilate 与 腐蚀Cv2.Dilate
// 边缘检测(Canny算子)
Cv2.Canny(gray, gray, 50, 150);
// 检测直线(霍夫直线变换)
LineSegmentPoint[] lines = Cv2.HoughLinesP(gray, 1, Math.PI / 180, 100, 20, 50);
if (lines.Count() > 0)
{
for (int i = 0; i < lines.Count(); i++)
{
// 画线
Cv2.Line(resultMat, lines[i].P1, lines[i].P2, new Scalar(0, 0, 255), 2, LineTypes.AntiAlias);
}
}
return lines;
}
2、识别圆(霍夫圆变换)
/// <summary>
/// 检测圆(霍夫圆变换)
/// 需要优化
/// </summary>
/// <param name="mat">图片</param>
/// <param name="resultMat">结果图片</param>
/// <returns>圆数据</returns>
public static CircleSegment[] CircleDetector_HoughCircles(Mat mat, out Mat resultMat)
{
resultMat = new Mat();
mat.CopyTo(resultMat);
// 转化为灰度图
Mat gray = new Mat();
Cv2.CvtColor(mat, gray, ColorConversionCodes.BGR2GRAY);
// 膨胀1次(图型添加轮廓)
var kenal3 = Cv2.GetStructuringElement(MorphShapes.Rect, new OpenCvSharp.Size(1, 1));
Cv2.Dilate(gray, gray, kenal3);
// 膨胀Cv2.Dilate 与 腐蚀Cv2.Dilate
// 边缘检测(Canny算子)
Cv2.Canny(gray, gray, 50, 150);
// 检测圆(霍夫圆变换)
//# - gray: 待检测的灰度图
//# - HoughModes_Gradient:检测的方法,霍夫梯度
//# - 1:检测的圆与原始图像具有相同的大小,dp=2,检测的圆是原始图像的一半
//# - 20:检测到的圆的中心的最小距离(如果参数为太小,除了一个真实的圆外,还可能会错误地检测到多个相邻圆。如果太大,可能会漏掉一些圆。)
//# - param1:在#HOUGHŠu梯度的情况下,它是较高的. 两个阈值传递到Canny边缘检测器(较低的一个小两倍)。
//# - param2:在#HOUGHŠu梯度的情况下,它是检测阶段圆心的累加器阈值。它越小,就越可能检测到假圆;
//# - minRadius:最小圆半径,也可能会检测到假圆
//# - maxRadius:最大圆半径,如果<=0,则使用最大图像尺寸。如果<0,则返回没有找到半径的中心。
CircleSegment[] circles = Cv2.HoughCircles(gray, HoughModes.Gradient, 1, 80, 70, 30, 10, 60);
if (circles.Count() > 0)
{
for (int i = 0; i < circles.Count(); i++)
{
// 画圆
Cv2.Circle(resultMat,
Convert.ToInt32(circles[i].Center.X), Convert.ToInt32(circles[i].Center.Y),
Convert.ToInt32(circles[i].Radius),
new Scalar(0, 0, 255), 2, LineTypes.AntiAlias);
// 画圆心
Cv2.Circle(resultMat,
Convert.ToInt32(circles[i].Center.X), Convert.ToInt32(circles[i].Center.Y),
3, new Scalar(0, 0, 255), 2, LineTypes.AntiAlias);
}
}
return circles;
}
3、检测简易斑点并标注
/// <summary>
/// 检测简易斑点并标注
/// </summary>
/// <param name="mat">图片</param>
/// <param name="resultMat">结果图片</param>
/// <returns>斑点中心点数据</returns>
public static KeyPoint[] SimpleblobDetector(Mat mat, out Mat resultMat)
{
// 转化为灰度图
Mat gray = new Mat();
Cv2.CvtColor(mat, gray, ColorConversionCodes.BGR2GRAY);
// 创建SimpleBlobDetector并设置参数
SimpleBlobDetector.Params parameters = new SimpleBlobDetector.Params();
// parameters.BlobColor:斑点的亮度值,取值为0或255,默认为0,表示只检测黑色斑点。
parameters.FilterByArea = true; // 是否根据斑点的面积进行过滤,默认为true
parameters.MinArea = 30; // 最小的斑点面积,默认为25
parameters.MaxArea = 6000; // 最大的斑点面积,默认为5000
// 创建SimpleBlobDetector
SimpleBlobDetector detector = SimpleBlobDetector.Create(parameters);
// 检测斑点
KeyPoint[] keypoints = detector.Detect(gray);
// 在图像上绘制斑点
resultMat = new Mat();
Cv2.DrawKeypoints(mat, keypoints, resultMat, Scalar.All(-1));
return keypoints;
}
二、轮廓检测
- 常见的串行边缘检测算子(轮廓跟踪、光栅跟踪、全向跟踪):;
- 常见的并行边缘检测算子:Roberts、Laplace、Sobel、Prewitt、Kirsch、LoG、Canny算子;
- 一阶微分:Sobel算子、Prewitt算子、Roberts算子、差分边缘检测;
- 二阶微分:拉普拉斯算子、高斯拉普拉斯算子、Canny算子;
- 一阶与二阶微分混合算子:Mar-Hildreth算子;
1、检测轮廓 - 检测轮廓并标注(固定阈值)
/// <summary>
/// 检测轮廓并标注(固定阈值)
/// 1、转灰度图
/// 2、二值化(固定阈值)
/// 3、查找轮廓
/// 4、绘制轮廓
/// </summary>
/// <param name="mat">图片</param>
/// <param name="resultMat">灰度结果图片</param>
/// <param name="thresh">中间阈值</param>
/// <param name="maxval">满足条件的最大值</param>
/// <param name="type">阈值类型</param>
/// <returns>轮廓点位集合</returns>
public static Point[][] ContoursDetector_Threshold(Mat mat, out Mat resultMat, double thresh, out HierarchyIndex[] hierarchy,
double maxval = 255, ThresholdTypes type = ThresholdTypes.Binary)
{
resultMat = new Mat();
mat.CopyTo(resultMat);
// 转化为灰度图
Mat gray = new Mat();
Cv2.CvtColor(mat, gray, ColorConversionCodes.BGR2GRAY);
// 滤波(预处理手段之一)
Cv2.Blur(gray, gray, new Size(3, 3));
// 二值化处理
//Scalar scalar = Cv2.Mean(gray); // 计算灰度图平均值;这里取的平均值
//Cv2.Threshold(gray, gray, scalar.Val0, maxval, type); // 二值化
Cv2.Threshold(gray, gray, thresh, maxval, type); // 二值化
/*
* 找轮廓(输入图像,out 轮廓集合,out 级别,轮廓检索模式,近似法,偏移量)
* 输入图像:单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像;
* 轮廓集合:contours
* 历史轮廓:hierarchy:0:后一个轮廓,1:前一个轮廓,2:父轮廓,3:内嵌轮廓
* 轮廓检索模式:轮廓的检索模式
* 取值一:CV_RETR_EXTERNAL 只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
* 取值二:CV_RETR_LIST 检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1,具体下文会讲到
* 取值三:CV_RETR_CCOMP 检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层
* 取值四:CV_RETR_TREE 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
* 近似方法:轮廓的近似方法
* 取值一:CV_CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点到contours向量内
* 取值二:CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留
* 取值三和四:CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法
* 偏移量:Point偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,且Point可以是负值。不填为默认不偏移Point()
*/
Cv2.FindContours(gray, out Point[][] contours, out hierarchy, RetrievalModes.List, ContourApproximationModes.ApproxSimple);
// 画轮廓
for (int i = 0; i < contours.Length; i++)
{
Cv2.DrawContours(resultMat, contours, i, Scalar.Green, 2, LineTypes.Link8, hierarchy); // 绘制轮廓(轮廓颜色为绿色,轮廓线粗细为2)
}
return contours;
}
2、检测轮廓 - 检测轮廓并标注(自适应阈值)
/// <summary>
/// 检测轮廓并标注(自适应阈值)
/// 1、转灰度图
/// 2、二值化(固定阈值)
/// 3、查找轮廓
/// 4、绘制轮廓
/// </summary>
/// <param name="mat">图片</param>
/// <param name="resultMat">灰度结果图片</param>
/// <param name="maxValue">满足条件的最大值</param>
/// <param name="adaptiveMethod">自适应阈值算法</param>
/// <param name="thresholdType">阈值类型</param>
/// <param name="blockSize">用于计算像素阈值的邻域块大小</param>
/// <param name="c">从平均值或加权平均值中减去常数</param>
/// <returns>轮廓点位集合</returns>
public static Point[][] ContoursDetector_AdaThreshold(Mat mat, out Mat resultMat, out HierarchyIndex[] hierarchy, double maxValue = 255,
AdaptiveThresholdTypes adaptiveMethod = AdaptiveThresholdTypes.MeanC, ThresholdTypes thresholdType = ThresholdTypes.Binary, int blockSize = 15, int c = -2)
{
resultMat = new Mat();
mat.CopyTo(resultMat);
// 转化为灰度图
Mat gray = new Mat();
Cv2.CvtColor(mat, gray, ColorConversionCodes.BGR2GRAY);
// 滤波
Cv2.Blur(gray, gray, new Size(3, 3));
// 自适应二值化处理
Cv2.AdaptiveThreshold(gray, gray, maxValue, adaptiveMethod, thresholdType, blockSize, c);
/*
* 找轮廓(输入图像,out 轮廓集合,out 级别,轮廓检索模式,近似法,偏移量)
* 输入图像:单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像;
* 轮廓集合:contours
* 历史轮廓:hierarchy:0:后一个轮廓,1:前一个轮廓,2:父轮廓,3:内嵌轮廓
* 轮廓检索模式:轮廓的检索模式
* 取值一:CV_RETR_EXTERNAL 只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
* 取值二:CV_RETR_LIST 检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1,具体下文会讲到
* 取值三:CV_RETR_CCOMP 检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层
* 取值四:CV_RETR_TREE 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
* 近似方法:轮廓的近似方法
* 取值一:CV_CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点到contours向量内
* 取值二:CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留
* 取值三和四:CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法
* 偏移量:Point偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,且Point可以是负值。不填为默认不偏移Point()
*/
Cv2.FindContours(gray, out Point[][] contours, out hierarchy, RetrievalModes.List, ContourApproximationModes.ApproxSimple);
// 画轮廓
for (int i = 0; i < contours.Length; i++)
{
Cv2.DrawContours(resultMat, contours, i, Scalar.Green, 2, LineTypes.Link8, hierarchy); // 绘制轮廓(轮廓颜色为绿色,轮廓线粗细为2)
}
return contours;
}
3、检测轮廓 - 检测边缘并标注(Sobel算子)
/// <summary>
/// 检测边缘并标注(Sobel算子)
/// 1、转灰度图
/// 2、Blur滤波等预处理
/// 3、边缘检测
/// 4、查找轮廓
/// 5、绘制轮廓
/// </summary>
/// <param name="mat">图片</param>
/// <param name="resultMat">结果图片</param>
/// <param name="hierarchy">历史轮廓</param>
/// <returns>轮廓信息</returns>
public static Point[][] MarginDetector_Sobel(Mat mat, out Mat resultMat, out HierarchyIndex[] hierarchy)
{
resultMat = new Mat();
mat.CopyTo(resultMat);
// 1、转灰度图
Mat gray = new Mat();
Cv2.CvtColor(mat, gray, ColorConversionCodes.BGR2GRAY);
// 2、Blur滤波处理(模糊)
//Cv2.Blur(gray, gray, new Size(2, 2));
// 2.1、膨胀1次(图型添加轮廓) // 膨胀Cv2.Dilate 与 腐蚀Cv2.Dilate
//var kenal3 = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(1, 1));
//Cv2.Dilate(gray, gray, kenal3);
// 3、边缘检测(Sobel算子)
Mat sobelMat = new Mat();
Cv2.Sobel(gray, sobelMat, MatType.CV_8U, 1, 0); // 输入图像,输出图像,输出图像的深度,在水平方向上进行边缘检测的阶数,在垂直方向上进行边缘检测的阶数,卷积核的大小默认为3,缩放结果,调整结果的偏移,指定边界的处理方式
// 4、查找轮廓
/*
* 找轮廓(输入图像,out 轮廓集合,out 级别,轮廓检索模式,近似法,偏移量)
* 输入图像:单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像;
* 轮廓集合:contours
* 历史轮廓:hierarchy:0:后一个轮廓,1:前一个轮廓,2:父轮廓,3:内嵌轮廓
* 轮廓检索模式:轮廓的检索模式
* 取值一:CV_RETR_EXTERNAL 只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
* 取值二:CV_RETR_LIST 检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1,具体下文会讲到
* 取值三:CV_RETR_CCOMP 检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层
* 取值四:CV_RETR_TREE 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
* 近似方法:轮廓的近似方法
* 取值一:CV_CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点到contours向量内
* 取值二:CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留
* 取值三和四:CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法
* 偏移量:Point偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,且Point可以是负值。不填为默认不偏移Point()
*/
Cv2.FindContours(sobelMat, out Point[][] contours, out hierarchy, RetrievalModes.List, ContourApproximationModes.ApproxSimple);
// 5、绘制轮廓
for (int i = 0; i < contours.Length; i++)
{
Cv2.DrawContours(resultMat, contours, i, Scalar.Green, 2, LineTypes.Link8, hierarchy); // 绘制轮廓(轮廓颜色为绿色,轮廓线粗细为2)
}
return contours;
}
4、检测轮廓 - 检测边缘并标注(Canny算子)
/// <summary>
/// 检测边缘并标注(Canny算子)
/// 1、转灰度图
/// 2、Blur滤波处理等预处理
/// 3、边缘检测
/// 4、查找轮廓
/// 5、绘制轮廓
/// </summary>
/// <param name="mat">图片</param>
/// <param name="resultMat">结果图片</param>
/// <param name="hierarchy">历史轮廓</param>
/// <returns>轮廓信息</returns>
public static Point[][] MarginDetector_Canny(Mat mat, out Mat resultMat, out HierarchyIndex[] hierarchy)
{
resultMat = new Mat();
mat.CopyTo(resultMat);
// 1、转灰度图
Mat gray = new Mat();
Cv2.CvtColor(mat, gray, ColorConversionCodes.BGR2GRAY);
// 2、Blur滤波处理(模糊)
Cv2.Blur(gray, gray, new Size(3, 3));
// 2.1、膨胀1次(图型添加轮廓) // 膨胀Cv2.Dilate 与 腐蚀Cv2.Dilate
var kenal3 = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(3, 3));
Cv2.Dilate(gray, gray, kenal3);
// 3、边缘检测(Canny算子)
Mat cannyMat = new Mat();
Cv2.Canny(gray, cannyMat, 30, 200); // 输入图像,输出图像,第一个边缘阈值,第二个边缘阈值,Sobel算子的孔径大小默认为3,求梯度大小
// 4、查找轮廓
/*
* 找轮廓(输入图像,out 轮廓集合,out 级别,轮廓检索模式,近似法,偏移量)
* 输入图像:单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像;
* 轮廓集合:contours
* 历史轮廓:hierarchy:0:后一个轮廓,1:前一个轮廓,2:父轮廓,3:内嵌轮廓
* 轮廓检索模式:轮廓的检索模式
* 取值一:CV_RETR_EXTERNAL 只检测最外围轮廓,包含在外围轮廓内的内围轮廓被忽略
* 取值二:CV_RETR_LIST 检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系,这就意味着这个检索模式下不存在父轮廓或内嵌轮廓,所以hierarchy向量内所有元素的第3、第4个分量都会被置为-1,具体下文会讲到
* 取值三:CV_RETR_CCOMP 检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层
* 取值四:CV_RETR_TREE 检测所有轮廓,所有轮廓建立一个等级树结构。外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓。
* 近似方法:轮廓的近似方法
* 取值一:CV_CHAIN_APPROX_NONE 保存物体边界上所有连续的轮廓点到contours向量内
* 取值二:CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours向量内,拐点与拐点之间直线段上的信息点不予保留
* 取值三和四:CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法
* 偏移量:Point偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在每一个检测出的轮廓点上加上该偏移量,且Point可以是负值。不填为默认不偏移Point()
*/
Cv2.FindContours(cannyMat, out Point[][] contours, out hierarchy, RetrievalModes.List, ContourApproximationModes.ApproxSimple);
// 5、绘制轮廓
for (int i = 0; i < contours.Length; i++)
{
Cv2.DrawContours(resultMat, contours, i, Scalar.Green, 2, LineTypes.Link8, hierarchy); // 绘制轮廓(轮廓颜色为绿色,轮廓线粗细为2)
}
return contours;
}
5、检测轮廓 - 检测多边形轮廓并获取中心点坐标(固定阈值)
/// <summary>
/// 检测多边形轮廓并获取中心点坐标(固定阈值)
/// </summary>
/// <param name="mat">图片</param>
/// <param name="resultMat">灰度结果图片</param>
/// <param name="thresh">中间阈值</param>
/// <returns>多边形的中心点坐标集合</returns>
public static List<KeyPoint> ContoursDetector_CenterPoint(Mat mat, out Mat resultMat, double thresh)
{
List<KeyPoint> keypoints = new List<KeyPoint>();
Point[][] contours = ContoursDetector_Threshold(mat, out resultMat, thresh, out HierarchyIndex[] hies);
foreach (Point[] contour in contours)
{
RotatedRect rect = Cv2.MinAreaRect(contour);
Point2f centerPoint = rect.Center;
KeyPoint keyPoint = new KeyPoint();
keyPoint.Pt.X = centerPoint.X;
keyPoint.Pt.Y = centerPoint.Y;
keyPoint.Octave = 1;
keyPoint.Size = 2;
keypoints.Add(keyPoint);
}
// 在图像上绘制斑点
Cv2.DrawKeypoints(mat, keypoints, resultMat, Scalar.All(-1));
return keypoints;
}
本文来自博客园,作者:꧁执笔小白꧂,转载请注明原文链接:https://www.cnblogs.com/qq2806933146xiaobai/p/18295592