19f2075f1a84d.png

1. 项目速览:给牲畜体表做一次“数字体检”📏

牲畜体表信息三维重建系统面向畜牧业中的体尺测量、体型评估与数字化建模需求,基于 Astra Pro RGB-D 相机获取牲畜体表的深度、RGB 与点云数据,再通过点云配准、姿态归一化和模板非刚性配准,得到可用于分析的三维体表模型。

这套系统的核心价值不在于“拍得好看”,而在于把原本依赖人工皮尺、经验观察和静态记录的体表测量,转换为可复现、可计算、可保存的三维数据流程。换句话说,它让牲畜体型从“看起来挺壮”变成“点云里有证据”。😄

项目要素 内容
项目来源 第一届 3D 视觉创新应用竞赛参赛选手陆杰
相机型号 Astra Pro
支持设备 Zora P1 开发板
核心任务 采集牲畜体表数据,并重建三维表面模型
主要技术 OpenNI、OpenCV、PCL、MATLAB、L-BFGS-B、非刚性模板配准
主要输出 深度图、RGB 图、彩色点云、配准点云、非刚性重建网格
源代码链接 3DReconstruction_Animal-body

2. 系统链路:先采集,再拼接,最后“塑形”🧩

19f207b03dfdb.png

工作主要分成五个部分:环境配置、数据获取、数据配准、姿势归一化和三维重建。代码目录也与这一逻辑基本对应,整体上是一条从传感器数据到体表模型的完整链路。

阶段 输入 处理 输出
环境配置 Zora P1、Astra Pro 配置 OpenNI 与 OpenCV 可运行采集环境
数据获取 深度流、RGB 流 保存深度图、RGB 图和彩色点云 PNG、PLY
数据配准 左右侧点云 同名点估计刚性变换 拼接点云
姿态归一化 分割后的牲畜点云 统一方向和坐标系 规范化点云
三维重建 模板网格、扫描点云、特征点 刚性初始化和非刚性变形 重建网格
目录/文件 作用 可读出的信息
牲畜体表信息三维重建.pdf 项目说明文档 五阶段技术路线、实验结果
第二部分代码/createDepthImg 深度图采集 OpenNI 读取深度流并保存 PNG
第二部分代码/saveRGBImg RGB 图采集 OpenCV UVC 接口读取彩色图
第二部分代码/createRGBpointcloud 彩色点云生成 深度反投影为 XYZ,并写入 RGB
第三部分代码/transform_twoside 点云配准 PCL 读取 PLY,SVD 估计变换矩阵
第四部分代码/姿势归一化软件地址.txt 姿态归一化工具 指向 LSSA 牲畜点云分析软件
第五部分代码/humanshape 模板拟合框架 基于人体形体模型代码改造到猪体

3. 数据采集:Astra Pro 先把“表面轮廓”看清楚📷

19f207e826983.png

采集端的核心工作分为两条流:深度流由 OpenNI 获取,RGB 流由 OpenCV 的 VideoCapture 获取。项目中保留了 DepthImage.png 与 saved_img.png 等结果文件,说明采集链路已经跑通。

深度图保存逻辑来自 createDepthImg/main.cpp。它先打开设备并启动深度流,再把 16 位深度帧转换为 OpenCV 矩阵,最后缩放成 8 位灰度图保存,方便观察距离层次。

// 节选自 createDepthImg/main.cpp
rc = depth.create(device, openni::SENSOR_DEPTH);
if (rc == openni::STATUS_OK)
    rc = depth.start();

rc_d = depth.readFrame(&frameDepth);
pDepth = (DepthPixel*)frameDepth.getData();

const cv::Mat mImageDepth(
    frameDepth.getHeight(),
    frameDepth.getWidth(),
    CV_16UC1,
    (void*)frameDepth.getData()
);

cv::Mat mScaledDepth;
int iMaxDepth = depth.getMaxPixelValue();
mImageDepth.convertTo(mScaledDepth, CV_8U, 255.0 / iMaxDepth);
cv::imwrite("DepthImage.png", mScaledDepth);

这里有一个很朴素但关键的设计:深度原始数据适合计算,灰度深度图适合观察。前者保留毫米级深度信息,后者帮助开发者判断传感器是否正确捕获到了场景结构。

RGB 图像采集则通过 UVC 摄像头接口完成:

