Skip to content

Commit d022ad9

Browse files
committed
(WIP) Created script for testing the RGBD integration capabilities of Open3D.
Based on this example: http://www.open3d.org/docs/tutorial/Advanced/rgbd_integration.html I created a get_points2(...) and process_folder2(...) based on 'pointcloud_compute_with_normals.py': microsoft#94
1 parent 4b2bcc7 commit d022ad9

File tree

1 file changed

+326
-0
lines changed

1 file changed

+326
-0
lines changed

Samples/py/test_read_model.py

Lines changed: 326 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,326 @@
1+
#!/usr/bin/env python2
2+
3+
# %% init
4+
5+
import os
6+
import cv2
7+
import pylab as plt
8+
9+
import re
10+
11+
import numpy as np
12+
from open3d import Image, \
13+
PointCloud, \
14+
Vector3dVector, \
15+
draw_geometries, \
16+
estimate_normals, \
17+
read_point_cloud, \
18+
write_point_cloud, \
19+
KDTreeSearchParamKNN, \
20+
KDTreeSearchParamHybrid, \
21+
orient_normals_towards_camera_location
22+
23+
import open3d
24+
25+
from read_model import read_model, qvec2rotmat
26+
from pcloud_compute import *
27+
28+
#import pickle
29+
30+
def get_sensor_poses(args, cam, time_stamp):
31+
# Input folder
32+
folder = args.workspace_path
33+
cam_folder = os.path.join(folder, cam)
34+
assert(os.path.exists(cam_folder))
35+
36+
# From frame to world coordinate system
37+
sensor_poses = read_sensor_poses(os.path.join(folder, cam + ".csv"),
38+
identity_camera_to_image=True)
39+
40+
world2cam = sensor_poses[time_stamp]
41+
42+
return world2cam
43+
44+
45+
# %%
46+
def get_points2(img, us, vs, cam2world, depth_range):
47+
distance_img = pgm2distance(img, encoded=False)
48+
empty = np.zeros((1024,1024))
49+
depth_img = (empty).astype(np.float32)
50+
51+
52+
if cam2world is not None:
53+
R = cam2world[:3, :3]
54+
t = cam2world[:3, 3]
55+
else:
56+
R, t = np.eye(3), np.zeros(3)
57+
58+
# from '[-1,1]x[-1,1]' to '[0,xdim]x[0,ydim]'
59+
xdim = depth_img.shape[1]
60+
ydim = depth_img.shape[0]
61+
fx = xdim/2
62+
fy = ydim/2
63+
cx = fx
64+
cy = fy
65+
cam_intrinsics = np.identity(3)
66+
cam_intrinsics[0,0] = fx
67+
cam_intrinsics[1,1] = fy
68+
cam_intrinsics[0,2] = cx
69+
cam_intrinsics[1,2] = cy
70+
71+
72+
points = []
73+
for i in np.arange(distance_img.shape[0]):
74+
for j in np.arange(distance_img.shape[1]):
75+
x = us[i, j]
76+
y = vs[i, j]
77+
D = distance_img[i, j]
78+
z = - float(D) / np.sqrt(x*x + y*y + 1)
79+
80+
if np.isinf(x) or np.isinf(y) or D < depth_range[0] or D > depth_range[1]:
81+
# depth_img[i,j] = 0
82+
continue
83+
84+
85+
# print("arr", np.array([x, y, 1.]))
86+
87+
point2D = cam_intrinsics.dot(np.array([x, y, 1.]))
88+
ii = np.int32(point2D[1])
89+
jj = np.int32(point2D[0])
90+
if ii < 0 or jj < 0:
91+
# print "What!!!"
92+
# print("original", np.array([x, y, 1.])) # (u,v) bigger than 1 or -1 ?? Why?!!
93+
# print("point2D", point2D)
94+
continue
95+
depth_img[ii,jj] = - z
96+
97+
98+
# 3D point in camera coordinate system
99+
point = np.array([x, y, 1.]) * z
100+
101+
# Camera to World
102+
point = np.dot(R, point) + t
103+
104+
points.append(point)
105+
106+
# # for debug
107+
# plt.subplot(1, 3, 1)
108+
# plt.title('img')
109+
# plt.imshow(img)
110+
# plt.subplot(1, 3, 2)
111+
# plt.title('distance_img')
112+
# plt.imshow(distance_img)
113+
# plt.subplot(1, 3, 3)
114+
# plt.title('depth_img')
115+
# plt.imshow(depth_img)
116+
# plt.show()
117+
118+
return np.vstack(points), depth_img
119+
120+
121+
def process_folder2(args, cam):
122+
# Input folder
123+
folder = args.workspace_path
124+
cam_folder = os.path.join(folder, cam)
125+
assert(os.path.exists(cam_folder))
126+
# Output folder
127+
output_folder = os.path.join(args.output_path, cam)
128+
if not os.path.exists(output_folder):
129+
os.makedirs(output_folder)
130+
131+
# Get camera projection info
132+
bin_path = os.path.join(args.workspace_path, "%s_camera_space_projection.bin" % cam)
133+
134+
# From frame to world coordinate system
135+
sensor_poses = None
136+
if not args.ignore_sensor_poses:
137+
sensor_poses = read_sensor_poses(os.path.join(folder, cam + ".csv"), identity_camera_to_image=True)
138+
139+
# Get appropriate depth thresholds
140+
depth_range = LONG_THROW_RANGE if 'long' in cam else SHORT_THROW_RANGE
141+
142+
# Get depth paths
143+
depth_paths = sorted(glob(os.path.join(cam_folder, "*pgm")))
144+
if args.max_num_frames == -1:
145+
args.max_num_frames = len(depth_paths)
146+
depth_paths = depth_paths[args.start_frame:(args.start_frame + args.max_num_frames)]
147+
148+
us = vs = None
149+
# Process paths
150+
merge_points = args.merge_points
151+
rewrite = args.rewrite
152+
use_cache = args.use_cache
153+
points_merged = []
154+
normals_merged = []
155+
156+
volume = open3d.ScalableTSDFVolume(voxel_length = 4.0 / 512.0,
157+
sdf_trunc = 0.1, color_type = open3d.TSDFVolumeColorType.None)
158+
# depth_sampling_stride = 16)
159+
160+
for i_path, path in enumerate(depth_paths):
161+
output_suffix = "_%s" % args.output_suffix if len(args.output_suffix) else ""
162+
# pcloud_output_path = os.path.join(output_folder, os.path.basename(path).replace(".pgm", "%s.obj" % output_suffix))
163+
164+
pcloud_output_path = \
165+
os.path.join(output_folder,
166+
os.path.basename(path).replace(
167+
".pgm","%s.ply" % output_suffix))
168+
print("File: " + pcloud_output_path)
169+
170+
# if file exist
171+
output_file_exist = os.path.exists(pcloud_output_path)
172+
if output_file_exist and use_cache:
173+
print("Progress (file cache): %d/%d" % (i_path+1, len(depth_paths)))
174+
pcd = read_point_cloud(pcloud_output_path)
175+
points = pcd.points
176+
normals = pcd.normals
177+
else:
178+
print("Progress: %d/%d" % (i_path+1, len(depth_paths)))
179+
img = cv2.imread(path, -1)
180+
if us is None or vs is None:
181+
us, vs = parse_projection_bin(bin_path, img.shape[1], img.shape[0])
182+
cam2world = get_cam2world(path, sensor_poses) if sensor_poses is not None else None
183+
points, depth_img = get_points2(img, us, vs, cam2world, depth_range)
184+
185+
# get normals (orientes towards the camera)
186+
t = cam2world[:3, 3]
187+
cam_center = t # (0,0,0) is the camera center
188+
189+
pcd = PointCloud()
190+
pcd.points = Vector3dVector(points)
191+
192+
# compute normal of the point cloud (per camera)
193+
estimate_normals(pcd,
194+
search_param =
195+
# KDTreeSearchParamKNN(knn = 30))
196+
KDTreeSearchParamHybrid(radius = 0.2, max_nn = 30))
197+
198+
# orient normals
199+
orient_normals_towards_camera_location(pcd, cam_center)
200+
201+
202+
203+
# get normal as python array
204+
normals = np.asarray(pcd.normals)
205+
206+
# from '[-1,1]x[-1,1]' to '[0,xdim]x[0,ydim]'
207+
w = depth_img.shape[1] # xdim
208+
h = depth_img.shape[0] # ydim
209+
fx = w/2
210+
fy = h/2
211+
cx = fx
212+
cy = fy
213+
214+
cam_intrinsic = open3d.PinholeCameraIntrinsic()
215+
cam_intrinsic.set_intrinsics(w, h, fx, fy, cx, cy)
216+
# print cam_intrinsic.intrinsic_matrix
217+
218+
219+
# convert to Open3d Image
220+
depth = open3d.Image((depth_img).astype(np.float32))
221+
222+
223+
# create dummy variable
224+
# color_type = open3d.TSDFVolumeColorType.None
225+
# we aren't using color
226+
color = Image( (np.ones((w,h))).astype(np.uint8) )
227+
# color = depth
228+
rgbd = open3d.create_rgbd_image_from_color_and_depth(color, depth,
229+
depth_scale=1.0,
230+
depth_trunc=4,
231+
convert_rgb_to_intensity = False);
232+
233+
# rgbd = open3d.create_rgbd_image_from_color_and_depth(color, depth,
234+
# depth_scale=1.0,
235+
# depth_trunc = depth_range[1],
236+
# convert_rgb_to_intensity = True);
237+
#
238+
239+
# This transformation is requiere before of the line:
240+
#
241+
# depth_img[... , ...] = - z
242+
#
243+
# in get_points(...)
244+
#
245+
img2cam = np.array(
246+
[[-1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]])
247+
248+
cam2world_new = np.dot(cam2world, img2cam)
249+
250+
# print("volume.integrate")
251+
volume.integrate(rgbd, cam_intrinsic, np.linalg.inv(cam2world_new) )
252+
253+
if merge_points:
254+
points_merged.extend(points)
255+
normals_merged.extend(normals)
256+
257+
if rewrite:
258+
write_point_cloud(pcloud_output_path, pcd)
259+
260+
261+
return points_merged, normals_merged, volume
262+
263+
264+
# %%
265+
class ArgsTest:
266+
workspace_path = ''
267+
output_path = ''
268+
ignore_sensor_poses = False
269+
start_frame = 0
270+
max_num_frames = -1
271+
output_suffix = ''
272+
merge_points = True
273+
use_cache = False
274+
rewrite = False
275+
276+
args = ArgsTest()
277+
#args.workspace_path = "../../data/HoloLensRecording__2018_10_23__14_05_39/"
278+
args.workspace_path = "../../data/HoloLens_recording_sample/"
279+
280+
args.output_path = args.workspace_path
281+
282+
args.start_frame = 0
283+
args.max_num_frames = 5
284+
285+
286+
# %%
287+
cam = 'long_throw_depth'
288+
points_merged, normals_merged, volume = process_folder2(args, cam)
289+
290+
pcd_merged = PointCloud()
291+
pcd_merged.points = Vector3dVector(points_merged)
292+
pcd_merged.normals = Vector3dVector(normals_merged)
293+
294+
#pcd.paint_uniform_color([1, 0.706, 0])
295+
#draw_geometries([pcd_merged])
296+
297+
298+
#folder = args.workspace_path
299+
#write_point_cloud(folder + "\\" + cam + ".ply", pcd)
300+
301+
#draw_geometries([pcd2])
302+
303+
#pcd_vol = volume.extract_point_cloud()
304+
#pcd_vol.transform([[-1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]])
305+
#print pcd_vol
306+
#draw_geometries([pcd_vol])
307+
308+
309+
# %%
310+
pcd_vol = volume.extract_point_cloud()
311+
#pcd_vol.transform([[-1, 0, 0, 0], [0, -1, 0, 0], [0, 0, -1, 0], [0, 0, 0, 1]])
312+
#print pcd_vol
313+
pcd_merged.paint_uniform_color([1, 0.706, 0])
314+
draw_geometries([pcd_vol, pcd_merged])
315+
316+
317+
# %%
318+
draw_geometries([pcd_vol])
319+
320+
321+
# %%
322+
mesh = volume.extract_triangle_mesh()
323+
mesh.compute_vertex_normals()
324+
print mesh
325+
draw_geometries([mesh])
326+

0 commit comments

Comments
 (0)