//---------------------------------------------------------
// 概要      : 肌色領域を追跡する
// File Name : tutorial_03.cpp
// Library   : OpenCV for MS-Windows 1.0
//---------------------------------------------------------

#include <stdio.h>
#include <cv.h>
#include <cxcore.h>
#include <highgui.h>

#define WIDTH	640	//	キャプチャ画像の横幅
#define HEIGHT	480	//	キャプチャ画像の縦幅

#define THRESH_BOTTOM	10		//	色相値の下限の閾値
#define THRESH_TOP	55			//	色相値の上限の閾値
#define THRESHOLD_MAX_VALUE	255	//	2値化の際に使用する最大値

int main( int argc, char **argv ) { 
	int key;							//	キー入力用の変数
	CvCapture *capture = NULL;			//	ビデオキャプチャ構造体へのポインタ
	IplImage *frameImage;				//	キャプチャ画像用IplImage

	//画像を生成する
	IplImage *backgroundImage = cvCreateImage( cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 1 );	//	背景画像用IplImage
	IplImage *grayImage = cvCreateImage( cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 1 );			//	グレースケール画像用IplImage
	IplImage *differenceImage = cvCreateImage( cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 1 );	//	差分画像用IplImage

	IplImage *hsvImage = cvCreateImage( cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 3 );			//	HSV画像用IplImage
	IplImage *hueImage = cvCreateImage( cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 1 );			//	色相(H)情報用IplImage
	IplImage *saturationImage = cvCreateImage( cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 1 );	//	彩度(S)情報用IplImage
	IplImage *valueImage = cvCreateImage( cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 1 );			//	明度(V)情報用IplImage
	IplImage *thresholdImage1 = cvCreateImage( cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 1 );	//	色相値がTHRES_BOTTOMより大きい領域用IplImage
	IplImage *thresholdImage2 = cvCreateImage( cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 1 );	//	色相値がTHRES_TOP以下の領域用IplImage
	IplImage *thresholdImage3 = cvCreateImage( cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 1 );	//	thresholdImage1とthresholdImage2のAND演算結果用IplImage
	IplImage *faceImage = cvCreateImage( cvSize(WIDTH, HEIGHT), IPL_DEPTH_8U, 1 );			//	顔領域の抽出結果用IplImage
	
	char windowNameCapture[] = "Capture"; 	//	キャプチャ画像を表示するウィンドウの名前
	char windowNameFace[] = "Face";			//	顔領域抽出結果を表示するウィンドウの名前
    
	//	カメラを初期化する
	if ( ( capture = cvCreateCameraCapture( -1 ) ) == NULL ) {
		//	カメラが見つからなかった場合
		printf( "カメラが見つかりません\n" );
		return -1;
	}

	//	ウィンドウを生成する
	cvNamedWindow( windowNameCapture, CV_WINDOW_AUTOSIZE );
	cvNamedWindow( windowNameFace, CV_WINDOW_AUTOSIZE );

	//	初期背景を設定するためにカメラから画像を取得
	frameImage = cvQueryFrame( capture );
	//	frameImageをグレースケール化し、背景画像とする
	cvCvtColor( frameImage, backgroundImage, CV_BGR2GRAY );

	//	メインループ
	while ( 1 ) {
		//	captureの入力画像1フレームをframeImageに格納する
		frameImage = cvQueryFrame( capture );
		//	frameImageをグレースケール化したものを、grayImageに格納する
		cvCvtColor( frameImage, grayImage, CV_BGR2GRAY );
		//	grayImageと背景画像との差分をとる
		cvAbsDiff( grayImage, backgroundImage, differenceImage );
		
		//	frameImageをBGRからHSVに変換する
		cvCvtColor( frameImage, hsvImage, CV_BGR2HSV );
		//	HSV画像をH、S、Vに分ける
		cvSplit( hsvImage, hueImage, saturationImage, valueImage, NULL );
		//	色相が肌色に近い部分を抽出、その部分のみ出力する
		cvThreshold( hueImage, thresholdImage1, THRESH_BOTTOM, THRESHOLD_MAX_VALUE, CV_THRESH_BINARY );
		cvThreshold( hueImage, thresholdImage2, THRESH_TOP, THRESHOLD_MAX_VALUE, CV_THRESH_BINARY_INV );
		cvAnd( thresholdImage1, thresholdImage2, thresholdImage3, NULL );
		
		//	背景差分画像と肌色領域とのANDをとる
		cvAnd( differenceImage, thresholdImage3, faceImage, NULL );

		if ( faceImage->origin == 0 ) {
			//　左上が原点の場合
			cvFlip( faceImage, faceImage, 0 );
		}

		//	画像を表示する
		cvShowImage( windowNameCapture, frameImage );
		cvShowImage( windowNameFace, faceImage );
		
		//	キー入力判定
		key = cvWaitKey( 1 );
		if ( key == 'q' ) {
			//	'q'キーが押されたらループを抜ける
			break;
		} else if ( key == 'b' ) {
			//	'b'キーが押されたら、その時点での画像を背景画像とする
		   frameImage = cvQueryFrame( capture );
		    cvCvtColor( frameImage, backgroundImage, CV_BGR2GRAY );
		}
}

	//	キャプチャを解放する
	cvReleaseCapture( &capture );
	//	メモリを解放する
	cvReleaseImage( &backgroundImage );
	cvReleaseImage( &grayImage );
	cvReleaseImage( &differenceImage );
	cvReleaseImage( &hsvImage );
	cvReleaseImage( &hueImage );
	cvReleaseImage( &saturationImage );
	cvReleaseImage( &valueImage );
	cvReleaseImage( &thresholdImage1 );
	cvReleaseImage( &thresholdImage2 );
	cvReleaseImage( &thresholdImage3 );
	cvReleaseImage( &faceImage );
	//	ウィンドウを破棄する
	cvDestroyWindow( windowNameCapture );
	cvDestroyWindow( windowNameFace );
	
	return 0;
}