//---------------------------------------------------------
// 概要      : 画像の合成
// File Name : chromakey.cpp
// Library   : OpenCV for MS-Windows 1.0
//---------------------------------------------------------

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

#define TRACKBAR_MAX_VALUE	255	//	トラックバーの最大値
#define MIN_VALUE	0			//	下限の閾値の初期値
#define MAX_VALUE	255			//	上限の閾値の初期値

//	閾値を保存する構造体
typedef struct border {
	int blue;	//	青の閾値
	int green;	//	緑の閾値
	int red;	//	赤の閾値
} BORDER;

int main( int argc, char **argv ) {
	int key;	//	キー入力用の変数
	BORDER lower = { MIN_VALUE, MIN_VALUE, MIN_VALUE };	//	下限の閾値
	BORDER upper = { MAX_VALUE, MAX_VALUE, MAX_VALUE };	//	上限の閾値

	char windowNameBackground[] = "Background";	//	背景表示するウィンドウの名前
	char windowNameChromakey[] = "Chromakey"; 	//	合成結果を表示するウィンドウの名前
	char windowNameExtract[] = "Extract"; 	//	抽出された物体を表示するウィンドウの名前
	char windowNameObject[]	 = "Object"; 	//	抽出された物体を表示するウィンドウの名前

	char trackbarNameLowerBlue[] = "LowerBlue";
	char trackbarNameUpperBlue[] = "UpperBlue";
	char trackbarNameLowerGreen[] = "LowerGreen";
	char trackbarNameUpperGreen[] = "UpperGreen";
	char trackbarNameLowerRed[] = "LowerRed";
	char trackbarNameUpperRed[] = "UpperRed";

	//	合成先画像と合成する物体の画像を読み込む
	IplImage *backgroundImage = cvLoadImage( "image/background.bmp", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR );	//	背景画像用IplImage
	IplImage *objectImage = cvLoadImage( "image/object.bmp", CV_LOAD_IMAGE_ANYDEPTH | CV_LOAD_IMAGE_ANYCOLOR );			//	合成対象画像用IplImage
	
	if ( backgroundImage == NULL || objectImage == NULL ) {
		//	画像が見つからなかった場合
		printf( "画像が見つかりません\n" );
		return -1;
	}

	//	画像サイズを得る
	CvSize sizeOfBackground	= cvGetSize( backgroundImage );
	CvSize sizeOfObject = cvGetSize( objectImage );

	if ( sizeOfBackground.height != sizeOfObject.height ||
			sizeOfBackground.width != sizeOfObject.width ) {
		//	画像のサイズが異なった場合
		printf( "画像のサイズが異なります\n" );
		return -1;
	}

	//	画像を生成する
	IplImage *maskImage = cvCreateImage( sizeOfObject, IPL_DEPTH_8U, 1 );			//	マスク用IplImage
	IplImage *inverseMaskImage = cvCreateImage( sizeOfObject, IPL_DEPTH_8U, 1 );	//	マスク反転用IplImage

	IplImage *extractedBackgroundImage = cvCreateImage( sizeOfObject, IPL_DEPTH_8U, 3 );	//	合成物体を除いた背景画像用IplImage
	IplImage *extractedObjectImage = cvCreateImage( sizeOfObject, IPL_DEPTH_8U, 3 );		//	合成物体が抽出された画像用IplImage
	IplImage *destinationImage = cvCreateImage( sizeOfObject, IPL_DEPTH_8U, 3 );			//	合成結果画像用IplImage

	//ウィンドウを生成する
	cvNamedWindow( windowNameBackground, CV_WINDOW_AUTOSIZE );
	cvNamedWindow( windowNameObject, CV_WINDOW_AUTOSIZE );
	cvNamedWindow( windowNameChromakey, CV_WINDOW_AUTOSIZE );
	cvNamedWindow( windowNameExtract, CV_WINDOW_AUTOSIZE );

	//	画像を表示する
	cvShowImage( windowNameBackground, backgroundImage );
	cvShowImage( windowNameObject, objectImage );
	
	//	トラックバーを生成する
	cvCreateTrackbar( trackbarNameLowerBlue, windowNameChromakey, &lower.blue, TRACKBAR_MAX_VALUE, NULL);
	cvCreateTrackbar( trackbarNameUpperBlue, windowNameChromakey, &upper.blue, TRACKBAR_MAX_VALUE, NULL);
	cvCreateTrackbar( trackbarNameLowerGreen, windowNameChromakey, &lower.green, TRACKBAR_MAX_VALUE, NULL);
	cvCreateTrackbar( trackbarNameUpperGreen, windowNameChromakey, &upper.green, TRACKBAR_MAX_VALUE, NULL);
	cvCreateTrackbar( trackbarNameLowerRed, windowNameChromakey, &lower.red, TRACKBAR_MAX_VALUE, NULL);
	cvCreateTrackbar( trackbarNameUpperRed, windowNameChromakey, &upper.red, TRACKBAR_MAX_VALUE, NULL);

	//	メインループ
	//	抜き出すRGBの範囲を用意　通常B,G,Rの順に格納されることに注意　
	//	範囲は最小値以上最大値未満になるのでupperを+1しておく
	while ( 1 ) {
		CvScalar lowerValue = cvScalar( lower.blue, lower.green, lower.red );
		CvScalar upperValue = cvScalar( upper.blue+1, upper.green+1, upper.red+1 );

		//	RGB各チャンネルごとに範囲内の値以外の画素をマスクに設定する
		cvInRangeS( objectImage, lowerValue, upperValue, maskImage );

		//	背景画像のうち合成する物体部分の画素値を0にする
		cvSetZero( extractedBackgroundImage );
		cvCopy( backgroundImage, extractedBackgroundImage, maskImage );

		//	マスク画像の0と1を反転する
		cvNot( maskImage, inverseMaskImage );

		//	トラックバーの条件を満たす合成物体が抽出された画像を作成 
		cvSetZero( extractedObjectImage );
		cvCopy( objectImage, extractedObjectImage, inverseMaskImage );

		//	背景画像と合成物体画像の合成
		cvAdd( extractedBackgroundImage, extractedObjectImage, destinationImage, NULL);

		//	画像を表示する
		cvShowImage( windowNameExtract,	extractedObjectImage );
		cvShowImage( windowNameChromakey, destinationImage );
		
		//	キー入力判定
		key = cvWaitKey( 1 );
		if ( key == 'q' ) {
			//	'q'キーが押されたらループを抜ける
			break;
		}
	}

	//	メモリを解放する
	cvReleaseImage( &objectImage );
	cvReleaseImage( &extractedObjectImage );
	cvReleaseImage( &extractedBackgroundImage );
	cvReleaseImage( &maskImage );
	cvReleaseImage( &inverseMaskImage );
	cvReleaseImage( &destinationImage );
	//	ウィンドウを破棄する
	cvDestroyWindow( windowNameBackground );
	cvDestroyWindow( windowNameObject );
	cvDestroyWindow( windowNameChromakey );
	cvDestroyWindow( windowNameExtract );

	return 0;
}