1. 程式人生 > >Opencv(Python) 教程-輪廓(2)輪廓特徵求取

Opencv(Python) 教程-輪廓(2)輪廓特徵求取

目標

查詢輪廓的不同特徵,例如面積,周長,重心,邊界框等,這些特徵在未來的影象識別中,會大量的用到。

矩的概念

影象識別的一個核心問題是影象的特徵提取,簡單描述即為用一組簡單的資料(影象描述量)來描述整個影象,這組資料越簡單越有代表性越好。良好的特徵不受光線、噪點、幾何形變的干擾。影象識別發展幾十年,不斷有新的特徵提出,而影象不變矩就是其中一個。

矩是概率與統計中的一個概念,是隨機變數的一種數字特徵。設X為隨機變數,c為常數,k為正整數。則量E[(xc)k]稱為X關於c點的k階矩

比較重要的有兩種情況:

1. c=0。這時ak=E(Xk)稱為Xk階原點矩

2. c=E(X)。這時μ

k=E[(XEX)k]稱為Xk階中心矩。

一階原點矩就是期望。一階中心矩μ1=0,二階中心矩μ2就是X的方差Var(X)。在統計學上,高於4階的矩極少使用。μ3可以去衡量分佈是否有偏。μ4可以去衡量分佈(密度)在均值附近的陡峭程度如何。

針對於一幅影象,我們把畫素的座標看成是一個二維隨機變數(X,Y),那麼一幅灰度影象可以用二維灰度密度函式來表示,因此可以用矩來描述灰度影象的特徵。

不變矩(Invariant Moments)是一處高度濃縮的影象特徵,具有平移、灰度、尺度、旋轉不變性。M.K.Hu在1961年首先提出了不變矩的概念。1979年M.R.Teague根據正交多項式理論提出了Zernike矩。

對於矩的相關具體計算方法及概念,參考連結http://blog.csdn.net/qq_18343569/article/details/46913501

import cv2
import numpy as np
img = cv2.imread('star.jpg',0)
img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) #彩色轉灰度
ret,thresh = cv2.threshold(img,127,255,0)   #二值化
image,contours,hierarchy = cv2.findContours(thresh, 1, 2)
cnt = contours[0]   #選取其中的第一個輪廓
M = cv2.moments(cnt)  #對第一個輪廓
print (M)             #打印出所有計算的M的引數,其各個數值的計算公式參考http://blog.csdn.net/qq_18343569/article/details/46913501
cx = int(M['m10']/M['m00'])  #X方向的重心,其中M['m10']表示的是x方向的一階空間矩,M['m00']表示面積,M['m00']也可以通過cv2.contourArea() 計算得到
cy = int(M['m01']/M['m00']) #Y方向的重心
 

另外,關於輪廓的函式還包括如下

輪廓周長

cv2.arcLength()

perimeter = cv2.arcLength(cnt,True)
這個函式的第二引數可以用來指定物件的形狀是閉合的(True),還是開啟的(一條曲線)。假如是一條閉合的曲線,那種方法計算結果一致,如果是開曲線,則兩者計算結果不同,其中閉合的方法,會在最後將起始點和終止點連一起的長度加進去。

輪廓近似

將輪廓形狀近似到另外一種由更少點組成的輪廓形狀,新輪廓的點的數目由我們設定的準確度來決定。使用的Douglas-Peucker演算法,你可以到維基百科獲得更多此演算法的細節。為了幫助理解,假設我們要在一幅影象中查詢一個矩形,但是由於影象的種種原因,我們不能得到一個完美的矩形,而是一個“壞形狀”(如下圖所示)。現在你就可以使用這個函式來近似這個形狀了。這個函式的第二個引數叫epsilon,它是從原始輪廓到近似輪廓的最大距離。它是一個準確度引數。選擇一個好的 epsilon 對於得到滿意結果非常重要。

epsilon = 0.1*cv2.arcLength(cnt,True)
approx = cv2.approxPolyDP(cnt,epsilon,True)
下邊,第二幅圖中的綠線是當 epsilon = 10% 時得到的近似輪廓,第三幅圖是當 epsilon = 1% 時得到的近似輪廓。第三個引數設定弧線是否閉合。


凸包

凸包與輪廓近似相似,但不同,雖然有些情況下它們給出的結果是一樣的。函式 cv2.convexHull() 可以用來檢測一個曲線是否具有凸性缺陷,並能糾正缺陷。一般來說,凸性曲線總是凸出來的,至少是平的。如果有地方凹進去了就被叫做凸性缺陷。例如下圖中的手。紅色曲線顯示了手的凸包,凸性缺陷被雙箭頭標出來了。

參考程式碼為:

hull = cv2.convexHull(points, hull, clockwise, returnPoints)
points 我們要傳入的輪廓• hull 輸出,通常不需要• clockwise 方向標誌。如果設定為 True,輸出的凸包是順時針方向的。否則為逆時針方向。• returnPoints 預設值為 True。它會返回凸包上點的座標。如果設定為 False,就會返回與凸包點對應的輪廓上的點。

凸性檢測

函式 cv2.isContourConvex() 可以可以用來檢測一個曲線是不是凸的。它只能返回 True 或 False。

k = cv2.isContourConvex(cnt)

邊界矩形

直邊界矩形 一個直矩形(就是沒有旋轉的矩形)。它不會考慮物件是否旋轉。所以邊界矩形的面積不是最小的。可以使用函式 cv2.boundingRect() 查詢得到。(x,y)為矩形左上角的座標,(w,h)是矩形的寬和高。

x,y,w,h = cv2.boundingRect(cnt)
img = cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),2)
旋轉的邊界矩形 這個邊界矩形是面積最小的,因為它考慮了物件的旋轉。用到的函式為 cv2.minAreaRect()。返回的是一個 Box2D 結構,其中包含矩形左上角角點的座標(x,y),矩形的寬和高(w,h),以及旋轉角度。但是要繪製這個矩形需要矩形的 4 個角點,可以通過函式 cv2.boxPoints() 獲得。
rect = cv2.minAreaRect(cnt)
box = cv2.cv.BoxPoints(rect)
box = np.int0(box)
cv2.drawContours(img, [box], 0, (0, 0, 255), 2)
把這兩中邊界矩形顯示在下圖中,其中綠色的為直矩形,紅的為旋轉矩形。完整程式碼如下:
# 用綠色(0, 255, 0)來畫出最小的矩形框架
x, y, w, h = cv2.boundingRect(cnt)
cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)

# 用紅色表示有旋轉角度的矩形框架
rect = cv2.minAreaRect(cnt)
box = cv2.cv.BoxPoints(rect)
box = np.int0(box)
cv2.drawContours(img, [box], 0, (0, 0, 255), 2)
cv2.imwrite('contours.png', img)

最小外接圓

函式 cv2.minEnclosingCircle() 可以幫我們找到一個物件的外切圓。它是所有能夠包括物件的圓中面積最小的一個。

(x,y),radius = cv2.minEnclosingCircle(cnt)
center = (int(x),int(y))
radius = int(radius)
img = cv2.circle(img,center,radius,(0,255,0),2)

橢圓擬合

使用的函式為 cv2.ellipse(),返回值其實就是旋轉邊界矩形的內切圓

ellipse = cv2.fitEllipse(cnt)
im = cv2.ellipse(im,ellipse,(0,255,0),2)

直線擬合

我們可以根據一組點擬合出一條直線,同樣我們也可以為影象中的白色點擬合出一條直線。

rows,cols = img.shape[:2]
[vx,vy,x,y] = cv2.fitLine(cnt, cv2.DIST_L2,0,0.01,0.01)
lefty = int((-x*vy/vx) + y)
righty = int(((cols-x)*vy/vx)+y)
img = cv2.line(img,(cols-1,righty),(0,lefty),(0,255,0),2)