• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
setRadius(int val)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 
setFrames(const std::vector<Mat> & val)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 
setMotionModel(MotionModel val)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 
setMotions(const std::vector<Mat> & val)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 
setStabilizedFrames(const std::vector<Mat> & val)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 
setStabilizationMotions(const std::vector<Mat> & val)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 
inpaint(int idx,Mat & frame,Mat & mask)104 void InpaintingPipeline::inpaint(int idx, Mat &frame, Mat &mask)
105 {
106     for (size_t i = 0; i < inpainters_.size(); ++i)
107         inpainters_[i]->inpaint(idx, frame, mask);
108 }
109 
110 
111 struct Pixel3
112 {
113     float intens;
114     Point3_<uchar> color;
operator <cv::videostab::Pixel3115     bool operator <(const Pixel3 &other) const { return intens < other.intens; }
116 };
117 
118 
ConsistentMosaicInpainter()119 ConsistentMosaicInpainter::ConsistentMosaicInpainter()
120 {
121     setStdevThresh(20.f);
122 }
123 
124 
inpaint(int idx,Mat & frame,Mat & mask)125 void ConsistentMosaicInpainter::inpaint(int idx, Mat &frame, Mat &mask)
126 {
127     CV_Assert(frame.type() == CV_8UC3);
128     CV_Assert(mask.size() == frame.size() && mask.type() == CV_8U);
129 
130     Mat invS = at(idx, *stabilizationMotions_).inv();
131     std::vector<Mat_<float> > vmotions(2*radius_ + 1);
132     for (int i = -radius_; i <= radius_; ++i)
133         vmotions[radius_ + i] = getMotion(idx, idx + i, *motions_) * invS;
134 
135     int n;
136     float mean, var;
137     std::vector<Pixel3> pixels(2*radius_ + 1);
138 
139     Mat_<Point3_<uchar> > frame_(frame);
140     Mat_<uchar> mask_(mask);
141 
142     for (int y = 0; y < mask.rows; ++y)
143     {
144         for (int x = 0; x < mask.cols; ++x)
145         {
146             if (!mask_(y, x))
147             {
148                 n = 0;
149                 mean = 0;
150                 var = 0;
151 
152                 for (int i = -radius_; i <= radius_; ++i)
153                 {
154                     const Mat_<Point3_<uchar> > &framei = at(idx + i, *frames_);
155                     const Mat_<float> &Mi = vmotions[radius_ + i];
156                     int xi = cvRound(Mi(0,0)*x + Mi(0,1)*y + Mi(0,2));
157                     int yi = cvRound(Mi(1,0)*x + Mi(1,1)*y + Mi(1,2));
158                     if (xi >= 0 && xi < framei.cols && yi >= 0 && yi < framei.rows)
159                     {
160                         pixels[n].color = framei(yi, xi);
161                         mean += pixels[n].intens = intensity(pixels[n].color);
162                         n++;
163                     }
164                 }
165 
166                 if (n > 0)
167                 {
168                     mean /= n;
169                     for (int i = 0; i < n; ++i)
170                         var += sqr(pixels[i].intens - mean);
171                     var /= std::max(n - 1, 1);
172 
173                     if (var < stdevThresh_ * stdevThresh_)
174                     {
175                         std::sort(pixels.begin(), pixels.begin() + n);
176                         int nh = (n-1)/2;
177                         int c1 = pixels[nh].color.x;
178                         int c2 = pixels[nh].color.y;
179                         int c3 = pixels[nh].color.z;
180                         if (n-2*nh)
181                         {
182                             c1 = (c1 + pixels[nh].color.x) / 2;
183                             c2 = (c2 + pixels[nh].color.y) / 2;
184                             c3 = (c3 + pixels[nh].color.z) / 2;
185                         }
186                         frame_(y, x) = Point3_<uchar>(
187                                 static_cast<uchar>(c1),
188                                 static_cast<uchar>(c2),
189                                 static_cast<uchar>(c3));
190                         mask_(y, x) = 255;
191                     }
192                 }
193             }
194         }
195     }
196 }
197 
198 
alignementError(const Mat & M,const Mat & frame0,const Mat & mask0,const Mat & frame1)199 static float alignementError(
200         const Mat &M, const Mat &frame0, const Mat &mask0, const Mat &frame1)
201 {
202     CV_Assert(frame0.type() == CV_8UC3 && frame1.type() == CV_8UC3);
203     CV_Assert(mask0.type() == CV_8U && mask0.size() == frame0.size());
204     CV_Assert(frame0.size() == frame1.size());
205     CV_Assert(M.size() == Size(3,3) && M.type() == CV_32F);
206 
207     Mat_<uchar> mask0_(mask0);
208     Mat_<float> M_(M);
209     float err = 0;
210 
211     for (int y0 = 0; y0 < frame0.rows; ++y0)
212     {
213         for (int x0 = 0; x0 < frame0.cols; ++x0)
214         {
215             if (mask0_(y0,x0))
216             {
217                 int x1 = cvRound(M_(0,0)*x0 + M_(0,1)*y0 + M_(0,2));
218                 int y1 = cvRound(M_(1,0)*x0 + M_(1,1)*y0 + M_(1,2));
219                 if (y1 >= 0 && y1 < frame1.rows && x1 >= 0 && x1 < frame1.cols)
220                     err += std::abs(intensity(frame1.at<Point3_<uchar> >(y1,x1)) -
221                                     intensity(frame0.at<Point3_<uchar> >(y0,x0)));
222             }
223         }
224     }
225 
226     return err;
227 }
228 
229 
230 class MotionInpaintBody
231 {
232 public:
operator ()(int x,int y)233     void operator ()(int x, int y)
234     {
235         float uEst = 0.f, vEst = 0.f, wSum = 0.f;
236 
237         for (int dy = -rad; dy <= rad; ++dy)
238         {
239             for (int dx = -rad; dx <= rad; ++dx)
240             {
241                 int qx0 = x + dx;
242                 int qy0 = y + dy;
243 
244                 if (qy0 >= 0 && qy0 < mask0.rows && qx0 >= 0 && qx0 < mask0.cols && mask0(qy0,qx0))
245                 {
246                     int qx1 = cvRound(qx0 + flowX(qy0,qx0));
247                     int qy1 = cvRound(qy0 + flowY(qy0,qx0));
248                     int px1 = qx1 - dx;
249                     int py1 = qy1 - dy;
250 
251                     if (qx1 >= 0 && qx1 < mask1.cols && qy1 >= 0 && qy1 < mask1.rows && mask1(qy1,qx1) &&
252                         px1 >= 0 && px1 < mask1.cols && py1 >= 0 && py1 < mask1.rows && mask1(py1,px1))
253                     {
254                         float dudx = 0.f, dvdx = 0.f, dudy = 0.f, dvdy = 0.f;
255 
256                         if (qx0 > 0 && mask0(qy0,qx0-1))
257                         {
258                             if (qx0+1 < mask0.cols && mask0(qy0,qx0+1))
259                             {
260                                 dudx = (flowX(qy0,qx0+1) - flowX(qy0,qx0-1)) * 0.5f;
261                                 dvdx = (flowY(qy0,qx0+1) - flowY(qy0,qx0-1)) * 0.5f;
262                             }
263                             else
264                             {
265                                 dudx = flowX(qy0,qx0) - flowX(qy0,qx0-1);
266                                 dvdx = flowY(qy0,qx0) - flowY(qy0,qx0-1);
267                             }
268                         }
269                         else if (qx0+1 < mask0.cols && mask0(qy0,qx0+1))
270                         {
271                             dudx = flowX(qy0,qx0+1) - flowX(qy0,qx0);
272                             dvdx = flowY(qy0,qx0+1) - flowY(qy0,qx0);
273                         }
274 
275                         if (qy0 > 0 && mask0(qy0-1,qx0))
276                         {
277                             if (qy0+1 < mask0.rows && mask0(qy0+1,qx0))
278                             {
279                                 dudy = (flowX(qy0+1,qx0) - flowX(qy0-1,qx0)) * 0.5f;
280                                 dvdy = (flowY(qy0+1,qx0) - flowY(qy0-1,qx0)) * 0.5f;
281                             }
282                             else
283                             {
284                                 dudy = flowX(qy0,qx0) - flowX(qy0-1,qx0);
285                                 dvdy = flowY(qy0,qx0) - flowY(qy0-1,qx0);
286                             }
287                         }
288                         else if (qy0+1 < mask0.rows && mask0(qy0+1,qx0))
289                         {
290                             dudy = flowX(qy0+1,qx0) - flowX(qy0,qx0);
291                             dvdy = flowY(qy0+1,qx0) - flowY(qy0,qx0);
292                         }
293 
294                         Point3_<uchar> cp = frame1(py1,px1), cq = frame1(qy1,qx1);
295                         float distColor = sqr(static_cast<float>(cp.x-cq.x))
296                                         + sqr(static_cast<float>(cp.y-cq.y))
297                                         + sqr(static_cast<float>(cp.z-cq.z));
298                         float w = 1.f / (std::sqrt(distColor * (dx*dx + dy*dy)) + eps);
299 
300                         uEst += w * (flowX(qy0,qx0) - dudx*dx - dudy*dy);
301                         vEst += w * (flowY(qy0,qx0) - dvdx*dx - dvdy*dy);
302                         wSum += w;
303                     }
304                 }
305             }
306         }
307 
308         if (wSum > 0.f)
309         {
310             flowX(y,x) = uEst / wSum;
311             flowY(y,x) = vEst / wSum;
312             mask0(y,x) = 255;
313         }
314     }
315 
316     Mat_<Point3_<uchar> > frame1;
317     Mat_<uchar> mask0, mask1;
318     Mat_<float> flowX, flowY;
319     float eps;
320     int rad;
321 };
322 
323 
MotionInpainter()324 MotionInpainter::MotionInpainter()
325 {
326 #ifdef HAVE_OPENCV_CUDAOPTFLOW
327     setOptFlowEstimator(makePtr<DensePyrLkOptFlowEstimatorGpu>());
328 #else
329     CV_Error(Error::StsNotImplemented, "Current implementation of MotionInpainter requires CUDA");
330 #endif
331     setFlowErrorThreshold(1e-4f);
332     setDistThreshold(5.f);
333     setBorderMode(BORDER_REPLICATE);
334 }
335 
336 
inpaint(int idx,Mat & frame,Mat & mask)337 void MotionInpainter::inpaint(int idx, Mat &frame, Mat &mask)
338 {
339     std::priority_queue<std::pair<float,int> > neighbors;
340     std::vector<Mat> vmotions(2*radius_ + 1);
341 
342     for (int i = -radius_; i <= radius_; ++i)
343     {
344         Mat motion0to1 = getMotion(idx, idx + i, *motions_) * at(idx, *stabilizationMotions_).inv();
345         vmotions[radius_ + i] = motion0to1;
346 
347         if (i != 0)
348         {
349             float err = alignementError(motion0to1, frame, mask, at(idx + i, *frames_));
350             neighbors.push(std::make_pair(-err, idx + i));
351         }
352     }
353 
354     if (mask1_.size() != mask.size())
355     {
356         mask1_.create(mask.size());
357         mask1_.setTo(255);
358     }
359 
360     cvtColor(frame, grayFrame_, COLOR_BGR2GRAY);
361 
362     MotionInpaintBody body;
363     body.rad = 2;
364     body.eps = 1e-4f;
365 
366     while (!neighbors.empty())
367     {
368         int neighbor = neighbors.top().second;
369         neighbors.pop();
370 
371         Mat motion1to0 = vmotions[radius_ + neighbor - idx].inv();
372 
373         // warp frame
374 
375         frame1_ = at(neighbor, *frames_);
376 
377         if (motionModel_ != MM_HOMOGRAPHY)
378             warpAffine(
379                     frame1_, transformedFrame1_, motion1to0(Rect(0,0,3,2)), frame1_.size(),
380                     INTER_LINEAR, borderMode_);
381         else
382             warpPerspective(
383                     frame1_, transformedFrame1_, motion1to0, frame1_.size(), INTER_LINEAR,
384                     borderMode_);
385 
386         cvtColor(transformedFrame1_, transformedGrayFrame1_, COLOR_BGR2GRAY);
387 
388         // warp mask
389 
390         if (motionModel_ != MM_HOMOGRAPHY)
391             warpAffine(
392                     mask1_, transformedMask1_, motion1to0(Rect(0,0,3,2)), mask1_.size(),
393                     INTER_NEAREST);
394         else
395             warpPerspective(mask1_, transformedMask1_, motion1to0, mask1_.size(), INTER_NEAREST);
396 
397         erode(transformedMask1_, transformedMask1_, Mat());
398 
399         // update flow
400 
401         optFlowEstimator_->run(grayFrame_, transformedGrayFrame1_, flowX_, flowY_, flowErrors_);
402 
403         calcFlowMask(
404                 flowX_, flowY_, flowErrors_, flowErrorThreshold_, mask, transformedMask1_,
405                 flowMask_);
406 
407         body.flowX = flowX_;
408         body.flowY = flowY_;
409         body.mask0 = flowMask_;
410         body.mask1 = transformedMask1_;
411         body.frame1 = transformedFrame1_;
412         fmm_.run(flowMask_, body);
413 
414         completeFrameAccordingToFlow(
415                 flowMask_, flowX_, flowY_, transformedFrame1_, transformedMask1_, distThresh_,
416                 frame, mask);
417     }
418 }
419 
420 
421 class ColorAverageInpaintBody
422 {
423 public:
operator ()(int x,int y)424     void operator ()(int x, int y)
425     {
426         float c1 = 0, c2 = 0, c3 = 0;
427         float wSum = 0;
428 
429         static const int lut[8][2] = {{-1,-1}, {-1,0}, {-1,1}, {0,-1}, {0,1}, {1,-1}, {1,0}, {1,1}};
430 
431         for (int i = 0; i < 8; ++i)
432         {
433             int qx = x + lut[i][0];
434             int qy = y + lut[i][1];
435             if (qy >= 0 && qy < mask.rows && qx >= 0 && qx < mask.cols && mask(qy,qx))
436             {
437                 c1 += frame.at<uchar>(qy,3*qx);
438                 c2 += frame.at<uchar>(qy,3*qx+1);
439                 c3 += frame.at<uchar>(qy,3*qx+2);
440                 wSum += 1;
441             }
442         }
443 
444         float wSumInv = 1.f / wSum;
445         frame(y,x) = Point3_<uchar>(
446                 static_cast<uchar>(c1*wSumInv),
447                 static_cast<uchar>(c2*wSumInv),
448                 static_cast<uchar>(c3*wSumInv));
449         mask(y,x) = 255;
450     }
451 
452     cv::Mat_<uchar> mask;
453     cv::Mat_<cv::Point3_<uchar> > frame;
454 };
455 
456 
inpaint(int,Mat & frame,Mat & mask)457 void ColorAverageInpainter::inpaint(int /*idx*/, Mat &frame, Mat &mask)
458 {
459     ColorAverageInpaintBody body;
460     body.mask = mask;
461     body.frame = frame;
462     fmm_.run(mask, body);
463 }
464 
465 
inpaint(int,Mat & frame,Mat & mask)466 void ColorInpainter::inpaint(int /*idx*/, Mat &frame, Mat &mask)
467 {
468     bitwise_not(mask, invMask_);
469     cv::inpaint(frame, invMask_, frame, radius_, method_);
470 }
471 
472 
calcFlowMask(const Mat & flowX,const Mat & flowY,const Mat & errors,float maxError,const Mat & mask0,const Mat & mask1,Mat & flowMask)473 void calcFlowMask(
474         const Mat &flowX, const Mat &flowY, const Mat &errors, float maxError,
475         const Mat &mask0, const Mat &mask1, Mat &flowMask)
476 {
477     CV_Assert(flowX.type() == CV_32F && flowX.size() == mask0.size());
478     CV_Assert(flowY.type() == CV_32F && flowY.size() == mask0.size());
479     CV_Assert(errors.type() == CV_32F && errors.size() == mask0.size());
480     CV_Assert(mask0.type() == CV_8U);
481     CV_Assert(mask1.type() == CV_8U && mask1.size() == mask0.size());
482 
483     Mat_<float> flowX_(flowX), flowY_(flowY), errors_(errors);
484     Mat_<uchar> mask0_(mask0), mask1_(mask1);
485 
486     flowMask.create(mask0.size(), CV_8U);
487     flowMask.setTo(0);
488     Mat_<uchar> flowMask_(flowMask);
489 
490     for (int y0 = 0; y0 < flowMask_.rows; ++y0)
491     {
492         for (int x0 = 0; x0 < flowMask_.cols; ++x0)
493         {
494             if (mask0_(y0,x0) && errors_(y0,x0) < maxError)
495             {
496                 int x1 = cvRound(x0 + flowX_(y0,x0));
497                 int y1 = cvRound(y0 + flowY_(y0,x0));
498 
499                 if (x1 >= 0 && x1 < mask1_.cols && y1 >= 0 && y1 < mask1_.rows && mask1_(y1,x1))
500                     flowMask_(y0,x0) = 255;
501             }
502         }
503     }
504 }
505 
506 
completeFrameAccordingToFlow(const Mat & flowMask,const Mat & flowX,const Mat & flowY,const Mat & frame1,const Mat & mask1,float distThresh,Mat & frame0,Mat & mask0)507 void completeFrameAccordingToFlow(
508         const Mat &flowMask, const Mat &flowX, const Mat &flowY, const Mat &frame1, const Mat &mask1,
509         float distThresh, Mat &frame0, Mat &mask0)
510 {
511     CV_Assert(flowMask.type() == CV_8U);
512     CV_Assert(flowX.type() == CV_32F && flowX.size() == flowMask.size());
513     CV_Assert(flowY.type() == CV_32F && flowY.size() == flowMask.size());
514     CV_Assert(frame1.type() == CV_8UC3 && frame1.size() == flowMask.size());
515     CV_Assert(mask1.type() == CV_8U && mask1.size() == flowMask.size());
516     CV_Assert(frame0.type() == CV_8UC3 && frame0.size() == flowMask.size());
517     CV_Assert(mask0.type() == CV_8U && mask0.size() == flowMask.size());
518 
519     Mat_<uchar> flowMask_(flowMask), mask1_(mask1), mask0_(mask0);
520     Mat_<float> flowX_(flowX), flowY_(flowY);
521 
522     for (int y0 = 0; y0 < frame0.rows; ++y0)
523     {
524         for (int x0 = 0; x0 < frame0.cols; ++x0)
525         {
526             if (!mask0_(y0,x0) && flowMask_(y0,x0))
527             {
528                 int x1 = cvRound(x0 + flowX_(y0,x0));
529                 int y1 = cvRound(y0 + flowY_(y0,x0));
530 
531                 if (x1 >= 0 && x1 < frame1.cols && y1 >= 0 && y1 < frame1.rows && mask1_(y1,x1)
532                     && sqr(flowX_(y0,x0)) + sqr(flowY_(y0,x0)) < sqr(distThresh))
533                 {
534                     frame0.at<Point3_<uchar> >(y0,x0) = frame1.at<Point3_<uchar> >(y1,x1);
535                     mask0_(y0,x0) = 255;
536                 }
537             }
538         }
539     }
540 }
541 
542 } // namespace videostab
543 } // namespace cv
544