基于OpenCV 的人体姿态估计

饭宝  论坛元老 | 2025-5-26 22:52:22 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 1880|帖子 1880|积分 5640

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x


这是一个基于 OpenCV 的人体姿态估计系统,可以或许从摄像头视频流中实时检测人体关键点,并通过简化算法重建 3D 姿态,最后在 3D 空间中进行仿真展示。系统主要包含 2D 姿态检测、3D 姿态重建和 3D 仿真三个焦点模块。

模块导入与环境准备

python
运行
  1. import cv2
  2. import numpy as np
  3. import os
  4. import time
  5. import matplotlib.pyplot as plt
  6. from mpl_toolkits.mplot3d import Axes3D
  7. # 确保目录存在
  8. os.makedirs("results/2d_poses", exist_ok=True)
  9. os.makedirs("results/3d_poses", exist_ok=True)
  10. os.makedirs("results/simulations", exist_ok=True)
复制代码



  • 导入须要的库:计算机视觉 (cv2)、数值计算 (numpy)、文件操纵 (os)、时间丈量 (time) 和画图工具 (matplotlib)
  • 创建结果生存目次,exist_ok=True 确保目次存在时不会报错
常量定义

python
运行
  1. JOINT_CONNECTIONS = [
  2.     (0, 1), (0, 4), (1, 2), (2, 3), (4, 5), (5, 6), (6, 7),  # 头部
  3.     (0, 11), (0, 12), (11, 12),  # 躯干
  4.     (11, 13), (13, 15), (15, 17), (17, 19), (19, 21),  # 左臂
  5.     (12, 14), (14, 16), (16, 18), (18, 20), (20, 22),  # 右臂
  6.     (11, 23), (12, 24), (23, 24),  # 骨盆
  7.     (23, 25), (25, 27), (27, 29), (29, 31),  # 左腿
  8.     (24, 26), (26, 28), (28, 30), (30, 32)  # 右腿
  9. ]
复制代码



  • 定义 33 个人体关键点的毗连关系,用于后续绘制骨架
2D 姿态估计类

python
运行
  1. class HumanPoseEstimator:
  2.     def __init__(self):
  3.         """初始化OpenCV人体姿态估计器"""
  4.         # 使用OpenCV的DNN模块加载预训练的姿态估计模型
  5.         self.proto_file = "pose_deploy_linevec_faster_4_stages.prototxt"
  6.         self.weights_file = "pose_iter_160000.caffemodel"
  7.         self.n_points = 18
  8.         
  9.         # 检查模型文件是否存在
  10.         if not os.path.exists(self.proto_file) or not os.path.exists(self.weights_file):
  11.             print("警告: 找不到OpenCV姿态估计模型文件")
  12.             print("请从https://github.com/CMU-Perceptual-Computing-Lab/openpose下载模型文件")
  13.             self.net = None
  14.         else:
  15.             self.net = cv2.dnn.readNetFromCaffe(self.proto_file, self.weights_file)
  16.             
  17.         # 定义COCO人体关键点映射到33点格式
  18.         self.coco_to_mp = {
  19.             0: 0,    # 鼻子
  20.             1: 1,    # 脖子
  21.             2: 12,   # 右肩
  22.             3: 14,   # 右肘
  23.             4: 16,   # 右腕
  24.             5: 11,   # 左肩
  25.             6: 13,   # 左肘
  26.             7: 15,   # 左腕
  27.             8: 24,   # 右髋
  28.             9: 26,   # 右膝
  29.             10: 28,  # 右踝
  30.             11: 23,  # 左髋
  31.             12: 25,  # 左膝
  32.             13: 27,  # 左踝
  33.             14: 5,   # 右眼
  34.             15: 2,   # 左眼
  35.             16: 7,   # 右耳
  36.             17: 4    # 左耳
  37.         }
复制代码



  • 类初始化:加载 OpenCV 预训练的 Caffe 模型
  • 关键点映射表:将 COCO 数据集的 18 个关键点映射到 MediaPipe 的 33 点格式

