日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

OpenCV-Python教程(9)(10)(11): 使用霍夫变换检测直线 直方图均衡化 轮廓检测

發(fā)布時(shí)間:2025/3/21 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 OpenCV-Python教程(9)(10)(11): 使用霍夫变换检测直线 直方图均衡化 轮廓检测 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

OpenCV-Python教程(9、使用霍夫變換檢測(cè)直線)

相比C++而言,Python適合做原型。本系列的文章介紹如何在Python中用OpenCV圖形庫,以及與C++調(diào)用相應(yīng)OpenCV函數(shù)的不同之處。這篇文章介紹在Python中使用OpenCV的霍夫變換檢測(cè)直線。

提示:

  • 轉(zhuǎn)載請(qǐng)?jiān)敿?xì)注明原作者及出處,謝謝!
  • 本文介紹在OpenCV-Python中使用霍夫變換檢測(cè)直線的方法。
  • 本文不介詳細(xì)的理論知識(shí),讀者可從其他資料中獲取相應(yīng)的背景知識(shí)。筆者推薦清華大學(xué)出版社的《圖像處理與計(jì)算機(jī)視覺算法及應(yīng)用(第2版)?》。

霍夫變換

Hough變換是經(jīng)典的檢測(cè)直線的算法。其最初用來檢測(cè)圖像中的直線,同時(shí)也可以將其擴(kuò)展,以用來檢測(cè)圖像中簡(jiǎn)單的結(jié)構(gòu)。

OpenCV提供了兩種用于直線檢測(cè)的Hough變換形式。其中基本的版本是cv2.HoughLines。其輸入一幅含有點(diǎn)集的二值圖(由非0像素表示),其中一些點(diǎn)互相聯(lián)系組成直線。通常這是通過如Canny算子獲得的一幅邊緣圖像。cv2.HoughLines函數(shù)輸出的是[float, float]形式的ndarray,其中每個(gè)值表示檢測(cè)到的線(ρ , θ)中浮點(diǎn)點(diǎn)值的參數(shù)。下面的例子首先使用Canny算子獲得圖像邊緣,然后使用Hough變換檢測(cè)直線。其中HoughLines函數(shù)的參數(shù)3和4對(duì)應(yīng)直線搜索的步長(zhǎng)。在本例中,函數(shù)將通過步長(zhǎng)為1的半徑和步長(zhǎng)為π/180的角來搜索所有可能的直線。最后一個(gè)參數(shù)是經(jīng)過某一點(diǎn)曲線的數(shù)量的閾值,超過這個(gè)閾值,就表示這個(gè)交點(diǎn)所代表的參數(shù)對(duì)(rho, theta)在原圖像中為一條直線。具體理論可參考這篇文章。

