這篇介紹OPENCV提供找出輪廓與繪製輪廓,凸包的函數。
<圖一輪廓,凸包>
什麼是輪廓:
輪廓可以簡單認為成將連續的點(連著邊界)連在一起的曲線,具有相同的顏色或者灰度。輪廓在形狀分析和物體的檢測和識別中很有用。
• 為了更加準確,必須使用二值化圖像。所以在尋找輪廓之前,要進行閾值化處理或者 Canny
邊界檢測。
• 查找輪廓的函數會修改原始圖像。如果你在找到輪廓之後還想使用原始圖像的話,應該將
原始圖像存儲到其他變數中。
• 在 OpenCV 中,查找輪廓就像在黑色背景中找超白色物體。注意,要找的物體應該是白色
而背景應該是黑色。
OPENCV提供函數 cv2.ftndContours()找輪廓:
它有三個參數,第一個是輸入圖像,第二個是輪廓檢索模式,第三個是輪廓近似方法。
返回值有三個,第一個是圖像,第二個是輪廓,第三個是輪廓的層析結構。
第二個返回值"輪廓"是一個Python 清單(list),其中存儲這圖像中的所有輪廓。每一個輪廓都是一個 Numpy 陣列,包含物件邊界點(x,y)的座標。
Ex:
img, contours_b, hierarchy = \
cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
參數:
thresh:輸入二值化圖像
cv2.RETR_TREE:輪廓檢索模式
cv2.CHAIN_APPROX_SIMPLE:輪廓近似方法
img:處理後的圖像
contours_b:輪廓
hierarchy:輪廓的層析結構
怎樣繪製輪廓:
OPENCV提供函數cv2.drawContours() 繪製輪廓:
它可以根據你提供的邊界點繪製任何形狀。它的第一個參數是原始圖像,第二個參數是輪廓,一個Python列表。第三個參數是輪廓的索引(在繪製獨立輪廓是很有用,當設 置為 -1
時繪製所有輪廓)。接下來的參數是輪廓的顏色和厚度。
EX:
cv2.drawContours(img_O, contours_b, 0, (255,0,0), 3)
參數:
img_O:原始圖像
contours_b:輪廓
0:輪廓的索引,-1:all contours
通常同一張圖上找到的輪廓不只一個,所以有輪廓個別的索引值
(255,0,0):顏色
3:畫線厚度
輪廓的近似方法:
在函數cv2.ftndCountours()的第三個參數。它到底代表什麼意思呢?
上面我們提到輪廓是一個形狀具有相同灰度值的邊界。它會儲存形狀邊界上所有的 (x,y) 座
標。但是需要將所有的這些邊界點都存儲嗎?答案就是可以根據這個參數來告訴
cv2.ftndContours函數。
參數如果被設置為 cv2.CHAIN_APPROX_NONE,所有的邊界點 都會被存儲。
但是我們真的需要這麼多點嗎?例如,當我們找的邊界是一條直線時。你用需要直線上所有
的點來表示直線嗎?
不是的,我們只需要這條直線的兩個端點而已。所以這時參數就可以設置為
cv2.CHAIN_APPROX_SIMPLE。
它會將輪廓上的冗餘點都去掉,從而節省記憶體開支!
輪廓特徵:
可以查找輪廓的不同特徵,例如面積,周長,重心,邊界框等。
矩(Image Moments):
圖像的矩可以説明我們計算圖像的質心,面積等。詳細資訊請查看維基百科Image Moments。
OPENCV 提供函數 cv2.moments() 可得到以一個字典的形式返回的矩。如下<圖二>紅色框框所示:
<圖二>
根據這些矩的值,我們可以計算出物件的重心:
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
P.S 這裡實際coding須注意,如果在沒找到輪廓的情況下,m00會等於0,會造成程式錯
誤,所以編程時需特別注意這一點。
輪廓面積:
輪廓的面積可以使用函數 cv2.contourArea() 計算得到。
area = cv2.contourArea(cnt)
輪廓周長:
也被稱為弧長。可以使用函數 cv2.arcLength() 計算得到。這個函數的第二參數可以用來指定物件的形狀是閉合的(True),還是打開的(一條曲線)。
perimeter = cv2.arcLength(cnt,True)
其重心,面積,周長如上<圖二>藍色框框所示。
凸包:
凸包與輪廓近似相似,但不同,雖然有些情況下它們給出的結果是一樣的。
函數 cv2.convexHull() 可以用來檢測一個曲線是否具有凸性缺陷,並能糾正缺陷。一般來說,凸性曲線總是凸出來的,至少是平的。如果有地方凹進去 了就被叫做"凸性缺陷"。
例如下圖中的手。紅色曲線顯示了手的凸包,凸性缺陷被雙箭頭標出來了。
<圖三凸包>
要獲得上圖的凸包,下面的命令就夠了:
hull = cv2.convexHull(cntb)
另外函數 cv2.isContourConvex() 可以用來檢測一個曲線是不是凸的。它只會返回 True 或 False。
True 代表它是凸包並有"凸性缺陷",False 代表像是輪廓剛好貼齊邊界,沒有"凸性缺陷"。
凸包可以畫出兩種邊界矩形
1.直邊界矩形
為一個直矩形就是沒有旋轉的矩形。它不會考慮物件是否旋轉。
所以邊界矩形的面積不是最小的。可以使用函數 cv2.boundingRect() 查找得到。
(x,y)為矩形左上角的座標,(w,h)是矩形的寬和高。
Ex:
x,y,w,h = cv2.boundingRect(cntb)
cv2.rectangle(imga,(x,y),(x+w,y+h),(0,255,0),2)
2.旋轉的邊界矩形
這個邊界矩形是面積最小的,因為它考慮了對象的旋轉。用到的函數為 cv2.minAreaRect()。返回的是一個 Box2D 結構,其中包含 矩形左上角角點的座標(x,y),矩形的寬和高(w,h),以及"旋轉角度"。要繪製這個矩形需要矩形的 4 個角點,可以通過函數 cv2.boxPoints() 獲 得。
程式執行結果如<圖一>右上角Flash_contours1
Ex:
rect = cv2.minAreaRect(cntb)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(imga,[box],0,(0,0,255),2)
最小外接圓:
函數 cv2.minEnclosingCircle() 可以幫我們找到一個物件的外切圓。 它是所有能夠包括物件的圓中面積最小的一個。
EX:
(x,y),radius = cv2.minEnclosingCircle(cntb)
center = (int(x),int(y))
radius = int(radius)
cv2.circle(imgb,center,radius,(0,255,255),2)
橢圓擬合:
使用的函數為 cv2.ellipse(),返回值其實就是旋轉邊界矩形的內切圓。
EX:
ellipse = cv2.fitEllipse(cntb)
cv2.ellipse(imgb,ellipse,(255,0,255),2)
程式執行結果如<圖一>左下角Flash_contours2
直線擬合:
我們可以根據一組點擬合出一條直線,同樣我們也可以為圖像中的白色點擬合出一條直線。
EX:
rows,cols = imgc.shape[:2]
[vx,vy,x,y] = cv2.fitLine(cntb, cv2.DIST_L2,0,0.01,0.01)
lefty = int((-x*vy/vx) + y)
righty = int(((cols-x)*vy/vx)+y)
cv2.line(imgc,(cols-1,righty),(0,lefty),(255,255,0),2)
程式執行結果如<圖一>右下角Flash_contours3
<完整範例程式>
https://www.facebook.com/arbu00/
<其他有關OPENCV 文章>
機器學習(2)--使用OPENCV SVM實作手寫辨識
機器學習(1)--使用OPENCV KNN實作手寫辨識
OPENCV(10)--Canny Edge Detection(Canny邊緣檢測)
OPENCV(9)--Image Gradients(圖像梯度)
OPENCV(8)--Histogram & Histograms Equalization(長條圖與長條圖均衡化)
OPENCV(7)--2D Convolution ,Image Filtering and Blurring (旋積,濾波與模糊)
OPENCV(6)--Trackbar(軌道桿)
OPENCV(5)--Drawing
OPENCV(4)--Grayscale,Binarization,Threshole(灰階化,二值化,閥值)
OPENCV(3)--Matplotlib pyplot bassic function
OPENCV(2)--Capture Video from Camera
OPENCV(1 )--How to install OPENCV in Python
<參考資料:>OPENCV官網
http://docs.opencv.org/3.1.0/index.html