Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/photo/src/seamless_cloning_impl.cpp
16339 views
1
/*M///////////////////////////////////////////////////////////////////////////////////////
2
//
3
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4
//
5
// By downloading, copying, installing or using the software you agree to this license.
6
// If you do not agree to this license, do not download, install,
7
// copy or use the software.
8
//
9
//
10
// License Agreement
11
// For Open Source Computer Vision Library
12
//
13
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
14
// Third party copyrights are property of their respective owners.
15
//
16
// Redistribution and use in source and binary forms, with or without modification,
17
// are permitted provided that the following conditions are met:
18
//
19
// * Redistribution's of source code must retain the above copyright notice,
20
// this list of conditions and the following disclaimer.
21
//
22
// * Redistribution's in binary form must reproduce the above copyright notice,
23
// this list of conditions and the following disclaimer in the documentation
24
// and/or other materials provided with the distribution.
25
//
26
// * The name of the copyright holders may not be used to endorse or promote products
27
// derived from this software without specific prior written permission.
28
//
29
// This software is provided by the copyright holders and contributors "as is" and
30
// any express or implied warranties, including, but not limited to, the implied
31
// warranties of merchantability and fitness for a particular purpose are disclaimed.
32
// In no event shall the Intel Corporation or contributors be liable for any direct,
33
// indirect, incidental, special, exemplary, or consequential damages
34
// (including, but not limited to, procurement of substitute goods or services;
35
// loss of use, data, or profits; or business interruption) however caused
36
// and on any theory of liability, whether in contract, strict liability,
37
// or tort (including negligence or otherwise) arising in any way out of
38
// the use of this software, even if advised of the possibility of such damage.
39
//
40
//M*/
41
42
#include "seamless_cloning.hpp"
43
44
using namespace cv;
45
using namespace std;
46
47
48
void Cloning::computeGradientX( const Mat &img, Mat &gx)
49
{
50
Mat kernel = Mat::zeros(1, 3, CV_8S);
51
kernel.at<char>(0,2) = 1;
52
kernel.at<char>(0,1) = -1;
53
54
if(img.channels() == 3)
55
{
56
filter2D(img, gx, CV_32F, kernel);
57
}
58
else if (img.channels() == 1)
59
{
60
Mat tmp[3];
61
for(int chan = 0 ; chan < 3 ; ++chan)
62
{
63
filter2D(img, tmp[chan], CV_32F, kernel);
64
}
65
merge(tmp, 3, gx);
66
}
67
}
68
69
void Cloning::computeGradientY( const Mat &img, Mat &gy)
70
{
71
Mat kernel = Mat::zeros(3, 1, CV_8S);
72
kernel.at<char>(2,0) = 1;
73
kernel.at<char>(1,0) = -1;
74
75
if(img.channels() == 3)
76
{
77
filter2D(img, gy, CV_32F, kernel);
78
}
79
else if (img.channels() == 1)
80
{
81
Mat tmp[3];
82
for(int chan = 0 ; chan < 3 ; ++chan)
83
{
84
filter2D(img, tmp[chan], CV_32F, kernel);
85
}
86
merge(tmp, 3, gy);
87
}
88
}
89
90
void Cloning::computeLaplacianX( const Mat &img, Mat &laplacianX)
91
{
92
Mat kernel = Mat::zeros(1, 3, CV_8S);
93
kernel.at<char>(0,0) = -1;
94
kernel.at<char>(0,1) = 1;
95
filter2D(img, laplacianX, CV_32F, kernel);
96
}
97
98
void Cloning::computeLaplacianY( const Mat &img, Mat &laplacianY)
99
{
100
Mat kernel = Mat::zeros(3, 1, CV_8S);
101
kernel.at<char>(0,0) = -1;
102
kernel.at<char>(1,0) = 1;
103
filter2D(img, laplacianY, CV_32F, kernel);
104
}
105
106
void Cloning::dst(const Mat& src, Mat& dest, bool invert)
107
{
108
Mat temp = Mat::zeros(src.rows, 2 * src.cols + 2, CV_32F);
109
110
int flag = invert ? DFT_ROWS + DFT_SCALE + DFT_INVERSE: DFT_ROWS;
111
112
src.copyTo(temp(Rect(1,0, src.cols, src.rows)));
113
114
for(int j = 0 ; j < src.rows ; ++j)
115
{
116
float * tempLinePtr = temp.ptr<float>(j);
117
const float * srcLinePtr = src.ptr<float>(j);
118
for(int i = 0 ; i < src.cols ; ++i)
119
{
120
tempLinePtr[src.cols + 2 + i] = - srcLinePtr[src.cols - 1 - i];
121
}
122
}
123
124
Mat planes[] = {temp, Mat::zeros(temp.size(), CV_32F)};
125
Mat complex;
126
127
merge(planes, 2, complex);
128
dft(complex, complex, flag);
129
split(complex, planes);
130
temp = Mat::zeros(src.cols, 2 * src.rows + 2, CV_32F);
131
132
for(int j = 0 ; j < src.cols ; ++j)
133
{
134
float * tempLinePtr = temp.ptr<float>(j);
135
for(int i = 0 ; i < src.rows ; ++i)
136
{
137
float val = planes[1].ptr<float>(i)[j + 1];
138
tempLinePtr[i + 1] = val;
139
tempLinePtr[temp.cols - 1 - i] = - val;
140
}
141
}
142
143
Mat planes2[] = {temp, Mat::zeros(temp.size(), CV_32F)};
144
145
merge(planes2, 2, complex);
146
dft(complex, complex, flag);
147
split(complex, planes2);
148
149
temp = planes2[1].t();
150
temp(Rect( 0, 1, src.cols, src.rows)).copyTo(dest);
151
}
152
153
void Cloning::solve(const Mat &img, Mat& mod_diff, Mat &result)
154
{
155
const int w = img.cols;
156
const int h = img.rows;
157
158
Mat res;
159
dst(mod_diff, res);
160
161
for(int j = 0 ; j < h-2; j++)
162
{
163
float * resLinePtr = res.ptr<float>(j);
164
for(int i = 0 ; i < w-2; i++)
165
{
166
resLinePtr[i] /= (filter_X[i] + filter_Y[j] - 4);
167
}
168
}
169
170
dst(res, mod_diff, true);
171
172
unsigned char * resLinePtr = result.ptr<unsigned char>(0);
173
const unsigned char * imgLinePtr = img.ptr<unsigned char>(0);
174
const float * interpLinePtr = NULL;
175
176
//first col
177
for(int i = 0 ; i < w ; ++i)
178
result.ptr<unsigned char>(0)[i] = img.ptr<unsigned char>(0)[i];
179
180
for(int j = 1 ; j < h-1 ; ++j)
181
{
182
resLinePtr = result.ptr<unsigned char>(j);
183
imgLinePtr = img.ptr<unsigned char>(j);
184
interpLinePtr = mod_diff.ptr<float>(j-1);
185
186
//first row
187
resLinePtr[0] = imgLinePtr[0];
188
189
for(int i = 1 ; i < w-1 ; ++i)
190
{
191
//saturate cast is not used here, because it behaves differently from the previous implementation
192
//most notable, saturate_cast rounds before truncating, here it's the opposite.
193
float value = interpLinePtr[i-1];
194
if(value < 0.)
195
resLinePtr[i] = 0;
196
else if (value > 255.0)
197
resLinePtr[i] = 255;
198
else
199
resLinePtr[i] = static_cast<unsigned char>(value);
200
}
201
202
//last row
203
resLinePtr[w-1] = imgLinePtr[w-1];
204
}
205
206
//last col
207
resLinePtr = result.ptr<unsigned char>(h-1);
208
imgLinePtr = img.ptr<unsigned char>(h-1);
209
for(int i = 0 ; i < w ; ++i)
210
resLinePtr[i] = imgLinePtr[i];
211
}
212
213
void Cloning::poissonSolver(const Mat &img, Mat &laplacianX , Mat &laplacianY, Mat &result)
214
{
215
const int w = img.cols;
216
const int h = img.rows;
217
218
Mat lap = laplacianX + laplacianY;
219
220
Mat bound = img.clone();
221
222
rectangle(bound, Point(1, 1), Point(img.cols-2, img.rows-2), Scalar::all(0), -1);
223
Mat boundary_points;
224
Laplacian(bound, boundary_points, CV_32F);
225
226
boundary_points = lap - boundary_points;
227
228
Mat mod_diff = boundary_points(Rect(1, 1, w-2, h-2));
229
230
solve(img,mod_diff,result);
231
}
232
233
void Cloning::initVariables(const Mat &destination, const Mat &binaryMask)
234
{
235
destinationGradientX = Mat(destination.size(),CV_32FC3);
236
destinationGradientY = Mat(destination.size(),CV_32FC3);
237
patchGradientX = Mat(destination.size(),CV_32FC3);
238
patchGradientY = Mat(destination.size(),CV_32FC3);
239
240
binaryMaskFloat = Mat(binaryMask.size(),CV_32FC1);
241
binaryMaskFloatInverted = Mat(binaryMask.size(),CV_32FC1);
242
243
//init of the filters used in the dst
244
const int w = destination.cols;
245
filter_X.resize(w - 2);
246
double scale = CV_PI / (w - 1);
247
for(int i = 0 ; i < w-2 ; ++i)
248
filter_X[i] = 2.0f * (float)std::cos(scale * (i + 1));
249
250
const int h = destination.rows;
251
filter_Y.resize(h - 2);
252
scale = CV_PI / (h - 1);
253
for(int j = 0 ; j < h - 2 ; ++j)
254
filter_Y[j] = 2.0f * (float)std::cos(scale * (j + 1));
255
}
256
257
void Cloning::computeDerivatives(const Mat& destination, const Mat &patch, const Mat &binaryMask)
258
{
259
initVariables(destination, binaryMask);
260
261
computeGradientX(destination, destinationGradientX);
262
computeGradientY(destination, destinationGradientY);
263
264
computeGradientX(patch, patchGradientX);
265
computeGradientY(patch, patchGradientY);
266
267
Mat Kernel(Size(3, 3), CV_8UC1);
268
Kernel.setTo(Scalar(1));
269
erode(binaryMask, binaryMask, Kernel, Point(-1,-1), 3);
270
271
binaryMask.convertTo(binaryMaskFloat, CV_32FC1, 1.0/255.0);
272
}
273
274
void Cloning::scalarProduct(Mat mat, float r, float g, float b)
275
{
276
vector <Mat> channels;
277
split(mat,channels);
278
multiply(channels[2],r,channels[2]);
279
multiply(channels[1],g,channels[1]);
280
multiply(channels[0],b,channels[0]);
281
merge(channels,mat);
282
}
283
284
void Cloning::arrayProduct(const cv::Mat& lhs, const cv::Mat& rhs, cv::Mat& result) const
285
{
286
vector <Mat> lhs_channels;
287
vector <Mat> result_channels;
288
289
split(lhs,lhs_channels);
290
split(result,result_channels);
291
292
for(int chan = 0 ; chan < 3 ; ++chan)
293
multiply(lhs_channels[chan],rhs,result_channels[chan]);
294
295
merge(result_channels,result);
296
}
297
298
void Cloning::poisson(const Mat &destination)
299
{
300
Mat laplacianX = destinationGradientX + patchGradientX;
301
Mat laplacianY = destinationGradientY + patchGradientY;
302
303
computeLaplacianX(laplacianX,laplacianX);
304
computeLaplacianY(laplacianY,laplacianY);
305
306
split(laplacianX,rgbx_channel);
307
split(laplacianY,rgby_channel);
308
309
split(destination,output);
310
311
for(int chan = 0 ; chan < 3 ; ++chan)
312
{
313
poissonSolver(output[chan], rgbx_channel[chan], rgby_channel[chan], output[chan]);
314
}
315
}
316
317
void Cloning::evaluate(const Mat &I, const Mat &wmask, const Mat &cloned)
318
{
319
bitwise_not(wmask,wmask);
320
321
wmask.convertTo(binaryMaskFloatInverted,CV_32FC1,1.0/255.0);
322
323
arrayProduct(destinationGradientX, binaryMaskFloatInverted, destinationGradientX);
324
arrayProduct(destinationGradientY, binaryMaskFloatInverted, destinationGradientY);
325
326
poisson(I);
327
328
merge(output,cloned);
329
}
330
331
void Cloning::normalClone(const Mat &destination, const Mat &patch, const Mat &binaryMask, Mat &cloned, int flag)
332
{
333
const int w = destination.cols;
334
const int h = destination.rows;
335
const int channel = destination.channels();
336
const int n_elem_in_line = w * channel;
337
338
computeDerivatives(destination,patch,binaryMask);
339
340
switch(flag)
341
{
342
case NORMAL_CLONE:
343
arrayProduct(patchGradientX, binaryMaskFloat, patchGradientX);
344
arrayProduct(patchGradientY, binaryMaskFloat, patchGradientY);
345
break;
346
347
case MIXED_CLONE:
348
{
349
AutoBuffer<int> maskIndices(n_elem_in_line);
350
for (int i = 0; i < n_elem_in_line; ++i)
351
maskIndices[i] = i / channel;
352
353
for(int i=0;i < h; i++)
354
{
355
float * patchXLinePtr = patchGradientX.ptr<float>(i);
356
float * patchYLinePtr = patchGradientY.ptr<float>(i);
357
const float * destinationXLinePtr = destinationGradientX.ptr<float>(i);
358
const float * destinationYLinePtr = destinationGradientY.ptr<float>(i);
359
const float * binaryMaskLinePtr = binaryMaskFloat.ptr<float>(i);
360
361
for(int j=0; j < n_elem_in_line; j++)
362
{
363
int maskIndex = maskIndices[j];
364
365
if(abs(patchXLinePtr[j] - patchYLinePtr[j]) >
366
abs(destinationXLinePtr[j] - destinationYLinePtr[j]))
367
{
368
patchXLinePtr[j] *= binaryMaskLinePtr[maskIndex];
369
patchYLinePtr[j] *= binaryMaskLinePtr[maskIndex];
370
}
371
else
372
{
373
patchXLinePtr[j] = destinationXLinePtr[j]
374
* binaryMaskLinePtr[maskIndex];
375
patchYLinePtr[j] = destinationYLinePtr[j]
376
* binaryMaskLinePtr[maskIndex];
377
}
378
}
379
}
380
}
381
break;
382
383
case MONOCHROME_TRANSFER:
384
Mat gray;
385
cvtColor(patch, gray, COLOR_BGR2GRAY );
386
387
computeGradientX(gray,patchGradientX);
388
computeGradientY(gray,patchGradientY);
389
390
arrayProduct(patchGradientX, binaryMaskFloat, patchGradientX);
391
arrayProduct(patchGradientY, binaryMaskFloat, patchGradientY);
392
break;
393
394
}
395
396
evaluate(destination,binaryMask,cloned);
397
}
398
399
void Cloning::localColorChange(Mat &I, Mat &mask, Mat &wmask, Mat &cloned, float red_mul=1.0,
400
float green_mul=1.0, float blue_mul=1.0)
401
{
402
computeDerivatives(I,mask,wmask);
403
404
arrayProduct(patchGradientX,binaryMaskFloat, patchGradientX);
405
arrayProduct(patchGradientY,binaryMaskFloat, patchGradientY);
406
scalarProduct(patchGradientX,red_mul,green_mul,blue_mul);
407
scalarProduct(patchGradientY,red_mul,green_mul,blue_mul);
408
409
evaluate(I,wmask,cloned);
410
}
411
412
void Cloning::illuminationChange(Mat &I, Mat &mask, Mat &wmask, Mat &cloned, float alpha, float beta)
413
{
414
CV_INSTRUMENT_REGION();
415
416
computeDerivatives(I,mask,wmask);
417
418
arrayProduct(patchGradientX,binaryMaskFloat, patchGradientX);
419
arrayProduct(patchGradientY,binaryMaskFloat, patchGradientY);
420
421
Mat mag;
422
magnitude(patchGradientX,patchGradientY,mag);
423
424
Mat multX, multY, multx_temp, multy_temp;
425
426
multiply(patchGradientX,pow(alpha,beta),multX);
427
pow(mag,-1*beta, multx_temp);
428
multiply(multX,multx_temp, patchGradientX);
429
patchNaNs(patchGradientX);
430
431
multiply(patchGradientY,pow(alpha,beta),multY);
432
pow(mag,-1*beta, multy_temp);
433
multiply(multY,multy_temp,patchGradientY);
434
patchNaNs(patchGradientY);
435
436
Mat zeroMask = (patchGradientX != 0);
437
438
patchGradientX.copyTo(patchGradientX, zeroMask);
439
patchGradientY.copyTo(patchGradientY, zeroMask);
440
441
evaluate(I,wmask,cloned);
442
}
443
444
void Cloning::textureFlatten(Mat &I, Mat &mask, Mat &wmask, float low_threshold,
445
float high_threshold, int kernel_size, Mat &cloned)
446
{
447
computeDerivatives(I,mask,wmask);
448
449
Mat out;
450
Canny(mask,out,low_threshold,high_threshold,kernel_size);
451
452
Mat zeros = Mat::zeros(patchGradientX.size(), CV_32FC3);
453
Mat zerosMask = (out != 255);
454
zeros.copyTo(patchGradientX, zerosMask);
455
zeros.copyTo(patchGradientY, zerosMask);
456
457
arrayProduct(patchGradientX,binaryMaskFloat, patchGradientX);
458
arrayProduct(patchGradientY,binaryMaskFloat, patchGradientY);
459
460
evaluate(I,wmask,cloned);
461
}
462
463