• OpenCV学习笔记(八) 边缘、线与圆的检测


    边缘检测

    对图像进行边缘检测之前,一般都需要先进行降噪(可调用GaussianBlur函数)。

    Sobel算子 与 Scharr算子

    都是一个离散微分算子 (discrete differentiation operator),用来计算图像灰度函数的近似梯度。结合了高斯平滑和微分求导。Sobel算子与Scharr算子的内核不同,Sobel内核产生误差比较明显,Scharr更为准确一些。

    Sobel算子的计算步骤:

    1. 在两个方向求导:将原图分别与两个3x3的内核进行卷积计算,得到Gx与Gy
    2. 在图像的每一点,结合Gx与Gy求出近似 梯度 :

    G = sqrt{ G_{x}^{2} + G_{y}^{2} } 或者 G = |G_{x}| + |G_{y}| (简单公式)

    /// 求 X方向梯度
    //Scharr( src_gray, grad_x, ddepth, 1, 0, scale, delta, BORDER_DEFAULT );
    Sobel( src_gray, grad_x, ddepth, 1, 0, 3, scale, delta, BORDER_DEFAULT );
    convertScaleAbs( grad_x, abs_grad_x );
    
    /// 求Y方向梯度
    //Scharr( src_gray, grad_y, ddepth, 0, 1, scale, delta, BORDER_DEFAULT );
    Sobel( src_gray, grad_y, ddepth, 0, 1, 3, scale, delta, BORDER_DEFAULT );
    convertScaleAbs( grad_y, abs_grad_y );
    
    /// 合并梯度(近似)
    addWeighted( abs_grad_x, 0.5, abs_grad_y, 0.5, 0, grad );
    • src_gray: 在本例中为输入图像,元素类型 CV_8U
    • grad_x/grad_y: 输出图像.
    • ddepth: 输出图像的深度,设定为 CV_16S 避免外溢。
    • x_orderx 方向求导的阶数。
    • y_ordery 方向求导的阶数。

    Laplace算子

     计算的是二阶导数。由于 Laplacian使用了图像梯度,它内部调用了 Sobel 算子。Laplacian 算子 的定义:

    Laplace(f) = dfrac{partial^{2} f}{partial x^{2}} + dfrac{partial^{2} f}{partial y^{2}}

    Laplacian( src_gray, dst, ddepth, kernel_size, scale, delta, BORDER_DEFAULT );
    convertScaleAbs( dst, abs_dst );
    
    • src_gray: 输入图像。
    • dst: 输出图像
    • ddepth: 输出图像的深度。 因为输入图像的深度是 CV_8U ,这里我们必须定义 ddepth = CV_16S 以避免外溢。
    • kernel_size: 内部调用的 Sobel算子的内核大小,此例中设置为3。
    • scaledelta 和 BORDER_DEFAULT: 使用默认值。

    Canny边缘检测

    被很多人认为是边缘检测的 最优算法。在Sober算子步骤后添加以下步骤:

    • 非极大值 抑制。 这一步排除非边缘像素, 仅仅保留了一些细线条(候选边缘)。
    • 滞后阈值: 最后一步,Canny 使用了滞后阈值,滞后阈值需要两个阈值(高阈值和低阈值):
      1. 如果某一像素位置的幅值超过  阈值, 该像素被保留为边缘像素。
      2. 如果某一像素位置的幅值小于  阈值, 该像素被排除。
      3. 如果某一像素位置的幅值在两个阈值之间,该像素仅仅在连接到一个高于  阈值的像素时被保留。
    Canny( origin_gray_image, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size );
    

    具体使用方法见此处范例

    Hough变换

    Hough线变换

    执行Hough线变换之前需执行高斯模糊(降噪)+Canny边缘检测。Hough线变换以Canny边缘检测的输出(二值图)为输入。

    一条直线可由参数 (r,	heta) 极径和极角表示:r_{	heta} = x_{0} cdot cos 	heta  + y_{0} cdot sin 	heta

    当x0与y0定下来之后,rθ随着θ变化而变化,可在平面 	heta - r 中画出相应曲线(是一条正弦曲线),一对(x0, y0)确定一条曲线。(x1,y1)与(x2, y2)相交于(r0, 	heta0)表示以这两个点作直线可由(r0, 	heta0)表示:

    y = left ( -dfrac{cos 	heta}{sin 	heta} 
ight ) x + left ( dfrac{r}{sin 	heta} 
ight )

    一条直线能够通过在平面 	heta - r 寻找交于一点的曲线数量来 检测. 越多曲线交于一点也就意味着这个交点表示的直线由更多的点组成. 一般来说我们可以通过设置直线上点的 阈值 来定义多少条曲线交于一点我们才认为 检测 到了一条直线.

    OpenCV实现了以下两种霍夫线变换:

    • 标准Hough线变换 HoughLines :提供一组参数对 (	heta, r_{	heta}) 的集合来表示检测到的直线
    • 统计概率Hough线变换 HoughLinesP :效率更高的Hough变换,输出检测到的直线的端点 (x_{0}, y_{0}, x_{1}, y_{1})
    // 标准Hough线变换
    vector<Vec2f> lines;
    HoughLines(dst, lines, 1, CV_PI/180, 100, 0, 0 );
    // 画出检测的直线
    for( size_t i = 0; i < lines.size(); i++ )
    {
      float rho = lines[i][0], theta = lines[i][1];
      Point pt1, pt2;
      double a = cos(theta), b = sin(theta);
      double x0 = a*rho, y0 = b*rho;
      pt1.x = cvRound(x0 + 1000*(-b));
      pt1.y = cvRound(y0 + 1000*(a));
      pt2.x = cvRound(x0 - 1000*(-b));
      pt2.y = cvRound(y0 - 1000*(a));
      line( cdst, pt1, pt2, Scalar(0,0,255), 3, CV_AA);
    }
    
    // 统计Hough线变换
    vector<Vec4i> lines;
    HoughLinesP(dst, lines, 1, CV_PI/180, 50, 50, 10 );
    • dst: 边缘检测的输出图像. 它应该是个灰度图 (但事实上是个二值化图)
    • lines: 储存着检测到的直线的参数对 (r,	heta) 的容器
    • rho : 参数极径 r 以像素值为单位的分辨率. 我们使用 1 像素.
    • theta: 参数极角 	heta 以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180)
    • threshold: 要”检测” 一条直线所需最少的的曲线交点
    • 标准Hough变换:
    • srn and stn: 参数默认为0. 查缺OpenCV参考文献来获取更多信息.
    • 统计Hough变换:
    • minLinLength: 能组成一条直线的最少点的数量. 点数量不足的直线将被抛弃.
    • maxLineGap: 能被认为在一条直线上的亮点的最大距离.

    Hough圆变换

    原理与线变换相似,在三维的“圆心点x, y还有半径r”空间中找交点。

    由于在三维空间的计算量大大增加的原因, 标准霍夫圆变化很难被应用到实际中,OpenCV实现的是一个比标准霍夫圆变换更为灵活的检测方法: 霍夫梯度法, 也叫2-1霍夫变换(21HT)。它的原理依据是圆心一定是在圆上的每个点的模向量上, 这些圆上点模向量的交点就是圆心, 霍夫梯度法的第一步就是找到这些圆心, 这样三维的累加平面就又转化为二维累加平面. 第二部根据所有候选中心的边缘非0像素对其的支持程度来确定半径.

    /// Convert it to gray
    cvtColor( src, src_gray, CV_BGR2GRAY );
    
    /// Reduce the noise so we avoid false circle detection
    GaussianBlur( src_gray, src_gray, Size(9, 9), 2, 2 );
    
    vector<Vec3f> circles;
    
    /// Apply the Hough Transform to find the circles
    HoughCircles( src_gray, circles, CV_HOUGH_GRADIENT, 1, src_gray.rows/8, 200, 100, 0, 0 );
    
    /// Draw the circles detected
    for( size_t i = 0; i < circles.size(); i++ )
    {
      Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));
      int radius = cvRound(circles[i][2]);
      // circle center
      circle( src, center, 3, Scalar(0,255,0), -1, 8, 0 );
      // circle outline
      circle( src, center, radius, Scalar(0,0,255), 3, 8, 0 );
    }
    
    • src_gray: 输入图像 (灰度图)
    • circles: 存储下面三个参数: x_{c}, y_{c}, r 集合的容器来表示每个检测到的圆.
    • CV_HOUGH_GRADIENT: 指定检测方法. 现在OpenCV中只有霍夫梯度法
    • dp = 1: 累加器图像的反比分辨率
    • min_dist = src_gray.rows/8: 检测到圆心之间的最小距离
    • param_1 = 200: Canny边缘函数的高阈值
    • param_2 = 100: 圆心检测阈值.
    • min_radius = 0: 能检测到的最小圆半径, 默认为0.
    • max_radius = 0: 能检测到的最大圆半径, 默认为0
  • 相关阅读:
    objective-c数组
    objective-c可变数组
    objective-c可变字典
    objective-c字典
    有1、2、3、4个数字,能组成多少个互不相同且无重复数字的三位数?都是多少?
    将一个正整数分解质因数。例如:输入90,打印出90=2*3*3*5
    一球从100米高度自由落下,每次落地后反跳回原高度的一半;再落下,求它在第10次落地时,共经过多少米?
    求123456789-23456789-3456789-456789-...-9的值
    编写一个程序,求s=1+(1+2)+(1+2+3)+…+(1+2+3+…+n)的值
    Unity3D笔记 GUI 二 、实现选项卡一窗口
  • 原文地址:https://www.cnblogs.com/ericxing/p/3578478.html
Copyright © 2020-2023  润新知