在复杂环境下,用Opencv手势和脸部区域提取

刚刚看完相关的opencv编程的书籍,于是自己想做点东西练练手

要求是:对给定的图片中人体的脸部和手势进行提取

现在先是对一张图片进行训练,再过度到小包(含有多张图片的文件夹),这是用摄像机对我同学进行拍摄的图片(希望他不会打我。。)

先读出一张图片,对图片进行简单的噪声和滤波处理,然后转换成灰度图进行处理。

这里我用的是高斯滤波,也可以用其他的方法,个人感觉高斯滤波效果好点

肤色模型提取

在这里我提供两种方法给大家,一个是改进的YCbCrd的肤色检测和HSV模型检测。

此处参考的链接https://blog.csdn.net/jacke121/article/details/77861696

改进的YCbCrd的肤色检测

 

具体是Hsu等人提出在YCbCr空间建立肤色模型。算法步骤如下:

(1)亮度补偿:R、G、B三通道5%非线性Gamma校正

(2)颜色空间转换:RGB颜色空间转换到YCbCr颜色空间;

(3)使用规则作肤色分割。

Image2.create(srcImage.rows, srcImage.cols, CV_8UC3);
float gamma = 0.95;
for (int r = 0;r < Image2.rows;r++)
{
for (int c = 0;c < Image2.cols;c++)
{
Image2.at<Vec3b>(r, c)[0] =(int) pow(srcImage.at<Vec3b>(r, c)[0], gamma);
Image2.at<Vec3b>(r, c)[1] = (int)pow(srcImage.at<Vec3b>(r, c)[1], gamma);
Image2.at<Vec3b>(r, c)[2] = (int)pow(srcImage.at<Vec3b>(r, c)[2], gamma);
}
}
Mat imgYcc;
cvtColor(Image2, imgYcc,COLOR_BGR2YCrCb);


cvtColor(srcImage, srcImage, COLOR_BGR2RGB);
Mat imgSkin;
imgSkin.create(srcImage.rows, srcImage.cols, CV_8UC3);
float Wcb = 46.97, Wcr = 38.76;
int WHCb = 14, WHCr = 10, WLCb = 23, WLCr = 20;
int Ymin = 16, Ymax = 235;
int Kl = 125, Kh = 188, WCb = 0, WCr = 0;
int CbCenter = 0, CrCenter = 0;
int skin;
for (int r = 0;r < srcImage.rows;r++)
{
for (int c = 0;c < srcImage.cols;c++)
{
skin = 0;
double  Y = imgYcc. at<Vec3b>(r, c)[0];
double  Cr = imgYcc.at<Vec3b>(r, c)[1];
double  Cb = imgYcc.at<Vec3b>(r, c)[2];
if (Y < Kl)
{
WCr = WLCr + (Y - Ymin)*(Wcr - WLCr) / (Kl - Ymin);
WCb = WLCb + (Y - Ymin)*(Wcb - WLCb) / (Kl - Ymin);
}
else if (Y > Kh)
{
WCr = WHCr + (Y - Ymax)*(Wcr - WHCr) / (Ymax - Kh);
WCb = WHCb + (Y - Ymax)*(Wcb - WHCb) / (Ymax - Kh);
CrCenter= 154 + (Y - Kh)*(154 - 132) / (Ymax - Kh);
CbCenter = 108 + (Y - Kh)*(118 - 108) / (Ymax - Kh);
}
if (Y<Kl || Y>Kh)
{
Cr = (Cr - CrCenter)*Wcr / WCr + 154;
Cb = (Cb - CbCenter)*Wcb / WCb + 108;
}
if (Cb > 77 && Cb < 127 && Cr>133 && Cr < 173)
skin = 1;
if (skin == 0)
{
imgSkin.at<Vec3b>(r, c)[0] = 0;
imgSkin. at<Vec3b>(r, c)[1] = 0;
imgSkin.at<Vec3b>(r, c)[2] = 0;
}
}

}

我开始用的该算法,不能说效果不好。只能说是我大意了,该算法运用在背景不是很复杂的情况下用有很好的效果,但是我给的图中,草地的颜色在此算法中产生了极大的干扰,话不多说我直接上效果图,你们自己感受一下。

 

HSV模型检测

