OpenCV示例學習筆記(1)-contours2.cpp-經過findContours 函數實現輪廓提取

這個系列的目的是經過對OpenCV示例,進一步瞭解OpenCV函數的使用,不涉及具體原理。
示例代碼地址: http://docs.opencv.org/3.0.0/examples.html(安裝openCV時可框選)
 
目錄
簡介
Example運行截圖
Example分析
Example代碼
 
簡介
本文記錄了對OpenCV示例contours2.cpp的分析。
資料地址: http://docs.opencv.org/3.0.0/d0/d2a/contours2_8cpp-example.html
 
這個示例主要演示瞭如何使用findContours 對圖像進行輪廓檢測。
示例涉及到findContours , approxPolyDP,drawContours,createTrackbar,和on_trackbar等四個函數的使用;
1.findContours函數 輪廓檢測 函數原型: void findContours(InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point());
 
參數說明:
image:輸入圖像必須爲一個2值單通道圖像
contours:檢測的輪廓數組,每個輪廓用一個point類型的vector表示
hiararchy:和輪廓個數相同,每一個輪廓contours[ i ]對應4個hierarchy元素hierarchy[ i ]                
      [0 ] ~hierarchy[ i ][ 3 ],分別表示後一個輪廓、前一個輪廓、父輪廓、內
      嵌輪廓的索引編號,若是沒有對應項,該值設置爲負數。
mode:表示輪廓的檢索模式     
    RETR_EXTERNAL表示只檢測外輪廓     
    RETR_LIST檢測的輪廓不創建等級關係     
    RETR_CCOMP創建兩個等級的輪廓,上面的一層爲外邊界,裏面的一層爲內孔的邊 界信息。若是內孔內還有一個連通物體,這個物體的邊界也在頂層。     
    RETR_TREE創建一個等級樹結構的輪廓。具體參考contours.c這個demo
method:爲輪廓的近似辦法
     CHAIN_APPROX_NONE存儲全部的輪廓點,相鄰的兩個點的像素位置差不超過1,即max(abs(x1-x2),abs(y2-y1))==1
     CHAIN_APPROX_SIMPLE壓縮水平方向,垂直方向,對角線方向的元素,只保留該方向的終點座標,例如一個矩形輪廓只需4個點來保存輪廓信息
     CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法
offset:表明輪廓點的偏移量,能夠設置爲任意值。對ROI圖像中找出的輪廓,並要在整個圖像中進行分析時,這個參數仍是頗有用的。
PS:findContours後會對輸入的2值圖像改變,因此若是不想改變該2值圖像,需建立新mat來存放。
以上描述摘至參考資料3
 
2.approxPolyDP
點集逼近
 
函數原型:
void approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed);

參數說明:html

InputArray curve:輸入的點集
OutputArray approxCurve:輸出的點集,當前點集是能最小包容指定點集的。draw出來便是一個多邊形;
double epsilon:指定的精度,也便是原始曲線與近似曲線之間的最大距離。
bool closed:若爲true,則說明近似曲線是閉合的,它的首位都是相連,反之,若爲false,則斷開。
PS:findContours後的輪廓信息contours可能過於複雜不平滑,能夠用approxPolyDP函數對該多邊形曲線作適當近似。
以上描述摘至參考資料4  
 
3.drawContours
繪製輪廓
 
函數原型:
void drawContours(InputOutputArray image, InputArrayOfArrays contours, int contourIdx, const Scalar& color, int thickness=1, int lineType=8, InputArray hierarchy=noArray(), int maxLevel=INT_MAX, Point offset=Point() )
 
參數說明:
InputOutputArray image:要繪製輪廓的圖像
InputArrayOfArrays contours:全部輸入的輪廓,每一個輪廓被保存成一個point向量
int contourIdx:指定要繪製輪廓的編號,若是是負數,則繪製全部的輪廓
const Scalar& color:繪製輪廓所用的顏色
int thickness=1:繪製輪廓的線的粗細,若是是負數,則輪廓內部被填充
int lineType=8:繪製輪廓的線的連通性
InputArray hierarchy=noArray():關於層級的可選參數,只有繪製部分輪廓時纔會用到
int maxLevel=INT_MAX:繪製輪廓的最高級別,這個參數只有hierarchy有效的時候纔有效

                                           maxLevel=0,繪製與輸入輪廓屬於同一等級的全部輪廓即輸入輪廓和與其相鄰的輪廓
                                           maxLevel=1, 繪製與輸入輪廓同一等級的全部輪廓與其子節點。
                                           maxLevel=2,繪製與輸入輪廓同一等級的全部輪廓與其子節點以及子節點的子節點ios

Point offset=Point():

PS:findContours()運行的時候,這個圖像會被直接塗改,所以若是是未來還有用的圖像,應該複製以後再傳給findContours()。算法

以上描述摘至參考資料5        
 
