OpenCV-Python教程(9)(10)(11): 使用霍夫變換檢測直線 直方圖均衡化 輪廓檢測

garfielder007發表於2016-05-05

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

相比C++而言,Python適合做原型。本系列的文章介紹如何在Python中用OpenCV圖形庫,以及與C++呼叫相應OpenCV函式的不同之處。這篇文章介紹在Python中使用OpenCV的霍夫變換檢測直線。

提示:

  • 轉載請詳細註明原作者及出處,謝謝!
  • 本文介紹在OpenCV-Python中使用霍夫變換檢測直線的方法。
  • 本文不介詳細的理論知識,讀者可從其他資料中獲取相應的背景知識。筆者推薦清華大學出版社的《影象處理與計算機視覺演算法及應用(第2版) 》。

霍夫變換

Hough變換是經典的檢測直線的演算法。其最初用來檢測影象中的直線,同時也可以將其擴充套件,以用來檢測影象中簡單的結構。

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

[python] view plain copy
  1. #coding=utf-8  
  2. import cv2  
  3. import numpy as np    
  4.   
  5. img = cv2.imread("/home/sunny/workspace/images/road.jpg"0)  
  6.   
  7. img = cv2.GaussianBlur(img,(3,3),0)  
  8. edges = cv2.Canny(img, 50150, apertureSize = 3)  
  9. lines = cv2.HoughLines(edges,1,np.pi/180,118#這裡對最後一個引數使用了經驗型的值  
  10. result = img.copy()  
  11. for line in lines[0]:  
  12.     rho = line[0#第一個元素是距離rho  
  13.     theta= line[1#第二個元素是角度theta  
  14.     print rho  
  15.     print theta  
  16.     if  (theta < (np.pi/4. )) or (theta > (3.*np.pi/4.0)): #垂直直線  
  17.                 #該直線與第一行的交點  
  18.         pt1 = (int(rho/np.cos(theta)),0)  
  19.         #該直線與最後一行的焦點  
  20.         pt2 = (int((rho-result.shape[0]*np.sin(theta))/np.cos(theta)),result.shape[0])  
  21.         #繪製一條白線  
  22.         cv2.line( result, pt1, pt2, (255))  
  23.     else#水平直線  
  24.         # 該直線與第一列的交點  
  25.         pt1 = (0,int(rho/np.sin(theta)))  
  26.         #該直線與最後一列的交點  
  27.         pt2 = (result.shape[1], int((rho-result.shape[1]*np.cos(theta))/np.sin(theta)))  
  28.         #繪製一條直線  
  29.         cv2.line(result, pt1, pt2, (255), 1)  
  30.   
  31. cv2.imshow('Canny', edges )  
  32. cv2.imshow('Result', result)  
  33. cv2.waitKey(0)  
  34. cv2.destroyAllWindows()  

結果如下:


注意:

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

[cpp] view plain copy
  1. std::vector<cv::Vec2f>::const_iterator it= lines.begin();  
  2. float rho= (*it)[0];  
  3. float theta= (*it)[1];  

這樣的方式訪問rho和theta。

而在Python中,返回的是一個三維的np.ndarray!。可通過檢驗HoughLines返回的lines的ndim屬性得到。如:

[python] view plain copy
  1. lines = cv2.HoughLines(edges,1,np.pi/180,118)  
  2. print lines.ndim  
  3. #將得到3  
至於為什麼是三維的,這和NumPy中ndarray的屬性有關(關於NumPy的相關內容,請移步至NumPy簡明教程),如果將HoughLines檢測到的的結果輸出,就一目瞭然了:
[python] view plain copy
  1. #上面例子中檢測到的lines的資料  
  2.   
  3. 3 #lines.ndim屬性  
  4. (152#lines.shape屬性  
  5.   
  6. #lines[0]  
  7. [[  4.20000000e+01   2.14675498e+00]  
  8.  [  4.50000000e+01   2.14675498e+00]  
  9.  [  3.50000000e+01   2.16420817e+00]  
  10.  [  1.49000000e+02   1.60570288e+00]  
  11.  [  2.24000000e+02   1.74532920e-01]]  
  12. ===============  
  13. #lines本身  
  14. [[[  4.20000000e+01   2.14675498e+00]  
  15.   [  4.50000000e+01   2.14675498e+00]  
  16.   [  3.50000000e+01   2.16420817e+00]  
  17.   [  1.49000000e+02   1.60570288e+00]  
  18.   [  2.24000000e+02   1.74532920e-01]]]  

概率霍夫變換

觀察前面的例子得到的結果圖片,其中Hough變換看起來就像在影象中查詢對齊的邊界畫素點集合。但這樣會在一些情況下導致虛假檢測,如畫素偶然對齊或多條直線穿過同樣的對齊畫素造成的多重檢測。

要避免這樣的問題,並檢測影象中分段的直線(而不是貫穿整個影象的直線),就誕生了Hough變化的改進版,即概率Hough變換(Probabilistic Hough)。在OpenCV中用函式cv::HoughLinesP 實現。如下:

[python] view plain copy
  1. #coding=utf-8  
  2. import cv2  
  3. import numpy as np    
  4.   
  5. img = cv2.imread("/home/sunny/workspace/images/road.jpg")  
  6.   
  7. img = cv2.GaussianBlur(img,(3,3),0)  
  8. edges = cv2.Canny(img, 50150, apertureSize = 3)  
  9. lines = cv2.HoughLines(edges,1,np.pi/180,118)  
  10. result = img.copy()  
  11.   
  12. #經驗引數  
  13. minLineLength = 200  
  14. maxLineGap = 15  
  15. lines = cv2.HoughLinesP(edges,1,np.pi/180,80,minLineLength,maxLineGap)  
  16. for x1,y1,x2,y2 in lines[0]:  
  17.     cv2.line(img,(x1,y1),(x2,y2),(0,255,0),2)  
  18.   
  19. cv2.imshow('Result', img)  
  20. cv2.waitKey(0)  
  21. cv2.destroyAllWindows()  
結果如下:

未完待續。。。

參考資料:

1、《Opencv2 Computer Vision Application Programming Cookbook》

2、《OpenCV References Manule》




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

相比C++而言,Python適合做原型。本系列的文章介紹如何在Python中用OpenCV圖形庫,以及與C++呼叫相應OpenCV函式的不同之處。這篇文章介紹在Python中使用OpenCV和NumPy對直方圖進行均衡化處理。

提示:

本文內容:

  • 使用查詢表拉伸直方圖
  • 使用OpenCV和NumPy的函式以不同的方式進行直方圖均衡化

在某些情況下,一副影象中大部分畫素的強度都集中在某一區域,而質量較高的影象中,畫素的強度應該均衡的分佈。為此,可將表示畫素強度的直方圖進行拉伸,將其平坦化。如下:


圖來自維基百科

實驗資料

本節的實驗資料來自維基百科,原圖如下:

其直方圖為:


使用查詢表來拉伸直方圖

在影象處理中,直方圖均衡化一般用來均衡影象的強度,或增加影象的對比度。在介紹使用直方圖均衡化來拉伸影象的直方圖之前,先介紹使用查詢表的方法。

觀察上圖中原始影象的直方圖,很容易發現大部分強度值範圍都沒有用到。因此先檢測影象非0的最低(imin)強度值和最高(imax)強度值。將最低值imin設為0,最高值imax設為255。中間的按255.0*(i-imin)/(imax-imin)+0.5)的形式設定。

實現的任務主要集中在查詢表的建立中,程式碼如下:

[python] view plain copy
  1. minBinNo, maxBinNo = 0255  
  2.   
  3. #計算從左起第一個不為0的直方圖位置  
  4. for binNo, binValue in enumerate(hist):  
  5.     if binValue != 0:  
  6.         minBinNo = binNo  
  7.         break  
  8. #計算從右起第一個不為0的直方圖位置  
  9. for binNo, binValue in enumerate(reversed(hist)):  
  10.     if binValue != 0:  
  11.         maxBinNo = 255-binNo  
  12.         break  
  13. print minBinNo, maxBinNo  
  14.   
  15. #生成查詢表,方法來自參考文獻1第四章第2節  
  16. for i,v in enumerate(lut):  
  17.     print i  
  18.     if i < minBinNo:  
  19.         lut[i] = 0  
  20.     elif i > maxBinNo:  
  21.         lut[i] = 255  
  22.     else:  
  23.         lut[i] = int(255.0*(i-minBinNo)/(maxBinNo-minBinNo)+0.5)  
查詢表建立完成後,就直接呼叫相應的OpenCV函式即可,這裡呼叫的是cv2.LUT函式:
[python] view plain copy
  1. #計算  
  2. result = cv2.LUT(image, lut)  

cv2.LUT函式只有兩個引數,分別為輸入影象和查詢表,其返回處理的結果,完整程式碼如下:

[python] view plain copy
  1. #coding=utf-8  
  2. import cv2  
  3. import numpy as np  
  4.   
  5. image = cv2.imread("D:/test/unequ.jpg"0)  
  6. lut = np.zeros(256, dtype = image.dtype )#建立空的查詢表  
  7. hist= cv2.calcHist([image], #計算影象的直方圖  
  8.     [0], #使用的通道  
  9.     None#沒有使用mask  
  10.     [256], #it is a 1D histogram  
  11.     [0.0,255.0])  
  12.       
  13. minBinNo, maxBinNo = 0255  
  14.   
  15. #計算從左起第一個不為0的直方圖柱的位置  
  16. for binNo, binValue in enumerate(hist):  
  17.     if binValue != 0:  
  18.         minBinNo = binNo  
  19.         break  
  20. #計算從右起第一個不為0的直方圖柱的位置  
  21. for binNo, binValue in enumerate(reversed(hist)):  
  22.     if binValue != 0:  
  23.         maxBinNo = 255-binNo  
  24.         break  
  25. print minBinNo, maxBinNo  
  26.   
  27. #生成查詢表,方法來自參考文獻1第四章第2節  
  28. for i,v in enumerate(lut):  
  29.     print i  
  30.     if i < minBinNo:  
  31.         lut[i] = 0  
  32.     elif i > maxBinNo:  
  33.         lut[i] = 255  
  34.     else:  
  35.         lut[i] = int(255.0*(i-minBinNo)/(maxBinNo-minBinNo)+0.5)  
  36.   
  37. #計算  
  38. result = cv2.LUT(image, lut)  
  39. cv2.imshow("Result", result)  
  40. cv2.imwrite("LutImage.jpg", result)  
  41. cv2.waitKey(0)  
  42. cv2.destroyAllWindows()  
直方圖結果如下,可以看到原來佔的區域很小的直方圖尖峰被移動了:


處理結果為:


關於直方圖的繪製,請參考這篇文章

直方圖均衡化

介紹

有時影象的視覺上的缺陷並不在強度值集中在很窄的範圍內。而是某些強度值的使用頻率很大。比如第一幅圖中,灰度圖中間值的佔了很大的比例。

在完美均衡的直方圖中,每個柱的值都應該相等。即50%的畫素值應該小於12825%的畫素值應該小於64。總結出的經驗可定義為:在標準的直方圖中p%的畫素擁有的強度值一定小於或等於255×p%。將該規律用於均衡直方圖中:強度i的灰度值應該在對應的畫素強度值低於i的百分比的強度中。因此,所需的查詢表可以由下面的式子建立:

[python] view plain copy
  1. lut[i] = int(255.0 *p[i]) #p[i]是是強度值小於或等於i的畫素的數目。  
p[i]即直方圖累積值,這是包含小於給點強度值的畫素的直方圖,以代替包含指定強度值畫素的數目。比如第一幅影象的累計直方圖如下圖中的藍線:


而完美均衡的直方圖,其累積直方圖應為一條斜線,如上圖中均衡化之後的紅線。

更專業一點,這種累積直方圖應稱為累積分佈(cumulative distribition)。在NumPy中有一個專門的函式來計算。這在NumPy實現直方圖均衡化一節中介紹。

通過上面的介紹,應該可以明白,直方圖均衡化就是對影象使用一種特殊的查詢表。在第三個例子中可以看到使用查詢表來獲得直方圖均衡化的效果。通常來說,直方圖均衡化大大增加了影象的表象。但根據影象可視內容的不同,不同影象的直方圖均衡化產生的效果不盡相同。

直方圖均衡化之OpenCV函式實現

用OpenCV實現直方圖均衡化很簡單,只需呼叫一個函式即可:

[python] view plain copy
  1. img = cv2.imread('影象路徑',0)  
  2. equ = cv2.equalizeHist(img)  
  3. cv2.imshow('equ',equ)  
這樣影象就均衡化了。可以通過直方圖的計算與顯示這篇文章中介紹的方法將結果繪製出來。

直方圖均衡化之NumPy函式實現

通過前面的介紹,可以明白直方圖均衡化就是用一種特殊的查詢表來實現的。所以這裡用NumPy函式,以查詢表的方式手動實現影象直方圖的均衡化:

[python] view plain copy
  1. #coding=utf-8  
  2. import cv2  
  3. import numpy as np  
  4.   
  5. image = cv2.imread("D:/test/unequ.jpg"0)  
  6.   
  7. lut = np.zeros(256, dtype = image.dtype )#建立空的查詢表  
  8.   
  9. hist,bins = np.histogram(image.flatten(),256,[0,256])   
  10. cdf = hist.cumsum() #計算累積直方圖  
  11. cdf_m = np.ma.masked_equal(cdf,0#除去直方圖中的0值  
  12. cdf_m = (cdf_m - cdf_m.min())*255/(cdf_m.max()-cdf_m.min())#等同於前面介紹的lut[i] = int(255.0 *p[i])公式  
  13. cdf = np.ma.filled(cdf_m,0).astype('uint8'#將掩模處理掉的元素補為0  
  14.   
  15. #計算  
  16. result2 = cdf[image]  
  17. result = cv2.LUT(image, cdf)  
  18.   
  19. cv2.imshow("OpenCVLUT", result)  
  20. cv2.imshow("NumPyLUT", result2)  
  21. cv2.waitKey(0)  
  22. cv2.destroyAllWindows()  
最終結果


驗證

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


可以看出,總體上來看是吻合的,但OpenCV中函式的實現可能還有一些細微的差別(有空去翻下原始碼,不過今天就先到這裡了)。

參考資料:

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++而言,Python適合做原型。本系列的文章介紹如何在Python中用OpenCV圖形庫,以及與C++呼叫相應OpenCV函式的不同之處。這篇文章介紹在Python中使用OpenCV檢測並繪製輪廓。

提示:

  • 轉載請詳細註明原作者及出處,謝謝!
  • 本文介紹在OpenCV-Python中檢測並繪製輪廓的方法。
  • 本文不介詳細的理論知識,讀者可從其他資料中獲取相應的背景知識。筆者推薦清華大學出版社的《影象處理與計算機視覺演算法及應用(第2版) 》。

輪廓檢測

輪廓檢測也是影象處理中經常用到的。OpenCV-Python介面中使用cv2.findContours()函式來查詢檢測物體的輪廓。

實現

使用方式如下:

[python] view plain copy
  1. import cv2  
  2.   
  3. img = cv2.imread('D:\\test\\contour.jpg')  
  4. gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  
  5. ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)  
  6.   
  7. contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)  
  8. cv2.drawContours(img,contours,-1,(0,0,255),3)  
  9.   
  10. cv2.imshow("img", img)  
  11. cv2.waitKey(0)  

需要注意的是cv2.findContours()函式接受的引數為二值圖,即黑白的(不是灰度圖),所以讀取的影象要先轉成灰度的,再轉成二值圖,參見4、5兩行。第六行是檢測輪廓,第七行是繪製輪廓。

結果

原圖如下:

檢測結果如下:

注意,findcontours函式會“原地”修改輸入的影象。這一點可通過下面的語句驗證:

[python] view plain copy
  1. cv2.imshow("binary", binary)  
  2. contours, hierarchy = cv2.findContours(binary,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)  
  3. cv2.imshow("binary2", binary)  
執行這些語句後會發現原圖被修改了。

cv2.findContours()函式

函式的原型為

[python] view plain copy
  1. cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])  
返回兩個值:contours:hierarchy。

引數

第一個引數是尋找輪廓的影象;

第二個參數列示輪廓的檢索模式,有四種(本文介紹的都是新的cv2介面):
    cv2.RETR_EXTERNAL表示只檢測外輪廓
    cv2.RETR_LIST檢測的輪廓不建立等級關係
    cv2.RETR_CCOMP建立兩個等級的輪廓,上面的一層為外邊界,裡面的一層為內孔的邊界資訊。如果內孔內還有一個連通物體,這個物體的邊界也在頂層。
    cv2.RETR_TREE建立一個等級樹結構的輪廓。

第三個引數method為輪廓的近似辦法
    cv2.CHAIN_APPROX_NONE儲存所有的輪廓點,相鄰的兩個點的畫素位置差不超過1,即max(abs(x1-x2),abs(y2-y1))==1
    cv2.CHAIN_APPROX_SIMPLE壓縮水平方向,垂直方向,對角線方向的元素,只保留該方向的終點座標,例如一個矩形輪廓只需4個點來儲存輪廓資訊
    cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似演算法

返回值

cv2.findContours()函式返回兩個值,一個是輪廓本身,還有一個是每條輪廓對應的屬性。

contour返回值

cv2.findContours()函式首先返回一個list,list中每個元素都是影象中的一個輪廓,用numpy中的ndarray表示。這個概念非常重要。在下面drawContours中會看見。通過
[python] view plain copy
  1. print (type(contours))  
  2. print (type(contours[0]))  
  3. print (len(contours))  
可以驗證上述資訊。會看到本例中有兩條輪廓,一個是五角星的,一個是矩形的。每個輪廓是一個ndarray,每個ndarray是輪廓上的點的集合。

由於我們知道返回的輪廓有兩個,因此可通過

[python] view plain copy
  1. cv2.drawContours(img,contours,0,(0,0,255),3)  
[python] view plain copy
  1. cv2.drawContours(img,contours,1,(0,255,0),3)  
分別繪製兩個輪廓,關於該引數可參見下面一節的內容。同時通過
[python] view plain copy
  1. print (len(contours[0]))  
  2. print (len(contours[1]))  
輸出兩個輪廓中儲存的點的個數,可以看到,第一個輪廓中只有4個元素,這是因為輪廓中並不是儲存輪廓上所有的點,而是隻儲存可以用直線描述輪廓的點的個數,比如一個“正立”的矩形,只需4個頂點就能描述輪廓了。

hierarchy返回值

此外,該函式還可返回一個可選的hiararchy結果,這是一個ndarray,其中的元素個數和輪廓個數相同,每個輪廓contours[i]對應4個hierarchy元素hierarchy[i][0] ~hierarchy[i][3],分別表示後一個輪廓、前一個輪廓、父輪廓、內嵌輪廓的索引編號,如果沒有對應項,則該值為負數。

通過

[python] view plain copy
  1. print (type(hierarchy))  
  2. print (hierarchy.ndim)  
  3. print (hierarchy[0].ndim)  
  4. print (hierarchy.shape)  
得到
[python] view plain copy
  1. 3  
  2. 2  
  3. (124)  
可以看出,hierarchy本身包含兩個ndarray,每個ndarray對應一個輪廓,每個輪廓有四個屬性。

輪廓的繪製

OpenCV中通過cv2.drawContours在影象上繪製輪廓。  

cv2.drawContours()函式

[python] view plain copy
  1. cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset ]]]]])  

  • 第一個引數是指明在哪幅影象上繪製輪廓;
  • 第二個引數是輪廓本身,在Python中是一個list。
  • 第三個引數指定繪製輪廓list中的哪條輪廓,如果是-1,則繪製其中的所有輪廓。後面的引數很簡單。其中thickness表明輪廓線的寬度,如果是-1(cv2.FILLED),則為填充模式。繪製引數將在以後獨立詳細介紹。

