1.1圖像輪廓與API函數html
輪廓是一系列相連的點組成的曲線,表明了物體的基本外形,相對於邊緣,輪廓是連續的,邊緣並不所有連續。通常地,獲取圖像輪廓要通過下面幾個步驟:算法
1) 讀取圖片。編程
2) 將彩色圖像轉換成灰度圖像。app
3) 將灰度圖像轉換成二值圖形並查找其二值圖像邊緣便可(如canny邊緣檢測)。函數
4) 顯示輪廓邊緣。學習
findContours尋找輪廓函數,原型爲:測試
CV_EXPORTS_W void findContours( InputOutputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset = Point()); /** @overload */ CV_EXPORTS void findContours( InputOutputArray image, OutputArrayOfArrays contours, int mode, int method, Point offset = Point());
1)image:圖像,單通道灰度圖。spa
2)contours:檢測到的輪廓,包含對象邊界點(x,y)的座標,每一個輪廓存儲爲一個點向量可用point類型的vector存儲。.net
3)hierarchy:輪廓的拓撲信息,每一個輪廓contours[i]包含4個hierarchy[i]元素,hierarchy[i][0]-- hierarchy[i][3],分別表明後一個輪廓、前一個輪廓、父輪廓、內嵌輪廓的編號。3d
4)mode:輪廓檢索模式。
RETR_EXTERNAL :只檢索最外面的輪廓;
RETR_LIST:檢索全部的輪廓,並將其保存到一條鏈表當中;
RETR_CCOMP:檢索全部的輪廓,並將他們組織爲兩層:頂層是各部分的外部邊界,次層是空洞的內層邊界;
RETR_TREE:檢索全部的輪廓,並重構嵌套輪廓的整個層次;
返回值的含義
5)method:輪廓逼近方法。
CHAIN_APPROX_NONE:輸出輪廓的每一個像素。
CHAIN_APPROX_SIMPLE:壓縮水平的、垂直的和斜的部分,函數只保留他們的終點座標。
drawContours繪製輪廓函數,原型爲:
CV_EXPORTS_W 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() );
image – 輸入圖像,單通道灰度圖。
contours - 全部的輸入輪廓,每一個輪廓爲點矢量(a point vector)/點向量形式,與findcontours中的contours 形式一致。
contourIdx - 指定輪廓列表的索引 ID(將被繪製),若爲負數,則全部的輪廓將會被繪製。
color - 繪製輪廓的顏色。
thickness - 繪製輪廓線條的寬度,若爲負值或CV.FILLED則將填充輪廓內部區域。
lineType – 線條的類型,8連通型或4連通型。
hierarchy - 層次結構信息,與函數findcontours()的hierarchy有關
maxLevel - 繪製輪廓的最高級別。若爲0,則繪製指定輪廓;若爲1,則繪製該輪廓和全部嵌套輪廓(nested contours);若爲2,則繪製該輪廓、嵌套輪廓(nested contours)/子輪廓和嵌套-嵌套輪廓(all the nested-to-nested contours)/孫輪廓,等等。該參數只有在層級結構時纔用到。
offset - 按照偏移量移動全部的輪廓(點座標)。
1.2圖像輪廓實例
測試代碼以下。
int main() { RNG rng(12345); Mat src_gray; Mat src = imread("D:\\WORK\\5.OpenCV\\LeanOpenCV\\pic_src\\pic9.bmp"); imshow("原圖", src); /// 轉成灰度並模糊化降噪 cvtColor(src, src_gray, CV_BGR2GRAY); blur(src_gray, src_gray, Size(3, 3)); int thresh = 100; int max_thresh = 255; Mat canny_output; vector<vector<Point> > contours; vector<Vec4i> hierarchy; /// 用Canny算子檢測邊緣 Canny(src_gray, canny_output, thresh, thresh * 2, 3); /// 尋找輪廓 findContours(canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0)); /// 繪出輪廓 Mat drawing = Mat::zeros(canny_output.size(), CV_8UC3); for (int i = 0; i < contours.size(); i++) { Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)); drawContours(drawing, contours, i, color, 1, 8, hierarchy, 0, Point()); } /// 在窗體中顯示結果 namedWindow("Contours", CV_WINDOW_AUTOSIZE); imshow("Contours", drawing); waitKey(0); }
輸出結果爲:
測試2,結果以下圖。
2.1凸包與API函數
在一個實數向量空間V中,對於給定集合X,全部包含X的凸集的交集S被稱爲X的凸包。X的凸包能夠用X內全部點(X1,…Xn)的凸組合來構造。簡單來說,對於一個二維空間的點集,這個點集當中的一些點總能夠造成一個凸多邊形,而這個凸多邊形以內剛好能夠包括除了組成凸包這個凸多邊之外的全部點,而這個凸多邊形就是凸包。凸包能夠想象成一條恰好包住全部點的橡皮圈,對於給定二維平面上的點集,凸包經常就是將最外層的點鏈接起來構成的凸多邊形,它能包含點擊中全部的點。
物體的凸包檢測常應用在物體識別、手勢識別及邊界檢測等領域。理解物體形狀或輪廓的一種方法是計算物體的凸包,而後計算其凸缺陷。下圖人手圖像畫圖了凸包線輪廓,而後標出了凸缺陷A—H。黑色的輪廓線爲convexity hull, 而convexity hull與手掌之間的部分爲convexity defects. 每一個convexity defect區域有四個特徵量:起始點(startPoint),結束點(endPoint),距離convexity hull最遠點(farPoint),最遠點到convexity hull的距離(depth)。
OpenCV使用convexHull函數作物體輪廓凸包檢測:
CV_EXPORTS_W void convexHull( InputArray points, OutputArray hull, bool clockwise = false, bool returnPoints = true );
point:輸入的二維點集,可儲存在向量或矩陣Mat中,表明輪廓點
hull:輸出凸包,這是一個整數索引的載體或點的矢量,能夠是vector<vector<Point>>和vector<vector<int>>
clockwise:方向標誌位,true爲順時針,false爲逆時針方向。
return Point :操做標準位,默認true,表示返回凸包的各點,不然返回凸包各點的指數。
OpenCV使用convexityDefects函數作輪廓凸包缺陷檢測:
CV_EXPORTS_W void convexityDefects( InputArray contour, InputArray convexhull, OutputArray convexityDefects );
coutour: 輸入參數,檢測到的輪廓,能夠調用findContours函數獲得;
convexhull: 輸入參數,檢測到的凸包,能夠調用convexHull函數獲得。注意,convexHull函數能夠獲得vector<vector<Point>>和vector<vector<int>>兩種類型結果,這裏的convexhull應該爲vector<vector<int>>類型,不然通不過ASSERT檢查;
convexityDefects:輸出參數,檢測到的最終結果,應爲vector<vector<Vec4i>>類型,Vec4i存儲了起始點(startPoint),結束點(endPoint),距離convexity hull最遠點(farPoint)以及最遠點到convexity hull的距離(depth)。
2.2圖像凸包檢測實例
圖像輪廓與凸包實例測試代碼以下。
int main() { RNG rng(12345); Mat src_gray; Mat src = imread("D:\\WORK\\5.OpenCV\\LeanOpenCV\\pic_src\\pic9.bmp"); imshow("原圖", src); /// 轉成灰度並模糊化降噪 cvtColor(src, src_gray, CV_BGR2GRAY); //blur(src_gray, src_gray, Size(5, 5)); imshow("src_gray", src_gray); int thresh = 100; int max_thresh = 255; Mat canny_output; vector<vector<Point> > contours; vector<Vec4i> hierarchy; /// 用Canny算子檢測邊緣 Canny(src_gray, canny_output, thresh, thresh * 2, 3); imshow("canny_output", canny_output); /// 尋找輪廓 findContours(canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0)); //經過發現輪廓獲得的候選點來繪製凸包 vector<vector<Point>> convexs(contours.size()); for (size_t i = 0; i < contours.size(); i++) { convexHull(contours[i], convexs[i], false, true); } /// 繪出輪廓 Mat drawing = Mat::zeros(canny_output.size(), CV_8UC3); Mat drawing_convex = Mat::zeros(canny_output.size(), CV_8UC3); for (int i = 0; i < contours.size(); i++) { Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)); drawContours(drawing, contours, i, color, 1, 8, hierarchy, 0, Point()); //白色線畫出凸包 drawContours(drawing_convex, convexs, i, Scalar(255, 255, 255), 1, LINE_8, noArray(), 0, Point()); } imshow("Contours", drawing); imshow("drawing_convex", drawing_convex); waitKey(0); }
輸出結果爲:
測試2,輸出結果爲:
3.1多邊形包圍輪廓和API
當咱們獲得對象輪廓後,可用boundingRect()獲得包覆此輪廓的最小正矩形,minAreaRect()獲得包覆輪廓的最小斜矩形,minEnclosingCircle()獲得包覆此輪廓的最小圓形。
CV_EXPORTS_W void approxPolyDP( InputArray curve, OutputArray approxCurve, double epsilon, bool closed ); CV_EXPORTS_W Rect boundingRect( InputArray points ); CV_EXPORTS_W void minEnclosingCircle( InputArray points, CV_OUT Point2f& center, CV_OUT float& radius );
3.2多邊形包圍輪廓測試實例
測試代碼以下:
int main() { RNG rng(12345); Mat src_gray; Mat src = imread("D:\\WORK\\5.OpenCV\\LeanOpenCV\\pic_src\\img3.bmp"); imshow("原圖", src); /// 轉成灰度並模糊化降噪 cvtColor(src, src_gray, CV_BGR2GRAY); //blur(src_gray, src_gray, Size(5, 5)); imshow("src_gray", src_gray); int thresh = 130; int max_thresh = 255; Mat canny_output; vector<vector<Point> > contours; vector<Vec4i> hierarchy; /// 用Canny算子檢測邊緣 Canny(src_gray, canny_output, thresh, thresh * 2, 3); imshow("canny_output", canny_output); /// 尋找輪廓 findContours(canny_output, contours, hierarchy, RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, Point(0, 0)); vector<vector<Point> > contours_poly(contours.size()); vector<Rect> boundRect(contours.size()); vector<Point2f>center(contours.size()); vector<float>radius(contours.size()); for (int i = 0; i < contours.size(); i++) { approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true); boundRect[i] = boundingRect(Mat(contours_poly[i])); minEnclosingCircle(contours_poly[i], center[i], radius[i]); } /// 繪出輪廓 Mat drawing = Mat::zeros(canny_output.size(), CV_8UC3); for (int i = 0; i < contours.size(); i++) { Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)); drawContours(drawing, contours, i, color, 1, 8, hierarchy, 0, Point()); /// 畫多邊形輪廓 + 包圍的矩形框 + 圓形框 rectangle(drawing, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0); circle(drawing, center[i], (int)radius[i], color, 2, 8, 0); } imshow("Contours", drawing); waitKey(0); }
輸出結果以下:
若是須要填充輪廓空心空白,其中drawContours的參數thicknes表明繪製輪廓線條的寬度,若爲負值或CV.FILLED,表示填充輪廓內部區域。因此將此參數置-1就出現下面結果。
一、《OpenCV3 編程入門》,電子工業出版社,毛星雨著
二、《學習OpenCV》,清華大學出版社,Gary Bradski, Adrian kaehler著
三、用opencv檢測convexity defects
https://www.it610.com/article/4474290.htm
四、計算物體的凸包
http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/shapedescriptors/hull/hull.html
五、提取輪廓的原理和代碼實例
https://blog.csdn.net/qq_29796317/article/details/78297920
六、在圖像中尋找輪廓
七、【計算幾何/凸包】安德魯算法(Andrew's Algorithm)詳解
https://blog.csdn.net/peng0614/article/details/81193484
技術博客,轉載請註明。