4.createTrackbar
窗口中快速建立一個滑動控件,用於手動調節閾值,具備很是直觀的效果。
函數原型:
int createTrackbar(const string& trackbarname, const string& winname,
                              int* value, int count,
                              TrackbarCallback onChange = 0,
                              void* userdata = 0);
 
參數說明:

trackbarname:滑動空間的名稱;數組

winname:滑動空間用於依附的圖像窗口的名稱;app

value:初始化閾值;ide

count:滑動控件的刻度範圍;函數

TrackbarCallback是回調函數。測試

 
5 .TrackbarCallback
createTrackbar調用的回調函數,用於響應滑動條的交互消息。
 
函數原型:
typedef void (CV_CDECL *TrackbarCallback)(int pos, void* userdata);  
 
參數說明:
pos:滑動條當前位置;
userdata:調用滑動條傳遞的數據。
 
PS:事實上pos和userdata用得很是少,更多的時候直接使用全局變量完成參數傳遞。
           
Example運行截圖
 原圖
 
 
 效果圖

 

取消approxPolyDP調用ui

 

Example分析spa

1.申明須要使用的變量

const int w = 500;
int levels = 3;
 
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
 
注意:
(1)w:圖像的寬度和高度
(2)levels:繪製輪廓的級別和數量
(3)contours:保存輪廓
(4)hierarchy:層級參數
 
2.主函數
2.1建立一個灰度圖像
Mat img = Mat::zeros(w, w, CV_8UC1);
 
2.2.繪製6我的臉用於測試
forint i = 0; i < 6; i++ )
    {
        int dx = (i%2)*250 - 30;
        int dy = (i/2)*150;
        const Scalar white = Scalar(255);
        const Scalar black = Scalar(0);
 
        if( i == 0 )
        {
            forint j = 0; j <= 10; j++ )
            {
                double angle = (j+5)*CV_PI/21;
                line(img, Point(cvRound(dx+100+j*10-80*cos(angle)),
                    cvRound(dy+100-90*sin(angle))),
                    Point(cvRound(dx+100+j*10-30*cos(angle)),
                    cvRound(dy+100-30*sin(angle))), white, 180);
            }
        }
 
        ellipse( img, Point(dx+150, dy+100), Size(100,70), 00360, white, -180 );
        ellipse( img, Point(dx+115, dy+70), Size(30,20), 00360, black, -180 );
        ellipse( img, Point(dx+185, dy+70), Size(30,20), 00360, black, -180 );
        ellipse( img, Point(dx+115, dy+70), Size(15,15), 00360, white, -180 );
        ellipse( img, Point(dx+185, dy+70), Size(15,15), 00360, white, -180 );
        ellipse( img, Point(dx+115, dy+70), Size(5,5), 00360, black, -180 );
        ellipse( img, Point(dx+185, dy+70), Size(5,5), 00360, black, -180 );
        ellipse( img, Point(dx+150, dy+100), Size(10,5), 00360, black, -180 );
        ellipse( img, Point(dx+150, dy+150), Size(40,10), 00360, black, -180 );
        ellipse( img, Point(dx+27, dy+100), Size(20,35), 00360, white, -180 );
        ellipse( img, Point(dx+273, dy+100), Size(20,35), 00360, white, -180 );
    }
View Code

 

2.3.建立顯示源圖像的窗口

namedWindow( "image", 1 );
 
2.4.顯示源圖像
imshow( "image", img );
 
