4、视频数据采集:帧率选择、运动模糊处理、视频切片工具、关键帧提取
视频数据采集,说实话,比图像采集要复杂一个量级。你想想看,一张图片就一个瞬间,但视频是一连串的瞬间,怎么选、怎么切、怎么提,全是坑。我最早做自动驾驶项目时,就因为在帧率上栽过跟头,后来才慢慢摸清楚这里面的门道。
4.1 帧率选择:不是越高越好
帧率,说白了就是每秒拍多少张画面。常见的帧率有 24fps、30fps、60fps,甚至 120fps。很多人觉得帧率越高越好,其实不一定。
核心原则:帧率要与场景运动速度匹配。
我举个例子。你拍一个静止的交通标志牌,24fps 就够了。但如果你拍一辆时速 80 公里的车,24fps 可能连车牌都看不清。为什么?因为两帧之间,车已经跑出去一米多了。
我个人习惯这样选帧率:
- 静态场景(如室内监控、文档扫描):15-24fps,够用,省存储
- 慢速运动(如行人走路、车辆转弯):30fps,稳妥
- 中速运动(如城市道路行驶):60fps,保证细节
- 高速运动(如高速公路、无人机航拍):90-120fps,但要注意存储成本
我在项目中遇到过一件事:客户要求采集高速公路场景,我们用了 30fps,结果标注员反馈说很多车辆在连续帧之间出现「跳跃」,根本没法准确标注。后来改成 60fps,问题就解决了。嗯,这里要注意,帧率翻倍,数据量也翻倍,存储和传输都要提前规划好。
小技巧:如果场景中既有静态又有动态,可以动态调整帧率。比如摄像头检测到运动区域时自动提高帧率,静止时降低。这个在智能摄像头里很常见。
4.2 运动模糊处理:该留还是该去?
运动模糊,就是物体快速移动时画面出现的拖影。很多人第一反应是「模糊就是坏数据,必须去掉」。但我不这么看。
其实运动模糊分两种:
- 自然模糊:物体本身在动,快门速度跟不上,产生的模糊。这种模糊其实包含了运动信息。
- 抖动模糊:摄像头本身在晃动,比如手持拍摄、车载抖动。这种模糊是噪声,需要处理。
我建议这样处理:
- 如果是目标检测任务,运动模糊严重的帧直接丢弃。因为标注员看不清边界,标注质量会下降。
- 如果是行为识别或轨迹预测任务,保留适度的运动模糊反而有帮助。它能提供运动方向和速度的线索。
- 如果是抖动模糊,可以用去抖动算法(如视频稳像)先处理,再决定是否保留。
注意:不要对所有模糊帧一刀切。我曾经有一个项目,把运动模糊帧全删了,结果发现模型在真实场景中表现很差。后来才明白,真实世界就是有模糊的,模型需要学会在模糊条件下工作。
判断运动模糊严重程度,可以用一个简单指标:拉普拉斯方差。值越低,模糊越严重。一般阈值设在 100-200 之间,低于阈值的帧可以考虑丢弃。
import cv2
def is_blurry(image, threshold=100):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
laplacian_var = cv2.Laplacian(gray, cv2.CV_64F).var()
return laplacian_var < threshold, laplacian_var
# 使用示例
frame = cv2.imread('frame_001.jpg')
blurry, score = is_blurry(frame)
print(f'模糊分数: {score:.2f}, 是否模糊: {blurry}')
4.3 视频切片工具:把长视频切成小段
视频采集通常是一段长视频,但标注和处理时,我们需要把它切成小段。为什么?因为长视频文件太大,加载慢,而且标注员很难在几十分钟的视频里精确定位。
我常用的切片工具有这几个:
| 工具 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| FFmpeg | 命令行批量处理 | 功能强大,跨平台 | 学习曲线陡 |
| OpenCV | Python 脚本集成 | 可编程,灵活 | 速度不如 FFmpeg |
| LosslessCut | 图形界面,快速切片 | 无损,操作简单 | 功能单一 |
| MoviePy | Python 高级处理 | 易用,支持特效 | 处理大视频慢 |
我个人习惯用 FFmpeg,因为它快,而且可以写脚本批量处理。比如这样切:
# 从第10秒开始,切5秒长的片段
ffmpeg -i input.mp4 -ss 00:00:10 -t 00:00:05 -c copy output.mp4
# 按固定时长切分(每30秒一段)
ffmpeg -i input.mp4 -c copy -map 0 -segment_time 30 -f segment output_%03d.mp4
这里有个坑:-ss 参数放在 -i 前面还是后面,效果不一样。放在前面是「快速跳转」,速度快但可能不精确;放在后面是「精确跳转」,速度慢但帧对齐。我一般先快速跳转到目标位置附近,再精确切。
切片策略:不要切成太短的片段。我建议每段 10-30 秒,太短了上下文丢失,太长了标注员疲劳。如果是动作识别任务,可以按动作边界来切,比如「人走进画面」到「人走出画面」为一段。
4.4 关键帧提取:从海量帧中挑出精华
视频数据里,相邻帧之间往往高度相似。比如一个人站着不动,连续 100 帧几乎一样。如果全部保留,数据冗余严重,训练效率低。这时候就需要关键帧提取。
关键帧提取,说白了就是「找变化」。我常用的方法有三种:
- 基于帧差法:计算相邻帧的像素差异,差异超过阈值就认为是关键帧。简单,但容易受噪声影响。
- 基于直方图:比较帧的颜色直方图,变化大就保留。对光照变化不敏感。
- 基于特征点:用 SIFT、ORB 等特征点匹配,变化大就保留。精度高,但计算量大。
我一般用帧差法做快速筛选,再用直方图做二次确认。代码大概这样:
import cv2
import numpy as np
def extract_keyframes(video_path, threshold=30):
cap = cv2.VideoCapture(video_path)
keyframes = []
prev_frame = None
frame_idx = 0
while True:
ret, frame = cap.read()
if not ret:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
if prev_frame is not None:
diff = cv2.absdiff(prev_frame, gray)
mean_diff = np.mean(diff)
if mean_diff > threshold:
keyframes.append((frame_idx, frame))
print(f'关键帧: 第 {frame_idx} 帧, 差异值: {mean_diff:.2f}')
prev_frame = gray
frame_idx += 1
cap.release()
return keyframes
# 使用
frames = extract_keyframes('video.mp4', threshold=25)
阈值怎么设?我一般先跑一遍,看看差异值的分布,再选一个合适的值。比如大部分帧差异在 10-20 之间,突然跳到 50 以上,那 30 就是个不错的阈值。
注意:关键帧提取不是越少越好。我见过有人把 1 小时的视频压缩成 10 帧,结果模型完全学不到东西。关键帧的密度要根据任务来定:检测任务可以稀疏一些,跟踪任务需要密集一些。
4.5 知识体系总览
下面这张图,是我梳理的视频数据采集核心流程。你可以把它当作一个检查清单,做项目时对照着来。
整个流程走下来,你会发现视频数据采集其实是个「做减法」的过程。从原始视频流开始,通过帧率选择控制数据量,通过模糊处理剔除坏数据,通过切片降低处理难度,最后通过关键帧提取保留最有价值的部分。每一步都在问同一个问题:这些数据,真的需要吗?
好了,这一章的内容就到这里。视频采集的门道不少,但核心就是四个字:取舍有度。你想想看,数据不是越多越好,而是越精越好。