17、Camera 视频防抖:VideoStabilizationMode、OIS/EIS区别、防抖对FOV的影响、防抖开启后的裁剪处理

视频防抖,说白了就是让画面“稳”下来。

你拿着手机边走边拍,画面晃得像地震,这时候防抖就派上用场了。我这些年调过的防抖方案,从OIS到EIS,再到两者混合,踩过的坑真不少。今天咱们就把这块彻底聊透。

17.1 防抖的两大流派:OIS 与 EIS

先搞清楚两个概念:OIS(光学防抖)和 EIS(电子防抖)。

OIS(Optical Image Stabilization),是硬件层面的。镜头模组里有个磁悬浮结构,能根据陀螺仪数据实时移动镜头,补偿手抖。好处是物理层面就稳了,不影响画质。坏处是——贵,而且只能补偿低频抖动。

EIS(Electronic Image Stabilization),是软件层面的。通过裁剪画面、分析帧间运动,用算法把画面“拉”回来。好处是成本低,所有SoC都能做。坏处是——会裁剪画面,FOV会变小。

我个人的习惯是:旗舰机上用OIS+EIS混合方案,中低端机用纯EIS。为什么?因为OIS能兜住低频抖动,EIS处理高频残留,两者互补,效果最好。

核心区别总结:

  • OIS:硬件补偿,不损失画质,不裁剪FOV,但成本高
  • EIS:软件补偿,会裁剪FOV,依赖算法,成本低
  • 混合方案:OIS兜底 + EIS精修,目前旗舰标配

17.2 VideoStabilizationMode 枚举

在Android Camera2 API里,防抖模式通过 CONTROL_VIDEO_STABILIZATION_MODE 控制。这个枚举值不多,但每个都有讲究。

模式 说明
OFF 0 关闭视频防抖
ON 1 开启EIS(电子防抖)
PREVIEW_STABILIZATION 2 预览流防抖(Android 12+)

嗯,这里要注意:ON 模式只代表“开启EIS”,不代表OIS。OIS的控制在 CONTROL_OIS_MODE 里,两者是独立的。

我曾经遇到过一个坑:某款手机开启了 VIDEO_STABILIZATION_MODE_ON,但预览画面依然抖得厉害。查了半天才发现,OIS没开,纯靠EIS扛不住大抖动。后来我建议他们同时开启OIS,效果立竿见影。

我的建议:

如果设备支持OIS,一定要同时开启 CONTROL_OIS_MODEOPTIMIZE。别只依赖EIS,否则大抖动场景下画面会崩。

17.3 防抖对FOV的影响

这是很多人忽略的点。EIS开启后,FOV会变小。为什么?

你想想看,EIS需要“裁剪”画面边缘作为缓冲区域。当检测到抖动时,算法会移动这个裁剪窗口,把画面“拉”回来。这个缓冲区域越大,防抖效果越好,但FOV损失也越大。

我实测过一组数据:

EIS裁剪比例 FOV损失 防抖效果
5% 约3° 轻微
10% 约6° 中等
15% 约9° 较强
20% 约12° 极强

说白了,防抖和FOV是跷跷板。你要画面稳,就得牺牲视角。你要广角,就得接受抖动。

我在项目中遇到过产品经理要求“既要防抖好,又要FOV不损失”的情况。我只能告诉他:物理定律摆在那,要么加OIS,要么接受裁剪。最后他们选了OIS+EIS混合方案,FOV损失控制在5%以内,效果能接受。

17.4 防抖开启后的裁剪处理

EIS的裁剪处理,核心流程是这样的:

  1. 陀螺仪数据采集:每帧都记录手机的姿态变化
  2. 运动估计:计算帧与帧之间的位移量
  3. 裁剪窗口计算:根据位移量,动态调整裁剪区域
  4. 图像重采样:将裁剪后的图像缩放回原始分辨率
  5. 输出稳定帧:最终输出给编码器

这里面最核心的是第三步——裁剪窗口的计算。算法需要预测未来几帧的运动趋势,提前调整窗口位置,否则会出现“滞后感”。

我给你们看一段伪代码,理解一下这个逻辑:

// 伪代码:EIS裁剪窗口计算
void calculateCropWindow(Frame currentFrame, GyroData gyro) {
    // 1. 获取当前帧的陀螺仪数据
    float pitch = gyro.pitch;
    float yaw = gyro.yaw;
    float roll = gyro.roll;

    // 2. 计算运动补偿量
    float deltaX = yaw * FOCAL_LENGTH;
    float deltaY = pitch * FOCAL_LENGTH;

    // 3. 应用低通滤波,防止突变
    deltaX = lowPassFilter(deltaX, prevDeltaX);
    deltaY = lowPassFilter(deltaY, prevDeltaY);

    // 4. 计算裁剪窗口(保留10%缓冲区域)
    int cropWidth = sensorWidth * 0.9f;
    int cropHeight = sensorHeight * 0.9f;
    int cropX = (sensorWidth - cropWidth) / 2 + deltaX;
    int cropY = (sensorHeight - cropHeight) / 2 + deltaY;

    // 5. 限制裁剪窗口不超出传感器边界
    cropX = clamp(cropX, 0, sensorWidth - cropWidth);
    cropY = clamp(cropY, 0, sensorHeight - cropHeight);

    // 6. 输出裁剪参数
    setCropRegion(cropX, cropY, cropWidth, cropHeight);
}

这段代码看着简单,但实际调优时很痛苦。低通滤波的系数、缓冲区域的大小、运动预测的算法,每个参数都影响最终效果。

注意:

裁剪窗口的计算必须在每一帧的VSYNC中断内完成,否则会出现帧率抖动。我曾经因为裁剪计算耗时过长,导致预览帧率从30fps掉到24fps,用户反馈“画面卡卡的”。后来优化了算法,把计算量压到1ms以内才解决。

17.5 知识体系总览

我把本章的核心逻辑画成了一张图,方便你理解整个防抖体系:

视频防抖知识体系 OIS(光学防抖) EIS(电子防抖) 硬件补偿 不损失画质 不裁剪FOV 成本高,补偿低频抖动 软件算法 裁剪画面 FOV变小 成本低,处理高频抖动 OIS + EIS 混合方案

17.6 实际调优经验

最后分享几个我在项目中积累的调优经验:

  • 陀螺仪采样率要够:至少200Hz,否则运动估计不准。我见过用100Hz的,画面像抽风一样。
  • 裁剪缓冲区域不要固定:动态调整,抖动大时扩大缓冲,抖动小时缩小,能平衡FOV和防抖效果。
  • OIS和EIS的协同:OIS负责低频(0-5Hz),EIS负责高频(5-20Hz),中间有重叠区域需要做融合滤波。
  • 预览和录制分开处理:预览流可以降低防抖强度以保持低延迟,录制流可以加强防抖以追求画质稳定。

避坑指南:

我曾经在某个项目里,把EIS的裁剪缓冲设成了20%,结果FOV损失太大,用户投诉“视角变窄了”。后来改成动态缓冲,平时10%,抖动大时15%,用户反馈好多了。所以,别一刀切,要根据场景动态调整。

好了,视频防抖这块就聊到这。记住:OIS是硬件兜底,EIS是软件精修,两者配合才能出好效果。FOV的损失是不可避免的,但可以通过动态裁剪来优化。裁剪处理的核心是运动估计和窗口计算,这块的算法调优决定了最终体验。

公众号:蓝海资料掘金营,微信deep3321