Aluode/PerceptionLabPortable
0
1# Copyright 2024 The HuggingFace Inc. team.2#3# Licensed under the Apache License, Version 2.0 (the "License");4# you may not use this file except in compliance with the License.5# You may obtain a copy of the License at6#7# http://www.apache.org/licenses/LICENSE-2.08#9# Unless required by applicable law or agreed to in writing, software10# distributed under the License is distributed on an "AS IS" BASIS,11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12# See the License for the specific language governing permissions and13# limitations under the License.14 15from collections.abc import Iterable16from copy import deepcopy17from functools import lru_cache, partial18from typing import Any, Optional, TypedDict, Union19 20import numpy as np21 22from .image_processing_utils import BaseImageProcessor, BatchFeature, get_size_dict23from .image_transforms import (24 convert_to_rgb,25 get_resize_output_image_size,26 get_size_with_aspect_ratio,27 group_images_by_shape,28 reorder_images,29)30from .image_utils import (31 ChannelDimension,32 ImageInput,33 ImageType,34 SizeDict,35 get_image_size,36 get_image_size_for_max_height_width,37 get_image_type,38 infer_channel_dimension_format,39 make_flat_list_of_images,40 validate_kwargs,41 validate_preprocess_arguments,42)43from .processing_utils import Unpack44from .utils import (45 TensorType,46 auto_docstring,47 is_torch_available,48 is_torchvision_available,49 is_vision_available,50 logging,51)52from .utils.import_utils import is_rocm_platform53 54 55if is_vision_available():56 from .image_utils import PILImageResampling57 58if is_torch_available():59 import torch60 61if is_torchvision_available():62 from torchvision.transforms.v2 import functional as F63 64 from .image_utils import pil_torch_interpolation_mapping65 66else:67 pil_torch_interpolation_mapping = None68 69 70logger = logging.get_logger(__name__)71 72 73@lru_cache(maxsize=10)74def validate_fast_preprocess_arguments(75 do_rescale: Optional[bool] = None,76 rescale_factor: Optional[float] = None,77 do_normalize: Optional[bool] = None,78 image_mean: Optional[Union[float, list[float]]] = None,79 image_std: Optional[Union[float, list[float]]] = None,80 do_center_crop: Optional[bool] = None,81 crop_size: Optional[SizeDict] = None,82 do_resize: Optional[bool] = None,83 size: Optional[SizeDict] = None,84 interpolation: Optional["F.InterpolationMode"] = None,85 return_tensors: Optional[Union[str, TensorType]] = None,86 data_format: ChannelDimension = ChannelDimension.FIRST,87):88 """89 Checks validity of typically used arguments in an `ImageProcessorFast` `preprocess` method.90 Raises `ValueError` if arguments incompatibility is caught.91 """92 validate_preprocess_arguments(93 do_rescale=do_rescale,94 rescale_factor=rescale_factor,95 do_normalize=do_normalize,96 image_mean=image_mean,97 image_std=image_std,98 do_center_crop=do_center_crop,99 crop_size=crop_size,100 do_resize=do_resize,101 size=size,102 interpolation=interpolation,103 )104 # Extra checks for ImageProcessorFast105 if return_tensors is not None and return_tensors != "pt":106 raise ValueError("Only returning PyTorch tensors is currently supported.")107 108 if data_format != ChannelDimension.FIRST:109 raise ValueError("Only channel first data format is currently supported.")110 111 112def safe_squeeze(tensor: "torch.Tensor", axis: Optional[int] = None) -> "torch.Tensor":113 """114 Squeezes a tensor, but only if the axis specified has dim 1.115 """116 if axis is None:117 return tensor.squeeze()118 119 try:120 return tensor.squeeze(axis=axis)121 except ValueError:122 return tensor123 124 125def max_across_indices(values: Iterable[Any]) -> list[Any]:126 """127 Return the maximum value across all indices of an iterable of values.128 """129 return [max(values_i) for values_i in zip(*values)]130 131 132def get_max_height_width(images: list["torch.Tensor"]) -> tuple[int, ...]:133 """134 Get the maximum height and width across all images in a batch.135 """136 137 _, max_height, max_width = max_across_indices([img.shape for img in images])138 139 return (max_height, max_width)140 141 142def divide_to_patches(143 image: Union[np.ndarray, "torch.Tensor"], patch_size: int144) -> list[Union[np.ndarray, "torch.Tensor"]]:145 """146 Divides an image into patches of a specified size.147 148 Args:149 image (`Union[np.array, "torch.Tensor"]`):150 The input image.151 patch_size (`int`):152 The size of each patch.153 Returns:154 list: A list of Union[np.array, "torch.Tensor"] representing the patches.155 """156 patches = []157 height, width = get_image_size(image, channel_dim=ChannelDimension.FIRST)158 for i in range(0, height, patch_size):159 for j in range(0, width, patch_size):160 patch = image[:, i : i + patch_size, j : j + patch_size]161 patches.append(patch)162 163 return patches164 165 166class DefaultFastImageProcessorKwargs(TypedDict, total=False):167 do_resize: Optional[bool]168 size: Optional[dict[str, int]]169 default_to_square: Optional[bool]170 resample: Optional[Union["PILImageResampling", "F.InterpolationMode"]]171 do_center_crop: Optional[bool]172 crop_size: Optional[dict[str, int]]173 do_rescale: Optional[bool]174 rescale_factor: Optional[Union[int, float]]175 do_normalize: Optional[bool]176 image_mean: Optional[Union[float, list[float]]]177 image_std: Optional[Union[float, list[float]]]178 do_pad: Optional[bool]179 pad_size: Optional[dict[str, int]]180 do_convert_rgb: Optional[bool]181 return_tensors: Optional[Union[str, TensorType]]182 data_format: Optional[ChannelDimension]183 input_data_format: Optional[Union[str, ChannelDimension]]184 device: Optional["torch.device"]185 disable_grouping: Optional[bool]186 187 188@auto_docstring189class BaseImageProcessorFast(BaseImageProcessor):190 resample = None191 image_mean = None192 image_std = None193 size = None194 default_to_square = True195 crop_size = None196 do_resize = None197 do_center_crop = None198 do_pad = None199 pad_size = None200 do_rescale = None201 rescale_factor = 1 / 255202 do_normalize = None203 do_convert_rgb = None204 return_tensors = None205 data_format = ChannelDimension.FIRST206 input_data_format = None207 device = None208 model_input_names = ["pixel_values"]209 valid_kwargs = DefaultFastImageProcessorKwargs210 unused_kwargs = None211 212 def __init__(self, **kwargs: Unpack[DefaultFastImageProcessorKwargs]):213 super().__init__(**kwargs)214 kwargs = self.filter_out_unused_kwargs(kwargs)215 size = kwargs.pop("size", self.size)216 self.size = (217 get_size_dict(size=size, default_to_square=kwargs.pop("default_to_square", self.default_to_square))218 if size is not None219 else None220 )221 crop_size = kwargs.pop("crop_size", self.crop_size)222 self.crop_size = get_size_dict(crop_size, param_name="crop_size") if crop_size is not None else None223 pad_size = kwargs.pop("pad_size", self.pad_size)224 self.pad_size = get_size_dict(size=pad_size, param_name="pad_size") if pad_size is not None else None225 226 for key in self.valid_kwargs.__annotations__:227 kwarg = kwargs.pop(key, None)228 if kwarg is not None:229 setattr(self, key, kwarg)230 else:231 setattr(self, key, deepcopy(getattr(self, key, None)))232 233 # get valid kwargs names234 self._valid_kwargs_names = list(self.valid_kwargs.__annotations__.keys())235 236 @property237 def is_fast(self) -> bool:238 """239 `bool`: Whether or not this image processor is a fast processor (backed by PyTorch and TorchVision).240 """241 return True242 243 def pad(244 self,245 images: "torch.Tensor",246 pad_size: SizeDict = None,247 fill_value: Optional[int] = 0,248 padding_mode: Optional[str] = "constant",249 return_mask: bool = False,250 disable_grouping: Optional[bool] = False,251 **kwargs,252 ) -> "torch.Tensor":253 """254 Pads images to `(pad_size["height"], pad_size["width"])` or to the largest size in the batch.255 256 Args:257 images (`torch.Tensor`):258 Images to pad.259 pad_size (`SizeDict`, *optional*):260 Dictionary in the format `{"height": int, "width": int}` specifying the size of the output image.261 fill_value (`int`, *optional*, defaults to `0`):262 The constant value used to fill the padded area.263 padding_mode (`str`, *optional*, defaults to "constant"):264 The padding mode to use. Can be any of the modes supported by265 `torch.nn.functional.pad` (e.g. constant, reflection, replication).266 return_mask (`bool`, *optional*, defaults to `False`):267 Whether to return a pixel mask to denote padded regions.268 disable_grouping (`bool`, *optional*, defaults to `False`):269 Whether to disable grouping of images by size.270 271 Returns:272 `torch.Tensor`: The resized image.273 """274 if pad_size is not None:275 if not (pad_size.height and pad_size.width):276 raise ValueError(f"Pad size must contain 'height' and 'width' keys only. Got pad_size={pad_size}.")277 pad_size = (pad_size.height, pad_size.width)278 else:279 pad_size = get_max_height_width(images)280 281 grouped_images, grouped_images_index = group_images_by_shape(images, disable_grouping=disable_grouping)282 processed_images_grouped = {}283 processed_masks_grouped = {}284 for shape, stacked_images in grouped_images.items():285 image_size = stacked_images.shape[-2:]286 padding_height = pad_size[0] - image_size[0]287 padding_width = pad_size[1] - image_size[1]288 if padding_height < 0 or padding_width < 0:289 raise ValueError(290 f"Padding dimensions are negative. Please make sure that the `pad_size` is larger than the "291 f"image size. Got pad_size={pad_size}, image_size={image_size}."292 )293 if image_size != pad_size:294 padding = (0, 0, padding_width, padding_height)295 stacked_images = F.pad(stacked_images, padding, fill=fill_value, padding_mode=padding_mode)296 processed_images_grouped[shape] = stacked_images297 298 if return_mask:299 # keep only one from the channel dimension in pixel mask300 stacked_masks = torch.zeros_like(stacked_images, dtype=torch.int64)[..., 0, :, :]301 stacked_masks[..., : image_size[0], : image_size[1]] = 1302 processed_masks_grouped[shape] = stacked_masks303 304 processed_images = reorder_images(processed_images_grouped, grouped_images_index)305 if return_mask:306 processed_masks = reorder_images(processed_masks_grouped, grouped_images_index)307 return processed_images, processed_masks308 309 return processed_images310 311 def resize(312 self,313 image: "torch.Tensor",314 size: SizeDict,315 interpolation: Optional["F.InterpolationMode"] = None,316 antialias: bool = True,317 **kwargs,318 ) -> "torch.Tensor":319 """320 Resize an image to `(size["height"], size["width"])`.321 322 Args:323 image (`torch.Tensor`):324 Image to resize.325 size (`SizeDict`):326 Dictionary in the format `{"height": int, "width": int}` specifying the size of the output image.327 interpolation (`InterpolationMode`, *optional*, defaults to `InterpolationMode.BILINEAR`):328 `InterpolationMode` filter to use when resizing the image e.g. `InterpolationMode.BICUBIC`.329 330 Returns:331 `torch.Tensor`: The resized image.332 """333 interpolation = interpolation if interpolation is not None else F.InterpolationMode.BILINEAR334 if size.shortest_edge and size.longest_edge:335 # Resize the image so that the shortest edge or the longest edge is of the given size336 # while maintaining the aspect ratio of the original image.337 new_size = get_size_with_aspect_ratio(338 image.size()[-2:],339 size.shortest_edge,340 size.longest_edge,341 )342 elif size.shortest_edge:343 new_size = get_resize_output_image_size(344 image,345 size=size.shortest_edge,346 default_to_square=False,347 input_data_format=ChannelDimension.FIRST,348 )349 elif size.max_height and size.max_width:350 new_size = get_image_size_for_max_height_width(image.size()[-2:], size.max_height, size.max_width)351 elif size.height and size.width:352 new_size = (size.height, size.width)353 else:354 raise ValueError(355 "Size must contain 'height' and 'width' keys, or 'max_height' and 'max_width', or 'shortest_edge' key. Got"356 f" {size}."357 )358 # This is a workaround to avoid a bug in torch.compile when dealing with uint8 on AMD MI3XX GPUs359 # Tracked in PyTorch issue: https://github.com/pytorch/pytorch/issues/155209360 # TODO: remove this once the bug is fixed (detected with torch==2.7.0+git1fee196, torchvision==0.22.0+9eb57cd)361 if torch.compiler.is_compiling() and is_rocm_platform():362 return self.compile_friendly_resize(image, new_size, interpolation, antialias)363 return F.resize(image, new_size, interpolation=interpolation, antialias=antialias)364 365 @staticmethod366 def compile_friendly_resize(367 image: "torch.Tensor",368 new_size: tuple[int, int],369 interpolation: Optional["F.InterpolationMode"] = None,370 antialias: bool = True,371 ) -> "torch.Tensor":372 """373 A wrapper around `F.resize` so that it is compatible with torch.compile when the image is a uint8 tensor.374 """375 if image.dtype == torch.uint8:376 # 256 is used on purpose instead of 255 to avoid numerical differences377 # see https://github.com/huggingface/transformers/pull/38540#discussion_r2127165652378 image = image.float() / 256379 image = F.resize(image, new_size, interpolation=interpolation, antialias=antialias)380 image = image * 256381 # torch.where is used on purpose instead of torch.clamp to avoid bug in torch.compile382 # see https://github.com/huggingface/transformers/pull/38540#discussion_r2126888471383 image = torch.where(image > 255, 255, image)384 image = torch.where(image < 0, 0, image)385 image = image.round().to(torch.uint8)386 else:387 image = F.resize(image, new_size, interpolation=interpolation, antialias=antialias)388 return image389 390 def rescale(391 self,392 image: "torch.Tensor",393 scale: float,394 **kwargs,395 ) -> "torch.Tensor":396 """397 Rescale an image by a scale factor. image = image * scale.398 399 Args:400 image (`torch.Tensor`):401 Image to rescale.402 scale (`float`):403 The scaling factor to rescale pixel values by.404 405 Returns:406 `torch.Tensor`: The rescaled image.407 """408 return image * scale409 410 def normalize(411 self,412 image: "torch.Tensor",413 mean: Union[float, Iterable[float]],414 std: Union[float, Iterable[float]],415 **kwargs,416 ) -> "torch.Tensor":417 """418 Normalize an image. image = (image - image_mean) / image_std.419 420 Args:421 image (`torch.Tensor`):422 Image to normalize.423 mean (`torch.Tensor`, `float` or `Iterable[float]`):424 Image mean to use for normalization.425 std (`torch.Tensor`, `float` or `Iterable[float]`):426 Image standard deviation to use for normalization.427 428 Returns:429 `torch.Tensor`: The normalized image.430 """431 return F.normalize(image, mean, std)432 433 @lru_cache(maxsize=10)434 def _fuse_mean_std_and_rescale_factor(435 self,436 do_normalize: Optional[bool] = None,437 image_mean: Optional[Union[float, list[float]]] = None,438 image_std: Optional[Union[float, list[float]]] = None,439 do_rescale: Optional[bool] = None,440 rescale_factor: Optional[float] = None,441 device: Optional["torch.device"] = None,442 ) -> tuple:443 if do_rescale and do_normalize:444 # Fused rescale and normalize445 image_mean = torch.tensor(image_mean, device=device) * (1.0 / rescale_factor)446 image_std = torch.tensor(image_std, device=device) * (1.0 / rescale_factor)447 do_rescale = False448 return image_mean, image_std, do_rescale449 450 def rescale_and_normalize(451 self,452 images: "torch.Tensor",453 do_rescale: bool,454 rescale_factor: float,455 do_normalize: bool,456 image_mean: Union[float, list[float]],457 image_std: Union[float, list[float]],458 ) -> "torch.Tensor":459 """460 Rescale and normalize images.461 """462 image_mean, image_std, do_rescale = self._fuse_mean_std_and_rescale_factor(463 do_normalize=do_normalize,464 image_mean=image_mean,465 image_std=image_std,466 do_rescale=do_rescale,467 rescale_factor=rescale_factor,468 device=images.device,469 )470 # if/elif as we use fused rescale and normalize if both are set to True471 if do_normalize:472 images = self.normalize(images.to(dtype=torch.float32), image_mean, image_std)473 elif do_rescale:474 images = self.rescale(images, rescale_factor)475 476 return images477 478 def center_crop(479 self,480 image: "torch.Tensor",481 size: SizeDict,482 **kwargs,483 ) -> "torch.Tensor":484 """485 Note: override torchvision's center_crop to have the same behavior as the slow processor.486 Center crop an image to `(size["height"], size["width"])`. If the input size is smaller than `crop_size` along487 any edge, the image is padded with 0's and then center cropped.488 489 Args:490 image (`"torch.Tensor"`):491 Image to center crop.492 size (`dict[str, int]`):493 Size of the output image.494 495 Returns:496 `torch.Tensor`: The center cropped image.497 """498 if size.height is None or size.width is None:499 raise ValueError(f"The size dictionary must have keys 'height' and 'width'. Got {size.keys()}")500 image_height, image_width = image.shape[-2:]501 crop_height, crop_width = size.height, size.width502 503 if crop_width > image_width or crop_height > image_height:504 padding_ltrb = [505 (crop_width - image_width) // 2 if crop_width > image_width else 0,506 (crop_height - image_height) // 2 if crop_height > image_height else 0,507 (crop_width - image_width + 1) // 2 if crop_width > image_width else 0,508 (crop_height - image_height + 1) // 2 if crop_height > image_height else 0,509 ]510 image = F.pad(image, padding_ltrb, fill=0) # PIL uses fill value 0511 image_height, image_width = image.shape[-2:]512 if crop_width == image_width and crop_height == image_height:513 return image514 515 crop_top = int((image_height - crop_height) / 2.0)516 crop_left = int((image_width - crop_width) / 2.0)517 return F.crop(image, crop_top, crop_left, crop_height, crop_width)518 519 def convert_to_rgb(520 self,521 image: ImageInput,522 ) -> ImageInput:523 """524 Converts an image to RGB format. Only converts if the image is of type PIL.Image.Image, otherwise returns the image525 as is.526 Args:527 image (ImageInput):528 The image to convert.529 530 Returns:531 ImageInput: The converted image.532 """533 return convert_to_rgb(image)534 535 def filter_out_unused_kwargs(self, kwargs: dict):536 """537 Filter out the unused kwargs from the kwargs dictionary.538 """539 if self.unused_kwargs is None:540 return kwargs541 542 for kwarg_name in self.unused_kwargs:543 if kwarg_name in kwargs:544 logger.warning_once(f"This processor does not use the `{kwarg_name}` parameter. It will be ignored.")545 kwargs.pop(kwarg_name)546 return kwargs547 548 def _prepare_images_structure(549 self,550 images: ImageInput,551 expected_ndims: int = 3,552 ) -> ImageInput:553 """554 Prepare the images structure for processing.555 556 Args:557 images (`ImageInput`):558 The input images to process.559 560 Returns:561 `ImageInput`: The images with a valid nesting.562 """563 # Checks for `str` in case of URL/local path and optionally loads images564 images = self.fetch_images(images)565 return make_flat_list_of_images(images, expected_ndims=expected_ndims)566 567 def _process_image(568 self,569 image: ImageInput,570 do_convert_rgb: Optional[bool] = None,571 input_data_format: Optional[Union[str, ChannelDimension]] = None,572 device: Optional["torch.device"] = None,573 ) -> "torch.Tensor":574 image_type = get_image_type(image)575 if image_type not in [ImageType.PIL, ImageType.TORCH, ImageType.NUMPY]:576 raise ValueError(f"Unsupported input image type {image_type}")577 578 if do_convert_rgb:579 image = self.convert_to_rgb(image)580 581 if image_type == ImageType.PIL:582 image = F.pil_to_tensor(image)583 elif image_type == ImageType.NUMPY:584 # not using F.to_tensor as it doesn't handle (C, H, W) numpy arrays585 image = torch.from_numpy(image).contiguous()586 587 # If the image is 2D, we need to unsqueeze it to add a channel dimension for processing588 if image.ndim == 2:589 image = image.unsqueeze(0)590 591 # Infer the channel dimension format if not provided592 if input_data_format is None:593 input_data_format = infer_channel_dimension_format(image)594 595 if input_data_format == ChannelDimension.LAST:596 # We force the channel dimension to be first for torch tensors as this is what torchvision expects.597 image = image.permute(2, 0, 1).contiguous()598 599 # Now that we have torch tensors, we can move them to the right device600 if device is not None:601 image = image.to(device)602 603 return image604 605 def _prepare_image_like_inputs(606 self,607 images: ImageInput,608 do_convert_rgb: Optional[bool] = None,609 input_data_format: Optional[Union[str, ChannelDimension]] = None,610 device: Optional["torch.device"] = None,611 expected_ndims: int = 3,612 ) -> list["torch.Tensor"]:613 """614 Prepare image-like inputs for processing.615 616 Args:617 images (`ImageInput`):618 The image-like inputs to process.619 do_convert_rgb (`bool`, *optional*):620 Whether to convert the images to RGB.621 input_data_format (`str` or `ChannelDimension`, *optional*):622 The input data format of the images.623 device (`torch.device`, *optional*):624 The device to put the processed images on.625 expected_ndims (`int`, *optional*):626 The expected number of dimensions for the images. (can be 2 for segmentation maps etc.)627 628 Returns:629 List[`torch.Tensor`]: The processed images.630 """631 632 # Get structured images (potentially nested)633 images = self._prepare_images_structure(images, expected_ndims=expected_ndims)634 635 process_image_partial = partial(636 self._process_image, do_convert_rgb=do_convert_rgb, input_data_format=input_data_format, device=device637 )638 639 # Check if we have nested structure, assuming the nesting is consistent640 has_nested_structure = len(images) > 0 and isinstance(images[0], (list, tuple))641 642 if has_nested_structure:643 processed_images = [[process_image_partial(img) for img in nested_list] for nested_list in images]644 else:645 processed_images = [process_image_partial(img) for img in images]646 647 return processed_images648 649 def _further_process_kwargs(650 self,651 size: Optional[SizeDict] = None,652 crop_size: Optional[SizeDict] = None,653 pad_size: Optional[SizeDict] = None,654 default_to_square: Optional[bool] = None,655 image_mean: Optional[Union[float, list[float]]] = None,656 image_std: Optional[Union[float, list[float]]] = None,657 data_format: Optional[ChannelDimension] = None,658 **kwargs,659 ) -> dict:660 """661 Update kwargs that need further processing before being validated662 Can be overridden by subclasses to customize the processing of kwargs.663 """664 if kwargs is None:665 kwargs = {}666 if size is not None:667 size = SizeDict(**get_size_dict(size=size, default_to_square=default_to_square))668 if crop_size is not None:669 crop_size = SizeDict(**get_size_dict(crop_size, param_name="crop_size"))670 if pad_size is not None:671 pad_size = SizeDict(**get_size_dict(size=pad_size, param_name="pad_size"))672 if isinstance(image_mean, list):673 image_mean = tuple(image_mean)674 if isinstance(image_std, list):675 image_std = tuple(image_std)676 if data_format is None:677 data_format = ChannelDimension.FIRST678 679 kwargs["size"] = size680 kwargs["crop_size"] = crop_size681 kwargs["pad_size"] = pad_size682 kwargs["image_mean"] = image_mean683 kwargs["image_std"] = image_std684 kwargs["data_format"] = data_format685 686 # torch resize uses interpolation instead of resample687 # Check if resample is an int before checking if it's an instance of PILImageResampling688 # because if pillow < 9.1.0, resample is an int and PILImageResampling is a module.689 # Checking PILImageResampling will fail with error `TypeError: isinstance() arg 2 must be a type or tuple of types`.690 resample = kwargs.pop("resample")691 kwargs["interpolation"] = (692 pil_torch_interpolation_mapping[resample] if isinstance(resample, (PILImageResampling, int)) else resample693 )694 695 return kwargs696 697 def _validate_preprocess_kwargs(698 self,699 do_rescale: Optional[bool] = None,700 rescale_factor: Optional[float] = None,701 do_normalize: Optional[bool] = None,702 image_mean: Optional[Union[float, tuple[float]]] = None,703 image_std: Optional[Union[float, tuple[float]]] = None,704 do_resize: Optional[bool] = None,705 size: Optional[SizeDict] = None,706 do_center_crop: Optional[bool] = None,707 crop_size: Optional[SizeDict] = None,708 interpolation: Optional["F.InterpolationMode"] = None,709 return_tensors: Optional[Union[str, TensorType]] = None,710 data_format: Optional[ChannelDimension] = None,711 **kwargs,712 ):713 """714 validate the kwargs for the preprocess method.715 """716 validate_fast_preprocess_arguments(717 do_rescale=do_rescale,718 rescale_factor=rescale_factor,719 do_normalize=do_normalize,720 image_mean=image_mean,721 image_std=image_std,722 do_resize=do_resize,723 size=size,724 do_center_crop=do_center_crop,725 crop_size=crop_size,726 interpolation=interpolation,727 return_tensors=return_tensors,728 data_format=data_format,729 )730 731 def __call__(self, images: ImageInput, *args, **kwargs: Unpack[DefaultFastImageProcessorKwargs]) -> BatchFeature:732 return self.preprocess(images, *args, **kwargs)733 734 @auto_docstring735 def preprocess(self, images: ImageInput, *args, **kwargs: Unpack[DefaultFastImageProcessorKwargs]) -> BatchFeature:736 # args are not validated, but their order in the `preprocess` and `_preprocess` signatures must be the same737 validate_kwargs(captured_kwargs=kwargs.keys(), valid_processor_keys=self._valid_kwargs_names)738 # Set default kwargs from self. This ensures that if a kwarg is not provided739 # by the user, it gets its default value from the instance, or is set to None.740 for kwarg_name in self._valid_kwargs_names:741 kwargs.setdefault(kwarg_name, getattr(self, kwarg_name, None))742 743 # Extract parameters that are only used for preparing the input images744 do_convert_rgb = kwargs.pop("do_convert_rgb")745 input_data_format = kwargs.pop("input_data_format")746 device = kwargs.pop("device")747 748 # Update kwargs that need further processing before being validated749 kwargs = self._further_process_kwargs(**kwargs)750 751 # Validate kwargs752 self._validate_preprocess_kwargs(**kwargs)753 754 # Pop kwargs that are not needed in _preprocess755 kwargs.pop("data_format")756 757 return self._preprocess_image_like_inputs(758 images, *args, do_convert_rgb=do_convert_rgb, input_data_format=input_data_format, device=device, **kwargs759 )760 761 def _preprocess_image_like_inputs(762 self,763 images: ImageInput,764 *args,765 do_convert_rgb: bool,766 input_data_format: ChannelDimension,767 device: Optional[Union[str, "torch.device"]] = None,768 **kwargs: Unpack[DefaultFastImageProcessorKwargs],769 ) -> BatchFeature:770 """771 Preprocess image-like inputs.772 To be overridden by subclasses when image-like inputs other than images should be processed.773 It can be used for segmentation maps, depth maps, etc.774 """775 # Prepare input images776 images = self._prepare_image_like_inputs(777 images=images, do_convert_rgb=do_convert_rgb, input_data_format=input_data_format, device=device778 )779 return self._preprocess(images, *args, **kwargs)780 781 def _preprocess(782 self,783 images: list["torch.Tensor"],784 do_resize: bool,785 size: SizeDict,786 interpolation: Optional["F.InterpolationMode"],787 do_center_crop: bool,788 crop_size: SizeDict,789 do_rescale: bool,790 rescale_factor: float,791 do_normalize: bool,792 image_mean: Optional[Union[float, list[float]]],793 image_std: Optional[Union[float, list[float]]],794 do_pad: Optional[bool],795 pad_size: Optional[SizeDict],796 disable_grouping: Optional[bool],797 return_tensors: Optional[Union[str, TensorType]],798 **kwargs,799 ) -> BatchFeature:800 # Group images by size for batched resizing801 grouped_images, grouped_images_index = group_images_by_shape(images, disable_grouping=disable_grouping)802 resized_images_grouped = {}803 for shape, stacked_images in grouped_images.items():804 if do_resize:805 stacked_images = self.resize(image=stacked_images, size=size, interpolation=interpolation)806 resized_images_grouped[shape] = stacked_images807 resized_images = reorder_images(resized_images_grouped, grouped_images_index)808 809 # Group images by size for further processing810 # Needed in case do_resize is False, or resize returns images with different sizes811 grouped_images, grouped_images_index = group_images_by_shape(resized_images, disable_grouping=disable_grouping)812 processed_images_grouped = {}813 for shape, stacked_images in grouped_images.items():814 if do_center_crop:815 stacked_images = self.center_crop(stacked_images, crop_size)816 # Fused rescale and normalize817 stacked_images = self.rescale_and_normalize(818 stacked_images, do_rescale, rescale_factor, do_normalize, image_mean, image_std819 )820 processed_images_grouped[shape] = stacked_images821 processed_images = reorder_images(processed_images_grouped, grouped_images_index)822 823 if do_pad:824 processed_images = self.pad(processed_images, pad_size=pad_size, disable_grouping=disable_grouping)825 826 processed_images = torch.stack(processed_images, dim=0) if return_tensors else processed_images827 return BatchFeature(data={"pixel_values": processed_images}, tensor_type=return_tensors)828 829 def to_dict(self):830 encoder_dict = super().to_dict()831 encoder_dict.pop("_valid_processor_keys", None)832 encoder_dict.pop("_valid_kwargs_names", None)833 return encoder_dict834 