19f21f8981706.png

来源:第一届 3D 视觉创新应用竞赛,许芮溧团队 相机型号:Orbbec Astra Pro 支持设备:Zora P1 开发板 项目代码: Measure_human-body

1. 项目概览:相机一开,人体数据开始“排队报到” ✨

人体三维精准量测与动作捕捉系统旨在利用低成本 RGB-D 深度传感器,实现人体三维建模、人体关键点识别、姿态分析和基础体态量测。它的应用目标很明确:让医疗监护、康复评估、跌倒检测、人体量体和低成本动作捕捉变得更轻量、更自动化。

说得通俗一点,这套系统想做的事情是:让 Astra Pro 不只是拍照,而是带着“卷尺”和“骨架理解能力”去看人。它既关注身高、臂长、胸围、臀围等几何信息,也关注人体姿态、关节位置和动作状态。相机不说话,但它看得挺认真。

项目维度 说明
应用主题 人体三维精准量测与动作捕捉
核心传感器 Astra Pro RGB-D 相机
计算平台 Zora P1 开发板,原型开发主要在 Windows 环境完成
核心算法 深度转点云、OpenPose 人体关键点、PCL 点云处理、ICP 配准
关键输出 彩色图、深度图、人体关键点、人体点云、粗略三维人体模型
应用方向 医疗看护、跌倒检测、病人状态监测、人体量体、动作捕捉

2. 为什么需要它:医护人员已经够忙了 🏥

在医疗场景中,病人状态监测、跌倒检测、康复姿态评估和基础体征记录往往需要持续观察。人工观察可靠但费力,传统摄像头便宜但缺少深度信息,专业动作捕捉设备精度高但成本不低。于是,低成本 RGB-D 方案就有了很实际的价值。

需求 传统方式 RGB-D 方案的价值
跌倒检测 人工巡视或普通摄像头 可结合姿态和空间高度判断
病人状态监测 护士或护工定时查看 可持续获取人体姿态变化
人体量体 手工测量 可从点云提取身高、围度、肢体长度
动作捕捉 专业光学动捕设备 降低硬件门槛,便于普及
康复评估 主观观察与量表 可记录关节位置和动作轨迹

系统的定位不是替代医生,而是减少重复性观测工作,让医护人员把精力留给更需要判断力和关怀的部分。机器负责盯数据,人负责做决定,这个分工比较健康。

3. 系统架构:从 RGB-D 数据到人体模型 🧠

19f21fe633783.png

系统整体流程可以拆成五个层次:

层次 主要内容 作用
采集层 Astra Pro、OpenNI2、OpenCV 获取彩色流和深度流
数据层 PNG、CSV、PCD、JSON 保存图像、深度、点云和关键点
识别层 OpenPose BODY_25 提取人体 25 个关键点
点云层 PCL 八叉树变化检测、直通滤波 从背景中提取人体点云
建模层 CloudCompare、ICP、点云融合 构建多视角人体点云模型

项目文件也基本按这个思路组织:

文件夹 内容 阅读重点
code/openni Python 采集脚本、图像、深度、点云数据 RGB-D 数据获取与预处理
code/code PCL C++ 点云处理代码 CSV 转 PCD、变化检测、滤波
code/openpose-master OpenPose 源码 姿态关键点识别依赖
人体三维精准量测与动作捕捉.pdf 开发文档 项目背景、流程、优势和复盘

4. 工作流程:人体数据的“闯关路线” 🧭

系统从采集到建模的流程如下:

19f2205f193fc.png

这个流程的核心思想是“双线并行”:一条线负责人体骨架,一条线负责人体形状。骨架用于动作捕捉与姿态判断,点云用于人体几何量测与三维建模。两条线最后汇合,才更接近“完整人体信息”。

5. 数据采集:彩色图和深度图先同框登场 📷

项目通过 Python + OpenNI2 + OpenCV 获取 Astra Pro 的深度流与彩色流。由于开发过程中 OpenNI 读取彩色流存在兼容问题,项目实际采用 OpenNI2 读取深度流,OpenCV 读取彩色流。

下面是 code/openni/takePhoto.py 中的核心采集逻辑,经过适度精简:

from openni import openni2
import numpy as np
import cv2

openni2.initialize()
dev = openni2.Device.open_any()
cap = cv2.VideoCapture(0)