// 节选自 saveRGBImg/main.cpp
VideoCapture videoCapture;
videoCapture.open(cv::CAP_ANY);

videoCapture.set(CV_CAP_PROP_FOURCC, CV_FOURCC('M', 'J', 'P', 'G'));
videoCapture.set(CV_CAP_PROP_FRAME_WIDTH, 640);
videoCapture.set(CV_CAP_PROP_FRAME_HEIGHT, 480);

for (int i = 0; i < 10; i++)
{
    Mat frame;
    videoCapture >> frame;
    imwrite("/home/orbbec/Desktop/saveRGBImg/Bin/Arm64-Release/saved_img.png", frame);
}
数据类型 采集接口 分辨率/格式 用途
深度图 OpenNI depth stream 16 位深度,转 8 位预览 反投影和结构分析
RGB 图 OpenCV VideoCapture 640×480,MJPG 给点云附加颜色
彩色点云 深度 + RGB PLY 顶点带 RGB 后续配准和重建

4. 彩色点云:把每个像素“拎”到三维空间里🌈

19f2081730ae4.png

项目中的 createRGBpointcloud/main.cpp 负责将深度图转换成三维坐标,并把 RGB 颜色写入 PLY 文件。其本质是相机模型的反投影:给定像素坐标 (u, v)、深度 z 和相机内参 (fx, fy, cx, cy),即可计算空间坐标。

// 节选自 createRGBpointcloud/main.cpp
float fdx = g_IntrinsicParam.f_x * ((float)(width) / RESOULTION_X);
float fdy = g_IntrinsicParam.f_y * ((float)(height) / RESOULTION_Y);
float u0 = g_IntrinsicParam.c_x * ((float)(width) / RESOULTION_X);
float v0 = g_IntrinsicParam.c_y * ((float)(height) / RESOULTION_Y);

float tx = (u - u0) / fdx;
float ty = (v - v0) / fdy;

world_x = depth * tx;
world_y = depth * ty;
world_z = depth;
fprintf(fp, "%f %f %f %d %d %dn",
        world_x, world_y, world_z,
        color_r, color_g, color_b);

这段代码对应的数学形式可以写成:

x = (u - cx) * z / fx
y = (v - cy) * z / fy
z = depth

point = [x, y, z, r, g, b]

项目生成的 pointcloud_1.ply 是 ASCII PLY,文件头显示包含 264772 个有效顶点,每个点都带有 x, y, z, red, green, blue。这意味着它不是只有几何形状的“灰模”,而是带有体表颜色信息的点云,更适合后续观察与人工标注。

PLY 字段 含义 作用
x, y, z 三维坐标 表达牲畜体表几何
red, green, blue 彩色信息 辅助观察与标注
vertex count 有效点数量 反映采集质量与过滤结果
depth range 20 mm 到 4000 mm 排除无效或过远点

5. 双侧点云配准:左边一片,右边一片,合体才像样🧷

19f2085ca3e52.png

牲畜体表通常不是一次单侧扫描就能完整获得。项目说明中提到,系统获取两侧点云数据后进行配准。代码中使用 PCL 读取多个 PLY 文件,通过同名点估计刚性变换矩阵,再把其中一侧点云变换到另一侧坐标系下。

// 节选自 transform_twoside.cpp
pcl::io::loadPLYFile(argv[1], *cloud_in);
pcl::io::loadPLYFile(argv[2], *cloud_in2);
pcl::io::loadPLYFile(argv[3], *cloud_in3);

*cloud_in2 += *cloud_in3;
*cloud_in3 = *cloud_in2;

pcl::registration::TransformationEstimationSVD<PointT, PointT> TESVD;
pcl::registration::TransformationEstimationSVD<PointT, PointT>::Matrix4 transformation2;
TESVD.estimateRigidTransformation(*corrspond_in, *corrspond_out, transformation2);

pcl::transformPointCloud(*cloud_in, *show_transformed_clouds, transformation2);
*show_transformed_clouds += *cloud_in2;

pcl::PCDWriter writer;
writer.write("registered_final2.pcd", *show_transformed_clouds, false);

pcl::PLYWriter writer2;
writer2.write("registered_final2.ply", *show_transformed_clouds, false);

这里的关键不是 ICP 大循环,而是先用同名点做一个可解释的刚性初始化。代码里手动给出了两组对应点,之后用 SVD 估计旋转和平移。这种方法对数据量要求低,特别适合项目早期验证:只要同名点选得稳,拼接效果就不会太离谱。

配准要素 项目实现 解读
输入点云 多个 PLY 文件 来自牲畜不同侧面的采集
对应关系 手动选取同名点 保证几何约束明确
变换估计 SVD 刚性配准 求解旋转和平移
输出结果 registered_final2.pcd、registered_final2.ply 用于后续归一化和重建

6. 姿势归一化:先把“站姿”摆正,再谈重建🧭

19f2087f44442.png

牲畜扫描数据会受到站姿、相机角度、左右侧扫描顺序等因素影响。如果直接把这些点云送入模板拟合,算法很可能把“姿态差异”误当成“体型差异”。所以项目在三维重建前加入姿势归一化步骤。

项目中引用了 LSSA_CAU 相关方法,并在 姿势归一化软件地址.txt 中给出了软件地址:

https://github.com/LiveStockShapeAnalysis/LiveStockShapeAnalysis-Software/releases/tag/v1.7

归一化的目标可以概括为三件事:

处理目标 具体含义 对重建的帮助
分割目标 从背景中分离牲畜体表点云 降低噪声干扰
统一方向 让头尾、左右、上下方向一致 方便模板对齐
统一坐标 将点云放入稳定坐标系 提高非刚配准收敛性

如果把非刚性配准比作给模板“穿衣服”,那么姿势归一化就是先让模板和扫描对象站到同一个坐标舞台上。舞台都没对齐,后面的变形就容易开始自由发挥,最后模型看起来会很努力,但不一定很正确。😄

7. 模板非刚性配准:让标准猪体模型贴合扫描数据🧠

19f2089db4a01.png

项目第五部分参考了 Leonid Pishchulin 等人的 3D Human Shape Modeling 方法,并将人体形体模板拟合框架迁移到牲畜三维重建场景。项目说明中写明:模板数据为普通猪的网格数据,扫描数据为姿势归一化后的猪体点云,并在模板和扫描数据上分别选取 37 个对应特征点。

整体过程分为两步:

步骤 方法 目的
刚性初始化 基于特征点的模板到扫描刚性配准 让模板先大致贴近点云
非刚性变形 每个模板点拥有 4×4 变换矩阵 让模板表面逐步贴合扫描体表

入口脚本 test.m 会读取测试目录中的 PLY 和标志点文件,然后调用 testFitMesh:

% 节选自 humanshape/test.m
expidx = 0;
p = expParams(expidx);

Path = [p.rootDir 'pig_test/orbbec_test/'];

scanFiles = dir(fullfile(Path,'*.ply'));
landmarkFiles = dir(fullfile(Path,'*.lnd'));
landmarkXML = dir(fullfile(Path,'*.xml'));
landmarkIdx = dir(fullfile(Path,'*.txt'));

assert(length(scanFilenames) == length(landmarkFilenames));

fprintf('register test scann');
testFitMesh(scanFilenames, landmarkFilenames, expidx);

参数文件 expParams.m 中可以看到项目已经将原始人体模型流程改成了猪体测试流程:

% 节选自 fitting/expParams.m
case 0
    p.landmarksSM = {[p.rootDir 'pig_test/orbbec_LandmarkIdxs_2.txt']};
    p.pathSM = [p.rootDir 'pig_test/pig_orbbec.ply'];

    p.name = 'test-nrd-3';
    p.fitDir = [p.expDir '/' p.name];
    p.initDir = p.fitDir;
    p.nrdWidx = 3;
    p.nPCA = 20;

其中 pig_orbbec.ply 是猪体模板,文件头显示包含 26238 个顶点和 52476 个三角面;orbbec_test/1_pig_sup_2.ply 是测试扫描点云,文件头显示包含 11876 个顶点,并带有 RGB 和 alpha 字段。

8. 非刚性优化:数据项、平滑项、特征点项三方拉扯⚖️

19f208b3e647c.png

非刚性配准的核心在 test_NRD.m、NRDFunction.m 与 NRDgradientFunction.m。项目为模板的每个顶点维护一个 4×4 变换矩阵,优化目标由三类约束组成:

约束项 对应代码变量 作用
数据项 E_DATA 模板点贴近扫描点云最近邻
平滑项 E_SMOOTH 相邻模板点的变换不要突变
特征点项 E_LM 保证关键体尺位置对齐

NRDFunction.m 中的目标函数非常直观:模板既要靠近扫描点,也要保持表面连续,还要尊重人工选取的特征点。也就是说,它不是“哪里有点就往哪里冲”,而是“贴近,但别把身体拧成奇怪形状”。严谨一点说,这是一种带正则化的非刚性配准问题。

% 节选自 fitting/NRDFunction.m
for i = 1:NUM_LM
    E_LM_Gen = E_LM_Gen + ...
        (norm(A_LM(:,:,i) * LM_TL(:,:,i) - LM_SC(:,:,i)))^2;
end

for i = 1:NUM_TL_ALL
    NNvertSMPoint = NN_VERT_TL{TL_ALL_IDX(1,i),1};
    for j = 1:size(NNvertSMPoint,2)
        E_SMOOTH_Gen = E_SMOOTH_Gen + ...
            norm(A(:,:,TL_ALL_IDX(1,i)) - A(:,:,NNvertSMPoint(j)),'fro')^2;
    end
end

for i = 1:NUM_TL_POINTS
    E_data_total = E_data_total + ...
        IS_VALID_NN(i,1) * CONF_SC_POINTS(i,1) * ...
        (norm(A_DATA(:,:,i) * TL_POINTS(:,:,i) - [NN_SC(i,:) 1]'))^2;
end

E_TOTAL = E_DATA + E_SMOOTH + E_LM;

test_NRD.m 中还设置了权重逐步降低的迭代策略。初期平滑项权重大,先保证模板整体稳定;随后逐步降低平滑约束,让模板更贴近真实扫描。这个策略有点像捏模型:先捏大形,再修细节,别一上来就抠局部,否则容易捏歪。

% 节选自 pig_test/test_NRD.m
W_DATA = 1;
W_SMOOTH_HEAD = 1e+6;
W_SMOOTH_HANDS = 1e+6;
W_SMOOTH_GEN = 1e+6;
W_LM = 0.1;

while (abs(errPrev - err) > eps_err && W_SMOOTH_GEN >= 1*1e+3)
    pointsIdxsScanNN = getNNheadAndBody(scan, template);
    Amatrix = lbfgsb(Amatrix, lowerbound, upperbound, ...
                     'NRDFunction', 'NRDgradientFunction');

    W_SMOOTH_GEN   = W_SMOOTH_GEN   * wrSmoothBody;
    W_SMOOTH_HANDS = W_SMOOTH_HANDS * wrSmoothHand;
    W_SMOOTH_HEAD  = W_SMOOTH_HEAD  * wrSmoothHead;
    W_LM = W_LM * wrLandmarks;
end

9. 实验结果:误差有数,模型有形📊

19f208e5b1946.png

项目文档给出了非刚性配准结果与原始点云之间的定量比较。平均距离误差约为 1.1497 mm,最大距离误差为 23.1323 mm,最小距离误差为 0.0014 mm。从指标看,模板拟合在整体上能够较贴近扫描点云,但局部极值仍提示边缘、遮挡、噪声或特征点区域可能存在偏差。

评价指标 数值 单位 解读
平均距离误差 1.1497 mm 整体贴合精度较高
最大距离误差 23.1323 mm 局部可能有遮挡或配准偏差
最小距离误差 0.0014 mm 部分区域几乎重合

项目目录中还保留了若干结果文件:

结果文件 位置 说明
DepthImage.png createDepthImg/Bin/Arm64-Release 深度图预览
saved_img.png saveRGBImg/Bin/Arm64-Release RGB 图像样例
pointcloud_*.ply createRGBpointcloud/Bin/Arm64-Release 多帧彩色点云
pig_orbbec.ply humanshape/pig_test 猪体模板网格
1_pig_sup_2.ply humanshape/pig_test/orbbec_test 测试扫描点云
NRD*.mat experiments/test-nrd-3/1_pig_sup_2 非刚性配准迭代结果

10. 为什么这套方案适合牲畜体表信息重建?🐾

牲畜体表重建不是普通室内场景重建。它的对象具有相似形态、局部曲面连续、姿态变化有限但不能忽略,并且实际测量往往更关心体尺、轮廓、局部表面而非完整纹理。因此,项目采用“点云采集 + 模板约束”的路线是合理的。

技术选择 对牲畜场景的意义
RGB-D 采集 低成本获取体表几何与颜色
双侧点云 降低单视角遮挡问题
同名点配准 小规模数据也能完成可控拼接
姿态归一化 减少站姿差异带来的误差
模板非刚性拟合 让稀疏或缺失区域获得形体先验
特征点约束 保证关键体尺位置更稳定

如果没有模板,点云缺失处只能“空着”;如果只有模板,没有扫描数据,又容易变成“平均牲畜”。项目的做法相当于把二者结合起来:扫描数据给真实个体,模板模型给结构先验。一个负责实事求是,一个负责别让形状跑偏。🙂

11. 工程环境:小板子、老接口、硬核依赖,一个都不能少🧰

19f209062634b.png
环境/组件 用途 备注
Zora P1 嵌入式采集端 用于连接 Astra Pro
Astra Pro RGB-D 数据来源 提供深度与彩色信息
OpenNI2 深度流读取 需配置 SDK 和驱动
OpenCV RGB 读取、图像保存 Makefile 中加入 pkg-config opencv
PCL 点云读取、配准、保存 用于 SVD 变换和 PCD/PLY 输出
MATLAB 模板拟合与优化 运行 humanshape 改造代码
L-BFGS-B 非刚性优化 用于求解变形矩阵

项目中的 Makefile 明确把 OpenCV 编译参数加入构建流程:

# 节选自 CommonCppMakefile
CFLAGS += ${shell pkg-config opencv --cflags}
LDFLAGS += ${shell pkg-config opencv --libs}

点云配准部分则使用 CMake 寻找 PCL:

# 节选自 transform_twoside/CMakeLists.txt
find_package(PCL 1.5 REQUIRED)

include_directories(${PCL_INCLUDE_DIRS})
link_directories(${PCL_LIBRARY_DIRS})
add_definitions(${PCL_DEFINITIONS})

add_executable(transform_twoside transform_twoside.cpp)
target_link_libraries(transform_twoside ${PCL_LIBRARIES})

12. 当前局限:它已经能重建,但还可以更“聪明”一点🛠️

局限 表现 改进方向
依赖手动同名点 配准质量受人工选点影响 引入自动特征点或粗配准
双侧扫描有限 顶部、腹部等区域可能缺失 增加多视角采集
RGB-D 对齐较简化 彩色与深度可能存在偏差 做精确内外参标定
姿态归一化依赖外部工具 流程自动化程度不足 集成归一化模块
模板迁移成本较高 需要特征点和猪体模板 建立不同品种模板库
MATLAB 依赖较重 部署不够轻量 改写为 Python/C++ 优化流程

13. 后续优化路线:从“能重建”走向“能测量、能分析、能上线”🚀

19f2093197155.png
优化方向 具体做法 预期收益
多视角采集 增加前后、顶部或环绕视角 减少遮挡与缺失
自动特征点 用几何曲率或学习方法检测关键点 降低人工标注成本
鲁棒配准 结合 RANSAC、ICP 和全局特征 提升拼接稳定性
自动分割 从背景中自动提取牲畜点云 提高系统易用性
体尺测量 从重建模型提取体长、体高、胸围等 直接服务养殖管理
模板库建设 按品种、体型建立统计模型 提升泛化能力
轻量部署 将拟合流程工程化为独立服务 便于实际落地

14. 总结:这不是“给牲畜拍照”,而是在建立可计算的体表档案🎯

牲畜体表信息三维重建系统把 Astra Pro、Zora P1、OpenNI、OpenCV、PCL 和模板非刚性配准串成了一条完整技术路线。它从深度图和彩色图出发,生成体表点云;再用同名点和 SVD 完成多侧点云配准;随后通过姿态归一化和非刚性模板拟合,得到更完整、更可分析的三维体表模型。

从学术与工程角度看,这个项目的亮点在于它没有停留在“获取点云”这一步,而是继续向体表结构表达、误差评价和形体建模推进。对畜牧业来说,这类系统未来可以进一步支撑自动体尺测量、育种评估、健康监测和数字化养殖管理。简单说,就是让牲畜体表信息从“人工经验”升级为“空间数据”。📐

发表回复

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