upsampling.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. from .module import Module
  2. from .. import functional as F
  3. from torch import Tensor
  4. from typing import Optional
  5. from ..common_types import _size_2_t, _ratio_2_t, _size_any_t, _ratio_any_t
  6. __all__ = ['Upsample', 'UpsamplingNearest2d', 'UpsamplingBilinear2d']
  7. class Upsample(Module):
  8. r"""Upsamples a given multi-channel 1D (temporal), 2D (spatial) or 3D (volumetric) data.
  9. The input data is assumed to be of the form
  10. `minibatch x channels x [optional depth] x [optional height] x width`.
  11. Hence, for spatial inputs, we expect a 4D Tensor and for volumetric inputs, we expect a 5D Tensor.
  12. The algorithms available for upsampling are nearest neighbor and linear,
  13. bilinear, bicubic and trilinear for 3D, 4D and 5D input Tensor,
  14. respectively.
  15. One can either give a :attr:`scale_factor` or the target output :attr:`size` to
  16. calculate the output size. (You cannot give both, as it is ambiguous)
  17. Args:
  18. size (int or Tuple[int] or Tuple[int, int] or Tuple[int, int, int], optional):
  19. output spatial sizes
  20. scale_factor (float or Tuple[float] or Tuple[float, float] or Tuple[float, float, float], optional):
  21. multiplier for spatial size. Has to match input size if it is a tuple.
  22. mode (str, optional): the upsampling algorithm: one of ``'nearest'``,
  23. ``'linear'``, ``'bilinear'``, ``'bicubic'`` and ``'trilinear'``.
  24. Default: ``'nearest'``
  25. align_corners (bool, optional): if ``True``, the corner pixels of the input
  26. and output tensors are aligned, and thus preserving the values at
  27. those pixels. This only has effect when :attr:`mode` is
  28. ``'linear'``, ``'bilinear'``, ``'bicubic'``, or ``'trilinear'``.
  29. Default: ``False``
  30. recompute_scale_factor (bool, optional): recompute the scale_factor for use in the
  31. interpolation calculation. If `recompute_scale_factor` is ``True``, then
  32. `scale_factor` must be passed in and `scale_factor` is used to compute the
  33. output `size`. The computed output `size` will be used to infer new scales for
  34. the interpolation. Note that when `scale_factor` is floating-point, it may differ
  35. from the recomputed `scale_factor` due to rounding and precision issues.
  36. If `recompute_scale_factor` is ``False``, then `size` or `scale_factor` will
  37. be used directly for interpolation.
  38. Shape:
  39. - Input: :math:`(N, C, W_{in})`, :math:`(N, C, H_{in}, W_{in})` or :math:`(N, C, D_{in}, H_{in}, W_{in})`
  40. - Output: :math:`(N, C, W_{out})`, :math:`(N, C, H_{out}, W_{out})`
  41. or :math:`(N, C, D_{out}, H_{out}, W_{out})`, where
  42. .. math::
  43. D_{out} = \left\lfloor D_{in} \times \text{scale\_factor} \right\rfloor
  44. .. math::
  45. H_{out} = \left\lfloor H_{in} \times \text{scale\_factor} \right\rfloor
  46. .. math::
  47. W_{out} = \left\lfloor W_{in} \times \text{scale\_factor} \right\rfloor
  48. .. warning::
  49. With ``align_corners = True``, the linearly interpolating modes
  50. (`linear`, `bilinear`, `bicubic`, and `trilinear`) don't proportionally
  51. align the output and input pixels, and thus the output values can depend
  52. on the input size. This was the default behavior for these modes up to
  53. version 0.3.1. Since then, the default behavior is
  54. ``align_corners = False``. See below for concrete examples on how this
  55. affects the outputs.
  56. .. note::
  57. If you want downsampling/general resizing, you should use :func:`~nn.functional.interpolate`.
  58. Examples::
  59. >>> input = torch.arange(1, 5, dtype=torch.float32).view(1, 1, 2, 2)
  60. >>> input
  61. tensor([[[[1., 2.],
  62. [3., 4.]]]])
  63. >>> m = nn.Upsample(scale_factor=2, mode='nearest')
  64. >>> m(input)
  65. tensor([[[[1., 1., 2., 2.],
  66. [1., 1., 2., 2.],
  67. [3., 3., 4., 4.],
  68. [3., 3., 4., 4.]]]])
  69. >>> # xdoctest: +IGNORE_WANT("other tests seem to modify printing styles")
  70. >>> m = nn.Upsample(scale_factor=2, mode='bilinear') # align_corners=False
  71. >>> m(input)
  72. tensor([[[[1.0000, 1.2500, 1.7500, 2.0000],
  73. [1.5000, 1.7500, 2.2500, 2.5000],
  74. [2.5000, 2.7500, 3.2500, 3.5000],
  75. [3.0000, 3.2500, 3.7500, 4.0000]]]])
  76. >>> m = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)
  77. >>> m(input)
  78. tensor([[[[1.0000, 1.3333, 1.6667, 2.0000],
  79. [1.6667, 2.0000, 2.3333, 2.6667],
  80. [2.3333, 2.6667, 3.0000, 3.3333],
  81. [3.0000, 3.3333, 3.6667, 4.0000]]]])
  82. >>> # Try scaling the same data in a larger tensor
  83. >>> input_3x3 = torch.zeros(3, 3).view(1, 1, 3, 3)
  84. >>> input_3x3[:, :, :2, :2].copy_(input)
  85. tensor([[[[1., 2.],
  86. [3., 4.]]]])
  87. >>> input_3x3
  88. tensor([[[[1., 2., 0.],
  89. [3., 4., 0.],
  90. [0., 0., 0.]]]])
  91. >>> # xdoctest: +IGNORE_WANT("seems to fail when other tests are run in the same session")
  92. >>> m = nn.Upsample(scale_factor=2, mode='bilinear') # align_corners=False
  93. >>> # Notice that values in top left corner are the same with the small input (except at boundary)
  94. >>> m(input_3x3)
  95. tensor([[[[1.0000, 1.2500, 1.7500, 1.5000, 0.5000, 0.0000],
  96. [1.5000, 1.7500, 2.2500, 1.8750, 0.6250, 0.0000],
  97. [2.5000, 2.7500, 3.2500, 2.6250, 0.8750, 0.0000],
  98. [2.2500, 2.4375, 2.8125, 2.2500, 0.7500, 0.0000],
  99. [0.7500, 0.8125, 0.9375, 0.7500, 0.2500, 0.0000],
  100. [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000]]]])
  101. >>> m = nn.Upsample(scale_factor=2, mode='bilinear', align_corners=True)
  102. >>> # Notice that values in top left corner are now changed
  103. >>> m(input_3x3)
  104. tensor([[[[1.0000, 1.4000, 1.8000, 1.6000, 0.8000, 0.0000],
  105. [1.8000, 2.2000, 2.6000, 2.2400, 1.1200, 0.0000],
  106. [2.6000, 3.0000, 3.4000, 2.8800, 1.4400, 0.0000],
  107. [2.4000, 2.7200, 3.0400, 2.5600, 1.2800, 0.0000],
  108. [1.2000, 1.3600, 1.5200, 1.2800, 0.6400, 0.0000],
  109. [0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000]]]])
  110. """
  111. __constants__ = ['size', 'scale_factor', 'mode', 'align_corners', 'name', 'recompute_scale_factor']
  112. name: str
  113. size: Optional[_size_any_t]
  114. scale_factor: Optional[_ratio_any_t]
  115. mode: str
  116. align_corners: Optional[bool]
  117. recompute_scale_factor: Optional[bool]
  118. def __init__(self, size: Optional[_size_any_t] = None, scale_factor: Optional[_ratio_any_t] = None,
  119. mode: str = 'nearest', align_corners: Optional[bool] = None,
  120. recompute_scale_factor: Optional[bool] = None) -> None:
  121. super().__init__()
  122. self.name = type(self).__name__
  123. self.size = size
  124. if isinstance(scale_factor, tuple):
  125. self.scale_factor = tuple(float(factor) for factor in scale_factor)
  126. else:
  127. self.scale_factor = float(scale_factor) if scale_factor else None
  128. self.mode = mode
  129. self.align_corners = align_corners
  130. self.recompute_scale_factor = recompute_scale_factor
  131. def forward(self, input: Tensor) -> Tensor:
  132. return F.interpolate(input, self.size, self.scale_factor, self.mode, self.align_corners,
  133. recompute_scale_factor=self.recompute_scale_factor)
  134. def extra_repr(self) -> str:
  135. if self.scale_factor is not None:
  136. info = 'scale_factor=' + repr(self.scale_factor)
  137. else:
  138. info = 'size=' + repr(self.size)
  139. info += ', mode=' + repr(self.mode)
  140. return info
  141. class UpsamplingNearest2d(Upsample):
  142. r"""Applies a 2D nearest neighbor upsampling to an input signal composed of several input
  143. channels.
  144. To specify the scale, it takes either the :attr:`size` or the :attr:`scale_factor`
  145. as it's constructor argument.
  146. When :attr:`size` is given, it is the output size of the image `(h, w)`.
  147. Args:
  148. size (int or Tuple[int, int], optional): output spatial sizes
  149. scale_factor (float or Tuple[float, float], optional): multiplier for
  150. spatial size.
  151. .. warning::
  152. This class is deprecated in favor of :func:`~nn.functional.interpolate`.
  153. Shape:
  154. - Input: :math:`(N, C, H_{in}, W_{in})`
  155. - Output: :math:`(N, C, H_{out}, W_{out})` where
  156. .. math::
  157. H_{out} = \left\lfloor H_{in} \times \text{scale\_factor} \right\rfloor
  158. .. math::
  159. W_{out} = \left\lfloor W_{in} \times \text{scale\_factor} \right\rfloor
  160. Examples::
  161. >>> input = torch.arange(1, 5, dtype=torch.float32).view(1, 1, 2, 2)
  162. >>> input
  163. tensor([[[[1., 2.],
  164. [3., 4.]]]])
  165. >>> m = nn.UpsamplingNearest2d(scale_factor=2)
  166. >>> m(input)
  167. tensor([[[[1., 1., 2., 2.],
  168. [1., 1., 2., 2.],
  169. [3., 3., 4., 4.],
  170. [3., 3., 4., 4.]]]])
  171. """
  172. def __init__(self, size: Optional[_size_2_t] = None, scale_factor: Optional[_ratio_2_t] = None) -> None:
  173. super().__init__(size, scale_factor, mode='nearest')
  174. class UpsamplingBilinear2d(Upsample):
  175. r"""Applies a 2D bilinear upsampling to an input signal composed of several input
  176. channels.
  177. To specify the scale, it takes either the :attr:`size` or the :attr:`scale_factor`
  178. as it's constructor argument.
  179. When :attr:`size` is given, it is the output size of the image `(h, w)`.
  180. Args:
  181. size (int or Tuple[int, int], optional): output spatial sizes
  182. scale_factor (float or Tuple[float, float], optional): multiplier for
  183. spatial size.
  184. .. warning::
  185. This class is deprecated in favor of :func:`~nn.functional.interpolate`. It is
  186. equivalent to ``nn.functional.interpolate(..., mode='bilinear', align_corners=True)``.
  187. Shape:
  188. - Input: :math:`(N, C, H_{in}, W_{in})`
  189. - Output: :math:`(N, C, H_{out}, W_{out})` where
  190. .. math::
  191. H_{out} = \left\lfloor H_{in} \times \text{scale\_factor} \right\rfloor
  192. .. math::
  193. W_{out} = \left\lfloor W_{in} \times \text{scale\_factor} \right\rfloor
  194. Examples::
  195. >>> input = torch.arange(1, 5, dtype=torch.float32).view(1, 1, 2, 2)
  196. >>> input
  197. tensor([[[[1., 2.],
  198. [3., 4.]]]])
  199. >>> # xdoctest: +IGNORE_WANT("do other tests modify the global state?")
  200. >>> m = nn.UpsamplingBilinear2d(scale_factor=2)
  201. >>> m(input)
  202. tensor([[[[1.0000, 1.3333, 1.6667, 2.0000],
  203. [1.6667, 2.0000, 2.3333, 2.6667],
  204. [2.3333, 2.6667, 3.0000, 3.3333],
  205. [3.0000, 3.3333, 3.6667, 4.0000]]]])
  206. """
  207. def __init__(self, size: Optional[_size_2_t] = None, scale_factor: Optional[_ratio_2_t] = None) -> None:
  208. super().__init__(size, scale_factor, mode='bilinear', align_corners=True)