1. 基于帧间差分法的抖动检测

任务目标

实现基于帧间差分的全局运动检测算法:输入连续视频帧,计算相邻帧间的平移运动矢量 (dx, dy),为后续图像稳定提供运动估计基础。本案例聚焦于全局平移运动的检测,不涉及旋转与缩放。

核心原理

帧间差分法假设相邻帧间的全局运动主要表现为像素的整体平移。通过最小化两帧间像素差的值之和(SAD),在搜索窗口内寻找最佳偏移量,该偏移量即为估计的运动矢量。为降低噪声影响并提升计算效率,算法在灰度图上执行,并采用金字塔下采样策略。

完整实现代码

import numpy as np
import cv2

def compute_motion_vector(prev_gray, curr_gray, search_range=8, downscale=2):
    """
    基于帧间差分法计算全局平移运动矢量。
    步骤:下采样 -> 全搜索SAD匹配 -> 上采样补偿。
    """
    # 1. 下采样加速
    prev_small = cv2.pyrDown(prev_gray, dstsize=(prev_gray.shape[1]//downscale,
                                                  prev_gray.shape[0]//downscale))
    curr_small = cv2.pyrDown(curr_gray, dstsize=(curr_gray.shape[1]//downscale,
                                                  curr_gray.shape[0]//downscale))

    h, w = prev_small.shape
    # 2. 在低分辨率上全搜索最佳偏移
    min_sad = float('inf')
    best_dx_small, best_dy_small = 0, 0
    for dy in range(-search_range, search_range+1):
        for dx in range(-search_range, search_range+1):
            # 平移当前帧(用M矩阵平移,边界填充0)
            M = np.float32([[1, 0, dx], [0, 1, dy]])
            shifted = cv2.warpAffine(curr_small, M, (w, h), flags=cv2.INTER_LINEAR,
                                     borderMode=cv2.BORDER_CONSTANT, borderValue=0)
            diff = cv2.absdiff(prev_small, shifted)
            sad = np.sum(diff)
            if sad < min_sad:
                min_sad = sad
                best_dx_small, best_dy_small = dx, dy

    # 3. 映射回原始分辨率(乘以下采样倍数)
    dx = best_dx_small * downscale
    dy = best_dy_small * downscale
    return dx, dy

# ---------- 最小可运行验证示例 ----------
if __name__ == "__main__":
    # 构造两帧测试图像:第二帧相对第一帧向右平移3像素、向下平移2像素
    h, w = 120, 160
    prev = np.zeros((h, w), dtype=np.uint8)
    cv2.rectangle(prev, (30, 30), (100, 80), 255, -1)  # 白色矩形
    cv2.circle(prev, (120, 60), 15, 200, -1)          # 灰色圆形

    M_true = np.float32([[1, 0, 3], [0, 1, 2]])
    curr = cv2.warpAffine(prev, M_true, (w, h), flags=cv2.INTER_LINEAR,
                          borderMode=cv2.BORDER_CONSTANT, borderValue=0)

    # 调用算法
    dx_est, dy_est = compute_motion_vector(prev, curr, search_range=8, downscale=2)
    print(f"估计运动矢量: dx={dx_est}, dy={dy_est}")
    print(f"真实运动矢量: dx=3, dy=2")
    print(f"误差: dx_err={abs(dx_est-3)}, dy_err={abs(dy_est-2)}")
    assert abs(dx_est-3) <= 2 and abs(dy_est-2) <= 2, "运动估计误差过大"
    print("验证通过:估计值与真实值偏差在允许范围内")

运行与验证

  • 依赖:Python 3.7+,NumPy,OpenCV(pip install numpy opencv-python)
  • 运行方式:将上述代码保存为 motion_detect.py,在终端执行 python motion_detect.py
  • 通过条件:程序输出估计运动矢量,且断言通过(误差≤2像素)。示例中真实平移为 (3, 2),算法应输出接近该值的估计结果。

关键说明

  • 搜索窗口 search_range 决定最大可检测位移(当前为±8像素,下采样后实际覆盖±16像素)。
  • 下采样倍数 downscale 降低计算量,但会损失亚像素精度,适用于整数像素级运动检测。
  • 边界填充使用常数0,避免平移时引入非真实边缘信息。
  • 若视频存在局部运动或光照突变,SAD全搜索可能失效,此时可先进行高斯平滑预处理。