6
#include "opencv2/core.hpp"
7
#include <opencv2/core/utility.hpp>
8
#include "opencv2/video.hpp"
9
#include "opencv2/imgproc.hpp"
10
#include "opencv2/videoio.hpp"
11
#include "opencv2/highgui.hpp"
12
#include "opencv2/videostab.hpp"
13
#include "opencv2/opencv_modules.hpp"
15
#define arg(name) cmd.get<string>(name)
16
#define argb(name) cmd.get<bool>(name)
17
#define argi(name) cmd.get<int>(name)
18
#define argf(name) cmd.get<float>(name)
19
#define argd(name) cmd.get<double>(name)
23
using namespace cv::videostab;
25
Ptr<IFrameSource> stabilizedFrames;
26
string saveMotionsPath;
32
void saveMotionsIfNecessary();
34
MotionModel motionModel(const string &str);
43
// for each stabilized frame
44
while (!(stabilizedFrame = stabilizedFrames->nextFrame()).empty())
48
// init writer (once) and save stabilized frame
49
if (!outputPath.empty())
51
if (!writer.isOpened())
52
writer.open(outputPath, VideoWriter::fourcc('X','V','I','D'),
53
outputFps, stabilizedFrame.size());
54
writer << stabilizedFrame;
57
// show stabilized frame
60
imshow("stabilizedFrame", stabilizedFrame);
61
char key = static_cast<char>(waitKey(3));
62
if (key == 27) { cout << endl; break; }
66
cout << "processed frames: " << nframes << endl
73
cout << "OpenCV video stabilizer.\n"
74
"Usage: videostab <file_path> [arguments]\n\n"
76
" -m=, --model=(transl|transl_and_scale|rigid|similarity|affine|homography)\n"
77
" Set motion model. The default is affine.\n"
78
" -lp=, --lin-prog-motion-est=(yes|no)\n"
79
" Turn on/off LP based motion estimation. The default is no.\n"
80
" --subset=(<int_number>|auto)\n"
81
" Number of random samples per one motion hypothesis. The default is auto.\n"
82
" --thresh=(<float_number>|auto)\n"
83
" Maximum error to classify match as inlier. The default is auto.\n"
84
" --outlier-ratio=<float_number>\n"
85
" Motion estimation outlier ratio hypothesis. The default is 0.5.\n"
86
" --min-inlier-ratio=<float_number>\n"
87
" Minimum inlier ratio to decide if estimated motion is OK. The default is 0.1.\n"
88
" --nkps=<int_number>\n"
89
" Number of keypoints to find in each frame. The default is 1000.\n"
90
" --local-outlier-rejection=(yes|no)\n"
91
" Perform local outlier rejection. The default is no.\n\n"
92
" -sm=, --save-motions=(<file_path>|no)\n"
93
" Save estimated motions into file. The default is no.\n"
94
" -lm=, --load-motions=(<file_path>|no)\n"
95
" Load motions from file. The default is no.\n\n"
96
" -r=, --radius=<int_number>\n"
97
" Set sliding window radius. The default is 15.\n"
98
" --stdev=(<float_number>|auto)\n"
99
" Set smoothing weights standard deviation. The default is auto\n"
100
" (i.e. sqrt(radius)).\n"
101
" -lps=, --lin-prog-stab=(yes|no)\n"
102
" Turn on/off linear programming based stabilization method.\n"
103
" --lps-trim-ratio=(<float_number>|auto)\n"
104
" Trimming ratio used in linear programming based method.\n"
105
" --lps-w1=(<float_number>|1)\n"
106
" 1st derivative weight. The default is 1.\n"
107
" --lps-w2=(<float_number>|10)\n"
108
" 2nd derivative weight. The default is 10.\n"
109
" --lps-w3=(<float_number>|100)\n"
110
" 3rd derivative weight. The default is 100.\n"
111
" --lps-w4=(<float_number>|100)\n"
112
" Non-translation motion components weight. The default is 100.\n\n"
113
" --deblur=(yes|no)\n"
115
" --deblur-sens=<float_number>\n"
116
" Set deblurring sensitivity (from 0 to +inf). The default is 0.1.\n\n"
117
" -t=, --trim-ratio=<float_number>\n"
118
" Set trimming ratio (from 0 to 0.5). The default is 0.1.\n"
119
" -et=, --est-trim=(yes|no)\n"
120
" Estimate trim ratio automatically. The default is yes.\n"
121
" -ic=, --incl-constr=(yes|no)\n"
122
" Ensure the inclusion constraint is always satisfied. The default is no.\n\n"
123
" -bm=, --border-mode=(replicate|reflect|const)\n"
124
" Set border extrapolation mode. The default is replicate.\n\n"
125
" --mosaic=(yes|no)\n"
126
" Do consistent mosaicing. The default is no.\n"
127
" --mosaic-stdev=<float_number>\n"
128
" Consistent mosaicing stdev threshold. The default is 10.0.\n\n"
129
" -mi=, --motion-inpaint=(yes|no)\n"
130
" Do motion inpainting (requires CUDA support). The default is no.\n"
131
" --mi-dist-thresh=<float_number>\n"
132
" Estimated flow distance threshold for motion inpainting. The default is 5.0.\n\n"
133
" -ci=, --color-inpaint=(no|average|ns|telea)\n"
134
" Do color inpainting. The defailt is no.\n"
135
" --ci-radius=<float_number>\n"
136
" Set color inpainting radius (for ns and telea options only).\n"
137
" The default is 2.0\n\n"
138
" -ws=, --wobble-suppress=(yes|no)\n"
139
" Perform wobble suppression. The default is no.\n"
140
" --ws-lp=(yes|no)\n"
141
" Turn on/off LP based motion estimation. The default is no.\n"
142
" --ws-period=<int_number>\n"
143
" Set wobble suppression period. The default is 30.\n"
144
" --ws-model=(transl|transl_and_scale|rigid|similarity|affine|homography)\n"
145
" Set wobble suppression motion model (must have more DOF than motion \n"
146
" estimation model). The default is homography.\n"
147
" --ws-subset=(<int_number>|auto)\n"
148
" Number of random samples per one motion hypothesis. The default is auto.\n"
149
" --ws-thresh=(<float_number>|auto)\n"
150
" Maximum error to classify match as inlier. The default is auto.\n"
151
" --ws-outlier-ratio=<float_number>\n"
152
" Motion estimation outlier ratio hypothesis. The default is 0.5.\n"
153
" --ws-min-inlier-ratio=<float_number>\n"
154
" Minimum inlier ratio to decide if estimated motion is OK. The default is 0.1.\n"
155
" --ws-nkps=<int_number>\n"
156
" Number of keypoints to find in each frame. The default is 1000.\n"
157
" --ws-local-outlier-rejection=(yes|no)\n"
158
" Perform local outlier rejection. The default is no.\n\n"
159
" -sm2=, --save-motions2=(<file_path>|no)\n"
160
" Save motions estimated for wobble suppression. The default is no.\n"
161
" -lm2=, --load-motions2=(<file_path>|no)\n"
162
" Load motions for wobble suppression from file. The default is no.\n\n"
164
" Use CUDA optimization whenever possible. The default is no.\n\n"
165
" -o=, --output=(no|<file_path>)\n"
166
" Set output file path explicitely. The default is stabilized.avi.\n"
167
" --fps=(<float_number>|auto)\n"
168
" Set output video FPS explicitely. By default the source FPS is used (auto).\n"
170
" Don't show output video frames.\n\n"
173
"Note: some argument configurations lead to two passes, some to single pass.\n\n";
176
// motion estimator builders are for concise creation of motion estimators
178
class IMotionEstimatorBuilder
181
virtual ~IMotionEstimatorBuilder() {}
182
virtual Ptr<ImageMotionEstimatorBase> build() = 0;
184
IMotionEstimatorBuilder(CommandLineParser &command) : cmd(command) {}
185
CommandLineParser cmd;
189
class MotionEstimatorRansacL2Builder : public IMotionEstimatorBuilder
192
MotionEstimatorRansacL2Builder(CommandLineParser &command, bool use_gpu, const string &_prefix = "")
193
: IMotionEstimatorBuilder(command), gpu(use_gpu), prefix(_prefix) {}
195
virtual Ptr<ImageMotionEstimatorBase> build()
197
Ptr<MotionEstimatorRansacL2> est = makePtr<MotionEstimatorRansacL2>(motionModel(arg(prefix + "model")));
199
RansacParams ransac = est->ransacParams();
200
if (arg(prefix + "subset") != "auto")
201
ransac.size = argi(prefix + "subset");
202
if (arg(prefix + "thresh") != "auto")
203
ransac.thresh = argf(prefix + "thresh");
204
ransac.eps = argf(prefix + "outlier-ratio");
205
est->setRansacParams(ransac);
207
est->setMinInlierRatio(argf(prefix + "min-inlier-ratio"));
209
Ptr<IOutlierRejector> outlierRejector = makePtr<NullOutlierRejector>();
210
if (arg(prefix + "local-outlier-rejection") == "yes")
212
Ptr<TranslationBasedLocalOutlierRejector> tblor = makePtr<TranslationBasedLocalOutlierRejector>();
213
RansacParams ransacParams = tblor->ransacParams();
214
if (arg(prefix + "thresh") != "auto")
215
ransacParams.thresh = argf(prefix + "thresh");
216
tblor->setRansacParams(ransacParams);
217
outlierRejector = tblor;
220
#if defined(HAVE_OPENCV_CUDAIMGPROC) && defined(HAVE_OPENCV_CUDAOPTFLOW)
223
Ptr<KeypointBasedMotionEstimatorGpu> kbest = makePtr<KeypointBasedMotionEstimatorGpu>(est);
224
kbest->setOutlierRejector(outlierRejector);
229
Ptr<KeypointBasedMotionEstimator> kbest = makePtr<KeypointBasedMotionEstimator>(est);
230
kbest->setDetector(GFTTDetector::create(argi(prefix + "nkps")));
231
kbest->setOutlierRejector(outlierRejector);
240
class MotionEstimatorL1Builder : public IMotionEstimatorBuilder
243
MotionEstimatorL1Builder(CommandLineParser &command, bool use_gpu, const string &_prefix = "")
244
: IMotionEstimatorBuilder(command), gpu(use_gpu), prefix(_prefix) {}
246
virtual Ptr<ImageMotionEstimatorBase> build()
248
Ptr<MotionEstimatorL1> est = makePtr<MotionEstimatorL1>(motionModel(arg(prefix + "model")));
250
Ptr<IOutlierRejector> outlierRejector = makePtr<NullOutlierRejector>();
251
if (arg(prefix + "local-outlier-rejection") == "yes")
253
Ptr<TranslationBasedLocalOutlierRejector> tblor = makePtr<TranslationBasedLocalOutlierRejector>();
254
RansacParams ransacParams = tblor->ransacParams();
255
if (arg(prefix + "thresh") != "auto")
256
ransacParams.thresh = argf(prefix + "thresh");
257
tblor->setRansacParams(ransacParams);
258
outlierRejector = tblor;
261
#if defined(HAVE_OPENCV_CUDAIMGPROC) && defined(HAVE_OPENCV_CUDAOPTFLOW)
264
Ptr<KeypointBasedMotionEstimatorGpu> kbest = makePtr<KeypointBasedMotionEstimatorGpu>(est);
265
kbest->setOutlierRejector(outlierRejector);
270
Ptr<KeypointBasedMotionEstimator> kbest = makePtr<KeypointBasedMotionEstimator>(est);
271
kbest->setDetector(GFTTDetector::create(argi(prefix + "nkps")));
272
kbest->setOutlierRejector(outlierRejector);
281
int main(int argc, const char **argv)
287
"{ m model | affine | }"
288
"{ lp lin-prog-motion-est | no | }"
289
"{ subset | auto | }"
290
"{ thresh | auto | }"
291
"{ outlier-ratio | 0.5 | }"
292
"{ min-inlier-ratio | 0.1 | }"
294
"{ extra-kps | 0 | }"
295
"{ local-outlier-rejection | no | }"
296
"{ sm save-motions | no | }"
297
"{ lm load-motions | no | }"
298
"{ r radius | 15 | }"
300
"{ lps lin-prog-stab | no | }"
301
"{ lps-trim-ratio | auto | }"
307
"{ deblur-sens | 0.1 | }"
308
"{ et est-trim | yes | }"
309
"{ t trim-ratio | 0.1 | }"
310
"{ ic incl-constr | no | }"
311
"{ bm border-mode | replicate | }"
313
"{ ms mosaic-stdev | 10.0 | }"
314
"{ mi motion-inpaint | no | }"
315
"{ mi-dist-thresh | 5.0 | }"
316
"{ ci color-inpaint | no | }"
317
"{ ci-radius | 2 | }"
318
"{ ws wobble-suppress | no | }"
319
"{ ws-period | 30 | }"
320
"{ ws-model | homography | }"
321
"{ ws-subset | auto | }"
322
"{ ws-thresh | auto | }"
323
"{ ws-outlier-ratio | 0.5 | }"
324
"{ ws-min-inlier-ratio | 0.1 | }"
325
"{ ws-nkps | 1000 | }"
326
"{ ws-extra-kps | 0 | }"
327
"{ ws-local-outlier-rejection | no | }"
329
"{ sm2 save-motions2 | no | }"
330
"{ lm2 load-motions2 | no | }"
332
"{ o output | stabilized.avi | }"
336
CommandLineParser cmd(argc, argv, keys);
338
// parse command arguments
346
if (arg("gpu") == "yes")
348
cout << "initializing GPU..."; cout.flush();
349
Mat hostTmp = Mat::zeros(1, 1, CV_32F);
350
cuda::GpuMat deviceTmp;
351
deviceTmp.upload(hostTmp);
355
StabilizerBase *stabilizer = 0;
357
// check if source video is specified
359
string inputPath = arg(0);
360
if (inputPath.empty())
361
throw runtime_error("specify video file path");
363
// get source video parameters
365
Ptr<VideoFileSource> source = makePtr<VideoFileSource>(inputPath);
366
cout << "frame count (rough): " << source->count() << endl;
367
if (arg("fps") == "auto")
368
outputFps = source->fps();
370
outputFps = argd("fps");
372
// prepare motion estimation builders
374
Ptr<IMotionEstimatorBuilder> motionEstBuilder;
375
if (arg("lin-prog-motion-est") == "yes")
376
motionEstBuilder.reset(new MotionEstimatorL1Builder(cmd, arg("gpu") == "yes"));
378
motionEstBuilder.reset(new MotionEstimatorRansacL2Builder(cmd, arg("gpu") == "yes"));
380
Ptr<IMotionEstimatorBuilder> wsMotionEstBuilder;
381
if (arg("ws-lp") == "yes")
382
wsMotionEstBuilder.reset(new MotionEstimatorL1Builder(cmd, arg("gpu") == "yes", "ws-"));
384
wsMotionEstBuilder.reset(new MotionEstimatorRansacL2Builder(cmd, arg("gpu") == "yes", "ws-"));
386
// determine whether we must use one pass or two pass stabilizer
388
arg("est-trim") == "yes" || arg("wobble-suppress") == "yes" || arg("lin-prog-stab") == "yes";
392
// we must use two pass stabilizer
394
TwoPassStabilizer *twoPassStabilizer = new TwoPassStabilizer();
395
stabilizer = twoPassStabilizer;
396
twoPassStabilizer->setEstimateTrimRatio(arg("est-trim") == "yes");
398
// determine stabilization technique
400
if (arg("lin-prog-stab") == "yes")
402
Ptr<LpMotionStabilizer> stab = makePtr<LpMotionStabilizer>();
403
stab->setFrameSize(Size(source->width(), source->height()));
404
stab->setTrimRatio(arg("lps-trim-ratio") == "auto" ? argf("trim-ratio") : argf("lps-trim-ratio"));
405
stab->setWeight1(argf("lps-w1"));
406
stab->setWeight2(argf("lps-w2"));
407
stab->setWeight3(argf("lps-w3"));
408
stab->setWeight4(argf("lps-w4"));
409
twoPassStabilizer->setMotionStabilizer(stab);
411
else if (arg("stdev") == "auto")
412
twoPassStabilizer->setMotionStabilizer(makePtr<GaussianMotionFilter>(argi("radius")));
414
twoPassStabilizer->setMotionStabilizer(makePtr<GaussianMotionFilter>(argi("radius"), argf("stdev")));
416
// init wobble suppressor if necessary
418
if (arg("wobble-suppress") == "yes")
420
Ptr<MoreAccurateMotionWobbleSuppressorBase> ws = makePtr<MoreAccurateMotionWobbleSuppressor>();
421
if (arg("gpu") == "yes")
422
#ifdef HAVE_OPENCV_CUDAWARPING
423
ws = makePtr<MoreAccurateMotionWobbleSuppressorGpu>();
425
throw runtime_error("OpenCV is built without CUDA support");
428
ws->setMotionEstimator(wsMotionEstBuilder->build());
429
ws->setPeriod(argi("ws-period"));
430
twoPassStabilizer->setWobbleSuppressor(ws);
432
MotionModel model = ws->motionEstimator()->motionModel();
433
if (arg("load-motions2") != "no")
435
ws->setMotionEstimator(makePtr<FromFileMotionReader>(arg("load-motions2")));
436
ws->motionEstimator()->setMotionModel(model);
438
if (arg("save-motions2") != "no")
440
ws->setMotionEstimator(makePtr<ToFileMotionWriter>(arg("save-motions2"), ws->motionEstimator()));
441
ws->motionEstimator()->setMotionModel(model);
447
// we must use one pass stabilizer
449
OnePassStabilizer *onePassStabilizer = new OnePassStabilizer();
450
stabilizer = onePassStabilizer;
451
if (arg("stdev") == "auto")
452
onePassStabilizer->setMotionFilter(makePtr<GaussianMotionFilter>(argi("radius")));
454
onePassStabilizer->setMotionFilter(makePtr<GaussianMotionFilter>(argi("radius"), argf("stdev")));
457
stabilizer->setFrameSource(source);
458
stabilizer->setMotionEstimator(motionEstBuilder->build());
460
// cast stabilizer to simple frame source interface to read stabilized frames
461
stabilizedFrames.reset(dynamic_cast<IFrameSource*>(stabilizer));
463
MotionModel model = stabilizer->motionEstimator()->motionModel();
464
if (arg("load-motions") != "no")
466
stabilizer->setMotionEstimator(makePtr<FromFileMotionReader>(arg("load-motions")));
467
stabilizer->motionEstimator()->setMotionModel(model);
469
if (arg("save-motions") != "no")
471
stabilizer->setMotionEstimator(makePtr<ToFileMotionWriter>(arg("save-motions"), stabilizer->motionEstimator()));
472
stabilizer->motionEstimator()->setMotionModel(model);
475
stabilizer->setRadius(argi("radius"));
478
if (arg("deblur") == "yes")
480
Ptr<WeightingDeblurer> deblurer = makePtr<WeightingDeblurer>();
481
deblurer->setRadius(argi("radius"));
482
deblurer->setSensitivity(argf("deblur-sens"));
483
stabilizer->setDeblurer(deblurer);
486
// set up trimming paramters
487
stabilizer->setTrimRatio(argf("trim-ratio"));
488
stabilizer->setCorrectionForInclusion(arg("incl-constr") == "yes");
490
if (arg("border-mode") == "reflect")
491
stabilizer->setBorderMode(BORDER_REFLECT);
492
else if (arg("border-mode") == "replicate")
493
stabilizer->setBorderMode(BORDER_REPLICATE);
494
else if (arg("border-mode") == "const")
495
stabilizer->setBorderMode(BORDER_CONSTANT);
497
throw runtime_error("unknown border extrapolation mode: "
498
+ cmd.get<string>("border-mode"));
501
InpaintingPipeline *inpainters = new InpaintingPipeline();
502
Ptr<InpainterBase> inpainters_(inpainters);
503
if (arg("mosaic") == "yes")
505
Ptr<ConsistentMosaicInpainter> inp = makePtr<ConsistentMosaicInpainter>();
506
inp->setStdevThresh(argf("mosaic-stdev"));
507
inpainters->pushBack(inp);
509
if (arg("motion-inpaint") == "yes")
511
Ptr<MotionInpainter> inp = makePtr<MotionInpainter>();
512
inp->setDistThreshold(argf("mi-dist-thresh"));
513
inpainters->pushBack(inp);
515
if (arg("color-inpaint") == "average")
516
inpainters->pushBack(makePtr<ColorAverageInpainter>());
517
else if (arg("color-inpaint") == "ns")
518
inpainters->pushBack(makePtr<ColorInpainter>(int(INPAINT_NS), argd("ci-radius")));
519
else if (arg("color-inpaint") == "telea")
520
inpainters->pushBack(makePtr<ColorInpainter>(int(INPAINT_TELEA), argd("ci-radius")));
521
else if (arg("color-inpaint") != "no")
522
throw runtime_error("unknown color inpainting method: " + arg("color-inpaint"));
523
if (!inpainters->empty())
525
inpainters->setRadius(argi("radius"));
526
stabilizer->setInpainter(inpainters_);
529
if (arg("output") != "no")
530
outputPath = arg("output");
532
quietMode = argb("quiet");
536
catch (const exception &e)
538
cout << "error: " << e.what() << endl;
539
stabilizedFrames.release();
542
stabilizedFrames.release();
547
MotionModel motionModel(const string &str)
550
return MM_TRANSLATION;
551
if (str == "transl_and_scale")
552
return MM_TRANSLATION_AND_SCALE;
555
if (str == "similarity")
556
return MM_SIMILARITY;
559
if (str == "homography")
560
return MM_HOMOGRAPHY;
561
throw runtime_error("unknown motion model: " + str);