補充:

寫著寫著發現一篇文章介紹不完,所以這裡先作為入門的。更多關於輪廓的資訊有機會再開一篇文章介紹。

但有朋友提出計算輪廓的極值點。可用下面的方式計算得到,如下

[python] view plain copy
  1. pentagram = contours[1#第二條輪廓是五角星  
  2.   
  3. leftmost = tuple(pentagram[:,0][pentagram[:,:,0].argmin()])  
  4. rightmost = tuple(pentagram[:,0][pentagram[:,:,0].argmin()])  
  5.   
  6. cv2.circle(img, leftmost, 2, (0,255,0),3)   
  7. cv2.circle(img, rightmost, 2, (0,0,255),3)   

注意!假設輪廓有100個點,OpenCV返回的ndarray的維數是(100, 1, 2)!!!而不是我們認為的(100, 2)。切記!!!人民郵電出版社出版了一本《NumPy攻略:Python科學計算與資料分析》,推薦去看一下。

更新:關於pentagram[:,0]的意思

在numpy的陣列中,用逗號分隔的是軸的索引。舉個例子,假設有如下的陣列:

[python] view plain copy
  1. a = np.array([[[3,4]], [[1,2]],[[5,7]],[[3,7]],[[1,8]]])  
其shape是(5, 1, 2)。與我們的輪廓是相同的。那麼a[:,0]的結果就是:
[python] view plain copy
  1. [3,4], [1,2], [5,7], [3,7], [1,8]  
這裡a[:,0]的意思就是a[0:5,0],也就是a[0:5,0:0:2],這三者是等價的

回頭看一下,a的shape是(5,1,2),表明是三個軸的。在numpy的陣列中,軸的索引是通過逗號分隔的。同時冒號索引“:”表示的是該軸的所有元素。因此a[:, 0]表示的是第一個軸的所有元素和第二個軸的第一個元素。在這裡既等價於a[0:5, 0]。

再者,若給出的索引數少於陣列中總索引數,則將已給出的索引樹預設按順序指派到軸上。比如a[0:5,0]只給出了兩個軸的索引,則第一個索引就是第一個軸的,第二個索引是第二個軸的,而第三個索引沒有,則預設為[:],即該軸的所有內容。因此a[0:5,0]也等價於a[0:5,0:0:2]。

再詳細一點,a的全體內容為:[[[3,4]], [[1,2]],[[5,7]],[[3,7]],[[1,8]]]。去掉第一層方括號,其中有五個元素,每個元素為[[3,4]]這樣的,所以第一個索引的範圍為[0:5]。注意OpenCV函式返回的多維陣列和常見的numpy陣列的不同之處!

觀察[[3,4]],我們發現其中只有一個元素,即[3, 4],第二個索引為[0:1]。

再去掉一層方括號,我們面對的是[3,4],有兩個元素,所以第三個索引的範圍為[0:2]。

再次強調一下OpenCVPython介面函式返回的NumPy陣列和普通的NumPy陣列在組織上的不同之處。

PS:OpenCV-Python討論群——219962286,歡迎大家加入互相探討學習。

得到的結果為如下:


參考資料:

1、《Opencv2 Computer Vision Application Programming Cookbook》

2、《OpenCV References Manule》

3、OpenCV官方文件Contour部分




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

相關文章