// version 2. jun hirabayashi jun@hirax.net

#include "stdafx.h"
#include <cv.h>
#include <highgui.h>
#include <stdio.h>
#include <shlobj.h>

#define IMAGE_VIEWER

char buffa[_MAX_PATH];

// 画像データ
IplImage* pImageSrc = 0;
IplImage* pImageGray = 0;
IplImage* pImageGray2 = 0;
IplImage* pImageSrc2 = 0;
IplImage* pImageSrc3 = 0;
IplImage* pImageDstTh = 0;
IplImage* pImageDstTh2 = 0;
IplImage* pImageDst = 0;

IplImage* pImageTemp = 0;
IplImage* pImageClosing = 0;

IplConvKernel* kernel = 0;

// パラメータ類
int scale = 26;
int scale2 = 10;
int th_scale = 74;
int th_scale2 = 34;
int keyboad = 0;
int noise_scale = 3;

int iNum = 0;

#define WIDTH 400

void ON_SCALE( int a );
void ON_SCALE2( int a );
void ON_SCALE3( int a );
void Gaussian( int a1, int a2 );
void Nancy( void );

//------ Nancy -----------------------
// エッジ部分とベタ部分の合成をする
void Nancy( void )
{
	pImageDstTh2 = cvCloneImage( pImageDstTh );
	CvSize size;
	CvSize size2;
	CvSize size3;
	CvSize size4;
	int step = 0;
	int step2 = 0;
	int step3 = 0;
	int step4 = 0;
	uchar* dst_data = 0;
	uchar* src_data = 0;
	uchar* src_data1 = 0;
	uchar* src_data2 = 0;
	cvGetRawData( pImageDstTh, &src_data, &step, &size );
	cvGetRawData( pImageDstTh2, &dst_data, &step2, &size2 );
	cvGetRawData( pImageSrc2, &src_data1, &step3, &size3 );
	cvGetRawData( pImageSrc3, &src_data2, &step4, &size4 );
	uchar *src = src_data;
	uchar *dst = dst_data;
	uchar *src1 = src_data1;
	uchar *src2 = src_data2;

	for( int y = 0; y < size.height; y++, 
		src += (step - 3 * size.width), dst += (step - 3 * size.width),
		src1 += (step3 - 3 * size.width), src2 += (step4 - 3 * size.width) )
		for( int x = 0; x < size.width; x++, dst += 3, src += 3,
			                                 src1 += 3, src2 += 3)
			if ( src2[0] > src1[0] || src2[0] < th_scale2  )
				if ( src2[0] < th_scale2 )
					if( src[ 0 ] == 255 )
				        dst[ 0 ] = dst[ 1 ] = dst[ 2 ] = 255;
					else
				        dst[ 0 ] = dst[ 1 ] = dst[ 2 ] = 0;
				else
					if( src[ 0 ] == 0 )
				        dst[ 0 ] = dst[ 1 ] = dst[ 2 ] = 0;
					else
				        dst[ 0 ] = dst[ 1 ] = dst[ 2 ] = 255;
			else
				if ( src2[0] > 255 - th_scale2 )
				    if( src[ 0 ] == 0 )
				        dst[ 0 ] = dst[ 1 ] = dst[ 2 ] = 255;
				    else
				        dst[ 0 ] = dst[ 1 ] = dst[ 2 ] = 0;
				else
				    if( src[ 0 ] == 0 )
				        dst[ 0 ] = dst[ 1 ] = dst[ 2 ] = 255;
				    else
				        dst[ 0 ] = dst[ 1 ] = dst[ 2 ] = 0;
}

//------Gaussian -----------------------
void Gaussian( int s1, int s2, int th )
{
	cvSub( pImageSrc2, pImageSrc3, pImageDst, NULL );
	cvThreshold( pImageDst, pImageDstTh, th, 255, CV_THRESH_BINARY );
    Nancy();
	kernel = cvCreateStructuringElementEx( noise_scale, noise_scale, 1, 1, CV_SHAPE_ELLIPSE, 0 );
	cvMorphologyEx( pImageDstTh2, pImageClosing, pImageTemp, kernel, CV_MOP_CLOSE, 1 );
	cvMorphologyEx( pImageClosing, pImageDstTh2, pImageTemp, kernel, CV_MOP_OPEN, 1 );
    cvShowImage( "作成画像", pImageDstTh2 );
}

