当前位置:首页>python>【防抖实战】--Python复刻了Gyroflow的视频防抖Pipeline

【防抖实战】--Python复刻了Gyroflow的视频防抖Pipeline

  • 2026-10-11 07:35:49
【防抖实战】--Python复刻了Gyroflow的视频防抖Pipeline

本文系微信公众号《MediaStack》原创文章,每周不定期更新,欢迎大家关注,随时进行交流。

起因:一个求助引发的2周左右的尝试
有人在后台里问:用大疆的设备采集视频导出陀螺仪数据尝试防抖算法,经过很多次操作后,最后视频还是很抖??
当时想的是,防抖嘛,不就是读陀螺仪数据、做四元数平滑、算个remap map、用OpenCV重映射?能有多难。
2周之后,我看着目录下二十多个Python文件和上百个测试视频,意识到这个"不难"的初判断让自己又学到不少东西。

最初版:能跑,但画面在抖

最早的版本gyro_eis.py,132行的代码里做了以下几件事:
  1. 输入: 原始视频 + 同步的陀螺仪数据(CSV)。
  2. 生成参考系: 把抖动的镜头轨迹“磨平”,得到一个理想的平滑轨迹。
  3. 计算修正量: 每一帧画面的修正量 = 理想位置 - 实际位置。
  4. 图像变形: 根据修正量旋转图片。
  5. 输出: 剪裁掉边缘黑边后的平滑视频。
也提供了一个视频,但是视频是从Gyroflow导出来的,并没有包含imu等数据(这个是后来分析一下视频才知道的,也浪费了一点时间来验证)。
当时卡住的第一个点是时间对齐。DJI Osmo Nano的陀螺仪采样率约1000Hz,视频帧率30fps,理论上每帧对应~33个IMU采样点。
但实际跑出来的画面有明显的水平抖动——不是算法问题,是帧中心时间戳和实际曝光时间有约22ms的偏移。
修了这个偏移之后,增加了一些时间同步之后,画面稳定了不少,但还不够好。

第二版:加了滚动快门,效果反而更差

第二个版本开始引入滚动快门校正。方案是分条带(strip)——把一帧画面分成N个水平条带,每个条带查询该行实际读取时刻对应的陀螺仪姿态,分别做变换。

条带数从16开始试。16条带的效果比第一版好,但画面有明显的水平条纹感——相邻条带之间不连续。

加到36条带,条纹感消失。

加到108条带(每10行一个条带),终于和Gyroflow的效果接近了。

但这时候出了另一个问题:四元数公式的符号。于是参考Gyroflow源码里frame_transform.rs的校正公式是:
smoothed_q * inv(org_q_center) * org_q_row,
而我们第一版写的是inv(stab_q) * org_q。看起来差不多,但坐标系不对——Gyroflow的旋转矩阵有一个flip_y() 操作,要把R的特定行列取反
(R[0,1]*=-1; R[0,2]*=-1; R[1,0]*=-1; R[2,0]*=-1)。
现在反思一下:代码还是需要细嚼慢咽,慢慢理解才行呀。
这个符号错误导致画面在俯仰方向是反的。表现为:镜头往下,画面往上;快速倾斜时画面像被"弹"了一下。
修了flip_y之后,108条带+正确的四元数公式,防抖效果终于说得过去了。但和Gyroflow的输出做帧对帧对比(写了一个脚本evaluate.py来计算MSE),差异还在5-8%左右。

第三版:把Gyroflow的渲染管线翻译过来

剩下的 5-8% 差异来自哪里?重新对比了一下Gyroflow源码的GPU渲染管线(stabilize.rs+lens.rs),发现两个之前完全忽略的东西。
鱼眼畸变的前向-逆向流程
之前的版本用cv2.fisheye.initUndistortRectifyMap一步到位,然而在Gyroflow的流程是三步:

把输出像素通过内参矩阵K逆投影到3D空间

应用旋转变换

用Newton法迭代求解鱼眼前向畸变模型,再乘以内参得到源像素坐标

关键区别在于:OpenCV的initUndistortRectifyMap假设了特定的畸变模型参数化方式,而Gyroflow用的是多段式多项式拟合(poly3/poly5/PTLens 等多种模型),前向畸变用 Newton迭代求解,精度更高,于是继续迭代。
最新版gyroflow_python.py有881多行(含批量处理和完整注释),实现了Gyroflow渲染pipeline的Python实现:

四元数运算(乘法、逆、SLERP、旋转矩阵)

Gyroflow Default平滑算法(速度自适应、双向滤波)

水平锁定(Horizon Lock)

逐行滚动快门校正

多模型鱼眼畸变(前向 + 逆向)

自适应FOV(内接矩形 + 包络跟随)

批量矩阵计算

这时候效果就可以接受了。

当然这个结果也不是最好的,还有很多可以继续优化的地方,待我有时间时候,继续迭代,做个通用版本。

好了本文集就介绍到这里了。


我是一枚爱跑步的程序猿,维护公众号和知乎专栏《MediaStack》,有兴趣可以关注,一起学习音视频知识,时不时分享实战经验。

最新文章

随机文章