1. 程式人生 > >opencv-霍夫直線變換與圓變換

opencv-霍夫直線變換與圓變換

計算機視覺 讓我 ali nsf ofo 統一 range 即使 round

轉自:https://blog.csdn.net/poem_qianmo/article/details/26977557

一、引言

在圖像處理和計算機視覺領域中,如何從當前的圖像中提取所需要的特征信息是圖像識別的關鍵所在。在許多應用場合中需要快速準確地檢測出直線或者圓。其中一種非常有效的解決問題的方法是霍夫(Hough)變換,其為圖像處理中從圖像中識別幾何形狀的基本方法之一,應用很廣泛,也有很多改進算法。最基本的霍夫變換是從黑白圖像中檢測直線(線段)。這篇文章就將介紹OpenCV中霍夫變換的使用方法和相關知識。

二、霍夫變換概述

霍夫變換(Hough Transform)是圖像處理中的一種特征提取技術,該過程在一個參數空間中通過計算累計結果的局部最大值

得到一個符合該特定形狀的集合作為霍夫變換結果

霍夫變換於1962年由PaulHough首次提出,最初的Hough變換是設計用來檢測直線和曲線,起初的方法要求知道物體邊界線的解析方程,但不需要有關區域位置的先驗知識。這種方法的一個突出優點是分割結果的Robustness,即對數據的不完全或噪聲不是非常敏感。然而,要獲得描述邊界的解析表達常常是不可能的。 後於1972年由Richard Duda & Peter Hart推廣使用,經典霍夫變換用來檢測圖像中的直線,後來霍夫變換擴展到任意形狀物體的識別,多為圓和橢圓。霍夫變換運用兩個坐標空間之間的變換將在一個空間中具有相同形狀的曲線或直線映射到另一個坐標空間的一個點上形成峰值,從而把檢測任意形狀的問題轉化為統計峰值問題

霍夫變換在OpenCV中分為霍夫線變換和霍夫圓變換兩種,我們下面將分別進行介紹。

三、霍夫線變換

3.1 OpenCV中的霍夫線變換

我們知道,霍夫線變換是一種用來尋找直線的方法. 在使用霍夫線變換之前, 首先要對圖像進行邊緣檢測的處理,也即霍夫線變換的直接輸入只能是邊緣二值圖像.

OpenCV支持三種不同的霍夫線變換,它們分別是:標準霍夫變換(Standard Hough Transform,SHT)和多尺度霍夫變換(Multi-Scale Hough Transform,MSHT)累計概率霍夫變換(Progressive Probabilistic Hough Transform ,PPHT)。

其中,多尺度霍夫變換(MSHT)為經典霍夫變換(SHT)在多尺度下的一個變種。累計概率霍夫變換(PPHT)算法是標準霍夫變換(SHT)算法的一個改進,它在一定的範圍內進行霍夫變換,計算單獨線段的方向以及範圍,從而減少計算量,縮短計算時間。之所以稱PPHT為“概率”的,是因為並不將累加器平面內的所有可能的點累加,而只是累加其中的一部分,該想法是如果峰值如果足夠高,只用一小部分時間去尋找它就夠了。這樣猜想的話,可以實質性地減少計算時間。

在OpenCV中,我們可以用HoughLines函數來調用標準霍夫變換SHT和多尺度霍夫變換MSHT。

而HoughLinesP函數用於調用累計概率霍夫變換PPHT。累計概率霍夫變換執行效率很高,所有相比於HoughLines函數,我們更傾向於使用HoughLinesP函數。

總結一下,OpenCV中的霍夫線變換有如下三種:

<1>標準霍夫變換(StandardHough Transform,SHT),由HoughLines函數調用。

<2>多尺度霍夫變換(Multi-ScaleHough Transform,MSHT),由HoughLines函數調用。

<3>累計概率霍夫變換(ProgressiveProbabilistic Hough Transform,PPHT),由HoughLinesP函數調用。

3.2 霍夫線變換的原理

【1】眾所周知, 一條直線在圖像二維空間可由兩個變量表示. 如:

<1>在笛卡爾坐標系: 可由參數: 斜率和截距(m,b) 表示。

<2>在極坐標系: 可由參數: 極徑和極角(γ,θ)表示。

技術分享圖片

對於霍夫變換, 我們將采用第二種方式極坐標系來表示直線. 因此, 直線的表達式可為:

技術分享圖片

化簡便可得到:

技術分享圖片

【2】一般來說對於點(x0,y0), 我們可以將通過這個點的一族直線統一定義為:

技術分享圖片

這就意味著每一對(γθ,θ)代表一條通過點(x0,y0)的直線。

【3】如果對於一個給定點(x0,y0)我們在極坐標對極徑極角平面繪出所有通過它的直線, 將得到一條正弦曲線. 例如, 對於給定點X_0= 8 和Y_0= 6 我們可以繪出下圖 (在平面):

技術分享圖片

只繪出滿足下列條件的點γ>0和0<θ<2π .

【4】我們可以對圖像中所有的點進行上述操作. 如果兩個不同點進行上述操作後得到的曲線在平面θ-γ相交, 這就意味著它們通過同一條直線. 例如,接上面的例子我們繼續對點x1=9,y1=4 和點x2=12,y2=3繪圖, 得到下圖:

技術分享圖片

這三條曲線在平面相交於點 (0.925, 9.6), 坐標表示的是參數對 θ-γ或者是說點(x0,y0), 點(x1,y1)和點(x2,y2)組成的平面內的的直線。

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

【6】這就是霍夫線變換要做的. 它追蹤圖像中每個點對應曲線間的交點. 如果交於一點的曲線的數量超過了閾值, 那麽可以認為這個交點所代表的參數對(θ,γθ)在原圖像中為一條直線。

3.3 HoughLines( )函數詳解

此函數可以找出采用標準霍夫變換的二值圖像線條。在OpenCV中,我們可以用其來調用標準霍夫變換SHT和多尺度霍夫變換MSHT的OpenCV內建算法。

C++: void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )

  • 第一個參數,InputArray類型的image,輸入圖像,即源圖像,需為8位的單通道二進制圖像,可以將任意的源圖載入進來後由函數修改成此格式後,再填在這裏。
  • 第二個參數,InputArray類型的lines,經過調用HoughLines函數後儲存了霍夫線變換檢測到線條的輸出矢量。每一條線由具有兩個元素的矢量(ρ,θ)表示,其中,ρ是離坐標原點((0,0)(也就是圖像的左上角)的距離。 θ是弧度線條旋轉角度(0~垂直線,π/2~水平線)。
  • 第三個參數,double類型的rho,以像素為單位的距離精度。另一種形容方式是直線搜索時的進步尺寸的單位半徑。PS:Latex中/rho就表示 ρ。
  • 第四個參數,double類型的theta,以弧度為單位的角度精度。另一種形容方式是直線搜索時的進步尺寸的單位角度。
  • 第五個參數,int類型的threshold,累加平面的閾值參數,即識別某部分為圖中的一條直線時它在累加平面中必須達到的值。大於閾值threshold的線段才可以被檢測通過並返回到結果中。
  • 第六個參數,double類型的srn,有默認值0。對於多尺度的霍夫變換,這是第三個參數進步尺寸rho的除數距離。粗略的累加器進步尺寸直接是第三個參數rho,而精確的累加器進步尺寸為rho/srn。
  • 第七個參數,double類型的stn,有默認值0,對於多尺度霍夫變換,srn表示第四個參數進步尺寸的單位角度theta的除數距離。且如果srn和stn同時為0,就表示使用經典的霍夫變換。否則,這兩個參數應該都為正數。

另外,關於霍夫變換的詳細解釋,可以看此英文頁面:

http://homepages.inf.ed.ac.uk/rbf/HIPR2/hough.htm

在學完函數解析,看看淺墨為大家準備的以HoughLines為核心的示例程序,就可以全方位了解HoughLines函數的使用方法了:

//-----------------------------------【頭文件包含部分】---------------------------------------
 
//    描述:包含程序所依賴的頭文件
 
//----------------------------------------------------------------------------------------------
 
#include <opencv2/opencv.hpp>
 
#include <opencv2/imgproc/imgproc.hpp> 
 
//-----------------------------------【命名空間聲明部分】---------------------------------------
 
//    描述:包含程序所使用的命名空間
 
//-----------------------------------------------------------------------------------------------
 
using namespace cv;
 
//-----------------------------------【main( )函數】--------------------------------------------
 
//    描述:控制臺應用程序的入口函數,我們的程序從這裏開始
 
//-----------------------------------------------------------------------------------------------
 
int main( )
 
{
 
//【1】載入原始圖和Mat變量定義
 
Mat srcImage = imread("1.jpg"); //工程目錄下應該有一張名為1.jpg的素材圖
 
Mat midImage,dstImage;//臨時變量和目標圖的定義
 
 
 
//【2】進行邊緣檢測和轉化為灰度圖
 
Canny(srcImage, midImage, 50, 200, 3);//進行一此canny邊緣檢測
 
cvtColor(midImage,dstImage, CV_GRAY2BGR);//轉化邊緣檢測後的圖為灰度圖
 
 
 
//【3】進行霍夫線變換
 
vector<Vec2f> lines;//定義一個矢量結構lines用於存放得到的線段矢量集合
 
HoughLines(midImage, lines, 1, CV_PI/180, 150, 0, 0 );
 
 
 
//【4】依次在圖中繪制出每條線段
 
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( dstImage, pt1, pt2, Scalar(55,100,195), 1, CV_AA);
 
}
 
//【5】顯示原始圖
 
imshow("【原始圖】", srcImage);
 
 
//【6】邊緣檢測後的圖
 
imshow("【邊緣檢測後的圖】", midImage);
 
 
//【7】顯示效果圖
 
imshow("【效果圖】", dstImage);
 
 
waitKey(0);  
return 0; 
}

運行截圖:

技術分享圖片

來一張大圖:

技術分享圖片

PS:可以通過調節line(dstImage, pt1, pt2, Scalar(55,100,195), 1, CV_AA);一句Scalar(55,100,195)參數中G、B、R顏色值的數值,得到圖中想要的線條顏色。

3.4 HoughLinesP( )函數詳解

此函數在HoughLines的基礎上末尾加了一個代表Probabilistic(概率)的P,表明它可以采用累計概率霍夫變換(PPHT)來找出二值圖像中的直線。

C++: void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0 )
  • 第一個參數,InputArray類型的image,輸入圖像,即源圖像,需為8位的單通道二進制圖像,可以將任意的源圖載入進來後由函數修改成此格式後,再填在這裏。
  • 第二個參數,InputArray類型的lines,經過調用HoughLinesP函數後後存儲了檢測到的線條的輸出矢量,每一條線由具有四個元素的矢量(x_1,y_1, x_2, y_2) 表示,其中,(x_1, y_1)和(x_2, y_2) 是是每個檢測到的線段的結束點。
  • 第三個參數,double類型的rho,以像素為單位的距離精度。另一種形容方式是直線搜索時的進步尺寸的單位半徑。
  • 第四個參數,double類型的theta,以弧度為單位的角度精度。另一種形容方式是直線搜索時的進步尺寸的單位角度。
  • 第五個參數,int類型的threshold,累加平面的閾值參數,即識別某部分為圖中的一條直線時它在累加平面中必須達到的值。大於閾值threshold的線段才可以被檢測通過並返回到結果中。
  • 第六個參數,double類型的minLineLength,有默認值0,表示最低線段的長度,比這個設定參數短的線段就不能被顯現出來。
  • 第七個參數,double類型的maxLineGap,有默認值0,允許將同一行點與點之間連接起來的最大的距離。

對於此函數,依然是為大家準備了示例程序:

//-----------------------------------【頭文件包含部分】---------------------------------------
 
// 描述:包含程序所依賴的頭文件
 
//----------------------------------------------------------------------------------------------
 
#include <opencv2/opencv.hpp>
 
#include <opencv2/imgproc/imgproc.hpp> 
 
//-----------------------------------【命名空間聲明部分】---------------------------------------
 
// 描述:包含程序所使用的命名空間
 
//-----------------------------------------------------------------------------------------------
 
using namespace cv;
 
//-----------------------------------【main( )函數】--------------------------------------------
 
// 描述:控制臺應用程序的入口函數,我們的程序從這裏開始
 
//-----------------------------------------------------------------------------------------------
 
int main( )
 
{
 
//【1】載入原始圖和Mat變量定義
 
Mat srcImage = imread("1.jpg"); //工程目錄下應該有一張名為1.jpg的素材圖
 
Mat midImage,dstImage;//臨時變量和目標圖的定義
 
 
 
//【2】進行邊緣檢測和轉化為灰度圖
 
Canny(srcImage, midImage, 50, 200, 3);//進行一此canny邊緣檢測
 
cvtColor(midImage,dstImage, CV_GRAY2BGR);//轉化邊緣檢測後的圖為灰度圖
 
 
//【3】進行霍夫線變換
 
vector<Vec4i> lines;//定義一個矢量結構lines用於存放得到的線段矢量集合
 
HoughLinesP(midImage, lines, 1, CV_PI/180, 80, 50, 10 );
  
 
//【4】依次在圖中繪制出每條線段
 
for( size_t i = 0; i < lines.size(); i++ )
 
{
 
Vec4i l = lines[i];
 
line( dstImage, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(186,88,255), 1, CV_AA);
 
}
  
//【5】顯示原始圖
 
imshow("【原始圖】", srcImage);
 
//【6】邊緣檢測後的圖
 
imshow("【邊緣檢測後的圖】", midImage);
 
//【7】顯示效果圖
 
imshow("【效果圖】", dstImage);
 
 
waitKey(0);
return 0;
}

運行截圖:

技術分享圖片

來一張大圖:

技術分享圖片

四、霍夫圓變換

霍夫圓變換的基本原理和上面講的霍夫線變化大體上是很類似的,只是點對應的二維極徑極角空間被三維的圓心點x, y還有半徑r空間取代。說“大體上類似”的原因是,如果完全用相同的方法的話,累加平面會被三維的累加容器所代替:在這三維中,一維是x,一維是y,另外一維是圓的半徑r。這就意味著需要大量的內存而且執行效率會很低,速度會很慢。

對直線來說, 一條直線能由參數極徑極角(γ,θ)表示. 而對圓來說, 我們需要三個參數來表示一個圓, 也就是:

技術分享圖片

這裏的 表示圓心的位置 (下圖中的綠點) 而 r 表示半徑, 這樣我們就能唯一的定義一個圓了, 見下圖:

技術分享圖片 技術分享圖片

在OpenCV中,我們一般通過一個叫做“霍夫梯度法”的方法來解決圓變換的問題。

4.1 霍夫梯度法的原理

霍夫梯度法的原理是這樣的。

【1】首先對圖像應用邊緣檢測,比如用canny邊緣檢測。

【2】然後,對邊緣圖像中的每一個非零點,考慮其局部梯度,即用Sobel()函數計算x和y方向的Sobel一階導數得到梯度。

【3】利用得到的梯度,由斜率指定的直線上的每一個點都在累加器中被累加,這裏的斜率是從一個指定的最小值到指定的最大值的距離。

【4】同時,標記邊緣圖像中每一個非0像素的位置。

【5】然後從二維累加器中這些點中選擇候選的中心,這些中心都大於給定閾值並且大於其所有近鄰。這些候選的中心按照累加值降序排列,以便於最支持像素的中心首先出現。

【6】接下來對每一個中心,考慮所有的非0像素。

【7】這些像素按照其與中心的距離排序。從到最大半徑的最小距離算起,選擇非0像素最支持的一條半徑。8.如果一個中心收到邊緣圖像非0像素最充分的支持,並且到前期被選擇的中心有足夠的距離,那麽它就會被保留下來。

這個實現可以使算法執行起來更高效,或許更加重要的是,能夠幫助解決三維累加器中會產生許多噪聲並且使得結果不穩定的稀疏分布問題。

人無完人,金無足赤。同樣,這個算法也並不是十全十美的,還有許多需要指出的缺點。

4.2 霍夫梯度法的缺點

<1>在霍夫梯度法中,我們使用Sobel導數來計算局部梯度,那麽隨之而來的假設是,其可以視作等同於一條局部切線,並這個不是一個數值穩定的做法。在大多數情況下,這樣做會得到正確的結果,但或許會在輸出中產生一些噪聲。

<2>在邊緣圖像中的整個非0像素集被看做每個中心的候選部分。因此,如果把累加器的閾值設置偏低,算法將要消耗比較長的時間。第三,因為每一個中心只選擇一個圓,如果有同心圓,就只能選擇其中的一個。

<3>因為中心是按照其關聯的累加器值的升序排列的,並且如果新的中心過於接近之前已經接受的中心的話,就不會被保留下來。且當有許多同心圓或者是近似的同心圓時,霍夫梯度法的傾向是保留最大的一個圓。可以說這是一種比較極端的做法,因為在這裏默認Sobel導數會產生噪聲,若是對於無窮分辨率的平滑圖像而言的話,這才是必須的。

4.3 HoughCircles( )函數詳解

HoughCircles函數可以利用霍夫變換算法檢測出灰度圖中的圓。它和之前的HoughLines和HoughLinesP比較明顯的一個區別是它不需要源圖是二值的,而HoughLines和HoughLinesP都需要源圖為二值圖像。

C++: void HoughCircles(InputArray image,OutputArray circles, int method, double dp, double minDist, double param1=100,double param2=100, int minRadius=0, int maxRadius=0 )
  • 第一個參數,InputArray類型的image,輸入圖像,即源圖像,需為8位的灰度單通道圖像。
  • 第二個參數,InputArray類型的circles,經過調用HoughCircles函數後此參數存儲了檢測到的圓的輸出矢量,每個矢量由包含了3個元素的浮點矢量(x, y, radius)表示。
  • 第三個參數,int類型的method,即使用的檢測方法,目前OpenCV中就霍夫梯度法一種可以使用,它的標識符為CV_HOUGH_GRADIENT,在此參數處填這個標識符即可。
  • 第四個參數,double類型的dp,用來檢測圓心的累加器圖像的分辨率於輸入圖像之比的倒數,且此參數允許創建一個比輸入圖像分辨率低的累加器。上述文字不好理解的話,來看例子吧。例如,如果dp= 1時,累加器和輸入圖像具有相同的分辨率。如果dp=2,累加器便有輸入圖像一半那麽大的寬度和高度。
  • 第五個參數,double類型的minDist,為霍夫變換檢測到的圓的圓心之間的最小距離,即讓我們的算法能明顯區分的兩個不同圓之間的最小距離。這個參數如果太小的話,多個相鄰的圓可能被錯誤地檢測成了一個重合的圓。反之,這個參數設置太大的話,某些圓就不能被檢測出來了。
  • 第六個參數,double類型的param1,有默認值100。它是第三個參數method設置的檢測方法的對應的參數。對當前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示傳遞給canny邊緣檢測算子的高閾值,而低閾值為高閾值的一半。
  • 第七個參數,double類型的param2,也有默認值100。它是第三個參數method設置的檢測方法的對應的參數。對當前唯一的方法霍夫梯度法CV_HOUGH_GRADIENT,它表示在檢測階段圓心的累加器閾值。它越小的話,就可以檢測到更多根本不存在的圓,而它越大的話,能通過檢測的圓就更加接近完美的圓形了。
  • 第八個參數,int類型的minRadius,有默認值0,表示圓半徑的最小值。
  • 第九個參數,int類型的maxRadius,也有默認值0,表示圓半徑的最大值。

