Skip to content

Commit f1583ef

Browse files
committed
Add utiities
1 parent 9651415 commit f1583ef

4 files changed

Lines changed: 87 additions & 6 deletions

File tree

gluefactory/geometry/reconstruction.py

Lines changed: 73 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,23 @@
77
import logging
88
import math
99
from pathlib import Path
10-
from typing import Dict, List, NamedTuple, Optional, Sequence, Tuple, Union
10+
from typing import (
11+
Any,
12+
Dict,
13+
List,
14+
NamedTuple,
15+
Optional,
16+
Sequence,
17+
Tuple,
18+
TypeAlias,
19+
Union,
20+
)
1121

1222
import h5py
1323
import numpy as np
1424

25+
Self: TypeAlias = Any
26+
1527
try:
1628
import pycolmap
1729
except ImportError:
@@ -199,6 +211,44 @@ def angular_error(self, other: "Pose", agg="max") -> torch.Tensor:
199211
"dt": lambda x: x[..., 1],
200212
}[agg](self.angular_drdt(other, stack=True))
201213

214+
def _opening_angle(self, return_cos: bool = False) -> float:
215+
v0 = torch.zeros_like(self.t)
216+
v0[..., -1] = 1.0
217+
v1 = self.R[..., 2, :]
218+
v01 = self.inv().t
219+
v01 = v01 / v01.norm()
220+
n = v0.cross(v01, dim=-1)
221+
n = n / n.norm()
222+
cos = n.dot(v1)
223+
return cos if return_cos else (90 - torch.acos(cos) * 180.0 / 3.1415).abs()
224+
225+
def opening_angle(self, return_cos: bool = False) -> float:
226+
return 0.5 * (
227+
self._opening_angle(return_cos=return_cos)
228+
+ self.inv()._opening_angle(return_cos=return_cos)
229+
)
230+
231+
@classmethod
232+
def exp(cls, delta: torch.Tensor):
233+
dr, dt = delta.split(3, dim=-1)
234+
return cls.from_aa(dr, dt)
235+
236+
def manifold(self, delta: torch.Tensor | None = None) -> torch.Tensor:
237+
if delta is None:
238+
delta = torch.zeros_like(self._data[..., :6])
239+
return delta
240+
241+
def update(self, delta: torch.Tensor | Self, inplace: bool = False) -> "Pose":
242+
if not isinstance(delta, self.__class__):
243+
delta = Pose.exp(delta)
244+
245+
updated_pose = delta @ self
246+
if inplace:
247+
self._data = updated_pose._data
248+
return self
249+
else:
250+
return updated_pose
251+
202252
def __repr__(self):
203253
return f"Pose: {self.shape} {self.dtype} {self.device}"
204254

@@ -377,13 +427,32 @@ def J_world2image(self, p3d: torch.Tensor):
377427
J = self.J_denormalize() @ self.J_distort(p2d_dist) @ self.J_project(p3d)
378428
return J, valid
379429

380-
@tensor.autocast
381-
def image2cam(self, p2d: torch.Tensor) -> torch.Tensor:
430+
def image2cam(self, p2d: torch.Tensor, homogeneous: bool = True) -> torch.Tensor:
382431
"""Convert 2D pixel corrdinates to 3D points with z=1"""
383432
assert self._data.shape
384433
p2d = self.normalize(p2d)
385434
# iterative undistortion
386-
return gtr.to_homogeneous(p2d)
435+
if homogeneous:
436+
return gtr.to_homogeneous(p2d)
437+
else:
438+
return p2d
439+
440+
def manifold(self, delta: torch.Tensor | None = None) -> torch.Tensor:
441+
if delta is None:
442+
delta = torch.zeros_like(self._data[..., 2:4])
443+
return delta
444+
445+
def update(self, delta: torch.Tensor | Self, inplace=False) -> "Camera":
446+
if isinstance(delta, self.__class__):
447+
delta = delta._data[..., 2:4]
448+
449+
if inplace:
450+
self._data[..., 2:4] += delta
451+
return self
452+
else:
453+
cam_copy = self.clone()
454+
cam_copy._data[..., 2:4] += delta
455+
return cam_copy
387456

388457
def to_cameradict(self, camera_model: Optional[str] = None) -> List[Dict]:
389458
data = self._data.clone()

gluefactory/geometry/transforms.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ def batched_eye_like(x: torch.Tensor, n: int):
4141
return torch.eye(n).to(x)[None].repeat(len(x), 1, 1)
4242

4343

44+
def eye_like(tensor: torch.Tensor) -> torch.Tensor:
45+
batch_dims = tensor.shape[:-2]
46+
identity = torch.eye(*tensor.shape[-2:], dtype=tensor.dtype, device=tensor.device)
47+
for _ in batch_dims:
48+
identity = identity[None]
49+
return identity.repeat(*batch_dims, 1, 1)
50+
51+
4452
def skew_symmetric(v):
4553
"""Create a skew-symmetric matrix from a (batched) vector of size (..., 3)."""
4654
z = torch.zeros_like(v[..., 0])

gluefactory/utils/misc.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111

1212

1313
def map_tensor(input_, func):
14-
string_classes = (str, bytes)
15-
if isinstance(input_, string_classes):
14+
scalar_classes = (str, bytes, bool, float, int)
15+
if isinstance(input_, scalar_classes):
1616
return input_
1717
elif isinstance(input_, Mapping):
1818
return {k: map_tensor(sample, func) for k, sample in input_.items()}

gluefactory/utils/tensor.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,10 @@ def squeeze(self, *args, **kwargs):
125125
"""Create a new tensor of the same type and device."""
126126
return self.__class__(self._data.squeeze(*args, **kwargs))
127127

128+
def clone(self, *args, **kwargs):
129+
"""Create a new tensor of the same type and device."""
130+
return self.__class__(self._data.clone(*args, **kwargs))
131+
128132
@classmethod
129133
def stack(cls, objects: list, dim=0, *, out=None):
130134
"""Stack a list of objects with the same type and shape."""

0 commit comments

Comments
 (0)