#include "ReferenceProc.h"

//-------------------------------------------------------------------------------
// コンストラクタ
CReferenceProc::CReferenceProc(void)
{
	m_Casmatch	= new CDetectCasmatch;
	m_Macbeth	= new CDetectMacbeth;
	m_ccmCL		= new CMRegressionLinear;
	m_ccmLX		= new CMRegressionRGB (LIN_XYZ_CONVERT_DIM);
	m_ccmXD		= new CMRegressionRGB (XYZ_DISP_CONVERT_DIM);
	m_crgbRC	= NULL;
	m_lrgbRC	= NULL;
	m_crgbRM	= NULL;
	m_lrgbRM	= NULL;
	m_xyzM		= NULL;
}

//-------------------------------------------------------------------------------
// デストラクタ
CReferenceProc::~CReferenceProc(void)
{
	SAFE_DELETE(m_Casmatch);
	SAFE_DELETE(m_Macbeth);
	SAFE_DELETE(m_ccmCL);
	SAFE_DELETE(m_ccmLX);
	SAFE_DELETE(m_ccmXD);
	SAFE_RELEASEMAT(m_crgbRC);
	SAFE_RELEASEMAT(m_lrgbRC);
	SAFE_RELEASEMAT(m_crgbRM);
	SAFE_RELEASEMAT(m_lrgbRM);
	SAFE_RELEASEMAT(m_xyzM);
}

//-------------------------------------------------------------------------------
// 初期化
bool CReferenceProc::Init()
{
	CALL(m_Casmatch->Init());
	CALL(m_Macbeth->Init());

	SAFE_RELEASEMAT(m_xyzM);
#ifdef XYZ_FROM_SPECTRAL_DATA
	m_xyzM = this->GenMacbethXYZSpect();
#else
	m_xyzM = GLoadCsv(MEASURED_MACBETH_XYZ, 24, 3, 2, 2);
#endif
	CALL(m_xyzM);

	// XYZ→ディスプレイRGB 変換行列
	CvMat *monitorRGB = GLoadCsv(MONITOR_RGB, 4913, 3, 2, 1);
	CALL(monitorRGB);
	CvMat *monitorXYZ = GLoadCsv(MONITOR_XYZ, 4913, 3, 2, 1);
	CALL(monitorXYZ);
#ifdef SHOW_REGRESSION_COEF
	printf("ccmXD  ");
#endif // SHOW_REGRESSION_COEF
	CALL(m_ccmXD->CalcCoef(monitorXYZ, monitorRGB));
	CALL(m_ccmXD->CalcError(monitorXYZ, monitorRGB));
//	m_ccmXD->DrawGraph4913(monitorRGB, monitorXYZ);
	SAFE_RELEASEMAT(monitorRGB);
	SAFE_RELEASEMAT(monitorXYZ);

	return true;
}

//-------------------------------------------------------------------------------
// 分光データからマクベスのXYZを生成する
CvMat *CReferenceProc::GenMacbethXYZSpect()
{
	// データの読み込み
	CvMat *light   = GLoadCsv(LIGHTSOURCE_SPECTRAL, SPECT_DIM,  1, 2, 2);
	CALL(light);
	CvMat *macbeth = GLoadCsv(MACBETH_SPECTRAL,     SPECT_DIM, 24, 2, 2);
	CALL(macbeth);
	CvMat *cmf	   = GLoadCsv(CMF_SPECTRAL,			SPECT_DIM,  3, 2, 2);
	CALL(cmf);

	// 線光源分光分布 列コピー演算用ベクトル
	double value[24] = {0.0};
	CvMat ones = cvMat(1, 24, CV_64F, value);
	cvAddS(&ones, cvScalar(1.0), &ones);

	// 線光源分光分布 24列コピー
	CvMat *light24 = cvCreateMat(SPECT_DIM, 24, CV_64F);
	cvMatMul(light, &ones, light24);

	// マクベスの分光反射率と光源を掛ける
	CvMat *refMacbeth = cvCreateMat(SPECT_DIM, 24, CV_64F);
	cvMul(macbeth, light24, refMacbeth);

	// 完全白色板 Y=100 とする正規化係数を算出
	CvMat *xyzWhite = cvCreateMat(1, 3, CV_64F);
	cvGEMM(light, cmf, 1.0, NULL, 1.0, xyzWhite, CV_GEMM_A_T);
	double k = 100.0 / cvmGet(xyzWhite, 0, 1);

	CvMat *xyzM = cvCreateMat(24, 3, CV_64F);
	cvGEMM(refMacbeth, cmf, k, NULL, 1.0, xyzM, CV_GEMM_A_T);
	GShowMat(xyzM, "xyzM", "%8.4f");

	SAFE_RELEASEMAT(light);
	SAFE_RELEASEMAT(macbeth);
	SAFE_RELEASEMAT(cmf);
	SAFE_RELEASEMAT(light24);
	SAFE_RELEASEMAT(refMacbeth);
	SAFE_RELEASEMAT(xyzWhite);

	return xyzM;
}

