OpenCV找圆系列(2)HoughCircles算子新增了HOUGH_GRADIENT_ALT方法,效果好多了

自OpenCV 4.3.0版本,imgproc模块的HoughCircles()函数,新增加了检测算法HOUGH_GRADIENT_ALT,精度得到了很大的提升。

1、举例opencv v4.5.5版本源码,

https://github.com/opencv/opencv/blob/4.5.5/modules/imgproc/src/hough.cpp

static void HoughCircles( InputArray _image, OutputArray _circles,
                          int method, double dp, double minDist,
                          double param1, double param2,
                          int minRadius, int maxRadius,
                          int maxCircles, double param3 )
{
    CV_INSTRUMENT_REGION();

    int type = CV_32FC3;
    if( _circles.fixedType() )
    {
        type = _circles.type();
        CV_CheckType(type, type == CV_32FC3 || type == CV_32FC4, "Wrong type of output circles");
    }

    CV_Assert(!_image.empty() && _image.type() == CV_8UC1 && (_image.isMat() || _image.isUMat()));

    if( dp <= 0 || minDist <= 0 || param1 <= 0)
        CV_Error( Error::StsOutOfRange, "dp, min_dist and canny_threshold must be all positive numbers" );

    switch( method )
    {
    case HOUGH_GRADIENT:
        {
            ......
        }
        break;
    case HOUGH_GRADIENT_ALT:
        {
            std::vector<EstimatedCircle> circles;
            Mat image = _image.getMat();
            HoughCirclesAlt(image, circles, dp, minDist, minRadius, maxRadius, param1, param2);
            std::sort(circles.begin(), circles.end(), cmpAccum);
            size_t i, ncircles = circles.size();

            if( type == CV_32FC4 )
            {
                std::vector<Vec4f> cw(ncircles);
                for( i = 0; i < ncircles; i++ )
                    cw[i] = GetCircle4f(circles[i]);
                if (ncircles > 0)
                    Mat(1, (int)ncircles, cv::traits::Type<Vec4f>::value, &cw[0]).copyTo(_circles);
            }
            else if( type == CV_32FC3 )
            {
                std::vector<Vec3f> cwow(ncircles);
                for( i = 0; i < ncircles; i++ )
                    cwow[i] = GetCircle(circles[i]);
                if (ncircles > 0)
                    Mat(1, (int)ncircles, cv::traits::Type<Vec3f>::value, &cwow[0]).copyTo(_circles);
            }
            else
                CV_Error(Error::StsError, "Internal error");
        }
        break;
    default:
        CV_Error( Error::StsBadArg, "Unrecognized method id. Actually only CV_HOUGH_GRADIENT is supported." );
    }
}

static void HoughCirclesAlt( const Mat& img, std::vector<EstimatedCircle>& circles, double dp, double rdMinDist,
                             double minRadius, double maxRadius, double cannyThreshold, double minCos2 )
{
    const int MIN_COUNT = 10;
    const int RAY_FP_BITS = 10;
    const int RAY_FP_SCALE = 1 << RAY_FP_BITS;
    const int ACCUM_FP_BITS = 6;
    const int RAY_SHIFT2 = ACCUM_FP_BITS/2;
    const int ACCUM_ALPHA_ONE = 1 << RAY_SHIFT2;
    const int ACCUM_ALPHA_MASK = ACCUM_ALPHA_ONE - 1;
    const int RAY_SHIFT1 = RAY_FP_BITS - RAY_SHIFT2;
    const int RAY_DELTA1 = 1 << (RAY_SHIFT1 - 1);

    const double ARC_DELTA = 80;
    const double ARC_EPS = 0.03;
    const double CIRCLE_AREA_OFFSET = 4000;
    const double ARC2CLUSTER_EPS = 0.06;
    const double CLUSTER_MERGE_EPS = 0.075;
    const double FINAL_MERGE_DIST_EPS = 0.01;
    const double FINAL_MERGE_AREA_EPS = CLUSTER_MERGE_EPS;

    if( maxRadius <= 0 )
        maxRadius = std::min(img.cols, img.rows)*0.5;
    if( minRadius > maxRadius )
        std::swap(minRadius, maxRadius);
    maxRadius = std::min(maxRadius, std::min(img.cols, img.rows)*0.5);
    maxRadius = std::max(maxRadius, 1.);
    minRadius = std::max(minRadius, 1.);
    minRadius = std::min(minRadius, maxRadius);
    cannyThreshold = std::max(cannyThreshold, 1.);
    dp = std::max(dp, 1.);

    Mat Dx, Dy, edges;
    Scharr(img, Dx, CV_16S, 1, 0);
    Scharr(img, Dy, CV_16S, 0, 1);
    Canny(Dx, Dy, edges, cannyThreshold/2, cannyThreshold, true);
    Mat mask(img.rows + 2, img.cols + 2, CV_8U, Scalar::all(0));
    double idp = 1./dp;
    ......
}

