findContours 輪廓檢測(cè)

1. findContours 找輪廓

void findContours( InputOutputArray image, OutputArrayOfArrays contours,
                              OutputArray hierarchy, int mode,
                              int method, Point offset = Point());
  • InputOutputArray image:輸入圖像是8位單通道的圖像(256級(jí)灰度圖)漂彤。

  • 其中像素點(diǎn)的非0灰度值被當(dāng)成1(轉(zhuǎn)化后即為255)榄攀,0值保持0,所以輸入圖像被當(dāng)成一個(gè)二值圖像對(duì)待。

  • 可以用 compare() , inRange() , threshold() , adaptiveThreshold() , Canny() 或者其他方法來從灰度圖或者彩色圖中生成二值圖像。該函數(shù)在提取輪廓的過程中會(huì)改變圖像

  • 如果第4個(gè)參數(shù) mode 為 CV_RETR_CCOMP 或者
    CV_RETR_FLOODFILL淤井,輸入圖像也可以是32位的整型圖像(CV_32SC1)。

  • OutputArrayOfArrays contours: 檢測(cè)到的輪廓

  • Each contour is stored as a vector of points. 每個(gè)輪廓會(huì)被存儲(chǔ)為vector<Point>

  • 所以 contours 的類型是vector<vector<Point>>摊趾。

  • OutputArray hierarchy: 可選的輸出向量币狠,包含圖像的拓?fù)湫畔?/p>

  • It has as many elements as the number of contours. 元素個(gè)數(shù) = 輪廓數(shù)

  • 對(duì)于第 i 個(gè)輪廓contours[i],hierarchy 的以下元素分別表示

hierarchy[i][0]: the next contour at the same hierarchical level
hierarchy[i][1]: the previous contour at the same hierarchical level
hierarchy[i][2]: the first child contour
hierarchy[i][3]: the parent contour
  • hierarchy 的這些元素的原始值為0砾层,如果不存在漩绵,置為負(fù)數(shù)

  • int mode: Contour retrieval mode 取回輪廓模式(復(fù)雜度依次增加)

  • CV_RETR_EXTERNAL retrieves only the extreme outer contours.
    It sets hierarchy[i][2]=hierarchy[i][3]=-1 for all the contours.
    只取回最外側(cè)輪廓

  • CV_RETR_LIST retrieves all of the contours without establishing any hierarchical relationships.
    取回所有輪廓,但是不建立層次關(guān)系

  • CV_RETR_CCOMP retrieves all of the contours and organizes them into a two-level hierarchy. At the top level, there are external boundaries of the components. At the second level, there are boundaries of the holes. If there is another contour inside a hole of a connected component, it is still put at the top level.
    取回所有輪廓并組織成為兩層
    頂層是外部邊界肛炮,第二層是洞的邊界
    如果有另外一個(gè)輪廓在一個(gè)連通部分的洞中止吐,放到頂層

  • CV_RETR_TREE retrieves all of the contours and reconstructs a full hierarchy of nested contours.
    取回所有輪廓并生成完整的嵌套的輪廓層次結(jié)構(gòu)

  • int method: Contour approximation method 輪廓估計(jì)方法

  • CV_CHAIN_APPROX_NONE stores absolutely all the contour points. That is, any 2 subsequent points (x1,y1) and (x2,y2) of the contour will be either horizontal, vertical or diagonal neighbors.
    that is, max(abs(x1-x2),abs(y2-y1))==1.
    存儲(chǔ)輪廓內(nèi)所有相鄰(水平宝踪、垂直、對(duì)角)的點(diǎn)
  • CV_CHAIN_APPROX_SIMPLE compresses horizontal, vertical, and diagonal segments and leaves only their end points. For example, an up-right rectangular contour is encoded with 4 points.
    壓縮輪廓內(nèi)(水平碍扔、垂直瘩燥、對(duì)角)的點(diǎn),只存儲(chǔ)邊界點(diǎn)不同,比如矩形就只存儲(chǔ)4個(gè)點(diǎn)颤芬。

  • CV_CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS applies one of the flavors of the Teh-Chin chain approximation algorithm. See [TehChin89] for details.

  • Point offset = Point() 可選的輪廓偏移

  • This is useful if the contours are extracted from the image ROI and then they should be analyzed in the whole image context. 從 ROI 轉(zhuǎn)移到 全局圖片

