void skinExtract(const Mat &frame, Mat &skinArea)
{
Mat YCbCr;
vector<Mat> planes;
cvtColor(frame, YCbCr, CV_RGB2YCrCb);
split(YCbCr, planes);
MatIterator_<uchar> it_Cb = planes[1].begin<uchar>(),
it_Cb_end = planes[1].end<uchar>();
MatIterator_<uchar> it_Cr = planes[2].begin<uchar>();
MatIterator_<uchar> it_skin = skinArea.begin<uchar>();
for( ; it_Cb != it_Cb_end; ++it_Cr, ++it_Cb, ++it_skin)
{
if (138 <= *it_Cr && *it_Cr <= 170 && 100 <= *it_Cb && *it_Cb <= 127)
*it_skin = 255;
else
*it_skin = 0;
}
dilate(skinArea, skinArea, Mat(5, 5, CV_8UC1), Point(-1, -1));
erode(skinArea, skinArea, Mat(5, 5, CV_8UC1), Point(-1, -1));
}
方法二:曲率分析法
见下图,可以看到,在指尖和两指之间的凹槽的曲率是最大的。假设手的轮廓是通过一系列点{Pi}来保存的,那么我们可以通过 [Pi, Pi-k]和 [Pi, Pi+k] 两个向量的内积(点乘)来寻找高曲率的点,如下图的Φ和β的计算结果,因为他们的两个向量的夹角趋向于90度,也就是它们的内积趋向于0,所以内积结果越小,曲率越大,我们只需要设置一个阈值,就可以把手的指尖和两指之间的凹槽都可以找到。
那么如何分辨指尖和两指之间的凹槽呢?我们可以通过两个向量的叉乘来计算,我们想象下手处于3D空间中,那么就还有一个z方向,β中两个向量的叉乘是大于0的(叉乘方向垂直纸面朝向你,z轴正方向),而Φ处的叉乘是小于0的(叉乘方向垂直纸面远离你,z轴负方向)。
综上,通过在每个点上构造两个向量,比较他们的点乘和叉乘就可以确定指尖了。
简单的代码如下:
1、对图像做高斯模糊;
2、肤色分割(背景不要有类肤色,如果有,就需要加其他信息来排除干扰);
3、找到手轮廓;
4、寻找指尖。