OpenCV与OpenPose结合的人体姿态识别:从视频流到动作判定
2026/9/23 23:47:00 网站建设 项目流程

简介:面向希望快速上手人体姿态识别的开发者,工程以 Python+OpenCV+OpenPose 为主线,覆盖视频与摄像头场景下的完整识别实现,既适合初学者跟着跑通流程,也适合中高级开发者作为二次开发基线。整个工程打包为 7z,共 40 个文件,约 8.12MB,其中 19 个 Python 脚本构成主体,覆盖训练、推理与演示;12 个 pyc 为编译缓存,另含 mp4 测试视频、jpg 演示图、md 说明文档、requirements 依赖清单及 license 等配置。目录结构分为模型、模块、脚本等层级,便于按模块理解采集、预处理、关键点检测到可视化输出的完整链路。目前已有 7000 余人学习下载。除核心识别逻辑外,资源还附带自定义数据集训练说明、标签制作脚本、ONNX 转换工具和关键点滤波模块,能支撑从模型训练到部署落地的扩展需求。相关能力可迁移到运动分析、健身指导、虚拟现实或康复监控等场景。

1. 人体形态算法识别的两条腿:为什么 OpenPose 离不开 OpenCV

一个很有意思的现象是,很多人以为“人体形态识别”的重点全在 OpenPose 这个深度模型上,但真正把项目从 demo 推向可用状态的,往往是 OpenCV 那半边。实际上,在没有 GPU 的普通笔记本上,OpenPose 的单帧推理耗时动辄 1 到 3 秒,根本跑不满 30 帧的视频流;而市面上所有能在摄像头前“实时”显示骨骼动画的方案,几乎都用了 OpenCV 的帧采集、缩放、绘制和视频写出能力。换句话说,OpenCV 负责“看”和“画”,OpenPose 负责“想”,两者拼在一起才是一个完整的人体形态识别链路。这篇东西会直接从视频采集讲到关键点提取,再落到姿态判定的可复现代码上,适合那些手里已经有一个摄像头或者一段视频、想快速验证人体骨架识别效果的工程师——无论你之前是否接触过深度学习推理,照着参数调就能跑起来。

2. OpenPose 的关键点定义与 OpenCV 的帧数据类型

2.1 先把“人体形态”翻译成数字:COCO 骨架的 18 个关键点

OpenPose 的输出不是一张画好骨头的图,而是一个形状为(1, 18, 3)的张量,或者更直白地说,是 18 个点的坐标加置信度。这 18 个点遵循 COCO 数据集的定义,从 0 到 17 依次对应鼻子、颈部、左右肩膀、左右肘部、左右手腕、左右髋部、左右膝盖、左右脚踝,外加两只眼睛和两只耳朵。注意,OpenPose 官方其实还支持 25 个点的 BODY_25 模型,但那套模型更偏重手部和脚部细节,计算量也更大;大多数人做人体形态识别,比如判断一个人是站立、弯腰还是举手,COCO 的 18 点模型已经绰绰有余。

关键点坐标的数据类型是浮点数,对应的图像坐标系原点在左上角,x 轴向右、y 轴向下。这和 OpenCV 的坐标方向完全一致,所以后面画骨架的时候不需要做任何坐标变换。每个点的第三个值是置信度,范围在 0 到 1 之间,低于某个阈值(比如 0.3)就说明这个点没有被可靠检测到,画图时要跳过,否则会出现诡异的“飘浮手臂”效果。

2.1.1 用 PAF(部分亲和场)理解 OpenPose 为什么“准”

OpenPose 在 2017 年 CVPR 提出时,最大的贡献不是关键点检测本身,而是解决了“多人情况下关键点怎么连接”的问题。它设计了一个名为 PAF(Part Affinity Fields,部分亲和场)的中间表示:网络不仅预测每个关键点的热力图,还预测每两个相邻关键点之间连线的方向和置信度。比如左肘到左腕这一段,PAF 会在每个像素位置输出一个二维向量,指示“如果这里是手臂,骨骼应该朝哪个方向延伸”。后续的匹配算法根据这些向量做匈牙利匹配,从而把分散的关键点正确组装成一个个完整的人体骨架。

