Added LP based global motion estimation (videostab)

This commit is contained in:
Alexey Spizhevoy
2012-04-25 13:21:38 +00:00
parent 11eacb9d14
commit 71391eb76f
7 changed files with 541 additions and 166 deletions

View File

@@ -73,6 +73,8 @@ void printHelp()
"Arguments:\n"
" -m, --model=(transl|transl_and_scale|rigid|similarity|affine|homography)\n"
" Set motion model. The default is affine.\n"
" -lp, --lin-prog-motion-est=(yes|no)\n"
" Turn on/off LP based motion estimation. The default is no.\n"
" --subset=(<int_number>|auto)\n"
" Number of random samples per one motion hypothesis. The default is auto.\n"
" --thresh=(<float_number>|auto)\n"
@@ -135,6 +137,8 @@ void printHelp()
" The default is 2.0\n\n"
" -ws, --wobble-suppress=(yes|no)\n"
" Perform wobble suppression. The default is no.\n"
" --ws-lp=(yes|no)\n"
" Turn on/off LP based motion estimation. The default is no.\n"
" --ws-period=<int_number>\n"
" Set wobble suppression period. The default is 30.\n"
" --ws-model=(transl|transl_and_scale|rigid|similarity|affine|homography)\n"
@@ -171,6 +175,133 @@ void printHelp()
"Note: some argument configurations lead to two passes, some to single pass.\n\n";
}
// motion estimator builders are for concise creation of motion estimators
class IMotionEstimatorBuilder
{
public:
virtual ~IMotionEstimatorBuilder() {}
virtual Ptr<GlobalMotionEstimatorBase> build() = 0;
protected:
IMotionEstimatorBuilder(CommandLineParser &cmd) : cmd(cmd) {}
CommandLineParser cmd;
};
class RansacMotionEstimatorBuilder : public IMotionEstimatorBuilder
{
public:
RansacMotionEstimatorBuilder(CommandLineParser &cmd, const string &prefix = "")
: IMotionEstimatorBuilder(cmd), prefix(prefix) {}
virtual Ptr<GlobalMotionEstimatorBase> build()
{
RansacMotionEstimator *est = new RansacMotionEstimator(motionModel(arg(prefix + "model")));
est->setDetector(new GoodFeaturesToTrackDetector(argi(prefix + "nkps")));
RansacParams ransac = est->ransacParams();
if (arg(prefix + "subset") != "auto")
ransac.size = argi(prefix + "subset");
if (arg(prefix + "thresh") != "auto")
ransac.thresh = argi(prefix + "thresh");
ransac.eps = argf(prefix + "outlier-ratio");
est->setRansacParams(ransac);
est->setGridSize(Size(argi(prefix + "extra-kps"), argi(prefix + "extra-kps")));
Ptr<IOutlierRejector> outlierRejector = new NullOutlierRejector();
if (arg(prefix + "local-outlier-rejection") == "yes")
{
TranslationBasedLocalOutlierRejector *tor = new TranslationBasedLocalOutlierRejector();
RansacParams ransacParams = tor->ransacParams();
if (arg(prefix + "thresh") != "auto")
ransacParams.thresh = argf(prefix + "thresh");
tor->setRansacParams(ransacParams);
outlierRejector = tor;
}
est->setOutlierRejector(outlierRejector);
est->setMinInlierRatio(argf(prefix + "min-inlier-ratio"));
return est;
}
private:
string prefix;
};
#if HAVE_OPENCV_GPU
class RansacMotionEstimatorBuilderGpu : public IMotionEstimatorBuilder
{
public:
RansacMotionEstimatorBuilderGpu(CommandLineParser &cmd, const string &prefix = "")
: IMotionEstimatorBuilder(cmd), prefix(prefix) {}
virtual Ptr<GlobalMotionEstimatorBase> build()
{
RansacMotionEstimatorGpu *est = new RansacMotionEstimatorGpu(motionModel(arg(prefix + "model")));
RansacParams ransac = est->ransacParams();
if (arg(prefix + "subset") != "auto")
ransac.size = argi(prefix + "subset");
if (arg(prefix + "thresh") != "auto")
ransac.thresh = argi(prefix + "thresh");
ransac.eps = argf(prefix + "outlier-ratio");
est->setRansacParams(ransac);
Ptr<IOutlierRejector> outlierRejector = new NullOutlierRejector();
if (arg(prefix + "local-outlier-rejection") == "yes")
{
TranslationBasedLocalOutlierRejector *tor = new TranslationBasedLocalOutlierRejector();
RansacParams ransacParams = tor->ransacParams();
if (arg(prefix + "thresh") != "auto")
ransacParams.thresh = argf(prefix + "thresh");
tor->setRansacParams(ransacParams);
outlierRejector = tor;
}
est->setOutlierRejector(outlierRejector);
est->setMinInlierRatio(argf(prefix + "min-inlier-ratio"));
return est;
}
private:
string prefix;
};
#endif
class LpBasedMotionEstimatorBuilder : public IMotionEstimatorBuilder
{
public:
LpBasedMotionEstimatorBuilder(CommandLineParser &cmd, const string &prefix = "")
: IMotionEstimatorBuilder(cmd), prefix(prefix) {}
virtual Ptr<GlobalMotionEstimatorBase> build()
{
LpBasedMotionEstimator *est = new LpBasedMotionEstimator(motionModel(arg(prefix + "model")));
est->setDetector(new GoodFeaturesToTrackDetector(argi(prefix + "nkps")));
Ptr<IOutlierRejector> outlierRejector = new NullOutlierRejector();
if (arg(prefix + "local-outlier-rejection") == "yes")
{
TranslationBasedLocalOutlierRejector *tor = new TranslationBasedLocalOutlierRejector();
RansacParams ransacParams = tor->ransacParams();
if (arg(prefix + "thresh") != "auto")
ransacParams.thresh = argf(prefix + "thresh");
tor->setRansacParams(ransacParams);
outlierRejector = tor;
}
est->setOutlierRejector(outlierRejector);
return est;
}
private:
string prefix;
};
int main(int argc, const char **argv)
{
@@ -178,7 +309,8 @@ int main(int argc, const char **argv)
{
const char *keys =
"{ 1 | | | | }"
"{ m | model | affine| }"
"{ m | model | affine | }"
"{ lp | lin-prog-motion-est | no | }"
"{ | subset | auto | }"
"{ | thresh | auto | }"
"{ | outlier-ratio | 0.5 | }"
@@ -218,6 +350,7 @@ int main(int argc, const char **argv)
"{ | ws-nkps | 1000 | }"
"{ | ws-extra-kps | 0 | }"
"{ | ws-local-outlier-rejection | no | }"
"{ | ws-lp | no | }"
"{ sm2 | save-motions2 | no | }"
"{ lm2 | load-motions2 | no | }"
"{ gpu | | no }"
@@ -246,23 +379,75 @@ int main(int argc, const char **argv)
}
#endif
StabilizerBase *stabilizer = 0;
// check if source video is specified
string inputPath = arg("1");
if (inputPath.empty()) throw runtime_error("specify video file path");
if (inputPath.empty())
throw runtime_error("specify video file path");
// get source video parameters
VideoFileSource *source = new VideoFileSource(inputPath);
cout << "frame count (rough): " << source->count() << endl;
if (arg("fps") == "auto") outputFps = source->fps(); else outputFps = argd("fps");
if (arg("fps") == "auto")
outputFps = source->fps();
else
outputFps = argd("fps");
StabilizerBase *stabilizer;
// prepare motion estimation builders
Ptr<IMotionEstimatorBuilder> motionEstBuilder;
#if HAVE_OPENCV_GPU
if (arg("gpu") == "yes")
{
if (arg("lin-prog-motion-est") == "yes")
motionEstBuilder = new LpBasedMotionEstimatorBuilder(cmd);
else
motionEstBuilder = new RansacMotionEstimatorBuilderGpu(cmd);
}
else
#endif
{
if (arg("lin-prog-motion-est") == "yes")
motionEstBuilder = new LpBasedMotionEstimatorBuilder(cmd);
else
motionEstBuilder = new RansacMotionEstimatorBuilder(cmd);
}
Ptr<IMotionEstimatorBuilder> wsMotionEstBuilder;
#if HAVE_OPENCV_GPU
if (arg("gpu") == "yes")
{
if (arg("ws-lp") == "yes")
wsMotionEstBuilder = new LpBasedMotionEstimatorBuilder(cmd, "ws-");
else
wsMotionEstBuilder = new RansacMotionEstimatorBuilderGpu(cmd, "ws-");
}
else
#endif
{
if (arg("ws-lp") == "yes")
wsMotionEstBuilder = new LpBasedMotionEstimatorBuilder(cmd, "ws-");
else
wsMotionEstBuilder = new RansacMotionEstimatorBuilder(cmd, "ws-");
}
// determine whether we must use one pass or two pass stabilizer
bool isTwoPass =
arg("est-trim") == "yes" || arg("wobble-suppress") == "yes" || arg("lin-prog-stab") == "yes";
if (isTwoPass)
{
// we must use two pass stabilizer
TwoPassStabilizer *twoPassStabilizer = new TwoPassStabilizer();
stabilizer = twoPassStabilizer;
twoPassStabilizer->setEstimateTrimRatio(arg("est-trim") == "yes");
// determine stabilization technique
if (arg("lin-prog-stab") == "yes")
{
LpMotionStabilizer *stab = new LpMotionStabilizer();
@@ -278,65 +463,22 @@ int main(int argc, const char **argv)
twoPassStabilizer->setMotionStabilizer(new GaussianMotionFilter(argi("radius")));
else
twoPassStabilizer->setMotionStabilizer(new GaussianMotionFilter(argi("radius"), argf("stdev")));
// init wobble suppressor if necessary
if (arg("wobble-suppress") == "yes")
{
MoreAccurateMotionWobbleSuppressorBase *ws = 0;
Ptr<IOutlierRejector> outlierRejector = new NullOutlierRejector();
if (arg("local-outlier-rejection") == "yes")
{
TranslationBasedLocalOutlierRejector *tor = new TranslationBasedLocalOutlierRejector();
RansacParams ransacParams = tor->ransacParams();
if (arg("ws-thresh") != "auto") ransacParams.thresh = argf("ws-thresh");
tor->setRansacParams(ransacParams);
outlierRejector = tor;
}
if (arg("gpu") == "no")
{
PyrLkRobustMotionEstimator *est = new PyrLkRobustMotionEstimator(motionModel(arg("ws-model")));
est->setDetector(new GoodFeaturesToTrackDetector(argi("ws-nkps")));
RansacParams ransac = est->ransacParams();
if (arg("ws-subset") != "auto") ransac.size = argi("ws-subset");
if (arg("ws-thresh") != "auto") ransac.thresh = argi("ws-thresh");
ransac.eps = argf("ws-outlier-ratio");
est->setRansacParams(ransac);
est->setMinInlierRatio(argf("ws-min-inlier-ratio"));
est->setGridSize(Size(argi("ws-extra-kps"), argi("ws-extra-kps")));
est->setOutlierRejector(outlierRejector);
ws = new MoreAccurateMotionWobbleSuppressor();
ws->setMotionEstimator(est);
}
else if (arg("gpu") == "yes")
{
MoreAccurateMotionWobbleSuppressorBase *ws = new MoreAccurateMotionWobbleSuppressor();
if (arg("gpu") == "yes")
#if HAVE_OPENCV_GPU
PyrLkRobustMotionEstimatorGpu *est = new PyrLkRobustMotionEstimatorGpu(motionModel(arg("ws-model")));
RansacParams ransac = est->ransacParams();
if (arg("ws-subset") != "auto") ransac.size = argi("ws-subset");
if (arg("ws-thresh") != "auto") ransac.thresh = argi("ws-thresh");
ransac.eps = argf("ws-outlier-ratio");
est->setRansacParams(ransac);
est->setMinInlierRatio(argf("ws-min-inlier-ratio"));
est->setOutlierRejector(outlierRejector);
ws = new MoreAccurateMotionWobbleSuppressorGpu();
ws->setMotionEstimator(est);
#else
throw runtime_error("OpenCV is built without GPU support");
#endif
}
else
{
throw runtime_error("bad gpu optimization argument value: " + arg("gpu"));
}
twoPassStabilizer->setWobbleSuppressor(ws);
ws->setMotionEstimator(wsMotionEstBuilder->build());
ws->setPeriod(argi("ws-period"));
twoPassStabilizer->setWobbleSuppressor(ws);
MotionModel model = ws->motionEstimator()->motionModel();
if (arg("load-motions2") != "no")
@@ -353,6 +495,8 @@ int main(int argc, const char **argv)
}
else
{
// we must use one pass stabilizer
OnePassStabilizer *onePassStabilizer = new OnePassStabilizer();
stabilizer = onePassStabilizer;
if (arg("stdev") == "auto")
@@ -362,59 +506,11 @@ int main(int argc, const char **argv)
}
stabilizer->setFrameSource(source);
stabilizer->setMotionEstimator(motionEstBuilder->build());
// cast stabilizer to simple frame source interface to read stabilized frames
stabilizedFrames = dynamic_cast<IFrameSource*>(stabilizer);
Ptr<IOutlierRejector> outlierRejector = new NullOutlierRejector();
if (arg("local-outlier-rejection") == "yes")
{
TranslationBasedLocalOutlierRejector *tor = new TranslationBasedLocalOutlierRejector();
RansacParams ransacParams = tor->ransacParams();
if (arg("thresh") != "auto") ransacParams.thresh = argf("thresh");
tor->setRansacParams(ransacParams);
outlierRejector = tor;
}
if (arg("gpu") == "no")
{
PyrLkRobustMotionEstimator *est = new PyrLkRobustMotionEstimator(motionModel(arg("model")));;
est->setDetector(new GoodFeaturesToTrackDetector(argi("nkps")));
RansacParams ransac = est->ransacParams();
if (arg("subset") != "auto") ransac.size = argi("subset");
if (arg("thresh") != "auto") ransac.thresh = argi("thresh");
ransac.eps = argf("outlier-ratio");
est->setRansacParams(ransac);
est->setMinInlierRatio(argf("min-inlier-ratio"));
est->setGridSize(Size(argi("extra-kps"), argi("extra-kps")));
est->setOutlierRejector(outlierRejector);
stabilizer->setMotionEstimator(est);
}
else if (arg("gpu") == "yes")
{
#if HAVE_OPENCV_GPU
PyrLkRobustMotionEstimatorGpu *est = new PyrLkRobustMotionEstimatorGpu(motionModel(arg("model")));;
RansacParams ransac = est->ransacParams();
if (arg("subset") != "auto") ransac.size = argi("subset");
if (arg("thresh") != "auto") ransac.thresh = argi("thresh");
ransac.eps = argf("outlier-ratio");
est->setRansacParams(ransac);
est->setMinInlierRatio(argf("min-inlier-ratio"));
est->setOutlierRejector(outlierRejector);
stabilizer->setMotionEstimator(est);
#else
throw runtime_error("OpenCV is built without GPU support");
#endif
}
else
{
throw runtime_error("bad gpu optimization argument value: " + arg("gpu"));
}
MotionModel model = stabilizer->motionEstimator()->motionModel();
if (arg("load-motions") != "no")
{
@@ -428,6 +524,8 @@ int main(int argc, const char **argv)
}
stabilizer->setRadius(argi("radius"));
// init deblurer
if (arg("deblur") == "yes")
{
WeightingDeblurer *deblurer = new WeightingDeblurer();
@@ -436,6 +534,7 @@ int main(int argc, const char **argv)
stabilizer->setDeblurer(deblurer);
}
// set up trimming paramters
stabilizer->setTrimRatio(argf("trim-ratio"));
stabilizer->setCorrectionForInclusion(arg("incl-constr") == "yes");
@@ -449,6 +548,7 @@ int main(int argc, const char **argv)
throw runtime_error("unknown border extrapolation mode: "
+ cmd.get<string>("border-mode"));
// init inpainter
InpaintingPipeline *inpainters = new InpaintingPipeline();
Ptr<InpainterBase> inpainters_(inpainters);
if (arg("mosaic") == "yes")