76dcf79c79455baf2290e05c5ff04821d38eb001
[profile/ivi/opencv.git] / modules / videostab / src / inpainting.cpp
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
49 using namespace std;
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     IInpainter::setRadius(val);
61 }
62
63
64 void InpaintingPipeline::setFrames(const vector<Mat> &val)
65 {
66     for (size_t i = 0; i < inpainters_.size(); ++i)
67         inpainters_[i]->setFrames(val);
68     IInpainter::setFrames(val);
69 }
70
71
72 void InpaintingPipeline::setMotions(const vector<Mat> &val)
73 {
74     for (size_t i = 0; i < inpainters_.size(); ++i)
75         inpainters_[i]->setMotions(val);
76     IInpainter::setMotions(val);
77 }
78
79
80 void InpaintingPipeline::setStabilizedFrames(const vector<Mat> &val)
81 {
82     for (size_t i = 0; i < inpainters_.size(); ++i)
83         inpainters_[i]->setStabilizedFrames(val);
84     IInpainter::setStabilizedFrames(val);
85 }
86
87
88 void InpaintingPipeline::setStabilizationMotions(const vector<Mat> &val)
89 {
90     for (size_t i = 0; i < inpainters_.size(); ++i)
91         inpainters_[i]->setStabilizationMotions(val);
92     IInpainter::setStabilizationMotions(val);
93 }
94
95
96 void InpaintingPipeline::inpaint(int idx, Mat &frame, Mat &mask)
97 {
98     for (size_t i = 0; i < inpainters_.size(); ++i)
99         inpainters_[i]->inpaint(idx, frame, mask);
100 }
101
102
103 struct Pixel3
104 {
105     float intens;
106     Point3_<uchar> color;
107     bool operator <(const Pixel3 &other) const { return intens < other.intens; }
108 };
109
110
111 ConsistentMosaicInpainter::ConsistentMosaicInpainter()
112 {
113     setStdevThresh(20);
114 }
115
116
117 void ConsistentMosaicInpainter::inpaint(int idx, Mat &frame, Mat &mask)
118 {
119     CV_Assert(frame.type() == CV_8UC3);
120     CV_Assert(mask.size() == frame.size() && mask.type() == CV_8U);
121
122     Mat invS = at(idx, *stabilizationMotions_).inv();
123     vector<Mat_<float> > motions(2*radius_ + 1);
124     for (int i = -radius_; i <= radius_; ++i)
125         motions[radius_ + i] = getMotion(idx, idx + i, *motions_) * invS;
126
127     int n;
128     float mean, var;
129     vector<Pixel3> pixels(2*radius_ + 1);
130
131     Mat_<Point3_<uchar> > frame_(frame);
132     Mat_<uchar> mask_(mask);
133
134     for (int y = 0; y < mask.rows; ++y)
135     {
136         for (int x = 0; x < mask.cols; ++x)
137         {
138             if (!mask_(y, x))
139             {
140                 n = 0;
141                 mean = 0;
142                 var = 0;
143
144                 for (int i = -radius_; i <= radius_; ++i)
145                 {
146                     const Mat_<Point3_<uchar> > &framei = at(idx + i, *frames_);
147                     const Mat_<float> &Mi = motions[radius_ + i];
148                     int xi = cvRound(Mi(0,0)*x + Mi(0,1)*y + Mi(0,2));
149                     int yi = cvRound(Mi(1,0)*x + Mi(1,1)*y + Mi(1,2));
150                     if (xi >= 0 && xi < framei.cols && yi >= 0 && yi < framei.rows)
151                     {
152                         pixels[n].color = framei(yi, xi);
153                         mean += pixels[n].intens = intensity(pixels[n].color);
154                         n++;
155                     }
156                 }
157
158                 if (n > 0)
159                 {
160                     mean /= n;
161                     for (int i = 0; i < n; ++i)
162                         var += sqr(pixels[i].intens - mean);
163                     var /= std::max(n - 1, 1);
164
165                     if (var < stdevThresh_ * stdevThresh_)
166                     {
167                         sort(pixels.begin(), pixels.begin() + n);
168                         int nh = (n-1)/2;
169                         int c1 = pixels[nh].color.x;
170                         int c2 = pixels[nh].color.y;
171                         int c3 = pixels[nh].color.z;
172                         if (n-2*nh)
173                         {
174                             c1 = (c1 + pixels[nh].color.x) / 2;
175                             c2 = (c2 + pixels[nh].color.y) / 2;
176                             c3 = (c3 + pixels[nh].color.z) / 2;
177                         }
178                         frame_(y, x) = Point3_<uchar>(
179                                 static_cast<uchar>(c1),
180                                 static_cast<uchar>(c2),
181                                 static_cast<uchar>(c3));
182                         mask_(y, x) = 255;
183                     }
184                 }
185             }
186         }
187     }
188 }
189
190
191 class MotionInpaintBody
192 {
193 public:
194     void operator ()(int x, int y)
195     {
196         float uEst = 0.f, vEst = 0.f, wSum = 0.f;
197
198         for (int dy = -rad; dy <= rad; ++dy)
199         {
200             for (int dx = -rad; dx <= rad; ++dx)
201             {
202                 int qx0 = x + dx;
203                 int qy0 = y + dy;               
204
205                 if (qy0 >= 0 && qy0 < mask0.rows && qx0 >= 0 && qx0 < mask0.cols && mask0(qy0,qx0))
206                 {
207                     int qx1 = cvRound(qx0 + flowX(qy0,qx0));
208                     int qy1 = cvRound(qy0 + flowY(qy0,qx0));
209                     int px1 = qx1 - dx;
210                     int py1 = qy1 - dy;
211
212                     if (qx1 >= 0 && qx1 < mask1.cols && qy1 >= 0 && qy1 < mask1.rows && mask1(qy1,qx1) &&
213                         px1 >= 0 && px1 < mask1.cols && py1 >= 0 && py1 < mask1.rows && mask1(py1,px1))
214                     {
215                         float dudx = 0.f, dvdx = 0.f, dudy = 0.f, dvdy = 0.f;
216
217                         if (qx0 > 0 && mask0(qy0,qx0-1))
218                         {
219                             if (qx0+1 < mask0.cols && mask0(qy0,qx0+1))
220                             {
221                                 dudx = (flowX(qy0,qx0+1) - flowX(qy0,qx0-1)) * 0.5f;
222                                 dvdx = (flowY(qy0,qx0+1) - flowY(qy0,qx0-1)) * 0.5f;
223                             }
224                             else
225                             {
226                                 dudx = flowX(qy0,qx0) - flowX(qy0,qx0-1);
227                                 dvdx = flowY(qy0,qx0) - flowY(qy0,qx0-1);
228                             }
229                         }
230                         else if (qx0+1 < mask0.cols && mask0(qy0,qx0+1))
231                         {
232                             dudx = flowX(qy0,qx0+1) - flowX(qy0,qx0);
233                             dvdx = flowY(qy0,qx0+1) - flowY(qy0,qx0);
234                         }
235
236                         if (qy0 > 0 && mask0(qy0-1,qx0))
237                         {
238                             if (qy0+1 < mask0.rows && mask0(qy0+1,qx0))
239                             {
240                                 dudy = (flowX(qy0+1,qx0) - flowX(qy0-1,qx0)) * 0.5f;
241                                 dvdy = (flowY(qy0+1,qx0) - flowY(qy0-1,qx0)) * 0.5f;
242                             }
243                             else
244                             {
245                                 dudy = flowX(qy0,qx0) - flowX(qy0-1,qx0);
246                                 dvdy = flowY(qy0,qx0) - flowY(qy0-1,qx0);
247                             }
248                         }
249                         else if (qy0+1 < mask0.rows && mask0(qy0+1,qx0))
250                         {
251                             dudy = flowX(qy0+1,qx0) - flowX(qy0,qx0);
252                             dvdy = flowY(qy0+1,qx0) - flowY(qy0,qx0);
253                         }
254
255                         Point3_<uchar> cp = frame1(py1,px1), cq = frame1(qy1,qx1);
256                         float distColor = sqr(cp.x-cq.x) + sqr(cp.y-cq.y) + sqr(cp.z-cq.z);
257                         float w = 1.f / (sqrt(distColor * (dx*dx + dy*dy)) + eps);
258
259                         uEst += w * (flowX(qy0,qx0) - dudx*dx - dudy*dy);
260                         vEst += w * (flowY(qy0,qx0) - dvdx*dx - dvdy*dy);
261                         wSum += w;
262                     }
263                 }
264             }
265         }
266
267         if (wSum > 0.f)
268         {
269             flowX(y,x) = uEst / wSum;
270             flowY(y,x) = vEst / wSum;
271             mask0(y,x) = 255;
272         }
273     }
274
275     Mat_<Point3_<uchar> > frame1;
276     Mat_<uchar> mask0, mask1;
277     Mat_<float> flowX, flowY;
278     float eps;
279     int rad;
280 };
281
282
283 MotionInpainter::MotionInpainter()
284 {
285 #if HAVE_OPENCV_GPU
286     setOptFlowEstimator(new DensePyrLkOptFlowEstimatorGpu());
287 #else
288     CV_Error(CV_StsNotImplemented, "Current implementation of MotionInpainter requires GPU");
289 #endif
290     setFlowErrorThreshold(1e-4f);
291     setBorderMode(BORDER_REPLICATE);
292 }
293
294
295 void MotionInpainter::inpaint(int idx, Mat &frame, Mat &mask)
296 {
297     priority_queue<pair<float,int> > neighbors;
298     vector<Mat> motions(2*radius_ + 1);
299
300     for (int i = -radius_; i <= radius_; ++i)
301     {
302         Mat motion0to1 = getMotion(idx, idx + i, *motions_) * at(idx, *stabilizationMotions_).inv();
303         motions[radius_ + i] = motion0to1;
304
305         if (i != 0)
306         {
307             float err = alignementError(motion0to1, frame, mask, at(idx + i, *frames_));
308             neighbors.push(make_pair(-err, idx + i));
309         }
310     }
311
312     if (mask1_.size() != mask.size())
313     {
314         mask1_.create(mask.size());
315         mask1_.setTo(255);
316     }
317
318     cvtColor(frame, grayFrame_, CV_BGR2GRAY);
319
320     MotionInpaintBody body;
321     body.rad = 2;
322     body.eps = 1e-4f;
323
324     while (!neighbors.empty())
325     {
326         int neighbor = neighbors.top().second;
327         neighbors.pop();
328
329         Mat motion1to0 = motions[radius_ + neighbor - idx].inv();
330
331         frame1_ = at(neighbor, *frames_);
332         warpAffine(
333                 frame1_, transformedFrame1_, motion1to0(Rect(0,0,3,2)), frame1_.size(),
334                 INTER_LINEAR, borderMode_);
335         cvtColor(transformedFrame1_, transformedGrayFrame1_, CV_BGR2GRAY);
336
337         warpAffine(
338                 mask1_, transformedMask1_, motion1to0(Rect(0,0,3,2)), mask1_.size(),
339                 INTER_NEAREST);
340         erode(transformedMask1_, transformedMask1_, Mat());
341
342         optFlowEstimator_->run(grayFrame_, transformedGrayFrame1_, flowX_, flowY_, flowErrors_);
343
344         calcFlowMask(
345                 flowX_, flowY_, flowErrors_, flowErrorThreshold_, mask, transformedMask1_,
346                 flowMask_);
347
348         body.flowX = flowX_;
349         body.flowY = flowY_;
350         body.mask0 = flowMask_;
351         body.mask1 = transformedMask1_;
352         body.frame1 = transformedFrame1_;
353         fmm_.run(flowMask_, body);
354
355         completeFrameAccordingToFlow(
356                 flowMask_, flowX_, flowY_, transformedFrame1_, transformedMask1_, frame, mask);
357     }
358 }
359
360
361 class ColorAverageInpaintBody
362 {
363 public:
364     void operator ()(int x, int y)
365     {
366         float c1 = 0, c2 = 0, c3 = 0;
367         float wSum = 0;
368
369         static const int lut[8][2] = {{-1,-1}, {-1,0}, {-1,1}, {0,-1}, {0,1}, {1,-1}, {1,0}, {1,1}};
370
371         for (int i = 0; i < 8; ++i)
372         {
373             int qx = x + lut[i][0];
374             int qy = y + lut[i][1];
375             if (qy >= 0 && qy < mask.rows && qx >= 0 && qx < mask.cols && mask(qy,qx))
376             {
377                 c1 += frame.at<uchar>(qy,3*qx);
378                 c2 += frame.at<uchar>(qy,3*qx+1);
379                 c3 += frame.at<uchar>(qy,3*qx+2);
380                 wSum += 1;
381             }
382         }
383
384         float wSumInv = 1.f / wSum;
385         frame(y,x) = Point3_<uchar>(
386                 static_cast<uchar>(c1*wSumInv),
387                 static_cast<uchar>(c2*wSumInv),
388                 static_cast<uchar>(c3*wSumInv));
389         mask(y,x) = 255;
390     }
391
392     cv::Mat_<uchar> mask;
393     cv::Mat_<cv::Point3_<uchar> > frame;
394 };
395
396
397 void ColorAverageInpainter::inpaint(int /*idx*/, Mat &frame, Mat &mask)
398 {
399     ColorAverageInpaintBody body;
400     body.mask = mask;
401     body.frame = frame;
402     fmm_.run(mask, body);
403 }
404
405
406 void ColorInpainter::inpaint(int /*idx*/, Mat &frame, Mat &mask)
407 {
408     bitwise_not(mask, invMask_);
409     cv::inpaint(frame, invMask_, frame, radius_, method_);
410 }
411
412
413 void calcFlowMask(
414         const Mat &flowX, const Mat &flowY, const Mat &errors, float maxError,
415         const Mat &mask0, const Mat &mask1, Mat &flowMask)
416 {
417     CV_Assert(flowX.type() == CV_32F && flowX.size() == mask0.size());
418     CV_Assert(flowY.type() == CV_32F && flowY.size() == mask0.size());
419     CV_Assert(errors.type() == CV_32F && errors.size() == mask0.size());
420     CV_Assert(mask0.type() == CV_8U);
421     CV_Assert(mask1.type() == CV_8U && mask1.size() == mask0.size());
422
423     Mat_<float> flowX_(flowX), flowY_(flowY), errors_(errors);
424     Mat_<uchar> mask0_(mask0), mask1_(mask1);
425
426     flowMask.create(mask0.size(), CV_8U);
427     flowMask.setTo(0);
428     Mat_<uchar> flowMask_(flowMask);
429
430     for (int y0 = 0; y0 < flowMask_.rows; ++y0)
431     {
432         for (int x0 = 0; x0 < flowMask_.cols; ++x0)
433         {
434             if (mask0_(y0,x0) && errors_(y0,x0) < maxError)
435             {
436                 int x1 = cvRound(x0 + flowX_(y0,x0));
437                 int y1 = cvRound(y0 + flowY_(y0,x0));
438
439                 if (x1 >= 0 && x1 < mask1_.cols && y1 >= 0 && y1 < mask1_.rows && mask1_(y1,x1))
440                     flowMask_(y0,x0) = 255;
441             }
442         }
443     }
444 }
445
446
447 void completeFrameAccordingToFlow(
448         const Mat &flowMask, const Mat &flowX, const Mat &flowY, const Mat &frame1, const Mat &mask1,
449         Mat &frame0, Mat &mask0)
450 {
451     CV_Assert(flowMask.type() == CV_8U);
452     CV_Assert(flowX.type() == CV_32F && flowX.size() == flowMask.size());
453     CV_Assert(flowY.type() == CV_32F && flowY.size() == flowMask.size());
454     CV_Assert(frame1.type() == CV_8UC3 && frame1.size() == flowMask.size());
455     CV_Assert(mask1.type() == CV_8U && mask1.size() == flowMask.size());
456     CV_Assert(frame0.type() == CV_8UC3 && frame0.size() == flowMask.size());
457     CV_Assert(mask0.type() == CV_8U && mask0.size() == flowMask.size());
458
459     Mat_<uchar> flowMask_(flowMask), mask1_(mask1), mask0_(mask0);
460     Mat_<float> flowX_(flowX), flowY_(flowY);
461
462     for (int y0 = 0; y0 < frame0.rows; ++y0)
463     {
464         for (int x0 = 0; x0 < frame0.cols; ++x0)
465         {
466             if (!mask0_(y0,x0) && flowMask_(y0,x0))
467             {
468                 int x1 = cvRound(x0 + flowX_(y0,x0));
469                 int y1 = cvRound(y0 + flowY_(y0,x0));
470
471                 if (x1 >= 0 && x1 < frame1.cols && y1 >= 0 && y1 < frame1.rows && mask1_(y1,x1))
472                 {
473                     frame0.at<Point3_<uchar> >(y0,x0) = frame1.at<Point3_<uchar> >(y1,x1);
474                     mask0_(y0,x0) = 255;
475                 }
476             }
477         }
478     }
479 }
480
481 } // namespace videostab
482 } // namespace cv