OpenCV HSVで肌色検出

肌色検出について

肌色といっても人によって違いがあり、さらにその場の明るさや暗さなどによっても色がだいぶ変わる。
そのため、RGBを使って肌検出を行おうとすると、暗い場所では全体のRGBは低く、明るい場所では全体的にRGBが高くなるので、RGBで肌色を検出しようと閾値の範囲が広くなってしまって必要な肌色以外の色も検出するようになってしまうだろう。
そのため、肌色検出ではRGBを使わずHSVYIQYCbCrといった色空間を使うようだ。

今回はHSVを使って肌色検出を行なってみる。

HSVでの肌色検出の場合H(Hue)色相をつかって判断する

人間の肌の色はだいたい0゜〜30゜位の間に入るようなので、その範囲に閾値を設定してやれば肌色を検出できる。
OpenCVで注意が必要なのはHueの値は0〜180となっているので、OpenCVの場合の閾値の設定は0〜15くらいを設定してやるといい。

これだけでも肌色部分は検出されるが、かなり余分な部分も検出される。

これは彩度と明度の関係やノイズの影響で黒や白、灰色っぽい色も光の関係などで検出されてしまうようだ。

彩度Sと明度Vにも閾値を指定した。
今回の画像は50<=S<=255 , 50<=V<=255 とした。

その結果がこちら

なかなか良い感じに検出されている。

後ろの方にあるのは木目調の電子ピアノだがそれも検出されてしまっている。
当たり前だが肌じゃなくても肌の色と似てるものがあれば検出されてしまう。
しかし、動画像でカメラが固定してあり、人間の肌のみを検出したい場合は、背景差分を使うことで人間の肌のみを検出することも可能だろう。


最後にソースコード

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

int main(int argc, char *argv[])
{
	cv::VideoCapture cap(0);
	cap.set(CV_CAP_PROP_FRAME_WIDTH, 320);
	cap.set(CV_CAP_PROP_FRAME_HEIGHT, 240);
	if(!cap.isOpened()) 
	{
		printf("カメラが検出できませんでした");
		return -1;
	}
	cv::Mat input_img;
	cv::Mat hsv_skin_img= cv::Mat(cv::Size(320,240),CV_8UC3);
	cv::Mat smooth_img;
	cv::Mat hsv_img;
	
	cv::namedWindow("input_img", CV_WINDOW_AUTOSIZE);
	cv::namedWindow("hsv_skin_img", CV_WINDOW_AUTOSIZE);

	while(1)
	{
		hsv_skin_img = cv::Scalar(0,0,0);
		cap >> input_img;
		cv::medianBlur(input_img,smooth_img,7);	//ノイズがあるので平滑化
		cv::cvtColor(smooth_img,hsv_img,CV_BGR2HSV);	//HSVに変換		
		for(int y=0; y<240;y++)
		{
			for(int x=0; x<320; x++)
			{
				int a = hsv_img.step*y+(x*3);
				if(hsv_img.data[a] >=0 && hsv_img.data[a] <=15 &&hsv_img.data[a+1] >=50 && hsv_img.data[a+2] >= 50 ) //HSVでの検出
				{
					hsv_skin_img.data[a] = 255; //肌色部分を青に
				}
			}
		}
		cv::imshow("input_img",input_img);
		cv::imshow("hsv_skin_img",hsv_skin_img);	
		if(cv::waitKey(30) >=0)
		{
			break;	
		}
	}
}