//-------------------------------------------------------------------------------
// 処理画像読み込みと各変換行列の算出
bool CReferenceProc::CalcMatrix(const char *filename, const char *outputFile)
{
	// マクベスの検出，パッチ色取得
	CALL(m_Macbeth->SetImage(filename));
	CALL(m_Macbeth->Detect());
	CALL(m_Macbeth->CalcPatchColor(&m_crgbRM));
#ifdef SHOW_RGB_VALUES
	GShowMat(m_crgbRM, "crgbRM");
#endif // SHOW_RGB_VALUES

	// 線形化係数の算出
	m_ccmCL->CalcCoef(m_crgbRM);
//	m_ccmCL->CalcCurve();	// フィッティングカーブを出力（デバッグ用）

	// キャスマッチの検出，パッチ色取得
#ifndef NO_CASMATCH
	CALL(m_Casmatch->SetImage(filename));
	CALL(m_Casmatch->Detect());
	CALL(m_Casmatch->CalcPatchColor9(&m_crgbRC));
#ifdef SHOW_RGB_VALUES
	GShowMat(m_crgbRC, "crgbRC");
#endif // SHOW_RGB_VALUES
#endif // NO_CASMATCH

	// パッチ色の線形化
	SAFE_RELEASEMAT(m_lrgbRM);
	m_lrgbRM = m_ccmCL->GenConvert(m_crgbRM);
#ifndef NO_CASMATCH
	SAFE_RELEASEMAT(m_lrgbRC);
	m_lrgbRC = m_ccmCL->GenConvert(m_crgbRC);
#endif // NO_CASMATCH
#ifdef SHOW_RGB_VALUES
	GShowMat(m_lrgbRM, "lrgbRM");
#ifndef NO_CASMATCH
	GShowMat(m_lrgbRC, "lrgbRC");
#endif // NO_CASMATCH
#endif // SHOW_RGB_VALUES

	// XYZ変換係数の算出
#ifdef SHOW_REGRESSION_COEF
	printf("ccmLX  ");
#endif // SHOW_REGRESSION_COEF
	m_ccmLX->CalcCoef(m_lrgbRM, m_xyzM);

	// 検出結果の描画
	IplImage *patched = m_Macbeth->GenPatchedImage();
#ifndef NO_CASMATCH
	CALL(m_Casmatch->DrawROI(patched));
#endif // NO_CASMATCH
#ifdef SHOW_CV_IMAGE
	GShowImage(patched, 1, "Detection result");
#endif // SHOW_CV_IMAGE
	cvSaveImage(outputFile, patched);
	SAFE_RELEASEIMG(patched);

#ifdef SHOW_CALIBRATED_MACBETH
	// 色補正結果の表示
	IplImage *imgCam = cvLoadImage(filename);
	IplImage *imgLin = m_ccmCL->GenConvert(imgCam);
	IplImage *imgXYZ = m_ccmLX->GenConvert(imgLin);
	IplImage *imgXD  = m_ccmXD->GenConvert(imgXYZ);
	IplImage *imgDisp = GGenAddGamma(imgXD);
	GShowImage(imgDisp, 2, "Calibrated Macbeth");
#endif // SHOW_CALIBRATED_MACBETH

	return true;
}

//-------------------------------------------------------------------------------
// 行列データの線形化
CvMat *CReferenceProc::GenLinearize(const CvMat *data)
{
	return m_ccmCL->GenConvert(data);
}

//-------------------------------------------------------------------------------
// 画像データの線形化
IplImage *CReferenceProc::GenLinearize(const IplImage *data)
{
	return m_ccmCL->GenConvert(data);
}

//-------------------------------------------------------------------------------
// 行列データのXYZ変換
CvMat *CReferenceProc::GenConvertXYZ(const CvMat *data)
{
	return m_ccmLX->GenConvert(data);
}

//-------------------------------------------------------------------------------
// 画像データのXYZ変換
IplImage *CReferenceProc::GenConvertXYZ(const IplImage *data)
{
	return m_ccmLX->GenConvert(data);
}

//-------------------------------------------------------------------------------
// 行列データのモニター色変換
CvMat *CReferenceProc::GenConvertDisp(const CvMat *data)
{
	return m_ccmXD->GenConvert(data);
}

//-------------------------------------------------------------------------------
// 画像データのモニター色変換
IplImage *CReferenceProc::GenConvertDisp(const IplImage *data)
{
	return m_ccmXD->GenConvert(data);
}

//-------------------------------------------------------------------------------
// スカラーデータの線形化
CvScalar CReferenceProc::ScalarLinearize(const CvScalar data)
{
	return m_ccmCL->ScalarConvert(data);
}

//-------------------------------------------------------------------------------
// スカラーデータのXYZ変換
CvScalar CReferenceProc::ScalarConvertXYZ(const CvScalar data)
{
	return m_ccmLX->ScalarConvert(data);
}

//-------------------------------------------------------------------------------
// スカラーデータのモニター色変換
CvScalar CReferenceProc::ScalarConvertDisp(const CvScalar data)
{
	return m_ccmXD->ScalarConvert(data);
}