2. drawContours 畫輪廓

void drawContours( InputOutputArray image, InputArrayOfArrays contours,
                              int contourIdx, const Scalar& color,
                              int thickness = 1, int lineType = LINE_8,
                              InputArray hierarchy = noArray(),
                              int maxLevel = INT_MAX, Point offset = Point() );
  • InputOutputArray image Destination image.
  • InputArrayOfArrays contours All the input contours. Each contour is stored as a point vector(頂點(diǎn)向量)
  • int contourIdx Parameter indicating a contour to draw. If it is negative, all the contours are drawn. 如果為負(fù),所有的輪廓已經(jīng)畫出
  • const Scalar& color Color of the contours. 輪廓顏色
  • int thickness = 1 Thickness of lines the contours are drawn with. 輪廓寬度
    If it is negative (for example, thickness=CV_FILLED), the contour interiors are drawn.
  • int lineType = LINE_8 Line connectivity. See line() for details.
  • InputArray hierarchy = noArray() Optional information about hierarchy. It is only needed if you want to draw only some of the contours (see maxLevel ).
  • int maxLevel = INT_MAX Maximal level for drawn contours. If it is 0, only the specified contour is drawn. If it is 1, the function draws the contour(s) and all the nested contours. If it is 2, the function draws the contours, all the nested contours, all the nested-to-nested contours, and so on. This parameter is only taken into account when there is hierarchy available.
  • Point offset = Point() Optional contour shift parameter. Shift all the drawn contours by the specified offset=(dx,dy).

3. 實(shí)例代碼

核心部分是回調(diào)函數(shù)

void on_trackbar(int, void *)
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <iomanip>

using namespace cv;
using namespace std;

Mat img;
int threshval = 160; // 軌跡條滑塊對(duì)應(yīng)的值套鹅,給初值160,命名這里用threshold會(huì)ambiguous

// 掃描灰度圖像矩陣
void scanImageMatrixG(Mat &img) {
    for (int i = 0; i < 10; ++i) {
        for (int j = 0; j < 10; ++j) {
            cout << setw(3) << (int) img.at<uchar>(i, j) << ", ";
        }
        cout << endl;
    }
    cout << endl << endl;
}

void on_trackbar(int, void *) {

    // 通過 threshval 二值化圖像
    // threshval = 160 > 128, 選擇 img > threshval
    // img 像素值 >160, 取 255汰具,<=160卓鹿,取 0
    Mat threshImage = threshval < 128 ? (img < threshval) : (img > threshval);

    cout << getTrackbarPos("threshold", "trackbar") << endl;
    scanImageMatrixG(threshImage);

    // 輸出的點(diǎn)的位置
    vector<vector<Point>> contours; // 二維的點(diǎn) { {[1,1], [1,2]}, ... }

    // 輸出圖像有4個(gè)通道
    vector<Vec4i> hierarchy; // vector<Vec<int, 4>> { { [1,2,3,4], [1,1,1,1], ... }, ... }

    // 尋找輪廓
    // CV_RETR_CCOMP: 取回輪廓采用兩層結(jié)構(gòu)
    // CV_CHAIN_APPROX_SIMPLE: 輪廓估計(jì)采用壓縮輪廓
    findContours(threshImage, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);

    // 必須用zeros初始化dst
    // 回調(diào)函數(shù)每次調(diào)用目標(biāo)矩陣dst都必須是個(gè)空矩陣
    // 不然會(huì)在上一個(gè)矩陣的基礎(chǔ)上疊加值
    Mat dst = Mat::zeros(img.size(), CV_8UC3); // 類方法

    // 輪廓上色
    if (!contours.empty() && !hierarchy.empty()) {
        // 遍歷所有頂層輪廓,頂層體現(xiàn)在 hierarchy[idx][0]留荔,同層
        // 隨機(jī)生成顏色值繪制給各連接組成部分
        int idx = 0;
        for (; idx >= 0; idx = hierarchy[idx][0]) { // hierarchy[idx][0]: the next contour at the same hierarchical level
            Scalar color((rand() & 255), (rand() & 255), (rand() & 255)); // 隨機(jī)色
            drawContours(dst, contours, idx, color, CV_FILLED, 8, hierarchy); // 繪制填充輪廓
        }
    }
    
    // 顯示輪廓檢測(cè)后的圖像
    imshow("trackbar", dst);
}


