【OpenCV+Python】輪廓檢測及繪製,可用以生成對應於不規則形狀ROI區域的mask
Overview
在OpenCV中文論壇上很多人問到這樣的問題,如何對影象的不規則區域設定ROI,即設定敏感區域,以用來做相關的影象處理。
根據若干博文的整理及自己的點點經驗,在此進行簡單的歸納:
第一部分主要敘述C++中對於不規則區域設定ROImask的方法,第二部分主要敘述Python中對於不規則區域檢測及繪製輪廓的方法。
C++部分
對影象的不規則區域設定ROI
OpenCV自帶的函式cvSetImageROI( IplImage* image, CvRect rect )只能設定矩形的敏感區域,而實際影象處理中遇到的處理物件都是非矩形的不規則形狀,此時用cvSetImageROI( IplImage* image, CvRect rect )顯然達不到目的。
我們可以用以下操作進行代替:
方法一:
cvCopy(src, dst, mask); //mask與src,dst通道數可以不一樣
方法二:
cvXor(src, mask, dst); //三者通道數必須一樣
用上面語句就可以將不規則區域影象摳出來(不改變影象的大小),進而做相應的處理。
然而如何生成不規則區域的mask影象呢?
不規則區域的mask影象的生成
步驟:
1、假設目標是實現對一不規則物體區域設定ROI,提取物體的最外圍輪廓contour,使之為一連通域。
cvFindContours(gray, storage, &contour, sizeof(CvContour) , CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE);
2、對輪廓連通域進行顏色填充。
方法一:
獲取輪廓內的任意一點作為cvFloodFill操作的起始種子點;
CvRect s;
CvPoint pt;
s=cvBoundingRect(contour);
pt = cvPoint(s.x + s.width / 2, s.y + s.height / 2); //可以用其他方式獲得連通域的一個內點作為起始種子點
對輪廓連通域進行顏色填充;
cvFloodFill(gray, pt ,cvScalarAll(255));
方法二:
將輪廓內部填充為白色,其他區域為黑色;
cvDrawContours(gray, contour, CV_RGB(255,255,255), CV_RGB(255,255 ,255), -1, CV_FILLED, 8);
此時獲得的影象gray即為不規則區域的mask
Python部分
相比C++而言,Python適合做原型。
該部分介紹如何在Python中使用OpenCV圖形庫檢測並繪製輪廓,以及與C++呼叫相應OpenCV函式的不同之處。
實現方式如下:
import cv2
img = cv2.imread('D:\\test\\contour.jpg')
# Img2Grey
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Grey2Binary
ret, binary = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 輪廓檢測
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 繪製輪廓
cv2.drawContours(img, contours, -1, (0, 0, 255), 3)
cv2.imshow("img", img)
cv2.waitKey(0)
需要注意的是cv2.findContours()函式接受的引數為二值圖,即黑白的(不是灰度圖),所以讀取的影象要先轉成灰度的,再轉成二值圖。
輪廓檢測
輪廓檢測是影象處理中會經常用到的方法,OpenCV-Python介面中使用cv2.findContours()函式來查詢檢測物體的輪廓。
cv2.findContours()函式
函式的原型為:
cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])
引數
第一個引數image是尋找輪廓的影象;
第二個引數mode表示輪廓的檢索模式,有四種(本文介紹的都是新的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近似演算法
返回值
cv2.findContours()函式返回兩個值:contours和hierarchy,一個是輪廓本身,還有一個是每條輪廓對應的屬性
其中hierarchy本身包含兩個ndarray,每個ndarray對應一個輪廓,每個輪廓有四個屬性
注意
findcontours函式會“原地”修改輸入的影象。這一點可通過下面的語句驗證:
cv2.imshow("binary", binary)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.imshow("binary2", binary)
執行這些語句後會發現原圖被修改了
繪製輪廓
OpenCV中通過cv2.drawContours在影象上繪製輪廓
cv2.drawContours()函式
函式的原型為:
cv2.drawContours(image, contours, contourIdx, color[, thickness[, lineType[, hierarchy[, maxLevel[, offset ]]]]])
引數
第一個引數image是指明在哪幅影象上繪製輪廓;
第二個引數contours是輪廓本身,在Python中是一個list;
第三個引數contourIdx指定繪製輪廓list中的哪條輪廓,如果是-1,則繪製其中的所有輪廓;
後面的引數很簡單。
其中thickness表明輪廓線的寬度,如果是-1(cv2.FILLED),則為填充模式
返回值
無返回值
References
更多內容可參見:
[1] 如何生成不規則形狀的mask,以解決對影象不規則區域設定ROI的問題 - hailong
[2] OpenCV-Python教程(11、輪廓檢測) - Daetalus
希望能夠對大家有所幫助~