#include "stdafx.h"

//-------------------------------------------------------------------------------
// 行列を標準出力に表示
void GShowMat(const CvMat *mat, const char *name, const char *format)
{
	printf("%s[%d,%d] = \n", name, mat->rows, mat->cols);
	for(int row = 0; row < mat->rows && row < SHOW_MAX_ROW; row ++)
	{
		printf("");
		for(int col=0; col<mat->cols; col ++)
		{
			CvScalar v = cvGet2D(mat, row, col);
			printf(format, v.val[0]);
			if (col < mat->cols - 1) printf(", ");
		}
		printf("\n");
	}
	if (mat->rows > SHOW_MAX_ROW) printf("continue....\n");
	printf("\n");
}

//-------------------------------------------------------------------------------
// ファイルの有無を判定する
bool GFileExists(const char *path)
{
	WIN32_FIND_DATA ffd;

	return (FindFirstFile(path, &ffd) != INVALID_HANDLE_VALUE);
}

//-------------------------------------------------------------------------------
// 任意チャネル，任意形式の行列からデータを取り出す
double GcvmGet(const CvMat *mat, const int row, const int col, const int ch)
{
	CvScalar v = cvGet2D(mat, row, col);
	return v.val[ch];
}

//-------------------------------------------------------------------------------
// 縮小を伴う画像表示（必要ならウインドウ生成も行う）
int GShowImage(const IplImage *img, const int num, const char *title, 
				const int wait)
{
	static int numDisp = 0;

	if (num < 1) return numDisp;

	// 表示画像生成
	IplImage *show = cvCreateImage(cvSize(DISP_W, DISP_H), 
			img->depth, img->nChannels);

	if (img->depth == IPL_DEPTH_8U) cvResize(img, show);
	else GcvResizeD(img, show);

	// タイトル描画
	char titleM[256];
	sprintf_s(titleM, sizeof(titleM), "%s%s", title, 
		(wait < 1 ? " [WAIT]" : ""));
	CvFont font;
	CvSize textSize;
	int baseline;
	cvInitFont(&font, CV_FONT_HERSHEY_COMPLEX, 0.7, 0.7, 0, 1);
	cvGetTextSize(titleM, &font, &textSize, &baseline);
	cvRectangle(show, cvPoint(0, 0), 
		cvPoint(textSize.width + 10, textSize.height + 10), cvScalarAll(0), 
		CV_FILLED);
	cvPutText(show, titleM, cvPoint(5, textSize.height + 5), &font, cvScalarAll(255));

	// ウインドウ作成
	char dispname[256];
	while (numDisp < num)
	{
		numDisp ++;
		sprintf_s(dispname, sizeof(dispname), "解析結果 %d", numDisp);
		cvNamedWindow(dispname);
	}

	// 表示
	sprintf_s(dispname, sizeof(dispname), "解析結果 %d", num);
	cvShowImage(dispname, show);
	int key = cvWaitKey(wait);
	if (key == 27) exit(1);

	SAFE_RELEASEIMG(show);

	return key;
}

//-------------------------------------------------------------------------------
// 浮動小数点データに対応した画像リサイズ（最近傍法）
void GcvResizeD(const IplImage *src, IplImage *dst)
{
	const double xScale = (double)dst->width  / (double)src->width;
	const double yScale = (double)dst->height / (double)src->height;

	double max, min, gmax, gmin, sc;

	// 正規化のための最大，最小値検索
	if (src->nChannels > 1)
	{
		IplImage *ch0 = cvCreateImage(cvGetSize(src), src->depth, 1);
		IplImage *ch1 = cvCreateImage(cvGetSize(src), src->depth, 1);
		IplImage *ch2 = cvCreateImage(cvGetSize(src), src->depth, 1);
		cvSplit(src, ch0, ch1, ch2, NULL);
		cvMinMaxLoc(ch0, &gmin, &gmax);
		cvMinMaxLoc(ch1, &min, &max);
		gmin = (min < gmin ? min : gmin);
		gmax = (max > gmax ? max : gmax);
		cvMinMaxLoc(ch2, &min, &max);
		gmin = (min < gmin ? min : gmin);
		gmax = (max > gmax ? max : gmax);
		SAFE_RELEASEIMG(ch0);
		SAFE_RELEASEIMG(ch1);
		SAFE_RELEASEIMG(ch2);
	}
	else
	{
		cvMinMaxLoc(src, &gmin, &gmax);
	}
	sc = 2.0 / (gmax - gmin);

	// OpenMPでスケーリング
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic)
#endif
	for (int dy = 0; dy < dst->height; dy ++)
	{
		for (int dx = 0; dx < dst->width; dx ++)
		{
			int sx = (int)((double)dx / xScale + 0.5);
			int sy = (int)((double)dy / yScale + 0.5);
			sx = sx >= src->width  ? src->width  - 1 : sx;
			sy = sy >= src->height ? src->height - 1 : sy;
			CvScalar v = cvGet2D(src, sy, sx);
			v = cvScalar((v.val[0] - gmin) * sc,
						 (v.val[1] - gmin) * sc,
						 (v.val[2] - gmin) * sc);
			cvSet2D(dst, dy, dx, v);
		}
	}
}

