#include "Detect.h"

//-------------------------------------------------------------------------------
// コンストラクタ
CDetect::CDetect(void)
	: m_NoUse(false)
{
	m_Input    = NULL;
	m_Template = NULL;
	m_PatchPos = NULL;
}

//-------------------------------------------------------------------------------
// デストラクタ
CDetect::~CDetect(void)
{
	SAFE_RELEASEIMG(m_Input);
	SAFE_RELEASEIMG(m_Template);
	SAFE_DELETEA(m_PatchPos);
}

//-------------------------------------------------------------------------------
// 処理画像をセット
bool CDetect::SetImage(const char *filename)
{
	SAFE_RELEASEIMG(m_Input);
	m_Input = cvLoadImage(filename);
	printf("Reading : %s\n", filename);
	if (!m_Input) ERROR_RET("Can't open input file for detection.");

	return true;
}

//-------------------------------------------------------------------------------
// テンプレート画像をセット
bool CDetect::SetTemplate(const char *filename)
{
	SAFE_RELEASEIMG(m_Template);
	m_Template = cvLoadImage(filename);
	if (!m_Template) ERROR_RET("Can't open template file.");

	return true;
}

//-------------------------------------------------------------------------------
// 検出
bool CDetect::Matching(CvPoint *matchPos, double *matchAngle)
{
	// 画像サイズ(ROIに対応）
	int width, height;
	if (m_Input->roi) width = m_Input->roi->width, height = m_Input->roi->height;
	else			  width = m_Input->width,      height = m_Input->height;

	// 方向別テンプレートマッチング
	double gMinC = -1.0;
	double startTime = omp_get_wtime();

	// 角度のループを並列処理する
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic)
#endif
	for(int i = 0; i < (int)(360.0 / m_RotateStep); i++)
	{
		double angle = m_RotateStep * i;

		// マッチングの準備
		CvMat *rotateMat = cvCreateMat(2, 3, CV_32FC1);
		IplImage *rotateTemplate = cvCreateImage(cvGetSize(m_Template), 
			IPL_DEPTH_8U, COLOR);
		IplImage *match = cvCreateImage(
			cvSize(width - m_Template->width + 1, height - m_Template->height + 1),
			IPL_DEPTH_32F , 1);

		// テンプレートの回転
		cv2DRotationMatrix(cvPoint2D32f(m_Template->width/2.0, 
			m_Template->height/2.0), angle, 1.0, rotateMat);
		cvWarpAffine(m_Template, rotateTemplate, rotateMat, 
			CV_WARP_FILL_OUTLIERS, cvScalarAll(255.0));

		// マッチング
		cvMatchTemplate(m_Input, rotateTemplate, match, CV_TM_SQDIFF_NORMED);
		CvPoint minPos, maxPos;
		double minC, maxC;
		cvMinMaxLoc(match, &minC, &maxC, &minPos, &maxPos);

		// 最大相関の検出（共有変数に書き込むのでクリティカルにする）
		#pragma omp critical(c1)
		{
			if (minC < gMinC || gMinC < 0)
			{
				*matchAngle = angle;
				gMinC = minC;
				matchPos->x = minPos.x + (m_Template->width  / 2);
				matchPos->y = minPos.y + (m_Template->height / 2);
				if (m_Input->roi)
				{
					matchPos->x += m_Input->roi->xOffset;
					matchPos->y += m_Input->roi->yOffset;
				}
			}
		}
		

		// 画像バッファの解放
		cvReleaseImage(&rotateTemplate);
		cvReleaseImage(&match);
		cvReleaseMat(&rotateMat);
	}

	double elapsedTime = (omp_get_wtime() - startTime) * 1000.0;
	printf("Matching Angle %3.0f, Coef %.4f, Position(%4d, %4d), Time %.0fms\n", 
		*matchAngle, gMinC, matchPos->x, matchPos->y, elapsedTime);

	return true;
}

//-------------------------------------------------------------------------------
// パッチの平均色の計算
bool CDetect::CalcPatchColor(CvMat **rgb)
{
	SAFE_RELEASEMAT(*rgb);
	*rgb = cvCreateMat(m_NumPatch, COLOR, CV_32FC1);

	for (int i = 0; i < m_NumPatch; i++)
	{
		int x1 = m_PatchPos[i].x - m_PatchSize;
		int y1 = m_PatchPos[i].y - m_PatchSize;
		int x2 = m_PatchPos[i].x + m_PatchSize;
		int y2 = m_PatchPos[i].y + m_PatchSize;
		int psize = m_PatchSize * 2 + 1;
		CvScalar mean, stdev;

		if (   (x1 < 0 || x1 >= m_Input->width || y1 < 0 || y1 > m_Input->height)
			&& (x2 < 0 || x2 >= m_Input->width || y2 < 0 || y2 > m_Input->height))
		{
			// パッチが画像の
			mean.val[0] = mean.val[1] = mean.val[2] = -1.0;
		}
		else
		{
			// パッチROI設定
			cvSetImageROI(m_Input, cvRect(x1, y1, psize, psize));

			// パッチの平均と標準偏差を計算
			cvAvgSdv(m_Input, &mean, &stdev);
		}

		// 行列に代入
		for(int c=0; c<COLOR; c++) cvmSet(*rgb, i, c, mean.val[c]);
	}
	cvResetImageROI(m_Input);

	return true;
}

//-------------------------------------------------------------------------------
// パッチROIの描画
bool CDetect::DrawROI(IplImage *img)
{
	for (int i = 0; i < m_NumPatch; i++)
	{
		int x1 = m_PatchPos[i].x - m_PatchSize;
		int y1 = m_PatchPos[i].y - m_PatchSize;
		int x2 = m_PatchPos[i].x + m_PatchSize;
		int y2 = m_PatchPos[i].y + m_PatchSize;

		// 結果画像にパッチを描画
		cvRectangle(img, cvPoint(x1, y1), cvPoint(x2, y2), 
			i < 2 ? cvScalar(0,0,255) : cvScalar(255,0,0), CV_FILLED);
	}

	return true;
}

//-------------------------------------------------------------------------------
// パッチ画像の生成
IplImage* CDetect::GenPatchedImage()
{
	IplImage *patched = (IplImage*)cvClone(m_Input);

	this->DrawROI(patched);

	return patched;
}
