環境: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模型
2)閾值分割:根據多次實驗確定,正常黃種人的Cr分量大約在140~·175之間,Cb分量大約在100~120之間。
代碼如下
#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;
}
處理前:
處理後:
處理效果還可以接受,可以通過調整閾值範圍,圖像去噪等操作獲得更好的結果。
本文主要爲了後續的手勢跟蹤與識別做準備,用處理後的膚色圖來做人手或是人臉的檢測跟蹤都有不小的便利之處。