CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
image_processing_vitpose.py685 linesDownload Raw Back to vitpose
1# coding=utf-82# Copyright 2024 The HuggingFace Inc. team. All rights reserved.3#4# Licensed under the Apache License, Version 2.0 (the "License");5# you may not use this file except in compliance with the License.6# You may obtain a copy of the License at7#8#     http://www.apache.org/licenses/LICENSE-2.09#10# Unless required by applicable law or agreed to in writing, software11# distributed under the License is distributed on an "AS IS" BASIS,12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13# See the License for the specific language governing permissions and14# limitations under the License.15"""Image processor class for VitPose."""16 17import itertools18import math19from typing import TYPE_CHECKING, Optional, Union20 21import numpy as np22 23from ...image_processing_utils import BaseImageProcessor, BatchFeature24from ...image_transforms import to_channel_dimension_format25from ...image_utils import (26    IMAGENET_DEFAULT_MEAN,27    IMAGENET_DEFAULT_STD,28    ChannelDimension,29    ImageInput,30    infer_channel_dimension_format,31    is_scaled_image,32    make_flat_list_of_images,33    to_numpy_array,34    valid_images,35)36from ...utils import TensorType, is_scipy_available, is_torch_available, is_vision_available, logging37 38 39if is_torch_available():40    import torch41 42if is_vision_available():43    import PIL44 45if is_scipy_available():46    from scipy.linalg import inv47    from scipy.ndimage import affine_transform, gaussian_filter48 49if TYPE_CHECKING:50    from .modeling_vitpose import VitPoseEstimatorOutput51 52logger = logging.get_logger(__name__)53 54 55# inspired by https://github.com/ViTAE-Transformer/ViTPose/blob/d5216452796c90c6bc29f5c5ec0bdba94366768a/mmpose/datasets/datasets/base/kpt_2d_sview_rgb_img_top_down_dataset.py#L13256def box_to_center_and_scale(57    box: Union[tuple, list, np.ndarray],58    image_width: int,59    image_height: int,60    normalize_factor: float = 200.0,61    padding_factor: float = 1.25,62):63    """64    Encodes a bounding box in COCO format into (center, scale).65 66    Args:67        box (`Tuple`, `List`, or `np.ndarray`):68            Bounding box in COCO format (top_left_x, top_left_y, width, height).69        image_width (`int`):70            Image width.71        image_height (`int`):72            Image height.73        normalize_factor (`float`):74            Width and height scale factor.75        padding_factor (`float`):76            Bounding box padding factor.77 78    Returns:79        tuple: A tuple containing center and scale.80 81        - `np.ndarray` [float32](2,): Center of the bbox (x, y).82        - `np.ndarray` [float32](2,): Scale of the bbox width & height.83    """84 85    top_left_x, top_left_y, width, height = box[:4]86    aspect_ratio = image_width / image_height87    center = np.array([top_left_x + width * 0.5, top_left_y + height * 0.5], dtype=np.float32)88 89    if width > aspect_ratio * height:90        height = width * 1.0 / aspect_ratio91    elif width < aspect_ratio * height:92        width = height * aspect_ratio93 94    scale = np.array([width / normalize_factor, height / normalize_factor], dtype=np.float32)95    scale = scale * padding_factor96 97    return center, scale98 99 100def coco_to_pascal_voc(bboxes: np.ndarray) -> np.ndarray:101    """102    Converts bounding boxes from the COCO format to the Pascal VOC format.103 104    In other words, converts from (top_left_x, top_left_y, width, height) format105    to (top_left_x, top_left_y, bottom_right_x, bottom_right_y).106 107    Args:108        bboxes (`np.ndarray` of shape `(batch_size, 4)):109            Bounding boxes in COCO format.110 111    Returns:112        `np.ndarray` of shape `(batch_size, 4) in Pascal VOC format.113    """114    bboxes[:, 2] = bboxes[:, 2] + bboxes[:, 0] - 1115    bboxes[:, 3] = bboxes[:, 3] + bboxes[:, 1] - 1116 117    return bboxes118 119 120def get_keypoint_predictions(heatmaps: np.ndarray) -> tuple[np.ndarray, np.ndarray]:121    """Get keypoint predictions from score maps.122 123    Args:124        heatmaps (`np.ndarray` of shape `(batch_size, num_keypoints, height, width)`):125            Model predicted heatmaps.126 127    Returns:128        tuple: A tuple containing aggregated results.129 130        - coords (`np.ndarray` of shape `(batch_size, num_keypoints, 2)`):131            Predicted keypoint location.132        - scores (`np.ndarray` of shape `(batch_size, num_keypoints, 1)`):133            Scores (confidence) of the keypoints.134    """135    if not isinstance(heatmaps, np.ndarray):136        raise TypeError("Heatmaps should be np.ndarray")137    if heatmaps.ndim != 4:138        raise ValueError("Heatmaps should be 4-dimensional")139 140    batch_size, num_keypoints, _, width = heatmaps.shape141    heatmaps_reshaped = heatmaps.reshape((batch_size, num_keypoints, -1))142    idx = np.argmax(heatmaps_reshaped, 2).reshape((batch_size, num_keypoints, 1))143    scores = np.amax(heatmaps_reshaped, 2).reshape((batch_size, num_keypoints, 1))144 145    preds = np.tile(idx, (1, 1, 2)).astype(np.float32)146    preds[:, :, 0] = preds[:, :, 0] % width147    preds[:, :, 1] = preds[:, :, 1] // width148 149    preds = np.where(np.tile(scores, (1, 1, 2)) > 0.0, preds, -1)150    return preds, scores151 152 153def post_dark_unbiased_data_processing(coords: np.ndarray, batch_heatmaps: np.ndarray, kernel: int = 3) -> np.ndarray:154    """DARK post-pocessing. Implemented by unbiased_data_processing.155 156    Paper references:157    - Huang et al. The Devil is in the Details: Delving into Unbiased Data Processing for Human Pose Estimation (CVPR 2020).158    - Zhang et al. Distribution-Aware Coordinate Representation for Human Pose Estimation (CVPR 2020).159 160    Args:161        coords (`np.ndarray` of shape `(num_persons, num_keypoints, 2)`):162            Initial coordinates of human pose.163        batch_heatmaps (`np.ndarray` of shape `(batch_size, num_keypoints, height, width)`):164            Batched heatmaps as predicted by the model.165            A batch_size of 1 is used for the bottom up paradigm where all persons share the same heatmap.166            A batch_size of `num_persons` is used for the top down paradigm where each person has its own heatmaps.167        kernel (`int`, *optional*, defaults to 3):168            Gaussian kernel size (K) for modulation.169 170    Returns:171        `np.ndarray` of shape `(num_persons, num_keypoints, 2)` ):172            Refined coordinates.173    """174    batch_size, num_keypoints, height, width = batch_heatmaps.shape175    num_coords = coords.shape[0]176    if not (batch_size == 1 or batch_size == num_coords):177        raise ValueError("The batch size of heatmaps should be 1 or equal to the batch size of coordinates.")178    radius = int((kernel - 1) // 2)179    batch_heatmaps = np.array(180        [181            [gaussian_filter(heatmap, sigma=0.8, radius=(radius, radius), axes=(0, 1)) for heatmap in heatmaps]182            for heatmaps in batch_heatmaps183        ]184    )185    batch_heatmaps = np.clip(batch_heatmaps, 0.001, 50)186    batch_heatmaps = np.log(batch_heatmaps)187 188    batch_heatmaps_pad = np.pad(batch_heatmaps, ((0, 0), (0, 0), (1, 1), (1, 1)), mode="edge").flatten()189 190    # calculate indices for coordinates191    index = coords[..., 0] + 1 + (coords[..., 1] + 1) * (width + 2)192    index += (width + 2) * (height + 2) * np.arange(0, batch_size * num_keypoints).reshape(-1, num_keypoints)193    index = index.astype(int).reshape(-1, 1)194    i_ = batch_heatmaps_pad[index]195    ix1 = batch_heatmaps_pad[index + 1]196    iy1 = batch_heatmaps_pad[index + width + 2]197    ix1y1 = batch_heatmaps_pad[index + width + 3]198    ix1_y1_ = batch_heatmaps_pad[index - width - 3]199    ix1_ = batch_heatmaps_pad[index - 1]200    iy1_ = batch_heatmaps_pad[index - 2 - width]201 202    # calculate refined coordinates using Newton's method203    dx = 0.5 * (ix1 - ix1_)204    dy = 0.5 * (iy1 - iy1_)205    derivative = np.concatenate([dx, dy], axis=1)206    derivative = derivative.reshape(num_coords, num_keypoints, 2, 1)207    dxx = ix1 - 2 * i_ + ix1_208    dyy = iy1 - 2 * i_ + iy1_209    dxy = 0.5 * (ix1y1 - ix1 - iy1 + i_ + i_ - ix1_ - iy1_ + ix1_y1_)210    hessian = np.concatenate([dxx, dxy, dxy, dyy], axis=1)211    hessian = hessian.reshape(num_coords, num_keypoints, 2, 2)212    hessian = np.linalg.inv(hessian + np.finfo(np.float32).eps * np.eye(2))213    coords -= np.einsum("ijmn,ijnk->ijmk", hessian, derivative).squeeze()214    return coords215 216 217def transform_preds(coords: np.ndarray, center: np.ndarray, scale: np.ndarray, output_size: np.ndarray) -> np.ndarray:218    """Get final keypoint predictions from heatmaps and apply scaling and219    translation to map them back to the image.220 221    Note:222        num_keypoints: K223 224    Args:225        coords (`np.ndarray` of shape `(num_keypoints, ndims)`):226 227            * If ndims=2, corrds are predicted keypoint location.228            * If ndims=4, corrds are composed of (x, y, scores, tags)229            * If ndims=5, corrds are composed of (x, y, scores, tags,230              flipped_tags)231 232        center (`np.ndarray` of shape `(2,)`):233            Center of the bounding box (x, y).234        scale (`np.ndarray` of shape `(2,)`):235            Scale of the bounding box wrt original image of width and height.236        output_size (`np.ndarray` of shape `(2,)`):237            Size of the destination heatmaps in (height, width) format.238 239    Returns:240        np.ndarray: Predicted coordinates in the images.241    """242    if coords.shape[1] not in (2, 4, 5):243        raise ValueError("Coordinates need to have either 2, 4 or 5 dimensions.")244    if len(center) != 2:245        raise ValueError("Center needs to have 2 elements, one for x and one for y.")246    if len(scale) != 2:247        raise ValueError("Scale needs to consist of a width and height")248    if len(output_size) != 2:249        raise ValueError("Output size needs to consist of a height and width")250 251    # Recover the scale which is normalized by a factor of 200.252    scale = scale * 200.0253 254    # We use unbiased data processing255    scale_y = scale[1] / (output_size[0] - 1.0)256    scale_x = scale[0] / (output_size[1] - 1.0)257 258    target_coords = np.ones_like(coords)259    target_coords[:, 0] = coords[:, 0] * scale_x + center[0] - scale[0] * 0.5260    target_coords[:, 1] = coords[:, 1] * scale_y + center[1] - scale[1] * 0.5261 262    return target_coords263 264 265def get_warp_matrix(theta: float, size_input: np.ndarray, size_dst: np.ndarray, size_target: np.ndarray):266    """267    Calculate the transformation matrix under the constraint of unbiased. Paper ref: Huang et al. The Devil is in the268    Details: Delving into Unbiased Data Processing for Human Pose Estimation (CVPR 2020).269 270    Source: https://github.com/open-mmlab/mmpose/blob/master/mmpose/core/post_processing/post_transforms.py271 272    Args:273        theta (`float`):274            Rotation angle in degrees.275        size_input (`np.ndarray`):276            Size of input image [width, height].277        size_dst (`np.ndarray`):278            Size of output image [width, height].279        size_target (`np.ndarray`):280            Size of ROI in input plane [w, h].281 282    Returns:283        `np.ndarray`: A matrix for transformation.284    """285    theta = np.deg2rad(theta)286    matrix = np.zeros((2, 3), dtype=np.float32)287    scale_x = size_dst[0] / size_target[0]288    scale_y = size_dst[1] / size_target[1]289    matrix[0, 0] = math.cos(theta) * scale_x290    matrix[0, 1] = -math.sin(theta) * scale_x291    matrix[0, 2] = scale_x * (292        -0.5 * size_input[0] * math.cos(theta) + 0.5 * size_input[1] * math.sin(theta) + 0.5 * size_target[0]293    )294    matrix[1, 0] = math.sin(theta) * scale_y295    matrix[1, 1] = math.cos(theta) * scale_y296    matrix[1, 2] = scale_y * (297        -0.5 * size_input[0] * math.sin(theta) - 0.5 * size_input[1] * math.cos(theta) + 0.5 * size_target[1]298    )299    return matrix300 301 302def scipy_warp_affine(src, M, size):303    """304    This function implements cv2.warpAffine function using affine_transform in scipy. See https://docs.scipy.org/doc/scipy/reference/generated/scipy.ndimage.affine_transform.html and https://docs.opencv.org/4.x/d4/d61/tutorial_warp_affine.html for more details.305 306    Note: the original implementation of cv2.warpAffine uses cv2.INTER_LINEAR.307    """308    channels = [src[..., i] for i in range(src.shape[-1])]309 310    # Convert to a 3x3 matrix used by SciPy311    M_scipy = np.vstack([M, [0, 0, 1]])312    # If you have a matrix for the ‘push’ transformation, use its inverse (numpy.linalg.inv) in this function.313    M_inv = inv(M_scipy)314    M_inv[0, 0], M_inv[0, 1], M_inv[1, 0], M_inv[1, 1], M_inv[0, 2], M_inv[1, 2] = (315        M_inv[1, 1],316        M_inv[1, 0],317        M_inv[0, 1],318        M_inv[0, 0],319        M_inv[1, 2],320        M_inv[0, 2],321    )322 323    new_src = [affine_transform(channel, M_inv, output_shape=size, order=1) for channel in channels]324    new_src = np.stack(new_src, axis=-1)325    return new_src326 327 328class VitPoseImageProcessor(BaseImageProcessor):329    r"""330    Constructs a VitPose image processor.331 332    Args:333        do_affine_transform (`bool`, *optional*, defaults to `True`):334            Whether to apply an affine transformation to the input images.335        size (`dict[str, int]` *optional*, defaults to `{"height": 256, "width": 192}`):336            Resolution of the image after `affine_transform` is applied. Only has an effect if `do_affine_transform` is set to `True`. Can337            be overridden by `size` in the `preprocess` method.338        do_rescale (`bool`, *optional*, defaults to `True`):339            Whether or not to apply the scaling factor (to make pixel values floats between 0. and 1.).340        rescale_factor (`int` or `float`, *optional*, defaults to `1/255`):341            Scale factor to use if rescaling the image. Can be overridden by `rescale_factor` in the `preprocess`342            method.343        do_normalize (`bool`, *optional*, defaults to `True`):344            Whether or not to normalize the input with mean and standard deviation.345        image_mean (`list[int]`, defaults to `[0.485, 0.456, 0.406]`, *optional*):346            The sequence of means for each channel, to be used when normalizing images.347        image_std (`list[int]`, defaults to `[0.229, 0.224, 0.225]`, *optional*):348            The sequence of standard deviations for each channel, to be used when normalizing images.349    """350 351    model_input_names = ["pixel_values"]352 353    def __init__(354        self,355        do_affine_transform: bool = True,356        size: Optional[dict[str, int]] = None,357        do_rescale: bool = True,358        rescale_factor: Union[int, float] = 1 / 255,359        do_normalize: bool = True,360        image_mean: Optional[Union[float, list[float]]] = None,361        image_std: Optional[Union[float, list[float]]] = None,362        **kwargs,363    ):364        super().__init__(**kwargs)365        self.do_affine_transform = do_affine_transform366        self.size = size if size is not None else {"height": 256, "width": 192}367        self.do_rescale = do_rescale368        self.rescale_factor = rescale_factor369        self.do_normalize = do_normalize370        self.image_mean = image_mean if image_mean is not None else IMAGENET_DEFAULT_MEAN371        self.image_std = image_std if image_std is not None else IMAGENET_DEFAULT_STD372        self.normalize_factor = 200.0373 374    def affine_transform(375        self,376        image: np.ndarray,377        center: tuple[float],378        scale: tuple[float],379        rotation: float,380        size: dict[str, int],381        data_format: Optional[ChannelDimension] = None,382        input_data_format: Optional[Union[str, ChannelDimension]] = None,383    ) -> np.ndarray:384        """385        Apply an affine transformation to an image.386 387        Args:388            image (`np.ndarray`):389                Image to transform.390            center (`tuple[float]`):391                Center of the bounding box (x, y).392            scale (`tuple[float]`):393                Scale of the bounding box with respect to height/width.394            rotation (`float`):395                Rotation angle in degrees.396            size (`dict[str, int]`):397                Size of the destination image.398            data_format (`ChannelDimension`, *optional*, defaults to `ChannelDimension.FIRST`):399                The channel dimension format of the output image.400            input_data_format (`str` or `ChannelDimension`, *optional*):401                The channel dimension format of the input image.402        """403 404        data_format = input_data_format if data_format is None else data_format405 406        size = (size["width"], size["height"])407 408        # one uses a pixel standard deviation of 200 pixels409        transformation = get_warp_matrix(rotation, center * 2.0, np.array(size) - 1.0, scale * 200.0)410 411        # input image requires channels last format412        image = (413            image414            if input_data_format == ChannelDimension.LAST415            else to_channel_dimension_format(image, ChannelDimension.LAST, input_data_format)416        )417        image = scipy_warp_affine(src=image, M=transformation, size=(size[1], size[0]))418 419        image = to_channel_dimension_format(image, data_format, ChannelDimension.LAST)420 421        return image422 423    def preprocess(424        self,425        images: ImageInput,426        boxes: Union[list[list[float]], np.ndarray],427        do_affine_transform: Optional[bool] = None,428        size: Optional[dict[str, int]] = None,429        do_rescale: Optional[bool] = None,430        rescale_factor: Optional[float] = None,431        do_normalize: Optional[bool] = None,432        image_mean: Optional[Union[float, list[float]]] = None,433        image_std: Optional[Union[float, list[float]]] = None,434        return_tensors: Optional[Union[str, TensorType]] = None,435        data_format: Union[str, ChannelDimension] = ChannelDimension.FIRST,436        input_data_format: Optional[Union[str, ChannelDimension]] = None,437    ) -> PIL.Image.Image:438        """439        Preprocess an image or batch of images.440 441        Args:442            images (`ImageInput`):443                Image to preprocess. Expects a single or batch of images with pixel values ranging from 0 to 255. If444                passing in images with pixel values between 0 and 1, set `do_rescale=False`.445 446            boxes (`list[list[list[float]]]` or `np.ndarray`):447                List or array of bounding boxes for each image. Each box should be a list of 4 floats representing the bounding448                box coordinates in COCO format (top_left_x, top_left_y, width, height).449 450            do_affine_transform (`bool`, *optional*, defaults to `self.do_affine_transform`):451                Whether to apply an affine transformation to the input images.452            size (`dict[str, int]` *optional*, defaults to `self.size`):453                Dictionary in the format `{"height": h, "width": w}` specifying the size of the output image after454                resizing.455            do_rescale (`bool`, *optional*, defaults to `self.do_rescale`):456                Whether to rescale the image values between [0 - 1].457            rescale_factor (`float`, *optional*, defaults to `self.rescale_factor`):458                Rescale factor to rescale the image by if `do_rescale` is set to `True`.459            do_normalize (`bool`, *optional*, defaults to `self.do_normalize`):460                Whether to normalize the image.461            image_mean (`float` or `list[float]`, *optional*, defaults to `self.image_mean`):462                Image mean to use if `do_normalize` is set to `True`.463            image_std (`float` or `list[float]`, *optional*, defaults to `self.image_std`):464                Image standard deviation to use if `do_normalize` is set to `True`.465            return_tensors (`str` or [`~utils.TensorType`], *optional*, defaults to `'np'`):466                If set, will return tensors of a particular framework. Acceptable values are:467 468                - `'tf'`: Return TensorFlow `tf.constant` objects.469                - `'pt'`: Return PyTorch `torch.Tensor` objects.470                - `'np'`: Return NumPy `np.ndarray` objects.471                - `'jax'`: Return JAX `jnp.ndarray` objects.472 473        Returns:474            [`BatchFeature`]: A [`BatchFeature`] with the following fields:475 476            - **pixel_values** -- Pixel values to be fed to a model, of shape (batch_size, num_channels, height,477              width).478        """479        do_affine_transform = do_affine_transform if do_affine_transform is not None else self.do_affine_transform480        size = size if size is not None else self.size481        do_rescale = do_rescale if do_rescale is not None else self.do_rescale482        rescale_factor = rescale_factor if rescale_factor is not None else self.rescale_factor483        do_normalize = do_normalize if do_normalize is not None else self.do_normalize484        image_mean = image_mean if image_mean is not None else self.image_mean485        image_std = image_std if image_std is not None else self.image_std486 487        images = make_flat_list_of_images(images)488 489        if not valid_images(images):490            raise ValueError(491                "Invalid image type. Must be of type PIL.Image.Image, numpy.ndarray, "492                "torch.Tensor, tf.Tensor or jax.ndarray."493            )494 495        if isinstance(boxes, list) and len(images) != len(boxes):496            raise ValueError(f"Batch of images and boxes mismatch : {len(images)} != {len(boxes)}")497        elif isinstance(boxes, np.ndarray) and len(images) != boxes.shape[0]:498            raise ValueError(f"Batch of images and boxes mismatch : {len(images)} != {boxes.shape[0]}")499 500        # All transformations expect numpy arrays.501        images = [to_numpy_array(image) for image in images]502 503        if is_scaled_image(images[0]) and do_rescale:504            logger.warning_once(505                "It looks like you are trying to rescale already rescaled images. If the input"506                " images have pixel values between 0 and 1, set `do_rescale=False` to avoid rescaling them again."507            )508 509        if input_data_format is None:510            # We assume that all images have the same channel dimension format.511            input_data_format = infer_channel_dimension_format(images[0])512 513        # transformations (affine transformation + rescaling + normalization)514        if self.do_affine_transform:515            new_images = []516            for image, image_boxes in zip(images, boxes):517                for box in image_boxes:518                    center, scale = box_to_center_and_scale(519                        box,520                        image_width=size["width"],521                        image_height=size["height"],522                        normalize_factor=self.normalize_factor,523                    )524                    transformed_image = self.affine_transform(525                        image, center, scale, rotation=0, size=size, input_data_format=input_data_format526                    )527                    new_images.append(transformed_image)528            images = new_images529 530        # For batch processing, the number of boxes must be consistent across all images in the batch.531        # When using a list input, the number of boxes can vary dynamically per image.532        # The image processor creates pixel_values of shape (batch_size*num_persons, num_channels, height, width)533 534        all_images = []535        for image in images:536            if do_rescale:537                image = self.rescale(image=image, scale=rescale_factor, input_data_format=input_data_format)538 539            if do_normalize:540                image = self.normalize(541                    image=image, mean=image_mean, std=image_std, input_data_format=input_data_format542                )543 544            all_images.append(image)545        images = [546            to_channel_dimension_format(image, data_format, input_channel_dim=input_data_format)547            for image in all_images548        ]549 550        data = {"pixel_values": images}551        encoded_inputs = BatchFeature(data=data, tensor_type=return_tensors)552 553        return encoded_inputs554 555    def keypoints_from_heatmaps(556        self,557        heatmaps: np.ndarray,558        center: np.ndarray,559        scale: np.ndarray,560        kernel: int = 11,561    ):562        """563        Get final keypoint predictions from heatmaps and transform them back to564        the image.565 566        Args:567            heatmaps (`np.ndarray` of shape `(batch_size, num_keypoints, height, width])`):568                Model predicted heatmaps.569            center (`np.ndarray` of shape `(batch_size, 2)`):570                Center of the bounding box (x, y).571            scale (`np.ndarray` of shape `(batch_size, 2)`):572                Scale of the bounding box wrt original images of width and height.573            kernel (int, *optional*, defaults to 11):574                Gaussian kernel size (K) for modulation, which should match the heatmap gaussian sigma when training.575                K=17 for sigma=3 and k=11 for sigma=2.576 577        Returns:578            tuple: A tuple containing keypoint predictions and scores.579 580            - preds (`np.ndarray` of shape `(batch_size, num_keypoints, 2)`):581                Predicted keypoint location in images.582            - scores (`np.ndarray` of shape `(batch_size, num_keypoints, 1)`):583                Scores (confidence) of the keypoints.584        """585        batch_size, _, height, width = heatmaps.shape586 587        coords, scores = get_keypoint_predictions(heatmaps)588 589        preds = post_dark_unbiased_data_processing(coords, heatmaps, kernel=kernel)590 591        # Transform back to the image592        for i in range(batch_size):593            preds[i] = transform_preds(preds[i], center=center[i], scale=scale[i], output_size=[height, width])594 595        return preds, scores596 597    def post_process_pose_estimation(598        self,599        outputs: "VitPoseEstimatorOutput",600        boxes: Union[list[list[list[float]]], np.ndarray],601        kernel_size: int = 11,602        threshold: Optional[float] = None,603        target_sizes: Union[TensorType, list[tuple]] = None,604    ):605        """606        Transform the heatmaps into keypoint predictions and transform them back to the image.607 608        Args:609            outputs (`VitPoseEstimatorOutput`):610                VitPoseForPoseEstimation model outputs.611            boxes (`list[list[list[float]]]` or `np.ndarray`):612                List or array of bounding boxes for each image. Each box should be a list of 4 floats representing the bounding613                box coordinates in COCO format (top_left_x, top_left_y, width, height).614            kernel_size (`int`, *optional*, defaults to 11):615                Gaussian kernel size (K) for modulation.616            threshold (`float`, *optional*, defaults to None):617                Score threshold to keep object detection predictions.618            target_sizes (`torch.Tensor` or `list[tuple[int, int]]`, *optional*):619                Tensor of shape `(batch_size, 2)` or list of tuples (`tuple[int, int]`) containing the target size620                `(height, width)` of each image in the batch. If unset, predictions will be resize with the default value.621        Returns:622            `list[list[Dict]]`: A list of dictionaries, each dictionary containing the keypoints and boxes for an image623            in the batch as predicted by the model.624        """625 626        # First compute centers and scales for each bounding box627        batch_size, num_keypoints, _, _ = outputs.heatmaps.shape628 629        if target_sizes is not None:630            if batch_size != len(target_sizes):631                raise ValueError(632                    "Make sure that you pass in as many target sizes as the batch dimension of the logits"633                )634 635        centers = np.zeros((batch_size, 2), dtype=np.float32)636        scales = np.zeros((batch_size, 2), dtype=np.float32)637        flattened_boxes = list(itertools.chain(*boxes))638        for i in range(batch_size):639            if target_sizes is not None:640                image_width, image_height = target_sizes[i][0], target_sizes[i][1]641                scale_factor = np.array([image_width, image_height, image_width, image_height])642                flattened_boxes[i] = flattened_boxes[i] * scale_factor643            width, height = self.size["width"], self.size["height"]644            center, scale = box_to_center_and_scale(flattened_boxes[i], image_width=width, image_height=height)645            centers[i, :] = center646            scales[i, :] = scale647 648        preds, scores = self.keypoints_from_heatmaps(649            outputs.heatmaps.cpu().numpy(), centers, scales, kernel=kernel_size650        )651 652        all_boxes = np.zeros((batch_size, 4), dtype=np.float32)653        all_boxes[:, 0:2] = centers[:, 0:2]654        all_boxes[:, 2:4] = scales[:, 0:2]655 656        poses = torch.tensor(preds)657        scores = torch.tensor(scores)658        labels = torch.arange(0, num_keypoints)659        bboxes_xyxy = torch.tensor(coco_to_pascal_voc(all_boxes))660 661        results: list[list[dict[str, torch.Tensor]]] = []662 663        pose_bbox_pairs = zip(poses, scores, bboxes_xyxy)664 665        for image_bboxes in boxes:666            image_results: list[dict[str, torch.Tensor]] = []667            for _ in image_bboxes:668                # Unpack the next pose and bbox_xyxy from the iterator669                pose, score, bbox_xyxy = next(pose_bbox_pairs)670                score = score.squeeze()671                keypoints_labels = labels672                if threshold is not None:673                    keep = score > threshold674                    pose = pose[keep]675                    score = score[keep]676                    keypoints_labels = keypoints_labels[keep]677                pose_result = {"keypoints": pose, "scores": score, "labels": keypoints_labels, "bbox": bbox_xyxy}678                image_results.append(pose_result)679            results.append(image_results)680 681        return results682 683 684__all__ = ["VitPoseImageProcessor"]685 
Aluode/PerceptionLabPortable · CoolFace