视频处理
本章节介绍如何使用 OpenCV 处理视频,包括视频文件的读写、摄像头操作和视频分析技术。
视频读取
OpenCV 使用 cv2.VideoCapture 类来读取视频文件或摄像头数据。
读取视频文件
import cv2
# 打开视频文件
cap = cv2.VideoCapture('video.mp4')
# 检查是否成功打开
if not cap.isOpened():
print("无法打开视频文件")
exit()
while True:
# 读取一帧
ret, frame = cap.read()
# ret 为 True 表示读取成功,frame 为图像帧
if not ret:
print("视频播放结束")
break
# 显示帧
cv2.imshow('视频', frame)
# 按 q 退出
if cv2.waitKey(25) & 0xFF == ord('q'):
break
# 释放资源
cap.release()
cv2.destroyAllWindows()
read() 方法返回两个值:
- ret:布尔值,表示是否成功读取帧
- frame:读取的图像帧,是一个 NumPy 数组
读取摄像头
import cv2
# 打开摄像头(0 表示默认摄像头)
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("无法打开摄像头")
exit()
while True:
ret, frame = cap.read()
if not ret:
print("无法获取帧")
break
cv2.imshow('摄像头', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
如果有多个摄像头,可以尝试不同的索引值(0, 1, 2...)。
获取视频属性
import cv2
cap = cv2.VideoCapture('video.mp4')
# 获取视频属性
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS)
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
duration = frame_count / fps # 视频时长(秒)
print(f"分辨率: {width}x{height}")
print(f"帧率: {fps} FPS")
print(f"总帧数: {frame_count}")
print(f"时长: {duration:.2f} 秒")
cap.release()
常用的视频属性:
| 属性 | 说明 |
|---|---|
CAP_PROP_FRAME_WIDTH | 帧宽度 |
CAP_PROP_FRAME_HEIGHT | 帧高度 |
CAP_PROP_FPS | 帧率 |
CAP_PROP_FRAME_COUNT | 总帧数 |
CAP_PROP_POS_FRAMES | 当前帧位置 |
CAP_PROP_POS_MSEC | 当前位置(毫秒) |
设置视频位置
import cv2
cap = cv2.VideoCapture('video.mp4')
# 跳转到第 100 帧
cap.set(cv2.CAP_PROP_POS_FRAMES, 100)
# 跳转到第 5 秒
cap.set(cv2.CAP_PROP_POS_MSEC, 5000)
ret, frame = cap.read()
if ret:
cv2.imshow('指定帧', frame)
cv2.waitKey(0)
cap.release()
cv2.destroyAllWindows()
视频写入
使用 cv2.VideoWriter 类将处理后的帧写入视频文件。
基本写入
import cv2
# 打开摄像头
cap = cv2.VideoCapture(0)
# 获取视频参数
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = 30.0
# 定义视频编码器和创建 VideoWriter
fourcc = cv2.VideoWriter_fourcc(*'mp4v') # MP4 编码
out = cv2.VideoWriter('output.mp4', fourcc, fps, (width, height))
while True:
ret, frame = cap.read()
if not ret:
break
# 写入帧
out.write(frame)
cv2.imshow('录制中', frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
out.release()
cv2.destroyAllWindows()
视频编码器
常用的视频编码器:
| 编码器 | FourCC 代码 | 文件格式 |
|---|---|---|
| MP4V | 'mp4v' | .mp4 |
| XVID | 'XVID' | .avi |
| MJPEG | 'MJPG' | .avi |
| H264 | 'avc1' | .mp4 |
# 不同的编码器设置方式
fourcc = cv2.VideoWriter_fourcc(*'mp4v') # 方式1
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v') # 方式2
处理视频并保存
import cv2
cap = cv2.VideoCapture('input.mp4')
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = cap.get(cv2.CAP_PROP_FPS)
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter('output.mp4', fourcc, fps, (width, height))
while True:
ret, frame = cap.read()
if not ret:
break
# 转换为灰度图
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 转换回 BGR(视频写入需要 3 通道)
gray_bgr = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
out.write(gray_bgr)
cv2.imshow('灰度视频', gray_bgr)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
out.release()
cv2.destroyAllWindows()
视频分析
背景减除
背景减除用于从视频中提取前景对象:
import cv2
cap = cv2.VideoCapture('video.mp4')
# 创建背景减除器
# MOG2 背景减除器
back_sub = cv2.createBackgroundSubtractorMOG2(
history=500, # 历史帧数
varThreshold=16, # 方差阈值
detectShadows=True # 检测阴影
)
while True:
ret, frame = cap.read()
if not ret:
break
# 应用背景减除
fg_mask = back_sub.apply(frame)
# 显示结果
cv2.imshow('原图', frame)
cv2.imshow('前景掩码', fg_mask)
if cv2.waitKey(30) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
光流法
光流法用于检测视频中物体的运动:
import cv2
import numpy as np
cap = cv2.VideoCapture('video.mp4')
# 读取第一帧
ret, old_frame = cap.read()
old_gray = cv2.cvtColor(old_frame, cv2.COLOR_BGR2GRAY)
# 检测角点
feature_params = dict(
maxCorners=100,
qualityLevel=0.3,
minDistance=7,
blockSize=7
)
p0 = cv2.goodFeaturesToTrack(old_gray, mask=None, **feature_params)
# Lucas-Kanade 光流参数
lk_params = dict(
winSize=(15, 15),
maxLevel=2,
criteria=(cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 0.03)
)
# 创建掩码用于绘制轨迹
mask = np.zeros_like(old_frame)
while True:
ret, frame = cap.read()
if not ret:
break
frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# 计算光流
p1, st, err = cv2.calcOpticalFlowPyrLK(
old_gray, frame_gray, p0, None, **lk_params
)
# 选择好的点
if p1 is not None:
good_new = p1[st == 1]
good_old = p0[st == 1]
# 绘制轨迹
for i, (new, old) in enumerate(zip(good_new, good_old)):
a, b = new.ravel()
c, d = old.ravel()
mask = cv2.line(mask, (int(a), int(b)), (int(c), int(d)),
(0, 255, 0), 2)
frame = cv2.circle(frame, (int(a), int(b)), 5, (0, 0, 255), -1)
img = cv2.add(frame, mask)
cv2.imshow('光流', img)
if cv2.waitKey(30) & 0xFF == ord('q'):
break
# 更新上一帧
old_gray = frame_gray.copy()
p0 = good_new.reshape(-1, 1, 2)
cap.release()
cv2.destroyAllWindows()
帧差法
帧差法是最简单的运动检测方法:
import cv2
cap = cv2.VideoCapture('video.mp4')
ret, prev_frame = cap.read()
prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)
prev_gray = cv2.GaussianBlur(prev_gray, (21, 21), 0)
while True:
ret, frame = cap.read()
if not ret:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
# 计算帧差
frame_diff = cv2.absdiff(prev_gray, gray)
# 二值化
_, thresh = cv2.threshold(frame_diff, 25, 255, cv2.THRESH_BINARY)
# 膨胀操作
thresh = cv2.dilate(thresh, None, iterations=2)
# 查找轮廓
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
# 绘制运动区域
for contour in contours:
if cv2.contourArea(contour) < 500: # 过滤小区域
continue
x, y, w, h = cv2.boundingRect(contour)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow('运动检测', frame)
cv2.imshow('帧差', thresh)
prev_gray = gray.copy()
if cv2.waitKey(30) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
目标跟踪
OpenCV 提供了多种目标跟踪算法:
import cv2
cap = cv2.VideoCapture('video.mp4')
ret, frame = cap.read()
if not ret:
print("无法读取视频")
exit()
# 选择跟踪区域
bbox = cv2.selectROI('选择目标', frame, False)
cv2.destroyWindow('选择目标')
# 创建跟踪器(CSRT 算法)
tracker = cv2.TrackerCSRT_create()
# 初始化跟踪器
tracker.init(frame, bbox)
while True:
ret, frame = cap.read()
if not ret:
break
# 更新跟踪器
success, bbox = tracker.update(frame)
if success:
# 绘制跟踪框
x, y, w, h = [int(v) for v in bbox]
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.putText(frame, '跟踪中', (x, y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
else:
cv2.putText(frame, '丢失目标', (100, 100),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2)
cv2.imshow('目标跟踪', frame)
if cv2.waitKey(30) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
常用的跟踪算法:
| 算法 | 类名 | 特点 |
|---|---|---|
| CSRT | TrackerCSRT_create | 精度高,速度较慢 |
| KCF | TrackerKCF_create | 速度快,精度中等 |
| MIL | TrackerMIL_create | 鲁棒性好 |
| MOSSE | TrackerMOSSE_create | 速度最快 |
实用示例
视频录制工具
import cv2
from datetime import datetime
def record_video(output_path=None, duration=10):
cap = cv2.VideoCapture(0)
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
fps = 30.0
if output_path is None:
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
output_path = f'record_{timestamp}.mp4'
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(output_path, fourcc, fps, (width, height))
frame_count = 0
total_frames = int(duration * fps)
print(f"开始录制,时长: {duration} 秒")
while frame_count < total_frames:
ret, frame = cap.read()
if not ret:
break
out.write(frame)
cv2.imshow('录制中', frame)
frame_count += 1
if cv2.waitKey(1) & 0xFF == ord('q'):
break
print(f"录制完成,保存到: {output_path}")
cap.release()
out.release()
cv2.destroyAllWindows()
record_video(duration=5)
运动检测报警
import cv2
import numpy as np
def motion_detection(threshold=500):
cap = cv2.VideoCapture(0)
ret, prev_frame = cap.read()
prev_gray = cv2.cvtColor(prev_frame, cv2.COLOR_BGR2GRAY)
prev_gray = cv2.GaussianBlur(prev_gray, (21, 21), 0)
motion_detected = False
while True:
ret, frame = cap.read()
if not ret:
break
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
frame_diff = cv2.absdiff(prev_gray, gray)
_, thresh = cv2.threshold(frame_diff, 25, 255, cv2.THRESH_BINARY)
thresh = cv2.dilate(thresh, None, iterations=2)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
motion = False
for contour in contours:
if cv2.contourArea(contour) < threshold:
continue
motion = True
x, y, w, h = cv2.boundingRect(contour)
cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 255, 0), 2)
if motion and not motion_detected:
print("检测到运动!")
motion_detected = True
elif not motion:
motion_detected = False
status = "运动检测" if motion else "正常"
cv2.putText(frame, status, (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255) if motion else (0, 255, 0), 2)
cv2.imshow('运动检测', frame)
prev_gray = gray.copy()
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
motion_detection()
下一步
掌握了视频处理后,下一章节我们将学习特征检测与匹配,了解如何检测图像中的关键点并进行特征匹配。