1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
# ----------------------------------------------------------------------------
# - Open3D: www.open3d.org -
# ----------------------------------------------------------------------------
# Copyright (c) 2018-2024 www.open3d.org
# SPDX-License-Identifier: MIT
# ----------------------------------------------------------------------------
import open3d as o3d
import numpy as np
import tempfile
import os
def test_apply_depth_noise_model():
class Simulator:
"""Original implementation of the simulator:
http://redwood-data.org/indoor/data/simdepth.py
"""
def loaddistmodel(self, fname):
data = np.loadtxt(fname, comments='%', skiprows=5)
dist = np.empty([80, 80, 5])
for y in range(0, 80):
for x in range(0, 80):
idx = (y * 80 + x) * 23 + 3
if (data[idx:idx + 5] < 8000).all():
dist[y, x, :] = 0
else:
dist[y, x, :] = data[idx + 15:idx + 20]
self.model = dist
def undistort(self, x, y, z):
i2 = int((z + 1) / 2)
i1 = i2 - 1
a = (z - (i1 * 2 + 1)) / 2
x = int(x / 8)
y = int(y / 6)
f = (1 - a) * self.model[y, x, min(max(i1, 0), 4)] + a * self.model[
y, x, min(i2, 4)]
if f == 0:
return 0
else:
return z / f
def simulate(self,
inputpng,
outputpng,
depth_scale=1000.0,
deterministic=False):
a = o3d.t.io.read_image(inputpng).as_tensor()
a = a.numpy().squeeze().astype(np.float32) / depth_scale
b = np.copy(a)
it = np.nditer(a, flags=['multi_index'], op_flags=['writeonly'])
while not it.finished:
# pixel shuffle
# here, 639 == width - 1, 479 == height - 1
if deterministic:
x = min(max(round(it.multi_index[1] + 0), 0), 639)
y = min(max(round(it.multi_index[0] + 0), 0), 479)
else:
x = min(
max(
round(it.multi_index[1] +
np.random.normal(0, 0.25)), 0), 639)
y = min(
max(
round(it.multi_index[0] +
np.random.normal(0, 0.25)), 0), 479)
# downsample
d = b[y - y % 2, x - x % 2]
# distortion
d = self.undistort(x, y, d)
# quantization and high freq noise
if d == 0:
it[0] = 0
else:
if deterministic:
it[0] = 35.130 * 8 / round((35.130 / d + 0) * 8)
else:
it[0] = 35.130 * 8 / round(
(35.130 / d + np.random.normal(0, 0.027778)) * 8)
it.iternext()
a = (a * depth_scale).astype(np.uint16)
a = np.expand_dims(a, axis=2)
o3d.t.io.write_image(outputpng, o3d.t.geometry.Image(a))
# Load dataset.
data = o3d.data.RedwoodIndoorLivingRoom1()
noise_model_path = data.noise_model_path
depth_scale = 1000.0
# Source image.
im_src_path = data.depth_paths[0]
im_src_uint16 = o3d.t.io.read_image(im_src_path).as_tensor().numpy()
im_src_float32 = im_src_uint16.astype(np.float32) / depth_scale
# Simulate "ground truth" noise depth image, with deterministic noise.
# We use the original simulator, which requires input and output path.
# See http://redwood-data.org/indoor/data/simdepth.py for the original
# implementation.
gt_simulator = Simulator()
gt_simulator.loaddistmodel(noise_model_path)
with tempfile.TemporaryDirectory() as dst_dir:
im_dst_path = os.path.join(dst_dir, "noisy_depth.png")
gt_simulator.simulate(im_src_path,
im_dst_path,
depth_scale=1000.0,
deterministic=True)
im_dst_gt = o3d.t.io.read_image(im_dst_path).as_tensor().numpy()
im_dst_gt = im_dst_gt.astype(np.float32) / depth_scale
# Our C++ implementation of the simulator.
simulator = o3d.t.io.DepthNoiseSimulator(noise_model_path)
simulator.enable_deterministic_debug_mode()
np.testing.assert_allclose(simulator.noise_model.numpy(),
gt_simulator.model)
# With uint16 input.
im_src = o3d.t.geometry.Image(im_src_uint16)
im_dst = simulator.simulate(im_src, depth_scale=depth_scale)
im_dst = im_dst.as_tensor().numpy().astype(np.float32) / depth_scale
np.testing.assert_allclose(im_dst, im_dst_gt)
# With float32 input.
im_src = o3d.t.geometry.Image(im_src_float32)
im_dst = simulator.simulate(im_src, depth_scale=1.0)
im_dst = im_dst.as_tensor().numpy()
im_dst = (im_dst * depth_scale).astype(np.uint16).astype(
np.float32) / depth_scale # Simulate rounding integers.
np.testing.assert_allclose(im_dst, im_dst_gt)
|