OpenCV学习笔记(十七):查找并绘制轮廓:findContours(),drawContours(),approxPolyDP()
OpenCV學習筆記(十七):查找并繪制輪廓:findContours()
1、findContours() 函數
該函數使用Suzuki85算法從二值圖像中檢索輪廓。輪廓線是一種用于形狀分析、目標檢測和識別的有效工具。
opencv輪廓檢測之FindContours函數算法解釋
該函數從二值圖像中檢索輪廓,并返回檢索到的輪廓數。函數將填充指針first_contour。它將包含指向第一個最外層輪廓的指針,如果沒有檢測到輪廓,則為空(如果圖像是完全黑色的)??梢允褂胔_next和v_next鏈接從first_contour到達其他輪廓。繪制等值線討論中的示例說明了如何使用等值線進行連接組件檢測。輪廓還可以用于形狀分析和對象識別-請參閱OpenCV示例目錄中的squares.c。
2、drawContours()函數
該函數使用算法從二值圖像中檢索輪廓。繪制輪廓線或填充輪廓線。如果厚度≥0,該函數在圖像中繪制輪廓輪廓;如果厚度<0,則填充輪廓邊界區域。
參考博客:
輪廓的層級關系詳解
OpenCV中findcontours函數hierarchy輪廓層級詳解
3、approxPolyDP()函數
以指定的精度近似生成多邊形曲線。
函數逼近一條曲線或另一條曲線/頂點較少的多邊形,使它們之間的距離小于或等于指定的精度。它使用Douglas-Peucker算法
4、示例一:
#include <opencv2/opencv.hpp>using namespace cv; using namespace std;int main() {QCoreApplication a(argc, argv);// 1、加載源圖像//Mat srcImage = imread( "F:/C++/2. OPENCV 3.1.0/TEST/a.jpg", 1 );Mat srcImage = imread( "F:/C++/2. OPENCV 3.1.0/TEST/test3.png", 1 );if(!srcImage.data ) { printf("讀取圖片錯誤,請確定目錄下是否有imread函數指定圖片存在~! \n"); return false; }cvtColor(srcImage,srcImage,CV_BGR2GRAY);// 2、初始化結果圖Mat dstImage = Mat::zeros(srcImage.size(), CV_8UC3);Mat dstImage1 =dstImage.clone();// 3、srcImage取大于閾值119的那部分//threshold(srcImage,srcImage,119,255,THRESH_BINARY);srcImage = srcImage > 119; // 取 閾值大于119imshow( "取閾值后的原始圖", srcImage );// 4、定義輪廓和層次結構// 儲存所有輪廓(每個輪廓為一個點向量集)// 儲存每個輪廓的層次元素,hierarchy[i][0]~hierarchy[i][1] 分別表示后一個、前一個、父輪廓、內嵌輪廓的索引編號vector<vector<Point> > contours;vector<Vec4i> hierarchy;// 5、查找輪廓// RETR_EXTERNAL 表示只檢測外輪廓// RETR_LIST 檢測的輪廓不建立等級關系// RETR_CCOMP 建立兩個等級的輪廓,上面的一層為外邊界,里面的一層為內孔的邊 界信息。如果內孔內還有一個連通物體,這個物體的邊界也在頂層。// RETR_TREE 建立一個等級樹結構的輪廓。具體參考contours.c這個demo// 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 近似算法findContours( srcImage, contours, hierarchy,CV_RETR_CCOMP ,CV_CHAIN_APPROX_SIMPLE );cout<<"輪廓數量:"<<contours.size()<<endl;// 6、遍歷所有頂層的輪廓, 以隨機顏色繪制出每個連接組件顏色// hierarchy[index][0] 表示該某輪廓點集的后一個輪廓點集 索引號// hierarchy[index][1] 表示該某輪廓點集的前一個輪廓點集 索引號// hierarchy[index][2] 表示該某輪廓點集的第一個子輪廓(內嵌)點集 索引號// hierarchy[index][3] 表示該某輪廓點集的父輪廓點集 索引號for( int index = 0; index >= 0; index = hierarchy[index][0] ) // 繪制輪廓//for( int index = 0; index < contours.size(); index ++ ) // 繪制所有內外輪廓{Scalar color( rand()&255, rand()&255, rand()&255 );drawContours( dstImage, contours, index, color, 1, 8,hierarchy ); //CV_FILLED}// // 7、檢測到的所有輪廓分別用不同顏色畫出來 // for(size_t j=0; j<contours.size(); j++) // { // Scalar color( rand()&255, rand()&255, rand()&255 ); // 為每個輪廓新建隨機顏色 // for (size_t i = 0; i < contours[j].size(); i++) // { // // 繪制邊緣 // line(dstImage1, contours[j][i], contours[j][(i + 1) % contours[j].size()], color, 1, 8); // } // }// 8、顯示最后的輪廓圖imshow( "輪廓圖", dstImage );//imshow( "邊緣圖", dstImage1 );waitKey(0);return 0; }結果:
分析:
1)“0輪廓"的下一條輪廓是"1輪廓”;“1輪廓"的下一條"3輪廓”;
2)"2輪廓"和"3輪廓"的下一條沒有;
3)"0輪廓"和"2輪廓"上一條是-1表示沒有;
4)“1輪廓"的子輪廓是"2輪廓”,其他的輪廓沒有子輪廓。(1輪廓包含2輪廓)
5)"0、1、3輪廓"沒有父輪廓,說明其為最外層輪廓(且為同級輪廓)
PS:輪廓層次關系 看hierarchy[index][2]和hierarchy[index][3]的值
5、示例二:
#include <opencv2/opencv.hpp>using namespace cv; using namespace std;int levels = 3; // 精度等級 Mat img; RNG g_rng(12345); vector<vector<Point> > contours,contours0; vector<Vec4i> hierarchy;int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);// 1、 讀取圖片img = imread( "F:/C++/2. OPENCV 3.1.0/TEST/face.png", 0 );if(!img.data ) { printf("讀取圖片錯誤,請確定目錄下是否有imread函數指定圖片存在~! \n"); return false; }imshow( "image", img );// // 繪制6個人臉用于測試 // // 創建 500*500 單通道矩陣 灰度圖 // Mat img = Mat::zeros(w, w, CV_8UC1); // for( int 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 ) // { // for( int 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, 1, 8, 0); // } // } // ellipse( img, Point(dx+150, dy+100), Size(100,70), 0, 0, 360, white, -1, 8, 0 ); // ellipse( img, Point(dx+115, dy+70), Size(30,20), 0, 0, 360, black, -1, 8, 0 ); // ellipse( img, Point(dx+185, dy+70), Size(30,20), 0, 0, 360, black, -1, 8, 0 ); // ellipse( img, Point(dx+115, dy+70), Size(15,15), 0, 0, 360, white, -1, 8, 0 ); // ellipse( img, Point(dx+185, dy+70), Size(15,15), 0, 0, 360, white, -1, 8, 0 ); // ellipse( img, Point(dx+115, dy+70), Size(5,5), 0, 0, 360, black, -1, 8, 0 ); // ellipse( img, Point(dx+185, dy+70), Size(5,5), 0, 0, 360, black, -1, 8, 0 ); // ellipse( img, Point(dx+150, dy+100), Size(10,5), 0, 0, 360, black, -1, 8, 0 ); // ellipse( img, Point(dx+150, dy+150), Size(40,10), 0, 0, 360, black, -1, 8, 0 ); // ellipse( img, Point(dx+27, dy+100), Size(20,35), 0, 0, 360, white, -1, 8, 0 ); // ellipse( img, Point(dx+273, dy+100), Size(20,35), 0, 0, 360, white, -1, 8, 0 ); // }// 顯示 // namedWindow( "image", 1 ); // imshow( "image", img );// 2、提取輪廓findContours( img, contours0, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);// 3、對輪廓進行逼近 獲得多邊形曲線輪廓向量點集contours.resize(contours0.size());for( size_t k = 0; k < contours0.size(); k++ )approxPolyDP(Mat(contours0[k]), contours[k], 0.1, true);// 4、滑動條調用// 設置軌跡條,控制從-3到3的輪廓等級namedWindow( "contours", 1 );createTrackbar( "levels+3", "contours", &levels, 7, on_trackbar );on_trackbar(0,0);// 初次調用waitKey(0);return a.exec(); }輪廓控制回調函數
static void on_trackbar(int, void*) {// 1、創建目標圖像Mat cnt_img = Mat::zeros(img.rows, img.cols, CV_8UC3);// 2、處理參數int _levels = levels - 3;// 3、繪制輪廓// _levels <= 0 ? 3 : -1Scalar color = Scalar( g_rng.uniform(0, 255), g_rng.uniform(0,255), g_rng.uniform(0,255) );//任意值drawContours( cnt_img, contours,_levels <= 0 ? 3 : -1, Scalar(128,255,255),1, LINE_AA, hierarchy, std::abs(_levels) ); imshow("contours", cnt_img); }結果:
總結
以上是生活随笔為你收集整理的OpenCV学习笔记(十七):查找并绘制轮廓:findContours(),drawContours(),approxPolyDP()的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pytorch的nn.CrossEntr
- 下一篇: python+OpenCV图像处理(三)