Path: blob/master/modules/photo/src/seamless_cloning_impl.cpp
16339 views
/*M///////////////////////////////////////////////////////////////////////////////////////1//2// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.3//4// By downloading, copying, installing or using the software you agree to this license.5// If you do not agree to this license, do not download, install,6// copy or use the software.7//8//9// License Agreement10// For Open Source Computer Vision Library11//12// Copyright (C) 2013, OpenCV Foundation, all rights reserved.13// Third party copyrights are property of their respective owners.14//15// Redistribution and use in source and binary forms, with or without modification,16// are permitted provided that the following conditions are met:17//18// * Redistribution's of source code must retain the above copyright notice,19// this list of conditions and the following disclaimer.20//21// * Redistribution's in binary form must reproduce the above copyright notice,22// this list of conditions and the following disclaimer in the documentation23// and/or other materials provided with the distribution.24//25// * The name of the copyright holders may not be used to endorse or promote products26// derived from this software without specific prior written permission.27//28// This software is provided by the copyright holders and contributors "as is" and29// any express or implied warranties, including, but not limited to, the implied30// warranties of merchantability and fitness for a particular purpose are disclaimed.31// In no event shall the Intel Corporation or contributors be liable for any direct,32// indirect, incidental, special, exemplary, or consequential damages33// (including, but not limited to, procurement of substitute goods or services;34// loss of use, data, or profits; or business interruption) however caused35// and on any theory of liability, whether in contract, strict liability,36// or tort (including negligence or otherwise) arising in any way out of37// the use of this software, even if advised of the possibility of such damage.38//39//M*/4041#include "seamless_cloning.hpp"4243using namespace cv;44using namespace std;454647void Cloning::computeGradientX( const Mat &img, Mat &gx)48{49Mat kernel = Mat::zeros(1, 3, CV_8S);50kernel.at<char>(0,2) = 1;51kernel.at<char>(0,1) = -1;5253if(img.channels() == 3)54{55filter2D(img, gx, CV_32F, kernel);56}57else if (img.channels() == 1)58{59Mat tmp[3];60for(int chan = 0 ; chan < 3 ; ++chan)61{62filter2D(img, tmp[chan], CV_32F, kernel);63}64merge(tmp, 3, gx);65}66}6768void Cloning::computeGradientY( const Mat &img, Mat &gy)69{70Mat kernel = Mat::zeros(3, 1, CV_8S);71kernel.at<char>(2,0) = 1;72kernel.at<char>(1,0) = -1;7374if(img.channels() == 3)75{76filter2D(img, gy, CV_32F, kernel);77}78else if (img.channels() == 1)79{80Mat tmp[3];81for(int chan = 0 ; chan < 3 ; ++chan)82{83filter2D(img, tmp[chan], CV_32F, kernel);84}85merge(tmp, 3, gy);86}87}8889void Cloning::computeLaplacianX( const Mat &img, Mat &laplacianX)90{91Mat kernel = Mat::zeros(1, 3, CV_8S);92kernel.at<char>(0,0) = -1;93kernel.at<char>(0,1) = 1;94filter2D(img, laplacianX, CV_32F, kernel);95}9697void Cloning::computeLaplacianY( const Mat &img, Mat &laplacianY)98{99Mat kernel = Mat::zeros(3, 1, CV_8S);100kernel.at<char>(0,0) = -1;101kernel.at<char>(1,0) = 1;102filter2D(img, laplacianY, CV_32F, kernel);103}104105void Cloning::dst(const Mat& src, Mat& dest, bool invert)106{107Mat temp = Mat::zeros(src.rows, 2 * src.cols + 2, CV_32F);108109int flag = invert ? DFT_ROWS + DFT_SCALE + DFT_INVERSE: DFT_ROWS;110111src.copyTo(temp(Rect(1,0, src.cols, src.rows)));112113for(int j = 0 ; j < src.rows ; ++j)114{115float * tempLinePtr = temp.ptr<float>(j);116const float * srcLinePtr = src.ptr<float>(j);117for(int i = 0 ; i < src.cols ; ++i)118{119tempLinePtr[src.cols + 2 + i] = - srcLinePtr[src.cols - 1 - i];120}121}122123Mat planes[] = {temp, Mat::zeros(temp.size(), CV_32F)};124Mat complex;125126merge(planes, 2, complex);127dft(complex, complex, flag);128split(complex, planes);129temp = Mat::zeros(src.cols, 2 * src.rows + 2, CV_32F);130131for(int j = 0 ; j < src.cols ; ++j)132{133float * tempLinePtr = temp.ptr<float>(j);134for(int i = 0 ; i < src.rows ; ++i)135{136float val = planes[1].ptr<float>(i)[j + 1];137tempLinePtr[i + 1] = val;138tempLinePtr[temp.cols - 1 - i] = - val;139}140}141142Mat planes2[] = {temp, Mat::zeros(temp.size(), CV_32F)};143144merge(planes2, 2, complex);145dft(complex, complex, flag);146split(complex, planes2);147148temp = planes2[1].t();149temp(Rect( 0, 1, src.cols, src.rows)).copyTo(dest);150}151152void Cloning::solve(const Mat &img, Mat& mod_diff, Mat &result)153{154const int w = img.cols;155const int h = img.rows;156157Mat res;158dst(mod_diff, res);159160for(int j = 0 ; j < h-2; j++)161{162float * resLinePtr = res.ptr<float>(j);163for(int i = 0 ; i < w-2; i++)164{165resLinePtr[i] /= (filter_X[i] + filter_Y[j] - 4);166}167}168169dst(res, mod_diff, true);170171unsigned char * resLinePtr = result.ptr<unsigned char>(0);172const unsigned char * imgLinePtr = img.ptr<unsigned char>(0);173const float * interpLinePtr = NULL;174175//first col176for(int i = 0 ; i < w ; ++i)177result.ptr<unsigned char>(0)[i] = img.ptr<unsigned char>(0)[i];178179for(int j = 1 ; j < h-1 ; ++j)180{181resLinePtr = result.ptr<unsigned char>(j);182imgLinePtr = img.ptr<unsigned char>(j);183interpLinePtr = mod_diff.ptr<float>(j-1);184185//first row186resLinePtr[0] = imgLinePtr[0];187188for(int i = 1 ; i < w-1 ; ++i)189{190//saturate cast is not used here, because it behaves differently from the previous implementation191//most notable, saturate_cast rounds before truncating, here it's the opposite.192float value = interpLinePtr[i-1];193if(value < 0.)194resLinePtr[i] = 0;195else if (value > 255.0)196resLinePtr[i] = 255;197else198resLinePtr[i] = static_cast<unsigned char>(value);199}200201//last row202resLinePtr[w-1] = imgLinePtr[w-1];203}204205//last col206resLinePtr = result.ptr<unsigned char>(h-1);207imgLinePtr = img.ptr<unsigned char>(h-1);208for(int i = 0 ; i < w ; ++i)209resLinePtr[i] = imgLinePtr[i];210}211212void Cloning::poissonSolver(const Mat &img, Mat &laplacianX , Mat &laplacianY, Mat &result)213{214const int w = img.cols;215const int h = img.rows;216217Mat lap = laplacianX + laplacianY;218219Mat bound = img.clone();220221rectangle(bound, Point(1, 1), Point(img.cols-2, img.rows-2), Scalar::all(0), -1);222Mat boundary_points;223Laplacian(bound, boundary_points, CV_32F);224225boundary_points = lap - boundary_points;226227Mat mod_diff = boundary_points(Rect(1, 1, w-2, h-2));228229solve(img,mod_diff,result);230}231232void Cloning::initVariables(const Mat &destination, const Mat &binaryMask)233{234destinationGradientX = Mat(destination.size(),CV_32FC3);235destinationGradientY = Mat(destination.size(),CV_32FC3);236patchGradientX = Mat(destination.size(),CV_32FC3);237patchGradientY = Mat(destination.size(),CV_32FC3);238239binaryMaskFloat = Mat(binaryMask.size(),CV_32FC1);240binaryMaskFloatInverted = Mat(binaryMask.size(),CV_32FC1);241242//init of the filters used in the dst243const int w = destination.cols;244filter_X.resize(w - 2);245double scale = CV_PI / (w - 1);246for(int i = 0 ; i < w-2 ; ++i)247filter_X[i] = 2.0f * (float)std::cos(scale * (i + 1));248249const int h = destination.rows;250filter_Y.resize(h - 2);251scale = CV_PI / (h - 1);252for(int j = 0 ; j < h - 2 ; ++j)253filter_Y[j] = 2.0f * (float)std::cos(scale * (j + 1));254}255256void Cloning::computeDerivatives(const Mat& destination, const Mat &patch, const Mat &binaryMask)257{258initVariables(destination, binaryMask);259260computeGradientX(destination, destinationGradientX);261computeGradientY(destination, destinationGradientY);262263computeGradientX(patch, patchGradientX);264computeGradientY(patch, patchGradientY);265266Mat Kernel(Size(3, 3), CV_8UC1);267Kernel.setTo(Scalar(1));268erode(binaryMask, binaryMask, Kernel, Point(-1,-1), 3);269270binaryMask.convertTo(binaryMaskFloat, CV_32FC1, 1.0/255.0);271}272273void Cloning::scalarProduct(Mat mat, float r, float g, float b)274{275vector <Mat> channels;276split(mat,channels);277multiply(channels[2],r,channels[2]);278multiply(channels[1],g,channels[1]);279multiply(channels[0],b,channels[0]);280merge(channels,mat);281}282283void Cloning::arrayProduct(const cv::Mat& lhs, const cv::Mat& rhs, cv::Mat& result) const284{285vector <Mat> lhs_channels;286vector <Mat> result_channels;287288split(lhs,lhs_channels);289split(result,result_channels);290291for(int chan = 0 ; chan < 3 ; ++chan)292multiply(lhs_channels[chan],rhs,result_channels[chan]);293294merge(result_channels,result);295}296297void Cloning::poisson(const Mat &destination)298{299Mat laplacianX = destinationGradientX + patchGradientX;300Mat laplacianY = destinationGradientY + patchGradientY;301302computeLaplacianX(laplacianX,laplacianX);303computeLaplacianY(laplacianY,laplacianY);304305split(laplacianX,rgbx_channel);306split(laplacianY,rgby_channel);307308split(destination,output);309310for(int chan = 0 ; chan < 3 ; ++chan)311{312poissonSolver(output[chan], rgbx_channel[chan], rgby_channel[chan], output[chan]);313}314}315316void Cloning::evaluate(const Mat &I, const Mat &wmask, const Mat &cloned)317{318bitwise_not(wmask,wmask);319320wmask.convertTo(binaryMaskFloatInverted,CV_32FC1,1.0/255.0);321322arrayProduct(destinationGradientX, binaryMaskFloatInverted, destinationGradientX);323arrayProduct(destinationGradientY, binaryMaskFloatInverted, destinationGradientY);324325poisson(I);326327merge(output,cloned);328}329330void Cloning::normalClone(const Mat &destination, const Mat &patch, const Mat &binaryMask, Mat &cloned, int flag)331{332const int w = destination.cols;333const int h = destination.rows;334const int channel = destination.channels();335const int n_elem_in_line = w * channel;336337computeDerivatives(destination,patch,binaryMask);338339switch(flag)340{341case NORMAL_CLONE:342arrayProduct(patchGradientX, binaryMaskFloat, patchGradientX);343arrayProduct(patchGradientY, binaryMaskFloat, patchGradientY);344break;345346case MIXED_CLONE:347{348AutoBuffer<int> maskIndices(n_elem_in_line);349for (int i = 0; i < n_elem_in_line; ++i)350maskIndices[i] = i / channel;351352for(int i=0;i < h; i++)353{354float * patchXLinePtr = patchGradientX.ptr<float>(i);355float * patchYLinePtr = patchGradientY.ptr<float>(i);356const float * destinationXLinePtr = destinationGradientX.ptr<float>(i);357const float * destinationYLinePtr = destinationGradientY.ptr<float>(i);358const float * binaryMaskLinePtr = binaryMaskFloat.ptr<float>(i);359360for(int j=0; j < n_elem_in_line; j++)361{362int maskIndex = maskIndices[j];363364if(abs(patchXLinePtr[j] - patchYLinePtr[j]) >365abs(destinationXLinePtr[j] - destinationYLinePtr[j]))366{367patchXLinePtr[j] *= binaryMaskLinePtr[maskIndex];368patchYLinePtr[j] *= binaryMaskLinePtr[maskIndex];369}370else371{372patchXLinePtr[j] = destinationXLinePtr[j]373* binaryMaskLinePtr[maskIndex];374patchYLinePtr[j] = destinationYLinePtr[j]375* binaryMaskLinePtr[maskIndex];376}377}378}379}380break;381382case MONOCHROME_TRANSFER:383Mat gray;384cvtColor(patch, gray, COLOR_BGR2GRAY );385386computeGradientX(gray,patchGradientX);387computeGradientY(gray,patchGradientY);388389arrayProduct(patchGradientX, binaryMaskFloat, patchGradientX);390arrayProduct(patchGradientY, binaryMaskFloat, patchGradientY);391break;392393}394395evaluate(destination,binaryMask,cloned);396}397398void Cloning::localColorChange(Mat &I, Mat &mask, Mat &wmask, Mat &cloned, float red_mul=1.0,399float green_mul=1.0, float blue_mul=1.0)400{401computeDerivatives(I,mask,wmask);402403arrayProduct(patchGradientX,binaryMaskFloat, patchGradientX);404arrayProduct(patchGradientY,binaryMaskFloat, patchGradientY);405scalarProduct(patchGradientX,red_mul,green_mul,blue_mul);406scalarProduct(patchGradientY,red_mul,green_mul,blue_mul);407408evaluate(I,wmask,cloned);409}410411void Cloning::illuminationChange(Mat &I, Mat &mask, Mat &wmask, Mat &cloned, float alpha, float beta)412{413CV_INSTRUMENT_REGION();414415computeDerivatives(I,mask,wmask);416417arrayProduct(patchGradientX,binaryMaskFloat, patchGradientX);418arrayProduct(patchGradientY,binaryMaskFloat, patchGradientY);419420Mat mag;421magnitude(patchGradientX,patchGradientY,mag);422423Mat multX, multY, multx_temp, multy_temp;424425multiply(patchGradientX,pow(alpha,beta),multX);426pow(mag,-1*beta, multx_temp);427multiply(multX,multx_temp, patchGradientX);428patchNaNs(patchGradientX);429430multiply(patchGradientY,pow(alpha,beta),multY);431pow(mag,-1*beta, multy_temp);432multiply(multY,multy_temp,patchGradientY);433patchNaNs(patchGradientY);434435Mat zeroMask = (patchGradientX != 0);436437patchGradientX.copyTo(patchGradientX, zeroMask);438patchGradientY.copyTo(patchGradientY, zeroMask);439440evaluate(I,wmask,cloned);441}442443void Cloning::textureFlatten(Mat &I, Mat &mask, Mat &wmask, float low_threshold,444float high_threshold, int kernel_size, Mat &cloned)445{446computeDerivatives(I,mask,wmask);447448Mat out;449Canny(mask,out,low_threshold,high_threshold,kernel_size);450451Mat zeros = Mat::zeros(patchGradientX.size(), CV_32FC3);452Mat zerosMask = (out != 255);453zeros.copyTo(patchGradientX, zerosMask);454zeros.copyTo(patchGradientY, zerosMask);455456arrayProduct(patchGradientX,binaryMaskFloat, patchGradientX);457arrayProduct(patchGradientY,binaryMaskFloat, patchGradientY);458459evaluate(I,wmask,cloned);460}461462463