基本使用
2.png
#include "win.h" win::win(QWidget *parent) : QWidget(parent) { this->resize(500,300); cv::Mat imageSource = cv::imread("D:/bb/tu/2.jpg", 0); cv::namedWindow("原圖像"); cv::imshow("原圖像", imageSource); cv::Mat image; cv::GaussianBlur(imageSource, image, cv::Size(3, 3), 0);//過濾 //cv::Canny(image, image, 100, 250); //注意:最好不要Canny,Canny之后每個邊界會形成一個環,變成內外兩個輪廓 //建議二值化 cv::threshold( image, image, 200, 255,0 );//二值化 std::vector<std::vector<cv::Point>> contours; std::vector<cv::Vec4i> hierarchy; cv::findContours(image, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point());//找出輪廓坐標點 /* 第一個參數:image,單通道圖像矩陣,可以是灰度圖,但更常用的是二值圖像, 一般是經過Canny、拉普拉斯等邊緣檢測算子處理過的二值圖像 第二個參數:contours,定義為“vector<vector<Point>> contours”,是一個向量, 並且是一個雙重向量,向量內每個元素保存了一組由連續的Point點構成的點的集合的向量,每一組Point點集就是一個輪廓。有多少輪廓,向量contours就有多少元素 第三個參數:hierarchy,定義為“vector<Vec4i> hierarchy”,先來看一下Vec4i的定義: typedef Vec<int, 4> Vec4i; Vec4i是Vec<int,4>的別名,定義了一個“向量內每一個元素包含了4個int型變量”的向量。 所以從定義上看,hierarchy也是一個向量,向量內每個元素保存了一個包含4個int整型的數組。 向量hiararchy內的元素和輪廓向量contours內的元素是一一對應的,向量的容量相同。 hierarchy向量內每一個元素的4個int型變量——hierarchy[i][0] ~hierarchy[i][3],分別表示第 i個輪廓的后一個輪廓、前一個輪廓、內嵌輪廓、父輪廓的索引編號。如果當前輪廓沒有對應的后一個 輪廓、前一個輪廓、父輪廓或內嵌輪廓的話,則hierarchy[i][0] ~hierarchy[i][3]的相應位被設置為默認值-1 例如:hierarchy[index][0] 表示index輪廓的后一個輪廓的序號 【用來保存輪廓層級關系的】 第四個參數:int型的mode,定義輪廓的檢索模式: 取值一:cv::RETR_EXTERNAL只檢測最外圍輪廓,包含在外圍輪廓內的內圍輪廓被忽略 取值二:cv::RETR_LIST 檢測所有的輪廓,包括內圍、外圍輪廓,但是檢測到的輪廓不建立等級關 系,彼此之間獨立,沒有等級關系,這就意味着這個檢索模式下不存在父輪廓或內嵌輪廓, 所以hierarchy向量內所有元素的第3、第4個分量都會被置為-1,具體下文會講到 取值三:cv::RETR_CCOMP 檢測所有的輪廓,但所有輪廓只建立兩個等級關系,外圍為頂層,若外圍 內的內圍輪廓還包含了其他的輪廓信息,則內圍內的所有輪廓均歸屬於頂層 取值四:cv::RETR_TREE, 檢測所有輪廓,所有輪廓建立一個等級樹結構。外層輪廓包含內層輪廓,內 層輪廓還可以繼續包含內嵌輪廓 第五個參數:int型的method,定義輪廓的保存方法: 取值一:cv::CHAIN_APPROX_NONE 保存物體邊界上所有連續的輪廓點到contours向量內 取值二:cv::CHAIN_APPROX_SIMPLE 僅保存輪廓的拐點信息,把所有輪廓拐點處的點保存入contours 向量內,拐點與拐點之間直線段上的信息點不予保留 取值三和四:cv::CHAIN_APPROX_TC89_L1,cv::CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近 似算法 第六個參數:Point偏移量,所有的輪廓信息相對於原始圖像對應點的偏移量,相當於在每一個檢測出的輪廓點上加 上該偏移量,並且Point還可以是負值 */ cv::namedWindow("image"); cv::imshow("image", image); int c = contours.size(); //返回容器的大小 std::cerr<< c <<std::endl; cv::Mat imageContours = cv::Mat::zeros(image.size(), CV_8UC1); cv::Mat Contours = cv::Mat::zeros(image.size(), CV_8UC1); for (int i = 0; i < contours.size(); i++) { //contours[i]代表的是第i個輪廓,contours[i].size()代表的是第i個輪廓上所有的像素點數 //【1個輪廓用兩個向量表示,contours[0]和contours[1]是同一個輪廓】 for (int j = 0; j < contours[i].size(); j++){ //繪制出contours向量內所有的像素點 cv::Point P = cv::Point(contours[i][j].x, contours[i][j].y); Contours.at<uchar>(P) = 255; } //輸出hierarchy向量內容,具體內容見下面圖 qDebug() << "向量hierarchy的第" << i << " 個元素內容為:" << hierarchy[i][0]<<", "<<hierarchy[i][1]<<", "<<hierarchy[i][2]<<", "<<hierarchy[i][3]; //繪制輪廓 cv::drawContours(imageContours, contours, i, cv::Scalar(255), 1, 8, hierarchy);//繪制輪廓,用於繪制找到的圖像輪廓 /* 參數1:要繪制輪廓的圖像 參數2:所有輸入的輪廓,每個輪廓被保存成一個point向量 參數3: int contourIdx指定要繪制輪廓的編號【內嵌輪廓也畫出】,如果是負數,則繪制所有的輪廓 參數4:繪制輪廓所用的顏色 參數5:繪制輪廓的線的粗細,如果是負數,則輪廓內部被填充 參數6:int lineType = 8, /繪制輪廓的線的連通性 參數7:輪廓結構信息 參數8:int maxLevel = INT_MAX,//繪制輪廓的最高級別,這個參數只有hierarchy有效的時候才有效 maxLevel=0,繪制與輸入輪廓屬於同一等級的所有輪廓即輸入輪廓和與其相鄰的輪廓 maxLevel=1, 繪制與輸入輪廓同一等級的所有輪廓與其子節點。 maxLevel=2,繪制與輸入輪廓同一等級的所有輪廓與其子節點以及子節點的子節點 */ } imshow("Contours Image", imageContours); //輪廓 imshow("Point of Contours", Contours); //向量contours內保存的所有輪廓點集 } win::~win() { }
理解cv::RETR_EXTERNAL只檢測最外圍輪廓
3.jpg
#include "win.h" win::win(QWidget *parent) : QWidget(parent) { this->resize(500,300); cv::Mat imageSource = cv::imread("D:/bb/tu/3.jpg", 0); cv::Mat image; cv::GaussianBlur(imageSource, image, cv::Size(3, 3), 0); cv::Canny(image, image, 100, 250); std::vector<std::vector<cv::Point>> contours; std::vector<cv::Vec4i> hierarchy; cv::findContours(image, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE, cv::Point()); //參數4:cv::RETR_EXTERNAL只檢測最外圍輪廓,包含在外圍輪廓內的內圍輪廓被忽略 //參數5:保存所有輪廓點 //畫出輪廓 cv::Mat Con = cv::Mat::zeros(image.size(), CV_8UC1);//輪廓圖像 for (int i = 0; i < contours.size(); i++){ cv::drawContours(Con, contours, i, cv::Scalar(255), 1, 8, hierarchy);//繪制輪廓,用於繪制找到的圖像輪廓 } //下面輸出hierarchy(層級關系)的數據 char ch[256]; for (int i = 0; i < contours.size(); i++){ sprintf_s(ch, "%d", i); std::string str = ch; std::cout << str << ":" << hierarchy[i] << std::endl; } //標注輪廓 std::vector<cv::Point2f> centers(contours.size());//圓心--數組 std::vector<float> radius(contours.size());// cv::Point2f point; int y=0;//偏移量,防止重合 for (int i = 0; i < contours.size(); i++){ //標注放到輪廓的左邊 cv::minEnclosingCircle(contours[i], centers[i], radius[i]);//尋找指定輪廓的最小圓 if(i%2 ==0){y=0;} if(i%2 !=0){y=30;} point=cv::Point2f(centers[i].x-radius[i]-5,centers[i].y+y); sprintf_s(ch, "%d", i); std::string str = ch; std::string str1(1, 'c'); str=str1+str; putText(Con,str, point,cv::FONT_HERSHEY_PLAIN,2, cv::Scalar(255,0,0),1); } cv::namedWindow("Con"); cv::imshow("Con", Con); } win::~win() { }
hierarchy數據:
理解cv::RETR_LIST
#include "win.h" win::win(QWidget *parent) : QWidget(parent) { this->resize(500,300); cv::Mat imageSource = cv::imread("D:/bb/tu/3.jpg", 0); cv::Mat image; cv::GaussianBlur(imageSource, image, cv::Size(3, 3), 0); //cv::Canny(image, image, 100, 250); //注意:最好不要Canny,Canny之后每個邊界會形成一個環,變成內外兩個輪廓 //建議二值化 cv::threshold( image, image, 200, 255,0 );//二值化 cv::imshow("image", image); std::vector<std::vector<cv::Point>> contours; std::vector<cv::Vec4i> hierarchy; cv::findContours(image, contours, hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_NONE, cv::Point()); //參數4:cv::RETR_LIST 檢測所有的輪廓,包括內圍、外圍輪廓,但是檢測到的輪廓不建立等級關系,彼此之間獨立,沒有等 // 級關系,這就意味着這個檢索模式下不存在父輪廓或內嵌輪廓 //參數5:保存所有輪廓點 //畫出輪廓 cv::Mat Con = cv::Mat::zeros(image.size(), CV_8UC1);//輪廓圖像 for (int i = 0; i < contours.size(); i++){ cv::drawContours(Con, contours, i, cv::Scalar(255), 1, 8, hierarchy);//繪制輪廓,用於繪制找到的圖像輪廓 } //下面輸出hierarchy(層級關系)的數據 char ch[256]; for (int i = 0; i < contours.size(); i++){ sprintf_s(ch, "%d", i); std::string str = ch; std::cout << str << ":" << hierarchy[i] << std::endl; } //標注輪廓 std::vector<cv::Point2f> centers(contours.size());//圓心--數組 std::vector<float> radius(contours.size());// cv::Point2f point; int y=0;//偏移量,防止重合 for (int i = 0; i < contours.size(); i++){ //標注放到輪廓的左邊 cv::minEnclosingCircle(contours[i], centers[i], radius[i]);//尋找指定輪廓的最小圓 if(i%2 ==0){y=0;} if(i%2 !=0){y=30;} point=cv::Point2f(centers[i].x-radius[i]+45,centers[i].y+y); sprintf_s(ch, "%d", i); std::string str = ch; std::string str1(1, 'c'); str=str1+str; putText(Con,str, point,cv::FONT_HERSHEY_PLAIN,2, cv::Scalar(255,0,0),1); } cv::namedWindow("Con"); cv::imshow("Con", Con); } win::~win() { }
分析cv::RETR_CCOMP
#include "win.h" win::win(QWidget *parent) : QWidget(parent) { this->resize(500,300); cv::Mat imageSource = cv::imread("D:/bb/tu/3.jpg", 0); cv::Mat image; cv::GaussianBlur(imageSource, image, cv::Size(3, 3), 0); //cv::Canny(image, image, 100, 250); //注意:最好不要Canny,Canny之后每個邊界會形成一個環,變成內外兩個輪廓 //建議二值化 cv::threshold( image, image, 200, 255,0 );//二值化 cv::imshow("image", image); std::vector<std::vector<cv::Point>> contours; std::vector<cv::Vec4i> hierarchy; cv::findContours(image, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_NONE, cv::Point()); //參數4:cv::RETR_CCOMP 檢測所有的輪廓,但所有輪廓只建立兩個等級關系,外圍為頂層,若外圍 // 內的內圍輪廓還包含了其他的輪廓信息,則內圍內的所有輪廓均歸屬於頂層 //參數5:保存所有輪廓點 //畫出輪廓 cv::Mat Con = cv::Mat::zeros(image.size(), CV_8UC1);//輪廓圖像 for (int i = 0; i < contours.size(); i++){ cv::drawContours(Con, contours, i, cv::Scalar(255), 1, 8, hierarchy);//繪制輪廓,用於繪制找到的圖像輪廓 } //下面輸出hierarchy(層級關系)的數據 char ch[256]; for (int i = 0; i < contours.size(); i++){ sprintf_s(ch, "%d", i); std::string str = ch; std::cout << str << ":" << hierarchy[i] << std::endl; } //標注輪廓 std::vector<cv::Point2f> centers(contours.size());//圓心--數組 std::vector<float> radius(contours.size());// cv::Point2f point; int y=0;//偏移量,防止重合 for (int i = 0; i < contours.size(); i++){ //標注放到輪廓的左邊 cv::minEnclosingCircle(contours[i], centers[i], radius[i]);//尋找指定輪廓的最小圓 if(i%2 ==0){y=0;} if(i%2 !=0){y=30;} point=cv::Point2f(centers[i].x-radius[i]+45,centers[i].y+y); sprintf_s(ch, "%d", i); std::string str = ch; std::string str1(1, 'c'); str=str1+str; putText(Con,str, point,cv::FONT_HERSHEY_PLAIN,2, cv::Scalar(255,0,0),1); } cv::namedWindow("Con"); cv::imshow("Con", Con); } win::~win() { }
分析cv::RETR_TREE樹結構的層級關系
3.jpg
#include "win.h" win::win(QWidget *parent) : QWidget(parent) { this->resize(500,300); cv::Mat imageSource = cv::imread("D:/bb/tu/3.jpg", 0); cv::Mat image; cv::GaussianBlur(imageSource, image, cv::Size(3, 3), 0); //cv::Canny(image, image, 100, 250); //注意:最好不要Canny,Canny之后每個邊界會形成一個環,變成內外兩個輪廓 //建議二值化 cv::threshold( image, image, 200, 255,0 );//二值化 cv::imshow("image", image); std::vector<std::vector<cv::Point>> contours; std::vector<cv::Vec4i> hierarchy; cv::findContours(image, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_NONE, cv::Point()); //參數4:cv::RETR_TREE, 檢測所有輪廓,所有輪廓建立一個等級樹結構。外層輪廓包含內層輪廓,內層輪廓還可以繼續包含內嵌輪廓 //參數5:保存所有輪廓點 //畫出輪廓 cv::Mat Con = cv::Mat::zeros(image.size(), CV_8UC1);//輪廓圖像 for (int i = 0; i < contours.size(); i++){ cv::drawContours(Con, contours, i, cv::Scalar(255), 1, 8, hierarchy);//繪制輪廓,用於繪制找到的圖像輪廓 } //下面輸出hierarchy(層級關系)的數據 char ch[256]; for (int i = 0; i < contours.size(); i++){ sprintf_s(ch, "%d", i); std::string str = ch; std::cout << str << ":" << hierarchy[i] << std::endl; } //標注輪廓 std::vector<cv::Point2f> centers(contours.size());//圓心--數組 std::vector<float> radius(contours.size());// cv::Point2f point; int y=0;//偏移量,防止重合 for (int i = 0; i < contours.size(); i++){ //標注放到輪廓的左邊 cv::minEnclosingCircle(contours[i], centers[i], radius[i]);//尋找指定輪廓的最小圓 if(i%2 ==0){y=0;} if(i%2 !=0){y=30;} point=cv::Point2f(centers[i].x-radius[i]+45,centers[i].y+y); sprintf_s(ch, "%d", i); std::string str = ch; std::string str1(1, 'c'); str=str1+str; putText(Con,str, point,cv::FONT_HERSHEY_PLAIN,2, cv::Scalar(255,0,0),1); } cv::namedWindow("Con"); cv::imshow("Con", Con); } win::~win() { }
分析hierarchy數據
根據上面圖和hierarchy數據進行分析
1.同層級輪廓用前后表示
2.不同層級用父子表示