[python]?view plaincopy
  • #coding=utf-8??
  • import?cv2??
  • import?numpy?as?np????
  • ??
  • img?=?cv2.imread("/home/sunny/workspace/images/road.jpg",?0)??
  • ??
  • img?=?cv2.GaussianBlur(img,(3,3),0)??
  • edges?=?cv2.Canny(img,?50,?150,?apertureSize?=?3)??
  • lines?=?cv2.HoughLines(edges,1,np.pi/180,118)?#這里對(duì)最后一個(gè)參數(shù)使用了經(jīng)驗(yàn)型的值??
  • result?=?img.copy()??
  • for?line?in?lines[0]:??
  • ????rho?=?line[0]?#第一個(gè)元素是距離rho??
  • ????theta=?line[1]?#第二個(gè)元素是角度theta??
  • ????print?rho??
  • ????print?theta??
  • ????if??(theta?<?(np.pi/4.?))?or?(theta?>?(3.*np.pi/4.0)):?#垂直直線??
  • ????????????????#該直線與第一行的交點(diǎn)??
  • ????????pt1?=?(int(rho/np.cos(theta)),0)??
  • ????????#該直線與最后一行的焦點(diǎn)??
  • ????????pt2?=?(int((rho-result.shape[0]*np.sin(theta))/np.cos(theta)),result.shape[0])??
  • ????????#繪制一條白線??
  • ????????cv2.line(?result,?pt1,?pt2,?(255))??
  • ????else:?#水平直線??
  • ????????#?該直線與第一列的交點(diǎn)??
  • ????????pt1?=?(0,int(rho/np.sin(theta)))??
  • ????????#該直線與最后一列的交點(diǎn)??
  • ????????pt2?=?(result.shape[1],?int((rho-result.shape[1]*np.cos(theta))/np.sin(theta)))??
  • ????????#繪制一條直線??
  • ????????cv2.line(result,?pt1,?pt2,?(255),?1)??
  • ??
  • cv2.imshow('Canny',?edges?)??
  • cv2.imshow('Result',?result)??
  • cv2.waitKey(0)??
  • cv2.destroyAllWindows()??
  • 結(jié)果如下:


    注意:

    在C++中,HoughLines函數(shù)得到的結(jié)果是一個(gè)向量lines,其中的元素是由兩個(gè)元素組成的子向量(rho, theta),所以lines的訪問方式類似二維數(shù)組。因此,可以以類似:

    [cpp]?view plaincopy
  • std::vector<cv::Vec2f>::const_iterator?it=?lines.begin();??
  • float?rho=?(*it)[0];??
  • float?theta=?(*it)[1];??
  • 這樣的方式訪問rho和theta。

    而在Python中,返回的是一個(gè)三維的np.ndarray!??赏ㄟ^檢驗(yàn)HoughLines返回的lines的ndim屬性得到。如:

    [python]?view plaincopy
  • lines?=?cv2.HoughLines(edges,1,np.pi/180,118)??
  • print?lines.ndim??
  • #將得到3??
  • 至于為什么是三維的,這和NumPy中ndarray的屬性有關(guān)(關(guān)于NumPy的相關(guān)內(nèi)容,請(qǐng)移步至 NumPy簡(jiǎn)明教程 ),如果將HoughLines檢測(cè)到的的結(jié)果輸出,就一目了然了: [python]?view plaincopy
  • #上面例子中檢測(cè)到的lines的數(shù)據(jù)??
  • ??
  • 3?#lines.ndim屬性??
  • (1,?5,?2)?#lines.shape屬性??
  • ??
  • #lines[0]??
  • [[??4.20000000e+01???2.14675498e+00]??
  • ?[??4.50000000e+01???2.14675498e+00]??
  • ?[??3.50000000e+01???2.16420817e+00]??
  • ?[??1.49000000e+02???1.60570288e+00]??
  • ?[??2.24000000e+02???1.74532920e-01]]??
  • ===============??
  • #lines本身??
  • [[[??4.20000000e+01???2.14675498e+00]??
  • ??[??4.50000000e+01???2.14675498e+00]??
  • ??[??3.50000000e+01???2.16420817e+00]??
  • ??[??1.49000000e+02???1.60570288e+00]??
  • ??[??2.24000000e+02???1.74532920e-01]]]??
  • 概率霍夫變換

    觀察前面的例子得到的結(jié)果圖片,其中Hough變換看起來就像在圖像中查找對(duì)齊的邊界像素點(diǎn)集合。但這樣會(huì)在一些情況下導(dǎo)致虛假檢測(cè),如像素偶然對(duì)齊或多條直線穿過同樣的對(duì)齊像素造成的多重檢測(cè)。

    要避免這樣的問題,并檢測(cè)圖像中分段的直線(而不是貫穿整個(gè)圖像的直線),就誕生了Hough變化的改進(jìn)版,即概率Hough變換(Probabilistic Hough)。在OpenCV中用函數(shù)cv::HoughLinesP 實(shí)現(xiàn)。如下:

    [python]?view plaincopy
  • #coding=utf-8??
  • import?cv2??
  • import?numpy?as?np????
  • ??
  • img?=?cv2.imread("/home/sunny/workspace/images/road.jpg")??
  • ??
  • img?=?cv2.GaussianBlur(img,(3,3),0)??
  • edges?=?cv2.Canny(img,?50,?150,?apertureSize?=?3)??
  • lines?=?cv2.HoughLines(edges,1,np.pi/180,118)??
  • result?=?img.copy()??
  • ??
  • #經(jīng)驗(yàn)參數(shù)??
  • minLineLength?=?200??
  • maxLineGap?=?15??
  • lines?=?cv2.HoughLinesP(edges,1,np.pi/180,80,minLineLength,maxLineGap)??
  • for?x1,y1,x2,y2?in?lines[0]:??
  • ????cv2.line(img,(x1,y1),(x2,y2),(0,255,0),2)??
  • ??
  • cv2.imshow('Result',?img)??
  • cv2.waitKey(0)??
  • cv2.destroyAllWindows()??
  • 結(jié)果如下:

    未完待續(xù)。。。

    參考資料:

    1、《Opencv2 Computer Vision Application Programming Cookbook》

    2、《OpenCV References Manule》




    OpenCV-Python教程(10、直方圖均衡化)

    相比C++而言,Python適合做原型。本系列的文章介紹如何在Python中用OpenCV圖形庫,以及與C++調(diào)用相應(yīng)OpenCV函數(shù)的不同之處。這篇文章介紹在Python中使用OpenCV和NumPy對(duì)直方圖進(jìn)行均衡化處理。

    提示:

    • 轉(zhuǎn)載請(qǐng)?jiān)敿?xì)注明原作者及出處,謝謝!
    • 本文不介詳細(xì)的理論知識(shí),讀者可從其他資料中獲取相應(yīng)的背景知識(shí)。筆者推薦清華大學(xué)出版社的《圖像處理與計(jì)算機(jī)視覺算法及應(yīng)用(第2版)?》,對(duì)于本節(jié)的內(nèi)容,建議直接參考維基百科直方圖均衡化,只需看下頁面最后的兩幅圖就能懂了。

    本文內(nèi)容:

    • 使用查找表拉伸直方圖
    • 使用OpenCV和NumPy的函數(shù)以不同的方式進(jìn)行直方圖均衡化

    在某些情況下,一副圖像中大部分像素的強(qiáng)度都集中在某一區(qū)域,而質(zhì)量較高的圖像中,像素的強(qiáng)度應(yīng)該均衡的分布。為此,可將表示像素強(qiáng)度的直方圖進(jìn)行拉伸,將其平坦化。如下:


    圖來自維基百科

    實(shí)驗(yàn)數(shù)據(jù)

    本節(jié)的實(shí)驗(yàn)數(shù)據(jù)來自維基百科,原圖如下:

    其直方圖為:


    使用查找表來拉伸直方圖

    在圖像處理中,直方圖均衡化一般用來均衡圖像的強(qiáng)度,或增加圖像的對(duì)比度。在介紹使用直方圖均衡化來拉伸圖像的直方圖之前,先介紹使用查詢表的方法。

    觀察上圖中原始圖像的直方圖,很容易發(fā)現(xiàn)大部分強(qiáng)度值范圍都沒有用到。因此先檢測(cè)圖像非0的最低(imin)強(qiáng)度值和最高(imax)強(qiáng)度值。將最低值imin設(shè)為0,最高值imax設(shè)為255。中間的按255.0*(i-imin)/(imax-imin)+0.5)的形式設(shè)置。

    實(shí)現(xiàn)的任務(wù)主要集中在查詢表的創(chuàng)建中,代碼如下:

    [python]?view plaincopy
  • minBinNo,?maxBinNo?=?0,?255??
  • ??
  • #計(jì)算從左起第一個(gè)不為0的直方圖位置??
  • for?binNo,?binValue?in?enumerate(hist):??
  • ????if?binValue?!=?0:??
  • ????????minBinNo?=?binNo??
  • ????????break??
  • #計(jì)算從右起第一個(gè)不為0的直方圖位置??
  • for?binNo,?binValue?in?enumerate(reversed(hist)):??
  • ????if?binValue?!=?0:??
  • ????????maxBinNo?=?255-binNo??
  • ????????break??
  • print?minBinNo,?maxBinNo??
  • ??
  • #生成查找表,方法來自參考文獻(xiàn)1第四章第2節(jié)??
  • for?i,v?in?enumerate(lut):??
  • ????print?i??
  • ????if?i?<?minBinNo:??
  • ????????lut[i]?=?0??
  • ????elif?i?>?maxBinNo:??
  • ????????lut[i]?=?255??
  • ????else:??
  • ????????lut[i]?=?int(255.0*(i-minBinNo)/(maxBinNo-minBinNo)+0.5)??
  • 查詢表創(chuàng)建完成后,就直接調(diào)用相應(yīng)的OpenCV函數(shù)即可,這里調(diào)用的是cv2.LUT函數(shù): [python]?view plaincopy
  • #計(jì)算??
  • result?=?cv2.LUT(image,?lut)??
  • cv2.LUT函數(shù)只有兩個(gè)參數(shù),分別為輸入圖像和查找表,其返回處理的結(jié)果,完整代碼如下:

    [python]?view plaincopy
  • #coding=utf-8??
  • import?cv2??
  • import?numpy?as?np??
  • ??
  • image?=?cv2.imread("D:/test/unequ.jpg",?0)??
  • lut?=?np.zeros(256,?dtype?=?image.dtype?)#創(chuàng)建空的查找表??
  • hist=?cv2.calcHist([image],?#計(jì)算圖像的直方圖??
  • ????[0],?#使用的通道??
  • ????None,?#沒有使用mask??
  • ????[256],?#it?is?a?1D?histogram??
  • ????[0.0,255.0])??
  • ??????
  • minBinNo,?maxBinNo?=?0,?255??
  • ??
  • #計(jì)算從左起第一個(gè)不為0的直方圖柱的位置??
  • for?binNo,?binValue?in?enumerate(hist):??
  • ????if?binValue?!=?0:??
  • ????????minBinNo?=?binNo??
  • ????????break??
  • #計(jì)算從右起第一個(gè)不為0的直方圖柱的位置??
  • for?binNo,?binValue?in?enumerate(reversed(hist)):??
  • ????if?binValue?!=?0:??
  • ????????maxBinNo?=?255-binNo??
  • ????????break??
  • print?minBinNo,?maxBinNo??
  • ??
  • #生成查找表,方法來自參考文獻(xiàn)1第四章第2節(jié)??
  • for?i,v?in?enumerate(lut):??
  • ????print?i??
  • ????if?i?<?minBinNo:??
  • ????????lut[i]?=?0??
  • ????elif?i?>?maxBinNo:??
  • ????????lut[i]?=?255??
  • ????else:??
  • ????????lut[i]?=?int(255.0*(i-minBinNo)/(maxBinNo-minBinNo)+0.5)??
  • ??
  • #計(jì)算??
  • result?=?cv2.LUT(image,?lut)??
  • cv2.imshow("Result",?result)??
  • cv2.imwrite("LutImage.jpg",?result)??
  • cv2.waitKey(0)??
  • cv2.destroyAllWindows()??
  • 直方圖結(jié)果如下,可以看到原來占的區(qū)域很小的直方圖尖峰被移動(dòng)了:


    處理結(jié)果為:


    關(guān)于直方圖的繪制,請(qǐng)參考這篇文章。

    直方圖均衡化

    介紹

    有時(shí)圖像的視覺上的缺陷并不在強(qiáng)度值集中在很窄的范圍內(nèi)。而是某些強(qiáng)度值的使用頻率很大。比如第一幅圖中,灰度圖中間值的占了很大的比例。

    在完美均衡的直方圖中,每個(gè)柱的值都應(yīng)該相等。即50%的像素值應(yīng)該小于12825%的像素值應(yīng)該小于64??偨Y(jié)出的經(jīng)驗(yàn)可定義為:在標(biāo)準(zhǔn)的直方圖中p%的像素?fù)碛械膹?qiáng)度值一定小于或等于255×p%。將該規(guī)律用于均衡直方圖中:強(qiáng)度i的灰度值應(yīng)該在對(duì)應(yīng)的像素強(qiáng)度值低于i的百分比的強(qiáng)度中。因此,所需的查詢表可以由下面的式子建立:

    [python]?view plaincopy
  • lut[i]?=?int(255.0?*p[i])?#p[i]是是強(qiáng)度值小于或等于i的像素的數(shù)目。??
  • p[i]即直方圖累積值,這是包含小于給點(diǎn)強(qiáng)度值的像素的直方圖,以代替包含指定強(qiáng)度值像素的數(shù)目。比如第一幅圖像的累計(jì)直方圖如下圖中的藍(lán)線:


    而完美均衡的直方圖,其累積直方圖應(yīng)為一條斜線,如上圖中均衡化之后的紅線。

    更專業(yè)一點(diǎn),這種累積直方圖應(yīng)稱為累積分布(cumulative distribition)。在NumPy中有一個(gè)專門的函數(shù)來計(jì)算。這在NumPy實(shí)現(xiàn)直方圖均衡化一節(jié)中介紹。

    通過上面的介紹,應(yīng)該可以明白,直方圖均衡化就是對(duì)圖像使用一種特殊的查詢表。在第三個(gè)例子中可以看到使用查詢表來獲得直方圖均衡化的效果。通常來說,直方圖均衡化大大增加了圖像的表象。但根據(jù)圖像可視內(nèi)容的不同,不同圖像的直方圖均衡化產(chǎn)生的效果不盡相同。

    直方圖均衡化之OpenCV函數(shù)實(shí)現(xiàn)

    用OpenCV實(shí)現(xiàn)直方圖均衡化很簡(jiǎn)單,只需調(diào)用一個(gè)函數(shù)即可:

    [python]?view plaincopy
  • img?=?cv2.imread('圖像路徑',0)??
  • equ?=?cv2.equalizeHist(img)??
  • cv2.imshow('equ',equ)??
  • 這樣圖像就均衡化了??梢酝ㄟ^ 直方圖的計(jì)算與顯示 這篇文章中介紹的方法將結(jié)果繪制出來。

    直方圖均衡化之NumPy函數(shù)實(shí)現(xiàn)

    通過前面的介紹,可以明白直方圖均衡化就是用一種特殊的查找表來實(shí)現(xiàn)的。所以這里用NumPy函數(shù),以查找表的方式手動(dòng)實(shí)現(xiàn)圖像直方圖的均衡化:

    [python]?view plaincopy
  • #coding=utf-8??
  • import?cv2??
  • import?numpy?as?np??
  • ??
  • image?=?cv2.imread("D:/test/unequ.jpg",?0)??
  • ??
  • lut?=?np.zeros(256,?dtype?=?image.dtype?)#創(chuàng)建空的查找表??
  • ??
  • hist,bins?=?np.histogram(image.flatten(),256,[0,256])???
  • cdf?=?hist.cumsum()?#計(jì)算累積直方圖??
  • cdf_m?=?np.ma.masked_equal(cdf,0)?#除去直方圖中的0值??
  • cdf_m?=?(cdf_m?-?cdf_m.min())*255/(cdf_m.max()-cdf_m.min())#等同于前面介紹的lut[i]?=?int(255.0?*p[i])公式??
  • cdf?=?np.ma.filled(cdf_m,0).astype('uint8')?#將掩模處理掉的元素補(bǔ)為0??
  • ??
  • #計(jì)算??
  • result2?=?cdf[image]??
  • result?=?cv2.LUT(image,?cdf)??
  • ??
  • cv2.imshow("OpenCVLUT",?result)??
  • cv2.imshow("NumPyLUT",?result2)??
  • cv2.waitKey(0)??
  • cv2.destroyAllWindows()??
  • 最終結(jié)果


    驗(yàn)證

    比較查找表和OpenCV直方圖均衡化生成的直方圖:


    可以看出,總體上來看是吻合的,但OpenCV中函數(shù)的實(shí)現(xiàn) 可能 還有一些細(xì)微的差別(有空去翻下源碼,不過今天就先到這里了)。

    參考資料:

    1、《Opencv2 Computer Vision Application Programming Cookbook》

    2、《OpenCV References Manule》

    3、http://opencvpython.blogspot.com/2013/03/histograms-2-histogram-equalization.html




    OpenCV-Python教程(11、輪廓檢測(cè))


    相比C++而言,Python適合做原型。本系列的文章介紹如何在Python中用OpenCV圖形庫,以及與C++調(diào)用相應(yīng)OpenCV函數(shù)的不同之處。這篇文章介紹在Python中使用OpenCV檢測(cè)并繪制輪廓。

    提示:

    • 轉(zhuǎn)載請(qǐng)?jiān)敿?xì)注明原作者及出處,謝謝!
    • 本文介紹在OpenCV-Python中檢測(cè)并繪制輪廓的方法。
    • 本文不介詳細(xì)的理論知識(shí),讀者可從其他資料中獲取相應(yīng)的背景知識(shí)。筆者推薦清華大學(xué)出版社的《圖像處理與計(jì)算機(jī)視覺算法及應(yīng)用(第2版)?》。

    輪廓檢測(cè)

    輪廓檢測(cè)也是圖像處理中經(jīng)常用到的。OpenCV-Python接口中使用cv2.findContours()函數(shù)來查找檢測(cè)物體的輪廓。

    實(shí)現(xiàn)

    使用方式如下:

    [python]?view plaincopy
  • import?cv2??
  • ??
  • img?=?cv2.imread('D:\\test\\contour.jpg')??
  • gray?=?cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)??
  • ret,?binary?=?cv2.threshold(gray,127,255,cv2.THRESH_BINARY)??
  • ??
  • contours,?hierarchy?=?cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)??
  • cv2.drawContours(img,contours,-1,(0,0,255),3)??
  • ??
  • cv2.imshow("img",?img)??
  • cv2.waitKey(0)??

  • 需要注意的是cv2.findContours()函數(shù)接受的參數(shù)為二值圖,即黑白的(不是灰度圖),所以讀取的圖像要先轉(zhuǎn)成灰度的,再轉(zhuǎn)成二值圖,參見4、5兩行。第六行是檢測(cè)輪廓,第七行是繪制輪廓。

    結(jié)果

    原圖如下:

    檢測(cè)結(jié)果如下:

    注意,findcontours函數(shù)會(huì)“原地”修改輸入的圖像。這一點(diǎn)可通過下面的語句驗(yàn)證:

    [python]?view plaincopy
  • cv2.imshow("binary",?binary)??
  • contours,?hierarchy?=?cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)??
  • cv2.imshow("binary2",?binary)??
  • 執(zhí)行這些語句后會(huì)發(fā)現(xiàn)原圖被修改了。

    cv2.findContours()函數(shù)

    函數(shù)的原型為

    [python]?view plaincopy
  • cv2.findContours(image,?mode,?method[,?contours[,?hierarchy[,?offset?]]])??
  • 返回兩個(gè)值:contours:hierarchy。

    參數(shù)

    第一個(gè)參數(shù)是尋找輪廓的圖像;

    第二個(gè)參數(shù)表示輪廓的檢索模式,有四種(本文介紹的都是新的cv2接口):
    ??? cv2.RETR_EXTERNAL表示只檢測(cè)外輪廓
    ??? cv2.RETR_LIST檢測(cè)的輪廓不建立等級(jí)關(guān)系
    ??? cv2.RETR_CCOMP建立兩個(gè)等級(jí)的輪廓,上面的一層為外邊界,里面的一層為內(nèi)孔的邊界信息。如果內(nèi)孔內(nèi)還有一個(gè)連通物體,這個(gè)物體的邊界也在頂層。
    ??? cv2.RETR_TREE建立一個(gè)等級(jí)樹結(jié)構(gòu)的輪廓。

    第三個(gè)參數(shù)method為輪廓的近似辦法
    ??? cv2.CHAIN_APPROX_NONE存儲(chǔ)所有的輪廓點(diǎn),相鄰的兩個(gè)點(diǎn)的像素位置差不超過1,即max(abs(x1-x2),abs(y2-y1))==1
    ??? cv2.CHAIN_APPROX_SIMPLE壓縮水平方向,垂直方向,對(duì)角線方向的元素,只保留該方向的終點(diǎn)坐標(biāo),例如一個(gè)矩形輪廓只需4個(gè)點(diǎn)來保存輪廓信息
    ??? cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

    返回值

    cv2.findContours()函數(shù)返回兩個(gè)值,一個(gè)是輪廓本身,還有一個(gè)是每條輪廓對(duì)應(yīng)的屬性。

    contour返回值

    cv2.findContours()函數(shù)首先返回一個(gè)list,list中每個(gè)元素都是圖像中的一個(gè)輪廓,用numpy中的ndarray表示。這個(gè)概念非常重要。在下面drawContours中會(huì)看見。通過
    [python]?view plaincopy
  • print?(type(contours))??
  • print?(type(contours[0]))??
  • print?(len(contours))??
  • 可以驗(yàn)證上述信息。會(huì)看到本例中有兩條輪廓,一個(gè)是五角星的,一個(gè)是矩形的。每個(gè)輪廓是一個(gè)ndarray,每個(gè)ndarray是輪廓上的點(diǎn)的集合。

    由于我們知道返回的輪廓有兩個(gè),因此可通過

    [python]?view plaincopy
  • cv2.drawContours(img,contours,0,(0,0,255),3)??
  • [python]?view plaincopy
  • cv2.drawContours(img,contours,1,(0,255,0),3)??
  • 分別繪制兩個(gè)輪廓,關(guān)于該參數(shù)可參見下面一節(jié)的內(nèi)容。同時(shí)通過
    [python]?view plaincopy
  • print?(len(contours[0]))??
  • print?(len(contours[1]))??
  • 輸出兩個(gè)輪廓中存儲(chǔ)的點(diǎn)的個(gè)數(shù),可以看到,第一個(gè)輪廓中只有4個(gè)元素,這是因?yàn)檩喞胁⒉皇谴鎯?chǔ)輪廓上所有的點(diǎn),而是只存儲(chǔ)可以用直線描述輪廓的點(diǎn)的個(gè)數(shù),比如一個(gè)“正立”的矩形,只需4個(gè)頂點(diǎn)就能描述輪廓了。

    hierarchy返回值

    此外,該函數(shù)還可返回一個(gè)可選的hiararchy結(jié)果,這是一個(gè)ndarray,其中的元素個(gè)數(shù)和輪廓個(gè)數(shù)相同,每個(gè)輪廓contours[i]對(duì)應(yīng)4個(gè)hierarchy元素hierarchy[i][0] ~hierarchy[i][3],分別表示后一個(gè)輪廓、前一個(gè)輪廓、父輪廓、內(nèi)嵌輪廓的索引編號(hào),如果沒有對(duì)應(yīng)項(xiàng),則該值為負(fù)數(shù)。

    通過

    [python]?view plaincopy
  • print?(type(hierarchy))??
  • print?(hierarchy.ndim)??
  • print?(hierarchy[0].ndim)??
  • print?(hierarchy.shape)??
  • 得到
    [python]?view plaincopy
  • 3??
  • 2??
  • (1,?2,?4)??
  • 可以看出,hierarchy本身包含兩個(gè)ndarray,每個(gè)ndarray對(duì)應(yīng)一個(gè)輪廓,每個(gè)輪廓有四個(gè)屬性。

    輪廓的繪制

    OpenCV中通過cv2.drawContours在圖像上繪制輪廓。??

    cv2.drawContours()函數(shù)

    [python]?view plaincopy
  • cv2.drawContours(image,?contours,?contourIdx,?color[,?thickness[,?lineType[,?hierarchy[,?maxLevel[,?offset?]]]]])??
    • 第一個(gè)參數(shù)是指明在哪幅圖像上繪制輪廓;
    • 第二個(gè)參數(shù)是輪廓本身,在Python中是一個(gè)list。
    • 第三個(gè)參數(shù)指定繪制輪廓list中的哪條輪廓,如果是-1,則繪制其中的所有輪廓。后面的參數(shù)很簡(jiǎn)單。其中thickness表明輪廓線的寬度,如果是-1(cv2.FILLED),則為填充模式。繪制參數(shù)將在以后獨(dú)立詳細(xì)介紹。

    補(bǔ)充:

    寫著寫著發(fā)現(xiàn)一篇文章介紹不完,所以這里先作為入門的。更多關(guān)于輪廓的信息有機(jī)會(huì)再開一篇文章介紹。

    但有朋友提出計(jì)算輪廓的極值點(diǎn)??捎孟旅娴姆绞接?jì)算得到,如下

    [python]?view plaincopy
  • pentagram?=?contours[1]?#第二條輪廓是五角星??
  • ??
  • leftmost?=?tuple(pentagram[:,0][pentagram[:,:,0].argmin()])??
  • rightmost?=?tuple(pentagram[:,0][pentagram[:,:,0].argmin()])??
  • ??
  • cv2.circle(img,?leftmost,?2,?(0,255,0),3)???
  • cv2.circle(img,?rightmost,?2,?(0,0,255),3)???

  • 注意!假設(shè)輪廓有100個(gè)點(diǎn),OpenCV返回的ndarray的維數(shù)是(100, 1, 2)!!!而不是我們認(rèn)為的(100, 2)。切記!!!人民郵電出版社出版了一本《NumPy攻略:Python科學(xué)計(jì)算與數(shù)據(jù)分析》,推薦去看一下。

    更新:關(guān)于pentagram[:,0]的意思

    在numpy的數(shù)組中,用逗號(hào)分隔的是軸的索引。舉個(gè)例子,假設(shè)有如下的數(shù)組:

    [python]?view plaincopy
  • a?=?np.array([[[3,4]],?[[1,2]],[[5,7]],[[3,7]],[[1,8]]])??
  • 其shape是(5, 1, 2)。與我們的輪廓是相同的。那么a[:,0]的結(jié)果就是: [python]?view plaincopy
  • [3,4],?[1,2],?[5,7],?[3,7],?[1,8]??
  • 這里a[:,0]的意思就是a[0:5,0],也就是a[0:5,0:0:2] ,這三者是等價(jià)的 。

    回頭看一下,a的shape是(5,1,2),表明是三個(gè)軸的。在numpy的數(shù)組中,軸的索引是通過逗號(hào)分隔的。同時(shí)冒號(hào)索引“:”表示的是該軸的所有元素。因此a[:, 0]表示的是第一個(gè)軸的所有元素和第二個(gè)軸的第一個(gè)元素。在這里既等價(jià)于a[0:5, 0]。

    再者,若給出的索引數(shù)少于數(shù)組中總索引數(shù),則將已給出的索引樹默認(rèn)按順序指派到軸上。比如a[0:5,0]只給出了兩個(gè)軸的索引,則第一個(gè)索引就是第一個(gè)軸的,第二個(gè)索引是第二個(gè)軸的,而第三個(gè)索引沒有,則默認(rèn)為[:],即該軸的所有內(nèi)容。因此a[0:5,0]也等價(jià)于a[0:5,0:0:2]。

    再詳細(xì)一點(diǎn),a的全體內(nèi)容為:[[[3,4]], [[1,2]],[[5,7]],[[3,7]],[[1,8]]]。去掉第一層方括號(hào),其中有五個(gè)元素,每個(gè)元素為[[3,4]]這樣的,所以第一個(gè)索引的范圍為[0:5]。注意OpenCV函數(shù)返回的多維數(shù)組和常見的numpy數(shù)組的不同之處!

    觀察[[3,4]],我們發(fā)現(xiàn)其中只有一個(gè)元素,即[3, 4],第二個(gè)索引為[0:1]。

    再去掉一層方括號(hào),我們面對(duì)的是[3,4],有兩個(gè)元素,所以第三個(gè)索引的范圍為[0:2]。

    再次強(qiáng)調(diào)一下OpenCVPython接口函數(shù)返回的NumPy數(shù)組和普通的NumPy數(shù)組在組織上的不同之處。

    PS:OpenCV-Python討論群——219962286,歡迎大家加入互相探討學(xué)習(xí)。

    得到的結(jié)果為如下:


    參考資料:

    1、《Opencv2 Computer Vision Application Programming Cookbook》

    2、《OpenCV References Manule》

    3、OpenCV官方文檔Contour部分




    from: http://blog.csdn.net/sunny2038/article/category/904451

    總結(jié)

    以上是生活随笔為你收集整理的OpenCV-Python教程(9)(10)(11): 使用霍夫变换检测直线 直方图均衡化 轮廓检测的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。