本次我們將討論OpenCV中圖像輪廓的另一些特徵,它們將非常有用。
有兩類邊界矩形。
▶直邊界矩形
一個直矩形(就是沒有旋轉的矩形)。它不會考慮對象是否旋轉。所以邊界矩形的面積不是最小的。可以使用函數cv2.boundingRect()查找得到,我們來看函數原型:
x,y,w,h=cv2.boundingRect(cnt)
(x,y)為矩形左上角的坐標;(w,h)是矩形的寬和高,通常情況下,cnt代表識別的輪廓。
之後我們利用cv2.rectangle(img, (x,y), (x+w,y+h), (0,255,0), 2)畫出矩形。
參數解釋:
第一個參數:img是原圖。
第二個參數:(x,y)是矩陣的左上點坐標。
第三個參數:(x+w,y+h)是矩陣的右下點坐標。
第四個參數:(0,255,0)是畫線對應的rgb顏色。
第五個參數:2是所畫的線的寬度。
我們來看代碼:
import cv2
# 讀取圖片並轉至灰度模式img = cv2.imread("tubao.png", 1)gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值化,取閾值為235ret, thresh = cv2.threshold(gray, 235, 255, cv2.THRESH_BINARY)
# 尋找圖像中的輪廓contours, hierarchy = cv2.findContours(thresh, 2, 1)
cnt = contours[0]x, y, w, h = cv2.boundingRect(cnt)cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)
cv2.imshow('finger', img)cv2.waitKey()可以看到一個外接矩形,但是它不是最小的外接矩形,接下來我們討論最小外接矩形。
▶旋轉的邊界矩形
這個邊界矩形是面積最小的,因為它考慮了對象的旋轉。
用到的函數為:
cv2.minAreaRect(cnt)
在這裡的cnt仍然跟上面的相同,該函數返回的是一個Box2D結構。
Box2D結構:
rect:(最小外接矩形的中心(x,y),(寬度,高度),旋轉角度)
但是要繪製此矩形,我們需要矩形的4個角。它是通過函cv2.boxPoints()獲得的。
我們來看代碼:
import cv2import numpy as np
# 讀取圖片並轉至灰度模式img = cv2.imread("tubao.png", 1)gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值化,取閾值為235ret, thresh = cv2.threshold(gray, 235, 255, cv2.THRESH_BINARY)
# 尋找圖像中的輪廓contours, hierarchy = cv2.findContours(thresh, 2, 1)
cnt = contours[0]
rect = cv2.minAreaRect(cnt)box = cv2.boxPoints(rect)box = np.int0(box)cv2.drawContours(img,[box],0,(0,0,255),2)
cv2.imshow('finger', img)cv2.waitKey()
可以看到,這裡得出的結果是最小外接矩形。
跟之前一樣,輪廓可以外接矩形,同時也可以外接圓形。接下來,我們使用函數cv.minEnclosingCircle()找到對象的外接圓。它是一個以最小面積完全覆蓋對象的圓圈。
函數原型:
center, radius=cv.minEnclosingCircle(points)
直接來看代碼:
import cv2import numpy as np
# 讀取圖片並轉至灰度模式img = cv2.imread("tubao.png", 1)gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值化,取閾值為235ret, thresh = cv2.threshold(gray, 235, 255, cv2.THRESH_BINARY)
# 尋找圖像中的輪廓contours, hierarchy = cv2.findContours(thresh, 2, 1)
cnt = contours[0]
(x, y), radius = cv2.minEnclosingCircle(cnt)center = (int(x), int(y))radius = int(radius)cv2.circle(img, center, radius, (0, 255, 0), 2)
cv2.imshow('finger', img)cv2.waitKey()
▶橢圓擬合
輪廓同樣也可以進行橢圓擬合,函數原型:
retval=cv.fitEllipse(points)
輸入參數跟之前的外界矩形是一樣的,就不一一詳述了。
代碼:
import cv2import numpy as np
# 讀取圖片並轉至灰度模式img = cv2.imread("tubao.png", 1)gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值化,取閾值為235ret, thresh = cv2.threshold(gray, 235, 255, cv2.THRESH_BINARY)
# 尋找圖像中的輪廓contours, hierarchy = cv2.findContours(thresh, 2, 1)
cnt = contours[0]
ellipse = cv2.fitEllipse(cnt)cv2.ellipse(img,ellipse,(0,255,0),2)
cv2.imshow('finger', img)cv2.waitKey()
▶直線擬合
同樣,我們可以將一條直線擬合到一組點。下圖包含一組白點,我們可以近似一條直線。
函數原型:
output=cv2.fitLine(InputArray points, distType, param, reps, aeps)
InputArray Points: 待擬合的直線的集合,必須是矩陣形式.
distType: 距離類型。fitline為距離最小化函數,擬合直線時,要使輸入點到擬合直線的距離和最小化。這裡的距離的類型有以下幾種:
cv2.DIST_USER : User defined distance
cv2.DIST_L1: distance = |x1-x2| + |y1-y2|
cv2.DIST_L2: 歐式距離,此時與最小二乘法相同
cv2.DIST_C:distance = max(|x1-x2|,|y1-y2|)
cv2.DIST_L12:L1-L2 metric: distance = 2(sqrt(1+x*x/2) - 1))
cv2.DIST_FAIR:distance = c^2(|x|/c-log(1+|x|/c)), c = 1.3998
cv2.DIST_WELSCH: distance = c2/2(1-exp(-(x/c)2)), c = 2.9846
cv2.DIST_HUBER:distance = |x|<c |x^2/2 : c(|x|-c/2), c=1.345
param:距離參數,跟所選的距離類型有關,值可以設置為0。
reps, aeps:第5/6個參數用於表示擬合直線所需要的徑向和角度精度,通常情況下兩個值均被設定為1e-2。
output :對於二維直線,輸出output為4維,前兩維代表擬合出的直線的方向,後兩位代表直線上的一點。(即通常說的點斜式直線)。
代碼:
import cv2import numpy as np
# 讀取圖片並轉至灰度模式img = cv2.imread("tubao.png", 1)gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值化,取閾值為235ret, thresh = cv2.threshold(gray, 235, 255, cv2.THRESH_BINARY)
# 尋找圖像中的輪廓contours, hierarchy = cv2.findContours(thresh, 2, 1)
cnt = contours[0]
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)cv2.line(img,(cols-1,righty),(0,lefty),(0,255,0),2)
cv2.imshow('finger', img)cv2.waitKey()
現在我們可以來一個實戰部分,用橢圓擬合來進行人眼識別之後標註出來,首先我們需要安裝dlib庫,這種一個人臉特徵檢測庫,接下來我們設置特徵檢測器,dlib有已經訓練的好的需要下載,也可以自己根據需要訓練:
下載連結
http://dlib.net/files/shape_predictor_68_face_landmarks.dat.bz2
下載完之後解壓,將路徑放到根目錄裡面,當然了,下載是需要科學上網的,否則下載速度非常慢。
我們使用圖片:
代碼:
import cv2 import dlib import numpy as np
detector = dlib.get_frontal_face_detector()landmark_predictor = dlib.shape_predictor('shape_predictor_68_face_landmarks.dat')
img = cv2.imread("min.jpg")faces = detector(img,1)left_eye = []right_eye = []if ( len(faces) > 0): for k,d in enumerate(faces): shape = landmark_predictor(img,d) for i in range(36,42): right_eye.append([shape.part(i).x,shape.part(i).y]) for i in range(42,48): left_eye.append([shape.part(i).x,shape.part(i).y])ellipse_left = cv2.fitEllipse(np.array(left_eye))ellipse_right = cv2.fitEllipse(np.array(right_eye))cv2.ellipse(img, ellipse_left, (0,255,0), 1)cv2.ellipse(img, ellipse_right, (0,255,0), 1)
ellipse_left((275.1310119628906, 197.24081420898438),(13.491097450256348, 47.203433990478516),84.19256591796875)
center = ellipse_left[0]size = ellipse_left[1]angle = ellipse_left[2]
cv2.imshow('PIC',img)cv2.waitKey(0)可以看到,人眼已經識別出來,並用橢圓進行了擬合,是不是很有趣。
本次我們主要討論輪廓的外界圖形,事實上它們都是非常有用的,下次我們將討論輪廓特徵的一些高階方法。