Skip to content

Commit c5edfaa

Browse files
authored
Merge pull request #45 from lbr-stack/dev-hydra-depth
Replaces point cloud by depth image
2 parents 19e2706 + 23f1268 commit c5edfaa

File tree

9 files changed

+157
-81
lines changed

9 files changed

+157
-81
lines changed

README.md

Lines changed: 41 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -119,23 +119,26 @@ Next:
119119
> In these examples, the [lbr_fri_ros2_stack](https://github.com/lbr-stack/lbr_fri_ros2_stack/) is used. Make sure to follow [Quick Start](https://github.com/lbr-stack/lbr_fri_ros2_stack/#quick-start) first. However, you can also use your own robot description files.
120120

121121
### Segment
122-
This is a required step to generate robot masks (also support SAM 2: `rr-sam2`).
122+
This is a required step to generate robot masks (also supports SAM: `rr-sam`).
123123

124124
```shell
125-
rr-sam \
126-
--path <path_to_images> \
125+
rr-sam2 \
126+
--path test/data/lbr_med7/zed2i \
127127
--pattern "*_image_*.png" \
128-
--checkpoint <full_path_to_checkpoint>/*.pth
128+
--n-positive-samples 5 \
129+
--n-negative-samples 5 \
130+
--device cuda
129131
```
130132

131133
### Hydra Robust ICP
132134
The Hydra robust ICP implements a point-to-plane ICP registration on a Lie algebra. It does not use rendering and can also be used on CPU.
133135

134136
```shell
135137
rr-hydra \
136-
--path test/data/lbr_med7/zed2i/high_res \
137-
--mask-pattern mask_*.png \
138-
--xyz-pattern xyz_*.npy \
138+
--camera-info-file test/data/lbr_med7/zed2i/left_camera_info.yaml \
139+
--path test/data/lbr_med7/zed2i \
140+
--mask-pattern mask_sam2_left_image_*.png \
141+
--depth-pattern depth_*.npy \
139142
--joint-states-pattern joint_states_*.npy \
140143
--ros-package lbr_description \
141144
--xacro-path urdf/med7/med7.xacro \
@@ -155,7 +158,7 @@ The camera swarm optimization can serve for finding an initial guess to [Monocul
155158

156159
```shell
157160
rr-cam-swarm \
158-
--n-cameras 50 \
161+
--n-cameras 100 \
159162
--min-distance 0.5 \
160163
--max-distance 3.0 \
161164
--angle-range 3.141 \
@@ -170,11 +173,11 @@ rr-cam-swarm \
170173
--end-link-name lbr_link_7 \
171174
--target-reduction 0.95 \
172175
--scale 0.25 \
173-
--camera-info-file test/data/lbr_med7/zed2i/stereo_data/left_camera_info.yaml \
174-
--path test/data/lbr_med7/zed2i/stereo_data \
175-
--image-pattern left_img_*.png \
176-
--joint-states-pattern joint_state_*.npy \
177-
--mask-pattern left_mask_*.png \
176+
--camera-info-file test/data/lbr_med7/zed2i/left_camera_info.yaml \
177+
--path test/data/lbr_med7/zed2i \
178+
--image-pattern left_image_*.png \
179+
--joint-states-pattern joint_states_*.npy \
180+
--mask-pattern mask_sam2_left_image_*.png \
178181
--output-file HT_cam_swarm.npy
179182
```
180183

@@ -189,19 +192,19 @@ This monocular differentiable rendering refinement requires a good initial estim
189192
```shell
190193
rr-mono-dr \
191194
--optimizer SGD \
192-
--lr 0.01 \
195+
--lr 0.001 \
193196
--max-iterations 100 \
194197
--display-progress \
195198
--ros-package lbr_description \
196199
--xacro-path urdf/med7/med7.xacro \
197200
--root-link-name lbr_link_0 \
198201
--end-link-name lbr_link_7 \
199-
--camera-info-file test/data/lbr_med7/zed2i/high_res/camera_info.yaml \
200-
--extrinsics-file test/data/lbr_med7/zed2i/high_res/HT_hydra_robust.npy \
201-
--path test/data/lbr_med7/zed2i/high_res \
202-
--image-pattern image_*.png \
202+
--camera-info-file test/data/lbr_med7/zed2i/left_camera_info.yaml \
203+
--extrinsics-file test/data/lbr_med7/zed2i/HT_hydra_robust.npy \
204+
--path test/data/lbr_med7/zed2i \
205+
--image-pattern left_image_*.png \
203206
--joint-states-pattern joint_states_*.npy \
204-
--mask-pattern mask_*.png \
207+
--mask-pattern mask_sam2_left_image_*.png \
205208
--output-file HT_dr.npy
206209
```
207210

@@ -216,23 +219,23 @@ This stereo differentiable rendering refinement requires a good initial estimate
216219
```shell
217220
rr-stereo-dr \
218221
--optimizer SGD \
219-
--lr 0.01 \
222+
--lr 0.001 \
220223
--max-iterations 100 \
221224
--display-progress \
222225
--ros-package lbr_description \
223226
--xacro-path urdf/med7/med7.xacro \
224227
--root-link-name lbr_link_0 \
225228
--end-link-name lbr_link_7 \
226-
--left-camera-info-file test/data/lbr_med7/zed2i/stereo_data/left_camera_info.yaml \
227-
--right-camera-info-file test/data/lbr_med7/zed2i/stereo_data/right_camera_info.yaml \
228-
--left-extrinsics-file test/data/lbr_med7/zed2i/stereo_data/HT_cam_swarm.npy \
229-
--right-extrinsics-file test/data/lbr_med7/zed2i/stereo_data/HT_right_to_left.npy \
230-
--path test/data/lbr_med7/zed2i/stereo_data \
231-
--left-image-pattern left_img_*.png \
232-
--right-image-pattern right_img_*.png \
233-
--joint-states-pattern joint_state_*.npy \
234-
--left-mask-pattern left_mask_*.png \
235-
--right-mask-pattern right_mask_*.png \
229+
--left-camera-info-file test/data/lbr_med7/zed2i/left_camera_info.yaml \
230+
--right-camera-info-file test/data/lbr_med7/zed2i/right_camera_info.yaml \
231+
--left-extrinsics-file test/data/lbr_med7/zed2i/HT_hydra_robust.npy \
232+
--right-extrinsics-file test/data/lbr_med7/zed2i/HT_right_to_left.npy \
233+
--path test/data/lbr_med7/zed2i \
234+
--left-image-pattern left_image_*.png \
235+
--right-image-pattern right_image_*.png \
236+
--joint-states-pattern joint_states_*.npy \
237+
--left-mask-pattern mask_sam2_left_image_*.png \
238+
--right-mask-pattern mask_sam2_right_image_*.png \
236239
--left-output-file HT_left_dr.npy \
237240
--right-output-file HT_right_dr.npy
238241
```
@@ -253,13 +256,13 @@ rr-render \
253256
--xacro-path urdf/med7/med7.xacro \
254257
--root-link-name lbr_link_0 \
255258
--end-link-name lbr_link_7 \
256-
--camera-info-file test/data/lbr_med7/zed2i/stereo_data/left_camera_info.yaml \
257-
--extrinsics-file test/data/lbr_med7/zed2i/stereo_data/HT_left_dr.npy \
258-
--images-path test/data/lbr_med7/zed2i/stereo_data \
259-
--joint-states-path test/data/lbr_med7/zed2i/stereo_data \
260-
--image-pattern left_img_*.png \
261-
--joint-states-pattern joint_state_*.npy \
262-
--output-path test/data/lbr_med7/zed2i/stereo_data
259+
--camera-info-file test/data/lbr_med7/zed2i/left_camera_info.yaml \
260+
--extrinsics-file test/data/lbr_med7/zed2i/HT_left_dr.npy \
261+
--images-path test/data/lbr_med7/zed2i \
262+
--joint-states-path test/data/lbr_med7/zed2i \
263+
--image-pattern left_image_*.png \
264+
--joint-states-pattern joint_states_*.npy \
265+
--output-path test/data/lbr_med7/zed2i
263266
```
264267

265268
## Testing
@@ -272,7 +275,7 @@ To run Hydra robust ICP on provided `xarm` and `realsense` data, run
272275
rr-hydra \
273276
--path test/data/xarm/realsense \
274277
--mask-pattern mask_*.png \
275-
--xyz-pattern xyz_*.npy \
278+
--depth-pattern depth_*.npy \
276279
--joint-states-pattern joint_state_*.npy \
277280
--ros-package xarm_description \
278281
--xacro-path urdf/xarm_device.urdf.xacro \

roboreg/cli/rr_cam_swarm.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ def fitness_closure() -> torch.Tensor:
370370
)
371371
renders = scene.observe_from("camera").squeeze()
372372
fitness = (
373-
soft_dice_loss(renders, masks)
373+
soft_dice_loss(renders.unsqueeze(-1), masks.unsqueeze(-1))
374374
.view(args.n_cameras, n_joint_states)
375375
.mean(dim=1)
376376
)

