Пустой экран при генерации облака точек из изображения с Open3D - PullRequest
0 голосов
/ 25 марта 2020

Итак, я попытался создать облако точек с библиотекой Open3D в python, и, в конце концов, это всего лишь две строки, как указано в здесь , , но когда я запускаю свой код ( см. ниже) все, что я получаю, это всплывающий белый экран. Я запустил его в записной книжке Jupyter, но запуск его в скрипте 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])

Пожалуйста, не обращайте внимания на хакерский способ импорта данных, так как я новичок в Open3D и сам производил данные, я делал это шаг за шагом для проверок и подтверждения целостности данных

The picture I tried to use with some statistics

Я предполагаю, что это может иметь отношение к моим параметрам для камеры-обскуры. Tbh, я понятия не имею какие параметры будут правильными, за исключением того, что cy, cy должны быть центром изображения, а fx, fy должны быть разумными . Поскольку мои значения глубины указаны в метрах Блендера, но Open3D, очевидно, ожидает миллиметры, масштабирование должно иметь смысл.

Я был бы признателен, если бы вы могли помочь мне в его отладке. Но если бы вы указали мне путь к лучшей рабочей библиотеке для создания трехмерных облаков точек из изображений, я бы тоже не возражал. Документация, которую я нашел с Open3D, в лучшем случае отсутствует.

Ответы [ 2 ]

0 голосов
/ 27 марта 2020

@ Ответ Цзина Чжаоса сработал! Однако я полагаю, что его версия 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. Не совсем уверен, где именно ошибка в моем коде, но я удовлетворен тем, что могу использовать код. Еще раз спасибо Цзин Чжао!

0 голосов
/ 26 марта 2020

Короче говоря, Open3D ожидает, что ваше 3-канальное цветное изображение будет иметь uint8 тип .

В противном случае будет возвращено пустое облако точек, в результате чего вы получите пустое окно. см.


Обновление 2020-3-27, поздно ночью в моем часовом поясе:)

Теперь, когда вы предоставили свой код, давайте Погрузитесь в!

Из названий ваших функций, я полагаю, вы используете Open3D 0.7.0 или что-то в этом роде. Код, который я предоставил, находится в 0.9.0. Изменены некоторые имена функций и добавлено новых функций .

Когда я запускаю ваш код в 0.9.0 (после некоторых незначительных изменений, конечно), появляется RuntimeError :

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

И мы можем видеть из источника Open3D 0.9.0 , что ваше цветное изображение должно иметь 3 канала и берут только 1 байт каждый (uint8) или из 1 канала и берут 4 байта (float, что означает интенсивность изображения):

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])

И результат:

enter image description here


Оригинал Ответ:

Вы неправильно поняли значение depth_scale.

Используйте эту строку:
depth = o3d.geometry.Image(np.array(img_array[:, :, 3]*1000).astype('uint16'))


В документации Open3D указано значения глубины сначала масштабируются, а затем усекаются . На самом деле это означает, что значения пикселей в вашем изображении глубины будут сначала делить это число , а не умножаться, как вы можете видеть в Open3D source :

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 при его создании. Я не знаком с Блендером, поэтому я не буду об этом говорить. Однако, если вы не понимаете общих черт камеры, вы можете взглянуть на this .

...