圖像中物理的輪廓偵測 Contour detection
Satoshi Suzuki在1985年提出的Topological structural analysis of digitized binary images by border following 在使用cv2內建的findContours()函數找尋輪廓時,輸入的圖片必須二值圖(可以使用cv2.threshold()函數),物件的輪廓可以用來找圖片中的物體,取得ROI(region of interest),用在物件的追蹤(object tracking).
語法: cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])
-
第一個參數是尋找輪廓的圖像
-
第二個參數表示輪廓的檢索模式,有四種(本文介紹的都是新的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 近似算法
#先產生一個圖像
import cv2
import numpy as np
from pylab import *
from scipy import ndimage
img = np.zeros((200, 200), dtype=np.uint8)
img[50:150, 50:150] = 255
In [4]:
imshow(img)
Out[4]:
<matplotlib.image.AxesImage at 0x181bed8ac8>
In [5]:
#使用threshold過濾器將pixel強度大於127的改為255,將pixel強度小於127的改為0
ret, thresh = cv2.threshold(img, 127, 255, 0)
In [6]:
# 顯示threshold過濾後的影像
imshow(thresh)
Out[6]:
<matplotlib.image.AxesImage at 0x181f2afe10>
第一個參數是尋找輪廓的圖像
第二個參數表示輪廓的檢索模式,有四種(本文介紹的都是新的cv2接口):
cv2.RETR_EXTERNAL表示只檢測外輪廓
cv2.RETR_LIST檢測的輪廓不建立等級關係
cv2.RETR_CCOMP建立兩個等級的輪廓,上面的一層為外邊界,裡面的一層為內孔的邊界信息。如果內孔內還有一個連通物體,這個物體的邊界也在頂層。
cv2.RETR_TREE建立一個等級樹結構的輪廓。
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.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 近似算法
In [7]:
#使用cv2.findContours()找尋輪廓
image, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#把影像變成彩色
color = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
#在原先的影像上疊上綠色輪廓,粗細為2
img = cv2.drawContours(color, contours, -1, (0,255,0), 2)
cv2.imshow("contours", color)
cv2.waitKey()
cv2.destroyAllWindows()
In [1]:
import cv2
import numpy as np
#使用pyrDown()對圖像做down sampling
img = cv2.pyrDown(cv2.imread("contour.jpg", cv2.IMREAD_UNCHANGED))
'''
Opencv中可以通過函數cv2.pyrDown()和cv2.pyrUp()來構建金字塔。
函數cv2.pyrDown()是從一個高分辨率圖像變成低分辨率圖像的。
cv2.pyrDown()函數接受3個參數:
tmp: 當前圖像,初始化為原圖像 src 。
dst: 目的圖像( 顯示圖像,為輸入圖像的一半)
Size( tmp.cols/2, tmp.rows/2 ) :目的圖像大小, 既然我們是向下採樣
默認情況下直接輸入需要操作的圖像就可以,他會把圖像按縮小1/4的來處理
'''
#設定threshod過濾器將pixel強度大於127的改為255
ret, thresh = cv2.threshold(cv2.cvtColor(img.copy(), cv2.COLOR_BGR2GRAY) , 127, 255, cv2.THRESH_BINARY)
#使用cv2.findContours()找尋輪廓
image, contours, hier = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
print('numbers of contour:',len(contours))
numbers of contour: 3
In [2]:
for c in contours:
# 把找到的contour用矩形圍起來
x,y,w,h = cv2.boundingRect(c)
#把contour標成綠色
cv2.rectangle(img, (x,y), (x+w, y+h), (0, 255, 0), 2)
# 找出圍繞contour的最小面積矩形
rect = cv2.minAreaRect(c)
# 計算最小矩形的四個座標點
box = cv2.boxPoints(rect)
# 把四個座標變成整數值
box = np.int0(box)
# 把最小矩形標示成紅色
cv2.drawContours(img, [box], 0, (0,0, 255), 3)
# 找到圍繞contour的最小圓形,並且回傳圓心座標和半徑
(x,y),radius = cv2.minEnclosingCircle(c)
# 把座標變成整數值
center = (int(x),int(y))
radius = int(radius)
# 用藍色表示最小圓形
img = cv2.circle(img,center,radius,(255,0,0),2)
# 用洋紅色標出所有找到的輪廓
cv2.drawContours(img, contours, -1, (255, 0, 255), 1)
cv2.imshow("contours", img)
cv2.waitKey()
cv2.destroyAllWindows()
找多邊形的方法:
step 1. 找尋輪廓(contour)
step 2. 使用Douglas–Peucker algorithm演算法找出多邊形:
- 在曲線首尾兩點A,B之間連線一條直線AB,該直線為曲線的弦;
- 得到曲線上離該直線段距離最大的點C,計算其與AB的距離d;
- 比較該距離與預先給定的閾值threshold的大小,如果小於threshold,則該直線段作為曲線的近似,該段曲線處理完畢。
- 如果距離大於閾值,則用C將曲線分為兩段AC和BC,並分別對兩段取信進行1~3的處理。
- 當所有曲線都處理完畢時,依次連線各個分割點形成的折線,即可以作為曲線的近似。 當threshold設的越小,找出的分割點連線越近似曲線
In [2]:
import cv2
import numpy as np
from pylab import *
from scipy import ndimage
#使用pyrDown()對圖像做down sampling
img = cv2.pyrDown(cv2.imread("contour.jpg", cv2.IMREAD_UNCHANGED))
#設定threshod過濾器將pixel強度大於127的改為255
ret, thresh = cv2.threshold(cv2.cvtColor(img.copy(), cv2.COLOR_BGR2GRAY) , 127, 255, cv2.THRESH_BINARY)
#初始化一張與原圖大小相同的黑圖,並把它變成彩色(3個channels)
black = cv2.cvtColor(np.zeros((img.shape[1], img.shape[0]), dtype=np.uint8), cv2.COLOR_GRAY2BGR)
#使用cv2.findContours()找尋輪廓
image, contours, hier = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
In [6]:
for cnt in contours:
# 設定epsilon值
epsilon = 0.01 * cv2.arcLength(cnt,True)
# 找輪廓的近似
approx = cv2.approxPolyDP(cnt,epsilon,True)
# 用內建的convexHull()函數找尋輪廓的convex
'''
convex的定義:隨機在convex內部取兩個點,兩點連線永遠都在convex內部
'''
hull = cv2.convexHull(cnt)
cv2.drawContours(black, [cnt], -1, (0, 255, 0), 2) #綠色
cv2.drawContours(black, [approx], -1, (255, 255, 0), 2) #青色
cv2.drawContours(black, [hull], -1, (0, 0, 255), 2) #紅色
cv2.imshow("hull", black)
cv2.waitKey()
cv2.destroyAllWindows()