Source code for nndt.space2.transformation
from abc import abstractmethod
from typing import *
import jax.numpy as jnp
import numpy as onp
from colorama import Fore
from nndt.space2.abstracts import AbstractBBoxNode, node_method
[docs]class AbstractTransformation(AbstractBBoxNode):
def __init__(self, name: str, bbox=((0.0, 0.0, 0.0), (0.0, 0.0, 0.0)), parent=None):
super(AbstractTransformation, self).__init__(
name, bbox=bbox, _print_color=Fore.RED, _nodetype="TR", parent=parent
)
self._transform_type = "unknown_transform"
def __len__(self):
return len(self.children)
def __getitem__(self, request_: Union[int, str]):
if isinstance(request_, int):
children_without_methods = [
ch for ch in self.children if isinstance(ch, AbstractBBoxNode)
]
return children_without_methods[request_]
elif isinstance(request_, str):
return self.resolver.get(self, request_)
else:
raise NotImplementedError()
def __repr__(self):
return (
self._print_color
+ f"{self._nodetype}:{self.name}"
+ Fore.WHITE
+ f" {self._transform_type}"
+ Fore.RESET
)
def _print_bbox(self):
a = self.bbox
return f"(({a[0][0]:.02f}, {a[0][1]:.02f}, {a[0][2]:.02f}), ({a[1][0]:.02f}, {a[1][1]:.02f}, {a[1][2]:.02f}))"
def _post_attach(self, parent):
if parent is not None:
setattr(parent, self.name, self)
def _post_detach(self, parent):
if parent is not None:
if hasattr(parent, self.name):
delattr(parent, self.name)
[docs] @abstractmethod
def transform_xyz_ps2ns(
self, xyz: Union[onp.ndarray, jnp.ndarray]
) -> Union[onp.ndarray, jnp.ndarray]:
pass
[docs] @abstractmethod
def transform_xyz_ns2ps(
self, xyz: Union[onp.ndarray, jnp.ndarray]
) -> Union[onp.ndarray, jnp.ndarray]:
pass
[docs] @abstractmethod
def transform_sdt_ns2ps(
self, sdt: Union[onp.ndarray, jnp.ndarray]
) -> Union[onp.ndarray, jnp.ndarray]:
pass
[docs] @abstractmethod
def transform_sdt_ps2ns(
self, sdt: Union[onp.ndarray, jnp.ndarray]
) -> Union[onp.ndarray, jnp.ndarray]:
pass
[docs]class IdentityTransform(AbstractTransformation):
"""
Transfer object of the physical space to normalized space without any changes.
Args:
ps_bbox (tuple, optional): boundary box in form ((X_min, Y_min, Z_min), (X_max, Y_max, Z_max)).
parent (_type_, optional): parent node. Defaults to None.
"""
def __init__(
self, ps_bbox: ((float, float, float), (float, float, float)), parent=None
):
super(IdentityTransform, self).__init__(
"transform", bbox=ps_bbox, parent=parent
)
self.bbox = ps_bbox
self._transform_type = "identity"
[docs] @node_method("transform_xyz_ps2ns(ps_xyz[..,3]) -> ns_xyz[..,3]")
def transform_xyz_ps2ns(
self, xyz: Union[onp.ndarray, jnp.ndarray]
) -> Union[onp.ndarray, jnp.ndarray]:
"""Transforms xyz physical space to normalized space
Args:
xyz (Union[onp.ndarray, jnp.ndarray]): xyz in physical space.
Returns:
Union[onp.ndarray, jnp.ndarray]: xyz in normalized space.
"""
return xyz
[docs] @node_method("transform_xyz_ns2ps(ns_xyz[..,3]) -> ps_xyz[..,3]")
def transform_xyz_ns2ps(
self, xyz: Union[onp.ndarray, jnp.ndarray]
) -> Union[onp.ndarray, jnp.ndarray]:
"""Transforms xyz normalized space to physical space
Args:
xyz (Union[onp.ndarray, jnp.ndarray]): xyz in normalized space.
Returns:
Union[onp.ndarray, jnp.ndarray]: xyz in physical space.
"""
return xyz
[docs] @node_method("transform_sdt_ns2ps(ns_sdt[..]) -> ps_sdt[..]")
def transform_sdt_ns2ps(
self, sdt: Union[onp.ndarray, jnp.ndarray]
) -> Union[onp.ndarray, jnp.ndarray]:
"""Transforms signed distance tensor of normalized space to physical space.
Args:
sdt (Union[onp.ndarray, jnp.ndarray]): Signed distance tensor in normalized space.
Returns:
Union[onp.ndarray, jnp.ndarray]: Signed distance tensor in physical space.
"""
return sdt
[docs] @node_method("transform_sdt_ps2ns(ps_sdt[..]) -> ns_sdt[..]")
def transform_sdt_ps2ns(
self, sdt: Union[onp.ndarray, jnp.ndarray]
) -> Union[onp.ndarray, jnp.ndarray]:
"""Transforms signed distance tensor of physical space to normalized space.
Args:
sdt (Union[onp.ndarray, jnp.ndarray]): Signed distance tensor in physical space.
Returns:
Union[onp.ndarray, jnp.ndarray]: Signed distance tensor in normalized space.
"""
return sdt
[docs]class ShiftAndScaleTransform(AbstractTransformation):
"""
Transfer objects from a physical space to a normalized space using shift and scale transformation.
Args:
ps_bbox (float, float, float): boundary box in a form.
ps_center (float, float, float): Physical space center.
ns_center (float, float, float): Normalized space center.
scale_ps2ns (float): Scale.
parent (_type_, optional): parent node. Defaults to None.
"""
def __init__(
self,
ps_bbox: ((float, float, float), (float, float, float)),
ps_center: (float, float, float),
ns_center: (float, float, float),
scale_ps2ns: float,
parent=None,
):
super(ShiftAndScaleTransform, self).__init__("transform", parent=parent)
self.ps_center = jnp.array(ps_center)
self.ns_center = jnp.array(ns_center)
self.scale_ps2ns = scale_ps2ns
self._transform_type = "shift_and_scale"
bbox_ = self.transform_xyz_ps2ns(jnp.array(ps_bbox))
self.bbox = (
(float(bbox_[0][0]), float(bbox_[0][1]), float(bbox_[0][2])),
(float(bbox_[1][0]), float(bbox_[1][1]), float(bbox_[1][2])),
)
[docs] @node_method("transform_xyz_ps2ns(ps_xyz[..,3]) -> ns_xyz[..,3]")
def transform_xyz_ps2ns(
self, xyz: Union[onp.ndarray, jnp.ndarray]
) -> Union[onp.ndarray, jnp.ndarray]:
"""Transforms xyz physical space to normalized space
Args:
xyz (Union[onp.ndarray, jnp.ndarray]): xyz in physical space.
Returns:
Union[onp.ndarray, jnp.ndarray]: xyz in normalized space.
"""
return (xyz - jnp.array(self.ps_center)) / self.scale_ps2ns + jnp.array(
self.ns_center
)
[docs] @node_method("transform_xyz_ns2ps(ns_xyz[..,3]) -> ps_xyz[..,3]")
def transform_xyz_ns2ps(
self, xyz: Union[onp.ndarray, jnp.ndarray]
) -> Union[onp.ndarray, jnp.ndarray]:
"""Transforms xyz normalized space to physical space
Args:
xyz (Union[onp.ndarray, jnp.ndarray]): xyz in normalized space.
Returns:
Union[onp.ndarray, jnp.ndarray]: xyz in physical space.
"""
return (xyz - jnp.array(self.ns_center)) * self.scale_ps2ns + jnp.array(
self.ps_center
)
[docs] @node_method("transform_sdt_ns2ps(ns_sdt[..]) -> ps_sdt[..]")
def transform_sdt_ns2ps(
self, sdt: Union[onp.ndarray, jnp.ndarray]
) -> Union[onp.ndarray, jnp.ndarray]:
"""Transforms signed distance tensor of normalized space to physical space.
Args:
sdt (Union[onp.ndarray, jnp.ndarray]): Signed distance tensor in normalized space.
Returns:
Union[onp.ndarray, jnp.ndarray]: Signed distance tensor in physical space.
"""
return sdt * self.scale_ps2ns
[docs] @node_method("transform_sdt_ps2ns(ps_sdt[..]) -> ns_sdt[..]")
def transform_sdt_ps2ns(
self, sdt: Union[onp.ndarray, jnp.ndarray]
) -> Union[onp.ndarray, jnp.ndarray]:
"""Transforms signed distance tensor of physical space to normalized space.
Args:
sdt (Union[onp.ndarray, jnp.ndarray]): Signed distance tensor in physical space.
Returns:
Union[onp.ndarray, jnp.ndarray]: Signed distance tensor in normalized space.
"""
return sdt / self.scale_ps2ns
[docs]class ToNormalCubeTransform(AbstractTransformation):
"""
Transfer objects from a physical space to a normalized space.
All objects are scaled to cubes with coordinates from -1 to 1.
This approach is similar to NormalScaler in scikit-learn.
Args:
ps_bbox (float, float, float): boundary box in the form.
parent (_type_, optional): parent node. Defaults to None.
"""
def __init__(
self, ps_bbox: ((float, float, float), (float, float, float)), parent=None
):
super(ToNormalCubeTransform, self).__init__("transform", parent=parent)
self.ps_lower = jnp.array(ps_bbox[0])
self.ps_upper = jnp.array(ps_bbox[1])
self.ps_center = (self.ps_lower + self.ps_upper) / 2.0
self.scale = (self.ps_upper - self.ps_lower) / 2.0
self.bbox = ((-1.0, -1.0, -1.0), (1.0, 1.0, 1.0))
self._transform_type = "to_cube"
[docs] @node_method("transform_xyz_ps2ns(ps_xyz[..,3]) -> ns_xyz[..,3]")
def transform_xyz_ps2ns(
self, xyz: Union[onp.ndarray, jnp.ndarray]
) -> Union[onp.ndarray, jnp.ndarray]:
"""Transforms xyz physical space to normalized space
Args:
xyz (Union[onp.ndarray, jnp.ndarray]): xyz in physical space.
Returns:
Union[onp.ndarray, jnp.ndarray]: xyz in normalized space.
"""
return (xyz - self.ps_center) / self.scale
[docs] @node_method("transform_xyz_ns2ps(ns_xyz[..,3]) -> ps_xyz[..,3]")
def transform_xyz_ns2ps(
self, xyz: Union[onp.ndarray, jnp.ndarray]
) -> Union[onp.ndarray, jnp.ndarray]:
"""Transforms xyz normalized space to physical space
Args:
xyz (Union[onp.ndarray, jnp.ndarray]): xyz in normalized space.
Returns:
Union[onp.ndarray, jnp.ndarray]: xyz in physical space.
"""
return (xyz * self.scale) + self.ps_center
[docs] @node_method("transform_sdt_ns2ps(ns_sdt[..]) -> ps_sdt[..]")
def transform_sdt_ns2ps(
self, sdt: Union[onp.ndarray, jnp.ndarray]
) -> Union[onp.ndarray, jnp.ndarray]:
"""Transforms signed distance tensor of normalized space to physical space.
Args:
sdt (Union[onp.ndarray, jnp.ndarray]): Signed distance tensor in normalized space.
Returns:
Union[onp.ndarray, jnp.ndarray]: Signed distance tensor in physical space.
"""
return sdt * self.scale
[docs] @node_method("transform_sdt_ps2ns(ps_sdt[..]) -> ns_sdt[..]")
def transform_sdt_ps2ns(
self, sdt: Union[onp.ndarray, jnp.ndarray]
) -> Union[onp.ndarray, jnp.ndarray]:
"""Transforms signed distance tensor of physical space to normalized space.
Args:
sdt (Union[onp.ndarray, jnp.ndarray]): Signed distance tensor in physical space.
Returns:
Union[onp.ndarray, jnp.ndarray]: Signed distance tensor in normalized space.
"""
return sdt / self.scale