python
运行
  1.     def detect_keypoints(self, image):
  2.         """
  3.         从图像中检测人体关键点
  4.         
  5.         返回:
  6.             keypoints_2d: 二维关键点坐标 [33, 3] (x, y, confidence)
  7.             annotated_image: 标注后的图像
  8.         """
  9.         if self.net is None:
  10.             print("错误: 姿态估计模型未正确加载")
  11.             return None, image
  12.             
  13.         # 准备输入
  14.         blob = cv2.dnn.blobFromImage(
  15.             image, 1.0 / 255, (368, 368), (0, 0, 0), swapRB=False, crop=False
  16.         )
  17.         self.net.setInput(blob)
  18.         
  19.         # 前向传播
  20.         output = self.net.forward()
  21.         
  22.         # 获取图像尺寸
  23.         h, w = image.shape[:2]
  24.         
  25.         # 初始化33个关键点的数组
  26.         keypoints_2d = np.zeros((33, 3))
  27.         
  28.         # 处理检测结果
  29.         points = []
  30.         for i in range(self.n_points):
  31.             # 查找关键点的置信度图
  32.             prob_map = output[0, i, :, :]
  33.             min_val, prob, min_loc, point = cv2.minMaxLoc(prob_map)
  34.             
  35.             # 缩放坐标
  36.             x = (w * point[0]) / output.shape[3]
  37.             y = (h * point[1]) / output.shape[2]
  38.             
  39.             if prob > 0.1:  # 置信度阈值
  40.                 points.append((int(x), int(y)))
  41.                
  42.                 # 映射到33点格式
  43.                 if i in self.coco_to_mp:
  44.                     mp_idx = self.coco_to_mp[i]
  45.                     keypoints_2d[mp_idx] = [x / w, y / h, prob]
  46.             else:
  47.                 points.append(None)
  48.         
  49.         # 可视化关键点
  50.         annotated_image = image.copy()
  51.         for i, p in enumerate(points):
  52.             if p is not None:
  53.                 cv2.circle(annotated_image, p, 8, (0, 255, 255), thickness=-1, lineType=cv2.FILLED)
  54.                 cv2.putText(annotated_image, f"{i}", p, cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, lineType=cv2.LINE_AA)
  55.         
  56.         # 绘制骨架连接
  57.         skeleton_pairs = [
  58.             (1, 2), (1, 5), (2, 3), (3, 4), (5, 6), (6, 7),
  59.             (1, 8), (8, 9), (9, 10), (1, 11), (11, 12), (12, 13),
  60.             (1, 0), (0, 14), (14, 16), (0, 15), (15, 17)
  61.         ]
  62.         
  63.         for pair in skeleton_pairs:
  64.             part_a, part_b = pair
  65.             if points[part_a] and points[part_b]:
  66.                 cv2.line(annotated_image, points[part_a], points[part_b], (0, 255, 0), 2)
  67.         
  68.         return keypoints_2d, annotated_image
复制代码



  • 图像预处置处罚:将输入图像转换为网络可担当的格式 (368x368)
  • 模型推理:通过前向传播获取关键点的置信度图
  • 后处置处罚:从置信度图中提取关键点坐标,应用阈值过滤低置信度点
  • 可视化:在原图上绘制关键点和骨架毗连,返回标准化的关键点坐标和可视化后的图像
3D 姿态估计类

python
运行
  1. class Simple3DPoseEstimator:
  2.     def __init__(self):
  3.         """简单的3D姿态估计器,使用固定比例关系"""
  4.         # 定义人体各部分的平均比例(单位:米)
  5.         self.body_proportions = {
  6.             "head": 0.25,
  7.             "torso": 0.5,
  8.             "upper_arm": 0.3,
  9.             "forearm": 0.25,
  10.             "hand": 0.1,
  11.             "upper_leg": 0.5,
  12.             "lower_leg": 0.5,
  13.             "foot": 0.2
  14.         }
  15.         
  16.         # 用于可视化
  17.         self.fig = plt.figure(figsize=(10, 8))
  18.         self.ax = self.fig.add_subplot(111, projection='3d')
复制代码



  • 初始化:定义人体各部门的标准比例(单位:米)
  • 创建 3D 画图环境用于可视化 3D 姿态

python
运行
  1.     def estimate_3d_pose(self, keypoints_2d, image_shape, visualize=False):
  2.         """
  3.         简单估计3D姿态
  4.         
  5.         参数:
  6.             keypoints_2d: 二维关键点 [33, 3]
  7.             image_shape: 图像形状 (h, w)
  8.             visualize: 是否可视化3D姿态
  9.             
  10.         返回:
  11.             keypoints_3d: 3D关键点 numpy数组 [33, 3]
  12.         """
  13.         if keypoints_2d is None:
  14.             return None
  15.             
  16.         h, w = image_shape[:2]
  17.         
  18.         # 创建3D关键点数组
  19.         keypoints_3d = np.zeros((33, 3))
  20.         
  21.         # 提取有效关键点
  22.         valid_mask = keypoints_2d[:, 2] > 0.3
  23.         if not np.any(valid_mask):
  24.             return None
  25.             
  26.         # 将2D坐标转换为图像坐标系
  27.         kp_2d_img = keypoints_2d.copy()
  28.         kp_2d_img[:, 0] *= w
  29.         kp_2d_img[:, 1] *= h
  30.         
  31.         # 计算人体中心
  32.         center = np.mean(kp_2d_img[valid_mask, :2], axis=0)
  33.         
  34.         # 估计人体尺寸
  35.         # 这里简化为使用肩宽作为参考
  36.         if valid_mask[11] and valid_mask[12]:  # 左右肩
  37.             shoulder_width = np.linalg.norm(kp_2d_img[11, :2] - kp_2d_img[12, :2])
  38.             scale = 0.4 / shoulder_width  # 假设平均肩宽为0.4米
  39.         else:
  40.             scale = 0.001  # 默认缩放比例
  41.             
  42.         # 基于2D关键点和人体比例估计3D位置
  43.         # 这里使用简化模型,主要基于深度感知和人体比例
  44.         for i in range(33):
  45.             if valid_mask[i]:
  46.                 x, y = kp_2d_img[i, :2]
  47.                
  48.                 # 计算相对中心的位置
  49.                 rel_x = (x - center[0]) * scale
  50.                 rel_y = (y - center[1]) * scale
  51.                
  52.                 # 估计深度(z轴)
  53.                 # 这里使用简化方法:离图像中心越远的点假设越远
  54.                 depth_factor = np.sqrt(rel_x**2 + rel_y**2) / max(w, h) * 0.5
  55.                
  56.                 # 设置3D坐标
  57.                 keypoints_3d[i] = [rel_x, rel_y, depth_factor]
  58.         
  59.         # 可视化
  60.         if visualize:
  61.             self.visualize_3d_pose(keypoints_3d)
  62.             
  63.         return keypoints_3d