这个设计带来的工程影响很直接:OpenPose 的多人检测效果比先检测人框再做单人姿态估计的方法更稳定,尤其是在两个人交叉走过的时候,不容易把 A 的手接到 B 的肩膀上。代价是计算量翻倍,因为网络要同时输出热力图和 PAF 两个分支。这一点在实际项目中非常重要——如果你只需要单人姿态,完全可以把 OpenPose 换成 MoveNet 或 MediaPipe 的 Pose Landmark,速度能快一个数量级;但如果你要处理“多人同时出现在画面里”的场景,比如健身房的多人动作计数,OpenPose 的 PAF 机制依然是准确率最稳的选择之一。

2.2 OpenCV 的 Mat/numpy 与 OpenPose 输入之间的“最后一公里”

OpenCV 读出来的视频帧是一个 numpy 数组,形状一般是(height, width, 3),通道顺序是 BGR。而绝大多数深度学习框架(包括 OpenPose 的 Caffe 模型)期望输入 RGB 顺序的浮点张量,形状是(1, 3, height, width),并且像素值要归一化到 0 到 1 之间,还要减去一个均值。这个转换链路如果写错,最典型的症状就是画面颜色发蓝或者发红,检测出来的关键点位置完全偏移。

下面这段代码就是标准的转换写法,顺序不能乱:

import cv2 import numpy as np def frame_to_net_input(frame_bgr, target_size=(368, 368)): # frame_bgr 来自 cv2.VideoCapture.read(),shape=(H, W, 3),dtype=uint8 # OpenPose 官方模型默认输入 368x368,但宽高比会被强制压缩 resized = cv2.resize(frame_bgr, target_size, interpolation=cv2.INTER_LINEAR) # BGR -> RGB,这是关键,Caffe 模型训练时用的是 RGB rgb = cv2.cvtColor(resized, cv2.COLOR_BGR2RGB) # 转 float32 并归一化到 [0, 1) rgb = rgb.astype(np.float32) / 255.0 # 减去训练时用的均值,OpenPose 官方用的均值是 [0.485, 0.456, 0.406] # 注意这里不是 BGR 的均值,必须按 RGB 顺序减 mean = np.array([0.485, 0.456, 0.406], dtype=np.float32) rgb -= mean # HWC -> CHW,并增加 batch 维度,最终 shape=(1, 3, 368, 368) chw = np.transpose(rgb, (2, 0, 1)) blob = np.expand_dims(chw, axis=0) return blob

有几个参数值得抠一下。target_size选 368 还是 656 直接影响速度和精度的平衡:368 是官方 trade-off 比较合适的值,单帧推理在 GTX 1060 上大约 40 到 60 毫秒;656 能明显提升小目标(比如远处的人)的检测率,但推理时间会翻两到三倍。mean向量的值取自 ImageNet 数据集的统计均值,不是随便拍脑袋写的——如果不做这步减均值,网络输出的热力图会出现系统性偏移,关键点位置误差可能达到几十个像素。

2.2.1 为什么 OpenCV 读帧要用cap.read()而不是cap.grab()

cv2.VideoCapture有两个读帧方法:read()grab()read()等价于grab()retrieve(),也就是先抓取帧再解码。很多人不知道的是,grab()只做传输层的帧抓取,不做解码,所以速度更快,但拿不到图像数据。在 OpenPose 这种推理耗时远大于读帧耗时的场景里,用read()就够了;只有当你要同时从多个摄像头读帧并保持同步时,才需要先grab()全部摄像头,再retrieve()逐路解码,否则一路摄像头读帧时会阻塞另一路。

cap = cv2.VideoCapture(0) # 0 代表默认摄像头 cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) cap.set(cv2.CAP_PROP_FPS, 30) while True: ret, frame = cap.read() if not ret: break # frame 在 Windows 上可能是空指针或全黑帧,ret=False 时直接跳过 cv2.imshow("raw", frame) if cv2.waitKey(1) & 0xFF == ord("q"): break cap.release() cv2.destroyAllWindows()

这里有一件容易被忽略的小事:cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)并不保证一定生效。很多 USB 摄像头固件只支持固定的分辨率档位,比如 640x480、1280x720、1920x1080,你设置一个 800x600,驱动会就近选择一个支持的分辨率。所以设置完之后最好用cap.get(cv2.CAP_PROP_FRAME_WIDTH)读回来确认一下实际值。另外,waitKey(1)的参数 1 表示等待 1 毫秒,这个值不是随便写的,它同时控制着显示刷新率和read()的调用节奏,设成 0 会导致程序卡在imshow上等按键,设得太大则会让视频明显变卡。

