上一篇教程中,我們學習了如何計算輪廓的凸包,其實對一個輪廓而言,可能它的凸包和它本身是重合的,也有可能不是重合的。比如下面左邊圖像的輪廓本身就是凸包,而右邊圖像的輪廓則不是。我們可以通過函數bool isContourConvex(InputArray contour),來判定一個輪廓是否是凸包,是的話返回true,否則false[注意測試的輪廓必須是簡單輪廓,沒有自交叉之類的]。
對一個非凸包的輪廓而言,它包括一系列的凹陷區域,這些區域稱作defect,比如下面手輪廓中,包括6個defect區域。在OpenCV中,我們用下面的結構來定義defect。
struct CvConvexityDefect { CvPoint* start; // 輪廓中defect的起點 CvPoint* end; // 輪廓中defect的終點 CvPoint* depth_point; // defect中到凸包最遠的點 float depth; // 最遠點和凸包之間的距離};
在OpenCV中,我們通過函數
void convexityDefects(InputArray contour, InputArray convexhull, OutputArray convexityDefects)
得到輪廓的凸包,其中第一個參數和第二個參數是輪廓以及輪廓對應的凸包,注意凸包應該使用vector<int>這樣的索引方式表示。第三個參數為返回的defect點集。
下面我們看下檢測輪廓defects的代碼:
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace cv;
using namespace std;
Mat src; Mat src_gray;
RNG rng(12345);
int main( int argc, char** argv )
{
//裝入圖像
src = imread("../hand1.jpg", 1 );
//轉化為灰度圖像
cvtColor( src, src_gray, CV_BGR2GRAY );
//blur( src_gray, src_gray, Size(3,3) );
namedWindow( "image");
imshow( "image", src_gray );
Mat threshold_output;
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
//得到二值圖
threshold( src_gray, threshold_output, 200, 255, THRESH_BINARY );
//查找輪廓
findContours( threshold_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );
printf("輪廓數目:%d\n", contours.size());
/// Find the convex hull object for each contour
vector<vector<Point> >hull( contours.size() );
for( int i = 0; i < contours.size(); i++ )
{ convexHull( Mat(contours[i]), hull[i], false ); }
/// Draw contours + hull results
Mat drawing = Mat::zeros( threshold_output.size(), CV_8UC3 );
int area = 0; //輪廓索引
int k = 0;
int i;
for(i = 0; i< contours.size(); i++ )
{
Scalar color1 = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
drawContours( drawing, contours, i, color1, 1, 8, vector<Vec4i>(), 0, Point() );
Scalar color2 = Scalar( rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255) );
drawContours( drawing, hull, i, color2, 1, 8, vector<Vec4i>(), 0, Point() );
int tt = contourArea(contours[i]);
printf("輪廓面積%d = %d\n", i, tt);
if( tt > area)
{
area = contourArea(contours[i]);
k = i;
}
}
vector<Point> hull1;
hull1 = hull[1];
for(i = 0; i< hull1.size(); i++ )
{
printf("point %d, %d, %d\n", i, hull1[i].x, hull1[i].y);
circle(drawing, hull1[i], 6, Scalar(255,0,0), 3, CV_AA);
}
int j;
for(j=0; j< contours.size(); j++)
{
//如果沒有defects或者輪廓小於三個點,則continue
if( isContourConvex(contours[j])|| contours[j].size()<3) continue;
vector<int> convexHull_IntIdx;
vector<Vec4i> defects;
if (contours[j].size() >3 )
{
convexHull(contours[j], convexHull_IntIdx, true);
convexityDefects(contours[j], convexHull_IntIdx, defects);
}
for(i=0;i < defects.size();++i)
{
Matx<int,4,1> defectVector = defects[i];
vector<Point> contours1 =contours[j];
Point point1 = contours1[defectVector.val[0]];//開始點
Point point2 = contours1[defectVector.val[1]];//結束點
Point point3 = contours1[defectVector.val[2]];//深度點
float dist = defectVector.val[3];
printf("dist: %f \n", dist);
//if ( defectVector.val[3] <= 1000 ) { continue; } // skip defects that are shorter than 100 pixel
circle(drawing, point1, 3, Scalar(255,255,0), 2, CV_AA);
circle(drawing, point2, 8, Scalar(0,255,0), 2, CV_AA);
circle(drawing, point3, 3, Scalar(0,255,255), 2, CV_AA);
}
}
/// Show in a window
namedWindow( "Hull demo");
imshow( "Hull demo", drawing );
waitKey(0);
return(0);
}
程序執行之后界面如下,注意左下有圖中
程序代碼:工程FirstOpenCV25