#include "Tracking.h"

//-------------------------------------------------------------------------------
// コンストラクタ
CTracking::CTracking(void)
{
	m_Point[0] = NULL;
	m_Point[1] = NULL;
	m_PointInit = NULL;
	m_Gray = NULL;
	m_GrayPre = NULL;
	m_Pyramid = NULL;
	m_PyramidPre = NULL;
	m_Mask1 = NULL;
	m_Mask2 = NULL;
	m_Mask3 = NULL;
	m_Mask4 = NULL;
	m_Status = NULL;
}

//-------------------------------------------------------------------------------
// デストラクタ
CTracking::~CTracking(void)
{
	this->Release();
}

//-------------------------------------------------------------------------------
// メモリ解放
void CTracking::Release(void)
{
	SAFE_DELETEA(m_Point[0]);
	SAFE_DELETEA(m_Point[1]);
	SAFE_DELETEA(m_PointInit);
	SAFE_RELEASEIMG(m_Gray);
	SAFE_RELEASEIMG(m_GrayPre);
	SAFE_RELEASEIMG(m_Pyramid);
	SAFE_RELEASEIMG(m_PyramidPre);
	SAFE_RELEASEIMG(m_Mask1);
	SAFE_RELEASEIMG(m_Mask2);
	SAFE_RELEASEIMG(m_Mask3);
	SAFE_RELEASEIMG(m_Mask4);
	SAFE_DELETEA(m_Status)
}

//-------------------------------------------------------------------------------
// 初期化
bool CTracking::Init(const CvPoint *point, const int numPoint,
					 const IplImage *img)
{
	this->Release();

	m_NumPoint = numPoint;
	m_Criteria = cvTermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 20, 0.03);
	m_PyrFlag  = 0;
	m_TotalMovement = 0;

	m_Point[0]   = new CvPoint2D32f [numPoint];
	m_Point[1]   = new CvPoint2D32f [numPoint];
	m_PointInit  = new CvPoint [numPoint];
	m_Gray       = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
	m_GrayPre    = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
	m_Pyramid    = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
	m_PyramidPre = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
	m_Mask1		 = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
	m_Mask2		 = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
	m_Mask3		 = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
	m_Mask4		 = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1);
	m_Status     = new char [numPoint];

	for (int i = 0; i < numPoint; i++)
	{
		m_PointInit[i] = point[i];
		m_Point[0][i] = cvPointTo32f(point[i]);
	}

	cvCvtColor(img, m_GrayPre, CV_BGR2GRAY);

	cvFindCornerSubPix(m_GrayPre, m_Point[0], numPoint, 
		cvSize(TRACK_WIN_SIZE, TRACK_WIN_SIZE), cvSize(-1, -1),
		m_Criteria);

	return true;
}

//-------------------------------------------------------------------------------
// フレーム処理（トラッキング）
bool CTracking::Frame(const IplImage *img)
{
#if 0 // トラッキングを無効に
	// グレースケール変換
	cvCvtColor(img, m_Gray, CV_BGR2GRAY);

	// オプティカルフロー検出
	cvCalcOpticalFlowPyrLK(m_GrayPre, m_Gray, m_PyramidPre, m_Pyramid,
		m_Point[0], m_Point[1], m_NumPoint, 
		cvSize(TRACK_WIN_SIZE, TRACK_WIN_SIZE),
		3, m_Status, 0, m_Criteria, m_PyrFlag);

	// 次回から前フレームのピラミッド計算を省略する
	m_PyrFlag |= CV_LKFLOW_PYR_A_READY;

	// バッファの交換
	IplImage *swap;
	CV_SWAP(m_Gray,    m_GrayPre,    swap);
	CV_SWAP(m_Pyramid, m_PyramidPre, swap);
	CvPoint2D32f *swapP;
	CV_SWAP(m_Point[0], m_Point[1], swapP);

	// 移動量算出
	for (int i = 0; i < m_NumPoint; i ++)
	{
		m_Movement = sqrt(
			pow((double)(m_Point[0][i].x - m_Point[1][i].x), 2.0) + 
			pow((double)(m_Point[0][i].y - m_Point[1][i].y), 2.0));
		m_TotalMovement += m_Movement;
	}

	// トラッキング成否
	for (int i = 0; i < m_NumPoint; i ++) if (!m_Status[i]) return false;
#endif
	this->ValidatePoints();
	if (m_Validate) this->CalcROIMask();

	return true;
}

