"""Resize transformation.""" from __future__ import annotations import math import numpy as np import torch from vis4d.common.typing import NDArrayF32, NDArrayI64 from vis4d.data.const import CommonKeys as K from vis4d.data.transforms.base import Transform from vis4d.data.transforms.resize import ResizeParam, resize_tensor @Transform(K.images, ["transforms.resize", K.input_hw]) class GenResizeParameters: """Generate the parameters for a resize operation.""" def __init__( self, shape: tuple[int, int], scales: tuple[float, float] | float = 1.0 ) -> None: """Create a new instance of the class.""" self.shape = shape self.scales = scales def __call__( self, images: list[NDArrayF32] ) -> tuple[list[ResizeParam], list[tuple[int, int]]]: """Compute the parameters and put them in the data dict.""" if isinstance(self.scales, float): random_scale = self.scales else: random_scale = np.random.uniform(self.scales[0], self.scales[1]) shape = ( math.ceil(self.shape[0] * random_scale - 0.5), math.ceil(self.shape[1] * random_scale - 0.5), ) output_ratio = shape[1] / shape[0] image = images[0] input_h, input_w = (image.shape[1], image.shape[2]) input_ratio = input_w / input_h if output_ratio > input_ratio: scale = shape[0] / input_h else: scale = shape[1] / input_w target_shape = ( math.ceil(input_h * scale - 0.5), math.ceil(input_w * scale - 0.5), ) scale_factor = (target_shape[0] / input_h, target_shape[1] / input_w) resize_params = [ ResizeParam(target_shape=target_shape, scale_factor=scale_factor) ] * len(images) target_shapes = [target_shape] * len(images) return resize_params, target_shapes @Transform( [K.panoptic_masks, "transforms.resize.target_shape"], K.panoptic_masks ) class ResizePanopticMasks: """Resize panoptic segmentation masks.""" def __call__( self, masks_list: list[NDArrayI64], target_shape_list: list[tuple[int, int]], ) -> list[NDArrayI64]: """Resize masks.""" for i, (masks, target_shape) in enumerate( zip(masks_list, target_shape_list) ): masks_ = torch.from_numpy(masks) masks_ = ( resize_tensor( masks_.float().unsqueeze(0).unsqueeze(0), target_shape, interpolation="nearest", ) .type(masks_.dtype) .squeeze(0) .squeeze(0) ) masks_list[i] = masks_.numpy() return masks_list @Transform([K.boxes3d, "transforms.resize.scale_factor"], K.boxes3d) class ResizeBoxes3D: """Resize list of 2D bounding boxes.""" def __call__( self, boxes_list: list[NDArrayF32], scale_factors: list[tuple[float, float]], ) -> list[NDArrayF32]: """Resize 2D bounding boxes. Args: boxes_list: (list[NDArrayF32]): The bounding boxes to be resized. scale_factors (list[tuple[float, float]]): scaling factors. Returns: list[NDArrayF32]: Resized bounding boxes according to parameters in resize. """ for i, (boxes, scale_factor) in enumerate( zip(boxes_list, scale_factors) ): boxes[:, 2] /= scale_factor[0] boxes_list[i] = boxes return boxes_list