0

所以我尝试在 python 中使用 Open3D 库创建一个点云,最后,它基本上只是这里引用的 2 行,但是当我运行我的代码(见下文)时,我得到的只是一个弹出的白屏。我已经在 J​​upyter 笔记本中运行它,但是从控制台在 python 脚本中运行它并没有改变任何东西,也没有引发错误。我应该提一下,我在 Blender 中创建了图像并将其保存为 OpenExr,这意味着深度值范围在 0 到 4 之间(我已将其截断为 4 作为背景)。您可以在下面看到它们是正确的图像,我还可以将它们转换为 Open3D 图片并毫无问题地显示它们。

编辑(27-03-2020):添加了完整的最小示例

import OpenEXR
import numpy as np
import array
import matplotlib.pyplot as plt
import open3d as o3d
%matplotlib inline

exr_img = OpenEXR.InputFile('frame0100.exr')

depth_img = array.array('f', exr_img.channel('View Layer.Depth.Z'))
r_img = array.array('f', exr_img.channel('View Layer.Combined.R'))
g_img = array.array('f', exr_img.channel('View Layer.Combined.G'))
b_img = array.array('f', exr_img.channel('View Layer.Combined.B'))

def reshape_img(img):
    return np.array(img).reshape(720, 1280)

img_array = np.dstack((reshape_img(r_img),
                     reshape_img(g_img),
                     reshape_img(b_img),
                     reshape_img(depth_img)))

#Background returns very large value, truncate it to 4
img_array[img_array == 10000000000.0] = 4

colour = o3d.geometry.Image(np.array(img_array[:, :, :3]))
depth = o3d.geometry.Image(np.array(img_array[:, :, 3]*1000).astype('uint16'))
o3d.draw_geometries([depth])

pinhole_cam = o3d.open3d.camera.PinholeCameraIntrinsic(width= 1280, height=720, cx=640,cy=360,fx=500,fy=500)

rgbd = o3d.create_rgbd_image_from_color_and_depth(colour, depth, convert_rgb_to_intensity = False, depth_scale=1000)
pcd = o3d.create_point_cloud_from_rgbd_image(rgbd, pinhole_cam)

o3d.draw_geometries([pcd])

请忽略导入数据的 hacky 方式,因为我是 Open3D 新手并自己生成数据,我一步一步地进行检查和确认数据完整性

我尝试使用一些统计数据的图片

我认为这可能与我的针孔相机参数有关。Tbh,我不知道什么是合适的参数,除了 cy, cy 应该是图像的中心并且 fx, fy 应该是合理的。由于我的深度值以 Blender 米为单位,但 Open3D 显然需要毫米,因此缩放应该是有意义的。

如果您能给我调试它的任何帮助,我将不胜感激。但是,如果您要向我指出一个更好的工作库以从图像创建 3D 点云的方向,我也不介意。我在 Open3D 中找到的文档充其量是缺乏的。

4

2 回答 2

2

简而言之,Open3D 期望您的 3 通道彩色图像uint8类型为.

否则,它将返回一个空点云,从而导致您看到的空白窗口。


更新 2020-3-27,在我的时区深夜:)

现在您已经提供了代码,让我们开始吧!

从您的函数名称来看,我猜您正在使用 Open3D0.7.0或类似的东西。我提供的代码在0.9.0. 更改了一些函数名称并添加了新功能

当我运行你的代码时0.9.0(当然经过一些小的修改),有一个RuntimeError

RuntimeError: [Open3D ERROR] [CreatePointCloudFromRGBDImage] Unsupported image format.

我们可以从 Open3D0.9.0 源代码中看到,您的彩色图像必须是3 个通道,每个通道只占用 1 个字节(uint8),或者是 1 个通道,占用 4 个字节(浮点数,这意味着强度图像):

