Spaces:
Running
on
Zero
Running
on
Zero
| """Horizontal flip augmentation.""" | |
| import numpy as np | |
| import torch | |
| from vis4d.common.typing import NDArrayF32, NDArrayUI8 | |
| from vis4d.data.const import AxisMode | |
| from vis4d.data.const import CommonKeys as K | |
| from vis4d.op.geometry.rotation import ( | |
| euler_angles_to_matrix, | |
| matrix_to_euler_angles, | |
| matrix_to_quaternion, | |
| quaternion_to_matrix, | |
| ) | |
| from .base import Transform | |
| class FlipImages: | |
| """Flip a list of numpy image array of shape [N, H, W, C].""" | |
| def __init__(self, direction: str = "horizontal"): | |
| """Creates an instance of FlipImage. | |
| Args: | |
| direction (str, optional): Either vertical or horizontal. | |
| Defaults to "horizontal". | |
| Raises: | |
| ValueError: If direction is not horizontal or vertical. | |
| """ | |
| if direction not in ["horizontal", "vertical"]: | |
| raise ValueError(f"Direction {direction} not known!") | |
| self.direction = direction | |
| def __call__(self, images: list[NDArrayF32]) -> list[NDArrayF32]: | |
| """Execute flipping op. | |
| Args: | |
| image (NDArrayF32): [N, H, W, C] array of image. | |
| Returns: | |
| list[NDArrayF32]: [N, H, W, C] array of flipped image. | |
| """ | |
| for i, image in enumerate(images): | |
| image_ = torch.from_numpy(image) | |
| if self.direction == "horizontal": | |
| images[i] = image_.flip(2).numpy() | |
| if self.direction == "vertical": | |
| images[i] = image_.flip(1).numpy() | |
| return images | |
| class FlipBoxes2D: | |
| """Flip a list of 2D bounding boxes.""" | |
| def __init__(self, direction: str = "horizontal"): | |
| """Creates an instance of FlipBoxes2D. | |
| Args: | |
| direction (str, optional): Either vertical or horizontal. | |
| Defaults to "horizontal". | |
| Raises: | |
| ValueError: If direction is not horizontal or vertical. | |
| """ | |
| if direction not in ["horizontal", "vertical"]: | |
| raise ValueError(f"Direction {direction} not known!") | |
| self.direction = direction | |
| def __call__( | |
| self, boxes_list: list[NDArrayF32], images: list[NDArrayF32] | |
| ) -> list[NDArrayF32]: | |
| """Execute flipping op. | |
| Args: | |
| boxes (list[NDArrayF32]): List of [M, 4] array of boxes. | |
| image (list[NDArrayF32]): List of [N, H, W, C] array of image. | |
| Returns: | |
| list[NDArrayF32]: List of [M, 4] array of flipped boxes. | |
| """ | |
| for i, (boxes, image) in enumerate(zip(boxes_list, images)): | |
| if self.direction == "horizontal": | |
| im_width = image.shape[2] | |
| tmp = im_width - boxes[..., 2::4] | |
| boxes[..., 2::4] = im_width - boxes[..., 0::4] | |
| boxes[..., 0::4] = tmp | |
| elif self.direction == "vertical": | |
| im_height = image.shape[1] | |
| tmp = im_height - boxes[..., 3::4] | |
| boxes[..., 3::4] = im_height - boxes[..., 1::4] | |
| boxes[..., 1::4] = tmp | |
| boxes_list[i] = boxes | |
| return boxes_list | |
| class FlipSegMasks: | |
| """Flip segmentation masks.""" | |
| def __init__(self, direction: str = "horizontal"): | |
| """Creates an instance of FlipSemanticMasks. | |
| Args: | |
| direction (str, optional): Either vertical or horizontal. | |
| Defaults to "horizontal". | |
| Raises: | |
| ValueError: If direction is not horizontal or vertical. | |
| """ | |
| if direction not in ["horizontal", "vertical"]: | |
| raise ValueError(f"Direction {direction} not known!") | |
| self.direction = direction | |
| def __call__(self, masks: list[NDArrayUI8]) -> list[NDArrayUI8]: | |
| """Execute flipping op. | |
| Args: | |
| masks (NDArrayUI8): [H, W] array of masks. | |
| Returns: | |
| list[NDArrayUI8]: [H, W] array of flipped masks. | |
| """ | |
| for i, mask in enumerate(masks): | |
| mask_ = torch.from_numpy(mask) | |
| if self.direction == "horizontal": | |
| mask = mask_.flip(1).numpy() | |
| if self.direction == "vertical": | |
| mask = mask_.flip(0).numpy() | |
| masks[i] = mask | |
| return masks | |
| class FlipDepthMaps: | |
| """Flip depth map.""" | |
| def __init__(self, direction: str = "horizontal"): | |
| """Creates an instance of FlipDepth. | |
| Args: | |
| direction (str, optional): Either vertical or horizontal. | |
| Defaults to "horizontal". | |
| """ | |
| self.direction = direction | |
| if direction not in ["horizontal", "vertical"]: | |
| raise ValueError(f"Direction {self.direction} not known!") | |
| def __call__(self, depths: list[NDArrayF32]) -> list[NDArrayF32]: | |
| """Execute flipping op. | |
| Args: | |
| depths (list[NDArrayF32]): Each is a [H, W] array of depth. | |
| Returns: | |
| list[NDArrayF32]: Each is a [H, W] array of flipped depth. | |
| """ | |
| for i, depth in enumerate(depths): | |
| depth_ = torch.from_numpy(depth) | |
| if self.direction == "horizontal": | |
| depths[i] = depth_.flip(1).numpy() | |
| if self.direction == "vertical": | |
| depths[i] = depth_.flip(0).numpy() | |
| return depths | |
| class FlipOpticalFlows: | |
| """Flip optical flow map.""" | |
| def __init__(self, direction: str = "horizontal"): | |
| """Creates an instance of FlipOpticalFlow. | |
| Args: | |
| direction (str, optional): Either vertical or horizontal. | |
| Defaults to "horizontal". | |
| """ | |
| self.direction = direction | |
| if direction not in ["horizontal", "vertical"]: | |
| raise ValueError(f"Direction {self.direction} not known!") | |
| def __call__(self, flows: list[NDArrayF32]) -> list[NDArrayF32]: | |
| """Execute flipping op. | |
| Args: | |
| flows (NDArrayF32): Each is a [H, W, 2] array of optical flow. | |
| Returns: | |
| list[NDArrayF32]: Each is a [H, W, 2] array of flipped optical | |
| flow. | |
| """ | |
| for i, flow in enumerate(flows): | |
| flow_ = torch.from_numpy(flow) | |
| if self.direction == "horizontal": | |
| image_flipped = flow_.flip(1).numpy() | |
| image_flipped[..., 0] *= -1 | |
| flows[i] = image_flipped | |
| if self.direction == "vertical": | |
| image_flipped = flow_.flip(0).numpy() | |
| image_flipped[..., 1] *= -1 | |
| flows[i] = image_flipped | |
| return flows | |
| class FlipInstanceMasks: | |
| """Flip instance masks.""" | |
| def __init__(self, direction: str = "horizontal"): | |
| """Creates an instance of FlipInstanceMasks. | |
| Args: | |
| direction (str, optional): Either vertical or horizontal. | |
| Defaults to "horizontal". | |
| Raises: | |
| ValueError: If direction is not horizontal or vertical. | |
| """ | |
| if direction not in ["horizontal", "vertical"]: | |
| raise ValueError(f"Direction {direction} not known!") | |
| self.direction = direction | |
| def __call__(self, masks: list[NDArrayUI8]) -> list[NDArrayUI8]: | |
| """Execute flipping op. | |
| Args: | |
| masks (list[NDArrayUI8]): List of [N, H, W] array of masks. | |
| Returns: | |
| list[NDArrayUI8]: List of [N, H, W] array of flipped masks. | |
| """ | |
| for i, mask in enumerate(masks): | |
| mask_ = torch.from_numpy(mask) | |
| if self.direction == "horizontal": | |
| mask = mask_.flip(2).numpy() | |
| if self.direction == "vertical": | |
| mask = mask_.flip(1).numpy() | |
| masks[i] = mask | |
| return masks | |
| def get_axis(direction: str, axis_mode: AxisMode) -> int: | |
| """Get axis number of certain direction given axis mode. | |
| Args: | |
| direction (str): One of horizontal, vertical and lateral. | |
| axis_mode (AxisMode): axis mode. | |
| Returns: | |
| int: Number of axis in certain direction. | |
| """ | |
| if direction not in {"horizontal", "lateral", "vertical"}: | |
| raise ValueError(f"Direction {direction} not known!") | |
| coord_mapping = { | |
| AxisMode.ROS: {"horizontal": 0, "lateral": 1, "vertical": 2}, | |
| AxisMode.OPENCV: {"horizontal": 0, "vertical": 1, "lateral": 2}, | |
| } | |
| return coord_mapping[axis_mode][direction] | |
| class FlipBoxes3D: | |
| """Flip 3D bounding box array.""" | |
| def __init__(self, direction: str = "horizontal"): | |
| """Creates an instance of FlipBoxes3D. | |
| Args: | |
| direction (str, optional): Either vertical or horizontal. | |
| Defaults to "horizontal". | |
| """ | |
| self.direction = direction | |
| def __call__( | |
| self, boxes_list: list[NDArrayF32], axis_mode_list: list[AxisMode] | |
| ) -> list[NDArrayF32]: | |
| """Execute flipping.""" | |
| for i, (boxes, axis_mode) in enumerate( | |
| zip(boxes_list, axis_mode_list) | |
| ): | |
| axis = get_axis(self.direction, axis_mode) | |
| angle_dir = ( | |
| "vertical" if self.direction == "horizontal" else "lateral" | |
| ) | |
| angles_axis = get_axis(angle_dir, axis_mode) | |
| boxes[:, axis] *= -1.0 | |
| angles = matrix_to_euler_angles( | |
| quaternion_to_matrix(torch.from_numpy(boxes[:, 6:])) | |
| ) | |
| angles[:, angles_axis] = np.pi - angles[:, angles_axis] | |
| boxes[:, 6:] = matrix_to_quaternion( | |
| euler_angles_to_matrix(angles) | |
| ).numpy() | |
| boxes_list[i] = boxes | |
| return boxes_list | |
| class FlipPoints3D: | |
| """Flip pointcloud array.""" | |
| def __init__(self, direction: str = "horizontal"): | |
| """Creates an instance of FlipBoxes2D. | |
| Args: | |
| direction (str, optional): Either vertical or horizontal. | |
| Defaults to "horizontal". | |
| """ | |
| self.direction = direction | |
| def __call__( | |
| self, points3d_list: list[NDArrayF32], axis_mode_list: list[AxisMode] | |
| ) -> list[NDArrayF32]: | |
| """Execute flipping.""" | |
| for i, (points3d, axis_mode) in enumerate( | |
| zip(points3d_list, axis_mode_list) | |
| ): | |
| points3d[:, get_axis(self.direction, axis_mode)] *= -1.0 | |
| points3d_list[i] = points3d | |
| return points3d_list | |
| class FlipIntrinsics: | |
| """Modify intrinsics for image flip.""" | |
| def __init__(self, direction: str = "horizontal"): | |
| """Creates an instance of FlipIntrinsics. | |
| Args: | |
| direction (str, optional): Either vertical or horizontal. | |
| Defaults to "horizontal". | |
| Raises: | |
| ValueError: If direction is not horizontal or vertical. | |
| """ | |
| if direction not in ["horizontal", "vertical"]: | |
| raise ValueError(f"Direction {direction} not known!") | |
| self.direction = direction | |
| def __call__( | |
| self, intrinsics_list: list[NDArrayF32], images: list[NDArrayF32] | |
| ) -> list[NDArrayF32]: | |
| """Execute flipping.""" | |
| for i, (intrinsics, image) in enumerate(zip(intrinsics_list, images)): | |
| if self.direction == "horizontal": | |
| center = image.shape[2] / 2 | |
| intrinsics[0, 2] = center - intrinsics[0, 2] + center | |
| elif self.direction == "vertical": | |
| center = image.shape[1] / 2 | |
| intrinsics[1, 2] = center - intrinsics[1, 2] + center | |
| intrinsics_list[i] = intrinsics | |
| return intrinsics_list | |