Skip to content

Commit 553857b

Browse files
authored
Merge pull request #165 from Visual-Behavior/add_pose_label
Noisy aug, pose update, depth abs, render, batch_list
2 parents 89205f3 + 362455b commit 553857b

File tree

6 files changed

+187
-22
lines changed

6 files changed

+187
-22
lines changed

alodataset/transforms.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -622,13 +622,14 @@ def set_params(self):
622622
def apply(self, frame: Frame):
623623
n_frame = frame.norm01()
624624

625-
gaussian_noise = torch.normal(mean=0, std=self.gaussian_std, size=frame.shape)
626-
shot_noise = torch.normal(mean=0, std=self.shot_std, size=frame.shape)
625+
gaussian_noise = torch.normal(mean=0, std=self.gaussian_std, size=frame.shape, device=frame.device)
626+
shot_noise = torch.normal(mean=0, std=self.shot_std, size=frame.shape, device=frame.device)
627627
noisy_frame = n_frame + n_frame * n_frame * shot_noise + gaussian_noise
628628
noisy_frame = torch.clip(noisy_frame, 0, 1)
629629

630-
if n_frame.normalization != frame.normalization:
631-
n_frame = n_frame.norm_as(frame)
630+
if noisy_frame.normalization != frame.normalization:
631+
noisy_frame = noisy_frame.norm_as(frame)
632+
632633
return noisy_frame
633634

634635

aloscene/__init__.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,62 @@
88
from .points_2d import Points2D
99
from .points_3d import Points3D
1010
from .disparity import Disparity
11+
from .pose import Pose
1112
from .bounding_boxes_2d import BoundingBoxes2D
1213
from .bounding_boxes_3d import BoundingBoxes3D
1314
from .oriented_boxes_2d import OrientedBoxes2D
1415
from .frame import Frame
16+
from .tensors.spatial_augmented_tensor import SpatialAugmentedTensor
17+
18+
from .renderer import Renderer
19+
20+
def batch_list(tensors):
21+
return SpatialAugmentedTensor.batch_list(tensors)
22+
23+
_renderer = None
24+
def render(
25+
views: list,
26+
renderer: str = "cv",
27+
size=None,
28+
record_file: str = None,
29+
fps=30,
30+
grid_size=None,
31+
skip_views=False,
32+
):
33+
"""Render a list of view.
34+
35+
Parameters
36+
----------
37+
views : list
38+
List of np.darray to display
39+
renderer : str
40+
String to set the renderer to use. Can be either ("cv" or "matplotlib")
41+
cell_grid_size : tuple
42+
Tuple or None. If not None, the tuple values (height, width) will be used
43+
to set the size of the each grid cell of the display. If only one view is used,
44+
the view will be resize to the cell grid size.
45+
record_file : str
46+
None by default. Used to save the rendering into one video.
47+
skip_views : bool, optional
48+
Skip views, in order to speed up the render process, by default False
49+
"""
50+
global _renderer
51+
_renderer = Renderer() if _renderer is None else _renderer
52+
53+
_renderer.render(
54+
views=views,
55+
renderer=renderer,
56+
cell_grid_size=size,
57+
record_file=record_file,
58+
fps=fps,
59+
grid_size=grid_size,
60+
skip_views=skip_views
61+
)
62+
63+
64+
def save_renderer():
65+
"""If render() was called with a `record_file`, then this method will
66+
save the final video on the system. Warning: It is currently not possible
67+
to save multiple stream directly with aloception. Todo so, one can manually create multiple `Renderer`.
68+
"""
69+
_renderer.save()

aloscene/camera_calib.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,23 @@ def __new__(cls, x, *args, **kwargs):
187187
tensor = super().__new__(cls, x, *args, **kwargs)
188188
return tensor
189189

190+
def translation_with(self, tgt_pos):
191+
""" Compute the translation with an other pos
192+
193+
Parameters
194+
----------
195+
tgt_pos: aloscene.Pose
196+
Target pose to compute the translation with
197+
198+
Returns
199+
-------
200+
n_pos: torch.tensor
201+
Translation tensor of shape (..., 3)
202+
"""
203+
Ttgt2self = torch.linalg.solve(self.as_tensor(), tgt_pos.as_tensor())
204+
translation = Ttgt2self[..., :3, -1]
205+
return translation
206+
190207
def __init__(self, x, *args, **kwargs):
191208
assert x.shape[-2] == 4 and x.shape[-1] == 4
192209
super().__init__(x)