roboreg/cli/rr_hydra.py

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,27 @@
77

88
from roboreg.differentiable import TorchKinematics, TorchMeshContainer
99
from roboreg.hydra_icp import hydra_centroid_alignment, hydra_robust_icp
10-
from roboreg.io import URDFParser, parse_hydra_data
10+
from roboreg.io import URDFParser, parse_camera_info, parse_hydra_data
1111
from roboreg.util import (
1212
RegistrationVisualizer,
1313
clean_xyz,
1414
compute_vertex_normals,
15+
depth_to_xyz,
1516
from_homogeneous,
17+
generate_ht_optical,
1618
mask_extract_boundary,
19+
to_homogeneous,
1720
)
1821

1922

2023
def args_factory() -> argparse.Namespace:
2124
parser = argparse.ArgumentParser()
25+
parser.add_argument(
26+
"--camera-info-file",
27+
type=str,
28+
required=True,
29+
help="Path to the camera parameters, <path_to>/camera_info.yaml.",
30+
)
2231
parser.add_argument("--path", type=str, required=True, help="Path to the data.")
2332
parser.add_argument(
2433
"--mask-pattern",
@@ -27,7 +36,10 @@ def args_factory() -> argparse.Namespace:
2736
help="Mask file pattern.",
2837
)
2938
parser.add_argument(
30-
"--xyz-pattern", type=str, default="xyz_*.npy", help="XYZ file pattern."
39+
"--depth-pattern",
40+
type=str,
41+
default="depth_*.npy",
42+
help="Depth file pattern. Note that depth values are expected in meters.",
3143
)
3244
parser.add_argument(
3345
"--joint-states-pattern",
@@ -113,12 +125,13 @@ def main():
113125
device = "cuda" if torch.cuda.is_available() else "cpu"
114126

115127
# load data
116-
joint_states, masks, xyzs = parse_hydra_data(
128+
joint_states, masks, depths = parse_hydra_data(
117129
path=args.path,
118130
joint_states_pattern=args.joint_states_pattern,
119131
mask_pattern=args.mask_pattern,
120-
xyz_pattern=args.xyz_pattern,
132+
depth_pattern=args.depth_pattern,
121133
)
134+
height, width, intrinsics = parse_camera_info(args.camera_info_file)
122135

123136
# instantiate kinematics
124137
urdf_parser = URDFParser()
@@ -158,7 +171,7 @@ def main():
158171
device=device,
159172
)
160173

161-
# process data
174+
# perform forward kinematics
162175
mesh_vertices = meshes.vertices.clone()
163176
joint_states = torch.tensor(
164177
np.array(joint_states), dtype=torch.float32, device=device
@@ -180,6 +193,22 @@ def main():
180193
ht.transpose(-1, -2),
181194
)
182195

196+
# turn depths into xyzs
197+
intrinsics = torch.tensor(intrinsics, dtype=torch.float32, device=device)
198+
depths = torch.tensor(np.array(depths), dtype=torch.float32, device=device)
199+
xyzs = depth_to_xyz(depth=depths, intrinsics=intrinsics, z_max=1.5)
200+
201+
# flatten BxHxWx3 -> Bx(H*W)x3
202+
xyzs = xyzs.view(-1, height * width, 3)
203+
xyzs = to_homogeneous(xyzs)
204+
ht_optical = generate_ht_optical(xyzs.shape[0], dtype=torch.float32, device=device)
205+
xyzs = torch.matmul(xyzs, ht_optical.transpose(-1, -2))
206+
xyzs = from_homogeneous(xyzs)
207+
208+
# unflatten
209+
xyzs = xyzs.view(-1, height, width, 3)
210+
xyzs = [xyz.squeeze() for xyz in xyzs.cpu().numpy()]
211+
183212
# mesh vertices to list
184213
mesh_vertices = from_homogeneous(mesh_vertices)
185214
mesh_vertices = [mesh_vertices[i].contiguous() for i in range(batch_size)]

roboreg/cli/rr_render.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,8 @@ def main():
144144
image_suffix = pathlib.Path(image_file).suffix
145145
cv2.imwrite(
146146
os.path.join(
147-
str(output_path.absolute()), f"overlay_{image_stem + image_suffix}"
147+
str(output_path.absolute()),
148+
f"overlay_render_{image_stem + image_suffix}",
148149
),
149150
overlay_mask(image, render, "b", scale=1.0),
150151
)

roboreg/cli/rr_sam.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ def main():
9999
path.absolute(), f"mask_sam_{image_stem + image_suffix}"
100100
)
101101
overlay_path = os.path.join(
102-
path.absolute(), f"mask_overlay_sam_{image_stem + image_suffix}"
102+
path.absolute(), f"overlay_sam_{image_stem + image_suffix}"
103103
)
104104
cv2.imwrite(probability_path, (probability * 255.0).astype(np.uint8))
105105
cv2.imwrite(mask_path, mask)

