CoolFace
Modelpublic

rhymes-ai/Aria-sequential_mlp

sourceHugging Faceapache-2.0updated 2y agoView on Hugging Face
17likes81downloads
vision_processor.py322 linesDownload Raw Back to root
1# Copyright 2024 Rhymes AI. All rights reserved.2#3# Licensed to the Apache Software Foundation (ASF) under one4# or more contributor license agreements.  See the NOTICE file5# distributed with this work for additional information6# regarding copyright ownership.  The ASF licenses this file7# to you under the Apache License, Version 2.0 (the8# "License"); you may not use this file except in compliance9# with the License.  You may obtain a copy of the License at10#11#   http://www.apache.org/licenses/LICENSE-2.012#13# Unless required by applicable law or agreed to in writing,14# software distributed under the License is distributed on an15# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY16# KIND, either express or implied.  See the License for the17# specific language governing permissions and limitations18# under the License.19 20from typing import List, Optional, Union21 22import numpy as np23import torch24from PIL import Image, ImageOps25from torchvision import transforms26from transformers import BaseImageProcessor, BatchFeature, TensorType27 28 29def _select_best_resolution(30    img_width: int, img_height: int, target_ratios: List[List[int]], patch_size: int31):32    """33    Selects the best resolution from a list of possible resolutions based on the original size.34 35    Args:36        img_width: the original widths of images.37        img_height: the original heights of images.38        target_ratios (2d numpy array): dimension size (M,2)39        patch_size (int): image patch size40 41    Returns:42        tuple: The best fit resolution in the format (width, height).43    """44 45    aspect_ratio = img_width / img_height46    best_ratio_diff = float("inf")47    best_ratio_w, best_ratio_h = 1, 148    area = np.int32(img_height) * np.int32(img_height)49    for ratio in target_ratios:50        target_aspect_ratio = ratio[0] / ratio[1]51        ratio_diff = abs(aspect_ratio - target_aspect_ratio)52        if ratio_diff < best_ratio_diff:53            best_ratio_diff = ratio_diff54            best_ratio_w, best_ratio_h = ratio[0], ratio[1]55        elif (56            ratio_diff == best_ratio_diff57            and area > 0.5 * patch_size * patch_size * ratio[0] * ratio[1]58        ):59            best_ratio_w, best_ratio_h = ratio[0], ratio[1]60 61    return best_ratio_w, best_ratio_h62 63 64def _split_image(65    image: Image.Image,66    split_image: bool,67    split_ratio: List[List[int]],68    patch_size: int,69) -> List[Image.Image]:70    """71    Split image into multiple patches72 73    Args:74        image (PIL.Image): Input image.75        split_image (bool): Whether to split the image into patches.76        split_ratio (2d numpy array): dimension size (M,2)77        patch_size (int): image patch size78 79    Returns:80        List[PIL.Image]: List of splitted images.81    """82    if split_image:83        ratio_width, ratio_height = _select_best_resolution(84            image.width, image.height, split_ratio, patch_size85        )86        resize_width = patch_size * ratio_width87        resize_height = patch_size * ratio_height88        blocks = ratio_width * ratio_height89        resized_img = image.resize((resize_width, resize_height))90        processed_images = []91        for i in range(blocks):92            box = (93                (i % (resize_width // patch_size)) * patch_size,94                (i // (resize_width // patch_size)) * patch_size,95                ((i % (resize_width // patch_size)) + 1) * patch_size,96                ((i // (resize_width // patch_size)) + 1) * patch_size,97            )98            # split the image99            split_img = resized_img.crop(box)100            processed_images.append(split_img)101        assert len(processed_images) == blocks102        if len(processed_images) != 1:103            processed_images.insert(0, image)104        return processed_images105    else:106        return [image]107 108 109def keep_ratio_resize_and_pixel_mask(110    img: Image.Image, max_size, min_size=336, padding_value=0111):112    """113    Resize an image while maintaining aspect ratio and create a pixel mask.114 115    Args:116        img (PIL.Image): Input image.117        max_size (int): Maximum size for the larger dimension of the image.118        min_size (int, optional): Minimum size for the smaller dimension. Defaults to 336.119        padding_value (int, optional): Value used for padding. Defaults to 0.120 121    Returns:122        tuple: A tuple containing:123            - PIL.Image: Resized and padded image.124            - torch.Tensor: Boolean pixel mask. This mask is a 2D tensor of shape (max_size, max_size) where:125                - True (1) values indicate pixels that belong to the original resized image.126                - False (0) values indicate pixels that are part of the padding.127              The mask helps distinguish between actual image content and padded areas in subsequent processing steps.128    """129    img = img.convert("RGB")130    # rescale the given image, keep the aspect ratio131    scale = max_size / max(img.size)132 133    w, h = img.size134    if w >= h:135        new_size = (max_size, max(int(h * scale), min_size))  # w, h136    else:137        new_size = (max(int(w * scale), min_size), max_size)  # w, h138 139    img_resized = img.resize(new_size, resample=Image.Resampling.BICUBIC)140 141    # padding the right/bottom142    padding_right, padding_bottom = max_size - new_size[0], max_size - new_size[1]143    img_padded = ImageOps.expand(144        img_resized, (0, 0, padding_right, padding_bottom), fill=padding_value145    )146 147    # Create a pixel mask148    pixel_mask = torch.zeros(max_size, max_size)149    pixel_mask[: new_size[1], : new_size[0]] = 1150    pixel_mask = pixel_mask.bool()151    return img_padded, pixel_mask152 153 154class AriaVisionProcessor(BaseImageProcessor):155    """156    A vision processor for the Aria model that handles image preprocessing.157    """158 159    def __init__(160        self,161        max_image_size=980,162        min_image_size=336,163        image_mean=[0.5, 0.5, 0.5],164        image_std=[0.5, 0.5, 0.5],165        **kwargs,166    ):167        """168        Initialize the AriaVisionProcessor.169 170        Args:171            max_image_size (int, optional): Maximum image size. Defaults to 980.172            min_image_size (int, optional): Minimum image size. Defaults to 336.173            mean (list, optional): Mean values for normalization. Defaults to [0.5, 0.5, 0.5].174            std (list, optional): Standard deviation values for normalization. Defaults to [0.5, 0.5, 0.5].175        """176        super().__init__(**kwargs)177 178        self.max_image_size = max_image_size179        self.min_image_size = min_image_size180        self.image_mean = image_mean181        self.image_std = image_std182        self.auto_map = {183            "AutoProcessor": "processing_aria.AriaProcessor",184            "AutoImageProcessor": "vision_processor.AriaVisionProcessor",185        }186 187        # we make the transform a property so that it is lazily initialized,188        # this could avoid the error "TypeError: Object of type Normalize is not JSON serializable"189        # when we used save_pretrained or from_pretrained.190        self._transform = None191        self._set_processor_class("AriaProcessor")192 193    @property194    def transform(self):195        if self._transform is None:196            # Recreate the transform when accessed197            self._transform = transforms.Compose(198                [199                    transforms.ToTensor(),200                    transforms.Normalize(self.image_mean, self.image_std),201                ]202            )203        return self._transform204 205    def __call__(206        self,207        images: Union[Image.Image, List[Image.Image]],208        max_image_size: Optional[int] = 980,209        min_image_size: Optional[int] = 336,210        return_tensors: Optional[Union[str, TensorType]] = "pt",211        split_image: Optional[bool] = False,212        split_ratio: Optional[List[List[int]]] = [213            [1, 2],214            [1, 3],215            [1, 4],216            [1, 5],217            [1, 6],218            [1, 7],219            [1, 8],220            [2, 4],221            [2, 3],222            [2, 2],223            [2, 1],224            [3, 1],225            [3, 2],226            [4, 1],227            [4, 2],228            [5, 1],229            [6, 1],230            [7, 1],231            [8, 1],232        ],233    ):234        """235        Process a list of images.236 237        Args:238            images (list): List of PIL.Image objects.239            max_image_size (int, optional): Override the default max image size. Defaults to None.240            return_tensors (str or TensorType, optional): The type of tensor to return. Defaults to "pt".241            split_image (bool, optional): Whether to split the image. Defaults to False.242            split_ratio (list, optional): The ratio for splitting the image. Defaults to a list of common split ratios.243        Returns:244            BatchFeature: A BatchFeature object containing:245                - 'pixel_values': Tensor of processed image pixel values.246                - 'pixel_mask': Boolean pixel mask. This mask is a 2D tensor of shape (max_size, max_size) where:247                    - True (1) values indicate pixels that belong to the original resized image.248                    - False (0) values indicate pixels that are part of the padding.249                  The mask helps distinguish between actual image content and padded areas in subsequent processing steps.250                - 'num_crops': Tensor of the number of crops for each image.251        """252        max_size = self.max_image_size if max_image_size is None else max_image_size253        min_size = self.min_image_size if min_image_size is None else min_image_size254 255        if max_size not in [490, 980]:256            raise ValueError("max_image_size must be either 490 or 980")257 258        if isinstance(images, Image.Image):259            images = [images]260 261        pixel_values = []262        pixel_masks = []263        num_crops = []264 265        for image in images:266            crop_images = _split_image(image, split_image, split_ratio, max_size)267            num_crops.append(torch.tensor(len(crop_images)))268            for crop_image in crop_images:269                img_padded, pixel_mask = keep_ratio_resize_and_pixel_mask(270                    crop_image, max_size, min_size271                )272                img_padded = self.transform(img_padded)273                pixel_values.append(img_padded)274                pixel_masks.append(pixel_mask)275 276        return BatchFeature(277            data={278                "pixel_values": torch.stack(pixel_values),279                "pixel_mask": torch.stack(pixel_masks),280                "num_crops": torch.stack(num_crops),281            },282            tensor_type=return_tensors,283        )284 285    def preprocess(286        self,287        images,288        max_image_size=None,289        min_image_size=None,290        return_tensors: Optional[Union[str, TensorType]] = None,291        split_image: Optional[bool] = False,292        split_ratio: Optional[List[List[int]]] = [293            [1, 2],294            [1, 3],295            [1, 4],296            [1, 5],297            [1, 6],298            [1, 7],299            [1, 8],300            [2, 4],301            [2, 3],302            [2, 2],303            [2, 1],304            [3, 1],305            [3, 2],306            [4, 1],307            [4, 2],308            [5, 1],309            [6, 1],310            [7, 1],311            [8, 1],312        ],313    ):314        return self.__call__(315            images,316            max_image_size=max_image_size,317            min_image_size=min_image_size,318            return_tensors=return_tensors,319            split_image=split_image,320            split_ratio=split_ratio,321        )322