2、视频数据基础:视频的数字化表示与实战读取

大家好,我是你们这门课的老朋友。今天咱们来聊聊视频数据的基础。说实话,我刚开始做行为识别那会儿,也踩过不少坑——比如读进来的视频全是花屏,或者帧率对不上导致模型训练直接崩掉。所以这一章,咱们把视频的“底裤”扒干净,看看它到底是怎么存的、怎么读的。

2.1 视频的数字化表示

视频说白了,就是一连串快速播放的图片。但计算机里存视频,可不会傻乎乎地存几千张独立图片——那样一个10秒的视频就能撑爆你的硬盘。所以,视频的数字化表示涉及几个核心概念。

2.1.1 帧(Frame)

帧就是视频里的每一张静态画面。比如一个30fps的视频,每秒就有30帧。人眼有个视觉暂留效应,只要帧率超过24fps,看起来就是流畅的。

我个人习惯:做行为识别时,通常不会把每一帧都送进模型。比如一个1分钟的视频有1800帧,全处理太慢了。我一般会隔几帧取一帧,或者只取关键帧。

2.1.2 码率(Bitrate)

码率就是视频每秒占用的数据量,单位是bps(比特每秒)。码率越高,画质越好,文件也越大。举个例子:

  • 低码率(500kbps):适合网络传输,但画面会有马赛克
  • 高码率(10Mbps):画质清晰,适合做精细分析
避坑指南:我曾经在项目里用了一个低码率的监控视频做异常检测,结果模型死活识别不出“摔倒”动作——因为画面太糊了,人形都看不清。后来我强制要求所有输入视频码率不低于2Mbps,问题就解决了。

2.1.3 分辨率(Resolution)

分辨率就是视频的宽×高,比如1920×1080(1080p)。分辨率越高,细节越丰富,但计算量也越大。

分辨率 常见场景 我推荐用于行为识别吗?
640×480 老式监控 不推荐,太糊了
1280×720 普通摄像头 可以,但注意细节
1920×1080 高清视频 推荐,平衡了清晰度和速度
3840×2160 4K视频 慎用,除非你有GPU集群

你想想看,如果做“挥手”识别,640×480可能还能用;但如果是“偷窃”这种需要看清手指动作的,至少得720p起步。

2.2 常见视频格式

视频格式说白了就是“容器+编码”。容器决定文件后缀,编码决定压缩方式。

2.2.1 MP4(最常用)

MP4是目前最通用的格式。它用H.264或H.265编码,压缩率高,画质损失小。我90%的项目都用MP4。

  • 优点:兼容性好,OpenCV直接支持
  • 缺点:编码复杂,解码稍慢

2.2.2 AVI(老牌格式)

AVI是微软的老格式,现在用得少了。它通常不压缩或采用低压缩率,所以文件巨大。一个10秒的AVI可能比MP4大10倍。

小技巧:如果你拿到一个AVI文件,我建议先用FFmpeg转成MP4再处理。我曾经在项目里直接读AVI,结果内存爆了——因为一帧的数据量太大了。

2.2.3 其他格式

  • MOV:苹果生态常用,OpenCV也能读
  • FLV:直播流常用,但行为识别很少用
  • MKV:多轨道格式,适合存多个音频/字幕

2.3 视频读取与帧提取实战(OpenCV基础)

好了,理论说完了,咱们直接上代码。OpenCV是视频处理最常用的库,没有之一。

2.3.1 安装OpenCV

如果你还没装,先跑这行命令:

pip install opencv-python

2.3.2 读取视频并获取基本信息

咱们先写个脚本,看看视频的“身份证”:

import cv2

# 打开视频文件
cap = cv2.VideoCapture('test.mp4')

# 获取基本信息
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)) # 高度
total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT)) # 总帧数

print(f"帧率: {fps} FPS")
print(f"分辨率: {width} x {height}")
print(f"总帧数: {total_frames}")

# 记得释放资源
cap.release()

嗯,这里要注意:cap.get()返回的是浮点数,记得转成int。我刚开始写的时候忘了转,结果后面做循环时索引报错,排查了半天。

2.3.3 逐帧提取并保存

接下来,咱们把视频的每一帧都提取出来,存成图片。这在做数据集准备时特别有用。

import cv2
import os

cap = cv2.VideoCapture('test.mp4')
output_dir = 'frames'
os.makedirs(output_dir, exist_ok=True)

frame_count = 0
while True:
    ret, frame = cap.read()
    if not ret:
        break  # 视频读完了
    
    # 保存每一帧
    filename = os.path.join(output_dir, f'frame_{frame_count:04d}.jpg')
    cv2.imwrite(filename, frame)
    frame_count += 1

cap.release()
print(f"共提取 {frame_count} 帧")
核心要点cap.read()返回两个值——ret(是否成功)和frame(图像数据)。如果ret为False,说明视频结束了或者出错了。一定要检查ret,否则会死循环。

2.3.4 按间隔提取帧(实战常用)

说实话,全量提取太浪费了。我一般每隔5帧或10帧取一帧,既能保留动作信息,又不会数据爆炸。

import cv2

cap = cv2.VideoCapture('test.mp4')
interval = 5  # 每5帧取一帧
frame_count = 0
saved_count = 0

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    if frame_count % interval == 0:
        cv2.imwrite(f'frame_{saved_count:04d}.jpg', frame)
        saved_count += 1
    
    frame_count += 1

cap.release()
print(f"原始帧数: {frame_count}, 保存帧数: {saved_count}")
我的经验:对于“走路”、“跑步”这类动作,间隔5帧完全够用。但对于“挥手”这种快速动作,间隔最好不超过3帧,否则会漏掉关键姿态。

2.3.5 实时显示视频(调试用)

有时候你想看看视频内容对不对,可以用imshow实时显示:

import cv2

cap = cv2.VideoCapture('test.mp4')

while True:
    ret, frame = cap.read()
    if not ret:
        break
    
    cv2.imshow('Video', frame)
    
    # 按'q'键退出
    if cv2.waitKey(25) & 0xFF == ord('q'):
        break

cap.release()
cv2.destroyAllWindows()

为什么会用waitKey(25)?因为25毫秒约等于40fps,和常见视频帧率匹配。如果视频是30fps,你可以改成33毫秒。

2.4 本章小结

这一章咱们把视频的“底”摸清了:帧、码率、分辨率是三个核心参数;MP4是主流格式;OpenCV的VideoCapture是读取视频的利器。下一章,咱们会深入视频的时空特征,开始真正的行为识别之旅。

记住:视频处理的第一步永远是“读对视频”。我见过太多人因为视频格式不对、帧率不匹配,导致后面模型训练全白费。所以,先把基础打牢,后面才能飞得稳。

课后练习:找一个你手机里的视频,用OpenCV读取它的帧率、分辨率、总帧数,然后每隔10帧提取一帧保存。试试看,你的视频有多少帧?提取了多少张图片?

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