1. 程式人生 > >【OpenCV+Python】輪廓檢測及繪製,可用以生成對應於不規則形狀ROI區域的mask

【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

希望能夠對大家有所幫助~