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,\theta) 極徑和極角表示:r_{\theta} = x_{0} \cdot \cos \theta  + y_{0} \cdot \sin \theta

當x0與y0定下來之后,rθ隨着θ變化而變化,可在平面 \theta - r 中畫出相應曲線(是一條正弦曲線),一對(x0, y0)確定一條曲線。(x1,y1)與(x2, y2)相交於(r0, \theta0)表示以這兩個點作直線可由(r0, \theta0)表示:

y = \left ( -\dfrac{\cos \theta}{\sin \theta} \right ) x + \left ( \dfrac{r}{\sin \theta} \right )

 

一條直線能夠通過在平面 \theta - r 尋找交於一點的曲線數量來 檢測. 越多曲線交於一點也就意味着這個交點表示的直線由更多的點組成. 一般來說我們可以通過設置直線上點的 閾值 來定義多少條曲線交於一點我們才認為 檢測 到了一條直線.

OpenCV實現了以下兩種霍夫線變換:

 

  • 標准Hough線變換 HoughLines :提供一組參數對 (\theta, r_{\theta}) 的集合來表示檢測到的直線
  • 統計概率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,\theta) 的容器
  • rho : 參數極徑 r 以像素值為單位的分辨率. 我們使用 1 像素.
  • theta: 參數極角 \theta 以弧度為單位的分辨率. 我們使用 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM