• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            【OpenCV入門教程之十四】OpenCV霍夫變換:霍夫線變換,霍夫圓變換合輯

            http://blog.csdn.net/poem_qianmo/article/details/26977557

            本系列文章由@淺墨_毛星云 出品,轉載請注明出處。  

            文章鏈接: http://blog.csdn.net/poem_qianmo/article/details/26977557

            作者:毛星云(淺墨)    微博:http://weibo.com/u/1723155442

            知乎:http://www.zhihu.com/people/mao-xing-yun

            郵箱: happylifemxy@163.com

            寫作當前博文時配套使用的OpenCV版本: 2.4.9

             本篇文章中,我們一起探討了OpenCV中霍夫變換相關的知識點,以及了解了opencv中實現霍夫線變換的HoughLines、HoughLinesP函數的使用方法,實現霍夫圓變換的HoughCircles函數的使用方法。此博文一共有四個配套的簡短的示例程序,其詳細注釋過的代碼都在文中貼出,且文章最后提供了綜合示例程序的下載。

            先嘗鮮一下其中一個示例程序的運行截圖:





            一、引言

             

            在圖像處理和計算機視覺領域中,如何從當前的圖像中提取所需要的特征信息是圖像識別的關鍵所在。在許多應用場合中需要快速準確地檢測出直線或者圓。其中一種非常有效的解決問題的方法是霍夫(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】一般來說對于點, 我們可以將通過這個點的一族直線統一定義為:


             


            這就意味著每一對代表一條通過點的直線。

             

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


             

             

             

             

             

            只繪出滿足下列條件的點  和   .


            【4】我們可以對圖像中所有的點進行上述操作. 如果兩個不同點進行上述操作后得到的曲線在平面相交, 這就意味著它

            們通過同一條直線. 例如,接上面的例子我們繼續對點  和點  繪圖, 得到下圖:


             

             

            這三條曲線在平面相交于點 (0.925, 9.6), 坐標表示的是參數對  或者是說點, 點和點組成的平面內的的直線。

             

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

             

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

             


             

             


            3.3 HoughLines( )函數詳解


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

            1. 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函數的使用方法了:


            1. //-----------------------------------【頭文件包含部分】---------------------------------------  
            2. //      描述:包含程序所依賴的頭文件  
            3. //----------------------------------------------------------------------------------------------   
            4. #include <opencv2/opencv.hpp>  
            5. #include <opencv2/imgproc/imgproc.hpp>  
            6.   
            7. //-----------------------------------【命名空間聲明部分】---------------------------------------  
            8. //      描述:包含程序所使用的命名空間  
            9. //-----------------------------------------------------------------------------------------------   
            10. using namespace cv;  
            11. //-----------------------------------【main( )函數】--------------------------------------------  
            12. //      描述:控制臺應用程序的入口函數,我們的程序從這里開始  
            13. //-----------------------------------------------------------------------------------------------  
            14. int main( )  
            15. {  
            16.     //【1】載入原始圖和Mat變量定義     
            17.     Mat srcImage = imread("1.jpg");  //工程目錄下應該有一張名為1.jpg的素材圖  
            18.     Mat midImage,dstImage;//臨時變量和目標圖的定義  
            19.   
            20.     //【2】進行邊緣檢測和轉化為灰度圖  
            21.     Canny(srcImage, midImage, 50, 200, 3);//進行一此canny邊緣檢測  
            22.     cvtColor(midImage,dstImage, CV_GRAY2BGR);//轉化邊緣檢測后的圖為灰度圖  
            23.   
            24.     //【3】進行霍夫線變換  
            25.     vector<Vec2f> lines;//定義一個矢量結構lines用于存放得到的線段矢量集合  
            26.     HoughLines(midImage, lines, 1, CV_PI/180, 150, 0, 0 );  
            27.   
            28.     //【4】依次在圖中繪制出每條線段  
            29.     for( size_t i = 0; i < lines.size(); i++ )  
            30.     {  
            31.         float rho = lines[i][0], theta = lines[i][1];  
            32.         Point pt1, pt2;  
            33.         double a = cos(theta), b = sin(theta);  
            34.         double x0 = a*rho, y0 = b*rho;  
            35.         pt1.x = cvRound(x0 + 1000*(-b));  
            36.         pt1.y = cvRound(y0 + 1000*(a));  
            37.         pt2.x = cvRound(x0 - 1000*(-b));  
            38.         pt2.y = cvRound(y0 - 1000*(a));  
            39.         line( dstImage, pt1, pt2, Scalar(55,100,195), 1, CV_AA);  
            40.     }  
            41.   
            42.     //【5】顯示原始圖    
            43.     imshow("【原始圖】", srcImage);    
            44.   
            45.     //【6】邊緣檢測后的圖   
            46.     imshow("【邊緣檢測后的圖】", midImage);    
            47.   
            48.     //【7】顯示效果圖    
            49.     imshow("【效果圖】", dstImage);    
            50.   
            51.     waitKey(0);    
            52.   
            53.     return 0;    
            54. }  



            運行截圖:

             


            來一張大圖:



            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)來找出二值圖像中的直線。

            1. 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,允許將同一行點與點之間連接起來的最大的距離。


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


            1. //-----------------------------------【頭文件包含部分】---------------------------------------  
            2. //      描述:包含程序所依賴的頭文件  
            3. //----------------------------------------------------------------------------------------------   
            4. #include <opencv2/opencv.hpp>  
            5. #include <opencv2/imgproc/imgproc.hpp>  
            6.   
            7. //-----------------------------------【命名空間聲明部分】---------------------------------------  
            8. //      描述:包含程序所使用的命名空間  
            9. //-----------------------------------------------------------------------------------------------   
            10. using namespace cv;  
            11. //-----------------------------------【main( )函數】--------------------------------------------  
            12. //      描述:控制臺應用程序的入口函數,我們的程序從這里開始  
            13. //-----------------------------------------------------------------------------------------------  
            14. int main( )  
            15. {  
            16.     //【1】載入原始圖和Mat變量定義     
            17.     Mat srcImage = imread("1.jpg");  //工程目錄下應該有一張名為1.jpg的素材圖  
            18.     Mat midImage,dstImage;//臨時變量和目標圖的定義  
            19.   
            20.     //【2】進行邊緣檢測和轉化為灰度圖  
            21.     Canny(srcImage, midImage, 50, 200, 3);//進行一此canny邊緣檢測  
            22.     cvtColor(midImage,dstImage, CV_GRAY2BGR);//轉化邊緣檢測后的圖為灰度圖  
            23.   
            24.     //【3】進行霍夫線變換  
            25.     vector<Vec4i> lines;//定義一個矢量結構lines用于存放得到的線段矢量集合  
            26.     HoughLinesP(midImage, lines, 1, CV_PI/180, 80, 50, 10 );  
            27.   
            28.     //【4】依次在圖中繪制出每條線段  
            29.     for( size_t i = 0; i < lines.size(); i++ )  
            30.     {  
            31.         Vec4i l = lines[i];  
            32.         line( dstImage, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(186,88,255), 1, CV_AA);  
            33.     }  
            34.   
            35.     //【5】顯示原始圖    
            36.     imshow("【原始圖】", srcImage);    
            37.   
            38.     //【6】邊緣檢測后的圖   
            39.     imshow("【邊緣檢測后的圖】", midImage);    
            40.   
            41.     //【7】顯示效果圖    
            42.     imshow("【效果圖】", dstImage);    
            43.   
            44.     waitKey(0);    
            45.   
            46.     return 0;    
            47. }  


            運行截圖:

             


            來一張大圖:




             

            四、霍夫圓變換

             


            霍夫圓變換的基本原理和上面講的霍夫線變化大體上是很類似的,只是點對應的二維極徑極角空間被三維的圓心點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都需要源圖為二值圖像。

            1. 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函數檢測出來的圓心,然后用額外的一些步驟來進一步確定半徑。

             

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

            1. //-----------------------------------【頭文件包含部分】---------------------------------------  
            2. //      描述:包含程序所依賴的頭文件  
            3. //----------------------------------------------------------------------------------------------   
            4. #include <opencv2/opencv.hpp>  
            5. #include <opencv2/imgproc/imgproc.hpp>  
            6.   
            7. //-----------------------------------【命名空間聲明部分】---------------------------------------  
            8. //      描述:包含程序所使用的命名空間  
            9. //-----------------------------------------------------------------------------------------------   
            10. using namespace cv;  
            11. //-----------------------------------【main( )函數】--------------------------------------------  
            12. //      描述:控制臺應用程序的入口函數,我們的程序從這里開始  
            13. //-----------------------------------------------------------------------------------------------  
            14. int main( )  
            15. {  
            16.     //【1】載入原始圖和Mat變量定義     
            17.     Mat srcImage = imread("1.jpg");  //工程目錄下應該有一張名為1.jpg的素材圖  
            18.     Mat midImage,dstImage;//臨時變量和目標圖的定義  
            19.   
            20.     //【2】顯示原始圖  
            21.     imshow("【原始圖】", srcImage);    
            22.   
            23.     //【3】轉為灰度圖,進行圖像平滑  
            24.     cvtColor(srcImage,midImage, CV_BGR2GRAY);//轉化邊緣檢測后的圖為灰度圖  
            25.     GaussianBlur( midImage, midImage, Size(9, 9), 2, 2 );  
            26.   
            27.     //【4】進行霍夫圓變換  
            28.     vector<Vec3f> circles;  
            29.     HoughCircles( midImage, circles, CV_HOUGH_GRADIENT,1.5, 10, 200, 100, 0, 0 );  
            30.   
            31.     //【5】依次在圖中繪制出圓  
            32.     for( size_t i = 0; i < circles.size(); i++ )  
            33.     {  
            34.         Point center(cvRound(circles[i][0]), cvRound(circles[i][1]));  
            35.         int radius = cvRound(circles[i][2]);  
            36.         //繪制圓心  
            37.         circle( srcImage, center, 3, Scalar(0,255,0), -1, 8, 0 );  
            38.         //繪制圓輪廓  
            39.         circle( srcImage, center, radius, Scalar(155,50,255), 3, 8, 0 );  
            40.     }  
            41.   
            42.     //【6】顯示效果圖    
            43.     imshow("【效果圖】", srcImage);    
            44.   
            45.     waitKey(0);    
            46.   
            47.     return 0;    
            48. }  


            運行截圖:




             

             

             

            五、源碼部分


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

             


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


            1. void cv::HoughLines( InputArray _image,OutputArray _lines,  
            2.                      double rho, double theta,int threshold,  
            3.                      double srn, double stn )  
            4. {  
            5.    Ptr<CvMemStorage> storage = cvCreateMemStorage(STORAGE_SIZE);  
            6.    Mat image = _image.getMat();  
            7.    CvMat c_image = image;  
            8.    CvSeq* seq = cvHoughLines2( &c_image, storage, srn == 0 &&stn == 0 ?  
            9.                     CV_HOUGH_STANDARD :CV_HOUGH_MULTI_SCALE,  
            10.                     rho, theta, threshold, srn,stn );  
            11.    seqToMat(seq, _lines);  
            12. }  

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

             


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


            1. CV_IMPL CvSeq*  
            2. cvHoughLines2( CvArr* src_image, void*lineStorage, int method,  
            3.                double rho, double theta, intthreshold,  
            4.                double param1, double param2 )  
            5. {  
            6.    CvSeq* result = 0;  
            7.    
            8.    CvMat stub, *img = (CvMat*)src_image;  
            9.    CvMat* mat = 0;  
            10.    CvSeq* lines = 0;  
            11.    CvSeq lines_header;  
            12.    CvSeqBlock lines_block;  
            13.    int lineType, elemSize;  
            14.    int linesMax = INT_MAX;  
            15.    int iparam1, iparam2;  
            16.    
            17.    img = cvGetMat( img, &stub );  
            18.    
            19.    if( !CV_IS_MASK_ARR(img))  
            20.        CV_Error( CV_StsBadArg, "The source image must be 8-bit,single-channel" );  
            21.    
            22.    if( !lineStorage )  
            23.        CV_Error( CV_StsNullPtr, "NULL destination" );  
            24.    
            25.    if( rho <= 0 || theta <= 0 || threshold <= 0 )  
            26.        CV_Error( CV_StsOutOfRange, "rho, theta and threshold must bepositive" );  
            27.    
            28.    if( method != CV_HOUGH_PROBABILISTIC )  
            29.     {  
            30.        lineType = CV_32FC2;  
            31.        elemSize = sizeof(float)*2;  
            32.     }  
            33.    else  
            34.     {  
            35.        lineType = CV_32SC4;  
            36.        elemSize = sizeof(int)*4;  
            37.     }  
            38.    
            39.    if( CV_IS_STORAGE( lineStorage ))  
            40.     {  
            41.        lines = cvCreateSeq( lineType, sizeof(CvSeq), elemSize,(CvMemStorage*)lineStorage );  
            42.     }  
            43.    else if( CV_IS_MAT( lineStorage ))  
            44.     {  
            45.        mat = (CvMat*)lineStorage;  
            46.    
            47.        if( !CV_IS_MAT_CONT( mat->type ) || (mat->rows != 1 &&mat->cols != 1) )  
            48.            CV_Error( CV_StsBadArg,  
            49.            "The destination matrix should be continuous and have a single rowor a single column" );  
            50.    
            51.        if( CV_MAT_TYPE( mat->type ) != lineType )  
            52.            CV_Error( CV_StsBadArg,  
            53.            "The destination matrix data type is inappropriate, see themanual" );  
            54.    
            55.        lines = cvMakeSeqHeaderForArray( lineType, sizeof(CvSeq), elemSize,mat->data.ptr,  
            56.                                         mat->rows + mat->cols - 1, &lines_header, &lines_block );  
            57.        linesMax = lines->total;  
            58.        cvClearSeq( lines );  
            59.     }  
            60.    else  
            61.        CV_Error( CV_StsBadArg, "Destination is not CvMemStorage* norCvMat*" );  
            62.    
            63.    iparam1 = cvRound(param1);  
            64.    iparam2 = cvRound(param2);  
            65.    
            66.    switch( method )  
            67.     {  
            68.    case CV_HOUGH_STANDARD:  
            69.          icvHoughLinesStandard( img, (float)rho,  
            70.                (float)theta, threshold,lines, linesMax );  
            71.          break;  
            72.    case CV_HOUGH_MULTI_SCALE:  
            73.          icvHoughLinesSDiv( img, (float)rho, (float)theta,  
            74.                 threshold, iparam1, iparam2,lines, linesMax );  
            75.          break;  
            76.    case CV_HOUGH_PROBABILISTIC:  
            77.          icvHoughLinesProbabilistic( img, (float)rho, (float)theta,  
            78.                 threshold, iparam1, iparam2,lines, linesMax );  
            79.          break;  
            80.    default:  
            81.        CV_Error( CV_StsBadArg, "Unrecognized method id" );  
            82.     }  
            83.    
            84.    if( mat )  
            85.     {  
            86.        if( mat->cols > mat->rows )  
            87.            mat->cols = lines->total;  
            88.        else  
            89.            mat->rows = lines->total;  
            90.     }  
            91.    else  
            92.        result = lines;  
            93.    
            94.    return result;  
            95. }  


             

             

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



            1. void cv::HoughLinesP( InputArray _image,OutputArray _lines,  
            2.                       double rho, double theta,int threshold,  
            3.                       double minLineLength,double maxGap )  
            4. {  
            5.    Ptr<CvMemStorage> storage = cvCreateMemStorage(STORAGE_SIZE);  
            6.    Mat image = _image.getMat();  
            7.    CvMat c_image = image;  
            8.     CvSeq*seq = cvHoughLines2( &c_image, storage, CV_HOUGH_PROBABILISTIC,  
            9.                     rho, theta, threshold,minLineLength, maxGap );  
            10.    seqToMat(seq, _lines);  
            11. }  


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




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


            1. void cv::HoughCircles( InputArray _image,OutputArray _circles,  
            2.                        int method, double dp,double min_dist,  
            3.                        double param1, doubleparam2,  
            4.                        int minRadius, int maxRadius )  
            5. {  
            6.    Ptr<CvMemStorage> storage = cvCreateMemStorage(STORAGE_SIZE);  
            7.    Mat image = _image.getMat();  
            8.    CvMat c_image = image;  
            9.    CvSeq* seq = cvHoughCircles( &c_image, storage, method,  
            10.                     dp, min_dist, param1,param2, minRadius, maxRadius );  
            11.    seqToMat(seq, _circles);  
            12. }  

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

             



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


            1. CV_IMPL CvSeq*  
            2. cvHoughCircles( CvArr* src_image, void*circle_storage,  
            3.                 int method, double dp, doublemin_dist,  
            4.                 double param1, double param2,  
            5.                 int min_radius, int max_radius)  
            6. {  
            7.    CvSeq* result = 0;  
            8.    
            9.    CvMat stub, *img = (CvMat*)src_image;  
            10.    CvMat* mat = 0;  
            11.    CvSeq* circles = 0;  
            12.    CvSeq circles_header;  
            13.    CvSeqBlock circles_block;  
            14.    int circles_max = INT_MAX;  
            15.    int canny_threshold = cvRound(param1);  
            16.    int acc_threshold = cvRound(param2);  
            17.    
            18.    img = cvGetMat( img, &stub );  
            19.    
            20.    if( !CV_IS_MASK_ARR(img))  
            21.        CV_Error( CV_StsBadArg, "The source image must be 8-bit,single-channel" );  
            22.    
            23.    if( !circle_storage )  
            24.        CV_Error( CV_StsNullPtr, "NULL destination" );  
            25.    
            26.    if( dp <= 0 || min_dist <= 0 || canny_threshold <= 0 ||acc_threshold <= 0 )  
            27.        CV_Error( CV_StsOutOfRange, "dp, min_dist, canny_threshold andacc_threshold must be all positive numbers" );  
            28.    
            29.    min_radius = MAX( min_radius, 0 );  
            30.    if( max_radius <= 0 )  
            31.        max_radius = MAX( img->rows, img->cols );  
            32.    else if( max_radius <= min_radius )  
            33.        max_radius = min_radius + 2;  
            34.    
            35.    if( CV_IS_STORAGE( circle_storage ))  
            36.     {  
            37.        circles = cvCreateSeq( CV_32FC3, sizeof(CvSeq),  
            38.            sizeof(float)*3, (CvMemStorage*)circle_storage );  
            39.     }  
            40.    else if( CV_IS_MAT( circle_storage ))  
            41.     {  
            42.        mat = (CvMat*)circle_storage;  
            43.    
            44.        if( !CV_IS_MAT_CONT( mat->type ) || (mat->rows != 1 &&mat->cols != 1) ||  
            45.            CV_MAT_TYPE(mat->type) != CV_32FC3 )  
            46.            CV_Error( CV_StsBadArg,  
            47.            "The destination matrix should be continuous and have a single rowor a single column" );  
            48.    
            49.        circles = cvMakeSeqHeaderForArray( CV_32FC3, sizeof(CvSeq),sizeof(float)*3,  
            50.                 mat->data.ptr, mat->rows +mat->cols - 1, &circles_header, &circles_block );  
            51.        circles_max = circles->total;  
            52.        cvClearSeq( circles );  
            53.     }  
            54.    else  
            55.        CV_Error( CV_StsBadArg, "Destination is not CvMemStorage* norCvMat*" );  
            56.    
            57.    switch( method )  
            58.     {  
            59.    case CV_HOUGH_GRADIENT:  
            60.        icvHoughCirclesGradient( img, (float)dp, (float)min_dist,  
            61.                                 min_radius,max_radius, canny_threshold,  
            62.                                 acc_threshold,circles, circles_max );  
            63.          break;  
            64.    default:  
            65.        CV_Error( CV_StsBadArg, "Unrecognized method id" );  
            66.     }  
            67.    
            68.    if( mat )  
            69.     {  
            70.        if( mat->cols > mat->rows )  
            71.            mat->cols = circles->total;  
            72.        else  
            73.            mat->rows = circles->total;  
            74.     }  
            75.    else  
            76.        result = circles;  
            77.    
            78.    return result;  
            79. }  





            五、綜合示例部分

             



            這次的綜合示例,淺墨在HoughLinesP函數的基礎上,為其添加了用于控制其第五個參數閾值threshold的滾動條。于是便能通過調節滾動條,改變閾值,動態地控制霍夫線變換檢測的線條多少。

            廢話不多說,直接上詳細注釋的代碼:

            1. //-----------------------------------【程序說明】----------------------------------------------  
            2. //      程序名稱::《【OpenCV入門教程之十四】OpenCV霍夫變換:霍夫線變換,霍夫圓變換合輯 》 博文配套源碼   
            3. //      開發所用IDE版本:Visual Studio 2010  
            4. //          開發所用OpenCV版本:   2.4.9  
            5. //      2014年5月26日 Created by 淺墨  
            6. //----------------------------------------------------------------------------------------------  
            7.   
            8. //-----------------------------------【頭文件包含部分】---------------------------------------  
            9. //      描述:包含程序所依賴的頭文件  
            10. //----------------------------------------------------------------------------------------------   
            11. #include <opencv2/opencv.hpp>  
            12. #include <opencv2/highgui/highgui.hpp>  
            13. #include <opencv2/imgproc/imgproc.hpp>  
            14.   
            15. //-----------------------------------【命名空間聲明部分】--------------------------------------  
            16. //      描述:包含程序所使用的命名空間  
            17. //-----------------------------------------------------------------------------------------------   
            18. using namespace std;  
            19. using namespace cv;  
            20.   
            21.   
            22. //-----------------------------------【全局變量聲明部分】--------------------------------------  
            23. //      描述:全局變量聲明  
            24. //-----------------------------------------------------------------------------------------------  
            25. Mat g_srcImage, g_dstImage,g_midImage;//原始圖、中間圖和效果圖  
            26. vector<Vec4i> g_lines;//定義一個矢量結構g_lines用于存放得到的線段矢量集合  
            27. //變量接收的TrackBar位置參數  
            28. int g_nthreshold=100;  
            29.   
            30. //-----------------------------------【全局函數聲明部分】--------------------------------------  
            31. //      描述:全局函數聲明  
            32. //-----------------------------------------------------------------------------------------------  
            33.   
            34. static void on_HoughLines(int, void*);//回調函數  
            35. static void ShowHelpText();  
            36.   
            37.   
            38. //-----------------------------------【main( )函數】--------------------------------------------  
            39. //      描述:控制臺應用程序的入口函數,我們的程序從這里開始  
            40. //-----------------------------------------------------------------------------------------------  
            41. int main( )  
            42. {  
            43.     //改變console字體顏色  
            44.     system("color 3F");    
            45.   
            46.     ShowHelpText();  
            47.   
            48.     //載入原始圖和Mat變量定義     
            49.     Mat g_srcImage = imread("1.jpg");  //工程目錄下應該有一張名為1.jpg的素材圖  
            50.   
            51.     //顯示原始圖    
            52.     imshow("【原始圖】", g_srcImage);    
            53.   
            54.     //創建滾動條  
            55.     namedWindow("【效果圖】",1);  
            56.     createTrackbar("值", "【效果圖】",&g_nthreshold,200,on_HoughLines);  
            57.   
            58.     //進行邊緣檢測和轉化為灰度圖  
            59.     Canny(g_srcImage, g_midImage, 50, 200, 3);//進行一次canny邊緣檢測  
            60.     cvtColor(g_midImage,g_dstImage, CV_GRAY2BGR);//轉化邊緣檢測后的圖為灰度圖  
            61.   
            62.     //調用一次回調函數,調用一次HoughLinesP函數  
            63.     on_HoughLines(g_nthreshold,0);  
            64.     HoughLinesP(g_midImage, g_lines, 1, CV_PI/180, 80, 50, 10 );  
            65.   
            66.     //顯示效果圖    
            67.     imshow("【效果圖】", g_dstImage);    
            68.   
            69.   
            70.     waitKey(0);    
            71.   
            72.     return 0;    
            73.   
            74. }  
            75.   
            76.   
            77. //-----------------------------------【on_HoughLines( )函數】--------------------------------  
            78. //      描述:【頂帽運算/黑帽運算】窗口的回調函數  
            79. //----------------------------------------------------------------------------------------------  
            80. static void on_HoughLines(int, void*)  
            81. {  
            82.     //定義局部變量儲存全局變量  
            83.      Mat dstImage=g_dstImage.clone();  
            84.      Mat midImage=g_midImage.clone();  
            85.   
            86.      //調用HoughLinesP函數  
            87.      vector<Vec4i> mylines;  
            88.     HoughLinesP(midImage, mylines, 1, CV_PI/180, g_nthreshold+1, 50, 10 );  
            89.   
            90.     //循環遍歷繪制每一條線段  
            91.     for( size_t i = 0; i < mylines.size(); i++ )  
            92.     {  
            93.         Vec4i l = mylines[i];  
            94.         line( dstImage, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(23,180,55), 1, CV_AA);  
            95.     }  
            96.     //顯示圖像  
            97.     imshow("【效果圖】",dstImage);  
            98. }  
            99.   
            100. //-----------------------------------【ShowHelpText( )函數】----------------------------------  
            101. //      描述:輸出一些幫助信息  
            102. //----------------------------------------------------------------------------------------------  
            103. static void ShowHelpText()  
            104. {  
            105.     //輸出一些幫助信息  
            106.     printf("\n\n\n\t請調整滾動條觀察圖像效果~\n\n");  
            107.     printf("\n\n\t\t\t\t\t\t\t\t by淺墨"  
            108.         );  
            109. }  




            放一些運行截圖吧。

            原始圖:


            閾值為95時:



             閾值為35時:


             

            閾值為200時:



            本篇文章的配套源代碼請點擊這里下載:

             

            【淺墨OpenCV入門教程之十四】配套源代碼下載

             

             

            OK

            posted on 2017-09-07 14:14 zmj 閱讀(553) 評論(0)  編輯 收藏 引用

            久久综合丁香激情久久| 久久久久久国产精品美女| 性做久久久久久久久| 亚洲精品美女久久777777| 国产精品久久久久影院嫩草| 久久青青草原国产精品免费| 大蕉久久伊人中文字幕| 亚洲а∨天堂久久精品| 久久久久久国产a免费观看黄色大片| 伊人久久精品无码av一区| 国产伊人久久| 成人久久综合网| 欧美日韩精品久久久免费观看| 久久婷婷色综合一区二区| 国产日韩久久免费影院| 热久久最新网站获取| 久久国产精品成人免费| 久久99精品久久久久子伦| 国产激情久久久久久熟女老人| 久久ww精品w免费人成| 婷婷五月深深久久精品| 国产—久久香蕉国产线看观看 | 日本高清无卡码一区二区久久| 久久久久久国产精品无码下载| 99久久精品免费看国产免费| 99久久国产热无码精品免费| 久久只有这里有精品4| 久久久久一级精品亚洲国产成人综合AV区| 波多野结衣中文字幕久久| 久久精品国产清自在天天线| 精品久久久久一区二区三区| 久久精品国产免费| 成人妇女免费播放久久久| 蜜臀久久99精品久久久久久小说| 欧美精品久久久久久久自慰| 久久无码AV一区二区三区| 99久久精品免费看国产一区二区三区 | 91久久精品国产91性色也| 久久精品国产一区| 久久中文娱乐网| 国产成人精品久久亚洲高清不卡 |