opencv学习笔记(一):基于YCrCb颜色空间的肤色检测
颜色空间YCrCb
HSV颜色空间中H、S、V的标准差相对于YCrCb空间的标准差要大。而在YCrCb空间中,Y的标准差要明显大于其他两者。标准差越大则表明其值在亮度变化时波动大,因此选择标准差小的颜色通道比较好。
在人脸检测中也常常用到YCrCb空间,因为一般的图像都是基于RGB空间的,在RGB空间里人脸的肤色受亮度影响相当大,所以肤色点很难从非肤色点中分离出来,也就是说在此空间经过处理后,肤色点是离散的点,中间嵌有很多非肤色,这为肤色区域标定(人脸标定、眼睛等)带来了难题。如果把RGB转为YCrCb空间的话,可以忽略Y(亮度)的影响,因为该空间受亮度影响很小,肤色会产生很好的类聚。这样就把三维的空间将为二维的CrCb,肤色点会形成一定得形状,如:人脸的话会看到一个人脸的区域,手臂的话会看到一条手臂的形态,对处理模式识别很有好处。
YUV与RGB相互转换的公式如下(RGB取值范围均为0-255)︰
Y = 0.299R + 0.587G + 0.114B
U = -0.147R - 0.289G + 0.436B
V = 0.615R - 0.515G - 0.100B
R = Y + 1.14V G = Y - 0.39U - 0.58V B = Y + 2.03U
#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); //CV_EXPORTS void split(const Mat& m, vector<Mat>& mv );
Y = channels.at(0);
Cr = channels.at(1);
Cb = channels.at(2);
result.create(frame.rows, frame.cols, CV_8UC1); //一般的图像文件格式使用的是 Unsigned 8bits,CvMat矩阵对应的参数类型就是CV_8UC1,CV_8UC2,CV_8UC3(最后的1、2、3表示通道数,譬如RGB3通道就用CV_8UC3)
/*遍历图像,将符合阈值范围的像素设置为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] > 30) && (currentCr[i] < 115) && (currentCb[i] > 90) && (currentCb[i] < 240))
current[i] = 255;
else
current[i] = 0;
}
}
imshow("frame", frame);
imshow("result", result);
if (waitKey(30) >= 0)
stop = true;
}
cv::waitKey();
return 0;
}
识别蓝色,参数为:
for (int i = 1; i < Y.cols - 1; i++)
{
if ((currentCr[i] > 30) && (currentCr[i] < 115) && (currentCb[i] > 140) && (currentCb[i] < 240))
current[i] = 255;
else
current[i] = 0;
}
此时受背景的灯的边缘光影响较小,略有噪点,且离摄像头的距离的影响也较大,深色浅色也有差别。