modular_dpt.py 11 KB

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