目錄
圖像輪廓
cv2.findContours(img, mode, method)
繪制輪廓
輪廓特征與近似
輪廓特征
輪廓近似
輪廓近似原理
opencv 實現輪廓近似
輪廓外接矩形
輪廓外接圓
圖像輪廓
cv2.findContours(img, mode, method)
mode:輪廓檢索模式(通常使用第四個模式)
-
RETR_EXTERNAL: 只檢索最外面的輪廓;
-
RETR_LIST: 檢索所有的輪廓,并將其保存到一條鏈表當中;
-
RETR_CCOMP: 檢索所有的輪廓,并將他們組織為兩層:頂層是各部分的外部邊界,第二層是空洞的邊界;
-
RETR_TREE: 檢索所有的輪廓,并重構嵌套輪廓的整個層次;
method:輪廓逼近方法
-
CHAIN_APPROX_NONE: 以Freeman鏈碼的方式輸出輪廓,所有其他方法輸出多邊形(頂點的序列)。
-
CHAIN_APPROX_SIMPLE: 壓縮水平的、垂直的和斜的部分,也就是,函數只保留他們的終點部分。
繪制輪廓
需要注意的是,當我們要進行輪廓檢測時,為了更高的準確率,使用黑白二值圖像。
輪廓特征與近似
輪廓特征
cnt = contours[0]
# 面積
cv2.contourArea(cnt)
# 周長,True 表示閉合的
cv2.arcLength(cnt, True)
輪廓近似
輪廓近似原理
如上圖,設定一個閾值 T,原曲線 AB,曲線 AB 上找一點C距離AB直線最遠,距離為d1,如果d1 < T,則曲線AB可以近似為直線 AB,如果d1 > T,則連接直線 AC 與 直線 CB,從曲線AC上找一點 D 距離直線 AC 最遠,距離為 d2,如果 d2 < T,則曲線 AC 可以近似為直線 AC,否則繼續連線。
整個過程有點類似于“二分法”。只要曲線上最遠一點小于閾值,則兩點取直線代替曲線。
通過調整閾值,我們就可以做到輪廓近似:(中間和右邊為不同閾值下的輪廓近似)
opencv 實現輪廓近似
img = cv2.imread("img1.jpg", cv2.IMREAD_COLOR)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# binary 為二值圖像, contours 為輪廓信息的集合, hierarchy 為輪廓層級信息
binary, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 獲取你想要進行近似操作的輪廓
cnt = contours[0]
# 一般閾值取百分比輪廓周長
epsilon = 0.1 * cv2.arcLength(cnt, True)
# 輪廓近似化
approx = cv2.approxPolyDP(cnt, epsilon, True)
# 需要注意copy,繪制輪廓函數會改變傳入原圖
draw_img = img.copy()
# 傳入繪制圖像,輪廓,輪廓索引(-1表示全部輪廓),線條顏色,線條寬度
res = cv2.drawContours(draw_img, approx, -1, (0, 255, 0), 1)cv2.imshow("res", res)
cv2.waitKey(0)
cv2.destroyAllWindows()
原圖:
輪廓近似化后:
輪廓外接矩形
# 獲取你想要進行近似操作的輪廓
cnt = contours[0]
# 輪廓外接矩形
x, y, w, h = cv2.boundingRect(cnt)
img = cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
我們也可以算出輪廓面積與邊界矩形的比:
area = cv2.contourArea(cnt)
x, y, w, h = cv2.boundingRect(cnt)
rect_area = w * h
extent = float(area) / rect_area
print("輪廓面積與邊界矩形比",extent)
輪廓外接圓
# 獲取你想要進行近似操作的輪廓
cnt = contours[0]
# 輪廓外接圓
(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.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()