std::shared_ptr<PointCloud> PointCloud::CreateFromRGBDImage(
        const RGBDImage &image,
        const camera::PinholeCameraIntrinsic &intrinsic,
        const Eigen::Matrix4d &extrinsic /* = Eigen::Matrix4d::Identity()*/,
        bool project_valid_depth_only) {
    if (image.depth_.num_of_channels_ == 1 &&
        image.depth_.bytes_per_channel_ == 4) {
        if (image.color_.bytes_per_channel_ == 1 &&
            image.color_.num_of_channels_ == 3) {
            return CreatePointCloudFromRGBDImageT<uint8_t, 3>(
                    image, intrinsic, extrinsic, project_valid_depth_only);
        } else if (image.color_.bytes_per_channel_ == 4 &&
                   image.color_.num_of_channels_ == 1) {
            return CreatePointCloudFromRGBDImageT<float, 1>(
                    image, intrinsic, extrinsic, project_valid_depth_only);
        }
    }
    utility::LogError(
            "[CreatePointCloudFromRGBDImage] Unsupported image format.");
    return std::make_shared<PointCloud>();
}

否则,会出现我遇到的错误。
但是,在 的版本中0.7.0源代码是:

std::shared_ptr<PointCloud> CreatePointCloudFromRGBDImage(
        const RGBDImage &image,
        const camera::PinholeCameraIntrinsic &intrinsic,
        const Eigen::Matrix4d &extrinsic /* = Eigen::Matrix4d::Identity()*/) {
    if (image.depth_.num_of_channels_ == 1 &&
        image.depth_.bytes_per_channel_ == 4) {
        if (image.color_.bytes_per_channel_ == 1 &&
            image.color_.num_of_channels_ == 3) {
            return CreatePointCloudFromRGBDImageT<uint8_t, 3>(image, intrinsic,
                                                              extrinsic);
        } else if (image.color_.bytes_per_channel_ == 4 &&
                   image.color_.num_of_channels_ == 1) {
            return CreatePointCloudFromRGBDImageT<float, 1>(image, intrinsic,
                                                            extrinsic);
        }
    }   
    utility::PrintDebug(
            "[CreatePointCloudFromRGBDImage] Unsupported image format.\n");
    return std::make_shared<PointCloud>();
}

这意味着 Open3D 仍然不支持它,但它只会警告你。并且仅在调试模式下!
之后,它将返回一个空点云。(实际上两个版本都这样做。)这解释了空白窗口。

现在你应该知道,你可以convert_rgb_to_intensity=True成功并成功。尽管您仍然应该首先标准化您的彩色图像。
或者您可以将彩色图像转换为范围[0, 255]和类型uint8
两者都会起作用。

现在我希望一切都清楚了。万岁!

PS 实际上,我通常发现 Open3D 源代码易于阅读。而且,如果您了解 C++,那么无论何时发生这种情况,您都可以阅读它。


2020 年 3 月 27 日更新:

我无法重现您的结果,也不知道为什么会发生(您应该提供一个最小的可重现示例)。

使用您在评论中提供的图像,我编写了以下代码并且效果很好。如果它仍然无法在您的计算机上运行,​​则可能是您的 Open3D 损坏了。

(我不熟悉 .exr 图像,因此数据提取可能很难看 :)

import Imath
import array
import OpenEXR

import numpy as np
import open3d as o3d


# extract data from exr files
f = OpenEXR.InputFile('frame.exr')
FLOAT = Imath.PixelType(Imath.PixelType.FLOAT)
cs = list(f.header()['channels'].keys())  # channels
data = [np.array(array.array('f', f.channel(c, FLOAT))) for c in cs] 
data = [d.reshape(720, 1280) for d in data]
rgb = np.concatenate([data[i][:, :, np.newaxis] for i in [3, 2, 1]], axis=-1)
# rgb /= np.max(rgb)  # this will result in a much darker image
np.clip(rgb, 0, 1.0)  # to better visualize as HDR is not supported?

# get rgbd image
img = o3d.geometry.Image((rgb * 255).astype(np.uint8))
depth = o3d.geometry.Image((data[-1] * 1000).astype(np.uint16))
rgbd = o3d.geometry.RGBDImage.create_from_color_and_depth(img, depth, convert_rgb_to_intensity=False)

