seamless_cloning (Possion Image Editing)

opencv3.0 photo 模块加入了seamless_cloning类。该类对应的论文是“Poisson Image Editing”

主要可以实现一下功能:

seamless clone:

seamless_cloning (Possion Image Editing)_第1张图片

纹理传输:

seamless_cloning (Possion Image Editing)_第2张图片


去除光噪:

seamless_cloning (Possion Image Editing)_第3张图片

等等。


本文主要以normal_clone为例, 从代码层面解释整个流程:

1.  在normal_clone 中需要准备三张图片,分别是source, mask, destination。 如最上面那张图,source就是熊,孩子那些要黏贴的图片,mask是这些前景的mask。 destination是背景,这里是水池。

2. 计算source和destination的x,y方向上的梯度

3. 计算lapx, lapy即对梯度再取梯度。得到lap

4. 取destination中非mask位置和source中mask位置lap,两者拼接得到最终lap

5. 用这个lap和原始图片,解决possion equation, 得到最终结果。(这部分不是很懂

用到了Discrete sine transform, 

参考:http://www.mathworks.com/help/pde/ug/dst.html
   http://www.mathworks.com/help/pde/ug/fast-solution-of-poissons-equation.html


注释代码如下:(做了少量修改,与opencv不完全相同)

#ifndef SEAMLESSCLONE_H_H
#define SEAMLESSCLONE_H_H 


//#include "precomp.hpp"
//#include "opencv2/photo.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/core/core.hpp"
#include <iostream>
#include <stdlib.h>
#include <complex>
#include "math.h"

using namespace std;
using namespace cv;

namespace customCV {

	class Cloning
	{

	public:

		//output: 每个通道的合成结果数组
		//rbgx_channel, rgby_channel是gxx, gyy 分通道结果
		vector <Mat> rgb_channel, rgbx_channel, rgby_channel, output;

		//smask是source图片的mask, smask1是smask取反的结果
		//grx, gry 是dst图片的梯度。 grx32, gry32是smask1区域的梯度
		//sgx, sgy 是source图片的梯度。 srx32, sry32是smask区域的梯度
		Mat grx, gry, sgx, sgy, srx32, sry32, grx32, gry32, smask, smask1;
		void init_var(Mat &I, Mat &wmask);
		void initialization(Mat &I, Mat &mask, Mat &wmask);
		void scalar_product(Mat mat, float r, float g, float b);
		void array_product(Mat mat1, Mat mat2, Mat mat3);
		void poisson(Mat &I, Mat &gx, Mat &gy, Mat &sx, Mat &sy);
		void evaluate(Mat &I, Mat &wmask, Mat &cloned);
		void getGradientx(const Mat &img, Mat &gx);
		void getGradienty(const Mat &img, Mat &gy);
		void lapx(const Mat &img, Mat &gxx);
		void lapy(const Mat &img, Mat &gyy);
		void dst(double *mod_diff, double *sineTransform, int h, int w);
		void idst(double *mod_diff, double *sineTransform, int h, int w);
		void transpose(double *mat, double *mat_t, int h, int w);
		void solve(const Mat &img, double *mod_diff, Mat &result);
		void poisson_solver(const Mat &img, Mat &gxx, Mat &gyy, Mat &result);
		void normal_clone(Mat &I, Mat &mask, Mat &wmask, Mat &cloned, int num);
		void local_color_change(Mat &I, Mat &mask, Mat &wmask, Mat &cloned, float red_mul, float green_mul, float blue_mul);
		void illum_change(Mat &I, Mat &mask, Mat &wmask, Mat &cloned, float alpha, float beta);
		void texture_flatten(Mat &I, Mat &mask, Mat &wmask, double low_threshold, double high_threhold, int kernel_size, Mat &cloned);
	};




	void seamlessClone(InputArray _src, InputArray _dst, InputArray _mask, Point p, OutputArray _blend, int flags);
	void colorChange(InputArray _src, InputArray _mask, OutputArray _dst, float r, float g, float b);
	void illuminationChange(InputArray _src, InputArray _mask, OutputArray _dst, float a, float b);
	void textureFlattening(InputArray _src, InputArray _mask, OutputArray _dst, double low_threshold, double high_threshold, int kernel_size);

}

#endif

//#include "precomp.hpp"
//#include "opencv2/photo.hpp"
#include <stdlib.h>
#include <opencv2/highgui/highgui.hpp>

#include "seamless_cloning.hpp"

using namespace std;
using namespace cv;

namespace customCV {

	void Cloning::getGradientx(const Mat &img, Mat &gx)
	{
		Mat kernel = Mat::zeros(1, 3, CV_8S);
		kernel.at<char>(0, 2) = 1;
		kernel.at<char>(0, 1) = -1;
		filter2D(img, gx, CV_32F, kernel);
	}

	void Cloning::getGradienty(const Mat &img, Mat &gy)
	{
		Mat kernel = Mat::zeros(3, 1, CV_8S);
		kernel.at<char>(2, 0) = 1;
		kernel.at<char>(1, 0) = -1;
		filter2D(img, gy, CV_32F, kernel);
	}

	//img是原始图像水平方向上的梯度。本函数是对梯度再求梯度
	void Cloning::lapx(const Mat &img, Mat &gxx)
	{
		Mat kernel = Mat::zeros(1, 3, CV_8S);
		kernel.at<char>(0, 0) = -1;
		kernel.at<char>(0, 1) = 1;
		filter2D(img, gxx, CV_32F, kernel);
	}

	void Cloning::lapy(const Mat &img, Mat &gyy)
	{
		Mat kernel = Mat::zeros(3, 1, CV_8S);
		kernel.at<char>(0, 0) = -1;
		kernel.at<char>(1, 0) = 1;
		filter2D(img, gyy, CV_32F, kernel);
	}

	//离散正弦变换
	//参考:http://www.mathworks.com/help/pde/ug/dst.html
	//参考:http://www.mathworks.com/help/pde/ug/fast-solution-of-poissons-equation.html
	void Cloning::dst(double *mod_diff, double *sineTransform, int h, int w)
	{

		unsigned long int idx;

		Mat temp = Mat(2 * h + 2, 1, CV_32F);
		Mat res = Mat(h, 1, CV_32F);

		Mat planes[] = { Mat_<float>(temp), Mat::zeros(temp.size(), CV_32F) };

		Mat result;
		int p = 0;
		for (int i = 0; i < w; i++)
		{
			temp.at<float>(0, 0) = 0.0;

			for (int j = 0, r = 1; j < h; j++, r++)
			{
				idx = j*w + i;
				temp.at<float>(r, 0) = (float)mod_diff[idx];
			}

			temp.at<float>(h + 1, 0) = 0.0;

			for (int j = h - 1, r = h + 2; j >= 0; j--, r++)
			{
				idx = j*w + i;
				temp.at<float>(r, 0) = (float)(-1.0 * mod_diff[idx]);
			}

			merge(planes, 2, result);

			dft(result, result, 0, 0);

			Mat planes1[] = { Mat::zeros(result.size(), CV_32F), Mat::zeros(result.size(), CV_32F) };

			split(result, planes1);

			std::complex<double> two_i = std::sqrt(std::complex<double>(-1));

			double factor = -2 * imag(two_i);

			for (int c = 1, z = 0; c < h + 1; c++, z++)
			{
				res.at<float>(z, 0) = (float)(planes1[1].at<float>(c, 0) / factor);
			}

			for (int q = 0, z = 0; q < h; q++, z++)
			{
				idx = q*w + p;
				sineTransform[idx] = res.at<float>(z, 0);
			}
			p++;
		}
	}

	void Cloning::idst(double *mod_diff, double *sineTransform, int h, int w)
	{
		int nn = h + 1;
		unsigned long int idx;
		dst(mod_diff, sineTransform, h, w);
		for (int i = 0; i < h; i++)
		for (int j = 0; j < w; j++)
		{
			idx = i*w + j;
			sineTransform[idx] = (double)(2 * sineTransform[idx]) / nn;
		}

	}

	void Cloning::transpose(double *mat, double *mat_t, int h, int w)
	{

		Mat tmp = Mat(h, w, CV_32FC1);
		unsigned long int idx;
		for (int i = 0; i < h; i++)
		{
			for (int j = 0; j < w; j++)
			{

				idx = i*(w)+j;
				tmp.at<float>(i, j) = (float)mat[idx];
			}
		}
		Mat tmp_t = tmp.t();

		for (int i = 0; i < tmp_t.size().height; i++)
		for (int j = 0; j < tmp_t.size().width; j++)
		{
			idx = i*tmp_t.size().width + j;
			mat_t[idx] = tmp_t.at<float>(i, j);
		}
	}

	void Cloning::solve(const Mat &img, double *mod_diff, Mat &result)
	{
		int w = img.size().width;
		int h = img.size().height;

		unsigned long int idx, idx1;

		double *sineTransform = new double[(h - 2)*(w - 2)];
		double *sineTransform_t = new double[(h - 2)*(w - 2)];
		double *denom = new double[(h - 2)*(w - 2)];
		double *invsineTransform = new double[(h - 2)*(w - 2)];
		double *invsineTransform_t = new double[(h - 2)*(w - 2)];
		double *img_d = new double[(h)*(w)];
		//结果存在img_d

		
		dst(mod_diff, sineTransform, h - 2, w - 2);
		transpose(sineTransform, sineTransform_t, h - 2, w - 2);


		dst(sineTransform_t, sineTransform, w - 2, h - 2);

		transpose(sineTransform, sineTransform_t, w - 2, h - 2);

		int cy = 1;

		for (int i = 0; i < w - 2; i++, cy++)
		{
			for (int j = 0, cx = 1; j < h - 2; j++, cx++)
			{
				idx = j*(w - 2) + i;
				denom[idx] = (float)2 * cos(CV_PI*cy / ((double)(w - 1))) - 2 + 2 * cos(CV_PI*cx / ((double)(h - 1))) - 2;

			}
		}

		for (idx = 0; idx < (unsigned)(w - 2)*(h - 2); idx++)
		{
			sineTransform_t[idx] = sineTransform_t[idx] / denom[idx];
		}

		idst(sineTransform_t, invsineTransform, h - 2, w - 2);

		transpose(invsineTransform, invsineTransform_t, h - 2, w - 2);

		idst(invsineTransform_t, invsineTransform, w - 2, h - 2);

		transpose(invsineTransform, invsineTransform_t, w - 2, h - 2);

		for (int i = 0; i < h; i++)
		{
			for (int j = 0; j < w; j++)
			{
				idx = i*w + j;
				img_d[idx] = (double)img.at<uchar>(i, j);
			}
		}
		for (int i = 1; i < h - 1; i++)
		{
			for (int j = 1; j < w - 1; j++)
			{
				idx = i*w + j;
				img_d[idx] = 0.0;
			}
		}
		for (int i = 1, id1 = 0; i < h - 1; i++, id1++)
		{
			for (int j = 1, id2 = 0; j < w - 1; j++, id2++)
			{
				idx = i*w + j;
				idx1 = id1*(w - 2) + id2;
				img_d[idx] = invsineTransform_t[idx1];
			}
		}


		for (int i = 0; i < h; i++)
		{
			for (int j = 0; j < w; j++)
			{
				idx = i*w + j;
				if (img_d[idx] < 0.0) {
					result.at<uchar>(i, j) = 0;
				}
				else if (img_d[idx] > 255.0)
					result.at<uchar>(i, j) = 255;
				else {
					result.at<uchar>(i, j) = (uchar)img_d[idx];
				}
			}
		}

		delete[] sineTransform;
		delete[] sineTransform_t;
		delete[] denom;
		delete[] invsineTransform;
		delete[] invsineTransform_t;
		delete[] img_d;
	}

	//由img和lap计算合成结果, 注意实际上lap有大量负数
	void Cloning::poisson_solver(const Mat &img, Mat &gxx, Mat &gyy, Mat &result)
	{

		int w = img.size().width;
		int h = img.size().height;

		unsigned long int idx;

		Mat lap = Mat(img.size(), CV_32FC1);

		lap = gxx + gyy;

		Mat bound = img.clone();
		
		//rectangle 外围保持原样,rect内部变为scalar
		rectangle(bound, Point(1, 1), Point(img.cols - 2, img.rows - 2), Scalar::all(0), -1);
		//rectangle(bound, Point(20, 20), Point(img.cols - 50, img.rows - 50), Scalar::all(0), -1);


		double *boundary_point = new double[h*w];

		Mat bound_map = cv::Mat(img.size(), CV_8UC1, cv::Scalar(0));

		for (int i = 1; i < h - 1; i++)
		for (int j = 1; j < w - 1; j++)
		{
			idx = i*w + j;
			boundary_point[idx] = -4 * (int)bound.at<uchar>(i, j) + (int)bound.at<uchar>(i, (j + 1)) + (int)bound.at<uchar>(i, (j - 1))
				+ (int)bound.at<uchar>(i - 1, j) + (int)bound.at<uchar>(i + 1, j);
			bound_map.at<uchar>(i, j) = boundary_point[idx];
		}



		Mat diff = Mat(h, w, CV_32FC1);
		for (int i = 0; i < h; i++)
		{
			for (int j = 0; j < w; j++)
			{
				idx = i*w + j;
				diff.at<float>(i, j) = (float)(lap.at<float>(i, j) - boundary_point[idx]);
			}
		}

		//diff和lap几乎没什么区别

		double *mod_diff = new double[(h - 2)*(w - 2)];
		for (int i = 0; i < h - 2; i++)
		{
			for (int j = 0; j < w - 2; j++)
			{
				idx = i*(w - 2) + j;
				mod_diff[idx] = diff.at<float>(i + 1, j + 1);

			}
		}
		///////////////////////////////////////////////////// Find DST  /////////////////////////////////////////////////////
		solve(img, mod_diff, result);

		delete[] mod_diff;
		delete[] boundary_point;
	}

	void Cloning::init_var(Mat &I, Mat &wmask)
	{
		grx = Mat(I.size(), CV_32FC3);
		gry = Mat(I.size(), CV_32FC3);
		sgx = Mat(I.size(), CV_32FC3);
		sgy = Mat(I.size(), CV_32FC3);

		split(I, rgb_channel);


		smask = Mat(wmask.size(), CV_32FC1);
		srx32 = Mat(I.size(), CV_32FC3);
		sry32 = Mat(I.size(), CV_32FC3);
		smask1 = Mat(wmask.size(), CV_32FC1);
		grx32 = Mat(I.size(), CV_32FC3);
		gry32 = Mat(I.size(), CV_32FC3);
	}

	void Cloning::initialization(Mat &I, Mat &mask, Mat &wmask)
	{
		//初始化各个mat
		init_var(I, wmask);

		//grx, gry 分别表示dest的x,y方向的梯度
		getGradientx(I, grx);
		getGradienty(I, gry);

		//sgx, sgy 分别表示在mask区域内的source图片在x,y方向的梯度
		getGradientx(mask, sgx);
		getGradienty(mask, sgy);

		Mat Kernel(Size(3, 3), CV_8UC1);
		Kernel.setTo(Scalar(1));
		//腐蚀
		erode(wmask, wmask, Kernel, Point(-1, -1), 3);

		wmask.convertTo(smask, CV_32FC1, 1.0 / 255.0);
		I.convertTo(srx32, CV_32FC3, 1.0 / 255.0);
		I.convertTo(sry32, CV_32FC3, 1.0 / 255.0);
	}

	void Cloning::scalar_product(Mat mat, float r, float g, float b)
	{
		vector <Mat> channels;
		split(mat, channels);
		multiply(channels[2], r, channels[2]);
		multiply(channels[1], g, channels[1]);
		multiply(channels[0], b, channels[0]);
		merge(channels, mat);
	}

	//mat1 = mat3.mul(mat2(:))  mat3为单通道,一般为mask
	void Cloning::array_product(Mat mat1, Mat mat2, Mat mat3)
	{
		vector <Mat> channels_temp1;
		vector <Mat> channels_temp2;
		split(mat1, channels_temp1);
		split(mat2, channels_temp2);
		multiply(channels_temp2[2], mat3, channels_temp1[2]);
		multiply(channels_temp2[1], mat3, channels_temp1[1]);
		multiply(channels_temp2[0], mat3, channels_temp1[0]);
		merge(channels_temp1, mat1);
	}

	void Cloning::poisson(Mat &I, Mat &gx, Mat &gy, Mat &sx, Mat &sy)
	{
		//fx, fy是两者组合的梯度
		Mat fx = Mat(I.size(), CV_32FC3);
		Mat fy = Mat(I.size(), CV_32FC3);

		fx = gx + sx;
		fy = gy + sy;


		Mat gxx = Mat(I.size(), CV_32FC3);
		Mat gyy = Mat(I.size(), CV_32FC3);

		//gxx, gyy 是在x,y方向的laplacian算子
		lapx(fx, gxx);
		lapy(fy, gyy);


		split(gxx, rgbx_channel);
		split(gyy, rgby_channel);

		split(I, output);


		poisson_solver(rgb_channel[2], rgbx_channel[2], rgby_channel[2], output[2]);
		poisson_solver(rgb_channel[1], rgbx_channel[1], rgby_channel[1], output[1]);
		poisson_solver(rgb_channel[0], rgbx_channel[0], rgby_channel[0], output[0]);
	}

	void Cloning::evaluate(Mat &I, Mat &wmask, Mat &cloned)
	{
		//mask取反
		bitwise_not(wmask, wmask);

		wmask.convertTo(smask1, CV_32FC1, 1.0 / 255.0);
		I.convertTo(grx32, CV_32FC3, 1.0 / 255.0);
		I.convertTo(gry32, CV_32FC3, 1.0 / 255.0);

		array_product(grx32, grx, smask1);
		array_product(gry32, gry, smask1);

		poisson(I, grx32, gry32, srx32, sry32);

		merge(output, cloned);
	}

	void Cloning::normal_clone(Mat &I, Mat &mask, Mat &wmask, Mat &cloned, int num)
	{
		int w = I.size().width;
		int h = I.size().height;
		int channel = I.channels();

		//初始化各个存储参数的mat,并计算原图于目标图的x,y方向梯度,对mask腐蚀
		initialization(I, mask, wmask);

		if (num == 1) //NORMAL_CLONE
		{
			//srx32, sry32是sgx,sgy的mask区域
			array_product(srx32, sgx, smask);
			array_product(sry32, sgy, smask);
		}
		else if (num == 2) //MIXED_CLONE
		{

			for (int i = 0; i < h; i++)
			{
				for (int j = 0; j < w; j++)
				{
					for (int c = 0; c<channel; ++c)
					{
						if (abs(sgx.at<float>(i, j*channel + c) - sgy.at<float>(i, j*channel + c)) >
							abs(grx.at<float>(i, j*channel + c) - gry.at<float>(i, j*channel + c)))
						{

							srx32.at<float>(i, j*channel + c) = sgx.at<float>(i, j*channel + c)
								* smask.at<float>(i, j);
							sry32.at<float>(i, j*channel + c) = sgy.at<float>(i, j*channel + c)
								* smask.at<float>(i, j);
						}
						else
						{
							srx32.at<float>(i, j*channel + c) = grx.at<float>(i, j*channel + c)
								* smask.at<float>(i, j);
							sry32.at<float>(i, j*channel + c) = gry.at<float>(i, j*channel + c)
								* smask.at<float>(i, j);
						}
					}
				}
			}

		}
		else if (num == 3) //FEATURE_EXCHANGE
		{
			Mat gray = Mat(mask.size(), CV_8UC1);
			Mat gray8 = Mat(mask.size(), CV_8UC3);
			cvtColor(mask, gray, COLOR_BGR2GRAY);
			vector <Mat> temp;
			split(I, temp);
			gray.copyTo(temp[2]);
			gray.copyTo(temp[1]);
			gray.copyTo(temp[0]);

			merge(temp, gray8);

			getGradientx(gray8, sgx);
			getGradienty(gray8, sgy);

			array_product(srx32, sgx, smask);
			array_product(sry32, sgy, smask);

		}

		evaluate(I, wmask, cloned);
	}

	void Cloning::local_color_change(Mat &I, Mat &mask, Mat &wmask, Mat &cloned, float red_mul = 1.0,
		float green_mul = 1.0, float blue_mul = 1.0)
	{
		initialization(I, mask, wmask);

		array_product(srx32, sgx, smask);
		array_product(sry32, sgy, smask);
		scalar_product(srx32, red_mul, green_mul, blue_mul);
		scalar_product(sry32, red_mul, green_mul, blue_mul);

		evaluate(I, wmask, cloned);
	}

	void Cloning::illum_change(Mat &I, Mat &mask, Mat &wmask, Mat &cloned, float alpha, float beta)
	{
		initialization(I, mask, wmask);

		array_product(srx32, sgx, smask);
		array_product(sry32, sgy, smask);

		Mat mag = Mat(I.size(), CV_32FC3);
		magnitude(srx32, sry32, mag);

		//opencv3.0 alpha中有bug,需要加入一下代码才不能得到正确结果。已经提交bug
		for (int i = 0; i < mag.cols; i++)
		{
			for (int j = 0; j < mag.rows; j++)
			{
				if (mag.at<cv::Vec3f>(j, i)[0] == 0)
					mag.at<cv::Vec3f>(j, i)[0] = 1e-8;

				if (mag.at<cv::Vec3f>(j, i)[1] == 0)
					mag.at<cv::Vec3f>(j, i)[1] = 1e-8;

				if (mag.at<cv::Vec3f>(j, i)[2] == 0)
					mag.at<cv::Vec3f>(j, i)[2] = 1e-8;
			}
		}

		Mat multX, multY, multx_temp, multy_temp;

		multiply(srx32, pow(alpha, beta), multX);
		pow(mag, -1 * beta, multx_temp);
		multiply(multX, multx_temp, srx32);

		multiply(sry32, pow(alpha, beta), multY);
		pow(mag, -1 * beta, multy_temp);
		multiply(multY, multy_temp, sry32);

		Mat zeroMask = (srx32 != 0);

		srx32.copyTo(srx32, zeroMask);
		sry32.copyTo(sry32, zeroMask);

		evaluate(I, wmask, cloned);
	}

	void Cloning::texture_flatten(Mat &I, Mat &mask, Mat &wmask, double low_threshold,
		double high_threshold, int kernel_size, Mat &cloned)
	{
		initialization(I, mask, wmask);

		Mat out = Mat(mask.size(), CV_8UC1);
		Canny(mask, out, low_threshold, high_threshold, kernel_size);

		Mat zeros(sgx.size(), CV_32FC3);
		zeros.setTo(0);
		Mat zerosMask = (out != 255);
		zeros.copyTo(sgx, zerosMask);
		zeros.copyTo(sgy, zerosMask);

		array_product(srx32, sgx, smask);
		array_product(sry32, sgy, smask);

		evaluate(I, wmask, cloned);
	}



	void seamlessClone(InputArray _src, InputArray _dst, InputArray _mask, Point p, OutputArray _blend, int flags)
	{
		Mat src = _src.getMat();
		Mat dest = _dst.getMat();
		Mat mask = _mask.getMat();
		_blend.create(dest.size(), CV_8UC3);
		Mat blend = _blend.getMat();

		int minx = INT_MAX, miny = INT_MAX, maxx = INT_MIN, maxy = INT_MIN;
		int h = mask.size().height;
		int w = mask.size().width;

		Mat gray = Mat(mask.size(), CV_8UC1);
		Mat dst_mask = Mat::zeros(dest.size(), CV_8UC1);
		Mat cs_mask = Mat::zeros(src.size(), CV_8UC3);
		Mat cd_mask = Mat::zeros(dest.size(), CV_8UC3);

		if (mask.channels() == 3)
			cvtColor(mask, gray, COLOR_BGR2GRAY);
		else
			gray = mask;

		for (int i = 0; i < h; i++)
		{
			for (int j = 0; j < w; j++)
			{
				if (gray.at<uchar>(i, j) == 255)
				{
					minx = std::min(minx, i);
					maxx = std::max(maxx, i);
					miny = std::min(miny, j);
					maxy = std::max(maxy, j);
				}
			}
		}

		int lenx = maxx - minx;
		int leny = maxy - miny;

		int minxd = p.y - lenx / 2;
		int maxxd = p.y + lenx / 2;
		int minyd = p.x - leny / 2;
		int maxyd = p.x + leny / 2;

		CV_Assert(minxd >= 0 && minyd >= 0 && maxxd <= dest.rows && maxyd <= dest.cols);

		Rect roi_d(minyd, minxd, leny, lenx);
		Rect roi_s(miny, minx, leny, lenx);

		Mat destinationROI = dst_mask(roi_d);
		Mat sourceROI = cs_mask(roi_s);

		gray(roi_s).copyTo(destinationROI);
		src(roi_s).copyTo(sourceROI, gray(roi_s));

		destinationROI = cd_mask(roi_d);
		cs_mask(roi_s).copyTo(destinationROI);

		Cloning obj;
		obj.normal_clone(dest, cd_mask, dst_mask, blend, flags);

	}

	void colorChange(InputArray _src, InputArray _mask, OutputArray _dst, float r, float g, float b)
	{
		Mat src = _src.getMat();
		Mat mask = _mask.getMat();
		_dst.create(src.size(), src.type());
		Mat blend = _dst.getMat();

		float red = r;
		float green = g;
		float blue = b;

		Mat gray = Mat::zeros(mask.size(), CV_8UC1);

		if (mask.channels() == 3)
			cvtColor(mask, gray, COLOR_BGR2GRAY);
		else
			gray = mask;

		Mat cs_mask = Mat::zeros(src.size(), CV_8UC3);

		src.copyTo(cs_mask, gray);

		Cloning obj;
		obj.local_color_change(src, cs_mask, gray, blend, red, green, blue);
	}

	void illuminationChange(InputArray _src, InputArray _mask, OutputArray _dst, float a, float b)
	{

		Mat src = _src.getMat();
		Mat mask = _mask.getMat();
		_dst.create(src.size(), src.type());
		Mat blend = _dst.getMat();
		float alpha = a;
		float beta = b;

		Mat gray = Mat::zeros(mask.size(), CV_8UC1);

		if (mask.channels() == 3)
			cvtColor(mask, gray, COLOR_BGR2GRAY);
		else
			gray = mask;

		Mat cs_mask = Mat::zeros(src.size(), CV_8UC3);

		src.copyTo(cs_mask, gray);

		Cloning obj;
		obj.illum_change(src, cs_mask, gray, blend, alpha, beta);
	}

	void textureFlattening(InputArray _src, InputArray _mask, OutputArray _dst,
		double low_threshold, double high_threshold, int kernel_size)
	{

		Mat src = _src.getMat();
		Mat mask = _mask.getMat();
		_dst.create(src.size(), src.type());
		Mat blend = _dst.getMat();

		Mat gray = Mat::zeros(mask.size(), CV_8UC1);

		if (mask.channels() == 3)
			cvtColor(mask, gray, COLOR_BGR2GRAY);
		else
			gray = mask;

		Mat cs_mask = Mat::zeros(src.size(), CV_8UC3);

		src.copyTo(cs_mask, gray);

		Cloning obj;
		obj.texture_flatten(src, cs_mask, gray, low_threshold, high_threshold, kernel_size, blend);
	}

}


你可能感兴趣的:(seamless_cloning (Possion Image Editing))