modular_dpt.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. # coding=utf-8
  2. # Copyright 2025 HuggingFace Inc. team. All rights reserved.
  3. #
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. import math
  17. from collections.abc import Iterable
  18. from typing import TYPE_CHECKING, Optional, Union
  19. import torch
  20. from ...image_processing_base import BatchFeature
  21. from ...image_processing_utils_fast import BaseImageProcessorFast, DefaultFastImageProcessorKwargs
  22. from ...image_transforms import group_images_by_shape, reorder_images
  23. from ...image_utils import (
  24. IMAGENET_STANDARD_MEAN,
  25. IMAGENET_STANDARD_STD,
  26. PILImageResampling,
  27. SizeDict,
  28. )
  29. from ...utils import (
  30. TensorType,
  31. auto_docstring,
  32. requires_backends,
  33. )
  34. from ..beit.image_processing_beit_fast import BeitImageProcessorFast
  35. if TYPE_CHECKING:
  36. from ...modeling_outputs import DepthEstimatorOutput
  37. from torchvision.transforms.v2 import functional as F
  38. def get_resize_output_image_size(
  39. input_image: "torch.Tensor",
  40. output_size: Union[int, Iterable[int]],
  41. keep_aspect_ratio: bool,
  42. multiple: int,
  43. ) -> SizeDict:
  44. def constrain_to_multiple_of(val, multiple, min_val=0, max_val=None):
  45. x = round(val / multiple) * multiple
  46. if max_val is not None and x > max_val:
  47. x = math.floor(val / multiple) * multiple
  48. if x < min_val:
  49. x = math.ceil(val / multiple) * multiple
  50. return x
  51. input_height, input_width = input_image.shape[-2:]
  52. output_height, output_width = output_size
  53. # determine new height and width
  54. scale_height = output_height / input_height
  55. scale_width = output_width / input_width
  56. if keep_aspect_ratio:
  57. # scale as little as possible
  58. if abs(1 - scale_width) < abs(1 - scale_height):
  59. # fit width
  60. scale_height = scale_width
  61. else:
  62. # fit height
  63. scale_width = scale_height
  64. new_height = constrain_to_multiple_of(scale_height * input_height, multiple=multiple)
  65. new_width = constrain_to_multiple_of(scale_width * input_width, multiple=multiple)
  66. return SizeDict(height=new_height, width=new_width)
  67. class DPTFastImageProcessorKwargs(DefaultFastImageProcessorKwargs):
  68. """
  69. ensure_multiple_of (`int`, *optional*, defaults to 1):
  70. If `do_resize` is `True`, the image is resized to a size that is a multiple of this value. Can be overridden
  71. by `ensure_multiple_of` in `preprocess`.
  72. size_divisor (`int`, *optional*):
  73. If `do_pad` is `True`, pads the image dimensions to be divisible by this value. This was introduced in the
  74. DINOv2 paper, which uses the model in combination with DPT.
  75. keep_aspect_ratio (`bool`, *optional*, defaults to `False`):
  76. If `True`, the image is resized to the largest possible size such that the aspect ratio is preserved. Can
  77. be overridden by `keep_aspect_ratio` in `preprocess`.
  78. do_reduce_labels (`bool`, *optional*, defaults to `self.do_reduce_labels`):
  79. Whether or not to reduce all label values of segmentation maps by 1. Usually used for datasets where 0
  80. is used for background, and background itself is not included in all classes of a dataset (e.g.
  81. ADE20k). The background label will be replaced by 255.
  82. """
  83. ensure_multiple_of: Optional[int]
  84. size_divisor: Optional[int]
  85. keep_aspect_ratio: Optional[bool]
  86. do_reduce_labels: Optional[bool]
  87. @auto_docstring
  88. class DPTImageProcessorFast(BeitImageProcessorFast):
  89. resample = PILImageResampling.BICUBIC
  90. image_mean = IMAGENET_STANDARD_MEAN
  91. image_std = IMAGENET_STANDARD_STD
  92. size = {"height": 384, "width": 384}
  93. do_resize = True
  94. do_rescale = True
  95. do_normalize = True
  96. do_pad = False
  97. rescale_factor = 1 / 255
  98. ensure_multiple_of = 1
  99. keep_aspect_ratio = False
  100. do_reduce_labels = False
  101. crop_size = None
  102. do_center_crop = None
  103. do_reduce_labels = None
  104. valid_kwargs = DPTFastImageProcessorKwargs
  105. def resize(
  106. self,
  107. image: "torch.Tensor",
  108. size: SizeDict,
  109. interpolation: Optional["F.InterpolationMode"] = None,
  110. antialias: bool = True,
  111. ensure_multiple_of: Optional[int] = 1,
  112. keep_aspect_ratio: bool = False,
  113. ) -> "torch.Tensor":
  114. """
  115. Resize an image to `(size["height"], size["width"])`.
  116. Args:
  117. image (`torch.Tensor`):
  118. Image to resize.
  119. size (`SizeDict`):
  120. Dictionary in the format `{"height": int, "width": int}` specifying the size of the output image.
  121. interpolation (`InterpolationMode`, *optional*, defaults to `InterpolationMode.BILINEAR`):
  122. `InterpolationMode` filter to use when resizing the image e.g. `InterpolationMode.BICUBIC`.
  123. antialias (`bool`, *optional*, defaults to `True`):
  124. Whether to use antialiasing when resizing the image
  125. ensure_multiple_of (`int`, *optional*):
  126. If `do_resize` is `True`, the image is resized to a size that is a multiple of this value
  127. keep_aspect_ratio (`bool`, *optional*, defaults to `False`):
  128. If `True`, and `do_resize` is `True`, the image is resized to the largest possible size such that the aspect ratio is preserved.
  129. Returns:
  130. `torch.Tensor`: The resized image.
  131. """
  132. if not size.height or not size.width:
  133. raise ValueError(f"The size dictionary must contain the keys 'height' and 'width'. Got {size.keys()}")
  134. output_size = get_resize_output_image_size(
  135. image,
  136. output_size=(size.height, size.width),
  137. keep_aspect_ratio=keep_aspect_ratio,
  138. multiple=ensure_multiple_of,
  139. )
  140. return BaseImageProcessorFast.resize(
  141. self, image, output_size, interpolation=interpolation, antialias=antialias
  142. )
  143. def pad_image(
  144. self,
  145. image: "torch.Tensor",
  146. size_divisor: int = 1,
  147. ) -> "torch.Tensor":
  148. r"""
  149. Center pad a batch of images to be a multiple of `size_divisor`.
  150. Args:
  151. image (`torch.Tensor`):
  152. Image to pad. Can be a batch of images of dimensions (N, C, H, W) or a single image of dimensions (C, H, W).
  153. size_divisor (`int`):
  154. The width and height of the image will be padded to a multiple of this number.
  155. """
  156. height, width = image.shape[-2:]
  157. def _get_pad(size, size_divisor):
  158. new_size = math.ceil(size / size_divisor) * size_divisor
  159. pad_size = new_size - size
  160. pad_size_left = pad_size // 2
  161. pad_size_right = pad_size - pad_size_left
  162. return pad_size_left, pad_size_right
  163. pad_top, pad_bottom = _get_pad(height, size_divisor)
  164. pad_left, pad_right = _get_pad(width, size_divisor)
  165. padding = (pad_left, pad_top, pad_right, pad_bottom)
  166. return F.pad(image, padding)
  167. def _preprocess(
  168. self,
  169. images: list["torch.Tensor"],
  170. do_reduce_labels: bool,
  171. do_resize: bool,
  172. size: SizeDict,
  173. interpolation: Optional["F.InterpolationMode"],
  174. do_center_crop: bool,
  175. crop_size: SizeDict,
  176. do_rescale: bool,
  177. rescale_factor: float,
  178. do_normalize: bool,
  179. image_mean: Optional[Union[float, list[float]]],
  180. image_std: Optional[Union[float, list[float]]],
  181. keep_aspect_ratio: bool,
  182. ensure_multiple_of: Optional[int],
  183. do_pad: bool,
  184. size_divisor: Optional[int],
  185. disable_grouping: Optional[bool],
  186. return_tensors: Optional[Union[str, TensorType]],
  187. **kwargs,
  188. ) -> BatchFeature:
  189. if do_reduce_labels:
  190. images = self.reduce_label(images)
  191. # Group images by size for batched resizing
  192. grouped_images, grouped_images_index = group_images_by_shape(images, disable_grouping=disable_grouping)
  193. resized_images_grouped = {}
  194. for shape, stacked_images in grouped_images.items():
  195. if do_resize:
  196. stacked_images = self.resize(
  197. image=stacked_images,
  198. size=size,
  199. interpolation=interpolation,
  200. ensure_multiple_of=ensure_multiple_of,
  201. keep_aspect_ratio=keep_aspect_ratio,
  202. )
  203. resized_images_grouped[shape] = stacked_images
  204. resized_images = reorder_images(resized_images_grouped, grouped_images_index)
  205. # Group images by size for further processing
  206. # Needed in case do_resize is False, or resize returns images with different sizes
  207. grouped_images, grouped_images_index = group_images_by_shape(resized_images, disable_grouping=disable_grouping)
  208. processed_images_grouped = {}
  209. for shape, stacked_images in grouped_images.items():
  210. if do_center_crop:
  211. stacked_images = self.center_crop(stacked_images, crop_size)
  212. if do_pad:
  213. stacked_images = self.pad_image(stacked_images, size_divisor)
  214. # Fused rescale and normalize
  215. stacked_images = self.rescale_and_normalize(
  216. stacked_images, do_rescale, rescale_factor, do_normalize, image_mean, image_std
  217. )
  218. processed_images_grouped[shape] = stacked_images
  219. processed_images = reorder_images(processed_images_grouped, grouped_images_index)
  220. processed_images = torch.stack(processed_images, dim=0) if return_tensors else processed_images
  221. return BatchFeature(data={"pixel_values": processed_images})
  222. def post_process_depth_estimation(
  223. self,
  224. outputs: "DepthEstimatorOutput",
  225. target_sizes: Optional[Union[TensorType, list[tuple[int, int]], None]] = None,
  226. ) -> list[dict[str, TensorType]]:
  227. """
  228. Converts the raw output of [`DepthEstimatorOutput`] into final depth predictions and depth PIL images.
  229. Only supports PyTorch.
  230. Args:
  231. outputs ([`DepthEstimatorOutput`]):
  232. Raw outputs of the model.
  233. target_sizes (`TensorType` or `List[Tuple[int, int]]`, *optional*):
  234. Tensor of shape `(batch_size, 2)` or list of tuples (`Tuple[int, int]`) containing the target size
  235. (height, width) of each image in the batch. If left to None, predictions will not be resized.
  236. Returns:
  237. `List[Dict[str, TensorType]]`: A list of dictionaries of tensors representing the processed depth
  238. predictions.
  239. """
  240. requires_backends(self, "torch")
  241. predicted_depth = outputs.predicted_depth
  242. if (target_sizes is not None) and (len(predicted_depth) != len(target_sizes)):
  243. raise ValueError(
  244. "Make sure that you pass in as many target sizes as the batch dimension of the predicted depth"
  245. )
  246. results = []
  247. target_sizes = [None] * len(predicted_depth) if target_sizes is None else target_sizes
  248. for depth, target_size in zip(predicted_depth, target_sizes):
  249. if target_size is not None:
  250. depth = torch.nn.functional.interpolate(
  251. depth.unsqueeze(0).unsqueeze(1), size=target_size, mode="bicubic", align_corners=False
  252. ).squeeze()
  253. results.append({"predicted_depth": depth})
  254. return results
  255. __all__ = ["DPTImageProcessorFast"]