depth_stream = dev.create_depth_stream()
depth_stream.start()

while True:
    depth_frame = depth_stream.read_frame()
    dframe_data = np.array(depth_frame.get_buffer_as_triplet()).reshape([480, 640, 2])

    dframe1 = np.asarray(dframe_data[:, :, 0], dtype='float32')
    dframe2 = np.asarray(dframe_data[:, :, 1], dtype='float32')
    dframe2 *= 255
    dframe = dframe1 + dframe2
    dframe = np.flipud(np.flip(dframe))

    ret, cframe = cap.read()

    cv2.imshow('depth', dframe)
    cv2.imshow('color', cframe)

这段代码完成了三个关键动作:

动作 技术含义
create_depth_stream() 创建深度流
VideoCapture(0) 调用普通彩色摄像头流
get_buffer_as_triplet() 将深度帧缓冲区转为可处理数组

开发文档中建议拍摄时将相机固定在测试人员正前方约 2 米、高约 0.5 米的位置,并让测试人员每次旋转 30 度,采集 12 个角度的人体数据,最后再采集一张背景图。这种“转一圈”的流程很适合做多视角人体点云融合。

6. 相机标定与深度反投影:点云从哪里来? 📐

由于项目开发时无法稳定从 Orbbec Viewer 导出 Astra Pro 内参,团队使用 GML Camera Calibration 和棋盘格完成相机标定。标定得到的内参被写入脚本,用于把深度图转换为三维点云。

点云生成的核心公式是:

X = Z * (u - cx) / fx
Y = Z * (v - cy) / fy
Z = depth(u, v)

code/openni/Depth2PointCloud.py 中对应实现如下:

CAM_WID, CAM_HGT = 640, 480
CAM_FX, CAM_FY = 598.088, 599.855
CAM_CX, CAM_CY = 316.466, 237.723

x, y = np.meshgrid(range(CAM_WID), range(CAM_HGT))
x = x.astype(np.float32) - CAM_CX
y = y.astype(np.float32) - CAM_CY

poi_z = depimg.copy()
poi_x = poi_z * x / CAM_FX
poi_y = poi_z * y / CAM_FY

poi = np.array([poi_x.ravel(), poi_y.ravel(), poi_z.ravel()]).T
np.savetxt(poip, poi, fmt='%.18e', delimiter=',')
参数 含义
CAM_FX, CAM_FY 相机焦距
CAM_CX, CAM_CY 相机主点
poi_z 深度值
poi_x, poi_y 反投影后的空间坐标
poi 每个像素对应的三维点

这一步是人体量测的地基。没有反投影,系统只能看二维照片;有了点云,身高、宽度、围度、表面形态才有计算基础。

7. 彩色点云:给 XYZ 披上 RGB 外衣 🎨

深度图生成的点云只有几何坐标,视觉上像一堆“灰色空间芝麻”。为了让点云更接近真实人体外观,系统将彩色图的 RGB 信息附加到每个三维点上,生成 XYZRGB 彩色点云。

code/openni/genColorPointCloud.py 的核心逻辑如下:

colimg = cv2.imread(colimgp)
xyzpoi = np.loadtxt(xyzp, delimiter=',')

r = colimg[:, :, 2].reshape(307200, )
g = colimg[:, :, 1].reshape(307200, )
b = colimg[:, :, 0].reshape(307200, )

xyzrgbpoi = np.insert(xyzpoi, 3, values=r, axis=1)
xyzrgbpoi = np.insert(xyzrgbpoi, 4, values=g, axis=1)
xyzrgbpoi = np.insert(xyzrgbpoi, 5, values=b, axis=1)

np.savetxt(xyzrgbp, xyzrgbpoi, delimiter=',')

这个实现默认彩色图和深度图分辨率均为 640 x 480,因此总像素数为 307200。每个像素点对应一行点云数据:

x, y, z, r, g, b

彩色点云不仅更适合展示,也为后续可能的纹理映射、人体区域识别、体表特征辅助定位提供了基础。

8. 人体关键点识别:OpenPose 提供 25 个“骨架钉子” 🦴

19f2210f8350b.png

项目使用 OpenPose 进行人体关键点识别,并选择 BODY_25 模型,仅开启人体姿态检测,关闭手部和面部检测,以降低计算负担并聚焦本项目目标。

开发文档中提到,赛事样例 demo 可识别的人体节点数量较少,团队因此引入 OpenPose,目标是提取 25 个关键点。样例 JSON 文件 code/openni/json/col2_keypoints.json 中,OpenPose 输出的数据结构如下:

{
  "version": 1.3,
  "people": [
    {
      "pose_keypoints_2d": [
        381.574, 74.3998, 0.807851,
        346.078, 104.732, 0.64519
      ],
      "face_keypoints_2d": [],
      "hand_left_keypoints_2d": [],
      "hand_right_keypoints_2d": []
    }
  ]
}

pose_keypoints_2d 的数据以三元组排列:

字段 含义
x 关键点横坐标
y 关键点纵坐标
confidence 关键点置信度

项目中的 readjson.py 将 JSON 解析为 CSV,便于后续查看与处理:

people = load_dict["people"]
keypointlist = []

for person in people:
    for key, value in person.items():
        if key == "pose_keypoints_2d":
            for i in range(0, len(value), 3):
                b = value[i:i + 3]
                x = b[0]
                y = b[1]
                keypoint = [keypointname[int(i / 3)], x, y]
                keypointlist.append(keypoint)

with open(outputfilename, 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerows(keypointlist)

解析后的样例 CSV 大致如下:

Nose,381.574,74.3998
Neck,346.078,104.732
RShoulder,348.816,107.5
RElbow,359.752,170.584
RWrist,378.997,217.117
MidHip,359.772,225.415
RAnkle,354.235,400.837

这些关键点可以用于姿态判断,例如跌倒检测可重点关注头部、颈部、髋部、膝部和踝部的相对位置变化;动作捕捉可记录关键点随时间变化的轨迹。工程化时建议修正 readjson.py 中 LKnee 与 LAnkle 标签拼接的小问题,避免后续数据字段混淆。

9. 从 CSV 到 PCD:让点云进入 PCL 世界 🌐

Python 生成的点云初始保存为 CSV,而 PCL 更常使用 PCD 格式。项目中的 code/code/1_csv2pcd.cpp 将 x, y, z, r, g, b 格式的 CSV 数据转换为 PointXYZRGB 点云。

typedef struct tagPOINT_3D
{
    double x;
    double y;
    double z;
    double r;
    double g;
    double b;
} POINT_WORLD;

pcl::PointCloud<pcl::PointXYZRGB> cloud;
cloud.width = my_csvPoints.size();
cloud.height = 1;
cloud.is_dense = false;
cloud.points.resize(cloud.width * cloud.height);

for (size_t i = 0; i < cloud.points.size(); ++i)
{
    cloud.points[i].x = my_csvPoints[i].x;
    cloud.points[i].y = my_csvPoints[i].y;
    cloud.points[i].z = my_csvPoints[i].z;
    cloud.points[i].r = my_csvPoints[i].r;
    cloud.points[i].g = my_csvPoints[i].g;
    cloud.points[i].b = my_csvPoints[i].b;
}

pcl::io::savePCDFileASCII(pcd_file_name, cloud);

转换后的 PCD 文件头示例如下:

# .PCD v0.7 - Point Cloud Data file format
FIELDS x y z rgb
WIDTH 18363
HEIGHT 1
POINTS 18363
DATA ascii

PCD 格式让点云可以被 PCL、CloudCompare 等工具直接读取,后续的变化检测、滤波、配准和可视化也就顺利接上了。

10. 人体点云提取:用八叉树找出“多出来的人” 🌲

19f221641c1e3.png

项目拍摄了背景点云和人体点云,通过 PCL 的八叉树变化检测提取新增点。背景中没有人,第二帧中多出来的点,大部分就是人体。这个思路简单直接,挺像在问点云:“刚才谁进来了?”

code/code/2_change_detection.cpp 的关键逻辑如下:

float resolution = 50.0f;
pcl::octree::OctreePointCloudChangeDetector<pcl::PointXYZRGB> octree(resolution);

octree.setInputCloud(sourceCloud);
octree.addPointsFromInputCloud();
octree.switchBuffers();

octree.setInputCloud(handledCloud);
octree.addPointsFromInputCloud();

std::vector<int> newPointIdxVector;
octree.getPointIndicesFromNewVoxels(newPointIdxVector);

pcl::PointCloud<pcl::PointXYZRGB>::Ptr cloud_result(
    new pcl::PointCloud<pcl::PointXYZRGB>
);

for (size_t i = 0; i < newPointIdxVector.size(); ++i)
{
    cloud_result->points[i].x = handledCloud->points[newPointIdxVector[i]].x;
    cloud_result->points[i].y = handledCloud->points[newPointIdxVector[i]].y;
    cloud_result->points[i].z = handledCloud->points[newPointIdxVector[i]].z;
}
技术点 说明
八叉树分辨率 项目中设置为 50.0 mm
输入一 背景点云
输入二 含人体点云
输出 新增体素中的点,近似为人体点云

这种方法适合固定相机、固定背景、人体进入场景的采集方式。它的前提也很清楚:背景要稳定,人体位置要明显,采集时不要让桌椅和灯光一起“抢戏”。

11. 点云滤波:把人体从噪声里捞出来 🧹

变化检测得到的人体点云仍包含不少噪点。项目使用 PCL 的直通滤波器对 x、y、z 三个方向分别设定范围,截取人体主体区域。

code/code/3_filtering.cpp 中的核心代码如下:

pcl::PassThrough<pcl::PointXYZRGB> pass;
pass.setInputCloud(sourceCloud);

pass.setFilterFieldName("x");
pass.setFilterLimits(-280.0, 300.0);
pass.filter(*cloud_result);

pass.setInputCloud(cloud_result);
pass.setFilterFieldName("y");
pass.setFilterLimits(-750.0, 830.0);
pass.filter(*cloud_result);

pass.setInputCloud(cloud_result);
pass.setFilterFieldName("z");
pass.setFilterLimits(2200.0, 2800.0);
pass.filter(*cloud_result);
方向 保留范围 作用
x -280 到 300 限制人体左右宽度
y -750 到 830 限制人体上下高度
z 2200 到 2800 限制人体与相机距离

这些阈值不是通用常数,而是根据相机位置、人体距离、采集环境调出来的参数。换一个房间、换一个人、换一个相机高度,都可能需要重新调参。工程上可以进一步用自适应包围盒或人体关键点约束来自动估计范围。

12. 多视角配准与融合:人体模型是“拼”出来的 🧩

19f221abb4ada.png

项目采集了人体 12 个角度的数据,每次旋转约 30 度。由于部分侧面点云质量较差,开发文档中说明最终舍弃了两份侧面数据,使用剩余 10 份点云构建较粗糙的完整人体点云模型。

配准与融合主要借助 CloudCompare 完成:

步骤 工具 目的
导入点云 CloudCompare 加载多角度 PCD
粗对齐 Matching bounding-box centers 让点云初步靠近
精配准 Fine registration ICP 优化点云相对位姿
融合 Merge multiple clouds 合并为局部人体模型
拼接 Translate/Rotate + Merge 合成正背面模型

这里需要严谨说明:项目原计划实现自动化人体点云配准,但由于时间和点云质量限制,最终主要采用 CloudCompare 的人工辅助流程完成点云拼接。这个结果不丢人,反而是很真实的工程开发经历:点云质量一旦不够,算法就会开始“沉默地崩溃”,看起来很安静,实际很扎心。

13. 人体量测与动作捕捉:怎么从数据走向应用? 🏃

系统的输出可分为两类:

输出类型 数据来源 可支持应用
人体关键点 OpenPose BODY_25 动作捕捉、姿态识别、跌倒检测
人体点云 Astra Pro 深度数据 身高、臂长、围度、体表形态量测

对于动作捕捉,可把关键点序列看作人体骨架随时间移动的轨迹:

for 每一帧:
    读取 BODY_25 关键点
    过滤置信度过低的节点
    计算关节间距离、角度、相对高度
    与动作模板或阈值模型比较
    输出站立、坐下、跌倒、抬手等姿态状态

对于基础量体,可从点云中提取人体包围盒和截面轮廓:

输入: 人体点云 P

身高 ≈ max(P.y) - min(P.y)
肩宽 ≈ 肩部高度截面上的 x 方向宽度
臂长 ≈ Shoulder -> Elbow -> Wrist 的空间距离
围度 ≈ 指定高度截面点云轮廓周长

工程化时,建议将关键点和点云结合起来:用关键点定位肩、肘、腕、髋、膝等人体部位,再到对应点云区域计算几何尺寸。这样比单纯在整个人体点云上硬切截面更稳定。

14. 医疗看护场景:系统可以做什么? 🛏️

19f22207d701a.png

在医疗应用中,系统最重要的价值不是“炫技”,而是稳定、及时、可解释。比如跌倒检测可以基于头部、颈部、髋部的高度变化;康复动作评估可以基于关节角度;体型量测可以基于点云截面。这些指标都比单纯“看画面像不像”更可靠。

15. 技术优势:低成本硬件 + 多源信息融合 ⚙️

根据开发文档,项目的主要技术优势可归纳为两点:

技术优势 说明
多视角观测融合 通过多角度点云拼接,补足单视角人体遮挡
深度与视觉融合 用彩色图提取关键点,用深度图提供空间信息

更具体地说,RGB-D 相机能同步获取彩色与深度信息;OpenPose 提供人体关键点;PCL 与 CloudCompare 负责点云处理和融合。这个组合让系统既能理解人体“骨架”,也能看到人体“外形”。

模块 优势 局限
Astra Pro 成本低,能输出深度图 点云精度与连续性有限
OpenPose 关键点数量多,识别能力强 依赖环境配置和计算资源
PCL 点云处理成熟 参数需要结合场景调节
CloudCompare 配准与可视化方便 原型阶段人工操作较多

16. 开发复盘:把问题讲清楚,也是科研的一部分 🔍

项目文档对开发中的问题记录得很诚实,这一点很有价值。主要问题包括:

问题 影响 项目处理方式
Astra Pro 设备识别不稳定 影响采集流程 改用 Python + OpenNI2 尝试读取
Orbbec Viewer 导出 ONI 异常 影响数据复用 使用脚本直接采集数据
OpenNI 彩色流兼容问题 不能稳定读取彩色帧 使用 OpenCV 读取彩色流
深度转点云呈稀疏切片 影响三维重建精度 尝试滤波、加密、人工配准
OpenPose 配置复杂 影响关键点提取进度 补齐 CUDA、cuDNN 等依赖

这些问题说明,低成本 RGB-D 系统的难点并不只在算法,还在传感器精度、驱动兼容、标定、数据质量和工具链整合。换句话说,真正的系统开发不是“调包一下就结束”,而是不断和数据现实谈判。

17. 后续优化方向:让数字量体师更靠谱 🚀

方向 可改进内容
标定优化 使用标准棋盘格流程,建立自动化内参校验
深度补全 对稀疏切片点云进行深度修复与表面重建
自动配准 引入关键点约束的 ICP 或特征辅助配准
动态捕捉 从单帧关键点扩展到连续帧骨架追踪
量测算法 建立人体语义部位分割和围度截面提取
医疗验证 与人工测量或标准设备进行误差对比

一个可行的升级方案是:用 OpenPose 的关键点作为点云配准的先验约束。例如先根据肩、髋、膝等关键点估算人体朝向和尺度,再对点云做精配准。这能减少 ICP 直接对稀疏切片点云配准时的误差。

18. 总结:低成本 RGB-D 的人体理解实验,值得继续打磨 ✅

人体三维精准量测与动作捕捉系统以 Astra Pro 为核心传感器,围绕人体数据采集、深度转点云、BODY_25 关键点提取、人体点云分割、滤波和多视角融合建立了一条完整技术路线。它的目标不只是生成好看的点云,而是为医疗监护、人体量体和动作捕捉提供更低成本的感知方案。

项目已经实现的关键成果包括:

  1. 获取彩色流和深度流数据。
  1. 通过相机内参将深度图转换为三维点云。
  1. 生成 XYZRGB 彩色点云。
  1. 使用 OpenPose 提取 25 个人体关键点。
  1. 使用 PCL 八叉树变化检测提取人体点云。
  1. 使用滤波和 CloudCompare 完成粗略人体点云建模。

从工程角度看,它仍受限于深度精度、点云连续性和自动配准能力;从研究角度看,它已经搭出了“RGB-D 采集 + 人体骨架 + 三维点云”的完整原型框架。只要继续优化标定、深度补全和多视角融合,这位“数字量体师”未来完全有机会从实验室走向病房、康复室和动作捕捉工作间。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注