• <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

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

            文章鏈接: 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

            寫作當(dāng)前博文時(shí)配套使用的OpenCV版本: 2.4.9

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

            先嘗鮮一下其中一個(gè)示例程序的運(yùn)行截圖:





            一、引言

             

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

             

             


            二、霍夫變換概述


            霍夫變換(Hough Transform)是圖像處理中的一種特征提取技術(shù),該過程在一個(gè)參數(shù)空間中通過計(jì)算累計(jì)結(jié)果的局部最大值得到一個(gè)符合該特定形狀的集合作為霍夫變換結(jié)果。

            霍夫變換于1962年由PaulHough首次提出,最初的Hough變換是設(shè)計(jì)用來檢測直線和曲線,起初的方法要求知道物體邊界線的解析方程,但不需要有關(guān)區(qū)域位置的先驗(yàn)知識(shí)。這種方法的一個(gè)突出優(yōu)點(diǎn)是分割結(jié)果的Robustness,即對(duì)數(shù)據(jù)的不完全或噪聲不是非常敏感。然而,要獲得描述邊界的解析表達(dá)常常是不可能的。 后于1972年由Richard Duda & Peter Hart推廣使用,經(jīng)典霍夫變換用來檢測圖像中的直線,后來霍夫變換擴(kuò)展到任意形狀物體的識(shí)別,多為圓和橢圓。霍夫變換運(yùn)用兩個(gè)坐標(biāo)空間之間的變換將在一個(gè)空間中具有相同形狀的曲線或直線映射到另一個(gè)坐標(biāo)空間的一個(gè)點(diǎn)上形成峰值,從而把檢測任意形狀的問題轉(zhuǎn)化為統(tǒng)計(jì)峰值問題。

             

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

             


             


            三、霍夫線變換

             


            3.1  OpenCV中的霍夫線變換

             


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

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

             

            其中,多尺度霍夫變換(MSHT)為經(jīng)典霍夫變換(SHT)在多尺度下的一個(gè)變種。累計(jì)概率霍夫變換(PPHT)算法是標(biāo)準(zhǔn)霍夫變換(SHT)算法的一個(gè)改進(jìn),它在一定的范圍內(nèi)進(jìn)行霍夫變換,計(jì)算單獨(dú)線段的方向以及范圍,從而減少計(jì)算量,縮短計(jì)算時(shí)間。之所以稱PPHT為“概率”的,是因?yàn)椴⒉粚⒗奂悠髌矫鎯?nèi)的所有可能的點(diǎn)累加,而只是累加其中的一部分,該想法是如果峰值如果足夠高,只用一小部分時(shí)間去尋找它就夠了。這樣猜想的話,可以實(shí)質(zhì)性地減少計(jì)算時(shí)間。

             

            在OpenCV中,我們可以用HoughLines函數(shù)來調(diào)用標(biāo)準(zhǔn)霍夫變換SHT和多尺度霍夫變換MSHT。

            而HoughLinesP函數(shù)用于調(diào)用累計(jì)概率霍夫變換PPHT。累計(jì)概率霍夫變換執(zhí)行效率很高,所有相比于HoughLines函數(shù),我們更傾向于使用HoughLinesP函數(shù)。

             

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


            <1>標(biāo)準(zhǔn)霍夫變換(StandardHough Transform,SHT),由HoughLines函數(shù)調(diào)用。

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

            <3>累計(jì)概率霍夫變換(ProgressiveProbabilistic Hough Transform,PPHT),由HoughLinesP函數(shù)調(diào)用。

             

             

             

            3.2 霍夫線變換的原理



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

             

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

            <2>在極坐標(biāo)系: 可由參數(shù): 極徑和極角表示。



             

            對(duì)于霍夫變換, 我們將采用第二種方式極坐標(biāo)系來表示直線. 因此, 直線的表達(dá)式可為:


             

            化簡便可得到:


             

            【2】一般來說對(duì)于點(diǎn), 我們可以將通過這個(gè)點(diǎn)的一族直線統(tǒng)一定義為:


             


            這就意味著每一對(duì)代表一條通過點(diǎn)的直線。

             

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


             

             

             

             

             

            只繪出滿足下列條件的點(diǎn)  和   .


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

            們通過同一條直線. 例如,接上面的例子我們繼續(xù)對(duì)點(diǎn)  和點(diǎn)  繪圖, 得到下圖:


             

             

            這三條曲線在平面相交于點(diǎn) (0.925, 9.6), 坐標(biāo)表示的是參數(shù)對(duì)  或者是說點(diǎn), 點(diǎn)和點(diǎn)組成的平面內(nèi)的的直線。

             

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

             

            【6】這就是霍夫線變換要做的. 它追蹤圖像中每個(gè)點(diǎn)對(duì)應(yīng)曲線間的交點(diǎn). 如果交于一點(diǎn)的曲線的數(shù)量超過了閾值, 那么可以認(rèn)為這個(gè)交點(diǎn)所代表的參數(shù)對(duì)在原圖像中為一條直線。

             


             

             


            3.3 HoughLines( )函數(shù)詳解


            此函數(shù)可以找出采用標(biāo)準(zhǔn)霍夫變換的二值圖像線條。在OpenCV中,我們可以用其來調(diào)用標(biāo)準(zhǔn)霍夫變換SHT和多尺度霍夫變換MSHT的OpenCV內(nèi)建算法。

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


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

             

            另外,關(guān)于霍夫變換的詳細(xì)解釋,可以看此英文頁面:

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

              

            在學(xué)完函數(shù)解析,看看淺墨為大家準(zhǔn)備的以HoughLines為核心的示例程序,就可以全方位了解HoughLines函數(shù)的使用方法了:


            1. //-----------------------------------【頭文件包含部分】---------------------------------------  
            2. //      描述:包含程序所依賴的頭文件  
            3. //----------------------------------------------------------------------------------------------   
            4. #include <opencv2/opencv.hpp>  
            5. #include <opencv2/imgproc/imgproc.hpp>  
            6.   
            7. //-----------------------------------【命名空間聲明部分】---------------------------------------  
            8. //      描述:包含程序所使用的命名空間  
            9. //-----------------------------------------------------------------------------------------------   
            10. using namespace cv;  
            11. //-----------------------------------【main( )函數(shù)】--------------------------------------------  
            12. //      描述:控制臺(tái)應(yīng)用程序的入口函數(shù),我們的程序從這里開始  
            13. //-----------------------------------------------------------------------------------------------  
            14. int main( )  
            15. {  
            16.     //【1】載入原始圖和Mat變量定義     
            17.     Mat srcImage = imread("1.jpg");  //工程目錄下應(yīng)該有一張名為1.jpg的素材圖  
            18.     Mat midImage,dstImage;//臨時(shí)變量和目標(biāo)圖的定義  
            19.   
            20.     //【2】進(jìn)行邊緣檢測和轉(zhuǎn)化為灰度圖  
            21.     Canny(srcImage, midImage, 50, 200, 3);//進(jìn)行一此canny邊緣檢測  
            22.     cvtColor(midImage,dstImage, CV_GRAY2BGR);//轉(zhuǎn)化邊緣檢測后的圖為灰度圖  
            23.   
            24.     //【3】進(jìn)行霍夫線變換  
            25.     vector<Vec2f> lines;//定義一個(gè)矢量結(jié)構(gòu)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. }  



            運(yùn)行截圖:

             


            來一張大圖:



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




            3.4 HoughLinesP( )函數(shù)詳解

             

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

            1. C++: void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0 )  


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


            對(duì)于此函數(shù),依然是為大家準(zhǔn)備了示例程序:


            1. //-----------------------------------【頭文件包含部分】---------------------------------------  
            2. //      描述:包含程序所依賴的頭文件  
            3. //----------------------------------------------------------------------------------------------   
            4. #include <opencv2/opencv.hpp>  
            5. #include <opencv2/imgproc/imgproc.hpp>  
            6.   
            7. //-----------------------------------【命名空間聲明部分】---------------------------------------  
            8. //      描述:包含程序所使用的命名空間  
            9. //-----------------------------------------------------------------------------------------------   
            10. using namespace cv;  
            11. //-----------------------------------【main( )函數(shù)】--------------------------------------------  
            12. //      描述:控制臺(tái)應(yīng)用程序的入口函數(shù),我們的程序從這里開始  
            13. //-----------------------------------------------------------------------------------------------  
            14. int main( )  
            15. {  
            16.     //【1】載入原始圖和Mat變量定義     
            17.     Mat srcImage = imread("1.jpg");  //工程目錄下應(yīng)該有一張名為1.jpg的素材圖  
            18.     Mat midImage,dstImage;//臨時(shí)變量和目標(biāo)圖的定義  
            19.   
            20.     //【2】進(jìn)行邊緣檢測和轉(zhuǎn)化為灰度圖  
            21.     Canny(srcImage, midImage, 50, 200, 3);//進(jìn)行一此canny邊緣檢測  
            22.     cvtColor(midImage,dstImage, CV_GRAY2BGR);//轉(zhuǎn)化邊緣檢測后的圖為灰度圖  
            23.   
            24.     //【3】進(jìn)行霍夫線變換  
            25.     vector<Vec4i> lines;//定義一個(gè)矢量結(jié)構(gòu)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. }  


            運(yùn)行截圖:

             


            來一張大圖:




             

            四、霍夫圓變換

             


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

             

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


             

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

              

             

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

             

             

            4.1 霍夫梯度法的原理


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


            【1】首先對(duì)圖像應(yīng)用邊緣檢測,比如用canny邊緣檢測。

            【2】然后,對(duì)邊緣圖像中的每一個(gè)非零點(diǎn),考慮其局部梯度,即用Sobel()函數(shù)計(jì)算x和y方向的Sobel一階導(dǎo)數(shù)得到梯度。

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

            【4】同時(shí),標(biāo)記邊緣圖像中每一個(gè)非0像素的位置。

            【5】然后從二維累加器中這些點(diǎn)中選擇候選的中心,這些中心都大于給定閾值并且大于其所有近鄰。這些候選的中心按照累加值降序排列,以便于最支持像素的中心首先出現(xiàn)。

            【6】接下來對(duì)每一個(gè)中心,考慮所有的非0像素。

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

             

            這個(gè)實(shí)現(xiàn)可以使算法執(zhí)行起來更高效,或許更加重要的是,能夠幫助解決三維累加器中會(huì)產(chǎn)生許多噪聲并且使得結(jié)果不穩(wěn)定的稀疏分布問題。

            人無完人,金無足赤。同樣,這個(gè)算法也并不是十全十美的,還有許多需要指出的缺點(diǎn)。

             


            4.2 霍夫梯度法的缺點(diǎn)


            <1>在霍夫梯度法中,我們使用Sobel導(dǎo)數(shù)來計(jì)算局部梯度,那么隨之而來的假設(shè)是,其可以視作等同于一條局部切線,并這個(gè)不是一個(gè)數(shù)值穩(wěn)定的做法。在大多數(shù)情況下,這樣做會(huì)得到正確的結(jié)果,但或許會(huì)在輸出中產(chǎn)生一些噪聲。

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

            <3>因?yàn)橹行氖前凑掌潢P(guān)聯(lián)的累加器值的升序排列的,并且如果新的中心過于接近之前已經(jīng)接受的中心的話,就不會(huì)被保留下來。且當(dāng)有許多同心圓或者是近似的同心圓時(shí),霍夫梯度法的傾向是保留最大的一個(gè)圓。可以說這是一種比較極端的做法,因?yàn)樵谶@里默認(rèn)Sobel導(dǎo)數(shù)會(huì)產(chǎn)生噪聲,若是對(duì)于無窮分辨率的平滑圖像而言的話,這才是必須的。

             

             

             

            4.3 HoughCircles( )函數(shù)詳解

             

            HoughCircles函數(shù)可以利用霍夫變換算法檢測出灰度圖中的圓。它和之前的HoughLines和HoughLinesP比較明顯的一個(gè)區(qū)別是它不需要源圖是二值的,而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 )  

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

             

            需要注意的是,使用此函數(shù)可以很容易地檢測出圓的圓心,但是它可能找不到合適的圓半徑。我們可以通過第八個(gè)參數(shù)minRadius和第九個(gè)參數(shù)maxRadius指定最小和最大的圓半徑,來輔助圓檢測的效果。或者,我們可以直接忽略返回半徑,因?yàn)樗鼈兌加兄J(rèn)值0,單單用HoughCircles函數(shù)檢測出來的圓心,然后用額外的一些步驟來進(jìn)一步確定半徑。

             

             依然是為大家準(zhǔn)備了基于此函數(shù)的示例程序:

            1. //-----------------------------------【頭文件包含部分】---------------------------------------  
            2. //      描述:包含程序所依賴的頭文件  
            3. //----------------------------------------------------------------------------------------------   
            4. #include <opencv2/opencv.hpp>  
            5. #include <opencv2/imgproc/imgproc.hpp>  
            6.   
            7. //-----------------------------------【命名空間聲明部分】---------------------------------------  
            8. //      描述:包含程序所使用的命名空間  
            9. //-----------------------------------------------------------------------------------------------   
            10. using namespace cv;  
            11. //-----------------------------------【main( )函數(shù)】--------------------------------------------  
            12. //      描述:控制臺(tái)應(yīng)用程序的入口函數(shù),我們的程序從這里開始  
            13. //-----------------------------------------------------------------------------------------------  
            14. int main( )  
            15. {  
            16.     //【1】載入原始圖和Mat變量定義     
            17.     Mat srcImage = imread("1.jpg");  //工程目錄下應(yīng)該有一張名為1.jpg的素材圖  
            18.     Mat midImage,dstImage;//臨時(shí)變量和目標(biāo)圖的定義  
            19.   
            20.     //【2】顯示原始圖  
            21.     imshow("【原始圖】", srcImage);    
            22.   
            23.     //【3】轉(zhuǎn)為灰度圖,進(jìn)行圖像平滑  
            24.     cvtColor(srcImage,midImage, CV_BGR2GRAY);//轉(zhuǎn)化邊緣檢測后的圖為灰度圖  
            25.     GaussianBlur( midImage, midImage, Size(9, 9), 2, 2 );  
            26.   
            27.     //【4】進(jìn)行霍夫圓變換  
            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. }  


            運(yùn)行截圖:




             

             

             

            五、源碼部分


            這個(gè)部分就是貼出OpenCV中本文相關(guān)函數(shù)的源碼實(shí)現(xiàn)細(xì)節(jié),來給想了解實(shí)現(xiàn)細(xì)節(jié)的小伙伴們參考的,淺墨就暫時(shí)不在源碼的細(xì)節(jié)上挖深作詳細(xì)注釋了。

             


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


            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. }  

            可以發(fā)現(xiàn)其內(nèi)部實(shí)現(xiàn)是基于OpenCV 1.X舊版的cvHoughLines2函數(shù),我們再來看看其舊版cvHoughLines2的函數(shù)源碼。

             


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


            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()函數(shù)源碼



            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. }  


            可以發(fā)現(xiàn)其內(nèi)部內(nèi)部實(shí)現(xiàn)依然是基于舊版OpenCV 1.X的cvHoughLines2函數(shù)的,上面我們已經(jīng)將cvHoughLines2()貼出來了,這里就不再次貼出了。




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


            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. }  

            可以發(fā)現(xiàn)其內(nèi)部內(nèi)部實(shí)現(xiàn)是基于舊版OpenCV 1.X的cvHoughCircles,我們再來看看其舊版cvHoughCircles( )的函數(shù)源碼。

             



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


            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函數(shù)的基礎(chǔ)上,為其添加了用于控制其第五個(gè)參數(shù)閾值threshold的滾動(dòng)條。于是便能通過調(diào)節(jié)滾動(dòng)條,改變閾值,動(dòng)態(tài)地控制霍夫線變換檢測的線條多少。

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

            1. //-----------------------------------【程序說明】----------------------------------------------  
            2. //      程序名稱::《【OpenCV入門教程之十四】OpenCV霍夫變換:霍夫線變換,霍夫圓變換合輯 》 博文配套源碼   
            3. //      開發(fā)所用IDE版本:Visual Studio 2010  
            4. //          開發(fā)所用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è)矢量結(jié)構(gòu)g_lines用于存放得到的線段矢量集合  
            27. //變量接收的TrackBar位置參數(shù)  
            28. int g_nthreshold=100;  
            29.   
            30. //-----------------------------------【全局函數(shù)聲明部分】--------------------------------------  
            31. //      描述:全局函數(shù)聲明  
            32. //-----------------------------------------------------------------------------------------------  
            33.   
            34. static void on_HoughLines(int, void*);//回調(diào)函數(shù)  
            35. static void ShowHelpText();  
            36.   
            37.   
            38. //-----------------------------------【main( )函數(shù)】--------------------------------------------  
            39. //      描述:控制臺(tái)應(yīng)用程序的入口函數(shù),我們的程序從這里開始  
            40. //-----------------------------------------------------------------------------------------------  
            41. int main( )  
            42. {  
            43.     //改變console字體顏色  
            44.     system("color 3F");    
            45.   
            46.     ShowHelpText();  
            47.   
            48.     //載入原始圖和Mat變量定義     
            49.     Mat g_srcImage = imread("1.jpg");  //工程目錄下應(yīng)該有一張名為1.jpg的素材圖  
            50.   
            51.     //顯示原始圖    
            52.     imshow("【原始圖】", g_srcImage);    
            53.   
            54.     //創(chuàng)建滾動(dòng)條  
            55.     namedWindow("【效果圖】",1);  
            56.     createTrackbar("值", "【效果圖】",&g_nthreshold,200,on_HoughLines);  
            57.   
            58.     //進(jìn)行邊緣檢測和轉(zhuǎn)化為灰度圖  
            59.     Canny(g_srcImage, g_midImage, 50, 200, 3);//進(jìn)行一次canny邊緣檢測  
            60.     cvtColor(g_midImage,g_dstImage, CV_GRAY2BGR);//轉(zhuǎn)化邊緣檢測后的圖為灰度圖  
            61.   
            62.     //調(diào)用一次回調(diào)函數(shù),調(diào)用一次HoughLinesP函數(shù)  
            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( )函數(shù)】--------------------------------  
            78. //      描述:【頂帽運(yùn)算/黑帽運(yùn)算】窗口的回調(diào)函數(shù)  
            79. //----------------------------------------------------------------------------------------------  
            80. static void on_HoughLines(int, void*)  
            81. {  
            82.     //定義局部變量儲(chǔ)存全局變量  
            83.      Mat dstImage=g_dstImage.clone();  
            84.      Mat midImage=g_midImage.clone();  
            85.   
            86.      //調(diào)用HoughLinesP函數(shù)  
            87.      vector<Vec4i> mylines;  
            88.     HoughLinesP(midImage, mylines, 1, CV_PI/180, g_nthreshold+1, 50, 10 );  
            89.   
            90.     //循環(huán)遍歷繪制每一條線段  
            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( )函數(shù)】----------------------------------  
            101. //      描述:輸出一些幫助信息  
            102. //----------------------------------------------------------------------------------------------  
            103. static void ShowHelpText()  
            104. {  
            105.     //輸出一些幫助信息  
            106.     printf("\n\n\n\t請調(diào)整滾動(dòng)條觀察圖像效果~\n\n");  
            107.     printf("\n\n\t\t\t\t\t\t\t\t by淺墨"  
            108.         );  
            109. }  




            放一些運(yùn)行截圖吧。

            原始圖:


            閾值為95時(shí):



             閾值為35時(shí):


             

            閾值為200時(shí):



            本篇文章的配套源代碼請點(diǎn)擊這里下載:

             

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

             

             

            OK

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


            只有注冊用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            色综合合久久天天给综看| 青青草原综合久久大伊人精品| 久久久久久久综合狠狠综合| 九九久久精品无码专区| 77777亚洲午夜久久多人| 久久久综合九色合综国产| 亚洲成av人片不卡无码久久| 亚洲午夜久久久影院伊人| 99久久综合国产精品二区| 久久99国产综合精品女同| 久久99热这里只频精品6| 久久夜色tv网站| 久久夜色精品国产噜噜噜亚洲AV | 亚洲第一永久AV网站久久精品男人的天堂AV| 成人午夜精品无码区久久| 四虎亚洲国产成人久久精品| 99久久精品国产高清一区二区 | 伊人久久综合精品无码AV专区| 精品久久久久久综合日本| 久久99国内精品自在现线| 婷婷五月深深久久精品| 久久综合亚洲色一区二区三区| 综合久久精品色| 99久久婷婷国产综合亚洲| 久久亚洲日韩精品一区二区三区| 免费无码国产欧美久久18| 久久人爽人人爽人人片AV| 精品少妇人妻av无码久久| 久久综合狠狠综合久久激情 | 国产精品无码久久四虎| 中文字幕久久波多野结衣av| 五月丁香综合激情六月久久| 国产精品久久久久无码av| 欧美大战日韩91综合一区婷婷久久青草 | 97久久精品无码一区二区天美| 久久久无码精品亚洲日韩蜜臀浪潮| 久久国产精品久久| 国产精品99久久99久久久| 亚洲精品美女久久777777| 久久影视综合亚洲| 久久久久99精品成人片牛牛影视 |