Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/modules/videostab/src/inpainting.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) 2000-2008, Intel Corporation, all rights reserved.
14
// Copyright (C) 2009-2011, Willow Garage Inc., all rights reserved.
15
// Third party copyrights are property of their respective owners.
16
//
17
// Redistribution and use in source and binary forms, with or without modification,
18
// are permitted provided that the following conditions are met:
19
//
20
// * Redistribution's of source code must retain the above copyright notice,
21
// this list of conditions and the following disclaimer.
22
//
23
// * Redistribution's in binary form must reproduce the above copyright notice,
24
// this list of conditions and the following disclaimer in the documentation
25
// and/or other materials provided with the distribution.
26
//
27
// * The name of the copyright holders may not be used to endorse or promote products
28
// derived from this software without specific prior written permission.
29
//
30
// This software is provided by the copyright holders and contributors "as is" and
31
// any express or implied warranties, including, but not limited to, the implied
32
// warranties of merchantability and fitness for a particular purpose are disclaimed.
33
// In no event shall the Intel Corporation or contributors be liable for any direct,
34
// indirect, incidental, special, exemplary, or consequential damages
35
// (including, but not limited to, procurement of substitute goods or services;
36
// loss of use, data, or profits; or business interruption) however caused
37
// and on any theory of liability, whether in contract, strict liability,
38
// or tort (including negligence or otherwise) arising in any way out of
39
// the use of this software, even if advised of the possibility of such damage.
40
//
41
//M*/
42
43
#include "precomp.hpp"
44
#include <queue>
45
#include "opencv2/videostab/inpainting.hpp"
46
#include "opencv2/videostab/global_motion.hpp"
47
#include "opencv2/videostab/fast_marching.hpp"
48
#include "opencv2/videostab/ring_buffer.hpp"
49
#include "opencv2/opencv_modules.hpp"
50
51
namespace cv
52
{
53
namespace videostab
54
{
55
56
void InpaintingPipeline::setRadius(int val)
57
{
58
for (size_t i = 0; i < inpainters_.size(); ++i)
59
inpainters_[i]->setRadius(val);
60
InpainterBase::setRadius(val);
61
}
62
63
64
void InpaintingPipeline::setFrames(const std::vector<Mat> &val)
65
{
66
for (size_t i = 0; i < inpainters_.size(); ++i)
67
inpainters_[i]->setFrames(val);
68
InpainterBase::setFrames(val);
69
}
70
71
72
void InpaintingPipeline::setMotionModel(MotionModel val)
73
{
74
for (size_t i = 0; i < inpainters_.size(); ++i)
75
inpainters_[i]->setMotionModel(val);
76
InpainterBase::setMotionModel(val);
77
}
78
79
80
void InpaintingPipeline::setMotions(const std::vector<Mat> &val)
81
{
82
for (size_t i = 0; i < inpainters_.size(); ++i)
83
inpainters_[i]->setMotions(val);
84
InpainterBase::setMotions(val);
85
}
86
87
88
void InpaintingPipeline::setStabilizedFrames(const std::vector<Mat> &val)
89
{
90
for (size_t i = 0; i < inpainters_.size(); ++i)
91
inpainters_[i]->setStabilizedFrames(val);
92
InpainterBase::setStabilizedFrames(val);
93
}
94
95
96
void InpaintingPipeline::setStabilizationMotions(const std::vector<Mat> &val)
97
{
98
for (size_t i = 0; i < inpainters_.size(); ++i)
99
inpainters_[i]->setStabilizationMotions(val);
100
InpainterBase::setStabilizationMotions(val);
101
}
102
103
104
void InpaintingPipeline::inpaint(int idx, Mat &frame, Mat &mask)
105
{
106
CV_INSTRUMENT_REGION();
107
108
for (size_t i = 0; i < inpainters_.size(); ++i)
109
inpainters_[i]->inpaint(idx, frame, mask);
110
}
111
112
113
struct Pixel3
114
{
115
float intens;
116
Point3_<uchar> color;
117
bool operator <(const Pixel3 &other) const { return intens < other.intens; }
118
};
119
120
121
ConsistentMosaicInpainter::ConsistentMosaicInpainter()
122
{
123
setStdevThresh(20.f);
124
}
125
126
127
void ConsistentMosaicInpainter::inpaint(int idx, Mat &frame, Mat &mask)
128
{
129
CV_INSTRUMENT_REGION();
130
131
CV_Assert(frame.type() == CV_8UC3);
132
CV_Assert(mask.size() == frame.size() && mask.type() == CV_8U);
133
134
Mat invS = at(idx, *stabilizationMotions_).inv();
135
std::vector<Mat_<float> > vmotions(2*radius_ + 1);
136
for (int i = -radius_; i <= radius_; ++i)
137
vmotions[radius_ + i] = getMotion(idx, idx + i, *motions_) * invS;
138
139
int n;
140
float mean, var;
141
std::vector<Pixel3> pixels(2*radius_ + 1);
142
143
Mat_<Point3_<uchar> > frame_(frame);
144
Mat_<uchar> mask_(mask);
145
146
for (int y = 0; y < mask.rows; ++y)
147
{
148
for (int x = 0; x < mask.cols; ++x)
149
{
150
if (!mask_(y, x))
151
{
152
n = 0;
153
mean = 0;
154
var = 0;
155
156
for (int i = -radius_; i <= radius_; ++i)
157
{
158
const Mat_<Point3_<uchar> > &framei = at(idx + i, *frames_);
159
const Mat_<float> &Mi = vmotions[radius_ + i];
160
int xi = cvRound(Mi(0,0)*x + Mi(0,1)*y + Mi(0,2));
161
int yi = cvRound(Mi(1,0)*x + Mi(1,1)*y + Mi(1,2));
162
if (xi >= 0 && xi < framei.cols && yi >= 0 && yi < framei.rows)
163
{
164
pixels[n].color = framei(yi, xi);
165
mean += pixels[n].intens = intensity(pixels[n].color);
166
n++;
167
}
168
}
169
170
if (n > 0)
171
{
172
mean /= n;
173
for (int i = 0; i < n; ++i)
174
var += sqr(pixels[i].intens - mean);
175
var /= std::max(n - 1, 1);
176
177
if (var < stdevThresh_ * stdevThresh_)
178
{
179
std::sort(pixels.begin(), pixels.begin() + n);
180
int nh = (n-1)/2;
181
int c1 = pixels[nh].color.x;
182
int c2 = pixels[nh].color.y;
183
int c3 = pixels[nh].color.z;
184
if (n-2*nh)
185
{
186
c1 = (c1 + pixels[nh].color.x) / 2;
187
c2 = (c2 + pixels[nh].color.y) / 2;
188
c3 = (c3 + pixels[nh].color.z) / 2;
189
}
190
frame_(y, x) = Point3_<uchar>(
191
static_cast<uchar>(c1),
192
static_cast<uchar>(c2),
193
static_cast<uchar>(c3));
194
mask_(y, x) = 255;
195
}
196
}
197
}
198
}
199
}
200
}
201
202
203
static float alignementError(
204
const Mat &M, const Mat &frame0, const Mat &mask0, const Mat &frame1)
205
{
206
CV_Assert(frame0.type() == CV_8UC3 && frame1.type() == CV_8UC3);
207
CV_Assert(mask0.type() == CV_8U && mask0.size() == frame0.size());
208
CV_Assert(frame0.size() == frame1.size());
209
CV_Assert(M.size() == Size(3,3) && M.type() == CV_32F);
210
211
Mat_<uchar> mask0_(mask0);
212
Mat_<float> M_(M);
213
float err = 0;
214
215
for (int y0 = 0; y0 < frame0.rows; ++y0)
216
{
217
for (int x0 = 0; x0 < frame0.cols; ++x0)
218
{
219
if (mask0_(y0,x0))
220
{
221
int x1 = cvRound(M_(0,0)*x0 + M_(0,1)*y0 + M_(0,2));
222
int y1 = cvRound(M_(1,0)*x0 + M_(1,1)*y0 + M_(1,2));
223
if (y1 >= 0 && y1 < frame1.rows && x1 >= 0 && x1 < frame1.cols)
224
err += std::abs(intensity(frame1.at<Point3_<uchar> >(y1,x1)) -
225
intensity(frame0.at<Point3_<uchar> >(y0,x0)));
226
}
227
}
228
}
229
230
return err;
231
}
232
233
234
class MotionInpaintBody
235
{
236
public:
237
void operator ()(int x, int y)
238
{
239
float uEst = 0.f, vEst = 0.f, wSum = 0.f;
240
241
for (int dy = -rad; dy <= rad; ++dy)
242
{
243
for (int dx = -rad; dx <= rad; ++dx)
244
{
245
int qx0 = x + dx;
246
int qy0 = y + dy;
247
248
if (qy0 >= 0 && qy0 < mask0.rows && qx0 >= 0 && qx0 < mask0.cols && mask0(qy0,qx0))
249
{
250
int qx1 = cvRound(qx0 + flowX(qy0,qx0));
251
int qy1 = cvRound(qy0 + flowY(qy0,qx0));
252
int px1 = qx1 - dx;
253
int py1 = qy1 - dy;
254
255
if (qx1 >= 0 && qx1 < mask1.cols && qy1 >= 0 && qy1 < mask1.rows && mask1(qy1,qx1) &&
256
px1 >= 0 && px1 < mask1.cols && py1 >= 0 && py1 < mask1.rows && mask1(py1,px1))
257
{
258
float dudx = 0.f, dvdx = 0.f, dudy = 0.f, dvdy = 0.f;
259
260
if (qx0 > 0 && mask0(qy0,qx0-1))
261
{
262
if (qx0+1 < mask0.cols && mask0(qy0,qx0+1))
263
{
264
dudx = (flowX(qy0,qx0+1) - flowX(qy0,qx0-1)) * 0.5f;
265
dvdx = (flowY(qy0,qx0+1) - flowY(qy0,qx0-1)) * 0.5f;
266
}
267
else
268
{
269
dudx = flowX(qy0,qx0) - flowX(qy0,qx0-1);
270
dvdx = flowY(qy0,qx0) - flowY(qy0,qx0-1);
271
}
272
}
273
else if (qx0+1 < mask0.cols && mask0(qy0,qx0+1))
274
{
275
dudx = flowX(qy0,qx0+1) - flowX(qy0,qx0);
276
dvdx = flowY(qy0,qx0+1) - flowY(qy0,qx0);
277
}
278
279
if (qy0 > 0 && mask0(qy0-1,qx0))
280
{
281
if (qy0+1 < mask0.rows && mask0(qy0+1,qx0))
282
{
283
dudy = (flowX(qy0+1,qx0) - flowX(qy0-1,qx0)) * 0.5f;
284
dvdy = (flowY(qy0+1,qx0) - flowY(qy0-1,qx0)) * 0.5f;
285
}
286
else
287
{
288
dudy = flowX(qy0,qx0) - flowX(qy0-1,qx0);
289
dvdy = flowY(qy0,qx0) - flowY(qy0-1,qx0);
290
}
291
}
292
else if (qy0+1 < mask0.rows && mask0(qy0+1,qx0))
293
{
294
dudy = flowX(qy0+1,qx0) - flowX(qy0,qx0);
295
dvdy = flowY(qy0+1,qx0) - flowY(qy0,qx0);
296
}
297
298
Point3_<uchar> cp = frame1(py1,px1), cq = frame1(qy1,qx1);
299
float distColor = sqr(static_cast<float>(cp.x-cq.x))
300
+ sqr(static_cast<float>(cp.y-cq.y))
301
+ sqr(static_cast<float>(cp.z-cq.z));
302
float w = 1.f / (std::sqrt(distColor * (dx*dx + dy*dy)) + eps);
303
304
uEst += w * (flowX(qy0,qx0) - dudx*dx - dudy*dy);
305
vEst += w * (flowY(qy0,qx0) - dvdx*dx - dvdy*dy);
306
wSum += w;
307
}
308
}
309
}
310
}
311
312
if (wSum > 0.f)
313
{
314
flowX(y,x) = uEst / wSum;
315
flowY(y,x) = vEst / wSum;
316
mask0(y,x) = 255;
317
}
318
}
319
320
Mat_<Point3_<uchar> > frame1;
321
Mat_<uchar> mask0, mask1;
322
Mat_<float> flowX, flowY;
323
float eps;
324
int rad;
325
};
326
327
328
#ifdef _MSC_VER
329
#pragma warning(disable: 4702) // unreachable code
330
#endif
331
MotionInpainter::MotionInpainter()
332
{
333
#ifdef HAVE_OPENCV_CUDAOPTFLOW
334
setOptFlowEstimator(makePtr<DensePyrLkOptFlowEstimatorGpu>());
335
setFlowErrorThreshold(1e-4f);
336
setDistThreshold(5.f);
337
setBorderMode(BORDER_REPLICATE);
338
#else
339
CV_Error(Error::StsNotImplemented, "Current implementation of MotionInpainter requires CUDA");
340
#endif
341
}
342
343
344
void MotionInpainter::inpaint(int idx, Mat &frame, Mat &mask)
345
{
346
CV_INSTRUMENT_REGION();
347
348
std::priority_queue<std::pair<float,int> > neighbors;
349
std::vector<Mat> vmotions(2*radius_ + 1);
350
351
for (int i = -radius_; i <= radius_; ++i)
352
{
353
Mat motion0to1 = getMotion(idx, idx + i, *motions_) * at(idx, *stabilizationMotions_).inv();
354
vmotions[radius_ + i] = motion0to1;
355
356
if (i != 0)
357
{
358
float err = alignementError(motion0to1, frame, mask, at(idx + i, *frames_));
359
neighbors.push(std::make_pair(-err, idx + i));
360
}
361
}
362
363
if (mask1_.size() != mask.size())
364
{
365
mask1_.create(mask.size());
366
mask1_.setTo(255);
367
}
368
369
cvtColor(frame, grayFrame_, COLOR_BGR2GRAY);
370
371
MotionInpaintBody body;
372
body.rad = 2;
373
body.eps = 1e-4f;
374
375
while (!neighbors.empty())
376
{
377
int neighbor = neighbors.top().second;
378
neighbors.pop();
379
380
Mat motion1to0 = vmotions[radius_ + neighbor - idx].inv();
381
382
// warp frame
383
384
frame1_ = at(neighbor, *frames_);
385
386
if (motionModel_ != MM_HOMOGRAPHY)
387
warpAffine(
388
frame1_, transformedFrame1_, motion1to0(Rect(0,0,3,2)), frame1_.size(),
389
INTER_LINEAR, borderMode_);
390
else
391
warpPerspective(
392
frame1_, transformedFrame1_, motion1to0, frame1_.size(), INTER_LINEAR,
393
borderMode_);
394
395
cvtColor(transformedFrame1_, transformedGrayFrame1_, COLOR_BGR2GRAY);
396
397
// warp mask
398
399
if (motionModel_ != MM_HOMOGRAPHY)
400
warpAffine(
401
mask1_, transformedMask1_, motion1to0(Rect(0,0,3,2)), mask1_.size(),
402
INTER_NEAREST);
403
else
404
warpPerspective(mask1_, transformedMask1_, motion1to0, mask1_.size(), INTER_NEAREST);
405
406
erode(transformedMask1_, transformedMask1_, Mat());
407
408
// update flow
409
410
optFlowEstimator_->run(grayFrame_, transformedGrayFrame1_, flowX_, flowY_, flowErrors_);
411
412
calcFlowMask(
413
flowX_, flowY_, flowErrors_, flowErrorThreshold_, mask, transformedMask1_,
414
flowMask_);
415
416
body.flowX = flowX_;
417
body.flowY = flowY_;
418
body.mask0 = flowMask_;
419
body.mask1 = transformedMask1_;
420
body.frame1 = transformedFrame1_;
421
fmm_.run(flowMask_, body);
422
423
completeFrameAccordingToFlow(
424
flowMask_, flowX_, flowY_, transformedFrame1_, transformedMask1_, distThresh_,
425
frame, mask);
426
}
427
}
428
429
430
class ColorAverageInpaintBody
431
{
432
public:
433
void operator ()(int x, int y)
434
{
435
float c1 = 0, c2 = 0, c3 = 0;
436
float wSum = 0;
437
438
static const int lut[8][2] = {{-1,-1}, {-1,0}, {-1,1}, {0,-1}, {0,1}, {1,-1}, {1,0}, {1,1}};
439
440
for (int i = 0; i < 8; ++i)
441
{
442
int qx = x + lut[i][0];
443
int qy = y + lut[i][1];
444
if (qy >= 0 && qy < mask.rows && qx >= 0 && qx < mask.cols && mask(qy,qx))
445
{
446
c1 += frame.at<uchar>(qy,3*qx);
447
c2 += frame.at<uchar>(qy,3*qx+1);
448
c3 += frame.at<uchar>(qy,3*qx+2);
449
wSum += 1;
450
}
451
}
452
453
float wSumInv = (std::fabs(wSum) > 0) ? (1.f / wSum) : 0; // if wSum is 0, c1-c3 will be 0 too
454
frame(y,x) = Point3_<uchar>(
455
static_cast<uchar>(c1*wSumInv),
456
static_cast<uchar>(c2*wSumInv),
457
static_cast<uchar>(c3*wSumInv));
458
mask(y,x) = 255;
459
}
460
461
cv::Mat_<uchar> mask;
462
cv::Mat_<cv::Point3_<uchar> > frame;
463
};
464
465
466
void ColorAverageInpainter::inpaint(int /*idx*/, Mat &frame, Mat &mask)
467
{
468
CV_INSTRUMENT_REGION();
469
470
ColorAverageInpaintBody body;
471
body.mask = mask;
472
body.frame = frame;
473
fmm_.run(mask, body);
474
}
475
476
477
void ColorInpainter::inpaint(int /*idx*/, Mat &frame, Mat &mask)
478
{
479
CV_INSTRUMENT_REGION();
480
481
bitwise_not(mask, invMask_);
482
cv::inpaint(frame, invMask_, frame, radius_, method_);
483
}
484
485
486
void calcFlowMask(
487
const Mat &flowX, const Mat &flowY, const Mat &errors, float maxError,
488
const Mat &mask0, const Mat &mask1, Mat &flowMask)
489
{
490
CV_INSTRUMENT_REGION();
491
492
CV_Assert(flowX.type() == CV_32F && flowX.size() == mask0.size());
493
CV_Assert(flowY.type() == CV_32F && flowY.size() == mask0.size());
494
CV_Assert(errors.type() == CV_32F && errors.size() == mask0.size());
495
CV_Assert(mask0.type() == CV_8U);
496
CV_Assert(mask1.type() == CV_8U && mask1.size() == mask0.size());
497
498
Mat_<float> flowX_(flowX), flowY_(flowY), errors_(errors);
499
Mat_<uchar> mask0_(mask0), mask1_(mask1);
500
501
flowMask.create(mask0.size(), CV_8U);
502
flowMask.setTo(0);
503
Mat_<uchar> flowMask_(flowMask);
504
505
for (int y0 = 0; y0 < flowMask_.rows; ++y0)
506
{
507
for (int x0 = 0; x0 < flowMask_.cols; ++x0)
508
{
509
if (mask0_(y0,x0) && errors_(y0,x0) < maxError)
510
{
511
int x1 = cvRound(x0 + flowX_(y0,x0));
512
int y1 = cvRound(y0 + flowY_(y0,x0));
513
514
if (x1 >= 0 && x1 < mask1_.cols && y1 >= 0 && y1 < mask1_.rows && mask1_(y1,x1))
515
flowMask_(y0,x0) = 255;
516
}
517
}
518
}
519
}
520
521
522
void completeFrameAccordingToFlow(
523
const Mat &flowMask, const Mat &flowX, const Mat &flowY, const Mat &frame1, const Mat &mask1,
524
float distThresh, Mat &frame0, Mat &mask0)
525
{
526
CV_INSTRUMENT_REGION();
527
528
CV_Assert(flowMask.type() == CV_8U);
529
CV_Assert(flowX.type() == CV_32F && flowX.size() == flowMask.size());
530
CV_Assert(flowY.type() == CV_32F && flowY.size() == flowMask.size());
531
CV_Assert(frame1.type() == CV_8UC3 && frame1.size() == flowMask.size());
532
CV_Assert(mask1.type() == CV_8U && mask1.size() == flowMask.size());
533
CV_Assert(frame0.type() == CV_8UC3 && frame0.size() == flowMask.size());
534
CV_Assert(mask0.type() == CV_8U && mask0.size() == flowMask.size());
535
536
Mat_<uchar> flowMask_(flowMask), mask1_(mask1), mask0_(mask0);
537
Mat_<float> flowX_(flowX), flowY_(flowY);
538
539
for (int y0 = 0; y0 < frame0.rows; ++y0)
540
{
541
for (int x0 = 0; x0 < frame0.cols; ++x0)
542
{
543
if (!mask0_(y0,x0) && flowMask_(y0,x0))
544
{
545
int x1 = cvRound(x0 + flowX_(y0,x0));
546
int y1 = cvRound(y0 + flowY_(y0,x0));
547
548
if (x1 >= 0 && x1 < frame1.cols && y1 >= 0 && y1 < frame1.rows && mask1_(y1,x1)
549
&& sqr(flowX_(y0,x0)) + sqr(flowY_(y0,x0)) < sqr(distThresh))
550
{
551
frame0.at<Point3_<uchar> >(y0,x0) = frame1.at<Point3_<uchar> >(y1,x1);
552
mask0_(y0,x0) = 255;
553
}
554
}
555
}
556
}
557
}
558
559
} // namespace videostab
560
} // namespace cv
561
562