int main() {

    // 讀入灰度圖
    img = imread("../pictures/pig.jpg", 0);

    // 顯示原圖
    namedWindow("img");
    imshow("img", img);

    // 原圖灰度矩陣
    scanImageMatrixG(img);

    // 創(chuàng)建處理窗口
    namedWindow("trackbar");

    // 創(chuàng)建軌跡條
    createTrackbar("threshold", "trackbar", &threshval, 255, on_trackbar);

    // 軌跡條回調(diào)函數(shù)
    on_trackbar(threshval, 0); // threshval: 軌跡條位置吟孙,是全局變量,userdata = 0

    waitKey(0);
    return 0;
}
(1)threshold = 160

原圖灰度矩陣 & 二值矩陣 threshImage

 64,  65,  66,  68,  68,  68,  68,  67,  58,  63, 
 63,  64,  66,  67,  68,  68,  67,  67,  58,  62, 
 65,  67,  68,  70,  71,  71,  70,  70,  65,  68, 
 75,  76,  78,  80,  81,  81,  81,  80,  82,  85, 
 94,  96,  97,  99, 101, 101, 101, 100, 103, 105, 
120, 121, 123, 125, 127, 127, 127, 127, 124, 126, 
145, 146, 148, 151, 152, 153, 153, 153, 145, 147, 
160, 162, 164, 166, 168, 168, 168, 168, 162, 163, 
170, 171, 173, 175, 176, 176, 176, 175, 175, 175, 
174, 176, 178, 180, 182, 183, 183, 183, 181, 181, 


160
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 
(2)threshold = 45

二值矩陣 threshImage

45
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0,

因?yàn)榕卸ǚ椒?/p>

Mat threshImage = threshval < 128 ? (img < threshval) : (img > threshval);

45 < 128, 選擇 img < threshval聚蝶,而像素值全部 >45杰妓,所以全為 0

(3)threshold = 200

二值矩陣 threshImage

200
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 
  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, 

因?yàn)榕卸ǚ椒?/p>

Mat threshImage = threshval < 128 ? (img < threshval) : (img > threshval);

200 > 128, 選擇 img > threshval,而像素值全部 <200碘勉,所以全為 0

4. 總結(jié)

輪廓檢測(cè)的步驟:

  1. imread("pic", 0) 讀入灰度圖
img = imread("../pictures/pig.jpg", 0);
  1. threshold 將灰度圖分為二值圖 (閾值影響著輪廓檢測(cè)精確度)
Mat threshImage = threshval < 128 ? (img < threshval) : (img > threshval);
  1. findContours 尋找圖像輪廓