3. Windows 和 Linux 下用 Python 装好 OpenPose 环境并跑通单帧检测

3.1 两个绕不开的坑:OpenPose 没有官方 pip 包和 Caffe 的依赖链

直接说结论:PyPI 上没有一个官方维护的openpose包能让你pip install openpose就跑起来。网上那些能装上的是第三方编译的 wheel,版本往往滞后且不一定支持你的 Python 版本。最稳妥的路线有两种:一是使用 OpenPose 官方仓库里提供的build/python/openpose编译产物,然后手动加入sys.path;二是用openpose社区维护的 PyTorch 重实现,比如pytorch-openpose,这类实现把 Caffe 换成了 PyTorch,pip 安装过程友好得多,但它在多人检测的 PAF 后处理上做了简化,精度略低于原版 Caffe 模型。

如果你的机器有 NVIDIA GPU,我建议走官方 Caffe 版,因为 OpenPose 原版对 CUDA 的优化最到位。但要注意,官方仓库对 CUDA 版本有硬性要求——CUDA 10.0 以下的旧版本不支持,CUDA 10.2 和 11.x 需要对应不同的编译选项。如果你是纯 CPU 环境,也不是不能跑,只是要把模型换成openpose_quantized_nchw.pb这类 TensorFlow 量化模型,或者干脆用openvino版的 OpenPose IR 模型,在 Intel CPU 上能跑到 15 FPS 左右。

3.1.1 最快跑通官方版的环境准备步骤(Linux)

下面的步骤假设你用的是 Ubuntu 20.04 以上的系统,GPU 驱动已装好,CUDA 11.1 和 cuDNN 8.0 已配置到/usr/local/cuda

# 1. 安装 CMake 和 OpenCV 开发库,注意 OpenPose 会自己拉 OpenCV 依赖 sudo apt-get update sudo apt-get install -y cmake git libopencv-dev python3-dev # 2. 克隆并编译 OpenPose git clone https://github.com/CMU-Perceptual-Computing-Lab/openpose.git cd openpose mkdir build && cd build cmake .. \ -DBUILD_PYTHON_API=ON \ -DPYTHON_EXECUTABLE=$(which python3) \ -DGPU_MODE=CUDA \ -DCUDA_TOOLKIT_ROOT_DIR=/usr/local/cuda make -j$(nproc) # 3. 下载模型权重(这一步经常因为网络问题失败,多试几次或用代理) cd .. python3 -m openpose.models.download_models --paf # 只下 BODY_25 模型

编译产物的核心是build/python/openpose/目录下一个叫pyopenpose.cpython-*.so的共享库。Python 程序要加载它,必须把这个目录加入sys.path,并且系统要能找到 OpenCV 的.so文件——如果运行时报libopencv_core.so.4.5: cannot open shared object file,就执行export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH

参数说明:-DGPU_MODE=CUDA是让 OpenPose 使用 CUDA 而非 OpenCL 的关键选项,OpenCL 模式在 AMD 和 Intel 显卡上的性能非常差;-DPYTHON_EXECUTABLE必须指定你实际运行 Python 的那个解释器路径,否则它可能默认找 Python 2 或者系统自带的另一个 Python。编译时间大约 10 到 20 分钟,取决于机器核数,期间如果报CMake Error: The following variables are used in this project, but set to NOTFOUND,基本都是 CUDA 路径写错。

3.2 用 Python 初始化 OpenPose 并跑第一帧推理

初始化 OpenPose 的方式和初始化 OpenCV 的人脸检测器有点像,都需要指定模型路径和参数,只不过 OpenPose 的参数是一个dict。下面这段代码是完整的单帧检测流程:

import sys import cv2 import numpy as np # 把编译好的 python 接口目录加入搜索路径 sys.path.append('/path/to/openpose/build/python/openpose') try: import pyopenpose as op except ImportError: raise RuntimeError("pyopenpose 导入失败,请检查 build/python/openpose 路径") # 初始化 OpenPose 的字典参数 params = dict() params["model_folder"] = "/path/to/openpose/models" params["model_pose"] = "BODY_25" # 可选 BODY_25 / COCO / MPI params["net_resolution"] = "368x368" # 输入分辨率,宽x高,注意顺序 params["number_people_max"] = 3 # 最多检测多少人,限制后速度更快 params["hand"] = False # 不检测手部关键点 params["face"] = False # 不检测面部关键点 # 创建 OpenPose 实例 op_wrapper = op.WrapperPython() op_wrapper.configure(params) op_wrapper.start() # 读一张测试图 frame = cv2.imread("test_person.jpg") if frame is None: raise FileNotFoundError("测试图片不存在") # 推理,datum 是输出的数据载体 datum = op.Datum() datum.cvInputData = frame op_wrapper.emplaceAndPop([datum]) # 输出关键点数组 shape=(人数, 25, 3),第三维为 x, y, confidence keypoints = datum.poseKeypoints if keypoints.size > 0: print("检测到 {} 个人".format(keypoints.shape[0])) # 第一行第一个人的鼻子坐标 nose = keypoints[0, 0, :2] conf = keypoints[0, 0, 2] print("鼻子坐标: ({:.1f}, {:.1f}), 置信度: {:.2f}".format(nose[0], nose[1], conf)) # 显示带骨架的渲染结果 result = datum.cvOutputData cv2.imshow("OpenPose", result) cv2.waitKey(0) cv2.destroyAllWindows()

net_resolution这里写成"368x368",注意不是"368,368",逗号分隔会直接报错。number_people_max限制的是网络后续匹配阶段的最大人数,设成 3 后 PAF 匹配的搜索空间变小,推理速度能提升 20% 左右,缺点是一旦画面里出现第 4 个人,第 4 个人会被跳过不输出。poseKeypoints在没检测到人时不是None而是一个空数组shape=(0,),所以判断要用size > 0而不要用is not None——这也是官方样例代码里一个容易误导初学者的地方。

4. 把关键点变成人体形态:角度计算和动作判定逻辑

4.1 用平面几何算关节角度:向量夹角公式的工程实现

有了 18 或 25 个关键点之后,“人体形态识别”真正的工作才开始。所谓形态,本质上是一组关节角度——肘关节弯曲了多少度、躯干前倾了多少度、膝盖是否伸直。判断这些角度的数学工具就是向量点积和反余弦,这么简单的东西之所以值得专门写一节,是因为坐标系的 y 轴向下,导致很多人算出来的角度是补角,明明是弯着的胳膊却显示 170 度。

import numpy as np def calc_angle(p1, p2, p3): """ 计算以 p2 为顶点,p1-p2 和 p3-p2 两条边形成的夹角 p1, p2, p3 都是 [x, y] 格式的坐标,坐标系 y 轴向下 返回角度值,范围 [0, 180] """ # 两条边的方向向量 v1 = np.array([p1[0] - p2[0], p1[1] - p2[1]], dtype=np.float32) v2 = np.array([p3[0] - p2[0], p3[1] - p2[1]], dtype=np.float32) # 向量模长 len1 = np.linalg.norm(v1) len2 = np.linalg.norm(v2) # 防御:某个关键点缺失或坐标重合时返回 0 if len1 < 1e-6 or len2 < 1e-6: return 0.0 # cos(夹角) = v1 · v2 / (|v1| * |v2|) cos_value = np.dot(v1, v2) / (len1 * len2) # 数值稳定性:cos_value 可能因为浮点误差超出 [-1, 1] cos_value = np.clip(cos_value, -1.0, 1.0) # arccos 返回弧度,转角度 angle = np.degrees(np.arccos(cos_value)) return angle

np.clip(cos_value, -1.0, 1.0)这行不是多余的。由于关键点坐标是浮点数计算的,两个几乎重合的向量在做点积和模长除法时,结果可能算出1.0000001这种值,直接传给np.arccos会返回nan,然后整个角度链条全部被污染。这个 bug 在常规测试里几乎碰不到,但一旦摄像头画面里出现手臂完全伸直的情况,就会偶发触发。

4.2 一个可落地的“站姿异常检测”规则引擎

下面是一个完整的姿态判定代码,判定的规则是:躯干倾角超过 25 度视为“前倾”,左膝角小于 160 度视为“屈膝”。这套规则看起来简单,但实际项目里做大分类(正常/异常)已经够用,原因是 OpenPose 本身存在 5 到 10 像素的坐标抖动,用连续角度值做精细分类反而容易误判,离散化成几个区间才稳定。