2、算子说明

HoughCircles(
        InputArray image, //输入图像,必须是8位的单通道灰度图像
        OutputArray circles, //输出结果,找到的圆信息
        Int method, //方法是HOUGH_GRADIENT或HOUGH_GRADIENT_ALT
        Double dp, //dp = 1或1.5
        Double mindist, //最短距离-可以分辨是两个圆的,否则认为是同心圆
        Double param1, //canny edge detection low threshold,Canny边缘检测的较大阈值
        Double param2, //方法不同,含义不同

        Int minradius, //最小半径
        Int maxradius //最大半径

     image:8 位、单通道、灰度输入图像
     circles:找到圆的输出向量。每个向量编码为 3 或 4 个元素的浮点向量 $(x,y,radius)(x,y,radius,votes)$
     method:检测方法。目前实现的方法是HOUGH_GRADIENT或HOUGH_GRADIENT_ALT
     dp:累加器分辨率与图像分辨率的反比。例如,如果 dp=1,则累加器具有与输入图像相同的分辨率。如果 dp=2,则累加器的宽度和高度都是一半
     对于HOUGH_GRADIENT_ALT,推荐值为 dp=1.5,除非需要检测一些非常小的圆。
     minDist:检测到圆的中心之间的最小距离。如果参数太小,除了一个真实的圆之外,可能错误地检测到多个相邻的圆圈。如果太大,可能会遗漏一些圆。
     param1:第一个特定方法参数。在HOUGH_GRADIENT和HOUGH_GRADIENT_ALT 的情况下,它是传递给 canny 边缘检测算子的高阀值,而低阀值为高阀值的一半
     注意,HOUGH_GRADIENT_ALT使用Scharr算法计算图像导数,因此阈值通常更高,例如 300 或正常曝光和对比度的图像。
     param2:第二个特定方法参数。在HOUGH_GRADIENT的情况下,它是检测阶段圆心的累加阀值。它越小,可以检测到更多的假圆。它越大,能通过检测的圆就更加接近完美的圆形。
     在HOUGH_GRADIENT_ALT算法的情况下,这是圆形“完美”度量。它越接近 1,形状圆算法选择的越好。在大多数情况下,0.9 应该没问题。
     如果您想更好地检测小圆圈,您可以将其降低到 0.85、0.8 甚至更低。
     minRadius:最小半径
     maxRadius:最大圆半径。如果≤0,则使用最大图像尺寸;如果<0,则返回中心而不查找半径

3、应用

由于HoughCircles函数内是调用Canny函数进行边缘检测,opencv的Canny函数是不包括平滑滤波这一步的,因此为了增强抗干扰能力,在使用HoughCircles函数之前,我们先对原图进行滤波处理,我们使用的是高斯模糊方法。

void hough()
{
    cv::Mat src, dst;
    src = cv::imread("d:\\circle.jpg");

    //中值滤波,因为霍夫圆检测对噪声比较敏感,所以首先要对图像做中值滤波
    cv::Mat mOut;
    cv::medianBlur(src, mOut, 3);

    cv::Mat grayImage;
    cv::cvtColor(mOut, grayImage, cv::COLOR_BGR2GRAY);

    //对灰度矩阵进行图像平滑处理(高斯模糊)
    cv::GaussianBlur(grayImage, grayImage, cv::Size(9, 9), 2, 2);

    //找霍夫圆
    std::vector<cv::Vec3f> cir;
    //cv::HoughCircles(grayImage, cir, cv::HOUGH_GRADIENT, 1, 15, 100, 30, 1, 100);
    cv::HoughCircles(grayImage, cir, cv::HOUGH_GRADIENT_ALT, 1.5, 15, 300, 0.8, 1, 100);
    src.copyTo(dst);

    for (size_t i = 0; i < cir.size(); i++)
    {
        cv::Vec3f cc = cir[i];
        cv::circle(dst, cv::Point(cc[0], cc[1]), cc[2], cv::Scalar(0, 0, 255), 2, cv::LINE_AA); //圆周
        cv::circle(dst, cv::Point(cc[0], cc[1]), 2, cv::Scalar(255, 0, 0), 2, cv::LINE_AA);     //圆心
    }

    cv::namedWindow("hough", cv::WINDOW_NORMAL);
    cv::imshow("hough", dst);
    cv::waitKey(0);
}

猜你喜欢

转载自blog.csdn.net/libaineu2004/article/details/122807673