//------Gaussian 1-----------------------
void ON_SCALE( int a )
{
	scale = a;
	if ( scale > 0 ) 
		cvSmooth( pImageSrc, pImageSrc2,
		        CV_GAUSSIAN, 0, 0, (double)scale / 10 );
	else
		cvCopy( pImageSrc, pImageSrc2 );
	Gaussian( scale, scale2, th_scale );
}
//------Gaussian 1-----------------------------
void ON_SCALE2( int a )
{
	scale2 = a;

	if ( ( scale + scale2 ) > 0 ) 
		cvSmooth( pImageSrc, pImageSrc3,
		        CV_GAUSSIAN, 0, 0, (double)( scale + scale2 ) / 10 );
	else
		cvCopy( pImageSrc, pImageSrc3 );
	Gaussian( scale, scale2, th_scale );
	
}
//----------------------------------------------
void ON_SCALE3( int a )
{
	th_scale = a;
	Gaussian( scale, scale2, th_scale );

}
//----------------------------------------------
void ON_SCALE4( int a )
{
	th_scale2 = a;
	Gaussian( scale, scale2, th_scale );
}
//----------------------------------------------
void ON_SCALE5( int a )
{
	noise_scale = a;
    if (noise_scale%2==0) noise_scale += 1;
//	noise_scale = noise_scale * 2 + 1;
	if (noise_scale<=1) noise_scale = 3;
	if (noise_scale>=8) noise_scale = 8;
	Gaussian( scale, scale2, th_scale );
}
//==================================================
int _tmain(int argc, _TCHAR* argv[])
{
    printf( "\tESC - プログラムの終了\n"
			"\ts - 画像を保存\n"
			"\t\n\n" );

	char* cFileName = 
		argc >= 2 ? argv[1] : (char*)"einstein.jpg";
	if ( ( pImageSrc = cvLoadImage( cFileName, 1 )  )
		== 0 ) return 0;

    cvNamedWindow( "オリジナル画像", CV_WINDOW_AUTOSIZE );
    cvShowImage( "オリジナル画像", pImageSrc );

	// グレースケールに変換
	CvSize cs;
	cs.width = pImageSrc->width;
	cs.height = pImageSrc->height;
	pImageGray = cvCreateImage(  cs , IPL_DEPTH_8U, 1 );
    cvSetZero( pImageGray );
    cvCvtColor( pImageSrc, pImageGray, CV_RGB2GRAY );
   
	// 画像サイズ変換
	cs.width = pImageSrc->width * 600 / pImageSrc->height;
	cs.height = 600;
	pImageGray2 = cvCreateImage(  cs , IPL_DEPTH_8U, 1 );
	cvResize( pImageGray, pImageGray2, CV_INTER_CUBIC );

	cs.width = pImageSrc->width * 600 / pImageSrc->height;
	cs.height = 600;	
	cvReleaseImage( &pImageSrc );
	pImageSrc = cvCreateImage(  cs , IPL_DEPTH_8U, 3 );
	cvCvtColor( pImageGray2, pImageSrc, CV_GRAY2RGB );

	// 画像バッファ作成
    pImageDst   = cvCloneImage( pImageSrc );     // Gaussian 1
    pImageSrc2  = cvCloneImage( pImageSrc );     // Gaussian 2
    pImageSrc3  = cvCloneImage( pImageSrc );     // Laplacian of Gassian
    pImageDstTh = cvCloneImage( pImageSrc );     // ThreshHold
	pImageTemp  = cvCloneImage( pImageSrc );
	pImageClosing  = cvCloneImage( pImageSrc );
	
    cvNamedWindow( "作成画像", 0);//CV_WINDOW_AUTOSIZE );
    
    cvCreateTrackbar( "細かさ",   "作成画像", &scale, 50, ON_SCALE );
	cvCreateTrackbar( "線の太さ", "作成画像", &scale2, 50, ON_SCALE2 );
	cvCreateTrackbar( "ベタ",     "作成画像", &th_scale2, 100, ON_SCALE4 );
	cvCreateTrackbar( "線際",     "作成画像", &th_scale, 256, ON_SCALE3 );
	cvCreateTrackbar( "単純に",   "作成画像", &noise_scale, 7, ON_SCALE5 );

	cvResizeWindow( "dst", WIDTH, WIDTH * pImageSrc2->height / pImageSrc2->width );
	cvResizeWindow( "dst2", WIDTH, WIDTH * pImageSrc2->height / pImageSrc2->width );
	cvResizeWindow( "result", WIDTH, WIDTH * pImageSrc->height / pImageSrc->width );
	cvResizeWindow( "作成画像", WIDTH, WIDTH * pImageSrc->height / pImageSrc->width );

	ON_SCALE( scale );
	ON_SCALE2( scale2 );
	ON_SCALE3( th_scale );
	ON_SCALE3( th_scale2 );
	ON_SCALE5( noise_scale );

	for(;;)
	{
		keyboad = cvWaitKey(0);
		if( keyboad=='\x1b' )	break;
		switch(keyboad)
		{
		case 's':
            char cSaveName[1024];
			sprintf( cSaveName, "%s.のパッチもん版画.%02d.jpg", cFileName, iNum++ );
			cvSaveImage( cSaveName, pImageDstTh2 );
			break;
		}
	}
	cvDestroyWindow( "オリジナル画像" );
	cvDestroyWindow( "作成画像" );

	cvReleaseImage( &pImageSrc );
	cvReleaseImage( &pImageGray );
	cvReleaseImage( &pImageGray2 );
	cvReleaseImage( &pImageSrc2 );
	cvReleaseImage( &pImageSrc3 );
	cvReleaseImage( &pImageDst );
	cvReleaseImage( &pImageDstTh );
	cvReleaseImage( &pImageDstTh2 );

	cvReleaseImage( &pImageTemp );
	cvReleaseImage( &pImageClosing );
	return 0;
}