需要註意的是,使用此函數可以很容易地檢測出圓的圓心,但是它可能找不到合適的圓半徑。我們可以通過第八個參數minRadius和第九個參數maxRadius指定最小和最大的圓半徑,來輔助圓檢測的效果。或者,我們可以直接忽略返回半徑,因為它們都有著默認值0,單單用HoughCircles函數檢測出來的圓心,然後用額外的一些步驟來進一步確定半徑。

依然是為大家準備了基於此函數的示例程序:

//-----------------------------------【頭文件包含部分】---------------------------------------
 
// 描述:包含程序所依賴的頭文件
 
//----------------------------------------------------------------------------------------------
 
#include <opencv2/opencv.hpp>
 
#include <opencv2/imgproc/imgproc.hpp>
 
//-----------------------------------【命名空間聲明部分】---------------------------------------
 
// 描述:包含程序所使用的命名空間
 
//-----------------------------------------------------------------------------------------------
 
using namespace cv;
 
//-----------------------------------【main( )函數】--------------------------------------------
 
// 描述:控制臺應用程序的入口函數,我們的程序從這裏開始
 
//-----------------------------------------------------------------------------------------------
 
int main( )
 
{
 
//【1】載入原始圖和Mat變量定義
 
Mat srcImage = imread("1.jpg"); //工程目錄下應該有一張名為1.jpg的素材圖
 
Mat midImage,dstImage;//臨時變量和目標圖的定義
 
 
//【2】顯示原始圖
 
imshow("【原始圖】", srcImage);
 
 
//【3】轉為灰度圖,進行圖像平滑
 
cvtColor(srcImage,midImage, CV_BGR2GRAY);//轉化邊緣檢測後的圖為灰度圖
 
GaussianBlur( midImage, midImage, Size(9, 9), 2, 2 );
 
 
//【4】進行霍夫圓變換
 
vector<Vec3f> circles;
 
HoughCircles( midImage, circles, CV_HOUGH_GRADIENT,1.5, 10, 200, 100, 0, 0 );
 
 
//【5】依次在圖中繪制出圓
 
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( srcImage, center, 3, Scalar(0,255,0), -1, 8, 0 );
 
//繪制圓輪廓
 
circle( srcImage, center, radius, Scalar(155,50,255), 3, 8, 0 );
 
}
 
//【6】顯示效果圖
 
imshow("【效果圖】", srcImage);
 
waitKey(0); 
return 0;
}
運行截圖:

技術分享圖片

技術分享圖片

五、源碼部分

這個部分就是貼出OpenCV中本文相關函數的源碼實現細節,來給想了解實現細節的小夥伴們參考的,淺墨就暫時不在源碼的細節上挖深作詳細註釋了。

5.1 OpenCV2.X中HoughLines( )函數源碼

void cv::HoughLines( InputArray _image,OutputArray _lines,
 
double rho, double theta,int threshold,
 
double srn, double stn )
 
{
 
Ptr<CvMemStorage> storage = cvCreateMemStorage(STORAGE_SIZE);
 
Mat image = _image.getMat();
 
CvMat c_image = image;
 
CvSeq* seq = cvHoughLines2( &c_image, storage, srn == 0 &&stn == 0 ?
 
CV_HOUGH_STANDARD :CV_HOUGH_MULTI_SCALE,
 
rho, theta, threshold, srn,stn );
 
seqToMat(seq, _lines);
 
}

可以發現其內部實現是基於OpenCV 1.X舊版的cvHoughLines2函數,我們再來看看其舊版cvHoughLines2的函數源碼。

5.1.1 OpenCV2.X中cvHoughLines2()函數源碼

CV_IMPL CvSeq*
 
cvHoughLines2( CvArr* src_image, void*lineStorage, int method,
 
double rho, double theta, intthreshold,
 
double param1, double param2 )
 