复制代码



  • 3D 姿态估计:基于 2D 关键点和人体比例关系计算 3D 坐标
  • 坐标缩放:利用肩宽作为参考来估计人体尺寸比例
  • 深度估计:利用离图像中心的间隔来大略估计深度信息(z 轴)

python
运行
  1.     def visualize_3d_pose(self, keypoints_3d, frame_id=None):
  2.         """可视化3D姿态"""
  3.         self.ax.clear()
  4.         
  5.         # 设置坐标轴范围
  6.         max_range = np.max(np.abs(keypoints_3d))
  7.         self.ax.set_xlim(-max_range, max_range)
  8.         self.ax.set_ylim(-max_range, max_range)
  9.         self.ax.set_zlim(-max_range, max_range)
  10.         
  11.         # 设置坐标轴标签
  12.         self.ax.set_xlabel('X')
  13.         self.ax.set_ylabel('Y')
  14.         self.ax.set_zlabel('Z')
  15.         
  16.         # 绘制关键点
  17.         self.ax.scatter(keypoints_3d[:, 0], keypoints_3d[:, 1], keypoints_3d[:, 2], c='r', s=50)
  18.         
  19.         # 绘制连接关系
  20.         for connection in JOINT_CONNECTIONS:
  21.             start_idx, end_idx = connection
  22.             if start_idx < len(keypoints_3d) and end_idx < len(keypoints_3d):
  23.                 self.ax.plot(
  24.                     [keypoints_3d[start_idx, 0], keypoints_3d[end_idx, 0]],
  25.                     [keypoints_3d[start_idx, 1], keypoints_3d[end_idx, 1]],
  26.                     [keypoints_3d[start_idx, 2], keypoints_3d[end_idx, 2]],
  27.                     c='b', linewidth=2
  28.                 )
  29.         
  30.         # 设置视角
  31.         self.ax.view_init(elev=-90, azim=90)  # 俯视视角
  32.         
  33.         # 保存图像
  34.         if frame_id is not None:
  35.             plt.savefig(f"results/3d_poses/3d_pose_frame_{frame_id}.png", dpi=300, bbox_inches='tight')
  36.         else:
  37.             plt.pause(0.01)
复制代码



  • 3D 姿态可视化:在 3D 空间中绘制关键点和骨架毗连
  • 视角设置:默认利用俯视视角 (-90 度仰角,90 度方位角)
  • 图像生存:根据需要生存 3D 姿态图像
3D 仿真器类

python
运行
  1. class SimpleSimulator:
  2.     def __init__(self, use_gui=True):
  3.         """简单的3D仿真器,使用matplotlib进行可视化"""
  4.         self.use_gui = use_gui
  5.         
  6.         # 用于可视化
  7.         self.fig = plt.figure(figsize=(10, 8))
  8.         self.ax = self.fig.add_subplot(111, projection='3d')
  9.         
  10.         # 设置固定的相机位置
  11.         self.ax.set_xlim(-1.5, 1.5)
  12.         self.ax.set_ylim(-1.5, 1.5)
  13.         self.ax.set_zlim(0, 2)
  14.         
  15.         self.ax.set_xlabel('X')
  16.         self.ax.set_ylabel('Y')
  17.         self.ax.set_zlabel('Z')
  18.         
  19.         # 绘制地面
  20.         x = np.linspace(-1.5, 1.5, 100)
  21.         y = np.linspace(-1.5, 1.5, 100)
  22.         X, Y = np.meshgrid(x, y)
  23.         Z = np.zeros_like(X)
  24.         self.ax.plot_surface(X, Y, Z, alpha=0.3, color='g')
  25.         
  26.         print("使用简单的3D可视化模拟器")
复制代码



  • 初始化:创建 3D 画图环境和固定巨细的场景
  • 绘制地面平面:利用绿色半透明平面表现地面