//-------------------------------------------------------------------------------
// 追跡点を描画
bool CTracking::DrawPoints(IplImage *img)
{
	// 点の描画
	for (int i = 0; i < m_NumPoint; i++)
	{
		cvCircle(img, cvPointFrom32f(m_Point[0][i]), 5, 
			CV_RGB(0, 0, i*60), CV_FILLED);
		cvLine(img, cvPointFrom32f(m_Point[0][i]), 
			cvPointFrom32f(m_Point[0][(i+1) % m_NumPoint]), CV_RGB(0, 0, 255));
	}

	// 初期点の描画
	for (int i = 0; i < m_NumPoint; i++)
	{
		cvLine(img, m_PointInit[i], 
			m_PointInit[(i+1) % m_NumPoint], CV_RGB(0, 255, 0));
	}

	return true;
}

//-------------------------------------------------------------------------------
// ROIを描画
bool CTracking::DrawROI(IplImage *img)
{
	cvSet(img, CV_RGB(255, 0, 0), m_Mask1);
	cvSet(img, CV_RGB(0, 255, 0), m_Mask2);
	cvSet(img, CV_RGB(0, 0, 255), m_Mask3);
	cvSet(img, CV_RGB(244, 163, 46), m_Mask4);
	cvDrawCircle(img, cvPoint(img->width - 50,  40), CALC_ROI_RADIUS, CV_RGB(255, 0, 0), CV_FILLED);
	cvDrawCircle(img, cvPoint(img->width - 50,  40), CALC_ROI_RADIUS, CV_RGB(  0, 0, 0), 1);
	cvDrawCircle(img, cvPoint(img->width - 50,  80), CALC_ROI_RADIUS, CV_RGB(0, 255, 0), CV_FILLED);
	cvDrawCircle(img, cvPoint(img->width - 50,  80), CALC_ROI_RADIUS, CV_RGB(  0, 0, 0), 1);
	cvDrawCircle(img, cvPoint(img->width - 50, 120), CALC_ROI_RADIUS, CV_RGB(0, 0, 255), CV_FILLED);
	cvDrawCircle(img, cvPoint(img->width - 50, 120), CALC_ROI_RADIUS, CV_RGB(  0, 0, 0), 1);
	cvDrawCircle(img, cvPoint(img->width - 50, 160), CALC_ROI_RADIUS, CV_RGB(244, 163, 46), CV_FILLED);
	cvDrawCircle(img, cvPoint(img->width - 50, 160), CALC_ROI_RADIUS, CV_RGB(  0, 0, 0), 1);
	CvFont font;
	cvInitFont(&font, CV_FONT_HERSHEY_COMPLEX, 1.0, 1.0);
	cvPutText(img, "1", cvPoint(img->width - 30,  48), &font, CV_RGB(0, 0, 0));
	cvPutText(img, "2", cvPoint(img->width - 30,  88), &font, CV_RGB(0, 0, 0));
	cvPutText(img, "3", cvPoint(img->width - 30, 128), &font, CV_RGB(0, 0, 0));
	cvPutText(img, "4", cvPoint(img->width - 30, 168), &font, CV_RGB(0, 0, 0));

	return true;
}

//-------------------------------------------------------------------------------
// 追跡点を描画
void CTracking::ValidatePoints()
{
	int conX[8][2] = {{0,2},{0,3},{0,4},{1,2},{1,3},{1,4},{2,3},{2,4}};
	int conY[8][2] = {{0,1},{0,2},{0,3},{1,2},{3,2},{4,1},{4,1},{4,3}};

	m_Validate = true;
	for (int i = 0; i < 8; i++)
	{
		if (m_Point[0][conX[i][0]].x >= m_Point[0][conX[i][1]].x) m_Validate = false;
		if (m_Point[0][conY[i][0]].y >= m_Point[0][conY[i][1]].y) m_Validate = false;
	}
}