//-------------------------------------------------------------------------------
// CSVファイルを読み込んで行列を生成する
//
// const char *filename		CSVファイル名
// const int rows			読み込む行数
// const int cols			読み込む列数
// const int startRow		読み込み開始行（1から数える）
// const int startCol		読み込み開始列（1から数える）
//
CvMat *GLoadCsv(const char *filename, const int rows, const int cols,
				const int startRow, const int startCol)
{
	// CSVファイルを開く
	FILE *fp;
	fopen_s(&fp, filename, "r");
	if (!fp)
	{
		printf("Can't open csv file: %s\n", filename);
		return NULL;
	}

	// 読み込みバッファの準備
	char buffer[1024];
	CvMat *mat = cvCreateMat(rows, cols, CV_64F);
	int readRow = 0, readCol = 0;

	// データ読み込み
	for (int row = 0; row < rows + startRow - 1; row ++)
	{
		fgets(buffer, sizeof(buffer), fp);
		char *begin = buffer;

		if (row < startRow - 1) continue;

		readCol = 0;
		for (int col = 0; col < cols + startCol - 1; col ++)
		{
			char *pt = begin;
			for (; *pt != ',' && *pt != '\n' && *pt != '\0'; pt ++);
			if (*pt == '\0')
			{
				printf("Csv file error: %s\n", filename);
				return NULL;
			}
			*pt = '\0';

			if (col >= startCol - 1)
			{
				cvmSet(mat, readRow, readCol, atof(begin));
				readCol ++;
			}

			begin = pt + 1;
		}
		readRow ++;
	}

	// ファイルを閉じる
	fclose(fp);

	// データ表示
#ifdef SHOW_FILELOAD
	GShowMat(mat, filename, "%8.4f");
#endif // SHOW_FILELOAD

	return mat;
}

//-------------------------------------------------------------------------------
// 画像の数値データを標準出力にダンプ表示
void GImageDumpD(const IplImage *src, const int num, const char *name,
				const char *format)
{
	if (src->depth != IPL_DEPTH_64F) return;

	printf("Image dump '%s'\n", name);
	for (int i = 0; i < num; i ++)
	{
		double val = *(((double*)src->imageData)+i);
		printf(format, val);
		printf(", ");
	}
	printf("\n");
}

//-------------------------------------------------------------------------------
// 文字列をトリミングする
// 文字列にNULLを書き込む（末尾位置が変わる）ので注意
void GTrimStr(char **buf)
{
	for (; GIsSpace(**buf); *buf ++);
	char *pt;
	for (pt = *buf + strlen(*buf) - 1; GIsSpace(*pt) && pt >= *buf; pt --);
	pt ++;
	if (GIsSpace(*pt)) *pt = '\0';
}

//-------------------------------------------------------------------------------
// 文字が空白系の文字（タブ，改行含む）かどうか
bool GIsSpace(const char ch)
{
	return (strchr(" \t\r\n", ch) != NULL);
}

//-------------------------------------------------------------------------------
// OpenMP を使ったMatMul
// 注意：cvMatMul より劇的に遅いので使えない （cvmGet/Set のせい？）
void GcvPMatMul(const CvMat *src1, const CvMat *src2, CvMat *dst)
{
	// 行列サイズチェック
	assert( src1->cols == src2->rows );

	const bool mode   = (src1->rows < src2->cols);
	const int  nLoop1 =  mode ? src2->cols : src1->rows;
	const int  nLoop2 = !mode ? src2->cols : src1->rows;
	//const double *data1 = (double*)src1->data.db;
	//const double *data2 = (double*)src2->data.db;
	//double *dataD = (double*)dst ->data.db;

	// OpenMPで外ループを並列処理
#ifdef _OPENMP
#pragma omp parallel for schedule(dynamic)
#endif
	for (int loop1 = 0; loop1 < nLoop1; loop1 ++)
	{
		for (int loop2 = 0; loop2 < nLoop2; loop2 ++)
		{
			const int r1 = mode ? loop2 : loop1;
			const int c2 = mode ? loop1 : loop2;

			double sum = 0;
			for (int i = 0; i < src1->cols; i ++)
				sum += cvmGet(src1, r1, i) * cvmGet(src2, i, c2);
			cvmSet(dst, r1, c2, sum);

			//	sum += data1[r1 * src1->cols + i] * data2[i * src2->cols + c2];
			//dataD[r1 * dst->cols + c2] = sum;
		}
	}
}

//-------------------------------------------------------------------------------
// 画像にガンマを掛ける
IplImage *GGenAddGamma(const IplImage *img)
{
	IplImage *gamma = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, COLOR);

	const double g = 1.0 / 2.2;
	int idx = 0;
	for (int y = 0; y < img->height; y ++)
	{
		for (int x = 0; x < img->width; x ++)
		{
			CvScalar v = cvGet2D(img, y, x);
			for (int c = 0; c < COLOR; c ++)
			{
				v.val[c] = pow(v.val[c] * 1000.0, g);
				v.val[c] = v.val[c] < 0 ? 0 : (v.val[c] > 255.0 ? 255.0 : v.val[c]);
				gamma->imageData[idx ++] = (BYTE)v.val[c];
			}
		}
	}

	return gamma;
}

CvScalar GXYZtoLab(CvScalar xyz)
{
	// 関数内だけで使用できるローカル関数を定義
	struct local {
		static double F(double x)
		{ return x > 0.008856 ? pow(x, 1.0/3.0) : 7.787 * x + (16.0/116.0); }
	};

	double white[3] = {92.219, 100.0, 95.965};
	double fx = local::F(xyz.val[0] / white[0]);
	double fy = local::F(xyz.val[1] / white[1]);
	double fz = local::F(xyz.val[2] / white[2]);
	CvScalar lab;
	lab.val[0] = 116.0 * fy - 16.0;
	lab.val[1] = 500.0 * (fx - fy);
	lab.val[2] = 200.0 * (fy - fz);

	return lab;
}
