前几年在做毕业设计时候曾用opencv1.0中defects做过简单的手势识别,这几天看OpenCV2.46中的轮廓函数,发现和以前差别挺大,函数调用完全不一样,重新实现了简单手势的代码。
1.首先用简单的肤色检测算法,得到手的区域。
Mat img = cv::imread("../hand2.jpg");
namedWindow("image");
imshow("image", img);
Mat hsvimg;
首先把图像转化到HSV颜色空间,利用肤色色度、饱和度和亮度的特殊范围,得到手的区域。最后对得到的二值手的区域进行开闭操作,去掉一些小的干扰点。
//得到HSV颜色空间的图像
cvtColor( img, hsvimg, CV_BGR2HSV );
//初始化手的图像和原始图像一样大小
Mat handimg1(img.rows,img.cols, CV_8UC1, Scalar(0));
//简单的肤色检测算法,基于HSV颜色空间和域值
uchar* p;
uchar* p1;
uchar h, s, v;
int i, j;
for( i = 0; i < hsvimg.rows; ++i)
{
p = hsvimg.ptr<uchar>(i);
p1= handimg1.ptr<uchar>(i);
for ( j = 0; j < hsvimg.cols; ++j)
{
//printf (" %d %d %d",p[j*3], p[j*3+1],p[j*3+2]);
h = p[j*3];
s = p[j*3+1];
v = p[j*3+2];
if(h <= 19 && s >= 48 && v > 40)
{
p1[j] = 255;
}
else p1[j] = 0;
}
}
//对得到手区域二值图做简单的开闭操作
cv::Mat element = cv::getStructuringElement( cv::MORPH_RECT,
cv::Size( 5,5 ),
cv::Point( 2, 2 ) );
morphologyEx( handimg1, handimg1, MORPH_OPEN, element );
morphologyEx( handimg1, handimg1, MORPH_CLOSE, element );
简单肤色算法后的效果:
接着对得到的手区域二值图,查找轮廓,找出面积最大的轮廓区域,这个区域一般为手的区域,对该区域求凸包以及defect数目,根据defect的数目,可以判断伸出几个手指头。注意:在判断defect数目时候,我们根据defect点到凸包的距离长度,过滤了一些小的defect。
过滤的代码为:
if ( defectVector.val[3] <= 10000 ) { continue; }
下面是过滤前后的defect点(黄色点),可以看到过滤后,只有4个defect点,我们可以得出此时伸出了5个手指头。
而下面的图,只有1个defect点,则表示伸出2个手指头。
检测defect的代码如下:
vector<vector<Point> > contours;vector<Vec4i> hierarchy; //查找轮廓findContours( handimg1, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );printf("轮廓数目:%d\n", contours.size());vector<vector<Point> >hull( contours.size() );for( int i = 0; i < contours.size(); i++ ) { convexHull( Mat(contours[i]), hull[i], false ); } Mat drawing = Mat::zeros( handimg1.size(), CV_8UC3 );int area = 0; //轮廓索引int k = 0;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; } } if (!isContourConvex(contours[k]) &&contours[k].size() > 3) { vector<int> convexHull_IntIdx; vector<Vec4i> defects; convexHull(contours[k], convexHull_IntIdx, true); convexityDefects(contours[k], convexHull_IntIdx, defects); int defectcount = 0;for(i=0;i < defects.size();++i) { Matx<int,4,1> defectVector = defects[i]; vector<Point> contours1 =contours[k]; 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] <= 10000 ) { 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); defectcount++; } printf("指缝数目:%d\n", defectcount); }
程序源码:工程FirstOpenCV26