# some guessed intrinsics
intr = o3d.open3d.camera.PinholeCameraIntrinsic(1280, 720, fx=570, fy=570, cx=640, cy=360)

# get point cloud and visualize
pcd = o3d.geometry.PointCloud.create_from_rgbd_image(rgbd, intr)
o3d.visualization.draw_geometries([pcd])

结果是:

在此处输入图像描述


原答案:

你误解了 的意思depth_scale

使用这一行:
depth = o3d.geometry.Image(np.array(img_array[:, :, 3]*1000).astype('uint16'))


Open3D文档深度值将首先被缩放,然后被截断。实际上,这意味着您的深度图像中的像素值将首先除此数字而不是相乘,正如您在 Open3D源代码中看到的那样:

std::shared_ptr<Image> Image::ConvertDepthToFloatImage(
        double depth_scale /* = 1000.0*/, double depth_trunc /* = 3.0*/) const {
    // don't need warning message about image type
    // as we call CreateFloatImage
    auto output = CreateFloatImage();
    for (int y = 0; y < output->height_; y++) {
        for (int x = 0; x < output->width_; x++) {
            float *p = output->PointerAt<float>(x, y);
            *p /= (float)depth_scale;
            if (*p >= depth_trunc) *p = 0.0f;
        }
    }
    return output;
}

实际上,我们通常理所当然地认为深度图像中的值应该是整数(我想这就是为什么 Open3D 在他们的文档中没有明确指出这一点的原因),因为浮点图像不太常见。
您不能将1.34米存储在 .png 图像中,因为它们会失去精度。因此,我们存储1340深度图像,稍后的过程将首先将其转换回1.34.


至于您的深度图像的相机内在函数,我想当您创建它时,Blender 中会有配置参数。我对Blender不熟悉,所以我不会谈论它。但是,如果您不了解一般的相机内在函数,您可能想看看这个

于 2020-03-26T03:45:44.547 回答
0

@Jing Zhaos 的回答奏效了!但是,我假设他的 Open3D 版本与我的不同,我不得不像这样更改 2 个函数调用(并稍微更改了命名):

exr_img = OpenEXR.InputFile('frame0100.exr')
cs = list(exr_img.header()['channels'].keys())  # channels
FLOAT = Imath.PixelType(Imath.PixelType.FLOAT)

img_data = [np.array(array.array('f', exr_img.channel(c, FLOAT))) for c in cs]
img_data = [d.reshape(720,1280) for d in img_data]

rgb = np.concatenate([img_data[i][:, :, np.newaxis] for i in [3, 2, 1]], axis=-1)
np.clip(rgb, 0, 1.0)  # to better visualize as HDR is not supported?


img = o3d.geometry.Image((rgb * 255).astype(np.uint8))
depth = o3d.geometry.Image((img_data[-1] * 1000).astype(np.uint16))
#####rgbd = o3d.geometry.RGBDImage.create_from_color_and_depth(img, depth, convert_rgb_to_intensity=False)

rgbd = o3d.create_rgbd_image_from_color_and_depth(img, depth, convert_rgb_to_intensity=False)


# some guessed intrinsics
intr = o3d.open3d.camera.PinholeCameraIntrinsic(1280, 720, fx=570, fy=570, cx=640, cy=360)

# get point cloud and visualize
#####pcd = o3d.geometry.PointCloud.create_from_rgbd_image(rgbd, intr)
pcd = o3d.create_point_cloud_from_rgbd_image(rgbd, intr)


o3d.visualization.draw_geometries([pcd])

否则我得到以下错误:

AttributeError: type object 'open3d.open3d.geometry.RGBDImage' has no attribute 'create_from_color_and_depth'

希望这也可以帮助其他人使用我的 Python/Open3D 版本。不太确定我的代码中的错误到底在哪里,但我很满意有可用的代码。再次感谢赵晶!

于 2020-03-27T14:30:57.620 回答