这个原理我就不说了,在网上挺多的,大家可以自己在网上找找。

//HSV颜色空间的肤色模型检测
double H, S, V;
int skin;
cvtColor(srcImage, srcImage, COLOR_BGR2RGB);
Mat imgSkin2,imgHsv;
imgSkin2.create(srcImage.rows, srcImage.cols, CV_8UC3);
imgHsv.create(srcImage.rows, srcImage.cols, CV_8UC3);
cvtColor(srcImage, imgHsv, COLOR_RGB2HSV);
for (int r=0;r < imgHsv.rows;r++)
{
for (int c = 0;c < imgHsv.cols;c++)
{
skin = 0;
H = imgHsv.at<Vec3b>(r, c)[0];
S = imgHsv.at<Vec3b>(r, c)[1];
V = imgHsv.at<Vec3b>(r, c)[2];


if (((H >= 0) && (H <= 25/2 )) || ((H >= 335 / 2) && (H <= 360 / 2)))
if ((S > 0.2 * 255) && (S <= 0.6 * 255) && (V >= 0.4 * 255))
skin = 1;
if (skin == 0)
{
imgSkin2.at<Vec3b>(r, c)[0] = 0;
imgSkin2.at<Vec3b>(r, c)[1] = 0;
imgSkin2.at<Vec3b>(r, c)[2] = 0;
}
}
}

这个里面的参数H,S,V是判断肤色的重要依据。

效果图如下:

从效果上,明显比上面的算法要好很多,两种算法的运用是根据人所处的环境带来的影响运用的,如果环境与人体肤色相差较大,那么改进的YCbCrd的肤色检测要好很多。

膨胀

因为效果图里面的亮点太多,所以我们需要将它们去掉,除了膨胀,也可以用开运算,其目的是使分散的亮点集中,然后进行去杂处理。

绘制轮廓

将所有亮点的区域用轮廓的方式收集起来。

Mat threshold_output=Mat::zeros(g_dstImage.rows,g_dstImage.cols,CV_8UC1);
vector<vector<Point>> contours;
vector<Vec4i> hierachy;
//边缘检测
threshold(g_dstImage, threshold_output, 150, 255, THRESH_BINARY);
findContours(threshold_output, contours, hierachy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));
vector<vector<Point>> contours_poly(contours.size());
for (unsigned int i = 0;i < contours.size();i++)
approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true);
Mat drawing = Mat::zeros(threshold_output.size(), CV_8UC3);
for (int unsigned i = 0;i < contours.size();i++)

drawContours(drawing, contours_poly, i, Scalar(255, 0, 0), 1, 8, hierachy, 0, Point());

效果图:

 

 

然后得到轮廓面积和长度,去掉小的轮廓和面积,剩下的就只有手和脸了,只要再将手和脸外围轮廓的点集收集起来,就可以得到我们想要的区域了。

for (unsigned int i = 0;i < contours_poly.size();i++)
mu[i] = moments(contours_poly[i], false);

for (unsigned int i = 0;i < contours_poly.size();i++)
{
//printf(">通过m00计算出轮廓的面积和长度分别为:%.2f,%.2f\n", contourArea(contours_poly[i]), arcLength(contours_poly[i], true));
double area = contourArea(contours_poly[i]);
double length = arcLength(contours_poly[i], true);
if(area>2000.0&&length>314)
contours1.push_back(contours_poly[i]);



}

这里的2000和314,是通过我抽象计算得出来,具体看镜头的远近。

获取ROI区域及提取轮廓里面的像素

最后这一步比较简单,做一个mask,将刚刚收集的手和脸的点集放进去,然后CopyTo进去就行了

Mat masking(drawing.size(), CV_8U, Scalar::all(0));
Mat endimage(srcImage.rows, srcImage.cols, CV_8UC3);
drawContours(masking, contours1, -1, Scalar(255), CV_FILLED);

g_srcImage.copyTo(endimage, masking);

imshow("最终图", endimage);

最后的效果图为:

还是有缺陷比如手的损失,皮肤为啥是蓝色等,但是我不想改了,哈哈哈,能达到我预期的要求。

我多给几个例子感受一下:

因为刚开始做项目,所以很粗糙,希望大家可以谅解一下,比心~

源代码地址:https://download.csdn.net/download/qq_39027890/10533536

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值