环境:Windows+VisualStudio2015+ C++ +OpenCV2.7.9
之前尝试写过基于HSV颜色空间的肤色识别程序,发现效果不甚理想,环境噪声比较大,而且光照变化时的检测效果不好。
正好看到了一篇论文《基于HSV与YCrCb颜色空间进行肤色检测的研究》。里面针对HSV和YCrCb空间的各个通道统计了一组不同光照条件下的肤色图像,得到了如下结果
可以看出HSV颜色空间中H、S、V的标准差相对于YCrCb空间的标准差要大。而在YCrCb空间中,Y的标准差要明显大于其他两者。标准差越大则表明其值在亮度变化时波动大,因此选择标准差小的颜色通道比较好。
因此本次我选择了Cr与Cb通道做肤色检测(YCrCb颜色空间中,Y代表的是亮度,Cr 与Cb代表的都是色度,与亮度关系小,符合前述实验中得到的结果)
思路如下:
1)将RGB模型转换为YCbCr模型
代码如下
#include "stdafx.h" #include <iostream> #include <vector> #include <opencv2/core/core.hpp> #include <opencv2/features2d/features2d.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/calib3d/calib3d.hpp> using namespace std; using namespace cv; int main() { VideoCapture cap(0); if (!cap.isOpened()) { return -1; } namedWindow("result"); namedWindow("frame"); Mat frame; Mat result, tmp; Mat Y, Cr, Cb; vector<Mat> channels; bool stop = false; while (!stop) { cap >> frame; //读取视频帧 frame.copyTo(tmp); //拷贝备份 /*转换颜色空间并分割颜色通道*/ cvtColor(tmp, tmp, CV_BGR2YCrCb); split(tmp, channels); Y = channels.at(0); Cr = channels.at(1); Cb = channels.at(2); result.create(frame.rows, frame.cols, CV_8UC1); /*遍历图像,将符合阈值范围的像素设置为255,其余为0*/ for (int j = 1; j < Y.rows - 1; j++) { uchar* currentCr = Cr.ptr< uchar>(j); uchar* currentCb = Cb.ptr< uchar>(j); uchar* current = result.ptr< uchar>(j); for (int i = 1; i < Y.cols - 1; i++) { if ((currentCr[i] > 137) && (currentCr[i] < 175) && (currentCb[i] > 100) && (currentCb[i] < 118)) current[i] = 255; else current[i] = 0; } } imshow("frame", frame); imshow("result", result); if (waitKey(30) >= 0) stop = true; } cv::waitKey(); return 0; }处理前:
处理后:
处理效果还可以接受,可以通过调整阈值范围,图像去噪等操作获得更好的结果。
本文主要为了后续的手势跟踪与识别做准备,用处理后的肤色图来做人手或是人脸的检测跟踪都有不小的便利之处。