指尖检测 & 手掌检测 & 手指弯曲程度检测

任务:得到手指的弯曲程度,进行评级(仅仅根据二维图像)。

方法:检测手指指尖,方法是首先阈值分割后检测指尖的曲率,找到符合要求的点,然后聚类,找出符合要求的在指尖上的点,最后得到指尖,得到指尖后,根据指尖的到手掌心的中心的距离程度,判断弯曲的程度。相应的截图如下所示:

原始图像:

CrCb图像

阈值分割图像:

指尖检测图像:

提取边缘和手掌心:

计算指尖到手掌心的距离,进行评级:

// Test.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include<opencv2\opencv.hpp>
#include<vector>
#include<iostream>
#include<string>
#include<algorithm>
using namespace cv;
using namespace std;
pair<Point, double> DetectInCircles(vector<Point> contour, Mat src)
{
	Mat dist_image;
	distanceTransform(src, dist_image, CV_DIST_L2, 3);
	int temp = 0, R = 0, cx = 0, cy = 0;
	int d;
	for (int i = 0; i<src.rows; i++)
		for (int j = 0; j<src.cols; j++)
		{
			/* checks if the point is inside the contour. Optionally computes the signed distance from the point to the contour boundary*/
			d = pointPolygonTest(contour, Point2f(j, i), 0);
			if (d>0)
			{
				temp = (int)dist_image.ptr<float>(i)[j];
				if (temp>R)
				{
					R = temp;
					cy = i;
					cx = j;
				}

			}
		}
	return make_pair(Point(cx, cy), R);
}
double max_distanceL2(vector<Point>& contour, Point center)
{
	double max = 0.0,tempL2;
	for (auto point : contour)
	{
		tempL2 = (point.x - center.x) ^ 2 + (point.y - center.y) ^ 2;
		if (tempL2>max)
		{
			max = tempL2;
		}
	}
	return sqrt(max);
}
void Getlevel(double distance, double maxlength, double minlength,Mat& image)
{
	int level = (int)((distance - minlength)*8.0 / (maxlength - minlength))+1;
	stringstream ss;
	string str;
	ss << level;
	ss >> str;
	putText(image, str, Point(50, 60), FONT_HERSHEY_SIMPLEX,2, Scalar(0,255,255),2);
	imshow("评级之后的图像", image);
}
const int num = 60;
const double thre = 0.8;
void CalCurvature(const vector<Point>& contour,int Kstep/*,Mat& image*/)
{
	Point rearPoint, prePoint;
	Point cur_pre, cur_rear;
	double cosangle;
	vector<double> cosVec;
	int N = contour.size();
	for (int i = 0; i < N; i++)
	{
		//P i-k
		(i - Kstep) >= 0 ? prePoint = contour[i - Kstep] : prePoint = contour[N - Kstep + i];
		//P i+k
		(i + Kstep) >= N ? rearPoint = contour[i + Kstep-N] : rearPoint = contour[Kstep + i];
		cur_pre = prePoint - contour[i];
		cur_rear = rearPoint - contour[i];
		double fenzi = cur_pre.x*cur_rear.x + cur_pre.y*cur_rear.y;
		double fenwu = sqrt(cur_pre.x*cur_pre.x + cur_pre.y*cur_pre.y)* sqrt(cur_rear.x*cur_rear.x + cur_rear.y*cur_rear.y);
		cosangle = (fenzi / fenwu);
		cosVec.push_back(cosangle);
	}
}
//聚类
int Clustering(vector<int>& indexVec, int Vecsize, vector<pair<int, int>>& index_label)
{
	//进行聚类处理
	//CV_Assert(indexVec.size() > 0);//排除只有一类的情况
	if (indexVec.size() == 0) return 0;
	int label = 0;
	index_label.push_back(make_pair(indexVec[0], label));
	for (int i = 1; i != indexVec.size(); ++i)
	{
		if (index_label[i - 1].first == indexVec[i] - 1 || index_label[i - 1].first == indexVec[i] - 2)
		{
			index_label.push_back(make_pair(indexVec[i], label));
		}
		else
		{
			++label;
			index_label.push_back(make_pair(indexVec[i], label));
		}
	}
	//是否可以收尾相连
	if (index_label[index_label.size() - 1].first == Vecsize || index_label[0].first == 0)//则首尾相连
	{
		for (auto & element : index_label)
		{
			if (element.second == label)
			{
				element.second = 0;
			}
		}
	}
	return label;
}
Point GetMidPoint(vector<int>& Classpoints, vector<Point> contour)
{
	int wei = Classpoints.size() - 1;
	int index = wei / 2;
	Point midPoint = contour[Classpoints[index]];
	Point weiPoint = contour[Classpoints[wei]];
	Point shouPoint = contour[Classpoints[0]];
	if (/*midPoint.y > shouPoint.y && */midPoint.y > weiPoint.y+10)
	{
		return Point(0, 0);
	}
	else
	{
		return midPoint;
	}
}
void Fingertip(int Kmax, int Kmin, const vector<Point>& contour,Mat& image)
{
	CV_Assert(Kmax > Kmin);
	Point rearPoint, prePoint;
	Point cur_pre, cur_rear;
	double sumcosangle=0.f;
	vector<double> cosVec;
	vector<int> indexVec;
	int N = contour.size();
	for (int i = 0; i != contour.size(); ++i)
	{
		for (int Kstep = Kmin; Kstep <= Kmax; ++Kstep)
		{
			(i - Kstep) >= 0 ? prePoint = contour[i - Kstep] : prePoint = contour[N - Kstep + i];
			//P i+k
			(i + Kstep) >= N ? rearPoint = contour[i + Kstep - N] : rearPoint = contour[Kstep + i];
			cur_pre = prePoint - contour[i];
			cur_rear = rearPoint - contour[i];
			double fenzi = cur_pre.x*cur_rear.x + cur_pre.y*cur_rear.y;
			double fenwu = sqrt(cur_pre.x*cur_pre.x + cur_pre.y*cur_pre.y)* sqrt(cur_rear.x*cur_rear.x + cur_rear.y*cur_rear.y);
			sumcosangle += (fenzi / fenwu);

		}
		cosVec.push_back(sumcosangle / (Kmax - Kmin + 1));
		sumcosangle = 0.f;
	}

	for (size_t i = 0; i < cosVec.size(); i++)
	{
		
		if (cosVec[i]>thre)//获取相应的索引
		{
			indexVec.push_back(i);
			cout << i << endl;
		}
	}
	vector<pair<int, int>> index_label;
	int sum_label = Clustering(indexVec, contour.size(), index_label);
	vector<vector<int>> classPoints;
	classPoints.resize(sum_label);

	for (int j = 0; j <= sum_label; ++j)
	{
		for (int i = 0; i < index_label.size(); ++i)
		{
			if (index_label[i].second==j)
			{
				classPoints[j].push_back(index_label[i].first);
			}
		}
	}
	vector<Point> maybeFinger;
	for (int i = 0; i != classPoints.size(); ++i)
	{
		Point center;
		center = GetMidPoint(classPoints[i], contour);
		if (center==Point(0,0))
		{
			continue;
		}
		maybeFinger.push_back(center);
		circle(image, center, 4, Scalar(0, 0, 255),3);
	}

	imshow("指尖检测图像", image);
}
int _tmain(int argc, _TCHAR* argv[])
{
	Mat srcImage_Big = imread("7.png");
	//进行一定的比例放缩
	double scale = 0.3;
	Mat srcImage,CrCbImage;
	resize(srcImage_Big, srcImage, srcImage.size(), scale, scale);
	
	imshow("原始图像", srcImage);
	//皮肤检测
	cvtColor(srcImage, CrCbImage, CV_BGR2YCrCb);
	imshow("Crcb图像", CrCbImage);
	//切割图像
	vector<Mat> channels;
	Mat Y, Cr, Cb;
	split(CrCbImage, channels);
	Y = channels.at(0);
	Cr = channels.at(1);
	Cb = channels.at(2);
	Mat dstImage;
	dstImage.create(CrCbImage.rows, CrCbImage.cols, CV_8UC1);

	//找出符合要求的区域图像
	/*遍历图像,将符合阈值范围的像素设置为255,其余为0*/
	//正常黄种人的Cr分量大约在140~·175之间,Cb分量大约在100~120之间
	for (int j = 1; j < Y.rows - 1; j++)
	{
		uchar* currentCr = Cr.ptr< uchar>(j);
		uchar* currentCb = Cb.ptr< uchar>(j);
		uchar* current = dstImage.ptr< uchar>(j);
		for (int i = 1; i < Y.cols - 1; i++)
		{
			if ((currentCr[i] > 125) && (currentCr[i] < 180) && (currentCb[i] > 80) && (currentCb[i] < 130))
				current[i] = 255;
			else
				current[i] = 0;
		}
	}
	//erode(dstImage, dstImage, Mat());
	Mat element = getStructuringElement(MORPH_CROSS, Size(3, 3));
	dilate(dstImage, dstImage, element);//白色区域膨胀
	erode(dstImage, dstImage, element);//白色区域腐蚀
	imshow("阈值分割之后的图像", dstImage);
	vector<vector<Point>> contours;
	//找出轮廓
	findContours(dstImage, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
	vector<vector<Point> >hull(contours.size());
	for (int i = 0; i < contours.size(); i++)
	{
		convexHull(Mat(contours[i]), hull[i], false);
	}
	int max_index;
	double maxarea = 0.0;
	for (size_t i = 0; i != contours.size();++i)
	{
		//cout << "area      " << contourArea(contour) << endl;
		double temp = contourArea(contours[i]);
		if (temp>maxarea)
		{
			maxarea = temp;
			max_index = i;
		}
	}
	//寻找指尖
	/*CalCurvature(contours[max_index], num, srcImage);*/
	Fingertip(70, 50, contours[max_index], srcImage);

	/// 绘出凸包
	Mat drawing = Mat::zeros(dstImage.size(), CV_8UC3);
	Scalar color = Scalar(100,100,100);
	drawContours(drawing, hull, max_index, color, 1, 8, vector<Vec4i>(), 0, Point());
	vector<Point> region;//逼近区域
	approxPolyDP(contours[max_index], region, CV_POLY_APPROX_DP, true);
	Moments dstMoments = moments(region);
	int px = dstMoments.m10 / dstMoments.m00;
	int py = dstMoments.m01 / dstMoments.m00;
	cout << "原图的重心:" << dstMoments.m10/dstMoments.m00 <<"     ";
	cout << dstMoments.m01 / dstMoments.m00 << endl;
	circle(drawing, Point(px, py),3,Scalar(100,255,0));
	//寻找掌心重心
	pair<Point, double> m = DetectInCircles(contours[max_index], dstImage);
	cout << m.first.x << " " << m.first.y << m.second << endl;
	circle(drawing, m.first, 3, Scalar(0, 0, 255), 2);
	circle(drawing, m.first, m.second, Scalar(0, 0, 255), 1);
	cout << "相距距离" << endl;
	cout << max_distanceL2(contours[max_index], m.first);
	cout << "相距距离比例" << endl;
	cout << max_distanceL2(contours[max_index], m.first)/m.second;
	imshow("提取边缘之后的图像", drawing);
	Getlevel(max_distanceL2(contours[max_index], m.first), 10.5, 16, srcImage);
	waitKey(0);
	return 0;
}






  • 2
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值