关于 video-processing
为使用 OpenCV 等计算机视觉库处理和分析视频提供指导的技能。它强调先检查帧和元数据,再开发并验证事件检测或运动跟踪逻辑。
主要功能
- 01检查样例帧和视频元数据
- 02构建事件检测流程
- 03使用帧差和轮廓检测
- 04跟踪物体随时间的位置
- 05验证时间合理性并测试多段视频
适用场景
- 01排查视频片段中的事件检测
- 02开发运动跟踪逻辑
- 03用已知帧验证检测结果
- 04调试不稳定的视频分析
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ letta-ai/ skills --skill video-processing安装后在 Claude Code 中输入/video-processing确认已加载。
详细介绍
视频处理
概述
本技能为使用 OpenCV 等计算机视觉库执行视频处理任务提供指导,涵盖帧级分析、事件检测和运动跟踪。它强调验证优先的方法,并帮助避免视频分析工作流中常见的陷阱。
核心方法:先验证,再实现
在编写检测算法之前,先准确了解视频内容:
- 提取并检查样例帧 - 将关键帧保存为图像,以直观确认特定帧中发生的情况
- 了解视频元数据 - 帧数、FPS、时长、分辨率
- 将预期事件映射到帧范围 - 如果存在测试数据,了解哪些帧对应哪些事件
- 先构建诊断工具 - 帧提取和可视化工具能提供关键线索
事件检测任务的工作流
阶段 1:探索视频
# Essential first steps for any video analysis task
import cv2
cap = cv2.VideoCapture(video_path)
frame_count = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
fps = cap.get(cv2.CAP_PROP_FPS)
duration = frame_count / fps
print(f"Frames: {frame_count}, FPS: {fps}, Duration: {duration:.2f}s")
重要:提取预期事件位置的帧,以确认理解是否正确:
def save_frame(video_path, frame_num, output_path):
cap = cv2.VideoCapture(video_path)
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_num)
ret, frame = cap.read()
if ret:
cv2.imwrite(output_path, frame)
cap.release()
# Save frames at expected event times for visual inspection
save_frame("video.mp4", 50, "frame_050.png")
save_frame("video.mp4", 60, "frame_060.png")
阶段 2:开发算法
开发检测算法时:
- 从简单方法开始 - 先尝试基本的帧差分或阈值处理,再使用复杂方法
- 使用可配置的阈值 - 避免硬编码神奇数字;根据数据推导阈值
- 先在已知帧上测试 - 在已知真实情况的帧上验证算法是否能产生预期结果
- 记录中间值 - 跟踪每一帧的指标,了解算法的行为
阶段 3:验证
定稿前:
- 对输出进行合理性检查 - 检测到的事件在顺序和时间上是否合理?
- 在多个视频上测试 - 验证算法能否泛化到不同输入
- 与预期范围进行比较 - 如果存在真实情况数据,验证检测准确性
常见检测方法
帧差分
将帧与参考帧(第一帧或前一帧)进行比较,以检测运动:
# Background subtraction approach
first_frame = cv2.cvtColor(first_frame, cv2.COLOR_BGR2GRAY)
first_frame = cv2.GaussianBlur(first_frame, (21, 21), 0)
# For each subsequent frame
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (21, 21), 0)
diff = cv2.absdiff(first_frame, gray)
陷阱:如果场景发生变化或摄像机移动,第一帧可能不适合作为参考帧。
基于轮廓的检测
通过在阈值处理后的图像中查找轮廓来识别对象:
_, thresh = cv2.threshold(diff, 25, 255, cv2.THRESH_BINARY)
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
陷阱:未经校准,阈值(例如 25)和轮廓最小面积都只是任意值。
跟踪随时间变化的位置
要检测跳跃或手势等事件,可跟踪对象在各帧中的位置:
positions = [] # (frame_num, x, y, area) tuples
for frame_num in range(frame_count):
# ... detection code ...
if detected:
positions.append((frame_num, cx, cy, area))
陷阱:坐标系很重要。在图像坐标中,Y 轴向下增大,因此“在帧中位置更高”意味着 Y 值更小。
验证策略
1. 目视检查
保存检测到事件时的帧,以确认结果是否正确:
# After detecting takeoff at frame N
save_frame(video_path, detected_takeoff, "detected_takeoff.png")
save_frame(video_path, detected_takeoff - 5, "before_takeoff.png")
save_frame(video_path, detected_takeoff + 5, "after_takeoff.png")
2. 时序合理性
检查检测到的事件在时间上是否合理:
duration_seconds = frame_count / fps
event_time = detected_frame / fps
# Example: A jump in a 4-second video shouldn't be detected in the last 0.5 seconds
if event_time > duration_seconds - 0.5:
print("WARNING: Event detected very late in video - verify correctness")
3. 序列验证
确保事件按逻辑顺序发生:
if detected_landing <= detected_takeoff:
print("ERROR: Landing cannot occur before or at takeoff")
4. 多视频测试
尽早在多个输入上测试,以发现算法对单个视频特征过拟合的问题。
常见陷阱
1. 未验证真实情况
问题:完全依赖计算出的指标,却不进行目视确认。
解决方案:始终保存并检查检测到事件位置的帧。
2. 数据解读中的确认偏误
问题:当数据呈现意外模式时,编造符合先入之见的解释,而不是质疑原有假设。
解决方案:如果检测结果看起来不对,应调查根本原因,而不是为意外行为找理由。
3. 神奇数字阈值
问题:使用任意阈值(轮廓面积取 500、二值阈值取 25),却没有实证依据。
解决方案:根据实际视频数据推导阈值,或将其设为可配置参数并提供合理的默认值。
4. 忽视检测缺失区间
问题:检测在一段帧范围内失败时,未经调查就认定这是预期行为。
解决方案:调查检测失败的原因——这可能表明算法存在缺陷,而不是预期行为。
5. 坐标系混淆
问题:误解 Y 坐标(在图像坐标系中,Y 值越小,位置越靠近画面上方)。
解决方案:明确记录坐标系假设,并通过目视检查进行验证。
6. 忽略时间合理性
问题:接受在时间上不合理的检测结果(例如,在一段 4 秒视频的最后 0.8 秒检测到事件)。
解决方案:对输出时间实现合理性检查。
7. 对单个视频过拟合
问题:算法在一个视频上有效,但在其他视频上失效。
解决方案:在开发早期就在多个视频上进行测试。
输出格式注意事项
输出结果时(例如,输出为 TOML、JSON):
import numpy as np
# Convert numpy types to Python native types for serialization
result = {
"takeoff_frame": int(takeoff_frame), # Not np.int64
"landing_frame": int(landing_frame),
}
调试检查清单
检测结果不正确时:
- 我是否目视检查了预期事件发生时间的帧?
- 我是否目视检查了检测到的事件发生时间的帧?
- 根据视频时长,我检测到的时间在时间上是否合理?
- 我是否在具有已知真值的帧上验证了算法?
- 我是否正确理解了坐标系?
- 我是否在多个视频上进行了测试?
- 我的阈值是根据数据得出的,还是随意设定的?
- 当某些帧检测失败时,我是否了解原因?