{
 
CvSeq* result = 0;
 
 
 
CvMat stub, *img = (CvMat*)src_image;
 
CvMat* mat = 0;
 
CvSeq* lines = 0;
 
CvSeq lines_header;
 
CvSeqBlock lines_block;
 
int lineType, elemSize;
 
int linesMax = INT_MAX;
 
int iparam1, iparam2;
 
 
 
img = cvGetMat( img, &stub );
 
 
 
if( !CV_IS_MASK_ARR(img))
 
CV_Error( CV_StsBadArg, "The source image must be 8-bit,single-channel" );
 
 
 
if( !lineStorage )
 
CV_Error( CV_StsNullPtr, "NULL destination" );
 
 
 
if( rho <= 0 || theta <= 0 || threshold <= 0 )
 
CV_Error( CV_StsOutOfRange, "rho, theta and threshold must bepositive" );
 
 
 
if( method != CV_HOUGH_PROBABILISTIC )
 
{
 
lineType = CV_32FC2;
 
elemSize = sizeof(float)*2;
 
}
 
else
 
{
 
lineType = CV_32SC4;
 
elemSize = sizeof(int)*4;
 
}
 
 
 
if( CV_IS_STORAGE( lineStorage ))
 
{
 
lines = cvCreateSeq( lineType, sizeof(CvSeq), elemSize,(CvMemStorage*)lineStorage );
 
}
 
else if( CV_IS_MAT( lineStorage ))
 
{
 
mat = (CvMat*)lineStorage;
 
 
 
if( !CV_IS_MAT_CONT( mat->type ) || (mat->rows != 1 &&mat->cols != 1) )
 
CV_Error( CV_StsBadArg,
 
"The destination matrix should be continuous and have a single rowor a single column" );
 
 
 
if( CV_MAT_TYPE( mat->type ) != lineType )
 
CV_Error( CV_StsBadArg,
 
"The destination matrix data type is inappropriate, see themanual" );
 
 
 
lines = cvMakeSeqHeaderForArray( lineType, sizeof(CvSeq), elemSize,mat->data.ptr,
 
mat->rows + mat->cols - 1, &lines_header, &lines_block );
 
linesMax = lines->total;
 
cvClearSeq( lines );
 
}
 
else
 
CV_Error( CV_StsBadArg, "Destination is not CvMemStorage* norCvMat*" );
 
 
 
iparam1 = cvRound(param1);
 
iparam2 = cvRound(param2);
 
 
 
switch( method )
 
{
 
case CV_HOUGH_STANDARD:
 
icvHoughLinesStandard( img, (float)rho,
 
(float)theta, threshold,lines, linesMax );
 
break;
 
case CV_HOUGH_MULTI_SCALE:
 
icvHoughLinesSDiv( img, (float)rho, (float)theta,
 
threshold, iparam1, iparam2,lines, linesMax );
 
break;
 
case CV_HOUGH_PROBABILISTIC:
 
icvHoughLinesProbabilistic( img, (float)rho, (float)theta,
 
threshold, iparam1, iparam2,lines, linesMax );
 
break;
 
default:
 
CV_Error( CV_StsBadArg, "Unrecognized method id" );
 
}
 
 
 
if( mat )
 
{
 
if( mat->cols > mat->rows )
 
mat->cols = lines->total;
 
else
 
mat->rows = lines->total;
 
}
 
else
 
result = lines;
 
 
 
return result;
 
}

5.2 OpenCV2.X中HoughLinesP()函數源碼

void cv::HoughLinesP( InputArray _image,OutputArray _lines,
 
double rho, double theta,int threshold,
 
double minLineLength,double maxGap )
 
{
 
Ptr<CvMemStorage> storage = cvCreateMemStorage(STORAGE_SIZE);
 
Mat image = _image.getMat();
 
CvMat c_image = image;
 
CvSeq*seq = cvHoughLines2( &c_image, storage, CV_HOUGH_PROBABILISTIC,
 
rho, theta, threshold,minLineLength, maxGap );
 
seqToMat(seq, _lines);
 
}

可以發現其內部內部實現依然是基於舊版OpenCV 1.X的cvHoughLines2函數的,上面我們已經將cvHoughLines2()貼出來了,這裏就不再次貼出了。

5.3 OpenCV2.X中HoughCircles()函數源碼