// 尋找輪廓
// CV_RETR_CCOMP: 取回輪廓采用兩層結(jié)構(gòu)
// CV_CHAIN_APPROX_SIMPLE: 輪廓估計(jì)采用壓縮輪廓
findContours(threshImage, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
  1. drawContours 畫出找到的輪廓
// 輪廓上色
if (!contours.empty() && !hierarchy.empty()) {
    // 遍歷所有頂層輪廓巷挥,頂層體現(xiàn)在 hierarchy[idx][0],同層
    // 隨機(jī)生成顏色值繪制給各連接組成部分
    int idx = 0;
    for (; idx >= 0; idx = hierarchy[idx][0]) { // hierarchy[idx][0]: the next contour at the same hierarchical level
        Scalar color((rand() & 255), (rand() & 255), (rand() & 255)); // 隨機(jī)色
        drawContours(dst, contours, idx, color, CV_FILLED, 8, hierarchy); // 繪制填充輪廓
    }
}

輪廓上色用隨機(jī)色的好處:一種顏色就代表了一個(gè)輪廓

如果用單一的顏色验靡,比如白色

Scalar color(255, 255, 255); // 白色

那么所有的輪廓就不能區(qū)分開了

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末倍宾,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子胜嗓,更是在濱河造成了極大的恐慌高职,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,013評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辞州,死亡現(xiàn)場(chǎng)離奇詭異怔锌,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)变过,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,205評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門埃元,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人媚狰,你說我怎么就攤上這事亚情。” “怎么了哈雏?”我有些...
    開封第一講書人閱讀 152,370評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵楞件,是天一觀的道長(zhǎng)衫生。 經(jīng)常有香客問我,道長(zhǎng)土浸,這世上最難降的妖魔是什么罪针? 我笑而不...
    開封第一講書人閱讀 55,168評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮黄伊,結(jié)果婚禮上泪酱,老公的妹妹穿的比我還像新娘。我一直安慰自己还最,他們只是感情好墓阀,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,153評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著斯撮,像睡著了一般。 火紅的嫁衣襯著肌膚如雪扶叉。 梳的紋絲不亂的頭發(fā)上勿锅,一...
    開封第一講書人閱讀 48,954評(píng)論 1 283
  • 那天,我揣著相機(jī)與錄音枣氧,去河邊找鬼溢十。 笑死,一個(gè)胖子當(dāng)著我的面吹牛达吞,可吹牛的內(nèi)容都是我干的张弛。 我是一名探鬼主播,決...
    沈念sama閱讀 38,271評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼酪劫,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼乌庶!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起契耿,我...
    開封第一講書人閱讀 36,916評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤瞒大,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后搪桂,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體透敌,經(jīng)...
    沈念sama閱讀 43,382評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,877評(píng)論 2 323
  • 正文 我和宋清朗相戀三年踢械,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了酗电。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 37,989評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡内列,死狀恐怖撵术,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情话瞧,我是刑警寧澤嫩与,帶...
    沈念sama閱讀 33,624評(píng)論 4 322
  • 正文 年R本政府宣布寝姿,位于F島的核電站,受9級(jí)特大地震影響划滋,放射性物質(zhì)發(fā)生泄漏饵筑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,209評(píng)論 3 307
  • 文/蒙蒙 一处坪、第九天 我趴在偏房一處隱蔽的房頂上張望根资。 院中可真熱鬧,春花似錦同窘、人聲如沸玄帕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,199評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽裤纹。三九已至,卻和暖如春案狠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背钱雷。 一陣腳步聲響...
    開封第一講書人閱讀 31,418評(píng)論 1 260
  • 我被黑心中介騙來泰國(guó)打工骂铁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人罩抗。 一個(gè)月前我還...
    沈念sama閱讀 45,401評(píng)論 2 352
  • 正文 我出身青樓拉庵,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親套蒂。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钞支,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,700評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • Opencv 輪廓檢測(cè)相關(guān)api文檔 opencv2的c++接口 官方文檔相關(guān)api Finds contours...
    東林鐘聲閱讀 41,128評(píng)論 2 16
  • 我們今天要講一個(gè)關(guān)于狐貍的故事。 森林里的小動(dòng)物總是懼怕老虎操刀,不屑于狐貍的狡猾烁挟,狐貍因此非常郁悶。在他看來骨坑,老虎并...
    紙上人生閱讀 1,022評(píng)論 0 4
  • 2點(diǎn)半閱讀 146評(píng)論 0 0
  • 時(shí)至今日的閱讀已不再墨守著紙介欢唾,傳統(tǒng)書籍且警、微博微信、QQ日志礁遣、電子書斑芜,豐盛而輕盈的方式提供了無限便捷的閱讀天地,當(dāng)...
    舒天閱讀 638評(píng)論 0 1