目標
尋找輪廓的不同特徵,例如面積,周長,重心,邊界框等,這些特徵在未來的Image Recognition中,會大量的用到。
矩的概念 Image Recognition的一個核心問題是映像的特徵提取,簡單描述即為用一組簡單的資料(映像描述量)來描述整個映像,這組資料越簡單越有代表性越好。良好的特徵不受光線、噪點、幾何形變的幹擾。Image Recognition發展幾十年,不斷有新的特徵提出,而映像不變矩就是其中一個。
矩是機率與統計中的一個概念,是隨機變數的一種數字特徵。設X為隨機變數,c為常數,k為正整數。則量E[(x−c)k]稱為X關於c點的k階矩
比較重要的有兩種情況:
1. c=0。這時ak=E(Xk)稱為X的k階原點矩
2. c=E(X)。這時μk=E[(X−EX)k]稱為X的k階中心矩。
一階原點矩就是期望。一階中心矩μ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 cv2import numpy as npimg = 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/46913501cx = 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)