Path: blob/master/modules/videostab/src/inpainting.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) 2000-2008, Intel Corporation, all rights reserved.13// Copyright (C) 2009-2011, Willow Garage Inc., 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 documentation24// and/or other materials provided with the distribution.25//26// * The name of the copyright holders may not be used to endorse or promote products27// derived from this software without specific prior written permission.28//29// This software is provided by the copyright holders and contributors "as is" and30// any express or implied warranties, including, but not limited to, the implied31// 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 damages34// (including, but not limited to, procurement of substitute goods or services;35// loss of use, data, or profits; or business interruption) however caused36// and on any theory of liability, whether in contract, strict liability,37// or tort (including negligence or otherwise) arising in any way out of38// the use of this software, even if advised of the possibility of such damage.39//40//M*/4142#include "precomp.hpp"43#include <queue>44#include "opencv2/videostab/inpainting.hpp"45#include "opencv2/videostab/global_motion.hpp"46#include "opencv2/videostab/fast_marching.hpp"47#include "opencv2/videostab/ring_buffer.hpp"48#include "opencv2/opencv_modules.hpp"4950namespace cv51{52namespace videostab53{5455void InpaintingPipeline::setRadius(int val)56{57for (size_t i = 0; i < inpainters_.size(); ++i)58inpainters_[i]->setRadius(val);59InpainterBase::setRadius(val);60}616263void InpaintingPipeline::setFrames(const std::vector<Mat> &val)64{65for (size_t i = 0; i < inpainters_.size(); ++i)66inpainters_[i]->setFrames(val);67InpainterBase::setFrames(val);68}697071void InpaintingPipeline::setMotionModel(MotionModel val)72{73for (size_t i = 0; i < inpainters_.size(); ++i)74inpainters_[i]->setMotionModel(val);75InpainterBase::setMotionModel(val);76}777879void InpaintingPipeline::setMotions(const std::vector<Mat> &val)80{81for (size_t i = 0; i < inpainters_.size(); ++i)82inpainters_[i]->setMotions(val);83InpainterBase::setMotions(val);84}858687void InpaintingPipeline::setStabilizedFrames(const std::vector<Mat> &val)88{89for (size_t i = 0; i < inpainters_.size(); ++i)90inpainters_[i]->setStabilizedFrames(val);91InpainterBase::setStabilizedFrames(val);92}939495void InpaintingPipeline::setStabilizationMotions(const std::vector<Mat> &val)96{97for (size_t i = 0; i < inpainters_.size(); ++i)98inpainters_[i]->setStabilizationMotions(val);99InpainterBase::setStabilizationMotions(val);100}101102103void InpaintingPipeline::inpaint(int idx, Mat &frame, Mat &mask)104{105CV_INSTRUMENT_REGION();106107for (size_t i = 0; i < inpainters_.size(); ++i)108inpainters_[i]->inpaint(idx, frame, mask);109}110111112struct Pixel3113{114float intens;115Point3_<uchar> color;116bool operator <(const Pixel3 &other) const { return intens < other.intens; }117};118119120ConsistentMosaicInpainter::ConsistentMosaicInpainter()121{122setStdevThresh(20.f);123}124125126void ConsistentMosaicInpainter::inpaint(int idx, Mat &frame, Mat &mask)127{128CV_INSTRUMENT_REGION();129130CV_Assert(frame.type() == CV_8UC3);131CV_Assert(mask.size() == frame.size() && mask.type() == CV_8U);132133Mat invS = at(idx, *stabilizationMotions_).inv();134std::vector<Mat_<float> > vmotions(2*radius_ + 1);135for (int i = -radius_; i <= radius_; ++i)136vmotions[radius_ + i] = getMotion(idx, idx + i, *motions_) * invS;137138int n;139float mean, var;140std::vector<Pixel3> pixels(2*radius_ + 1);141142Mat_<Point3_<uchar> > frame_(frame);143Mat_<uchar> mask_(mask);144145for (int y = 0; y < mask.rows; ++y)146{147for (int x = 0; x < mask.cols; ++x)148{149if (!mask_(y, x))150{151n = 0;152mean = 0;153var = 0;154155for (int i = -radius_; i <= radius_; ++i)156{157const Mat_<Point3_<uchar> > &framei = at(idx + i, *frames_);158const Mat_<float> &Mi = vmotions[radius_ + i];159int xi = cvRound(Mi(0,0)*x + Mi(0,1)*y + Mi(0,2));160int yi = cvRound(Mi(1,0)*x + Mi(1,1)*y + Mi(1,2));161if (xi >= 0 && xi < framei.cols && yi >= 0 && yi < framei.rows)162{163pixels[n].color = framei(yi, xi);164mean += pixels[n].intens = intensity(pixels[n].color);165n++;166}167}168169if (n > 0)170{171mean /= n;172for (int i = 0; i < n; ++i)173var += sqr(pixels[i].intens - mean);174var /= std::max(n - 1, 1);175176if (var < stdevThresh_ * stdevThresh_)177{178std::sort(pixels.begin(), pixels.begin() + n);179int nh = (n-1)/2;180int c1 = pixels[nh].color.x;181int c2 = pixels[nh].color.y;182int c3 = pixels[nh].color.z;183if (n-2*nh)184{185c1 = (c1 + pixels[nh].color.x) / 2;186c2 = (c2 + pixels[nh].color.y) / 2;187c3 = (c3 + pixels[nh].color.z) / 2;188}189frame_(y, x) = Point3_<uchar>(190static_cast<uchar>(c1),191static_cast<uchar>(c2),192static_cast<uchar>(c3));193mask_(y, x) = 255;194}195}196}197}198}199}200201202static float alignementError(203const Mat &M, const Mat &frame0, const Mat &mask0, const Mat &frame1)204{205CV_Assert(frame0.type() == CV_8UC3 && frame1.type() == CV_8UC3);206CV_Assert(mask0.type() == CV_8U && mask0.size() == frame0.size());207CV_Assert(frame0.size() == frame1.size());208CV_Assert(M.size() == Size(3,3) && M.type() == CV_32F);209210Mat_<uchar> mask0_(mask0);211Mat_<float> M_(M);212float err = 0;213214for (int y0 = 0; y0 < frame0.rows; ++y0)215{216for (int x0 = 0; x0 < frame0.cols; ++x0)217{218if (mask0_(y0,x0))219{220int x1 = cvRound(M_(0,0)*x0 + M_(0,1)*y0 + M_(0,2));221int y1 = cvRound(M_(1,0)*x0 + M_(1,1)*y0 + M_(1,2));222if (y1 >= 0 && y1 < frame1.rows && x1 >= 0 && x1 < frame1.cols)223err += std::abs(intensity(frame1.at<Point3_<uchar> >(y1,x1)) -224intensity(frame0.at<Point3_<uchar> >(y0,x0)));225}226}227}228229return err;230}231232233class MotionInpaintBody234{235public:236void operator ()(int x, int y)237{238float uEst = 0.f, vEst = 0.f, wSum = 0.f;239240for (int dy = -rad; dy <= rad; ++dy)241{242for (int dx = -rad; dx <= rad; ++dx)243{244int qx0 = x + dx;245int qy0 = y + dy;246247if (qy0 >= 0 && qy0 < mask0.rows && qx0 >= 0 && qx0 < mask0.cols && mask0(qy0,qx0))248{249int qx1 = cvRound(qx0 + flowX(qy0,qx0));250int qy1 = cvRound(qy0 + flowY(qy0,qx0));251int px1 = qx1 - dx;252int py1 = qy1 - dy;253254if (qx1 >= 0 && qx1 < mask1.cols && qy1 >= 0 && qy1 < mask1.rows && mask1(qy1,qx1) &&255px1 >= 0 && px1 < mask1.cols && py1 >= 0 && py1 < mask1.rows && mask1(py1,px1))256{257float dudx = 0.f, dvdx = 0.f, dudy = 0.f, dvdy = 0.f;258259if (qx0 > 0 && mask0(qy0,qx0-1))260{261if (qx0+1 < mask0.cols && mask0(qy0,qx0+1))262{263dudx = (flowX(qy0,qx0+1) - flowX(qy0,qx0-1)) * 0.5f;264dvdx = (flowY(qy0,qx0+1) - flowY(qy0,qx0-1)) * 0.5f;265}266else267{268dudx = flowX(qy0,qx0) - flowX(qy0,qx0-1);269dvdx = flowY(qy0,qx0) - flowY(qy0,qx0-1);270}271}272else if (qx0+1 < mask0.cols && mask0(qy0,qx0+1))273{274dudx = flowX(qy0,qx0+1) - flowX(qy0,qx0);275dvdx = flowY(qy0,qx0+1) - flowY(qy0,qx0);276}277278if (qy0 > 0 && mask0(qy0-1,qx0))279{280if (qy0+1 < mask0.rows && mask0(qy0+1,qx0))281{282dudy = (flowX(qy0+1,qx0) - flowX(qy0-1,qx0)) * 0.5f;283dvdy = (flowY(qy0+1,qx0) - flowY(qy0-1,qx0)) * 0.5f;284}285else286{287dudy = flowX(qy0,qx0) - flowX(qy0-1,qx0);288dvdy = flowY(qy0,qx0) - flowY(qy0-1,qx0);289}290}291else if (qy0+1 < mask0.rows && mask0(qy0+1,qx0))292{293dudy = flowX(qy0+1,qx0) - flowX(qy0,qx0);294dvdy = flowY(qy0+1,qx0) - flowY(qy0,qx0);295}296297Point3_<uchar> cp = frame1(py1,px1), cq = frame1(qy1,qx1);298float distColor = sqr(static_cast<float>(cp.x-cq.x))299+ sqr(static_cast<float>(cp.y-cq.y))300+ sqr(static_cast<float>(cp.z-cq.z));301float w = 1.f / (std::sqrt(distColor * (dx*dx + dy*dy)) + eps);302303uEst += w * (flowX(qy0,qx0) - dudx*dx - dudy*dy);304vEst += w * (flowY(qy0,qx0) - dvdx*dx - dvdy*dy);305wSum += w;306}307}308}309}310311if (wSum > 0.f)312{313flowX(y,x) = uEst / wSum;314flowY(y,x) = vEst / wSum;315mask0(y,x) = 255;316}317}318319Mat_<Point3_<uchar> > frame1;320Mat_<uchar> mask0, mask1;321Mat_<float> flowX, flowY;322float eps;323int rad;324};325326327#ifdef _MSC_VER328#pragma warning(disable: 4702) // unreachable code329#endif330MotionInpainter::MotionInpainter()331{332#ifdef HAVE_OPENCV_CUDAOPTFLOW333setOptFlowEstimator(makePtr<DensePyrLkOptFlowEstimatorGpu>());334setFlowErrorThreshold(1e-4f);335setDistThreshold(5.f);336setBorderMode(BORDER_REPLICATE);337#else338CV_Error(Error::StsNotImplemented, "Current implementation of MotionInpainter requires CUDA");339#endif340}341342343void MotionInpainter::inpaint(int idx, Mat &frame, Mat &mask)344{345CV_INSTRUMENT_REGION();346347std::priority_queue<std::pair<float,int> > neighbors;348std::vector<Mat> vmotions(2*radius_ + 1);349350for (int i = -radius_; i <= radius_; ++i)351{352Mat motion0to1 = getMotion(idx, idx + i, *motions_) * at(idx, *stabilizationMotions_).inv();353vmotions[radius_ + i] = motion0to1;354355if (i != 0)356{357float err = alignementError(motion0to1, frame, mask, at(idx + i, *frames_));358neighbors.push(std::make_pair(-err, idx + i));359}360}361362if (mask1_.size() != mask.size())363{364mask1_.create(mask.size());365mask1_.setTo(255);366}367368cvtColor(frame, grayFrame_, COLOR_BGR2GRAY);369370MotionInpaintBody body;371body.rad = 2;372body.eps = 1e-4f;373374while (!neighbors.empty())375{376int neighbor = neighbors.top().second;377neighbors.pop();378379Mat motion1to0 = vmotions[radius_ + neighbor - idx].inv();380381// warp frame382383frame1_ = at(neighbor, *frames_);384385if (motionModel_ != MM_HOMOGRAPHY)386warpAffine(387frame1_, transformedFrame1_, motion1to0(Rect(0,0,3,2)), frame1_.size(),388INTER_LINEAR, borderMode_);389else390warpPerspective(391frame1_, transformedFrame1_, motion1to0, frame1_.size(), INTER_LINEAR,392borderMode_);393394cvtColor(transformedFrame1_, transformedGrayFrame1_, COLOR_BGR2GRAY);395396// warp mask397398if (motionModel_ != MM_HOMOGRAPHY)399warpAffine(400mask1_, transformedMask1_, motion1to0(Rect(0,0,3,2)), mask1_.size(),401INTER_NEAREST);402else403warpPerspective(mask1_, transformedMask1_, motion1to0, mask1_.size(), INTER_NEAREST);404405erode(transformedMask1_, transformedMask1_, Mat());406407// update flow408409optFlowEstimator_->run(grayFrame_, transformedGrayFrame1_, flowX_, flowY_, flowErrors_);410411calcFlowMask(412flowX_, flowY_, flowErrors_, flowErrorThreshold_, mask, transformedMask1_,413flowMask_);414415body.flowX = flowX_;416body.flowY = flowY_;417body.mask0 = flowMask_;418body.mask1 = transformedMask1_;419body.frame1 = transformedFrame1_;420fmm_.run(flowMask_, body);421422completeFrameAccordingToFlow(423flowMask_, flowX_, flowY_, transformedFrame1_, transformedMask1_, distThresh_,424frame, mask);425}426}427428429class ColorAverageInpaintBody430{431public:432void operator ()(int x, int y)433{434float c1 = 0, c2 = 0, c3 = 0;435float wSum = 0;436437static const int lut[8][2] = {{-1,-1}, {-1,0}, {-1,1}, {0,-1}, {0,1}, {1,-1}, {1,0}, {1,1}};438439for (int i = 0; i < 8; ++i)440{441int qx = x + lut[i][0];442int qy = y + lut[i][1];443if (qy >= 0 && qy < mask.rows && qx >= 0 && qx < mask.cols && mask(qy,qx))444{445c1 += frame.at<uchar>(qy,3*qx);446c2 += frame.at<uchar>(qy,3*qx+1);447c3 += frame.at<uchar>(qy,3*qx+2);448wSum += 1;449}450}451452float wSumInv = (std::fabs(wSum) > 0) ? (1.f / wSum) : 0; // if wSum is 0, c1-c3 will be 0 too453frame(y,x) = Point3_<uchar>(454static_cast<uchar>(c1*wSumInv),455static_cast<uchar>(c2*wSumInv),456static_cast<uchar>(c3*wSumInv));457mask(y,x) = 255;458}459460cv::Mat_<uchar> mask;461cv::Mat_<cv::Point3_<uchar> > frame;462};463464465void ColorAverageInpainter::inpaint(int /*idx*/, Mat &frame, Mat &mask)466{467CV_INSTRUMENT_REGION();468469ColorAverageInpaintBody body;470body.mask = mask;471body.frame = frame;472fmm_.run(mask, body);473}474475476void ColorInpainter::inpaint(int /*idx*/, Mat &frame, Mat &mask)477{478CV_INSTRUMENT_REGION();479480bitwise_not(mask, invMask_);481cv::inpaint(frame, invMask_, frame, radius_, method_);482}483484485void calcFlowMask(486const Mat &flowX, const Mat &flowY, const Mat &errors, float maxError,487const Mat &mask0, const Mat &mask1, Mat &flowMask)488{489CV_INSTRUMENT_REGION();490491CV_Assert(flowX.type() == CV_32F && flowX.size() == mask0.size());492CV_Assert(flowY.type() == CV_32F && flowY.size() == mask0.size());493CV_Assert(errors.type() == CV_32F && errors.size() == mask0.size());494CV_Assert(mask0.type() == CV_8U);495CV_Assert(mask1.type() == CV_8U && mask1.size() == mask0.size());496497Mat_<float> flowX_(flowX), flowY_(flowY), errors_(errors);498Mat_<uchar> mask0_(mask0), mask1_(mask1);499500flowMask.create(mask0.size(), CV_8U);501flowMask.setTo(0);502Mat_<uchar> flowMask_(flowMask);503504for (int y0 = 0; y0 < flowMask_.rows; ++y0)505{506for (int x0 = 0; x0 < flowMask_.cols; ++x0)507{508if (mask0_(y0,x0) && errors_(y0,x0) < maxError)509{510int x1 = cvRound(x0 + flowX_(y0,x0));511int y1 = cvRound(y0 + flowY_(y0,x0));512513if (x1 >= 0 && x1 < mask1_.cols && y1 >= 0 && y1 < mask1_.rows && mask1_(y1,x1))514flowMask_(y0,x0) = 255;515}516}517}518}519520521void completeFrameAccordingToFlow(522const Mat &flowMask, const Mat &flowX, const Mat &flowY, const Mat &frame1, const Mat &mask1,523float distThresh, Mat &frame0, Mat &mask0)524{525CV_INSTRUMENT_REGION();526527CV_Assert(flowMask.type() == CV_8U);528CV_Assert(flowX.type() == CV_32F && flowX.size() == flowMask.size());529CV_Assert(flowY.type() == CV_32F && flowY.size() == flowMask.size());530CV_Assert(frame1.type() == CV_8UC3 && frame1.size() == flowMask.size());531CV_Assert(mask1.type() == CV_8U && mask1.size() == flowMask.size());532CV_Assert(frame0.type() == CV_8UC3 && frame0.size() == flowMask.size());533CV_Assert(mask0.type() == CV_8U && mask0.size() == flowMask.size());534535Mat_<uchar> flowMask_(flowMask), mask1_(mask1), mask0_(mask0);536Mat_<float> flowX_(flowX), flowY_(flowY);537538for (int y0 = 0; y0 < frame0.rows; ++y0)539{540for (int x0 = 0; x0 < frame0.cols; ++x0)541{542if (!mask0_(y0,x0) && flowMask_(y0,x0))543{544int x1 = cvRound(x0 + flowX_(y0,x0));545int y1 = cvRound(y0 + flowY_(y0,x0));546547if (x1 >= 0 && x1 < frame1.cols && y1 >= 0 && y1 < frame1.rows && mask1_(y1,x1)548&& sqr(flowX_(y0,x0)) + sqr(flowY_(y0,x0)) < sqr(distThresh))549{550frame0.at<Point3_<uchar> >(y0,x0) = frame1.at<Point3_<uchar> >(y1,x1);551mask0_(y0,x0) = 255;552}553}554}555}556}557558} // namespace videostab559} // namespace cv560561562