Split stabilizer into OnePassStabilizer and TwoPassStabilizer

This commit is contained in:
Alexey Spizhevoy 2012-03-22 07:52:17 +00:00
parent 6708869425
commit 984583981c
11 changed files with 454 additions and 246 deletions

View File

@ -53,24 +53,26 @@ namespace videostab
CV_EXPORTS float calcBlurriness(const Mat &frame); CV_EXPORTS float calcBlurriness(const Mat &frame);
class CV_EXPORTS IDeblurer class CV_EXPORTS DeblurerBase
{ {
public: public:
IDeblurer() : radius_(0), frames_(0), motions_(0) {} DeblurerBase() : radius_(0), frames_(0), motions_(0) {}
virtual ~IDeblurer() {} virtual ~DeblurerBase() {}
virtual void setRadius(int val) { radius_ = val; } virtual void setRadius(int val) { radius_ = val; }
int radius() const { return radius_; } virtual int radius() const { return radius_; }
virtual void setFrames(const std::vector<Mat> &val) { frames_ = &val; } virtual void setFrames(const std::vector<Mat> &val) { frames_ = &val; }
const std::vector<Mat>& frames() const { return *frames_; } virtual const std::vector<Mat>& frames() const { return *frames_; }
virtual void setMotions(const std::vector<Mat> &val) { motions_ = &val; } virtual void setMotions(const std::vector<Mat> &val) { motions_ = &val; }
const std::vector<Mat>& motions() const { return *motions_; } virtual const std::vector<Mat>& motions() const { return *motions_; }
virtual void setBlurrinessRates(const std::vector<float> &val) { blurrinessRates_ = &val; } virtual void setBlurrinessRates(const std::vector<float> &val) { blurrinessRates_ = &val; }
const std::vector<float>& blurrinessRates() const { return *blurrinessRates_; } virtual const std::vector<float>& blurrinessRates() const { return *blurrinessRates_; }
virtual void update() {}
virtual void deblur(int idx, Mat &frame) = 0; virtual void deblur(int idx, Mat &frame) = 0;
@ -81,13 +83,13 @@ protected:
const std::vector<float> *blurrinessRates_; const std::vector<float> *blurrinessRates_;
}; };
class CV_EXPORTS NullDeblurer : public IDeblurer class CV_EXPORTS NullDeblurer : public DeblurerBase
{ {
public: public:
virtual void deblur(int /*idx*/, Mat &/*frame*/) {} virtual void deblur(int /*idx*/, Mat &/*frame*/) {}
}; };
class CV_EXPORTS WeightingDeblurer : public IDeblurer class CV_EXPORTS WeightingDeblurer : public DeblurerBase
{ {
public: public:
WeightingDeblurer(); WeightingDeblurer();

View File

@ -129,6 +129,8 @@ private:
float minInlierRatio_; float minInlierRatio_;
}; };
CV_EXPORTS Mat getMotion(int from, int to, const Mat *motions, int size);
CV_EXPORTS Mat getMotion(int from, int to, const std::vector<Mat> &motions); CV_EXPORTS Mat getMotion(int from, int to, const std::vector<Mat> &motions);
} // namespace videostab } // namespace videostab

View File

@ -54,29 +54,31 @@ namespace cv
namespace videostab namespace videostab
{ {
class CV_EXPORTS IInpainter class CV_EXPORTS InpainterBase
{ {
public: public:
IInpainter() InpainterBase()
: radius_(0), frames_(0), motions_(0), : radius_(0), frames_(0), motions_(0),
stabilizedFrames_(0), stabilizationMotions_(0) {} stabilizedFrames_(0), stabilizationMotions_(0) {}
virtual ~IInpainter() {} virtual ~InpainterBase() {}
virtual void setRadius(int val) { radius_ = val; } virtual void setRadius(int val) { radius_ = val; }
int radius() const { return radius_; } virtual int radius() const { return radius_; }
virtual void setFrames(const std::vector<Mat> &val) { frames_ = &val; } virtual void setFrames(const std::vector<Mat> &val) { frames_ = &val; }
const std::vector<Mat>& frames() const { return *frames_; } virtual const std::vector<Mat>& frames() const { return *frames_; }
virtual void setMotions(const std::vector<Mat> &val) { motions_ = &val; } virtual void setMotions(const std::vector<Mat> &val) { motions_ = &val; }
const std::vector<Mat>& motions() const { return *motions_; } virtual const std::vector<Mat>& motions() const { return *motions_; }
virtual void setStabilizedFrames(const std::vector<Mat> &val) { stabilizedFrames_ = &val; } virtual void setStabilizedFrames(const std::vector<Mat> &val) { stabilizedFrames_ = &val; }
const std::vector<Mat>& stabilizedFrames() const { return *stabilizedFrames_; } virtual const std::vector<Mat>& stabilizedFrames() const { return *stabilizedFrames_; }
virtual void setStabilizationMotions(const std::vector<Mat> &val) { stabilizationMotions_ = &val; } virtual void setStabilizationMotions(const std::vector<Mat> &val) { stabilizationMotions_ = &val; }
const std::vector<Mat>& stabilizationMotions() const { return *stabilizationMotions_; } virtual const std::vector<Mat>& stabilizationMotions() const { return *stabilizationMotions_; }
virtual void update() {}
virtual void inpaint(int idx, Mat &frame, Mat &mask) = 0; virtual void inpaint(int idx, Mat &frame, Mat &mask) = 0;
@ -88,16 +90,16 @@ protected:
const std::vector<Mat> *stabilizationMotions_; const std::vector<Mat> *stabilizationMotions_;
}; };
class CV_EXPORTS NullInpainter : public IInpainter class CV_EXPORTS NullInpainter : public InpainterBase
{ {
public: public:
virtual void inpaint(int /*idx*/, Mat &/*frame*/, Mat &/*mask*/) {} virtual void inpaint(int /*idx*/, Mat &/*frame*/, Mat &/*mask*/) {}
}; };
class CV_EXPORTS InpaintingPipeline : public IInpainter class CV_EXPORTS InpaintingPipeline : public InpainterBase
{ {
public: public:
void pushBack(Ptr<IInpainter> inpainter) { inpainters_.push_back(inpainter); } void pushBack(Ptr<InpainterBase> inpainter) { inpainters_.push_back(inpainter); }
bool empty() const { return inpainters_.empty(); } bool empty() const { return inpainters_.empty(); }
virtual void setRadius(int val); virtual void setRadius(int val);
@ -106,13 +108,15 @@ public:
virtual void setStabilizedFrames(const std::vector<Mat> &val); virtual void setStabilizedFrames(const std::vector<Mat> &val);
virtual void setStabilizationMotions(const std::vector<Mat> &val); virtual void setStabilizationMotions(const std::vector<Mat> &val);
virtual void update();
virtual void inpaint(int idx, Mat &frame, Mat &mask); virtual void inpaint(int idx, Mat &frame, Mat &mask);
private: private:
std::vector<Ptr<IInpainter> > inpainters_; std::vector<Ptr<InpainterBase> > inpainters_;
}; };
class CV_EXPORTS ConsistentMosaicInpainter : public IInpainter class CV_EXPORTS ConsistentMosaicInpainter : public InpainterBase
{ {
public: public:
ConsistentMosaicInpainter(); ConsistentMosaicInpainter();
@ -126,7 +130,7 @@ private:
float stdevThresh_; float stdevThresh_;
}; };
class CV_EXPORTS MotionInpainter : public IInpainter class CV_EXPORTS MotionInpainter : public InpainterBase
{ {
public: public:
MotionInpainter(); MotionInpainter();
@ -159,7 +163,7 @@ private:
Mat_<uchar> flowMask_; Mat_<uchar> flowMask_;
}; };
class CV_EXPORTS ColorAverageInpainter : public IInpainter class CV_EXPORTS ColorAverageInpainter : public InpainterBase
{ {
public: public:
virtual void inpaint(int idx, Mat &frame, Mat &mask); virtual void inpaint(int idx, Mat &frame, Mat &mask);
@ -168,7 +172,7 @@ private:
FastMarchingMethod fmm_; FastMarchingMethod fmm_;
}; };
class CV_EXPORTS ColorInpainter : public IInpainter class CV_EXPORTS ColorInpainter : public InpainterBase
{ {
public: public:
ColorInpainter(int method = INPAINT_TELEA, double radius = 2.) ColorInpainter(int method = INPAINT_TELEA, double radius = 2.)

View File

@ -51,23 +51,44 @@ namespace cv
namespace videostab namespace videostab
{ {
class CV_EXPORTS IMotionFilter class CV_EXPORTS IMotionStabilizer
{ {
public: public:
virtual ~IMotionFilter() {} virtual void stabilize(const Mat *motions, int size, Mat *stabilizationMotions) const = 0;
virtual int radius() const = 0;
virtual Mat apply(int index, std::vector<Mat> &Ms) const = 0;
}; };
class CV_EXPORTS GaussianMotionFilter : public IMotionFilter class CV_EXPORTS MotionFilterBase : public IMotionStabilizer
{ {
public: public:
GaussianMotionFilter(int radius, float stdev); MotionFilterBase() : radius_(0) {}
virtual ~MotionFilterBase() {}
virtual void setRadius(int val) { radius_ = val; }
virtual int radius() const { return radius_; } virtual int radius() const { return radius_; }
virtual Mat apply(int idx, std::vector<Mat> &motions) const;
virtual void update() {}
virtual Mat stabilize(int index, const Mat *motions, int size) const = 0;
virtual void stabilize(const Mat *motions, int size, Mat *stabilizationMotions) const;
protected:
int radius_;
};
class CV_EXPORTS GaussianMotionFilter : public MotionFilterBase
{
public:
GaussianMotionFilter() : stdev_(-1.f) {}
void setStdev(float val) { stdev_ = val; }
float stdev() const { return stdev_; }
virtual void update();
virtual Mat stabilize(int index, const Mat *motions, int size) const;
private: private:
int radius_; float stdev_;
std::vector<float> weight_; std::vector<float> weight_;
}; };

View File

@ -58,79 +58,126 @@ namespace cv
namespace videostab namespace videostab
{ {
class CV_EXPORTS Stabilizer : public IFrameSource class CV_EXPORTS StabilizerBase
{ {
public: public:
Stabilizer(); virtual ~StabilizerBase() {}
void setLog(Ptr<ILog> log) { log_ = log; } void setLog(Ptr<ILog> log) { log_ = log; }
Ptr<ILog> log() const { return log_; } Ptr<ILog> log() const { return log_; }
void setFrameSource(Ptr<IFrameSource> val) { frameSource_ = val; reset(); } void setRadius(int val) { radius_ = val; }
int radius() const { return radius_; }
void setFrameSource(Ptr<IFrameSource> val) { frameSource_ = val; }
Ptr<IFrameSource> frameSource() const { return frameSource_; } Ptr<IFrameSource> frameSource() const { return frameSource_; }
void setMotionEstimator(Ptr<IGlobalMotionEstimator> val) { motionEstimator_ = val; } void setMotionEstimator(Ptr<IGlobalMotionEstimator> val) { motionEstimator_ = val; }
Ptr<IGlobalMotionEstimator> motionEstimator() const { return motionEstimator_; } Ptr<IGlobalMotionEstimator> motionEstimator() const { return motionEstimator_; }
void setMotionFilter(Ptr<IMotionFilter> val) { motionFilter_ = val; reset(); } void setDeblurer(Ptr<DeblurerBase> val) { deblurer_ = val; }
Ptr<IMotionFilter> motionFilter() const { return motionFilter_; } Ptr<DeblurerBase> deblurrer() const { return deblurer_; }
void setDeblurer(Ptr<IDeblurer> val) { deblurer_ = val; reset(); } void setTrimRatio(float val) { trimRatio_ = val; }
Ptr<IDeblurer> deblurrer() const { return deblurer_; }
void setEstimateTrimRatio(bool val) { mustEstimateTrimRatio_ = val; reset(); }
bool mustEstimateTrimRatio() const { return mustEstimateTrimRatio_; }
void setTrimRatio(float val) { trimRatio_ = val; reset(); }
float trimRatio() const { return trimRatio_; } float trimRatio() const { return trimRatio_; }
void setInclusionConstraint(bool val) { inclusionConstraint_ = val; } void setCorrectionForInclusion(bool val) { doCorrectionForInclusion_ = val; }
bool inclusionConstraint() const { return inclusionConstraint_; } bool doCorrectionForInclusion() const { return doCorrectionForInclusion_; }
void setBorderMode(int val) { borderMode_ = val; } void setBorderMode(int val) { borderMode_ = val; }
int borderMode() const { return borderMode_; } int borderMode() const { return borderMode_; }
void setInpainter(Ptr<IInpainter> val) { inpainter_ = val; reset(); } void setInpainter(Ptr<InpainterBase> val) { inpainter_ = val; }
Ptr<IInpainter> inpainter() const { return inpainter_; } Ptr<InpainterBase> inpainter() const { return inpainter_; }
virtual void reset(); protected:
virtual Mat nextFrame(); StabilizerBase();
private: void setUp(int cacheSize, const Mat &frame);
void estimateMotionsAndTrimRatio(); Mat nextStabilizedFrame();
void processFirstFrame(Mat &frame); bool doOneIteration();
bool processNextFrame(); void stabilizeFrame(const Mat &stabilizationMotion);
void stabilizeFrame(int idx);
virtual void setUp(Mat &firstFrame) = 0;
virtual void stabilizeFrame() = 0;
virtual void estimateMotion() = 0;
Ptr<ILog> log_;
Ptr<IFrameSource> frameSource_; Ptr<IFrameSource> frameSource_;
Ptr<IGlobalMotionEstimator> motionEstimator_; Ptr<IGlobalMotionEstimator> motionEstimator_;
Ptr<IMotionFilter> motionFilter_; Ptr<DeblurerBase> deblurer_;
Ptr<IDeblurer> deblurer_; Ptr<InpainterBase> inpainter_;
Ptr<IInpainter> inpainter_; int radius_;
bool mustEstimateTrimRatio_;
float trimRatio_; float trimRatio_;
bool inclusionConstraint_; bool doCorrectionForInclusion_;
int borderMode_; int borderMode_;
Ptr<ILog> log_;
Size frameSize_; Size frameSize_;
Mat frameMask_; Mat frameMask_;
int radius_;
int curPos_; int curPos_;
int curStabilizedPos_; int curStabilizedPos_;
bool auxPassWasDone_;
bool doDeblurring_; bool doDeblurring_;
Mat preProcessedFrame_; Mat preProcessedFrame_;
bool doInpainting_; bool doInpainting_;
Mat inpaintingMask_; Mat inpaintingMask_;
std::vector<Mat> frames_; std::vector<Mat> frames_;
std::vector<Mat> motions_; // motions_[i] is the motion from i to i+1 frame std::vector<Mat> motions_; // motions_[i] is the motion from i-th to i+1-th frame
std::vector<float> blurrinessRates_; std::vector<float> blurrinessRates_;
std::vector<Mat> stabilizedFrames_; std::vector<Mat> stabilizedFrames_;
std::vector<Mat> stabilizedMasks_; std::vector<Mat> stabilizedMasks_;
std::vector<Mat> stabilizationMotions_; std::vector<Mat> stabilizationMotions_;
}; };
class CV_EXPORTS OnePassStabilizer : public StabilizerBase, public IFrameSource
{
public:
OnePassStabilizer();
void setMotionFilter(Ptr<MotionFilterBase> val) { motionFilter_ = val; }
Ptr<MotionFilterBase> motionFilter() const { return motionFilter_; }
virtual void reset() { resetImpl(); }
virtual Mat nextFrame() { return nextStabilizedFrame(); }
private:
void resetImpl();
virtual void setUp(Mat &firstFrame);
virtual void estimateMotion();
virtual void stabilizeFrame();
Ptr<MotionFilterBase> motionFilter_;
};
class CV_EXPORTS TwoPassStabilizer : public StabilizerBase, public IFrameSource
{
public:
TwoPassStabilizer();
void setMotionStabilizer(Ptr<IMotionStabilizer> val) { motionStabilizer_ = val; }
Ptr<IMotionStabilizer> motionStabilizer() const { return motionStabilizer_; }
void setEstimateTrimRatio(bool val) { mustEstTrimRatio_ = val; }
bool mustEstimateTrimaRatio() const { return mustEstTrimRatio_; }
virtual void reset() { resetImpl(); }
virtual Mat nextFrame();
private:
void resetImpl();
void runPrePassIfNecessary();
virtual void setUp(Mat &firstFrame);
virtual void estimateMotion() { /* do nothing as motion was estimation in pre-pass */ }
virtual void stabilizeFrame();
Ptr<IMotionStabilizer> motionStabilizer_;
bool mustEstTrimRatio_;
int frameCount_;
bool isPrePassDone_;
};
} // namespace videostab } // namespace videostab
} // namespace cv } // namespace cv

View File

@ -296,22 +296,28 @@ Mat PyrLkRobustMotionEstimator::estimate(const Mat &frame0, const Mat &frame1)
} }
Mat getMotion(int from, int to, const vector<Mat> &motions) Mat getMotion(int from, int to, const Mat *motions, int size)
{ {
Mat M = Mat::eye(3, 3, CV_32F); Mat M = Mat::eye(3, 3, CV_32F);
if (to > from) if (to > from)
{ {
for (int i = from; i < to; ++i) for (int i = from; i < to; ++i)
M = at(i, motions) * M; M = at(i, motions, size) * M;
} }
else if (from > to) else if (from > to)
{ {
for (int i = to; i < from; ++i) for (int i = to; i < from; ++i)
M = at(i, motions) * M; M = at(i, motions, size) * M;
M = M.inv(); M = M.inv();
} }
return M; return M;
} }
Mat getMotion(int from, int to, const vector<Mat> &motions)
{
return getMotion(from, to, &motions[0], motions.size());
}
} // namespace videostab } // namespace videostab
} // namespace cv } // namespace cv

View File

@ -57,7 +57,7 @@ void InpaintingPipeline::setRadius(int val)
{ {
for (size_t i = 0; i < inpainters_.size(); ++i) for (size_t i = 0; i < inpainters_.size(); ++i)
inpainters_[i]->setRadius(val); inpainters_[i]->setRadius(val);
IInpainter::setRadius(val); InpainterBase::setRadius(val);
} }
@ -65,7 +65,7 @@ void InpaintingPipeline::setFrames(const vector<Mat> &val)
{ {
for (size_t i = 0; i < inpainters_.size(); ++i) for (size_t i = 0; i < inpainters_.size(); ++i)
inpainters_[i]->setFrames(val); inpainters_[i]->setFrames(val);
IInpainter::setFrames(val); InpainterBase::setFrames(val);
} }
@ -73,7 +73,7 @@ void InpaintingPipeline::setMotions(const vector<Mat> &val)
{ {
for (size_t i = 0; i < inpainters_.size(); ++i) for (size_t i = 0; i < inpainters_.size(); ++i)
inpainters_[i]->setMotions(val); inpainters_[i]->setMotions(val);
IInpainter::setMotions(val); InpainterBase::setMotions(val);
} }
@ -81,7 +81,7 @@ void InpaintingPipeline::setStabilizedFrames(const vector<Mat> &val)
{ {
for (size_t i = 0; i < inpainters_.size(); ++i) for (size_t i = 0; i < inpainters_.size(); ++i)
inpainters_[i]->setStabilizedFrames(val); inpainters_[i]->setStabilizedFrames(val);
IInpainter::setStabilizedFrames(val); InpainterBase::setStabilizedFrames(val);
} }
@ -89,7 +89,15 @@ void InpaintingPipeline::setStabilizationMotions(const vector<Mat> &val)
{ {
for (size_t i = 0; i < inpainters_.size(); ++i) for (size_t i = 0; i < inpainters_.size(); ++i)
inpainters_[i]->setStabilizationMotions(val); inpainters_[i]->setStabilizationMotions(val);
IInpainter::setStabilizationMotions(val); InpainterBase::setStabilizationMotions(val);
}
void InpaintingPipeline::update()
{
for (size_t i = 0; i < inpainters_.size(); ++i)
inpainters_[i]->update();
InpainterBase::update();
} }

View File

@ -51,26 +51,34 @@ namespace cv
namespace videostab namespace videostab
{ {
GaussianMotionFilter::GaussianMotionFilter(int radius, float stdev) : radius_(radius) void MotionFilterBase::stabilize(const Mat *motions, int size, Mat *stabilizationMotions) const
{ {
for (int i = 0; i < size; ++i)
stabilizationMotions[i] = stabilize(i, motions, size);
}
void GaussianMotionFilter::update()
{
float sigma = stdev_ > 0.f ? stdev_ : sqrt(static_cast<float>(radius_));
float sum = 0; float sum = 0;
weight_.resize(2*radius_ + 1); weight_.resize(2*radius_ + 1);
for (int i = -radius_; i <= radius_; ++i) for (int i = -radius_; i <= radius_; ++i)
sum += weight_[radius_ + i] = std::exp(-i*i/(stdev*stdev)); sum += weight_[radius_ + i] = std::exp(-i*i/(sigma*sigma));
for (int i = -radius_; i <= radius_; ++i) for (int i = -radius_; i <= radius_; ++i)
weight_[radius_ + i] /= sum; weight_[radius_ + i] /= sum;
} }
Mat GaussianMotionFilter::apply(int idx, vector<Mat> &motions) const Mat GaussianMotionFilter::stabilize(int index, const Mat *motions, int size) const
{ {
const Mat &cur = at(idx, motions); const Mat &cur = at(index, motions, size);
Mat res = Mat::zeros(cur.size(), cur.type()); Mat res = Mat::zeros(cur.size(), cur.type());
float sum = 0.f; float sum = 0.f;
for (int i = std::max(idx - radius_, 0); i <= idx + radius_; ++i) for (int i = std::max(index - radius_, 0); i <= index + radius_; ++i)
{ {
res += weight_[radius_ + i - idx] * getMotion(idx, i, motions); res += weight_[radius_ + i - index] * getMotion(index, i, motions, size);
sum += weight_[radius_ + i - idx]; sum += weight_[radius_ + i - index];
} }
return res / sum; return res / sum;
} }

View File

@ -62,9 +62,19 @@ inline float intensity(const cv::Point3_<uchar> &bgr)
return 0.3f*bgr.x + 0.59f*bgr.y + 0.11f*bgr.z; return 0.3f*bgr.x + 0.59f*bgr.y + 0.11f*bgr.z;
} }
template <typename T> inline T& at(int index, const T *items, int size)
{
return items[cv::borderInterpolate(index, size, cv::BORDER_WRAP)];
}
template <typename T> inline const T& at(int index, const T *items, int size)
{
return items[cv::borderInterpolate(index, size, cv::BORDER_WRAP)];
}
template <typename T> inline T& at(int index, std::vector<T> &items) template <typename T> inline T& at(int index, std::vector<T> &items)
{ {
return items[cv::borderInterpolate(index, items.size(), cv::BORDER_WRAP)]; return at(index, &items[0], items.size());
} }
template <typename T> inline const T& at(int index, const std::vector<T> &items) template <typename T> inline const T& at(int index, const std::vector<T> &items)

View File

@ -50,130 +50,23 @@ namespace cv
namespace videostab namespace videostab
{ {
Stabilizer::Stabilizer() StabilizerBase::StabilizerBase()
{ {
setLog(new NullLog());
setFrameSource(new NullFrameSource()); setFrameSource(new NullFrameSource());
setMotionEstimator(new PyrLkRobustMotionEstimator()); setMotionEstimator(new PyrLkRobustMotionEstimator());
setMotionFilter(new GaussianMotionFilter(15, sqrt(15.f)));
setDeblurer(new NullDeblurer()); setDeblurer(new NullDeblurer());
setInpainter(new NullInpainter()); setInpainter(new NullInpainter());
setEstimateTrimRatio(true); setRadius(15);
setTrimRatio(0); setTrimRatio(0);
setInclusionConstraint(false); setCorrectionForInclusion(false);
setBorderMode(BORDER_REPLICATE); setBorderMode(BORDER_REPLICATE);
setLog(new NullLog());
} }
void Stabilizer::reset() void StabilizerBase::setUp(int cacheSize, const Mat &frame)
{ {
radius_ = 0; InpainterBase *inpainter = static_cast<InpainterBase*>(inpainter_);
curPos_ = -1;
curStabilizedPos_ = -1;
auxPassWasDone_ = false;
frames_.clear();
motions_.clear();
stabilizedFrames_.clear();
stabilizationMotions_.clear();
doDeblurring_ = false;
doInpainting_ = false;
}
Mat Stabilizer::nextFrame()
{
if (mustEstimateTrimRatio_ && !auxPassWasDone_)
{
estimateMotionsAndTrimRatio();
auxPassWasDone_ = true;
frameSource_->reset();
}
if (curStabilizedPos_ == curPos_ && curStabilizedPos_ != -1)
return Mat(); // we've processed all frames already
bool processed;
do {
processed = processNextFrame();
} while (processed && curStabilizedPos_ == -1);
if (curStabilizedPos_ == -1)
return Mat(); // frame source is empty
const Mat &stabilizedFrame = at(curStabilizedPos_, stabilizedFrames_);
int dx = static_cast<int>(floor(trimRatio_ * stabilizedFrame.cols));
int dy = static_cast<int>(floor(trimRatio_ * stabilizedFrame.rows));
return stabilizedFrame(Rect(dx, dy, stabilizedFrame.cols - 2*dx, stabilizedFrame.rows - 2*dy));
}
void Stabilizer::estimateMotionsAndTrimRatio()
{
log_->print("estimating motions and trim ratio");
Size size;
Mat prevFrame, frame;
int frameCount = 0;
while (!(frame = frameSource_->nextFrame()).empty())
{
if (frameCount > 0)
motions_.push_back(motionEstimator_->estimate(prevFrame, frame));
else
size = frame.size();
prevFrame = frame;
frameCount++;
log_->print(".");
}
radius_ = motionFilter_->radius();
for (int i = 0; i < radius_; ++i)
motions_.push_back(Mat::eye(3, 3, CV_32F));
log_->print("\n");
trimRatio_ = 0;
for (int i = 0; i < frameCount; ++i)
{
Mat S = motionFilter_->apply(i, motions_);
trimRatio_ = std::max(trimRatio_, estimateOptimalTrimRatio(S, size));
stabilizationMotions_.push_back(S);
}
log_->print("estimated trim ratio: %f\n", static_cast<double>(trimRatio_));
}
void Stabilizer::processFirstFrame(Mat &frame)
{
log_->print("processing frames");
frameSize_ = frame.size();
frameMask_.create(frameSize_, CV_8U);
frameMask_.setTo(255);
radius_ = motionFilter_->radius();
int cacheSize = 2*radius_ + 1;
frames_.resize(cacheSize);
stabilizedFrames_.resize(cacheSize);
stabilizedMasks_.resize(cacheSize);
if (!auxPassWasDone_)
{
motions_.resize(cacheSize);
stabilizationMotions_.resize(cacheSize);
}
for (int i = -radius_; i < 0; ++i)
{
at(i, motions_) = Mat::eye(3, 3, CV_32F);
at(i, frames_) = frame;
}
at(0, frames_) = frame;
IInpainter *inpainter = static_cast<IInpainter*>(inpainter_);
doInpainting_ = dynamic_cast<NullInpainter*>(inpainter) == 0; doInpainting_ = dynamic_cast<NullInpainter*>(inpainter) == 0;
if (doInpainting_) if (doInpainting_)
{ {
@ -182,9 +75,10 @@ void Stabilizer::processFirstFrame(Mat &frame)
inpainter_->setMotions(motions_); inpainter_->setMotions(motions_);
inpainter_->setStabilizedFrames(stabilizedFrames_); inpainter_->setStabilizedFrames(stabilizedFrames_);
inpainter_->setStabilizationMotions(stabilizationMotions_); inpainter_->setStabilizationMotions(stabilizationMotions_);
inpainter_->update();
} }
IDeblurer *deblurer = static_cast<IDeblurer*>(deblurer_); DeblurerBase *deblurer = static_cast<DeblurerBase*>(deblurer_);
doDeblurring_ = dynamic_cast<NullDeblurer*>(deblurer) == 0; doDeblurring_ = dynamic_cast<NullDeblurer*>(deblurer) == 0;
if (doDeblurring_) if (doDeblurring_)
{ {
@ -196,11 +90,33 @@ void Stabilizer::processFirstFrame(Mat &frame)
deblurer_->setFrames(frames_); deblurer_->setFrames(frames_);
deblurer_->setMotions(motions_); deblurer_->setMotions(motions_);
deblurer_->setBlurrinessRates(blurrinessRates_); deblurer_->setBlurrinessRates(blurrinessRates_);
deblurer_->update();
} }
log_->print("processing frames");
} }
bool Stabilizer::processNextFrame() Mat StabilizerBase::nextStabilizedFrame()
{
if (curStabilizedPos_ == curPos_ && curStabilizedPos_ != -1)
return Mat(); // we've processed all frames already
bool processed;
do processed = doOneIteration();
while (processed && curStabilizedPos_ == -1);
if (curStabilizedPos_ == -1)
return Mat(); // frame source is empty
const Mat &stabilizedFrame = at(curStabilizedPos_, stabilizedFrames_);
int dx = static_cast<int>(floor(trimRatio_ * stabilizedFrame.cols));
int dy = static_cast<int>(floor(trimRatio_ * stabilizedFrame.rows));
return stabilizedFrame(Rect(dx, dy, stabilizedFrame.cols - 2*dx, stabilizedFrame.rows - 2*dy));
}
bool StabilizerBase::doOneIteration()
{ {
Mat frame = frameSource_->nextFrame(); Mat frame = frameSource_->nextFrame();
if (!frame.empty()) if (!frame.empty())
@ -214,21 +130,16 @@ bool Stabilizer::processNextFrame()
if (doDeblurring_) if (doDeblurring_)
at(curPos_, blurrinessRates_) = calcBlurriness(frame); at(curPos_, blurrinessRates_) = calcBlurriness(frame);
if (!auxPassWasDone_) estimateMotion();
{
Mat motionPrevToCur = motionEstimator_->estimate(
at(curPos_ - 1, frames_), at(curPos_, frames_));
at(curPos_ - 1, motions_) = motionPrevToCur;
}
if (curPos_ >= radius_) if (curPos_ >= radius_)
{ {
curStabilizedPos_ = curPos_ - radius_; curStabilizedPos_ = curPos_ - radius_;
stabilizeFrame(curStabilizedPos_); stabilizeFrame();
} }
} }
else else
processFirstFrame(frame); setUp(frame);
log_->print("."); log_->print(".");
return true; return true;
@ -239,7 +150,7 @@ bool Stabilizer::processNextFrame()
curStabilizedPos_++; curStabilizedPos_++;
at(curStabilizedPos_ + radius_, frames_) = at(curPos_, frames_); at(curStabilizedPos_ + radius_, frames_) = at(curPos_, frames_);
at(curStabilizedPos_ + radius_ - 1, motions_) = at(curPos_ - 1, motions_); at(curStabilizedPos_ + radius_ - 1, motions_) = at(curPos_ - 1, motions_);
stabilizeFrame(curStabilizedPos_); stabilizeFrame();
log_->print("."); log_->print(".");
return true; return true;
@ -249,42 +160,215 @@ bool Stabilizer::processNextFrame()
} }
void Stabilizer::stabilizeFrame(int idx) void StabilizerBase::stabilizeFrame(const Mat &stabilizationMotion)
{ {
Mat stabMotion; Mat stabilizationMotion_;
if (!auxPassWasDone_) if (doCorrectionForInclusion_)
stabMotion = motionFilter_->apply(idx, motions_); stabilizationMotion_ = ensureInclusionConstraint(stabilizationMotion, frameSize_, trimRatio_);
else else
stabMotion = at(idx, stabilizationMotions_); stabilizationMotion_ = stabilizationMotion.clone();
if (inclusionConstraint_ && !mustEstimateTrimRatio_) at(curStabilizedPos_, stabilizationMotions_) = stabilizationMotion_;
stabMotion = ensureInclusionConstraint(stabMotion, frameSize_, trimRatio_);
at(idx, stabilizationMotions_) = stabMotion;
if (doDeblurring_) if (doDeblurring_)
{ {
at(idx, frames_).copyTo(preProcessedFrame_); at(curStabilizedPos_, frames_).copyTo(preProcessedFrame_);
deblurer_->deblur(idx, preProcessedFrame_); deblurer_->deblur(curStabilizedPos_, preProcessedFrame_);
} }
else else
preProcessedFrame_ = at(idx, frames_); preProcessedFrame_ = at(curStabilizedPos_, frames_);
// apply stabilization transformation // apply stabilization transformation
warpAffine( warpAffine(
preProcessedFrame_, at(idx, stabilizedFrames_), stabMotion(Rect(0,0,3,2)), preProcessedFrame_, at(curStabilizedPos_, stabilizedFrames_),
frameSize_, INTER_LINEAR, borderMode_); stabilizationMotion_(Rect(0,0,3,2)), frameSize_, INTER_LINEAR, borderMode_);
if (doInpainting_) if (doInpainting_)
{ {
warpAffine( warpAffine(
frameMask_, at(idx, stabilizedMasks_), stabMotion(Rect(0,0,3,2)), frameSize_, frameMask_, at(curStabilizedPos_, stabilizedMasks_),
INTER_NEAREST); stabilizationMotion_(Rect(0,0,3,2)), frameSize_, INTER_NEAREST);
erode(at(idx, stabilizedMasks_), at(idx, stabilizedMasks_), Mat());
at(idx, stabilizedMasks_).copyTo(inpaintingMask_); erode(at(curStabilizedPos_, stabilizedMasks_), at(curStabilizedPos_, stabilizedMasks_),
inpainter_->inpaint(idx, at(idx, stabilizedFrames_), inpaintingMask_); Mat());
at(curStabilizedPos_, stabilizedMasks_).copyTo(inpaintingMask_);
inpainter_->inpaint(
curStabilizedPos_, at(curStabilizedPos_, stabilizedFrames_), inpaintingMask_);
} }
} }
OnePassStabilizer::OnePassStabilizer()
{
setMotionFilter(new GaussianMotionFilter());
resetImpl();
}
void OnePassStabilizer::resetImpl()
{
curPos_ = -1;
curStabilizedPos_ = -1;
frames_.clear();
motions_.clear();
stabilizedFrames_.clear();
stabilizationMotions_.clear();
doDeblurring_ = false;
doInpainting_ = false;
}
void OnePassStabilizer::setUp(Mat &firstFrame)
{
frameSize_ = firstFrame.size();
frameMask_.create(frameSize_, CV_8U);
frameMask_.setTo(255);
int cacheSize = 2*radius_ + 1;
frames_.resize(cacheSize);
stabilizedFrames_.resize(cacheSize);
stabilizedMasks_.resize(cacheSize);
motions_.resize(cacheSize);
stabilizationMotions_.resize(cacheSize);
for (int i = -radius_; i < 0; ++i)
{
at(i, motions_) = Mat::eye(3, 3, CV_32F);
at(i, frames_) = firstFrame;
}
at(0, frames_) = firstFrame;
motionFilter_->setRadius(radius_);
motionFilter_->update();
StabilizerBase::setUp(cacheSize, firstFrame);
}
void OnePassStabilizer::estimateMotion()
{
at(curPos_ - 1, motions_) = motionEstimator_->estimate(
at(curPos_ - 1, frames_), at(curPos_, frames_));
}
void OnePassStabilizer::stabilizeFrame()
{
Mat stabilizationMotion = motionFilter_->stabilize(curStabilizedPos_, &motions_[0], motions_.size());
StabilizerBase::stabilizeFrame(stabilizationMotion);
}
TwoPassStabilizer::TwoPassStabilizer()
{
setMotionStabilizer(new GaussianMotionFilter());
setEstimateTrimRatio(true);
resetImpl();
}
Mat TwoPassStabilizer::nextFrame()
{
runPrePassIfNecessary();
return StabilizerBase::nextStabilizedFrame();
}
void TwoPassStabilizer::resetImpl()
{
isPrePassDone_ = false;
frameCount_ = 0;
curPos_ = -1;
curStabilizedPos_ = -1;
frames_.clear();
motions_.clear();
stabilizedFrames_.clear();
stabilizationMotions_.clear();
doDeblurring_ = false;
doInpainting_ = false;
}
void TwoPassStabilizer::runPrePassIfNecessary()
{
if (!isPrePassDone_)
{
log_->print("first pass: estimating motions");
Mat prevFrame, frame;
while (!(frame = frameSource_->nextFrame()).empty())
{
if (frameCount_ > 0)
motions_.push_back(motionEstimator_->estimate(prevFrame, frame));
else
{
frameSize_ = frame.size();
frameMask_.create(frameSize_, CV_8U);
frameMask_.setTo(255);
}
prevFrame = frame;
frameCount_++;
log_->print(".");
}
for (int i = 0; i < radius_; ++i)
motions_.push_back(Mat::eye(3, 3, CV_32F));
log_->print("\n");
IMotionStabilizer *motionStabilizer = static_cast<IMotionStabilizer*>(motionStabilizer_);
MotionFilterBase *motionFilterBase = dynamic_cast<MotionFilterBase*>(motionStabilizer);
if (motionFilterBase)
{
motionFilterBase->setRadius(radius_);
motionFilterBase->update();
}
stabilizationMotions_.resize(frameCount_);
motionStabilizer_->stabilize(&motions_[0], frameCount_, &stabilizationMotions_[0]);
if (mustEstTrimRatio_)
{
trimRatio_ = 0;
for (int i = 0; i < frameCount_; ++i)
{
Mat S = stabilizationMotions_[i];
trimRatio_ = std::max(trimRatio_, estimateOptimalTrimRatio(S, frameSize_));
}
log_->print("estimated trim ratio: %f\n", static_cast<double>(trimRatio_));
}
isPrePassDone_ = true;
frameSource_->reset();
}
}
void TwoPassStabilizer::setUp(Mat &firstFrame)
{
int cacheSize = 2*radius_ + 1;
frames_.resize(cacheSize);
stabilizedFrames_.resize(cacheSize);
stabilizedMasks_.resize(cacheSize);
for (int i = -radius_; i <= 0; ++i)
at(i, frames_) = firstFrame;
StabilizerBase::setUp(cacheSize, firstFrame);
}
void TwoPassStabilizer::stabilizeFrame()
{
StabilizerBase::stabilizeFrame(stabilizationMotions_[curStabilizedPos_]);
}
} // namespace videostab } // namespace videostab
} // namespace cv } // namespace cv

View File

@ -12,7 +12,7 @@ using namespace std;
using namespace cv; using namespace cv;
using namespace cv::videostab; using namespace cv::videostab;
Ptr<Stabilizer> stabilizer; Ptr<IFrameSource> stabilizedFrames;
double outputFps; double outputFps;
string outputPath; string outputPath;
bool quietMode; bool quietMode;
@ -25,7 +25,7 @@ void run()
VideoWriter writer; VideoWriter writer;
Mat stabilizedFrame; Mat stabilizedFrame;
while (!(stabilizedFrame = stabilizer->nextFrame()).empty()) while (!(stabilizedFrame = stabilizedFrames->nextFrame()).empty())
{ {
if (!outputPath.empty()) if (!outputPath.empty())
{ {
@ -87,7 +87,7 @@ void printHelp()
" Do color inpainting. The defailt is no.\n" " Do color inpainting. The defailt is no.\n"
" --color-inpaint-radius=<float_number>\n" " --color-inpaint-radius=<float_number>\n"
" Set color inpainting radius (for ns and telea options only).\n\n" " Set color inpainting radius (for ns and telea options only).\n\n"
" -o, --output=<file_path>\n" " -o, --output=(no|<file_path>)\n"
" Set output file path explicitely. The default is stabilized.avi.\n" " Set output file path explicitely. The default is stabilized.avi.\n"
" --fps=<int_number>\n" " --fps=<int_number>\n"
" Set output video FPS explicitely. By default the source FPS is used.\n" " Set output video FPS explicitely. By default the source FPS is used.\n"
@ -134,9 +134,35 @@ int main(int argc, const char **argv)
{ {
printHelp(); printHelp();
return 0; return 0;
}
StabilizerBase *stabilizer;
GaussianMotionFilter *motionFilter = 0;
if (!cmd.get<string>("stdev").empty())
{
motionFilter = new GaussianMotionFilter();
motionFilter->setStdev(cmd.get<float>("stdev"));
} }
stabilizer = new Stabilizer(); bool isTwoPass = cmd.get<string>("est-trim") == "yes";
if (isTwoPass)
{
TwoPassStabilizer *twoPassStabilizer = new TwoPassStabilizer();
if (!cmd.get<string>("est-trim").empty())
twoPassStabilizer->setEstimateTrimRatio(cmd.get<string>("est-trim") == "yes");
if (motionFilter)
twoPassStabilizer->setMotionStabilizer(motionFilter);
stabilizer = twoPassStabilizer;
}
else
{
OnePassStabilizer *onePassStabilizer= new OnePassStabilizer();
if (motionFilter)
onePassStabilizer->setMotionFilter(motionFilter);
stabilizer = onePassStabilizer;
}
string inputPath = cmd.get<string>("1"); string inputPath = cmd.get<string>("1");
if (inputPath.empty()) if (inputPath.empty())
@ -169,16 +195,8 @@ int main(int argc, const char **argv)
stabilizer->setMotionEstimator(motionEstimator); stabilizer->setMotionEstimator(motionEstimator);
int smoothRadius = -1;
float smoothStdev = -1;
if (!cmd.get<string>("radius").empty()) if (!cmd.get<string>("radius").empty())
smoothRadius = cmd.get<int>("radius"); stabilizer->setRadius(cmd.get<int>("radius"));
if (!cmd.get<string>("stdev").empty())
smoothStdev = cmd.get<float>("stdev");
if (smoothRadius > 0 && smoothStdev > 0)
stabilizer->setMotionFilter(new GaussianMotionFilter(smoothRadius, smoothStdev));
else if (smoothRadius > 0 && smoothStdev < 0)
stabilizer->setMotionFilter(new GaussianMotionFilter(smoothRadius, sqrt(static_cast<float>(smoothRadius))));
if (cmd.get<string>("deblur") == "yes") if (cmd.get<string>("deblur") == "yes")
{ {
@ -188,14 +206,11 @@ int main(int argc, const char **argv)
stabilizer->setDeblurer(deblurer); stabilizer->setDeblurer(deblurer);
} }
if (!cmd.get<string>("est-trim").empty())
stabilizer->setEstimateTrimRatio(cmd.get<string>("est-trim") == "yes");
if (!cmd.get<string>("trim-ratio").empty()) if (!cmd.get<string>("trim-ratio").empty())
stabilizer->setTrimRatio(cmd.get<float>("trim-ratio")); stabilizer->setTrimRatio(cmd.get<float>("trim-ratio"));
if (!cmd.get<string>("incl-constr").empty()) if (!cmd.get<string>("incl-constr").empty())
stabilizer->setInclusionConstraint(cmd.get<string>("incl-constr") == "yes"); stabilizer->setCorrectionForInclusion(cmd.get<string>("incl-constr") == "yes");
if (cmd.get<string>("border-mode") == "reflect") if (cmd.get<string>("border-mode") == "reflect")
stabilizer->setBorderMode(BORDER_REFLECT); stabilizer->setBorderMode(BORDER_REFLECT);
@ -250,22 +265,23 @@ int main(int argc, const char **argv)
stabilizer->setLog(new LogToStdout()); stabilizer->setLog(new LogToStdout());
outputPath = cmd.get<string>("output"); outputPath = cmd.get<string>("output") != "no" ? cmd.get<string>("output") : "";
if (!cmd.get<string>("fps").empty()) if (!cmd.get<string>("fps").empty())
outputFps = cmd.get<double>("fps"); outputFps = cmd.get<double>("fps");
quietMode = cmd.get<bool>("quiet"); quietMode = cmd.get<bool>("quiet");
// run video processing stabilizedFrames = dynamic_cast<IFrameSource*>(stabilizer);
run(); run();
} }
catch (const exception &e) catch (const exception &e)
{ {
cout << "error: " << e.what() << endl; cout << "error: " << e.what() << endl;
stabilizer.release(); stabilizedFrames.release();
return -1; return -1;
} }
stabilizer.release(); stabilizedFrames.release();
return 0; return 0;
} }