C++-OpenCV(6)-视频稳态
对于一个出现抖动的视频,我们如何处理它让它不抖动呢?

关键步骤:
1.计算相邻帧的变换
获取前一帧图像中的特征点: goodFeaturesToTrack()
获得下一帧图像中的特征点位置:calcOpticalFlowPyrLK()
计算两帧间的变换: estimateAffinePartial2D() dx+dy+旋转角度
2*3的矩阵 第1列和第2列是旋转和缩放 可以求出旋转角度
第3列 是X 方向上和y方向上的平移,可以得到dx dy 以及平移的角度 每个平移间的角度都存起来,相邻帧之间的变换。
2.计算相邻帧之间的运行轨迹
特征点的运动轨迹:cumsum
帧间的位移和旋转角度累积起来
视频平滑:smooth;计算方法: 滑动平均 累加/count
滑动窗口大小:radius
重新计算:transforms_smooth=smoothed_trajectory-trajectory
3. wapAffine 重新生成下一帧图像
代码如下:
//输入视频序列帧
VideoCapture cap(0);
int n_frames = int(cap.get(CAP_PROP_FRAME_COUNT));//宽高和频率
int w = int(cap.get(CAP_PROP_FRAME_WIDTH));
int h = int(cap.get(CAP_PROP_FRAME_HEIGHT));
double fps = cap.get(CAP_PROP_FPS);
//输出视频
string outfile = "video_out.avi";
VideoWriter out("video_out.avi", VideoWriter::fourcc('M', 'J', 'P', 'G'), fps, Size(2 * w, h));
//前一帧和下一帧,且为灰度图
Mat curr, curr_gray;
Mat prev, prev_gray;
cap >> prev;
cvtColor(prev, prev_gray, COLOR_BGR2GRAY);
//1.寻找特征点并计算相邻帧的变换 相邻帧
vector <TransformParam> transforms;
Mat last_T;
for (int i = 1; i < n_frames - 1; i++)
{
//前一帧的点和当前帧的点
vector <Point2f> prev_pts, curr_pts;
//特征点在前一帧的位置
goodFeaturesToTrack(prev_gray, prev_pts, 200, 0.01, 30);
//读取当前帧
bool success = cap.read(curr);
if (!success) break;
cvtColor(curr, curr_gray, COLOR_BGR2GRAY);
vector <uchar> status;
vector <float> err;
//输入前一帧、当前帧、特征点
calcOpticalFlowPyrLK(prev_gray, curr_gray, prev_pts, curr_pts, status, err);
// 过滤点
auto prev_it = prev_pts.begin();
auto curr_it = curr_pts.begin();
for (size_t k = 0; k < status.size(); k++)
{
if (status[k])
{
prev_it++;
curr_it++;
}
else
{
prev_it = prev_pts.erase(prev_it);
curr_it = curr_pts.erase(curr_it);
}
}
// 计算两帧之间的变化 dx+dy+旋转角度
Mat T = estimateAffinePartial2D(prev_pts, curr_pts);
if (T.data == NULL) last_T.copyTo(T);
T.copyTo(last_T);
// x y的偏移量 第3列
double dx = T.at<double>(0, 2);
double dy = T.at<double>(1, 2);
//根据第0 1列求 旋转角度 Extract rotation angle
double da = atan2(T.at<double>(1, 0), T.at<double>(0, 0));
// Store transformation
transforms.push_back(TransformParam(dx, dy, da));
// Move to next frame
curr_gray.copyTo(prev_gray);
cout << "Frame: " << i << "/" << n_frames << " - Tracked points : " << prev_pts.size() << endl;
}
// 2.计算运动轨迹:存的transforms_smooth=平滑后(trajectory)-平滑前(trajectory)
vector <Trajectory> trajectory = cumsum(transforms);
vector <Trajectory> smoothed_trajectory = smooth(trajectory, SMOOTHING_RADIUS);
vector <TransformParam> transforms_smooth;
for (size_t i = 0; i < transforms.size(); i++)
{
//
double diff_x = smoothed_trajectory[i].x - trajectory[i].x;
double diff_y = smoothed_trajectory[i].y - trajectory[i].y;
double diff_a = smoothed_trajectory[i].a - trajectory[i].a;
// Calculate newer transformation array
double dx = transforms[i].dx + diff_x;
double dy = transforms[i].dy + diff_y;
double da = transforms[i].da + diff_a;
transforms_smooth.push_back(TransformParam(dx, dy, da));
}
//3.生成新的图像
cap.set(CAP_PROP_POS_FRAMES, 0);
Mat T(2, 3, CV_64F);
Mat frame, frame_stabilized, frame_out;
for (int i = 0; i < n_frames - 1; i++)
{
bool success = cap.read(frame);
if (!success) break;
// Extract transform from translation and rotation angle.
transforms_smooth[i].getTransform(T);
// Apply affine wrapping to the given frame
warpAffine(frame, frame_stabilized, T, frame.size());
// Scale image to remove black border artifact
fixBorder(frame_stabilized);
// Now draw the original and stablised side by side for coolness
hconcat(frame, frame_stabilized, frame_out);
// If the image is too big, resize it.
if (frame_out.cols > 1920)
{
resize(frame_out, frame_out, Size(frame_out.cols / 2, frame_out.rows / 2));
}
//imshow("Before and After", frame_out);
out.write(frame_out);
//waitKey(10);
}
// Release video
cap.release();
out.release();

浙公网安备 33010602011771号