OpenCV4入门161:视频稳像

索引地址:系列索引

视频稳定(简称稳像),是指利用相关的算法,对视频设备采集的原始视频序列进行处理,去除其中的抖动。视频稳像的目的,一方面是为了让人眼观感舒适,有利于人工观测、判别等,另一方面也作为诸多其他后续处理的预处理阶段,如检测、跟踪和压缩。

稳像按作用机制分为光学、机械和电子稳像。

  • 光学稳像通过主动光学部件自适应调整光路,补偿由于摄像平台抖动造成的图像运动,达到稳定图像的的目的;
  • 机械稳像通过陀螺传感器等器件检测摄像平台的抖动,然后对伺服系统进行调整而达到稳定图像的目的;
  • 电子(数字)稳像基于在连续视频图像之间进行运动估计,然后对视频中的每一帧图像进行运动滤波和运动补偿处理得到稳定的图像。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#include <string>
#include <iostream>
#include <opencv2/opencv.hpp>
#include "opencv2/videostab.hpp"

using namespace std;
using namespace cv;
using namespace cv::videostab;

void processing(Ptr<IFrameSource> stabilizedFrames, string outputPath);

int main(int argc, const char **argv)
{
Ptr<IFrameSource> stabilizedFrames;
try
{
// 1-Prepare the input video and check it
string inputPath;
string outputPath;
if (argc > 1)
inputPath = argv[1];
else
inputPath = ".\\cube4.avi";

if (argc > 2)
outputPath = argv[2];
else
outputPath = ".\\cube4_stabilized.avi";

Ptr<VideoFileSource> source = makePtr<VideoFileSource>(inputPath);
cout << "frame count (rough): " << source->count() << endl;


// 2-Prepare the motion estimator
// first, prepare the motion the estimation builder, RANSAC L2
double min_inlier_ratio = 0.1;
Ptr<MotionEstimatorRansacL2> est = makePtr<MotionEstimatorRansacL2>(MM_AFFINE);
RansacParams ransac = est->ransacParams();
ransac.size = 3;
ransac.thresh = 5;
ransac.eps = 0.5;
est->setRansacParams(ransac);
est->setMinInlierRatio(min_inlier_ratio);

// second, create a feature detector
int nkps = 1000;
Ptr<GoodFeaturesToTrackDetector> feature_detector = makePtr<GoodFeaturesToTrackDetector>(nkps);

// third, create the motion estimator
Ptr<KeypointBasedMotionEstimator> motionEstBuilder = makePtr<KeypointBasedMotionEstimator>(est);
motionEstBuilder->setDetector(feature_detector);
Ptr<IOutlierRejector> outlierRejector = makePtr<NullOutlierRejector>();
motionEstBuilder->setOutlierRejector(outlierRejector);


// 3-Prepare the stabilizer
StabilizerBase *stabilizer = 0;

// first, prepare the one or two pass stabilizer
bool isTwoPass = 1;
int radius_pass = 15;
if (isTwoPass)
{
// with a two pass stabilizer
bool est_trim = true;

TwoPassStabilizer *twoPassStabilizer = new TwoPassStabilizer();
twoPassStabilizer->setEstimateTrimRatio(est_trim);
twoPassStabilizer->setMotionStabilizer(makePtr<GaussianMotionFilter>(radius_pass));

stabilizer = twoPassStabilizer;
}
else
{
// with an one pass stabilizer
OnePassStabilizer *onePassStabilizer = new OnePassStabilizer();
onePassStabilizer->setMotionFilter(makePtr<GaussianMotionFilter>(radius_pass));

stabilizer = onePassStabilizer;
}

// second, set up the parameters
int radius = 15;
double trim_ratio = 0.1;
bool incl_constr = false;
stabilizer->setFrameSource(source);
stabilizer->setMotionEstimator(motionEstBuilder);
stabilizer->setRadius(radius);
stabilizer->setTrimRatio(trim_ratio);
stabilizer->setCorrectionForInclusion(incl_constr);
stabilizer->setBorderMode(BORDER_REPLICATE);


// cast stabilizer to simple frame source interface to read stabilized frames
stabilizedFrames.reset(dynamic_cast<IFrameSource*>(stabilizer));


// 4-Processing the stabilized frames. The results are showed and saved.
processing(stabilizedFrames, outputPath);
}
catch (const exception &e)
{
cout << "error: " << e.what() << endl;
stabilizedFrames.release();
return -1;
}
stabilizedFrames.release();
return 0;
}


void processing(Ptr<IFrameSource> stabilizedFrames, string outputPath)
{
VideoWriter writer;
Mat stabilizedFrame;
int nframes = 0;
double outputFps = 25;

// for each stabilized frame
while (!(stabilizedFrame = stabilizedFrames->nextFrame()).empty())
{
nframes++;

// init writer (once) and save stabilized frame
if (!outputPath.empty())
{
if (!writer.isOpened())
writer.open(outputPath, VideoWriter::fourcc('X','V','I','D'),
outputFps, stabilizedFrame.size());
writer << stabilizedFrame;
}

imshow("stabilizedFrame", stabilizedFrame);
char key = static_cast<char>(waitKey(3));
if (key == 27)
{
cout << endl;
break;
}

}
cout << "processed frames: " << nframes << endl;
cout << "finished " << endl;
}