roboreg/cli/rr_sam2.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ def main():
9191
path.absolute(), f"mask_sam2_{image_stem + image_suffix}"
9292
)
9393
overlay_path = os.path.join(
94-
path.absolute(), f"mask_overlay_sam2_{image_stem + image_suffix}"
94+
path.absolute(), f"overlay_sam2_{image_stem + image_suffix}"
9595
)
9696
cv2.imwrite(probability_path, (probability * 255.0).astype(np.uint8))
9797
cv2.imwrite(mask_path, mask)

roboreg/io.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -314,15 +314,15 @@ def parse_hydra_data(
314314
path: str,
315315
joint_states_pattern: str = "joint_states_*.npy",
316316
mask_pattern: str = "mask_*.png",
317-
xyz_pattern: str = "xyz_*.npy",
317+
depth_pattern: str = "depth_*.npy",
318318
) -> Tuple[List[np.ndarray], List[np.ndarray], List[np.ndarray]]:
319319
r"""Parse data for Hydra registration.
320320
321321
Args:
322322
path (str): Path to the data.
323323
joint_states_pattern (str): Pattern for joint states files.
324324
mask_pattern (str): Pattern for mask files.
325-
xyz_pattern (str): Pattern for xyz files.
325+
depth_pattern (str): Pattern for depth files. Note that depth values are expected in meters.
326326
327327
Returns:
328328
Tuple[List[np.ndarray],List[np.ndarray],List[np.ndarray]]:
@@ -332,19 +332,19 @@ def parse_hydra_data(
332332
"""
333333
joint_state_files = find_files(path, joint_states_pattern)
334334
mask_files = find_files(path, mask_pattern)
335-
xyz_files = find_files(path, xyz_pattern)
335+
depth_files = find_files(path, depth_pattern)
336336

