新开的CSDN 博客。准备开始写点东西
初学CV,翻很多文献。找了很多指尖检测的实例。不懂数学的我其实遇到了很多困难。
主要是从候选点获取指尖的算法上面。鲁棒性好像都不是很好。
关于指尖检测
主要下面两种
1.用Opencv 自带的凸包检测
主要步骤下
1.阈值操作 (一般用 前景检测 或者 肤色检测)
2.寻找轮廓
3.寻找凸包
4.选出指尖
凸包结构的点
1.Start
2.Depth_point
3.End
约束的条件
1.Depth 的角度 < 90 度
2.当前组 Start 点 与上一组 End 点的距离 < 20 (第一组 与 最后一组比较 )
则当前组的start点可以认为是指尖
检测效果如下
2.计算重心到轮廓边缘的距离
主要步骤(16年更新:我之前高中没有学过计算几何方面的内容。之前的代码完全靠想象)
1.阈值操作 (一般用 前景检测 或者 肤色检测)
2.寻找轮廓
3.寻找重心
计算一阶矩
4.列举重心到边缘的距离
5.选出指尖点
网上随便找了一张手的图
做完阈值操作后
寻找轮廓
计算重心的坐标
然后获取重心到轮廓距离。
下面这张图
横坐标是点的顺序
纵坐标是重心到轮廓边缘的距离
可以看出图中有 5 个峰值点。
这五个峰值点表示对应的就是 五个手指的位置。
检测效果图如下
换一张
检测结果还是比较精确的 。大拇指的点可能不满足条件未检测到。
如果用最小二乘拟合峰值附近的点几个点 可以获取亚像素精度的指尖点。
void gethandpoint(Mat frame){
Mat show_img;
frame.copyTo(show_img);
Mat derivative_img=cvCreateMat(300,900,show_img.type());
derivative_img.setTo(255);
GaussianBlur(frame, frame, Size(3, 3), 0);
threshold(frame,frame,240,255,THRESH_BINARY_INV);
vector<vector<Point> > contours;
vector<Vec4i> hierarchy;
findContours(frame, contours, hierarchy, CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE);
//获取轮廓
int index;
double area, maxArea(0);
for (int i=0; i < contours.size(); i++)
{
area = contourArea(Mat(contours[i]));
if (area > maxArea)
{
maxArea = area;
index = i;
}
}
//drawContours(frame, contours, index, Scalar(0, 0, 255), 2, 8, hierarchy );
Moments moment = moments(frame, true);
Point center(moment.m10/moment.m00, moment.m01/moment.m00);
//获取重心
//circle(show_img, center, 8 ,Scalar(0, 0, 255), CV_FILLED);
vector<Point> couPoint = contours[index];
float current(0), depth(50000),mosthigher(0);
//count Num
int q(0),m(0),k(0),j(0),notice(0),spos(0);
Point fingerTips_single;
Point p, n, r;
for(int i=1;i < couPoint.size();i++){
float pn = sqrt(float((couPoint[i].x - center.x) * (couPoint[i].x - center.x) + (couPoint[i].y - center.y) * (couPoint[i].y - center.y)) ); //计算重心到轮廓边缘的距离
line(derivative_img,cvPoint(i,300),cvPoint(i,300-pn/3),Scalar(0,0,0,0));
//line(show_img,couPoint[i],center,Scalar(0,0,0,0));
//cout<<int(pn)<<endl;
if (pn>=current)
{
current=pn;//找出第一次的峰值
}
else
{
m++;
if(m ==1 ){
notice=i;
fingerTips_single=couPoint[i];
depth=500000;
}
if (depth>=pn)
{
depth=pn;
k++;
//从峰值向下开始爬。
}
else
{
if(k>20)
{
//如果爬的步长 > 20 则认为是指尖的候选点。
// spos=notice;
if(notice<10)
{
notice= 10;
}
//更进一步的获取精确的指尖位置 枚举候选点周围的 10 个点 选出距离重心最大的点
for (int k = notice-10;k<notice+10;k++)
{
current = sqrt(float((couPoint[k].x - center.x) * (couPoint[k].x - center.x) + (couPoint[k].y - center.y) * (couPoint[k].y - center.y)) );
if(current>mosthigher){
mosthigher = current;
spos = k ;
}
}
mosthigher=0;
fingerTips_single =couPoint[spos];
//获得的指尖
cout<<"["<<fingerTips_single.x<<","<<fingerTips_single.y<<"]"<<endl;
circle(show_img,couPoint[spos],3,Scalar(0,0,0,0),5,CV_AA);
}
current=0 ;
m=0;
k=0;
k=0;
}
}
//waitKey(0);
}
imshow("hand",show_img);
imshow("derivative_img",derivative_img);
}
20130611