void cv::HoughCircles( InputArray _image,OutputArray _circles,
 
int method, double dp,double min_dist,
 
double param1, doubleparam2,
 
int minRadius, int maxRadius )
 
{
 
Ptr<CvMemStorage> storage = cvCreateMemStorage(STORAGE_SIZE);
 
Mat image = _image.getMat();
 
CvMat c_image = image;
 
CvSeq* seq = cvHoughCircles( &c_image, storage, method,
 
dp, min_dist, param1,param2, minRadius, maxRadius );
 
seqToMat(seq, _circles);
 
}

可以發現其內部內部實現是基於舊版OpenCV 1.X的cvHoughCircles,我們再來看看其舊版cvHoughCircles( )的函數源碼。

5.3.1 OpenCV2.X中cvHoughCircles()函數源碼

CV_IMPL CvSeq*
 
cvHoughCircles( CvArr* src_image, void*circle_storage,
 
int method, double dp, doublemin_dist,
 
double param1, double param2,
 
int min_radius, int max_radius)
 
{
 
CvSeq* result = 0;
 
 
 
CvMat stub, *img = (CvMat*)src_image;
 
CvMat* mat = 0;
 
CvSeq* circles = 0;
 
CvSeq circles_header;
 
CvSeqBlock circles_block;
 
int circles_max = INT_MAX;
 
int canny_threshold = cvRound(param1);
 
int acc_threshold = cvRound(param2);
 
 
 
img = cvGetMat( img, &stub );
 
 
 
if( !CV_IS_MASK_ARR(img))
 
CV_Error( CV_StsBadArg, "The source image must be 8-bit,single-channel" );
 
 
 
if( !circle_storage )
 
CV_Error( CV_StsNullPtr, "NULL destination" );
 
 
 
if( dp <= 0 || min_dist <= 0 || canny_threshold <= 0 ||acc_threshold <= 0 )
 
CV_Error( CV_StsOutOfRange, "dp, min_dist, canny_threshold andacc_threshold must be all positive numbers" );
 
 
 
min_radius = MAX( min_radius, 0 );
 
if( max_radius <= 0 )
 
max_radius = MAX( img->rows, img->cols );
 
else if( max_radius <= min_radius )
 
max_radius = min_radius + 2;
 
 
 
if( CV_IS_STORAGE( circle_storage ))
 
{
 
circles = cvCreateSeq( CV_32FC3, sizeof(CvSeq),
 
sizeof(float)*3, (CvMemStorage*)circle_storage );
 
}
 
else if( CV_IS_MAT( circle_storage ))
 
{
 
mat = (CvMat*)circle_storage;
 
 
 
if( !CV_IS_MAT_CONT( mat->type ) || (mat->rows != 1 &&mat->cols != 1) ||
 
CV_MAT_TYPE(mat->type) != CV_32FC3 )
 
CV_Error( CV_StsBadArg,
 
"The destination matrix should be continuous and have a single rowor a single column" );
 
 
 
circles = cvMakeSeqHeaderForArray( CV_32FC3, sizeof(CvSeq),sizeof(float)*3,
 
mat->data.ptr, mat->rows +mat->cols - 1, &circles_header, &circles_block );
 
circles_max = circles->total;
 
cvClearSeq( circles );
 
}
 
else
 
CV_Error( CV_StsBadArg, "Destination is not CvMemStorage* norCvMat*" );
 
 
 
switch( method )
 
{
 
case CV_HOUGH_GRADIENT:
 
icvHoughCirclesGradient( img, (float)dp, (float)min_dist,
 
min_radius,max_radius, canny_threshold,
 
acc_threshold,circles, circles_max );
 
break;
 
default:
 
CV_Error( CV_StsBadArg, "Unrecognized method id" );
 
}
 
 
 
if( mat )
 
{
 
if( mat->cols > mat->rows )
 
mat->cols = circles->total;
 
else
 
mat->rows = circles->total;
 
}
 
else
 
result = circles;
 
 
return result;
 
}

opencv-霍夫直線變換與圓變換