任务:得到手指的弯曲程度,进行评级(仅仅根据二维图像)。
方法:检测手指指尖,方法是首先阈值分割后检测指尖的曲率,找到符合要求的点,然后聚类,找出符合要求的在指尖上的点,最后得到指尖,得到指尖后,根据指尖的到手掌心的中心的距离程度,判断弯曲的程度。相应的截图如下所示:
原始图像:
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;
}