2.5.查找輪廓
vector<vector<Point> > contours0;
findContours( img, contours0, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
 
2.6.對輪廓進行逼近
contours.resize(contours0.size());
for( size_t k = 0; k < contours0.size(); k++ )
      approxPolyDP(Mat(contours0[k]), contours[k], 3, true);
 
2.7.建立顯示目標圖像(繪製輪廓圖像)的窗口
namedWindow( "contours", 1 );
 
2.8.爲目標圖像預覽窗口建立滑動條
createTrackbar( "levels+3", "contours", &levels, 7, on_trackbar );
 
注意:
爲窗口添加滑動條,在OpenCV研發中很是廣泛和實用,其大體步驟以下:
step1: 參數全局變量的聲明與初始化;
step2:聲明存儲圖像的全局變量;
step3:定義響應滑動條的回調函數,根據參數全局變量,和圖像全局變量,以及圖像算法進行處理,並顯示;
step4:建立顯示圖像的窗口;
step5:在函數中使用createTrackbar建立滑動條;
step6:在函數中手動調用回調函數on_trackbar 一次,用於初始化。
 
2.9.手動調用
on_trackbar(0,0);
 
2.10等待鍵盤事件
waitKey();
 
注意:
在OpenCV中經常使用waitKey函數,由於顯示圖像後,須要駐留代碼。以便於觀察和操做。
 
3.on_trackbar函數分析
3.1建立目標圖像
Mat cnt_img = Mat::zeros(w, w, CV_8UC3);
 
3.2處理參數
int _levels = levels - 3;
 
3.3繪製輪廓
drawContours( cnt_img, contours, _levels <= 0 ? 3 : -1, Scalar(128,255,255),
                  3, LINE_AA, hierarchy, std::abs(_levels) );
 
3.4顯示目標圖像
imshow("contours", cnt_img);
 
Example代碼
 1 #include "opencv2/imgproc/imgproc.hpp"
 2 #include "opencv2/highgui/highgui.hpp"
 3 #include <math.h>
 4 #include <iostream>
 5 
 6 using namespace cv;
 7 using namespace std;
 8 
 9 static void help()
10 {
11     cout
12     << "\nThis program illustrates the use of findContours and drawContours\n"
13     << "The original image is put up along with the image of drawn contours\n"
14     << "Usage:\n"
15     << "./contours2\n"
16     << "\nA trackbar is put up which controls the contour level from -3 to 3\n"
17     << endl;
18 }
19 
20 const int w = 500;
21 int levels = 3;
22 
23 vector<vector<Point> > contours;
24 vector<Vec4i> hierarchy;
25 
26 static void on_trackbar(int, void*)
27 {
28     Mat cnt_img = Mat::zeros(w, w, CV_8UC3);
29     int _levels = levels - 3;
30     drawContours( cnt_img, contours, _levels <= 0 ? 3 : -1, Scalar(128,255,255),
31                   3, LINE_AA, hierarchy, std::abs(_levels) );
32 
33     imshow("contours", cnt_img);
34 }
35 
36 int main( int argc, char**)
37 {
38     Mat img = Mat::zeros(w, w, CV_8UC1);
39     if(argc > 1)
40     {
41         help();
42         return -1;
43     }
44     //Draw 6 faces
45     for( int i = 0; i < 6; i++ )
46     {
47         int dx = (i%2)*250 - 30;
48         int dy = (i/2)*150;
49         const Scalar white = Scalar(255);
50         const Scalar black = Scalar(0);
51 
52         if( i == 0 )
53         {
54             for( int j = 0; j <= 10; j++ )
55             {
56                 double angle = (j+5)*CV_PI/21;
57                 line(img, Point(cvRound(dx+100+j*10-80*cos(angle)),
58                     cvRound(dy+100-90*sin(angle))),
59                     Point(cvRound(dx+100+j*10-30*cos(angle)),
60                     cvRound(dy+100-30*sin(angle))), white, 1, 8, 0);
61             }
62         }
63 
64         ellipse( img, Point(dx+150, dy+100), Size(100,70), 0, 0, 360, white, -1, 8, 0 );
65         ellipse( img, Point(dx+115, dy+70), Size(30,20), 0, 0, 360, black, -1, 8, 0 );
66         ellipse( img, Point(dx+185, dy+70), Size(30,20), 0, 0, 360, black, -1, 8, 0 );
67         ellipse( img, Point(dx+115, dy+70), Size(15,15), 0, 0, 360, white, -1, 8, 0 );
68         ellipse( img, Point(dx+185, dy+70), Size(15,15), 0, 0, 360, white, -1, 8, 0 );
69         ellipse( img, Point(dx+115, dy+70), Size(5,5), 0, 0, 360, black, -1, 8, 0 );
70         ellipse( img, Point(dx+185, dy+70), Size(5,5), 0, 0, 360, black, -1, 8, 0 );
71         ellipse( img, Point(dx+150, dy+100), Size(10,5), 0, 0, 360, black, -1, 8, 0 );
72         ellipse( img, Point(dx+150, dy+150), Size(40,10), 0, 0, 360, black, -1, 8, 0 );
73         ellipse( img, Point(dx+27, dy+100), Size(20,35), 0, 0, 360, white, -1, 8, 0 );
74         ellipse( img, Point(dx+273, dy+100), Size(20,35), 0, 0, 360, white, -1, 8, 0 );
75     }
76     //show the faces
77     namedWindow( "image", 1 );
78     imshow( "image", img );
79     //Extract the contours so that
80     vector<vector<Point> > contours0;
81     findContours( img, contours0, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
82 
83     contours.resize(contours0.size());
84     for( size_t k = 0; k < contours0.size(); k++ )
85         approxPolyDP(Mat(contours0[k]), contours[k], 3, true);
86 
87     namedWindow( "contours", 1 );
88     createTrackbar( "levels+3", "contours", &levels, 7, on_trackbar );
89 
90     on_trackbar(0,0);
91     waitKey();
92 
93     return 0;
94 }
View Code

 

 
參考資料:
1.《opencv 例程四 尋找輪廓》 http://blog.sina.com.cn/s/blog_662c78590100z0rg.html
2.《實用OpenCV》(六) 圖像中的形狀(1)》 http://www.2cto.com/kf/201401/270283.html
3.《findContours函數參數說明及相關函數》 http://blog.sina.com.cn/s/blog_7155fb1a0101a90h.html
相關文章
相關標籤/搜索