def analyze_posture(keypoints, conf_threshold=0.4): """ 输入:单个人的关键点数组 shape=(25, 3) 输出:姿态判定结果 dict """ # BODY_25 的关键点序号映射 left_hip, right_hip = 9, 12 left_knee, right_knee = 10, 13 left_ankle, right_ankle = 11, 14 neck = 1 mid_hip = 8 def kpt(idx): # 返回 [x, y],如果置信度低于阈值则返回 None if keypoints[idx, 2] < conf_threshold: return None return keypoints[idx, :2] # 躯干倾角:颈部到骨盆中点连线与竖直方向的夹角 neck_p = kpt(neck) hip_center = kpt(mid_hip) trunk_angle = 0.0 if neck_p is not None and hip_center is not None: # 竖直向下方向向量为 (0, 1) dx = neck_p[0] - hip_center[0] dy = neck_p[1] - hip_center[1] # 计算与竖直方向的夹角:atan2(|dx|, dy) 转角度 trunk_angle = np.degrees(np.arctan2(abs(dx), max(dy, 1e-6))) # 左膝角:左髋 - 左膝 - 左踝 left_knee_angle = 0.0 hip_p = kpt(left_hip) knee_p = kpt(left_knee) ankle_p = kpt(left_ankle) if hip_p is not None and knee_p is not None and ankle_p is not None: left_knee_angle = calc_angle(hip_p, knee_p, ankle_p) # 判定规则 status = "normal" if trunk_angle > 25: status = "leaning_forward" if left_knee_angle < 160 and status == "normal": status = "squatting" return { "status": status, "trunk_angle": trunk_angle, "left_knee_angle": left_knee_angle, }

注意mid_hip = 8这个序号在 BODY_25 模型里对应的是骨盆中心点,而 COCO 模型没有这个点,所以如果你用的是 COCO 模型,这段逻辑就要改成用左右髋部的中点代替。arctan2算躯干角度时,max(dy, 1e-6)是为了防止人和摄像头完全水平时dy=0导致除零错误。

4.3 把这个逻辑串到视频流里:带 FPS 监控的完整代码

import cv2 import numpy as np import time import sys sys.path.append('/path/to/openpose/build/python/openpose') import pyopenpose as op params = dict() params["model_folder"] = "/path/to/openpose/models" params["net_resolution"] = "368x368" params["number_people_max"] = 1 op_wrapper = op.WrapperPython() op_wrapper.configure(params) op_wrapper.start() # 支持两个输入源:摄像头和视频文件 input_source = 0 # 0 是摄像头,也可以改成 "test_video.mp4" cap = cv2.VideoCapture(input_source) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) fps_history = [] while True: start_time = time.time() ret, frame = cap.read() if not ret: break # 推理 datum = op.Datum() datum.cvInputData = frame op_wrapper.emplaceAndPop([datum]) # 绘制姿态判定结果 result = datum.cvOutputData.copy() if datum.poseKeypoints.size > 0: pose = analyze_posture(datum.poseKeypoints[0]) status_text = { "normal": "NORMAL", "leaning_forward": "LEANING", "squatting": "SQUAT" }.get(pose["status"], "UNKNOWN") cv2.putText(result, "Status: " + status_text, (20, 40), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 255, 0), 2) # 计算并显示 FPS elapsed_ms = (time.time() - start_time) * 1000 fps = 1000.0 / max(elapsed_ms, 0.001) fps_history.append(fps) if len(fps_history) > 30: fps_history.pop(0) avg_fps = sum(fps_history) / len(fps_history) cv2.putText(result, "FPS: {:.1f}".format(avg_fps), (20, 80), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 255), 2) cv2.imshow("Posture Analysis", result) if cv2.waitKey(1) & 0xFF == ord("q"): break cap.release() cv2.destroyAllWindows()

这段代码里fps_history做了一个简单的时间窗口平均,而不是用瞬时 FPS,因为 OpenPose 单帧耗时会上下波动,瞬时值会让人误判系统性能。avg_fps如果你要做日志输出,注意它统计的是整个循环的耗时,包括读帧和绘制,不是纯推理耗时——如果你只关心模型速度,需要在emplaceAndPop前后分别打点计时。

5. 性能瓶颈和三个立竿见影的提速参数

5.1 先定位瓶颈在哪:用 OpenCV 自带的 TickMeter 做分阶段计时

很多人一上来就调 OpenPose 的参数,但实际瓶颈可能在cap.read()的解码环节——特别是用 4K 摄像头时,USB 带宽可能成为瓶颈。先用 OpenCV 自带的cv2.TickMeter做一次粗定位,比凭感觉优化靠谱得多:

timer = cv2.TickMeter() timer.start() ret, frame = cap.read() timer.stop() print("read cost: {:.1f} ms".format(timer.getTimeMilli())) timer.reset() timer.start() datum = op.Datum() datum.cvInputData = frame op_wrapper.emplaceAndPop([datum]) timer.stop() print("openpose infer: {:.1f} ms".format(timer.getTimeMilli()))

在常见的 i5 + GTX 1660 配置上,如果read耗时超过 20 毫秒,问题基本在摄像头分辨率设置过高或驱动不吃 OpenCV 的CAP_PROP_FRAME_WIDTH设置;如果infer耗时占到 80% 以上,才值得去调 OpenPose 的参数。

5.2 三个必调的 OpenPose 参数和它们的权衡

第一个是net_resolution。从"368x368"降到"320x240",推理速度大约提升 40%,但小目标检测率明显下降——如果你的人离摄像头超过 4 米,关键点置信度会从 0.75 掉到 0.4 以下。第二个是number_people_max。在单人场景下把它从默认值 -1(不限人数)改成 1,PAF 匹配阶段的计算量会大幅下降,这是性价比最高的参数。第三个是resolution(输出分辨率)。这个参数控制的是输出图像的大小,不影响推理速度,只影响datum.cvOutputData的尺寸;如果你只需要关键点数据不需要画骨架图,把它设成"-1x-1"可以跳过输出图像渲染,省掉那一份内存拷贝和缩放时间。

params["net_resolution"] = "320x240" params["number_people_max"] = 1 params["resolution"] = "-1x-1" # 不渲染输出图,速度再快一截

还有一个容易被忽略的选项是"disable_multi_thread"。OpenPose 默认会为每路视频流开多个线程做 PAF 和热力图的并行后处理,如果你只跑单路视频,这个多线程反而会引入线程切换开销。把它设为True能让单路推理快 5% 到 10%,但代价是同时处理多路视频时帧率下降明显。

5.3 CPU 跑不动时的替代方案:降分辨率配合隔帧检测

当你在没有 GPU 的机器上运行,单帧推理可能需要 1 到 2 秒,这时候强行让程序跑 30 FPS 没有意义。常见做法是“隔帧推理”:视频流每 5 帧只推理 1 帧,其余 4 帧直接复用上一帧的骨架结果做绘制。这样 FPS 显示是 30,但骨架刷新率只有 6 FPS。对动作判定来说,6 FPS 足够覆盖绝大多数人体动作——人最快的手部动作频率也就 5 到 8 Hz,而躯干动作通常不超过 3 Hz。

frame_count = 0 infer_every_n_frames = 5 stored_keypoints = None while True: ret, frame = cap.read() if not ret: break if frame_count % infer_every_n_frames == 0: datum = op.Datum() datum.cvInputData = frame op_wrapper.emplaceAndPop([datum]) stored_keypoints = datum.poseKeypoints.copy() frame_count += 1 # 用 stored_keypoints 做绘制,而不是当前帧直接推理的结果 if stored_keypoints is not None and stored_keypoints.size > 0: # 绘制逻辑略,和之前相同 pass

这里要注意一个细节:datum.poseKeypoints在下一轮推理时会被复用和覆盖,所以必须用.copy()存下来,否则隔帧引用时会拿到空数组或者指向最新推理结果的引用。这个 bug 很像 C/C++ 里的悬垂指针,排查起来相当隐蔽。

5.3.1 OpenCV 拉流中断的实用排查方法

摄像头跑久了偶发黑屏或ret=False,大概率不是 OpenPose 的问题,而是 OpenCV 读取 USB 摄像头时驱动缓冲溢出。常见做法是连续三次读到ret=False就执行cap.release()再重新cap.open(0),这是我在实际项目中用下来最有效的恢复手段。另一个办法是调大cv2.CAP_PROP_BUFFERSIZE,默认值在某些驱动下只有 1,改成 4 能缓解帧丢失,但会引入额外延迟,实时交互场景要慎用。

frame_fail_count = 0 while True: ret, frame = cap.read() if not ret: frame_fail_count += 1 if frame_fail_count >= 3: cap.release() cap.open(0) # 重新打开摄像头 frame_fail_count = 0 continue frame_fail_count = 0 # 正常推理流程

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询