python
运行
  1.     def update_pose(self, keypoints_3d):
  2.         """
  3.         根据3D姿态更新仿真模型
  4.         
  5.         参数:
  6.             keypoints_3d: 3D关键点 [33, 3]
  7.         """
  8.         if keypoints_3d is None:
  9.             return
  10.             
  11.         self.ax.clear()
  12.         
  13.         # 设置坐标轴范围
  14.         self.ax.set_xlim(-1.5, 1.5)
  15.         self.ax.set_ylim(-1.5, 1.5)
  16.         self.ax.set_zlim(0, 2)
  17.         
  18.         # 设置坐标轴标签
  19.         self.ax.set_xlabel('X')
  20.         self.ax.set_ylabel('Y')
  21.         self.ax.set_zlabel('Z')
  22.         
  23.         # 绘制地面
  24.         x = np.linspace(-1.5, 1.5, 100)
  25.         y = np.linspace(-1.5, 1.5, 100)
  26.         X, Y = np.meshgrid(x, y)
  27.         Z = np.zeros_like(X)
  28.         self.ax.plot_surface(X, Y, Z, alpha=0.3, color='g')
  29.         
  30.         # 绘制关键点
  31.         self.ax.scatter(keypoints_3d[:, 0], keypoints_3d[:, 1], keypoints_3d[:, 2], c='r', s=50)
  32.         
  33.         # 绘制连接关系
  34.         for connection in JOINT_CONNECTIONS:
  35.             start_idx, end_idx = connection
  36.             if start_idx < len(keypoints_3d) and end_idx < len(keypoints_3d):
  37.                 self.ax.plot(
  38.                     [keypoints_3d[start_idx, 0], keypoints_3d[end_idx, 0]],
  39.                     [keypoints_3d[start_idx, 1], keypoints_3d[end_idx, 1]],
  40.                     [keypoints_3d[start_idx, 2], keypoints_3d[end_idx, 2]],
  41.                     c='b', linewidth=2
  42.                 )
  43.         
  44.         # 设置视角
  45.         self.ax.view_init(elev=30, azim=45)  # 侧视视角
  46.         
  47.         if self.use_gui:
  48.             plt.pause(0.01)
  49.    
  50.     def render_scene(self, frame_id):
  51.         """
  52.         渲染当前场景并保存
  53.         
  54.         参数:
  55.             frame_id: 帧ID
  56.         """
  57.         plt.savefig(f"results/simulations/simulation_frame_{frame_id}.png", dpi=300, bbox_inches='tight')
复制代码



  • 更新姿态:根据新的 3D 关键点数据更新场景
  • 固定视角:利用侧视视角 (30 度仰角,45 度方位角)
  • 场景渲染:将当前场景生存为图像
主函数

python
运行
  1. def main(camera_id=0, use_gui=True):
  2.     """
  3.     完整流程:从摄像头读取到3D仿真
  4.    
  5.     参数:
  6.         camera_id: 摄像头ID,0表示默认摄像头
  7.         use_gui: 是否使用GUI模式
  8.     """
  9.     # 1. 初始化模块
  10.     pose_estimator = HumanPoseEstimator()
  11.     pose_3d_estimator = Simple3DPoseEstimator()
  12.     simulator = SimpleSimulator(use_gui=use_gui)
  13.    
  14.     # 2. 打开摄像头
  15.     cap = cv2.VideoCapture(camera_id)
  16.    
  17.     # 检查摄像头是否成功打开
  18.     if not cap.isOpened():
  19.         print(f"无法打开摄像头 {camera_id}")
  20.         return
  21.    
  22.     # 获取摄像头信息
  23.     fps = cap.get(cv2.CAP_PROP_FPS)
  24.     width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
  25.     height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
  26.     print(f"摄像头参数: {width}x{height}, 帧率: {fps}")
  27.    
  28.     # 创建窗口
  29.     cv2.namedWindow("2D Pose Estimation", cv2.WINDOW_NORMAL)
  30.     cv2.resizeWindow("2D Pose Estimation", 800, 600)
  31.    
  32.     frame_id = 0
  33.    
  34.     # 3. 处理摄像头帧
  35.     while True:
  36.         ret, frame = cap.read()
  37.         if not ret:
  38.             print("无法获取帧,退出...")
  39.             break
  40.             
  41.         # 翻转帧,使其成为镜像效果
  42.         frame = cv2.flip(frame, 1)
  43.         
  44.         print(f"处理第{frame_id}帧...")
  45.         
  46.         # 3.1 2D姿态识别
  47.         start_time = time.time()
  48.         keypoints_2d, vis_frame = pose_estimator.detect_keypoints(frame)
  49.         
  50.         # 显示2D姿态结果
  51.         cv2.imshow("2D Pose Estimation", vis_frame)
  52.         
  53.         # 保存2D姿态结果
  54.         cv2.imwrite(f"results/2d_poses/2d_pose_frame_{frame_id}.png", vis_frame)
  55.         
  56.         # 3.2 3D姿态重建
  57.         keypoints_3d = pose_3d_estimator.estimate_3d_pose(
  58.             keypoints_2d, frame.shape, visualize=False
  59.         )
  60.         
  61.         # 可视化3D姿态
  62.         if keypoints_3d is not None:
  63.             pose_3d_estimator.visualize_3d_pose(keypoints_3d, frame_id)
  64.         
  65.         # 3.3 更新3D仿真
  66.         simulator.update_pose(keypoints_3d)
  67.         
  68.         # 3.4 渲染场景
  69.         simulator.render_scene(frame_id)
  70.         
  71.         # 计算处理时间
  72.         process_time = time.time() - start_time
  73.         print(f"处理时间: {process_time:.3f}秒")
  74.         
  75.         frame_id += 1
  76.         
  77.         # 按ESC键退出
  78.         key = cv2.waitKey(1)
  79.         if key == 27:  # ESC键
  80.             break
  81.    
  82.     # 4. 释放资源
  83.     cap.release()
  84.     cv2.destroyAllWindows()
  85.     print(f"处理完成,共{frame_id}帧,结果保存在results目录")
复制代码



  • 初始化所有模块:2D 姿态估计器、3D 姿态估计器和 3D 仿真器
  • 打开摄像头并获取视频流参数
  • 主循环处置处罚每一帧:

    • 读取摄像头帧并翻转
    • 进行 2D 姿态检测
    • 基于 2D 结果进行 3D 姿态重建
    • 更新 3D 仿真场景
    • 生存所有处置处罚结果
    • 计算处置处罚时间

  • 资源释放:关闭摄像头和窗口
程序入口

python
运行
  1. if __name__ == "__main__":
  2.     # 运行主程序
  3.     main(
  4.         camera_id=0,  # 摄像头ID,0表示默认摄像头
  5.         use_gui=True  # 是否使用GUI模式
  6.     )
复制代码



  • 程序入口点,调用 main 函数启动整个系统
  • 可以通过修改参数来调解系统举动
总结

这段代码实现了一个完整的人体姿态估计和 3D 仿真系统,主要特点包括:


  • 利用 OpenCV 预训练模型进行 2D 姿态检测
  • 基于人体比例关系的简化 3D 姿态重建方法
  • 利用 matplotlib 进行 3D 姿态可视化和仿真
  • 实时处置处罚摄像头视频流
  • 生存所有处置处罚结果到指定目次

该系统可以用于姿势分析、运动跟踪、人机交互等多种应用场景,并且提供了精良的扩展性,可以根据需求进一步优化 3D 姿态估计算法或添加更多功能。

