2013年4月7日 星期日

EmguCV Image Process: Extracting Lines, Contours, and Components part 5

20060907001.jpg
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)的結構來表示

更小的就不予取出


可以自己多試試看這幾種方法

比較其中的差異

才能夠了解各個方法的不同之處

下一篇介紹透過一些方法來幫助運用及了解輪廓的形狀描述

沒有留言:

張貼留言