1.尋找輪廓
api
void cv::findContours( InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset = Point()
各個參數詳解如下:
- Image表示輸入圖像,必須是二值圖像,二值圖像可以threshold輸出、Canny輸出、inRange輸出、自適應閾值輸出等。
- Contours獲取的輪廓,每個輪廓是一系列的點集合
- Hierarchy輪廓的層次信息,每個輪廓有四個相關信息,分別是同層下一個、前一個、第一個子節點、父節點
- mode 表示輪廓尋找時候的拓撲結構返回 -RETR_EXTERNAL表示只返回最外層輪廓 -RETR_TREE表示返回輪廓樹結構
-
CV_RETR_EXTERNAL:只檢測外輪廓。忽略輪廓內部的洞
-
CV_RETR_LIST:檢測所有輪廓,但不建立繼承(包含)關系
-
CV_RETR_TREE:檢測所有輪廓,並且建立所有的繼承(包含)關系。也就是說用CV_RETR_EXTERNAL和CV_RETR_LIST方法的時候hierarchy這個變量是沒用的,因為前者沒有包含關系,找到的都是外輪廓,后者僅僅是找到所喲的輪廓但並不把包含關系區分。用TREE這種檢測方法的時候我們的hierarchy這個參數才是有意義的
-
CV_RETR_CCOMP:檢測所有輪廓,但是僅僅建立兩層包含關系。外輪廓放到頂層,外輪廓包含的第一層內輪廓放到底層,如果內輪廓還包含輪廓,那就把這些內輪廓放到頂層去。
-
- Method表示輪廓點集合取得是基於什么算法,常見的是基於CHAIN_APPROX_SIMPLE鏈式編碼方法
注意,如果圖像底色是白色,則檢測最外層的輪廓為圖像邊框
2.繪制輪廓外接矩形
繪制外接矩形包括兩種:
- 繪制最大外接矩形
(Rect cv::boundingRect( InputArray points ))
其中,輸入參數points為一系列點的集合(findContours中contours中的一個元素),對輪廓來說就是該輪廓的點集 返回結果是一個矩形,x, y, w, h
- 繪制最小外接矩形
RotatedRect cv::minAreaRect( InputArray points )
其中,輸入參數points為一系列點的集合(findContours中contours中的一個元素) ,對輪廓來說就是該輪廓的點集 返回結果是一個旋轉矩形,包含下面的信息: - 矩形中心位置 - 矩形的寬高 - 旋轉角度。
3.代碼
EdgeDetection.h
1 #pragma once 2 #include<opencv2/opencv.hpp> 3 #include<iostream> 4 5 using namespace std; 6 using namespace cv; 7 8 9 class EdgeDetection 10 { 11 cv::Mat m_img; 12 cv::Mat m_canny; 13 public: 14 EdgeDetection(cv::Mat iamge); 15 bool cannyProcess(unsigned int downThreshold,unsigned int upThreshold); 16 bool getContours(); 17 18 ~EdgeDetection(); 19 };
EdgeDetection.cpp
1 #include "EdgeDetection.h" 2 3 EdgeDetection::EdgeDetection(cv::Mat image) 4 { 5 m_img = image; 6 } 7 8 bool EdgeDetection::cannyProcess(unsigned int downThreshold, unsigned int upThreshold) 9 { 10 bool ret=true; 11 12 if (m_img.empty()) 13 { 14 ret = false; 15 } 16 17 cv::Canny(m_img, m_canny, downThreshold, upThreshold); 18 cv::imshow("Canny", m_canny); 19 20 return ret; 21 } 22 23 bool EdgeDetection::getContours() 24 { 25 bool ret = true; 26 if (m_canny.empty()) 27 { 28 ret = false; 29 } 30 31 cv::Mat k = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3), cv::Point(-1, -1)); 32 cv::dilate(m_canny, m_canny, k); 33 imshow("dilate", m_canny); 34 35 // 輪廓發現與繪制 36 vector<vector<cv::Point> > contours; 37 vector<Vec4i> hierarchy; 38 findContours(m_canny, contours, cv::RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point()); 39 40 for (size_t i = 0; i < contours.size();++i) 41 { 42 // 最大外接輪廓 43 cv::Rect rect = cv::boundingRect(contours[i]); 44 cv::rectangle(m_img,rect,cv::Scalar(0,255,0),2,LINE_8); 45 46 47 // 最小外接輪廓 48 RotatedRect rrt = minAreaRect(contours[i]); 49 Point2f pts[4]; 50 rrt.points(pts); 51 // 繪制旋轉矩形與中心位置 52 for (int i = 0; i < 4; i++) { 53 line(m_img, pts[i % 4], pts[(i + 1) % 4], Scalar(0, 0, 255), 2, 8, 0); 54 } 55 Point2f cpt = rrt.center; 56 circle(m_img, cpt, 2, Scalar(255, 0, 0), 2, 8, 0); 57 } 58 59 60 61 62 63 imshow("contours", m_img); 64 return ret; 65 } 66 67 EdgeDetection::~EdgeDetection() 68 { 69 }
main.cpp
#include"EdgeDetection.h" using namespace std; using namespace cv; int main(int argc, char* argv[]) { Mat src = imread("rect.jpg"); if (src.empty()) { cout << "image is empty" << endl; return -1; } imshow("input", src); EdgeDetection ed(src); ed.cannyProcess(80,160); ed.getContours(); waitKey(0); return 0; }
原圖
canny
目標圖