完整代码

  1. import cv2
  2. import numpy as np
  3. import os
  4. import time
  5. import matplotlib.pyplot as plt
  6. from mpl_toolkits.mplot3d import Axes3D
  7. # 确保目录存在
  8. os.makedirs("results/2d_poses", exist_ok=True)
  9. os.makedirs("results/3d_poses", exist_ok=True)
  10. os.makedirs("results/simulations", exist_ok=True)
  11. # 定义常量JOINT_CONNECTIONS = [
  12.     (0, 1), (0, 4), (1, 2), (2, 3), (4, 5), (5, 6), (6, 7),  # 头部
  13.     (0, 11), (0, 12), (11, 12),  # 躯干
  14.     (11, 13), (13, 15), (15, 17), (17, 19), (19, 21),  # 左臂
  15.     (12, 14), (14, 16), (16, 18), (18, 20), (20, 22),  # 右臂
  16.     (11, 23), (12, 24), (23, 24),  # 骨盆
  17.     (23, 25), (25, 27), (27, 29), (29, 31),  # 左腿
  18.     (24, 26), (26, 28), (28, 30), (30, 32)  # 右腿
  19. ]
  20. class HumanPoseEstimator:
  21.     def __init__(self):
  22.         """初始化OpenCV人体姿态估计器"""
  23.         # 使用OpenCV的DNN模块加载预训练的姿态估计模型
  24.         self.proto_file = "pose_deploy_linevec_faster_4_stages.prototxt"
  25.         self.weights_file = "pose_iter_160000.caffemodel"
  26.         self.n_points = 18
  27.         
  28.         # 检查模型文件是否存在
  29.         if not os.path.exists(self.proto_file) or not os.path.exists(self.weights_file):
  30.             print("警告: 找不到OpenCV姿态估计模型文件")
  31.             print("请从https://github.com/CMU-Perceptual-Computing-Lab/openpose下载模型文件")
  32.             self.net = None
  33.         else:
  34.             self.net = cv2.dnn.readNetFromCaffe(self.proto_file, self.weights_file)
  35.             
  36.         # 定义COCO人体关键点映射到33点格式
  37.         self.coco_to_mp = {
  38.             0: 0,    # 鼻子
  39.             1: 1,    # 脖子
  40.             2: 12,   # 右肩
  41.             3: 14,   # 右肘
  42.             4: 16,   # 右腕
  43.             5: 11,   # 左肩
  44.             6: 13,   # 左肘
  45.             7: 15,   # 左腕
  46.             8: 24,   # 右髋
  47.             9: 26,   # 右膝
  48.             10: 28,  # 右踝
  49.             11: 23,  # 左髋
  50.             12: 25,  # 左膝
  51.             13: 27,  # 左踝
  52.             14: 5,   # 右眼
  53.             15: 2,   # 左眼
  54.             16: 7,   # 右耳
  55.             17: 4    # 左耳
  56.         }
  57.             def detect_keypoints(self, image):
  58.         """
  59.         从图像中检测人体关键点
  60.         
  61.         返回:
  62.             keypoints_2d: 二维关键点坐标 [33, 3] (x, y, confidence)
  63.             annotated_image: 标注后的图像
  64.         """
  65.         if self.net is None:
  66.             print("错误: 姿态估计模型未正确加载")
  67.             return None, image
  68.             
  69.         # 准备输入
  70.         blob = cv2.dnn.blobFromImage(
  71.             image, 1.0 / 255, (368, 368), (0, 0, 0), swapRB=False, crop=False
  72.         )
  73.         self.net.setInput(blob)
  74.         
  75.         # 前向传播
  76.         output = self.net.forward()
  77.         
  78.         # 获取图像尺寸
  79.         h, w = image.shape[:2]
  80.         
  81.         # 初始化33个关键点的数组
  82.         keypoints_2d = np.zeros((33, 3))
  83.         
  84.         # 处理检测结果
  85.         points = []
  86.         for i in range(self.n_points):
  87.             # 查找关键点的置信度图
  88.             prob_map = output[0, i, :, :]
  89.             min_val, prob, min_loc, point = cv2.minMaxLoc(prob_map)
  90.             
  91.             # 缩放坐标
  92.             x = (w * point[0]) / output.shape[3]
  93.             y = (h * point[1]) / output.shape[2]
  94.             
  95.             if prob > 0.1:  # 置信度阈值
  96.                 points.append((int(x), int(y)))
  97.                
  98.                 # 映射到33点格式
  99.                 if i in self.coco_to_mp:
  100.                     mp_idx = self.coco_to_mp[i]
  101.                     keypoints_2d[mp_idx] = [x / w, y / h, prob]
  102.             else:
  103.                 points.append(None)
  104.         
  105.         # 可视化关键点
  106.         annotated_image = image.copy()
  107.         for i, p in enumerate(points):
  108.             if p is not None:
  109.                 cv2.circle(annotated_image, p, 8, (0, 255, 255), thickness=-1, lineType=cv2.FILLED)
  110.                 cv2.putText(annotated_image, f"{i}", p, cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2, lineType=cv2.LINE_AA)
  111.         
  112.         # 绘制骨架连接
  113.         skeleton_pairs = [
  114.             (1, 2), (1, 5), (2, 3), (3, 4), (5, 6), (6, 7),
  115.             (1, 8), (8, 9), (9, 10), (1, 11), (11, 12), (12, 13),
  116.             (1, 0), (0, 14), (14, 16), (0, 15), (15, 17)
  117.         ]
  118.         
  119.         for pair in skeleton_pairs:
  120.             part_a, part_b = pair
  121.             if points[part_a] and points[part_b]:
  122.                 cv2.line(annotated_image, points[part_a], points[part_b], (0, 255, 0), 2)
  123.         
  124.         return keypoints_2d, annotated_image
  125. class Simple3DPoseEstimator:
  126.     def __init__(self):
  127.         """简单的3D姿态估计器,使用固定比例关系"""
  128.         # 定义人体各部分的平均比例(单位:米)
  129.         self.body_proportions = {
  130.             "head": 0.25,
  131.             "torso": 0.5,
  132.             "upper_arm": 0.3,
  133.             "forearm": 0.25,
  134.             "hand": 0.1,
  135.             "upper_leg": 0.5,
  136.             "lower_leg": 0.5,
  137.             "foot": 0.2
  138.         }
  139.         
  140.         # 用于可视化
  141.         self.fig = plt.figure(figsize=(10, 8))
  142.         self.ax = self.fig.add_subplot(111, projection='3d')
  143.         def estimate_3d_pose(self, keypoints_2d, image_shape, visualize=False):
  144.         """
  145.         简单估计3D姿态
  146.         
  147.         参数:
  148.             keypoints_2d: 二维关键点 [33, 3]
  149.             image_shape: 图像形状 (h, w)
  150.             visualize: 是否可视化3D姿态
  151.             
  152.         返回:
  153.             keypoints_3d: 3D关键点 numpy数组 [33, 3]
  154.         """
  155.         if keypoints_2d is None:
  156.             return None
  157.             
  158.         h, w = image_shape[:2]
  159.         
  160.         # 创建3D关键点数组
  161.         keypoints_3d = np.zeros((33, 3))
  162.         
  163.         # 提取有效关键点
  164.         valid_mask = keypoints_2d[:, 2] > 0.3
  165.         if not np.any(valid_mask):
  166.             return None
  167.             
  168.         # 将2D坐标转换为图像坐标系
  169.         kp_2d_img = keypoints_2d.copy()
  170.         kp_2d_img[:, 0] *= w
  171.         kp_2d_img[:, 1] *= h
  172.         
  173.         # 计算人体中心
  174.         center = np.mean(kp_2d_img[valid_mask, :2], axis=0)
  175.         
  176.         # 估计人体尺寸
  177.         # 这里简化为使用肩宽作为参考
  178.         if valid_mask[11] and valid_mask[12]:  # 左右肩
  179.             shoulder_width = np.linalg.norm(kp_2d_img[11, :2] - kp_2d_img[12, :2])
  180.             scale = 0.4 / shoulder_width  # 假设平均肩宽为0.4米
  181.         else:
  182.             scale = 0.001  # 默认缩放比例
  183.             
  184.         # 基于2D关键点和人体比例估计3D位置
  185.         # 这里使用简化模型,主要基于深度感知和人体比例
  186.         for i in range(33):
  187.             if valid_mask[i]:
  188.                 x, y = kp_2d_img[i, :2]
  189.                
  190.                 # 计算相对中心的位置
  191.                 rel_x = (x - center[0]) * scale
  192.                 rel_y = (y - center[1]) * scale
  193.                
  194.                 # 估计深度(z轴)
  195.                 # 这里使用简化方法:离图像中心越远的点假设越远
  196.                 depth_factor = np.sqrt(rel_x**2 + rel_y**2) / max(w, h) * 0.5
  197.                
  198.                 # 设置3D坐标
  199.                 keypoints_3d[i] = [rel_x, rel_y, depth_factor]
  200.         
  201.         # 可视化
  202.         if visualize:
  203.             self.visualize_3d_pose(keypoints_3d)
  204.             
  205.         return keypoints_3d
  206.         def visualize_3d_pose(self, keypoints_3d, frame_id=None):
  207.         """可视化3D姿态"""
  208.         self.ax.clear()
  209.         
  210.         # 设置坐标轴范围
  211.         max_range = np.max(np.abs(keypoints_3d))
  212.         self.ax.set_xlim(-max_range, max_range)
  213.         self.ax.set_ylim(-max_range, max_range)
  214.         self.ax.set_zlim(-max_range, max_range)
  215.         
  216.         # 设置坐标轴标签
  217.         self.ax.set_xlabel('X')
  218.         self.ax.set_ylabel('Y')
  219.         self.ax.set_zlabel('Z')
  220.         
  221.         # 绘制关键点
  222.         self.ax.scatter(keypoints_3d[:, 0], keypoints_3d[:, 1], keypoints_3d[:, 2], c='r', s=50)
  223.         
  224.         # 绘制连接关系
  225.         for connection in JOINT_CONNECTIONS:
  226.             start_idx, end_idx = connection
  227.             if start_idx < len(keypoints_3d) and end_idx < len(keypoints_3d):
  228.                 self.ax.plot(
  229.                     [keypoints_3d[start_idx, 0], keypoints_3d[end_idx, 0]],
  230.                     [keypoints_3d[start_idx, 1], keypoints_3d[end_idx, 1]],
  231.                     [keypoints_3d[start_idx, 2], keypoints_3d[end_idx, 2]],
  232.                     c='b', linewidth=2
  233.                 )
  234.         
  235.         # 设置视角
  236.         self.ax.view_init(elev=-90, azim=90)  # 俯视视角
  237.         
  238.         # 保存图像
  239.         if frame_id is not None:
  240.             plt.savefig(f"results/3d_poses/3d_pose_frame_{frame_id}.png", dpi=300, bbox_inches='tight')
  241.         else:
  242.             plt.pause(0.01)
  243. class SimpleSimulator:
  244.     def __init__(self, use_gui=True):
  245.         """简单的3D仿真器,使用matplotlib进行可视化"""
  246.         self.use_gui = use_gui
  247.         
  248.         # 用于可视化
  249.         self.fig = plt.figure(figsize=(10, 8))
  250.         self.ax = self.fig.add_subplot(111, projection='3d')
  251.         
  252.         # 设置固定的相机位置
  253.         self.ax.set_xlim(-1.5, 1.5)
  254.         self.ax.set_ylim(-1.5, 1.5)
  255.         self.ax.set_zlim(0, 2)
  256.         
  257.         self.ax.set_xlabel('X')
  258.         self.ax.set_ylabel('Y')
  259.         self.ax.set_zlabel('Z')
  260.         
  261.         # 绘制地面
  262.         x = np.linspace(-1.5, 1.5, 100)
  263.         y = np.linspace(-1.5, 1.5, 100)
  264.         X, Y = np.meshgrid(x, y)
  265.         Z = np.zeros_like(X)
  266.         self.ax.plot_surface(X, Y, Z, alpha=0.3, color='g')
  267.         
  268.         print("使用简单的3D可视化模拟器")
  269.             def update_pose(self, keypoints_3d):
  270.         """
  271.         根据3D姿态更新仿真模型
  272.         
  273.         参数:
  274.             keypoints_3d: 3D关键点 [33, 3]
  275.         """
  276.         if keypoints_3d is None:
  277.             return
  278.             
  279.         self.ax.clear()
  280.         
  281.         # 设置坐标轴范围
  282.         self.ax.set_xlim(-1.5, 1.5)
  283.         self.ax.set_ylim(-1.5, 1.5)
  284.         self.ax.set_zlim(0, 2)
  285.         
  286.         # 设置坐标轴标签
  287.         self.ax.set_xlabel('X')
  288.         self.ax.set_ylabel('Y')
  289.         self.ax.set_zlabel('Z')
  290.         
  291.         # 绘制地面
  292.         x = np.linspace(-1.5, 1.5, 100)
  293.         y = np.linspace(-1.5, 1.5, 100)
  294.         X, Y = np.meshgrid(x, y)
  295.         Z = np.zeros_like(X)
  296.         self.ax.plot_surface(X, Y, Z, alpha=0.3, color='g')
  297.         
  298.         # 绘制关键点
  299.         self.ax.scatter(keypoints_3d[:, 0], keypoints_3d[:, 1], keypoints_3d[:, 2], c='r', s=50)
  300.         
  301.         # 绘制连接关系
  302.         for connection in JOINT_CONNECTIONS:
  303.             start_idx, end_idx = connection
  304.             if start_idx < len(keypoints_3d) and end_idx < len(keypoints_3d):
  305.                 self.ax.plot(
  306.                     [keypoints_3d[start_idx, 0], keypoints_3d[end_idx, 0]],
  307.                     [keypoints_3d[start_idx, 1], keypoints_3d[end_idx, 1]],
  308.                     [keypoints_3d[start_idx, 2], keypoints_3d[end_idx, 2]],
  309.                     c='b', linewidth=2
  310.                 )
  311.         
  312.         # 设置视角
  313.         self.ax.view_init(elev=30, azim=45)  # 侧视视角
  314.         
  315.         if self.use_gui:
  316.             plt.pause(0.01)
  317.    
  318.     def render_scene(self, frame_id):
  319.         """
  320.         渲染当前场景并保存
  321.         
  322.         参数:
  323.             frame_id: 帧ID
  324.         """
  325.         plt.savefig(f"results/simulations/simulation_frame_{frame_id}.png", dpi=300, bbox_inches='tight')
  326. def main(camera_id=0, use_gui=True):
  327.     """
  328.     完整流程:从摄像头读取到3D仿真
  329.    
  330.     参数:
  331.         camera_id: 摄像头ID,0表示默认摄像头
  332.         use_gui: 是否使用GUI模式
  333.     """
  334.     # 1. 初始化模块
  335.     pose_estimator = HumanPoseEstimator()
  336.     pose_3d_estimator = Simple3DPoseEstimator()
  337.     simulator = SimpleSimulator(use_gui=use_gui)
  338.    
  339.     # 2. 打开摄像头
  340.     cap = cv2.VideoCapture(camera_id)
  341.    
  342.     # 检查摄像头是否成功打开
  343.     if not cap.isOpened():
  344.         print(f"无法打开摄像头 {camera_id}")
  345.         return
  346.    
  347.     # 获取摄像头信息
  348.     fps = cap.get(cv2.CAP_PROP_FPS)
  349.     width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
  350.     height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
  351.     print(f"摄像头参数: {width}x{height}, 帧率: {fps}")
  352.    
  353.     # 创建窗口
  354.     cv2.namedWindow("2D Pose Estimation", cv2.WINDOW_NORMAL)
  355.     cv2.resizeWindow("2D Pose Estimation", 800, 600)
  356.    
  357.     frame_id = 0
  358.    
  359.     # 3. 处理摄像头帧
  360.     while True:
  361.         ret, frame = cap.read()
  362.         if not ret:
  363.             print("无法获取帧,退出...")
  364.             break
  365.             
  366.         # 翻转帧,使其成为镜像效果
  367.         frame = cv2.flip(frame, 1)
  368.         
  369.         print(f"处理第{frame_id}帧...")
  370.         
  371.         # 3.1 2D姿态识别
  372.         start_time = time.time()
  373.         keypoints_2d, vis_frame = pose_estimator.detect_keypoints(frame)
  374.         
  375.         # 显示2D姿态结果
  376.         cv2.imshow("2D Pose Estimation", vis_frame)
  377.         
  378.         # 保存2D姿态结果
  379.         cv2.imwrite(f"results/2d_poses/2d_pose_frame_{frame_id}.png", vis_frame)
  380.         
  381.         # 3.2 3D姿态重建
  382.         keypoints_3d = pose_3d_estimator.estimate_3d_pose(
  383.             keypoints_2d, frame.shape, visualize=False
  384.         )
  385.         
  386.         # 可视化3D姿态
  387.         if keypoints_3d is not None:
  388.             pose_3d_estimator.visualize_3d_pose(keypoints_3d, frame_id)
  389.         
  390.         # 3.3 更新3D仿真
  391.         simulator.update_pose(keypoints_3d)
  392.         
  393.         # 3.4 渲染场景
  394.         simulator.render_scene(frame_id)
  395.         
  396.         # 计算处理时间
  397.         process_time = time.time() - start_time
  398.         print(f"处理时间: {process_time:.3f}秒")
  399.         
  400.         frame_id += 1
  401.         
  402.         # 按ESC键退出
  403.         key = cv2.waitKey(1)
  404.         if key == 27:  # ESC键
  405.             break
  406.    
  407.     # 4. 释放资源
  408.     cap.release()
  409.     cv2.destroyAllWindows()
  410.     print(f"处理完成,共{frame_id}帧,结果保存在results目录")
  411. if __name__ == "__main__":
  412.     # 运行主程序
  413.     main(
  414.         camera_id=0,  # 摄像头ID,0表示默认摄像头
  415.         use_gui=True  # 是否使用GUI模式
  416.     )
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

饭宝

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表