// Calculate b
// using convolution.
Mat PoissonBlending::getB1(Mat &src, Mat &dst, Mat &roiMask,vector<Point>&Points,Point loc, Rect ROI)
{
	Mat lapMat;
	int mh = roiMask.rows;
	int mw = roiMask.cols;
	filter2D(src, lapMat, -1, getLaplacian());
	Mat B = Mat::zeros(Points.size(), 1, CV_32FC1);

	for (int i = 0; i < Points.size(); i++)
	{
		Point pt = Points[i];
		double tmp = 0.0;
		tmp = lapMat.ATF(ROI.y + pt.y, ROI.x + pt.x);
		if (pt.y>0 && roiMask.ATU(pt.y - 1, pt.x) != 255)
			tmp -= dst.ATF(pt.y + loc.y - 1, pt.x + loc.x);
		if (pt.y+1 <mh && roiMask.ATU(pt.y + 1, pt.x) != 255)
			tmp -= dst.ATF(pt.y + loc.y + 1, pt.x + loc.x);
		if (pt.x + 1<mw && roiMask.ATU(pt.y, pt.x + 1) != 255)
			tmp -= dst.ATF(pt.y + loc.y, pt.x + loc.x + 1);
		if (pt.x>0 && roiMask.ATU(pt.y , pt.x-1) != 255)
			tmp -= dst.ATF(pt.y + loc.y, pt.x + loc.x - 1);
		B.ATF(i, 0) = tmp;
	}
	return B;
}
// Calculate b
// using Gradients Mixing.
Mat PoissonBlending::getB3(Mat &src, Mat &dst, Mat &roiMask, vector<Point>&Points, Point loc, Rect ROI)
{
	Mat gradMat = getGradient(src) + getGradient(dst);
	int mh = roiMask.rows;
	int mw = roiMask.cols;
	Mat B = Mat::zeros(Points.size(), 1, CV_32FC1);
	
	for (int i = 0; i < Points.size(); i++)
	{
		Point pt = Points[i];
		double tmp = 0.0;
		tmp = gradMat.ATF(ROI.y + pt.y, ROI.x + pt.x);
		if (pt.y>0 && roiMask.ATU(pt.y - 1, pt.x) != 255)
			tmp -= dst.ATF(pt.y + loc.y - 1, pt.x + loc.x);
		if (pt.y + 1<mh && roiMask.ATU(pt.y + 1, pt.x) != 255)
			tmp -= dst.ATF(pt.y + loc.y + 1, pt.x + loc.x);
		if (pt.x + 1<mw && roiMask.ATU(pt.y, pt.x + 1) != 255)
			tmp -= dst.ATF(pt.y + loc.y, pt.x + loc.x + 1);
		if (pt.x>0 && roiMask.ATU(pt.y, pt.x - 1) != 255)
			tmp -= dst.ATF(pt.y + loc.y, pt.x + loc.x - 1);
		B.ATF(i, 0) = tmp;
	}
	return B;
}
// img1: 3-channel image, we wanna move something in it into img2.
// img2: 3-channel image, dst image.
// ROI: the position and size of the block we want to move in img1.
// posX, posY: where we want to move the block to in img2
void PoissonBlending::poisson_blending(Mat &src, Mat &dst, Mat &result, Mat &roiMask, Rect roi, Point loc)
{
	cout << "Poisson Blender" << endl;
	src.convertTo(src, CV_32FC3);
	src /= 255;
	dst.convertTo(dst, CV_32FC3);
	dst /= 255;

	vector<Point> Points;
#pragma omp parallel for
	for (int i = 0; i < roi.height; i++)
	{
#pragma omp parallel for
		for (int j = 0; j < roi.width; j++)
		{
			int z=roiMask.ATU(i, j);
			if (roiMask.ATU(i, j) == 255)
			{
#pragma omp critical(ppb)
		{
			Points.push_back(Point(j, i));
		}
			}
		} 
	}
	cout << "Total Points: " << Points.size()<< endl;
	cout << "Calculating Matrix A" << endl;
	Mat A = getA(roiMask, Points);

	// we must do the poisson blending to each channel.
	vector<Mat> src_rgb;
	split(src, src_rgb);
	vector<Mat> dst_rgb;
	split(dst, dst_rgb);
	vector<Mat> res_v;
	res_v.resize(3);
	cout << "Calculating B" << endl;
	// process 3 channels

	#pragma omp parallel for schedule(dynamic, 1) 
	for (int i = 0; i < 3; i++)
	{
		#pragma omp critical(cout)
		cout << "  " << i + 1 << "/3 channel processing..." << endl;
		Mat res, b;
		b = getB2(src_rgb[i], dst_rgb[i], roiMask, Points, loc, roi);
		//b = getB1(src_rgb[i], dst_rgb[i], roiMask, Points, loc, roi);
		#pragma omp critical(cout)
		res = getResult(A, b);
		#pragma omp critical(res_v)
			res_v[i]=res; 
		#pragma omp critical(cout)
			cout << "  "<< i + 1 << "/3 channel finished!!!" << endl;

	}
	Mat res_m;
	// merge the 3 gray images into a 3-channel image
	cout << "Merging result" << endl;
	merge(res_v, res_m);
	dst.copyTo(result);
	#pragma omp parallel for
	for (int i = 0; i < res_m.rows; i++)
	{
		Point p = Points[i]+Point(roi.x,roi.y);
		result.at<Vec3f>(p) = res_m.at<Vec3f>(i, 0);
	}

}