#include <iostream>
#include "alg.h"
#include "AlgCommon.h"

/*
截图方式说明：
1.鼠标左键按下则为截图起始点
方法一：鼠标左键一直按下，直到鼠标移动到目标点再松开，即可完成一次截图；
方法二：鼠标左键单击后，鼠标移动到目标点再次鼠标左键单击，即可完成一次截图；
若是在截图过程中，单击鼠标右键可取消本次截图状态
2.截图不会立即进行保存，截完图后可以单击鼠标右键进行取消保存上一次的截图；
存储方式一：若是截图完成后，再次进入截图状态，则会自动存储上一轮截图；
存储方式二：若是跳到下一张原始图片，则上一张原始图片中未保存的上一轮截图会自动存储；
*/


#define  MIN_ROI_AREA 	300  //有效截图的最小面积限制		

//定义全局变量
static cv::Point g_pt;		//存储鼠标第一次按下的坐标，即截图的左上角坐标；
static cv::Mat g_img_src;	//存储当前的原始图像；
static cv::Mat g_img_dst;	//存储截图过程实时的图像；
static cv::Mat g_img_roi;	//存储截图完成时的局部图；
static cv::Rect roi_area;	//存储截图完成时的局部图；
static bool g_flag = false;	//是否已处于截图状态的标示	
static bool g_issave = false;	//是否已经有未存储的缓存roi的标示
static std::string g_window_name = "image";//窗口名

//保存截取的roi图像
void save_roi(cv::Mat image)
{
	//定义输出路径，imwrite()输出，略；
}

//	鼠标的回调函数
static void onMouse(int event, int x, int y, int, void*)
{
	//触发鼠标左键按下事件
	if (cv::EVENT_LBUTTONDOWN == event)
	{
		//若当前不是截图状态，则进入截图状态，当前点为起始点并存储；	
		if (g_flag == false)
		{
			//进入截图状态；
			g_flag = true;
			//存储截图起始点
			g_pt = cv::Point(x, y);

			//若未保存上一轮roi的保存，则进行存储；
			if (g_issave == true)
			{
				save_roi(g_img_roi);
				g_issave = false;
			}
		}
		//若当前处于截图状态，则结束截图状态，并获取roi
		if (g_flag == true && abs(x - g_pt.x)*abs(y - g_pt.y) > MIN_ROI_AREA)
		{
			//结束截图状态
			g_flag = false;
			// 利用截图区域生成g_img_roi
			roi_area = cv::Rect(g_pt, cv::Point(x, y));
			g_img_src(roi_area).copyTo(g_img_roi);
			//cv::imshow("roi", g_img_roi);
			g_issave = true;
		}
	}//if(CV_EVENT_LBUTTONDOWN == event)

	//触发鼠标移动事件，并处于截图状态时
	else if (cv::EVENT_MOUSEMOVE == event && g_flag)
	{
		//重新初始化g_img_dst，确保g_img_dst上始终只有一个框
		g_img_src.copyTo(g_img_dst);
		cv::rectangle(g_img_dst, g_pt, cv::Point(x, y), cv::Scalar(255, 0, 0), 3, 8);
		cv::imshow(g_window_name, g_img_dst);
	}

	//触发鼠标左键松开事件
	else if (cv::EVENT_LBUTTONUP == event &&
		abs(x - g_pt.x)*abs(y - g_pt.y) > MIN_ROI_AREA)
	{
		//结束截图状态
		g_flag = false;
		//获取roi
		roi_area = cv::Rect(g_pt, cv::Point(x, y));
		g_img_src(roi_area).copyTo(g_img_roi);
		//cv::imshow("roi", g_img_roi);
		g_issave = true;
	}

	//触发鼠标右键按下事件
	else if (cv::EVENT_RBUTTONDOWN == event)
	{
		//在截图状态，则取消截图状态
		if (g_flag == true)
		{
			g_flag = false;
			//更新图像
			cv::imshow(g_window_name, g_img_src);
		}
		//不在截图状态，则无效上一轮的截图缓存；
		if (g_flag == false)
		{
			g_issave = false;
			//更新图像
			cv::imshow(g_window_name, g_img_src);
			//关闭局部窗口
			//cv::destroyWindow("roi");
		}
	}
}

bool inline getMinMax(std::vector<cv::Point3i> &points, cv::Point3i &min, cv::Point3i &max)
{
	int minx = 0, miny = 0, minz = 0;
	int maxx = 0, maxy = 0, maxz = 0;

	for (int i = 0; i < points.size(); i++) {
		if (points[i].x < minx)minx = points[i].x;
		if (points[i].y < miny)miny = points[i].y;
		if (points[i].z < minz)minz = points[i].z;
		if (points[i].x > maxx)maxx = points[i].x;
		if (points[i].y > maxy)maxy = points[i].y;
		if (points[i].z > maxz)maxz = points[i].z;
	}
	
	min.x = minx;
	min.y = miny;
	min.z = minz;
	max.x = maxx;
	max.y = maxy;
	max.z = maxz;

	return true;
}

bool Alg::ScreenShot(cv::Mat imgfull)
{
	cv::namedWindow(g_window_name, cv::WINDOW_AUTOSIZE);
	cv::setMouseCallback(g_window_name, onMouse, 0);
	cv::Mat img = imgfull;

	if (img.empty())
	{
		std::cout << "empty image" << std::endl;
		return false;
	}
	//存储原始图像
	g_img_src = img.clone();
	cv::imshow(g_window_name, g_img_src);

	while (1) { 
		cv::waitKey(100);
		if(g_issave)break;
	}
	//图像选择窗口
	cv::destroyWindow(g_window_name);

	img = g_img_roi.clone();
	if (img.empty())
	{
		std::cout << "empty roi image" << std::endl;
		return false;
	}

	RefRect = roi_area;
	return true;
}

//*****************图像移位函数*******************
void dftshift(cv::Mat &img_r, cv::Mat &img_i)
{
	int cx = img_r.cols / 2;
	int cy = img_r.rows / 2;//以下的操作是移动图像  (零频移到中心)
	cv::Mat part1_r(img_r, cv::Rect(0, 0, cx, cy));  //元素坐标表示为(cx,cy)
	cv::Mat part2_r(img_r, cv::Rect(cx, 0, cx, cy));
	cv::Mat part3_r(img_r, cv::Rect(0, cy, cx, cy));
	cv::Mat part4_r(img_r, cv::Rect(cx, cy, cx, cy));

	cv::Mat temp;
	part1_r.copyTo(temp);  //左上与右下交换位置(实部)
	part4_r.copyTo(part1_r);
	temp.copyTo(part4_r);

	part2_r.copyTo(temp);  //右上与左下交换位置(实部)
	part3_r.copyTo(part2_r);
	temp.copyTo(part3_r);

	cv::Mat part1_i(img_i, cv::Rect(0, 0, cx, cy));  //元素坐标(cx,cy)
	cv::Mat part2_i(img_i, cv::Rect(cx, 0, cx, cy));
	cv::Mat part3_i(img_i, cv::Rect(0, cy, cx, cy));
	cv::Mat part4_i(img_i, cv::Rect(cx, cy, cx, cy));

	part1_i.copyTo(temp);  //左上与右下交换位置(虚部)
	part4_i.copyTo(part1_i);
	temp.copyTo(part4_i);

	part2_i.copyTo(temp);  //右上与左下交换位置(虚部)
	part3_i.copyTo(part2_i);
	temp.copyTo(part3_i);
}
//*****************频率域滤波*******************
cv::Mat freqfilt(cv::Mat &scr, cv::Mat &blur)
{
	cv::Mat img_r = scr.clone();
	cv::Mat img_i = cv::Mat::zeros(scr.size(), CV_32FC1);
	//***********************DFT*******************
	cv::Mat plane[] = { img_r, img_i}; //创建通道，存储dft后的实部与虚部（CV_32F，必须为单通道数）
	cv::Mat complexIm;
	merge(plane, 2, complexIm);//合并通道 （把两个矩阵合并为一个2通道的Mat类容器）
	dft(complexIm, complexIm);//进行傅立叶变换，结果保存在自身

	//***************中心化********************
	split(complexIm, plane);//分离通道（数组分离）
    //plane[0] = plane[0](cv::Rect(0, 0, plane[0].cols & -2, plane[0].rows & -2));//这里为什么&上-2具体查看opencv文档
//    //其实是为了把行和列变成偶数 -2的二进制是11111111.......10 最后一位是0
	dftshift(plane[0], plane[1]);

	//*****************滤波器函数与DFT结果的乘积****************
	cv::Mat BLUR;
	{
		cv::Mat blur_r, blur_i;
		multiply(plane[0], blur, blur_r); //滤波（实部与滤波器模板对应元素相乘）
		multiply(plane[1], blur, blur_i);//滤波（虚部与滤波器模板对应元素相乘）
		cv::Mat plane1[] = { blur_r, blur_i };
		merge(plane1, 2, BLUR);//实部与虚部合并
	}
	//*********************得到原图频谱图***********************************
	//magnitude(plane[0], plane[1], plane[0]);//获取幅度图像，0通道为实部通道，1为虚部，因为二维傅立叶变换结果是复数
	//plane[0] += cv::Scalar::all(1);  //傅立叶变换后的图片不好分析，进行对数处理，结果比较好看
	//log(plane[0], plane[0]);    // float型的灰度空间为[0，1])
	//normalize(plane[0], plane[0], 1, 0, cv::NORM_MINMAX);  //归一化便于显示
	//cv::Mat iiimg=plane[0];
    //imshow("原图像频谱图",plane[0]);

	idft(BLUR, BLUR);    //idft结果也为复数
	split(BLUR, plane);//分离通道，主要获取通道
	magnitude(plane[0], plane[1], plane[0]);  //求幅值(模)
	normalize(plane[0], plane[0], 1, 0, cv::NORM_MINMAX);  //归一化便于显示
	return plane[0];//返回参数
}

cv::Mat ideal_lbrf_kernel(cv::Mat &scr, float sigma)
{
	cv::Mat ideal_low_pass(scr.size(), CV_32FC1); //，CV_32FC1
	cv::Mat gauss_low_pass(scr.size(), CV_32FC1); //，CV_32FC1
	cv::Mat gauss_high_pass(scr.size(), CV_32FC1); //，CV_32FC1
	////*****************理想低通滤波器***********************
	//float d0 = sigma;//半径D0越小，模糊越大；半径D0越大，模糊越小
	//for (int i = 0; i < scr.rows; i++) {
	//	for (int j = 0; j < scr.cols; j++) {
	//		double d = sqrt(pow((i - scr.rows / 2), 2) + pow((j - scr.cols / 2), 2));//分子,距离频率中心的距离,计算pow必须为float型
	//		if (d <= d0) {
	//			ideal_low_pass.at<float>(i, j) = 1;
	//		}
	//		else {
	//			ideal_low_pass.at<float>(i, j) = 0;
	//		}
	//	}
	//}
	//std::string name = "理想低通滤波器d0=" + std::to_string(sigma);
	//*****************高斯带通低通滤波器***********************
	//float sigma_gauss_low = sigma;
	//for (int i = 0; i < scr.rows; i++) {
	//	for (int j = 0; j < scr.cols; j++) {
	//		double d = sqrt(pow((i - scr.rows / 2), 2) + pow((j - scr.cols / 2), 2));//分子,计算pow必须为float型
	//		double gauss_value = -1 * pow(d, 2) / (2 * pow(sigma_gauss_low, 2));
	//		gauss_value = pow(2.718281828459, gauss_value);
	//		gauss_low_pass.at<float>(i, j) = gauss_value;
	//	}
	//}
	//name = "高斯低通滤波器d0=" + std::to_string(sigma);
	//*****************高斯带通高通滤波器***********************
	float sigma_gauss_high = sigma;
	for (int i = 0; i < scr.rows; i++) {
		for (int j = 0; j < scr.cols; j++) {
			double d = sqrt(pow((i - scr.rows / 2), 2) + pow((j - scr.cols / 2), 2));//分子,计算pow必须为float型
			double gauss_value = -1 * pow(d, 2) / (2 * pow(sigma_gauss_high, 2));
			gauss_value = pow(2.718281828459, gauss_value);
			gauss_high_pass.at<float>(i, j) = 1-gauss_value;
		}
	}
	//name = "高斯低通滤波器d0=" + std::to_string(sigma);

	return gauss_high_pass;
}

cv::Mat ideal_Low_Pass_Filter(cv::Mat &src, float sigma)
{
	int M = cv::getOptimalDFTSize(src.rows);
	int N = cv::getOptimalDFTSize(src.cols);
	cv::Mat padded;                 //调整图像加速傅里叶变换
	copyMakeBorder(src, padded, 0, M - src.rows, 0, N - src.cols, cv::BORDER_CONSTANT, cv::Scalar::all(0));
	padded.convertTo(padded, CV_32FC1); //将图像转换为flaot型

	cv::Mat ideal_kernel = ideal_lbrf_kernel(padded, sigma);//理想低通滤波器
	cv::Mat result = freqfilt(padded, ideal_kernel);
	return result;
}

void Alg::process_2dfft(cv::Mat &input,cv::Mat &output)
{
	double t = (double)cv::getTickCount();

	cv::Mat img_clone = input.clone();
	resize(img_clone, input,cv::Size(512,512));
	cv::Mat img_thr;
	threshold(img_clone, img_thr, 200, 255, cv::THRESH_TOZERO_INV);
	threshold(img_thr, img_thr, 50, 255, cv::THRESH_BINARY);

	cv::Mat pro_src = img_thr.clone();
	fistEROthenDIL(pro_src,3, 3);
	//查找轮廓
	double maxarea = 0;
	int maxAreaIdx = 0;
	std::vector<std::vector<cv::Point> > contours_pre;
	std::vector < cv::Vec4i > hierarchy_pre;
	cv::findContours(pro_src, contours_pre, hierarchy_pre, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
	for (int i = 0; i < contours_pre.size(); i++)
	{
		double tmparea = fabs(contourArea(contours_pre[i]));
		if (tmparea > maxarea)
		{
			maxarea = tmparea;
			maxAreaIdx = i;//记录最大轮廓的索引号  
		}
	}

	cv::Mat second_input;
	if (contours_pre.size())
	{
		cv::Rect rect_pre = cv::boundingRect(contours_pre[maxAreaIdx]);
		second_input = img_clone(rect_pre).clone();
	}
	else
	{
		second_input = img_clone;
	}


	cv::Mat ideal = ideal_Low_Pass_Filter(second_input, 30);
	ideal = ideal(cv::Rect(0, 0, second_input.cols, second_input.rows));

	cv::Mat third_input;
	normalize(ideal, third_input, 0, 255, cv::NORM_MINMAX);
	third_input.convertTo(third_input, CV_8UC1);
	threshold(third_input, third_input, 10, 255, cv::THRESH_BINARY);
	fistEROthenDIL(third_input, 3, 5);

	output = third_input.clone();
	//cv::waitKey(1);

	t = ((double)cv::getTickCount() - t) / cv::getTickFrequency();
	std::cout << "cost time\t" << t <<" s" << std::endl;
}

bool Alg::process_bool(cv::Mat &input, cv::Mat &output)
{
	double t = (double)cv::getTickCount();

	cv::Mat img_clone = input.clone();
	cv::Mat img_input;
	threshold(img_clone, img_clone, 200, 0, cv::THRESH_TOZERO_INV);
	threshold(img_clone, img_input, 90, 0, cv::THRESH_TOZERO);
	// third_input.convertTo(third_input, CV_8UC1);

	fistEROthenDIL(img_input, 13, 13);

	// output = third_input.clone();

	// //查找轮廓
	// cv::Mat temp = third_input.clone();
	// double maxarea = 0;
	// int maxAreaIdx = 0;
	// std::vector<std::vector<cv::Point> > contours_pre;
	// std::vector < cv::Vec4i > hierarchy_pre;
	// cv::findContours(temp, contours_pre, hierarchy_pre, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
	// for (int i = 0; i < contours_pre.size(); i++)
	// {
	// 	drawContours(temp, contours_pre, i, cv::Scalar(255), 2, 8, hierarchy_pre);
	// 	double tmparea = fabs(contourArea(contours_pre[i]));
	// 	if (tmparea > maxarea)
	// 	{
	// 		maxarea = tmparea;
	// 		maxAreaIdx = i;//记录最大轮廓的索引号  
	// 	}
	// }
	
	t = ((double)cv::getTickCount() - t) / cv::getTickFrequency();
	std::cout << "cost time\t" << t << " s" << std::endl;
	return 1;
}