337-
if len(joint_state_files) == 0 or len(mask_files) == 0 or len(xyz_files) == 0:
337+
if len(joint_state_files) == 0 or len(mask_files) == 0 or len(depth_files) == 0:
338338
raise ValueError("No files found.")
339339
if len(joint_state_files) != len(mask_files) or len(joint_state_files) != len(
340-
xyz_files
340+
depth_files
341341
):
342342
raise ValueError("Number of files do not match.")
343343

344344
rich.print("Found the following files:")
345345
rich.print(f"Joint states: {joint_state_files}")
346346
rich.print(f"Masks: {mask_files}")
347-
rich.print(f"XYZ: {xyz_files}")
347+
rich.print(f"Depths: {depth_files}")
348348

349349
# load data
350350
joint_states = [
@@ -355,5 +355,5 @@ def parse_hydra_data(
355355
cv2.imread(os.path.join(path, mask_file), cv2.IMREAD_GRAYSCALE)
356356
for mask_file in mask_files
357357
]
358-
xyzs = [np.load(os.path.join(path, xyz_file)) for xyz_file in xyz_files]
359-
return joint_states, masks, xyzs
358+
depths = [np.load(os.path.join(path, depth_file)) for depth_file in depth_files]
359+
return joint_states, masks, depths

0 commit comments

Comments
 (0)