OpenCV 使用 findContours 函數 Note1: 利用各個輪廓的相互階層(hierarchy)關系


輸入圖像

void Detect_Object(Mat img)
{
    Mat gray, binaryIMG;
    Mat correct_IMG;

    cvtColor(img, gray, CV_BGR2GRAY);
    Canny(gray, binaryIMG, 90, 180);
    blur(binaryIMG, binaryIMG, Size(3, 3));

    // Find contours
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;
    double TargetArea = 0;
    findContours(binaryIMG, contours, hierarchy, RETR_TREE, CV_CHAIN_APPROX_NONE, Point(0, 0));

    for (int i = 0; i < contours.size(); i++)
    {
        drawContours(img, contours, i, Scalar(0, 255, 0), 2, 8, hierarchy, 0, Point());
    }
}

先上代碼, 上述函數是尋找一個圖像內的所有輪廓。

執行后,利用

drawContours(img, contours, i, Scalar(0, 255, 0), 2, 8, hierarchy, 0, Point());

 這個函數,會畫出所有圖像內的輪廓。

執行上述代碼后,我的代碼上顯示 contour.size() ,即輪廓個數是34個。 這個輪廓個數不是絕對的, 結果由你對canny()選取的閾值, blur() mask的大小選取都有影響。

Canny 和 Blurring 算法結果圖像。

 

紅色顯示的是 FindContours函數所檢測到的輪廓。

 

你或許會覺得,最終顯示的結果像一幅捕捉邊緣的圖像,即Edge Map。也會不解,有了Canny,何須多此一舉在尋找輪廓。

接下來我就要介紹本章的主角輪廓 Hierarchy的階層關系。

先看一段修改后的代碼:

    for (int i = 0; i < contours.size(); i++)
    {
        if (hierarchy[i][3] == -1)
            {
                drawContours(img, contours, i, Scalar(0, 0, 255), 1, 8, hierarchy, 0, Point());
            }
    }

 

這段代碼中,多了一段條件語句,語句里寫了 

hierarchy[i][3] == -1

首先,我們確認一下結果:

這次的結果里,只檢測出了最外圍輪廓。很明顯

hierarchy[i][3] == -1 起了決定性的作用。 

我們先看看OpenCV官方文件是怎么寫的。

OpenCV represents it as an array of four values : [Next, Previous, First_Child, Parent].   (Link me: https://docs.opencv.org/3.4.0/d9/d8b/tutorial_py_contours_hierarchy.html)

 

hierarchy[i][3] == -1, 這里的hierarchy[i]指的是第i個輪廓的階層關系。而hierarchy[i][0],hierarchy[i][1],hierarchy[i][2],hierarchy[i][3]分別指的是Next, Previous, First_child, Parent。

我們設定的hierarchy[i][3]== -1 的意思就是 “輪廓沒有父母”, 即“這個輪廓沒有上層階級的輪廓”。

同理, hierarchy[i][2]== -1 的意思就是 “此輪廓沒有第一個孩子”,即“此輪廓沒有下層階級的輪廓”。

至於,hierarchy[][0],hierarchy[][1] 指的是此輪廓的后一個輪廓,和前一個輪廓。他們都是同一個階級的輪廓。這個前后順序可能是很隨意的,至今還沒找到規律。所以還沒有到怎么利用他們。

所以,
hierarchy[i][3]== -1 條件下的結果, 是選中了沒有父母的輪廓,即他的外圍沒有包圍他的輪廓。

再看看
hierarchy[i][2]== -1 條件下的結果:

 

和預想的一樣,它只標出了沒有“孩子”的輪廓,即此輪廓內沒有更小的輪廓。

對了,我要強調一下,這個例子里我在使用FindContours函數的時候,我用了RETR_TREE 模式。這個模式是“萬能的”,把圖像內各輪廓的親屬關系都聯系上了。簡而言之,你能知道一個輪廓的”爺爺奶奶“,”孫子孫女“。甚至更深的祖輩關系。

opencv 提供了各種模式,RETR_LIST, RETR_EXTERNAL等。 上面的官方文件有詳細說明。
RETR_LIST 就是這個圖像的輪廓只可能是兩個階層的其中之一,要么你就是爹, 要么你就是兒子。本章只講RETR_TREE。

這時候你可能還沒領會到Hierarchy的魅力。 他不會馬上幫你挑出你最想要的信息,但是他確實個幫你排除“雜質”幫手。

例如, 我們這次測試的Heliport圖像, 很明顯這個圖像里的“H”是叫無人機去識別, 並且在其中心降落的。 那我們怎么去識別他呢? 如果不用Deep Learning。

我們可以在這個圖像里找沒有孩子的輪廓,但沒有孩子的輪廓候補也很多。怎么辦?
很簡單, 選取輪廓面積最大的,或者大於一定面積以上的,其實方法真的很多。只要你多加一個條件語句。

簡單的看下代碼:
    for (int i = 0; i < contours.size(); i++)
    {
        if (hierarchy[i][2] == -1 && contourArea(contours[i]) > 9800)
            {
                drawContours(img, contours, i, Scalar(0, 0, 255), 2, 8, hierarchy, 0, Point());

            }
    }

 

我加了一段 面積大於 9800 像素的條件。結果就找到了 “H”。

 

你問我怎么算的9800? 估算的, 但是我想說的不是面積這部分,利用面積也只是方法之一。 想要更精確確定的方法有很多。比如是高和寬的比例。

今天主要是為了介紹hierarchy的魅力,其實利用好真的能排除很多沒用的信息。hierarchy 只是OpenCV Contours 這部分的魅力之一, 仔細翻閱官方文件你會發現,opencv 這部分還真的准備了很多寶貝。

 

 

 
        
 
        

 

 

 
          
        

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM