| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- # Copyright 2025 HuggingFace Inc. team. All rights reserved.
- #
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
- # You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- import math
- from collections.abc import Iterable
- from typing import TYPE_CHECKING
- import torch
- from ...image_processing_backends import TorchvisionBackend
- from ...image_processing_base import BatchFeature
- from ...image_transforms import group_images_by_shape, reorder_images
- from ...image_utils import (
- IMAGENET_STANDARD_MEAN,
- IMAGENET_STANDARD_STD,
- PILImageResampling,
- SizeDict,
- )
- from ...processing_utils import ImagesKwargs
- from ...utils import TensorType, auto_docstring, requires_backends
- from ..beit.image_processing_beit import BeitImageProcessor
- if TYPE_CHECKING:
- from ...modeling_outputs import DepthEstimatorOutput
- from torchvision.transforms.v2 import functional as tvF
- def get_resize_output_image_size(
- input_image: "torch.Tensor",
- output_size: int | Iterable[int],
- keep_aspect_ratio: bool,
- multiple: int,
- ) -> SizeDict:
- def constrain_to_multiple_of(val, multiple, min_val=0, max_val=None):
- x = round(val / multiple) * multiple
- if max_val is not None and x > max_val:
- x = math.floor(val / multiple) * multiple
- if x < min_val:
- x = math.ceil(val / multiple) * multiple
- return x
- input_height, input_width = input_image.shape[-2:]
- output_height, output_width = output_size
- # determine new height and width
- scale_height = output_height / input_height
- scale_width = output_width / input_width
- if keep_aspect_ratio:
- # scale as little as possible
- if abs(1 - scale_width) < abs(1 - scale_height):
- # fit width
- scale_height = scale_width
- else:
- # fit height
- scale_width = scale_height
- new_height = constrain_to_multiple_of(scale_height * input_height, multiple=multiple)
- new_width = constrain_to_multiple_of(scale_width * input_width, multiple=multiple)
- return SizeDict(height=new_height, width=new_width)
- class DPTImageProcessorKwargs(ImagesKwargs, total=False):
- r"""
- ensure_multiple_of (`int`, *optional*, defaults to 1):
- If `do_resize` is `True`, the image is resized to a size that is a multiple of this value. Can be overridden
- by `ensure_multiple_of` in `preprocess`.
- keep_aspect_ratio (`bool`, *optional*, defaults to `False`):
- If `True`, the image is resized to the largest possible size such that the aspect ratio is preserved. Can
- be overridden by `keep_aspect_ratio` in `preprocess`.
- do_reduce_labels (`bool`, *optional*, defaults to `self.do_reduce_labels`):
- Whether or not to reduce all label values of segmentation maps by 1. Usually used for datasets where 0
- is used for background, and background itself is not included in all classes of a dataset (e.g.
- ADE20k). The background label will be replaced by 255.
- """
- ensure_multiple_of: int
- size_divisor: int
- keep_aspect_ratio: bool
- do_reduce_labels: bool
- @auto_docstring
- class DPTImageProcessor(BeitImageProcessor):
- resample = PILImageResampling.BICUBIC
- image_mean = IMAGENET_STANDARD_MEAN
- image_std = IMAGENET_STANDARD_STD
- size = {"height": 384, "width": 384}
- do_resize = True
- do_rescale = True
- do_normalize = True
- do_pad = False
- rescale_factor = 1 / 255
- ensure_multiple_of = 1
- keep_aspect_ratio = False
- # necessary for modular conversion
- crop_size = None
- do_center_crop = None
- do_reduce_labels = None
- valid_kwargs = DPTImageProcessorKwargs
- def resize(
- self,
- image: "torch.Tensor",
- size: SizeDict,
- resample: "PILImageResampling | tvF.InterpolationMode | int | None",
- antialias: bool = True,
- ensure_multiple_of: int | None = 1,
- keep_aspect_ratio: bool = False,
- ) -> "torch.Tensor":
- """
- Resize an image to `(size["height"], size["width"])`.
- Args:
- image (`torch.Tensor`):
- Image to resize.
- size (`SizeDict`):
- Dictionary in the format `{"height": int, "width": int}` specifying the size of the output image.
- interpolation (`InterpolationMode`, *optional*, defaults to `InterpolationMode.BILINEAR`):
- `InterpolationMode` filter to use when resizing the image e.g. `InterpolationMode.BICUBIC`.
- antialias (`bool`, *optional*, defaults to `True`):
- Whether to use antialiasing when resizing the image
- ensure_multiple_of (`int`, *optional*):
- If `do_resize` is `True`, the image is resized to a size that is a multiple of this value
- keep_aspect_ratio (`bool`, *optional*, defaults to `False`):
- If `True`, and `do_resize` is `True`, the image is resized to the largest possible size such that the aspect ratio is preserved.
- Returns:
- `torch.Tensor`: The resized image.
- """
- if not size.height or not size.width:
- raise ValueError(f"The size dictionary must contain the keys 'height' and 'width'. Got {size.keys()}")
- output_size = get_resize_output_image_size(
- image,
- output_size=(size.height, size.width),
- keep_aspect_ratio=keep_aspect_ratio,
- multiple=ensure_multiple_of,
- )
- return TorchvisionBackend.resize(self, image, output_size, resample=resample, antialias=antialias)
- def pad_image(
- self,
- image: "torch.Tensor",
- size_divisor: int = 1,
- ) -> "torch.Tensor":
- r"""
- Center pad a batch of images to be a multiple of `size_divisor`.
- Args:
- image (`torch.Tensor`):
- Image to pad. Can be a batch of images of dimensions (N, C, H, W) or a single image of dimensions (C, H, W).
- size_divisor (`int`):
- The width and height of the image will be padded to a multiple of this number.
- """
- height, width = image.shape[-2:]
- def _get_pad(size, size_divisor):
- new_size = math.ceil(size / size_divisor) * size_divisor
- pad_size = new_size - size
- pad_size_left = pad_size // 2
- pad_size_right = pad_size - pad_size_left
- return pad_size_left, pad_size_right
- pad_top, pad_bottom = _get_pad(height, size_divisor)
- pad_left, pad_right = _get_pad(width, size_divisor)
- padding = (pad_left, pad_top, pad_right, pad_bottom)
- return tvF.pad(image, padding)
- def _preprocess(
- self,
- images: list["torch.Tensor"],
- do_reduce_labels: bool,
- do_resize: bool,
- size: SizeDict,
- resample: "PILImageResampling | tvF.InterpolationMode | int | None",
- do_center_crop: bool,
- crop_size: SizeDict,
- do_rescale: bool,
- rescale_factor: float,
- do_normalize: bool,
- image_mean: float | list[float] | None,
- image_std: float | list[float] | None,
- keep_aspect_ratio: bool,
- ensure_multiple_of: int | None,
- do_pad: bool,
- size_divisor: int | None,
- disable_grouping: bool | None,
- **kwargs,
- ) -> BatchFeature:
- if do_reduce_labels:
- images = self.reduce_label(images)
- # Group images by size for batched resizing
- grouped_images, grouped_images_index = group_images_by_shape(images, disable_grouping=disable_grouping)
- resized_images_grouped = {}
- for shape, stacked_images in grouped_images.items():
- if do_resize:
- stacked_images = self.resize(
- image=stacked_images,
- size=size,
- resample=resample,
- ensure_multiple_of=ensure_multiple_of,
- keep_aspect_ratio=keep_aspect_ratio,
- )
- resized_images_grouped[shape] = stacked_images
- resized_images = reorder_images(resized_images_grouped, grouped_images_index)
- # Group images by size for further processing
- # Needed in case do_resize is False, or resize returns images with different sizes
- grouped_images, grouped_images_index = group_images_by_shape(resized_images, disable_grouping=disable_grouping)
- processed_images_grouped = {}
- for shape, stacked_images in grouped_images.items():
- if do_center_crop:
- stacked_images = self.center_crop(stacked_images, crop_size)
- # Fused rescale and normalize
- stacked_images = self.rescale_and_normalize(
- stacked_images, do_rescale, rescale_factor, do_normalize, image_mean, image_std
- )
- if do_pad:
- stacked_images = self.pad_image(stacked_images, size_divisor)
- processed_images_grouped[shape] = stacked_images
- processed_images = reorder_images(processed_images_grouped, grouped_images_index)
- return processed_images
- def post_process_depth_estimation(
- self,
- outputs: "DepthEstimatorOutput",
- target_sizes: TensorType | list[tuple[int, int]] | None | None = None,
- ) -> list[dict[str, TensorType]]:
- """
- Converts the raw output of [`DepthEstimatorOutput`] into final depth predictions and depth PIL images.
- Only supports PyTorch.
- Args:
- outputs ([`DepthEstimatorOutput`]):
- Raw outputs of the model.
- target_sizes (`TensorType` or `List[Tuple[int, int]]`, *optional*):
- Tensor of shape `(batch_size, 2)` or list of tuples (`Tuple[int, int]`) containing the target size
- (height, width) of each image in the batch. If left to None, predictions will not be resized.
- Returns:
- `List[Dict[str, TensorType]]`: A list of dictionaries of tensors representing the processed depth
- predictions.
- """
- requires_backends(self, "torch")
- predicted_depth = outputs.predicted_depth
- if (target_sizes is not None) and (len(predicted_depth) != len(target_sizes)):
- raise ValueError(
- "Make sure that you pass in as many target sizes as the batch dimension of the predicted depth"
- )
- results = []
- target_sizes = [None] * len(predicted_depth) if target_sizes is None else target_sizes
- for depth, target_size in zip(predicted_depth, target_sizes):
- if target_size is not None:
- depth = torch.nn.functional.interpolate(
- depth.unsqueeze(0).unsqueeze(1), size=target_size, mode="bicubic", align_corners=False
- ).squeeze()
- results.append({"predicted_depth": depth})
- return results
- __all__ = ["DPTImageProcessor"]
|