1. 基于陀螺仪数据的简单平移补偿

基于陀螺仪数据的简单平移补偿 学习卡片

任务说明

本案例实现运动相机电子防抖中最基础的环节:读取陀螺仪角速度数据,积分得到帧间旋转角度,进而计算视频帧的全局平移量,对帧进行平移补偿,输出稳定后的视频。案例聚焦于纯平移补偿模型,即假设抖动主要体现为小角度旋转引起的画面平移,忽略透视与缩放变化。

实现方案

整体流程为:解析陀螺仪数据文件(CSV格式,包含时间戳与三轴角速度)→ 对相邻视频帧间的时间段积分得到旋转角 → 将旋转角映射为像素平移量 → 使用OpenCV的仿射变换对每帧进行平移补偿 → 写出稳定视频。为简化,本案例仅处理绕Y轴(偏航)与X轴(俯仰)的旋转,对应画面的水平与垂直平移。

代码实现

import cv2
import numpy as np
import pandas as pd
from scipy.integrate import cumulative_trapezoid

def load_gyro_data(csv_path):
    """读取陀螺仪CSV: 列名为 timestamp, gx, gy, gz (rad/s)"""
    df = pd.read_csv(csv_path)
    return df['timestamp'].values, df[['gx', 'gy', 'gz']].values

def compute_frame_rotation(timestamps, gyro, fps=30):
    """
    计算每帧对应的累积旋转角(rad)。
    帧i的旋转角 = 从帧i-1到帧i时间段内的角速度积分。
    返回形状为(N,2)的数组,列对应 [绕Y轴(偏航), 绕X轴(俯仰)]。
    """
    dt = np.diff(timestamps)
    # 累积积分得到每个采样时刻的总旋转角
    cum_angle = cumulative_trapezoid(gyro, timestamps, initial=0)
    frame_count = int((timestamps[-1] - timestamps[0]) * fps) + 1
    frame_times = np.linspace(timestamps[0], timestamps[-1], frame_count)
    # 在帧时间点插值得到每帧的累积旋转角
    interp_angles = np.column_stack([
        np.interp(frame_times, timestamps, cum_angle[:, 1]),  # 偏航
        np.interp(frame_times, timestamps, cum_angle[:, 0])   # 俯仰
    ])
    # 帧间旋转角 = 当前帧累积角 - 前一帧累积角
    frame_rot = np.diff(interp_angles, axis=0, prepend=interp_angles[0:1])
    return frame_rot

def rotation_to_pixel_shift(angles, focal_length_px):
    """将旋转角(rad)转换为像素平移量。小角度近似: shift = f * angle"""
    return angles * focal_length_px

def stabilize_video(input_video, gyro_csv, output_video, focal_length_px=500):
    """主函数: 读取视频与陀螺仪数据, 输出平移补偿后的稳定视频"""
    cap = cv2.VideoCapture(input_video)
    fps = cap.get(cv2.CAP_PROP_FPS)
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    fourcc = cv2.VideoWriter_fourcc(*'mp4v')
    out = cv2.VideoWriter(output_video, fourcc, fps, (width, height))

    timestamps, gyro = load_gyro_data(gyro_csv)
    frame_rot = compute_frame_rotation(timestamps, gyro, fps)
    shifts = rotation_to_pixel_shift(frame_rot, focal_length_px)

    frame_idx = 0
    while True:
        ret, frame = cap.read()
        if not ret:
            break
        if frame_idx < len(shifts):
            dx, dy = shifts[frame_idx]
            # 平移变换矩阵
            M = np.float32([[1, 0, -dx], [0, 1, -dy]])
            stabilized = cv2.warpAffine(frame, M, (width, height),
                                        borderMode=cv2.BORDER_REPLICATE)
        else:
            stabilized = frame
        out.write(stabilized)
        frame_idx += 1

    cap.release()
    out.release()

if __name__ == '__main__':
    # 示例调用: 请替换为实际文件路径
    stabilize_video('input.mp4', 'gyro_data.csv', 'output_stabilized.mp4')

输入数据格式

陀螺仪CSV文件需包含三列:timestamp(秒)、gx(绕X轴角速度,rad/s)、gy(绕Y轴角速度,rad/s)。gz列可省略或忽略。示例数据前几行:

timestamp,gx,gy
0.000,-0.012,0.008
0.005,-0.015,0.010
0.010,-0.010,0.006

验证方法

运行以下命令处理测试视频:

python stabilize.py

判定标准:

  1. 程序正常结束且无报错,输出视频文件可正常播放;
  2. 输出视频时长与输入一致,分辨率不变;
  3. 目视检查输出视频:画面中的静态物体(如墙壁边缘)在抖动场景下比原视频更平稳,平移幅度明显减小;
  4. 若原视频包含明显的上下或左右抖动,补偿后这些抖动应被显著抑制。
平移补偿EIS处理流程 陀螺仪CSV 时间戳+角速度 输入视频 原始抖动帧 帧间旋转角计算 角速度积分+插值 旋转→像素平移 shift = f × angle 融合 仿射变换补偿 warpAffine 平移 稳定视频输出 MP4文件 关键参数 • 焦距 f (像素): 决定旋转角到平移量的比例,需根据相机FOV标定 • 帧率 fps: 用于将陀螺仪时间戳对齐到视频帧索引

关键说明

  • 本实现假设陀螺仪时间戳与视频帧时间戳已对齐(同一时钟源),实际使用时需先进行时间同步校准;
  • 焦距参数focal_length_px需根据相机内参设定,典型运动相机广角模式约为400~600像素;
  • 平移补偿仅适用于小角度抖动(<5°),大角度旋转会产生透视畸变,需使用更完整的单应变换模型。