//-------------------------------------------------------------------------------
// ROIマスク画像を作る
void CTracking::CalcROIMask()
{
	// ROIマスク画像1
	// 0____________ 4
	//  |           |
	//  |           |
	//  |           |
	// 1|●       ●|3
	//  舌　　　   /
	//   舌_______/
	//        2

	int points1[2][3] = {{0, 2, 1}, {4, 2, 3}};

	cvSet(m_Mask1, cvScalar(0));
	for (int i = 0; i < 2; i ++)
	{
		CvPoint2D32f roi;
		float cx1 = (m_Point[0][points1[i][0]].x + m_Point[0][points1[i][1]].x) / 2;
		float cy1 = (m_Point[0][points1[i][0]].y + m_Point[0][points1[i][1]].y) / 2;
		roi.x = (m_Point[0][points1[i][2]].x + cx1) / 2;
		roi.y = (m_Point[0][points1[i][2]].y + cy1) / 2;
		cvDrawCircle(m_Mask1, cvPointFrom32f(roi), CALC_ROI_RADIUS, cvScalar(1), CV_FILLED);
	}
	
	// ROIマスク画像2
	// 0____________ 4
	//  |   ● ●   |
	//  |           |
	//  |           |
	// 1|           |3
	//  舌　　　   /
	//   舌_______/
	//        2

	int points2[2][4] = {{0, 3, 0, 4}, {4, 1, 4, 0}};

	cvSet(m_Mask2, cvScalar(0));
	for (int i = 0; i < 2; i ++)
	{
		CvPoint2D32f roi;
		float cx1 = m_Point[0][points2[i][0]].x + (m_Point[0][points2[i][1]].x - m_Point[0][points2[i][0]].x) / 4;
		float cy1 = m_Point[0][points2[i][0]].y + (m_Point[0][points2[i][1]].y - m_Point[0][points2[i][0]].y) / 4;
		float cx2 = (m_Point[0][points2[i][2]].x + m_Point[0][points2[i][3]].x) / 2 ;
		float cy2 = (m_Point[0][points2[i][2]].y + m_Point[0][points2[i][3]].y) / 2;
		roi.x = (cx1 + cx2) / 2;
		roi.y = (cy1 + cy2) / 2;
		cvDrawCircle(m_Mask2, cvPointFrom32f(roi), CALC_ROI_RADIUS, cvScalar(1), CV_FILLED);
	}

	// ROIマスク画像3
	// 0____________ 4
	//  |           |
	//  |           |
	//  |           |
	// 1|   ● ●   |3
	//  舌　　　   /
	//   舌_______/
	//        2

	int points3[2][3] = {{0, 3, 2}, {4, 1, 2}};

	cvSet(m_Mask3, cvScalar(0));
	for (int i = 0; i < 2; i ++)
	{
		CvPoint2D32f roi;
		float cx1 = m_Point[0][points3[i][0]].x + (m_Point[0][points3[i][1]].x - m_Point[0][points3[i][0]].x) / 4;
		float cy1 = m_Point[0][points3[i][0]].y + (m_Point[0][points3[i][1]].y - m_Point[0][points3[i][0]].y) / 4;
		roi.x = (m_Point[0][points3[i][2]].x + cx1) / 2;
		roi.y = (m_Point[0][points3[i][2]].y + cy1) / 2;
		cvDrawCircle(m_Mask3, cvPointFrom32f(roi), CALC_ROI_RADIUS, cvScalar(1), CV_FILLED);
	}

	// ROIマスク画像3
	// 0____________ 4
	//  |           |
	//  |           |
	//  |           |
	// 1|           |3
	//  舌　　　   /
	//   舌_●_●_/
	//        2

	int points4[2][2] = {{0, 2}, {4, 2}};

	cvSet(m_Mask4, cvScalar(0));
	for (int i = 0; i < 2; i ++)
	{
		CvPoint2D32f roi;
		roi.x = m_Point[0][points4[i][0]].x + (m_Point[0][points4[i][1]].x - m_Point[0][points4[i][0]].x) * 7 / 8;
		roi.y = m_Point[0][points4[i][0]].y + (m_Point[0][points4[i][1]].y - m_Point[0][points4[i][0]].y) * 7 / 8;
		cvDrawCircle(m_Mask4, cvPointFrom32f(roi), CALC_ROI_RADIUS, cvScalar(1), CV_FILLED);
	}


}

//-------------------------------------------------------------------------------
// ROIの平均色を計算
CvScalar CTracking::ROIColor1(IplImage *img)
{
	return cvAvg(img, m_Mask1);
}

CvScalar CTracking::ROIColor2(IplImage *img)
{
	return cvAvg(img, m_Mask2);
}

CvScalar CTracking::ROIColor3(IplImage *img)
{
	return cvAvg(img, m_Mask3);
}

CvScalar CTracking::ROIColor4(IplImage *img)
{
	return cvAvg(img, m_Mask4);
}
