1.28【OpenCV圖像處理】凸包計算
凸包計算convexHull()、繪製凸包drawContours()
1.28.1 凸包 Convex Hull
凸包(Convex Hull):基於圖像尋找輪廓的基礎上,在一個多變形邊緣或內部的任意兩個點的連線都被包含在多邊形邊界或者內部。
正式定義:包含點集合 S 中所有點的最小凸多邊形稱為凸包(凸形外殼)。檢測凸包的演算法常用Graham掃描法。
作用:二維凸包可以用來解決圍欄問題、城市規劃問題、聚類分析等等。
1.28.2 Graham掃描演算法
Graham掃描演算法原理:
a. 首先選擇Y方向最低的點作為起始點p0
b. 從p0開始極坐標掃描,依次添加p1….pn(排序順序是根據極坐標的角度大小,逆時針方向)
c. 對每個點pi來說,如果添加pi點到凸包中導致一個左轉向(逆時針方法)則添加該點到凸包, 反之如果導致一個右轉向(順時針方向)刪除該點從凸包中
1.28.3 API說明
OpenCV已經實現了凸包Graham掃描演算法和API提供我們使用:
convexHull(
InputArray points,// 輸入候選點,來自findContours輪廓點
OutputArray hull,// 輸出凸包點
bool clockwise,//定向標誌,true輸出凸殼順時針方向;默認false逆時針方向.。假定坐標系的x軸指向右側,y軸指向上方。
bool returnPoints)//默認為true時,函數返回凸包點;否則返回凸包點的指數。如果第二個參數是數組vector<Point>則自動忽略此標誌。
)
/*凸包計算*/nvector<vector<Point>> convexs(contours.size()); // 定義數組nfor (size_t i = 0; i < contours.size(); i++) {n convexHull(contours[i], convexs[i], false, true); //凸包計算:輪廓點,輸出凸包點,false逆時針方向,true返回凸包點n}n
實現步驟:
1)轉灰度並濾波去噪 - cvtColor()、blur()
/*轉灰度並濾波去噪*/ncvtColor(src, src_gray, CV_BGR2GRAY); //轉灰度nblur(src_gray, src_gray, Size(3, 3), Point(-1, -1), BORDER_DEFAULT); //均值濾波,去噪nimshow("gray image", src_gray);n
2)轉為二值圖像 - threshold()
/*固定閾值轉為二值圖像*/nthreshold(src_gray, bin_output, threshold_value, threshold_max, THRESH_BINARY); //固定閾值:輸入灰度圖,輸出二值化圖,已知閾值,閾值最大值,閾值類型n
3)通過尋找輪廓得到候選點 - findContours()
/*尋找輪廓*/nvector<vector<Point>> contours;nvector<Vec4i> hierachy;nfindContours(bin_output, contours, hierachy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0)); //尋找輪廓:輸入二值圖像,輪廓點,輸出層次結構,輪廓檢索演算法(RETR_TREE多層次所有輪廓,RETR_EXTERNAL單層次外部輪廓),輪廓逼近演算法,輪廓點位移n
4)凸包計算 - convexHull()
/*凸包計算*/nvector<vector<Point>> convexs(contours.size()); // 定義數組nfor (size_t i = 0; i < contours.size(); i++) {nconvexHull(contours[i], convexs[i], false, true); //凸包計算:輪廓點,輸出凸包點,false逆時針方向,true返回凸包點n}n
5)繪製凸包(也是一種輪廓) - drawContours()
/*繪製凸包*/ndst = Mat::zeros(src.size(), CV_8UC3);nvector<Vec4i> empty(0); //層次結構為空nfor (size_t k = 0; k < contours.size(); k++) {nScalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)); //隨機顏色n drawContours(dst, contours, k, color, 2, LINE_8, hierachy, 0, Point(0, 0)); //繪製輪廓:輸入,輪廓點,輪廓索引號,顏色,線寬,線類型,層次,最大層數0隻繪製當前的,輪廓位移ndrawContours(dst, convexs, k, color, 2, LINE_8, empty, 0, Point(0, 0));//繪製凸包:輸入,輪廓點,輪廓索引號,顏色,線寬,線類型,層次為空,最大層數0隻繪製當前的,輪廓位移n}nimshow(output_win, dst);n
完整程序:
/*1.28 凸包計算*/n#include <opencv2/opencv.hpp>n#include <iostream>n#include <math.h>nnusing namespace std;nusing namespace cv;nMat src, src_gray, dst, bin_output;nint threshold_value = 95;nint threshold_max = 255;nconst char* output_win = "convex hull demo";nvoid Threshold_Callback(int, void*);nRNG rng(12345);nnint main(int argc, char** argv) {n src = imread("E:/OpenCV/testimage/money2.jpg");nif (!src.data) {n printf("could not load image...n");nreturn -1;n }nconst char* input_win = "input image";n namedWindow(input_win, CV_WINDOW_AUTOSIZE);n imshow(input_win, src);n namedWindow(output_win, CV_WINDOW_NORMAL);nconst char* trackbar_label = "Threshold : ";nn /*轉灰度並濾波去噪*/n cvtColor(src, src_gray, CV_BGR2GRAY); //轉灰度n blur(src_gray, src_gray, Size(3, 3), Point(-1, -1), BORDER_DEFAULT); //均值濾波,去噪n imshow("gray image", src_gray);nn createTrackbar(trackbar_label, output_win, &threshold_value, threshold_max, Threshold_Callback); //跟蹤條控制二值化的固定閾值nn Threshold_Callback(0, 0);n waitKey(0);nreturn 0;n}nnvoid Threshold_Callback(int, void*) {n/*固定閾值轉為二值圖像*/n threshold(src_gray, bin_output, threshold_value, threshold_max, THRESH_BINARY); //固定閾值:輸入灰度圖,輸出二值化圖,已知閾值,閾值最大值,閾值類型n imshow("binary image", bin_output);nn /*尋找輪廓*/nvector<vector<Point>> contours;nvector<Vec4i> hierachy;n findContours(bin_output, contours, hierachy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0)); //尋找輪廓:輸入二值圖像,輪廓點,輸出層次結構,輪廓檢索演算法(RETR_TREE多層次所有輪廓,RETR_EXTERNAL單層次外部輪廓),輪廓逼近演算法,輪廓點位移nn /*凸包計算*/nvector<vector<Point>> convexs(contours.size()); // 定義數組nfor (size_t i = 0; i < contours.size(); i++) {n convexHull(contours[i], convexs[i], false, true); //凸包計算:輪廓點,輸出凸包點,false逆時針方向,true返回凸包點n}nn/*繪製凸包*/n dst = Mat::zeros(src.size(), CV_8UC3);nvector<Vec4i> empty(0); //層次結構為空nfor (size_t k = 0; k < contours.size(); k++) {nScalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)); //隨機顏色n drawContours(dst, contours, k, color, 2, LINE_8, hierachy, 0, Point(0, 0)); //繪製輪廓:輸入,輪廓點,輪廓索引號,顏色,線寬,線類型,層次,最大層數0隻繪製當前的,輪廓位移n drawContours(dst, convexs, k, color, 2, LINE_8, empty, 0, Point(0, 0));//繪製凸包:輸入,輪廓點,輪廓索引號,顏色,線寬,線類型,層次為空,最大層數0隻繪製當前的,輪廓位移n }n imshow(output_win, dst);nnreturn;n}n
運行結果:
以下各種圖結果的輪廓檢索演算法不同,各個的第一幅是RETR_EXTERNAL單層次外部輪廓,第二幅是RETR_TREE多層次所有輪廓:
推薦閱讀:
※線性代數的直覺理解(5)
※1.8【OpenCV圖像處理】繪製形狀與文字
※50行代碼實現人臉檢測
※1.9【OpenCV圖像處理】平滑模糊濾波
※最近在搞Kinect的指尖識別,手指都可以畫出來了,有什麼方法可以將每隻手指對應的名稱識別出來啊?