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;
}
posted @ 2024-07-11 10:52  ꧁执笔小白꧂  阅读(344)  评论(0编辑  收藏  举报