輸入圖像
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 這部分還真的准備了很多寶貝。