CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
image_processing_vilt.py493 linesDownload Raw Back to vilt
1# coding=utf-82# Copyright 2022 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 Vilt."""16 17from collections.abc import Iterable18from typing import Any, Optional, Union19 20import numpy as np21 22from ...image_processing_utils import BaseImageProcessor, BatchFeature, get_size_dict23from ...image_transforms import PaddingMode, pad, resize, to_channel_dimension_format24from ...image_utils import (25    IMAGENET_STANDARD_MEAN,26    IMAGENET_STANDARD_STD,27    ChannelDimension,28    ImageInput,29    PILImageResampling,30    get_image_size,31    infer_channel_dimension_format,32    is_scaled_image,33    make_flat_list_of_images,34    to_numpy_array,35    valid_images,36    validate_preprocess_arguments,37)38from ...utils import TensorType, filter_out_non_signature_kwargs, is_vision_available, logging39from ...utils.import_utils import requires40 41 42if is_vision_available():43    import PIL44 45 46logger = logging.get_logger(__name__)47 48 49def max_across_indices(values: Iterable[Any]) -> list[Any]:50    """51    Return the maximum value across all indices of an iterable of values.52    """53    return [max(values_i) for values_i in zip(*values)]54 55 56def make_pixel_mask(57    image: np.ndarray, output_size: tuple[int, int], input_data_format: Optional[Union[str, ChannelDimension]] = None58) -> np.ndarray:59    """60    Make a pixel mask for the image, where 1 indicates a valid pixel and 0 indicates padding.61 62    Args:63        image (`np.ndarray`):64            Image to make the pixel mask for.65        output_size (`tuple[int, int]`):66            Output size of the mask.67    """68    input_height, input_width = get_image_size(image, channel_dim=input_data_format)69    mask = np.zeros(output_size, dtype=np.int64)70    mask[:input_height, :input_width] = 171    return mask72 73 74def get_max_height_width(75    images: list[np.ndarray], input_data_format: Optional[Union[str, ChannelDimension]] = None76) -> list[int]:77    """78    Get the maximum height and width across all images in a batch.79    """80    if input_data_format is None:81        input_data_format = infer_channel_dimension_format(images[0])82 83    if input_data_format == ChannelDimension.FIRST:84        _, max_height, max_width = max_across_indices([img.shape for img in images])85    elif input_data_format == ChannelDimension.LAST:86        max_height, max_width, _ = max_across_indices([img.shape for img in images])87    else:88        raise ValueError(f"Invalid channel dimension format: {input_data_format}")89    return (max_height, max_width)90 91 92def get_resize_output_image_size(93    input_image: np.ndarray,94    shorter: int = 800,95    longer: int = 1333,96    size_divisor: int = 32,97    input_data_format: Optional[Union[str, ChannelDimension]] = None,98) -> tuple[int, int]:99    input_height, input_width = get_image_size(input_image, input_data_format)100    min_size, max_size = shorter, longer101 102    scale = min_size / min(input_height, input_width)103 104    if input_height < input_width:105        new_height = min_size106        new_width = scale * input_width107    else:108        new_height = scale * input_height109        new_width = min_size110 111    if max(new_height, new_width) > max_size:112        scale = max_size / max(new_height, new_width)113        new_height = scale * new_height114        new_width = scale * new_width115 116    new_height, new_width = int(new_height + 0.5), int(new_width + 0.5)117    new_height = new_height // size_divisor * size_divisor118    new_width = new_width // size_divisor * size_divisor119 120    return new_height, new_width121 122 123@requires(backends=("vision",))124class ViltImageProcessor(BaseImageProcessor):125    r"""126    Constructs a ViLT image processor.127 128    Args:129        do_resize (`bool`, *optional*, defaults to `True`):130            Whether to resize the image's (height, width) dimensions to the specified `size`. Can be overridden by the131            `do_resize` parameter in the `preprocess` method.132        size (`dict[str, int]` *optional*, defaults to `{"shortest_edge": 384}`):133            Resize the shorter side of the input to `size["shortest_edge"]`. The longer side will be limited to under134            `int((1333 / 800) * size["shortest_edge"])` while preserving the aspect ratio. Only has an effect if135            `do_resize` is set to `True`. Can be overridden by the `size` parameter in the `preprocess` method.136        size_divisor (`int`, *optional*, defaults to 32):137            The size by which to make sure both the height and width can be divided. Only has an effect if `do_resize`138            is set to `True`. Can be overridden by the `size_divisor` parameter in the `preprocess` method.139        resample (`PILImageResampling`, *optional*, defaults to `Resampling.BICUBIC`):140            Resampling filter to use if resizing the image. Only has an effect if `do_resize` is set to `True`. Can be141            overridden by the `resample` parameter in the `preprocess` method.142        do_rescale (`bool`, *optional*, defaults to `True`):143            Wwhether to rescale the image by the specified scale `rescale_factor`. Can be overridden by the144            `do_rescale` parameter in the `preprocess` method.145        rescale_factor (`int` or `float`, *optional*, defaults to `1/255`):146            Scale factor to use if rescaling the image. Only has an effect if `do_rescale` is set to `True`. Can be147            overridden by the `rescale_factor` parameter in the `preprocess` method.148        do_normalize (`bool`, *optional*, defaults to `True`):149            Whether to normalize the image. Can be overridden by the `do_normalize` parameter in the `preprocess`150            method. Can be overridden by the `do_normalize` parameter in the `preprocess` method.151        image_mean (`float` or `list[float]`, *optional*, defaults to `IMAGENET_STANDARD_MEAN`):152            Mean to use if normalizing the image. This is a float or list of floats the length of the number of153            channels in the image. Can be overridden by the `image_mean` parameter in the `preprocess` method. Can be154            overridden by the `image_mean` parameter in the `preprocess` method.155        image_std (`float` or `list[float]`, *optional*, defaults to `IMAGENET_STANDARD_STD`):156            Standard deviation to use if normalizing the image. This is a float or list of floats the length of the157            number of channels in the image. Can be overridden by the `image_std` parameter in the `preprocess` method.158            Can be overridden by the `image_std` parameter in the `preprocess` method.159        do_pad (`bool`, *optional*, defaults to `True`):160            Whether to pad the image to the `(max_height, max_width)` of the images in the batch. Can be overridden by161            the `do_pad` parameter in the `preprocess` method.162    """163 164    model_input_names = ["pixel_values"]165 166    def __init__(167        self,168        do_resize: bool = True,169        size: Optional[dict[str, int]] = None,170        size_divisor: int = 32,171        resample: PILImageResampling = PILImageResampling.BICUBIC,172        do_rescale: bool = True,173        rescale_factor: Union[int, float] = 1 / 255,174        do_normalize: bool = True,175        image_mean: Optional[Union[float, list[float]]] = None,176        image_std: Optional[Union[float, list[float]]] = None,177        do_pad: bool = True,178        **kwargs,179    ) -> None:180        if "pad_and_return_pixel_mask" in kwargs:181            do_pad = kwargs.pop("pad_and_return_pixel_mask")182 183        super().__init__(**kwargs)184        size = size if size is not None else {"shortest_edge": 384}185        size = get_size_dict(size, default_to_square=False)186 187        self.do_resize = do_resize188        self.size = size189        self.size_divisor = size_divisor190        self.resample = resample191        self.do_rescale = do_rescale192        self.rescale_factor = rescale_factor193        self.do_normalize = do_normalize194        self.image_mean = image_mean if image_mean is not None else IMAGENET_STANDARD_MEAN195        self.image_std = image_std if image_std is not None else IMAGENET_STANDARD_STD196        self.do_pad = do_pad197 198    @classmethod199    def from_dict(cls, image_processor_dict: dict[str, Any], **kwargs):200        """201        Overrides the `from_dict` method from the base class to make sure `pad_and_return_pixel_mask` is updated if image processor202        is created using from_dict and kwargs e.g. `ViltImageProcessor.from_pretrained(checkpoint,203        pad_and_return_pixel_mask=False)`204        """205        image_processor_dict = image_processor_dict.copy()206        if "pad_and_return_pixel_mask" in kwargs:207            image_processor_dict["pad_and_return_pixel_mask"] = kwargs.pop("pad_and_return_pixel_mask")208        return super().from_dict(image_processor_dict, **kwargs)209 210    def resize(211        self,212        image: np.ndarray,213        size: dict[str, int],214        size_divisor: int = 32,215        resample: PILImageResampling = PILImageResampling.BICUBIC,216        data_format: Optional[Union[str, ChannelDimension]] = None,217        input_data_format: Optional[Union[str, ChannelDimension]] = None,218        **kwargs,219    ) -> np.ndarray:220        """221        Resize an image.222 223        Resizes the shorter side of the image to `size["shortest_edge"]` while preserving the aspect ratio. If the224        longer side is larger than the max size `(int(`size["shortest_edge"]` * 1333 / 800))`, the longer side is then225        resized to the max size while preserving the aspect ratio.226 227        Args:228            image (`np.ndarray`):229                Image to resize.230            size (`dict[str, int]`):231                Controls the size of the output image. Should be of the form `{"shortest_edge": int}`.232            size_divisor (`int`, *optional*, defaults to 32):233                The image is resized to a size that is a multiple of this value.234            resample (`PILImageResampling` filter, *optional*, defaults to `PILImageResampling.BICUBIC`):235                Resampling filter to use when resiizing the image.236            data_format (`str` or `ChannelDimension`, *optional*):237                The channel dimension format of the image. If not provided, it will be the same as the input image.238            input_data_format (`str` or `ChannelDimension`, *optional*):239                The channel dimension format of the input image. If not provided, it will be inferred.240        """241        size = get_size_dict(size, default_to_square=False)242        if "shortest_edge" not in size:243            raise ValueError(f"The `size` dictionary must contain the key `shortest_edge`. Got {size.keys()}")244        shorter = size["shortest_edge"]245        longer = int(1333 / 800 * shorter)246        output_size = get_resize_output_image_size(247            image, shorter=shorter, longer=longer, size_divisor=size_divisor, input_data_format=input_data_format248        )249        return resize(250            image,251            size=output_size,252            resample=resample,253            data_format=data_format,254            input_data_format=input_data_format,255            **kwargs,256        )257 258    def _pad_image(259        self,260        image: np.ndarray,261        output_size: tuple[int, int],262        constant_values: Union[float, Iterable[float]] = 0,263        data_format: Optional[ChannelDimension] = None,264        input_data_format: Optional[Union[str, ChannelDimension]] = None,265    ) -> np.ndarray:266        """267        Pad an image with zeros to the given size.268        """269        input_height, input_width = get_image_size(image, channel_dim=input_data_format)270        output_height, output_width = output_size271 272        pad_bottom = output_height - input_height273        pad_right = output_width - input_width274        padding = ((0, pad_bottom), (0, pad_right))275        padded_image = pad(276            image,277            padding,278            mode=PaddingMode.CONSTANT,279            constant_values=constant_values,280            data_format=data_format,281            input_data_format=input_data_format,282        )283        return padded_image284 285    def pad(286        self,287        images: list[np.ndarray],288        constant_values: Union[float, Iterable[float]] = 0,289        return_pixel_mask: bool = True,290        return_tensors: Optional[Union[str, TensorType]] = None,291        data_format: Optional[ChannelDimension] = None,292        input_data_format: Optional[Union[str, ChannelDimension]] = None,293    ) -> BatchFeature:294        """295        Pads a batch of images to the bottom and right of the image with zeros to the size of largest height and width296        in the batch and optionally returns their corresponding pixel mask.297 298        Args:299            image (`np.ndarray`):300                Image to pad.301            constant_values (`float` or `Iterable[float]`, *optional*):302                The value to use for the padding if `mode` is `"constant"`.303            return_pixel_mask (`bool`, *optional*, defaults to `True`):304                Whether to return a pixel mask.305            return_tensors (`str` or `TensorType`, *optional*):306                The type of tensors to return. Can be one of:307                    - Unset: Return a list of `np.ndarray`.308                    - `TensorType.TENSORFLOW` or `'tf'`: Return a batch of type `tf.Tensor`.309                    - `TensorType.PYTORCH` or `'pt'`: Return a batch of type `torch.Tensor`.310                    - `TensorType.NUMPY` or `'np'`: Return a batch of type `np.ndarray`.311                    - `TensorType.JAX` or `'jax'`: Return a batch of type `jax.numpy.ndarray`.312            data_format (`str` or `ChannelDimension`, *optional*):313                The channel dimension format of the image. If not provided, it will be the same as the input image.314            input_data_format (`ChannelDimension` or `str`, *optional*):315                The channel dimension format of the input image. If not provided, it will be inferred.316        """317        pad_size = get_max_height_width(images, input_data_format=input_data_format)318 319        padded_images = [320            self._pad_image(321                image,322                pad_size,323                constant_values=constant_values,324                data_format=data_format,325                input_data_format=input_data_format,326            )327            for image in images328        ]329        data = {"pixel_values": padded_images}330 331        if return_pixel_mask:332            masks = [333                make_pixel_mask(image=image, output_size=pad_size, input_data_format=input_data_format)334                for image in images335            ]336            data["pixel_mask"] = masks337 338        return BatchFeature(data=data, tensor_type=return_tensors)339 340    @filter_out_non_signature_kwargs()341    def preprocess(342        self,343        images: ImageInput,344        do_resize: Optional[bool] = None,345        size: Optional[dict[str, int]] = None,346        size_divisor: Optional[int] = None,347        resample: Optional[PILImageResampling] = None,348        do_rescale: Optional[bool] = None,349        rescale_factor: Optional[float] = None,350        do_normalize: Optional[bool] = None,351        image_mean: Optional[Union[float, list[float]]] = None,352        image_std: Optional[Union[float, list[float]]] = None,353        do_pad: Optional[bool] = None,354        return_tensors: Optional[Union[str, TensorType]] = None,355        data_format: ChannelDimension = ChannelDimension.FIRST,356        input_data_format: Optional[Union[str, ChannelDimension]] = None,357    ) -> PIL.Image.Image:358        """359        Preprocess an image or batch of images.360 361        Args:362            images (`ImageInput`):363                Image to preprocess. Expects a single or batch of images with pixel values ranging from 0 to 255. If364                passing in images with pixel values between 0 and 1, set `do_rescale=False`.365            do_resize (`bool`, *optional*, defaults to `self.do_resize`):366                Whether to resize the image.367            size (`dict[str, int]`, *optional*, defaults to `self.size`):368                Controls the size of the image after `resize`. The shortest edge of the image is resized to369                `size["shortest_edge"]` whilst preserving the aspect ratio. If the longest edge of this resized image370                is > `int(size["shortest_edge"] * (1333 / 800))`, then the image is resized again to make the longest371                edge equal to `int(size["shortest_edge"] * (1333 / 800))`.372            size_divisor (`int`, *optional*, defaults to `self.size_divisor`):373                The image is resized to a size that is a multiple of this value.374            resample (`PILImageResampling`, *optional*, defaults to `self.resample`):375                Resampling filter to use if resizing the image. Only has an effect if `do_resize` is set to `True`.376            do_rescale (`bool`, *optional*, defaults to `self.do_rescale`):377                Whether to rescale the image values between [0 - 1].378            rescale_factor (`float`, *optional*, defaults to `self.rescale_factor`):379                Rescale factor to rescale the image by if `do_rescale` is set to `True`.380            do_normalize (`bool`, *optional*, defaults to `self.do_normalize`):381                Whether to normalize the image.382            image_mean (`float` or `list[float]`, *optional*, defaults to `self.image_mean`):383                Image mean to normalize the image by if `do_normalize` is set to `True`.384            image_std (`float` or `list[float]`, *optional*, defaults to `self.image_std`):385                Image standard deviation to normalize the image by if `do_normalize` is set to `True`.386            do_pad (`bool`, *optional*, defaults to `self.do_pad`):387                Whether to pad the image to the (max_height, max_width) in the batch. If `True`, a pixel mask is also388                created and returned.389            return_tensors (`str` or `TensorType`, *optional*):390                The type of tensors to return. Can be one of:391                    - Unset: Return a list of `np.ndarray`.392                    - `TensorType.TENSORFLOW` or `'tf'`: Return a batch of type `tf.Tensor`.393                    - `TensorType.PYTORCH` or `'pt'`: Return a batch of type `torch.Tensor`.394                    - `TensorType.NUMPY` or `'np'`: Return a batch of type `np.ndarray`.395                    - `TensorType.JAX` or `'jax'`: Return a batch of type `jax.numpy.ndarray`.396            data_format (`ChannelDimension` or `str`, *optional*, defaults to `ChannelDimension.FIRST`):397                The channel dimension format for the output image. Can be one of:398                    - `ChannelDimension.FIRST`: image in (num_channels, height, width) format.399                    - `ChannelDimension.LAST`: image in (height, width, num_channels) format.400            input_data_format (`ChannelDimension` or `str`, *optional*):401                The channel dimension format for the input image. If unset, the channel dimension format is inferred402                from the input image. Can be one of:403                - `"channels_first"` or `ChannelDimension.FIRST`: image in (num_channels, height, width) format.404                - `"channels_last"` or `ChannelDimension.LAST`: image in (height, width, num_channels) format.405                - `"none"` or `ChannelDimension.NONE`: image in (height, width) format.406        """407        do_resize = do_resize if do_resize is not None else self.do_resize408        size_divisor = size_divisor if size_divisor is not None else self.size_divisor409        resample = resample if resample is not None else self.resample410        do_rescale = do_rescale if do_rescale is not None else self.do_rescale411        rescale_factor = rescale_factor if rescale_factor is not None else self.rescale_factor412        do_normalize = do_normalize if do_normalize is not None else self.do_normalize413        image_mean = image_mean if image_mean is not None else self.image_mean414        image_std = image_std if image_std is not None else self.image_std415        do_pad = do_pad if do_pad is not None else self.do_pad416 417        size = size if size is not None else self.size418        size = get_size_dict(size, default_to_square=False)419 420        images = make_flat_list_of_images(images)421 422        if not valid_images(images):423            raise ValueError(424                "Invalid image type. Must be of type PIL.Image.Image, numpy.ndarray, "425                "torch.Tensor, tf.Tensor or jax.ndarray."426            )427 428        # Here the pad() method does not require any additional argument as it takes the maximum of (height, width).429        # Hence, it does not need to be passed to a validate_preprocess_arguments() method.430        validate_preprocess_arguments(431            do_rescale=do_rescale,432            rescale_factor=rescale_factor,433            do_normalize=do_normalize,434            image_mean=image_mean,435            image_std=image_std,436            do_resize=do_resize,437            size=size,438            resample=resample,439        )440 441        # All transformations expect numpy arrays.442        images = [to_numpy_array(image) for image in images]443 444        if do_rescale and is_scaled_image(images[0]):445            logger.warning_once(446                "It looks like you are trying to rescale already rescaled images. If the input"447                " images have pixel values between 0 and 1, set `do_rescale=False` to avoid rescaling them again."448            )449 450        if input_data_format is None:451            # We assume that all images have the same channel dimension format.452            input_data_format = infer_channel_dimension_format(images[0])453 454        if do_resize:455            images = [456                self.resize(457                    image=image,458                    size=size,459                    size_divisor=size_divisor,460                    resample=resample,461                    input_data_format=input_data_format,462                )463                for image in images464            ]465 466        if do_rescale:467            images = [468                self.rescale(image=image, scale=rescale_factor, input_data_format=input_data_format)469                for image in images470            ]471 472        if do_normalize:473            images = [474                self.normalize(image=image, mean=image_mean, std=image_std, input_data_format=input_data_format)475                for image in images476            ]477 478        images = [479            to_channel_dimension_format(image, data_format, input_channel_dim=input_data_format) for image in images480        ]481 482        if do_pad:483            encoded_outputs = self.pad(484                images, return_pixel_mask=True, return_tensors=return_tensors, input_data_format=data_format485            )486        else:487            encoded_outputs = BatchFeature(data={"pixel_values": images}, tensor_type=return_tensors)488 489        return encoded_outputs490 491 492__all__ = ["ViltImageProcessor"]493 
Aluode/PerceptionLabPortable · CoolFace