You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将深度图转换为cv::rgbd::RgbdNormals适用的3D点输入格式

问题说明

尝试基于OpenCV实现表面法向量计算,初始实现代码如下:

// load 16bit uchar depth image (values are in millimeters)
cv::Mat img = cv::imread("depth.png", CV_16UC1);

// define our camera matrix
cv::Mat k = (cv::Mat1d(3, 3) << 900, 0, 640, 0, 900, 360, 0, 0, 1);

// compute surface normals
cv::Mat normals;
cv::rgbd::RgbdNormals normal_computer(img.rows, img.cols, CV_32F, k, 3);
normal_computer(img, normals);

std::cout << "normals" << std::endl
          << normals << std::endl
          << normals.size() << std::endl
          << normals.channels() << std::endl
          << std::endl;

代码运行时触发断言报错:

terminate called after throwing an instance of 'cv::Exception'
  what():  OpenCV(4.2.0) ../contrib/modules/rgbd/src/normal.cpp:776: error: (-215:Assertion failed) points3d_ori.channels() == 3 in function 'operator()'

Aborted (core dumped)

根据OpenCV 4.2.0官方文档,cv::rgbd::RgbdNormals算子仅支持两类输入格式:

  • 尺寸为rows×cols×3、数据类型为CV_32F/CV_64F的三维点云矩阵
  • 尺寸为rows×cols×1、数据类型为CV_16S的单通道深度矩阵
    直接加载得到的CV_16UC1格式深度图不符合输入要求,因此触发报错。当前运行环境为Ubuntu 20.04 LTS,OpenCV版本为v4.2.0。
解决方案

方案1:转换为3通道点云输入(推荐,兼容性最好)

该方案不会出现类型、单位匹配的隐式问题,是官方示例优先使用的方式,步骤如下:

  1. 将加载的16位无符号毫米单位深度图转换为32位浮点格式,同时把深度值单位从毫米转换为米(与相机内参量纲匹配)
  2. 调用cv::rgbd::depthTo3d接口,结合相机内参将深度图转换为rows×cols×3规格的CV_32F类型三维点云
  3. 将生成的点云传入法向量计算器即可得到正确结果

修正后的完整代码片段:

#include <opencv2/rgbd.hpp>
// 其余依赖头文件按实际需求引入

int main() {
    // 加载16位无符号单通道深度图,深度值单位为毫米
    cv::Mat depth = cv::imread("depth.png", cv::IMREAD_UNCHANGED);
    // 校验加载结果与格式,避免后续异常
    CV_Assert(!depth.empty() && depth.type() == CV_16UC1);

    // 定义相机内参矩阵
    cv::Mat k = (cv::Mat1d(3, 3) << 900, 0, 640, 0, 900, 360, 0, 0, 1);

    // 深度值单位转换:毫米转米,格式转换为32位浮点
    cv::Mat depth_float;
    depth.convertTo(depth_float, CV_32F, 1.0 / 1000.0);

    // 深度图转三维点云,输出为3通道CV_32F格式矩阵
    cv::Mat points3d;
    cv::rgbd::depthTo3d(depth_float, k, points3d);

    // 初始化法向量计算器
    cv::Mat normals;
    cv::rgbd::RgbdNormals normal_computer(depth.rows, depth.cols, CV_32F, k, 3);
    // 传入符合格式要求的点云计算法向量
    normal_computer(points3d, normals);

    // 后续结果处理逻辑
    std::cout << "normals size: " << normals.size() << ", channels: " << normals.channels() << std::endl;
    return 0;
}

方案2:转换为符合要求的单通道深度输入

如果不需要显式生成点云,可以直接将现有CV_16UC1格式的深度图转换为接口要求的CV_16S类型,只要场景深度范围不超过32米(毫米单位下32767为CV_16S的最大正数值)就不会出现溢出问题,转换代码如下:

cv::Mat depth_16s;
img.convertTo(depth_16s, CV_16S);
// 后续直接将depth_16s传入normal_computer即可

注意:单通道输入方式对深度值范围、单位要求严格,若深度值超出CV_16S表示范围会出现数值溢出,导致法向量计算结果完全错误,生产环境优先选择方案1。

内容的提问来源于stack exchange,提问作者ravi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 02:27:27