aloscene/depth.py

Lines changed: 57 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ class Depth(aloscene.tensors.SpatialAugmentedTensor):
2323
occlusion : aloscene.Mask
2424
Occlusion mask for this Depth map. Default value : None.
2525
is_bsolute: bool
26-
Either depth values refer to real values or shifted and scaled ones.
26+
Either depth values refer to real values or shifted and scaled ones.
2727
scale: float
2828
Scale used to to shift depth. Pass this argument only if is_bsolute is set to True
2929
shift: float
@@ -32,14 +32,14 @@ class Depth(aloscene.tensors.SpatialAugmentedTensor):
3232

3333
@staticmethod
3434
def __new__(
35-
cls,
36-
x,
37-
occlusion: Mask = None,
38-
is_absolute=False,
39-
scale=None,
40-
shift=None,
41-
*args,
42-
names=("C", "H", "W"),
35+
cls,
36+
x,
37+
occlusion: Mask = None,
38+
is_absolute=False,
39+
scale=None,
40+
shift=None,
41+
*args,
42+
names=("C", "H", "W"),
4343
**kwargs):
4444
if is_absolute and not (shift and scale):
4545
raise AttributeError('absolute depth requires shift and scale arguments')
@@ -58,9 +58,20 @@ def __new__(
5858
def __init__(self, x, *args, **kwargs):
5959
super().__init__(x)
6060

61-
def encode_inverse(self):
61+
def encode_inverse(self, prior_clamp_min=None, prior_clamp_max=None, post_clamp_min=None, post_clamp_max=None):
6262
"""Undo encode_absolute tansformation
63-
63+
64+
Parameters
65+
----------
66+
prior_clamp_min: float | None
67+
Clamp min depth before to convert to idepth
68+
prior_clamp_max: float | None
69+
Clamp max depth before to convert to idepth
70+
post_clamp_min: float | None
71+
Clamp min output idepth
72+
post_clamp_max: float | None
73+
Clamp max output idepth
74+
6475
Exemples
6576
-------
6677
>>> not_absolute_depth = Depth(torch.ones((1, 1, 1)), is_absolute=False)
@@ -72,23 +83,40 @@ def encode_inverse(self):
7283
depth = self
7384
if not depth.is_absolute:
7485
raise ExecError('can not inverse depth, already inversed')
86+
shift = depth.shift if depth.shift is not None else 0
87+
scale = depth.scale if depth.scale is not None else 1
88+
89+
if prior_clamp_min is not None or prior_clamp_max is not None:
90+
depth = torch.clamp(depth, min=prior_clamp_min, max=prior_clamp_max)
91+
7592
depth = 1 / depth
76-
depth = (depth - depth.shift) / depth.scale
93+
depth = (depth - shift) / scale
94+
95+
if post_clamp_min is not None or post_clamp_max is not None:
96+
depth = torch.clamp(depth, min=post_clamp_min, max=post_clamp_max)
97+
7798
depth.scale = None
7899
depth.shift = None
79100
depth.is_absolute = False
80101
return depth
81-
82-
def encode_absolute(self, scale=1, shift=0):
102+
103+
def encode_absolute(self, scale=1, shift=0, prior_clamp_min=None, prior_clamp_max=None, post_clamp_min=None, post_clamp_max=None):
83104
"""Transforms inverted depth to absolute depth
84-
105+
85106
Parameters
86107
----------
87108
scale: (: float)
88109
Multiplication factor. Default is 1.
89-
90110
shift: (: float)
91111
Addition intercept. Default is 0.
112+
prior_clamp_min: float | None
113+
Clamp min idepth before to convert to depth
114+
prior_clamp_max: float | None
115+
Clamp max idepth before to convert to depth
116+
post_clamp_min: float | None
117+
Clamp min output idepth
118+
post_clamp_max: float | None
119+
Clamp max output idepth
92120
93121
Exemples
94122
--------
@@ -100,12 +128,24 @@ def encode_absolute(self, scale=1, shift=0):
100128
depth, names = self.rename(None), self.names
101129
if depth.is_absolute:
102130
raise ExecError('depth already in absolute state, call encode_inverse first')
131+
103132
depth = depth * scale + shift
133+
134+
if prior_clamp_min is not None or prior_clamp_max is not None:
135+
depth = torch.clamp(depth, min=prior_clamp_min, max=prior_clamp_max)
136+
104137
depth[torch.unsqueeze(depth < 1e-8, dim=0)] = 1e-8
105138
depth.scale = scale
106139
depth.shift = shift
107140
depth.is_absolute = True
108-
return (1 / depth).rename(*names)
141+
142+
n_depth = (1 / depth).rename(*names)
143+
144+
if post_clamp_min is not None or post_clamp_max is not None:
145+
n_depth = torch.clamp(n_depth, min=post_clamp_min, max=post_clamp_max)
146+
147+
return n_depth
148+
109149

110150
def append_occlusion(self, occlusion: Mask, name: str = None):
111151
"""Attach an occlusion mask to the depth tensor.

aloscene/frame.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
import aloscene
88
from aloscene.renderer import View
9-
from aloscene import BoundingBoxes2D, BoundingBoxes3D, Depth, Disparity, Flow, Mask, Labels, Points2D, Points3D
9+
from aloscene import BoundingBoxes2D, BoundingBoxes3D, Depth, Disparity, Flow, Mask, Labels, Points2D, Points3D, Pose
1010

1111
# from aloscene.camera_calib import CameraExtrinsic, CameraIntrinsic
1212
from aloscene.io.image import load_image
@@ -113,6 +113,7 @@ def __new__(
113113
tensor.add_child("depth", depth, align_dim=["B", "T"], mergeable=True)
114114
tensor.add_child("segmentation", segmentation, align_dim=["B", "T"], mergeable=False)
115115
tensor.add_child("labels", labels, align_dim=["B", "T"], mergeable=True)
116+
tensor.add_child("pose", labels, align_dim=["B", "T"], mergeable=True)
116117

117118
# Add other tensor property
118119
tensor.add_property("normalization", normalization)
@@ -304,6 +305,19 @@ def append_segmentation(self, segmentation: Mask, name: str = None):
304305
"""
305306
self._append_child("segmentation", segmentation, name)
306307

308+
def append_pose(self, pose: Pose, name: str = None):
309+
"""Attach a pose to the frame.
310+
311+
Parameters
312+
----------
313+
pose : :mod:`Pose <aloscene.pose>`
314+
Depth to attach to the Frame
315+
name : str
316+
If none, the pose will be attached without name (if possible). Otherwise if no other unnamed
317+
pose is attached to the frame, the pose will be added to the set of poses.
318+
"""
319+
self._append_child("pose", pose, name)
320+
307321
@staticmethod
308322
def _get_mean_std_tensor(shape, names, mean_std: tuple, device="cpu"):
309323
"""Utils method to a get the mean and the std

aloscene/pose.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
from aloscene.camera_calib import CameraExtrinsic
2+
import torch
3+
4+
5+
class Pose(CameraExtrinsic):
6+
"""Pose Tensor. Usually use to store World2Frame coordinates
7+
8+
Parameters
9+
----------
10+
x: torch.Tensor
11+
Pose matrix
12+
"""
13+
14+
@staticmethod
15+
def __new__(cls, x, *args, names=(None, None), **kwargs):
16+
tensor = super().__new__(cls, x, *args, names=names, **kwargs)
17+
return tensor
18+
19+
def __init__(self, x, *args, **kwargs):
20+
super().__init__(x)
21+
22+
def _hflip(self, *args, **kwargs):
23+
return self.clone()
24+
25+
def _vflip(self, *args, **kwargs):
26+
return self.clone()
27+
28+
def _resize(self, *args, **kwargs):
29+
# Resize image does not change cam extrinsic
30+
return self.clone()
31+
32+
def _crop(self, *args, **kwargs):
33+
# Cropping image does not change cam extrinsic
34+
return self.clone()
35+
36+
def _pad(self, *args, **kwargs):
37+
# Padding image does not change cam extrinsic
38+
return self.clone()

0 commit comments

Comments
 (0)