|
from typing import Optional, Union |
|
import torch |
|
from kornia.geometry.conversions import convert_points_from_homogeneous |
|
|
|
|
|
class LineCollection: |
|
def __init__( |
|
self, |
|
support: torch.tensor, |
|
direction_norm: torch.tensor, |
|
direction: Optional[torch.tensor] = None, |
|
): |
|
"""Wrapper class to represent lines by support and direction vectors. |
|
|
|
Args: |
|
support (torch.tensor): with shape (*, {2,3}) |
|
direction_norm (torch.tensor): with shape (*, {2,3}) |
|
direction (Optional[torch.tensor], optional): Unnormalized direction vector. Defaults to None. |
|
""" |
|
self.support = support |
|
self.direction_norm = direction_norm |
|
self.direction = direction |
|
|
|
def __copy__(self): |
|
return LineCollection( |
|
self.support.clone(), |
|
self.direction_norm.clone(), |
|
self.direction.clone() if self.direction is not None else None, |
|
) |
|
|
|
def copy(self): |
|
return self.__copy__() |
|
|
|
def shape(self): |
|
return f"support={self.support.shape} direction_norm={self.direction_norm.shape} direction={self.direction.shape if self.direction else None}" |
|
|
|
def __repr__(self) -> str: |
|
return f"{self.__class__} " + self.shape() |
|
|
|
|
|
def distance_line_pointcloud_3d( |
|
e1: torch.Tensor, |
|
r1: torch.Tensor, |
|
pc: torch.Tensor, |
|
reduce: Union[None, str] = None, |
|
) -> torch.Tensor: |
|
""" |
|
Line to point cloud distance with arbitrary leading dimensions. |
|
|
|
TODO. if cross = (0.0.0) -> distance=0 otherwise NaNs are returned |
|
|
|
https://mathworld.wolfram.com/Point-LineDistance2-Dimensional.html |
|
Args: |
|
e1 (torch.Tensor): direction vector of shape (*, B, 1, 3) |
|
r1 (torch.Tensor): support vector of shape (*, B, 1, 3) |
|
pc (torch.Tensor): point cloud of shape (*, B, A, 3) |
|
reduce (Union[None, str]): reduce distance for all points to one using 'mean' or 'min' |
|
Returns: |
|
distance of an infinite line to given points, (*, B, ) using reduce='mean' or reduce='min' or (*, B, A) if reduce=False |
|
""" |
|
|
|
num_points = pc.shape[-2] |
|
_sub = r1 - pc |
|
|
|
cross = torch.cross(e1.repeat_interleave(num_points, dim=-2), _sub, dim=-1) |
|
|
|
e1_norm = torch.linalg.norm(e1, dim=-1) |
|
cross_norm = torch.linalg.norm(cross, dim=-1) |
|
|
|
d = cross_norm / e1_norm |
|
if reduce == "mean": |
|
return d.mean(dim=-1) |
|
elif reduce == "min": |
|
return d.min(dim=-1)[0] |
|
|
|
return d |
|
|
|
|
|
def distance_point_pointcloud(points: torch.Tensor, pointcloud: torch.Tensor) -> torch.Tensor: |
|
"""Batched version for point-pointcloud distance calculation |
|
Args: |
|
points (torch.Tensor): N points in homogenous coordinates; shape (B, T, 3, S, N) |
|
pointcloud (torch.Tensor): N_star points for each pointcloud; shape (B, T, S, N_star, 2) |
|
|
|
Returns: |
|
torch.Tensor: Minimum distance for each point N to pointcloud; shape (B, T, 1, S, N) |
|
""" |
|
|
|
batch_size, T, _, S, N = points.shape |
|
batch_size, T, S, N_star, _ = pointcloud.shape |
|
|
|
pointcloud = pointcloud.reshape(batch_size * T * S, N_star, 2) |
|
|
|
points = convert_points_from_homogeneous( |
|
points.permute(0, 1, 3, 4, 2).reshape(batch_size * T * S, N, 3) |
|
) |
|
|
|
|
|
distances = torch.cdist(points, pointcloud, p=2) |
|
|
|
distances = distances.view(batch_size, T, S, N, N_star) |
|
distances = distances.unsqueeze(-4) |
|
|
|
|
|
distances = distances.min(dim=-1)[0] |
|
return distances |
|
|