EmguCV Image Process: Extracting Lines, Contours, and Components
參考OpenCV 2 Computer Vision Application Programming Cookbook第七章
介紹內容如下:
Detecting image contours with the Canny operator
Detecting circles in images with the Hough transform
Fitting a line to a set of points
Extracting the components' contours
Computing components' shape descriptors
前一篇提到如何從給定的點集合中找出直線的方法
而這篇要來介紹如何取出影像中物體的輪廓
一張影像中,通常會包含物體的表示方法
常用的影像分析的目的就是識別物體、擷取物體
在物體偵測或識別的應用中
(object detection/recognition)
第一步就是利用二值化的影像表示感興趣的物體的位置
(例如之前的介紹)
而下一步就是擷取出物體
這裡利用先前處理過的二值化影像來做下一步的處理
這裡必須擷取連通物體(connected components)
意思就是擷取相連pixels的形狀出來
簡單的程式如下:
Image<Gray, Byte> image = new Image<Gray, Byte>(@"binary.bmp"); Contour<Point> contours = image.FindContours( //all pixels of each contours Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_NONE, //retrieve the external contours Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_EXTERNAL); Image<Gray, Byte> blackImage = image.CopyBlank(); //Draw white contours on a black image for (; contours != null; contours = contours.HNext) { blackImage.Draw(contours, new Gray(255), 1); }在EmguCV中Image類別直接提供FindContours的方法
但此方法僅限於二值化影像(binary image)
最常見的方式就是搭配Canny演算法
傳入的參數有兩個
第一個參數是CHAIN_APPROX_METHOD列舉
這裡選用的方法是CV_CHAIN_APPROX_NONE
也就是會列出contours的所有點的位置
若是選擇CV_CHAIN_APPROX_SIMPLE
則只會列出水平、垂直、對角的結束點(end points)的位置
第二個參數是RETR_TYPE列舉
也就是輪廓的類型
這裡選用CV_RETR_EXTERNAL
表示只擷取外部輪廓(external contours)
若是物體內部有破洞(hole)則予以忽略
而FindContours方法的回傳值是一個點的集合
採用的是EmguCV的一種泛型類別Contour,並帶入Point的型別
Contour是一種link list的結構
所以透過一個for迴圈
不斷的指向下一個contours
並把contours畫在一張黑色的影像上
結果如下:
可以看到不論多細小的區域,都被框選出來了!!
輪廓擷取採用一種簡單的演算法
其中包含系統式的掃描影像
直到掃到物體(component)為止
並從這個點作為起始點,找出其他邊界的點
當一個輪廓完成時,又會從剛剛的位置繼續掃描。
找到的這些輪廓,可以個別的予以分析和討論
像是可以利用輪廓的大小、輪廓的周長等條件來過濾
例如利用輪廓的周長(也就是輪廓包含的點的數量)
//Draw white contours on a black image for (; contours != null; contours = contours.HNext) { if (contours.Total > 100 && contours.Total < 1000) { blackImage.Draw(contours, new Gray(255), 1); } }限制輪廓的點的數量介於100~1000之間
可以濾掉過小或過大的區域
結果如下:
可以看到排除掉很多雜訊的部分
剛剛的CV_RETR_EXTERNAL方法
是避免取出區域內空洞的部分
當然也可以利用不同的演算方法取出更多的輪廓
像是如果要取出全部的輪廓就可以利用CV_RETR_LIST
Contour<Point> contours = image.FindContours( //all pixels of each contours Emgu.CV.CvEnum.CHAIN_APPROX_METHOD.CV_CHAIN_APPROX_SIMPLE, //retrieve all contours Emgu.CV.CvEnum.RETR_TYPE.CV_RETR_LIST);
取出的結果如下:
可以與第一張影像作比較
乍看之下好像沒什麼差別
但是在沙灘的部分
仔細看的話可以發現更多的輪廓被取出
那也可以利用階層式的方法CV_RETR_TREE把輪廓取出
利用階層式的結構來表示輪廓之間的關係
如果某個大的輪廓中內還包含著小的空洞輪廓
那這些小的空洞輪廓就會位於這個大的輪廓的子集合(children)
如果這些小的空洞輪廓還包含更小的輪廓
則這些更小的輪廓就會位於這些小的空洞輪廓的子集合(children)
不斷的以此類推
以樹狀結構的方式來表示彼此的關係
而CV_RETR_CCOMP這個方法
則與上面的樹狀結構類似
但最多只取出兩層(level)的結構來表示
更小的就不予取出
可以自己多試試看這幾種方法
比較其中的差異
才能夠了解各個方法的不同之處
下一篇介紹透過一些方法來幫助運用及了解輪廓的形狀描述
沒有留言:
張貼留言