@@ -474,7 +474,7 @@
Loading
474 474
475 475
476 476
@torch.jit.unused
477 -
def affine(img, matrix, resample=0, fillcolor=None):
477 +
def affine(img, matrix, interpolation=0, fill=None):
478 478
    """PRIVATE METHOD. Apply affine transformation on the PIL Image keeping image center invariant.
479 479
480 480
    .. warning::
@@ -485,11 +485,11 @@
Loading
485 485
    Args:
486 486
        img (PIL Image): image to be rotated.
487 487
        matrix (list of floats): list of 6 float values representing inverse matrix for affine transformation.
488 -
        resample (``PIL.Image.NEAREST`` or ``PIL.Image.BILINEAR`` or ``PIL.Image.BICUBIC``, optional):
488 +
        interpolation (``PIL.Image.NEAREST`` or ``PIL.Image.BILINEAR`` or ``PIL.Image.BICUBIC``, optional):
489 489
            An optional resampling filter.
490 490
            See `filters`_ for more information.
491 491
            If omitted, or if the image has mode "1" or "P", it is set to ``PIL.Image.NEAREST``.
492 -
        fillcolor (int): Optional fill color for the area outside the transform in the output image. (Pillow>=5.0.0)
492 +
        fill (int): Optional fill color for the area outside the transform in the output image. (Pillow>=5.0.0)
493 493
494 494
    Returns:
495 495
        PIL Image: Transformed image.
@@ -498,12 +498,12 @@
Loading
498 498
        raise TypeError('img should be PIL Image. Got {}'.format(type(img)))
499 499
500 500
    output_size = img.size
501 -
    opts = _parse_fill(fillcolor, img, '5.0.0')
502 -
    return img.transform(output_size, Image.AFFINE, matrix, resample, **opts)
501 +
    opts = _parse_fill(fill, img, '5.0.0')
502 +
    return img.transform(output_size, Image.AFFINE, matrix, interpolation, **opts)
503 503
504 504
505 505
@torch.jit.unused
506 -
def rotate(img, angle, resample=0, expand=False, center=None, fill=None):
506 +
def rotate(img, angle, interpolation=0, expand=False, center=None, fill=None):
507 507
    """PRIVATE METHOD. Rotate PIL image by angle.
508 508
509 509
    .. warning::
@@ -514,7 +514,7 @@
Loading
514 514
    Args:
515 515
        img (PIL Image): image to be rotated.
516 516
        angle (float or int): rotation angle value in degrees, counter-clockwise.
517 -
        resample (``PIL.Image.NEAREST`` or ``PIL.Image.BILINEAR`` or ``PIL.Image.BICUBIC``, optional):
517 +
        interpolation (``PIL.Image.NEAREST`` or ``PIL.Image.BILINEAR`` or ``PIL.Image.BICUBIC``, optional):
518 518
            An optional resampling filter. See `filters`_ for more information.
519 519
            If omitted, or if the image has mode "1" or "P", it is set to ``PIL.Image.NEAREST``.
520 520
        expand (bool, optional): Optional expansion flag.
@@ -538,7 +538,7 @@
Loading
538 538
        raise TypeError("img should be PIL Image. Got {}".format(type(img)))
539 539
540 540
    opts = _parse_fill(fill, img, '5.2.0')
541 -
    return img.rotate(angle, resample, expand, center, **opts)
541 +
    return img.rotate(angle, interpolation, expand, center, **opts)
542 542
543 543
544 544
@torch.jit.unused

@@ -1,6 +1,6 @@
Loading
1 1
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
2 2
import torch
3 -
from torch import nn
3 +
from torch import nn, Tensor
4 4
5 5
from torch.jit.annotations import List, Optional, Dict
6 6
from .image_list import ImageList
@@ -56,8 +56,8 @@
Loading
56 56
    # For every (aspect_ratios, scales) combination, output a zero-centered anchor with those values.
57 57
    # (scales, aspect_ratios) are usually an element of zip(self.scales, self.aspect_ratios)
58 58
    # This method assumes aspect ratio = height / width for an anchor.
59 -
    def generate_anchors(self, scales, aspect_ratios, dtype=torch.float32, device="cpu"):
60 -
        # type: (List[int], List[float], int, Device) -> Tensor  # noqa: F821
59 +
    def generate_anchors(self, scales: List[int], aspect_ratios: List[float], dtype: torch.dtype = torch.float32,
60 +
                         device: torch.device = torch.device("cpu")):
61 61
        scales = torch.as_tensor(scales, dtype=dtype, device=device)
62 62
        aspect_ratios = torch.as_tensor(aspect_ratios, dtype=dtype, device=device)
63 63
        h_ratios = torch.sqrt(aspect_ratios)
@@ -69,8 +69,7 @@
Loading
69 69
        base_anchors = torch.stack([-ws, -hs, ws, hs], dim=1) / 2
70 70
        return base_anchors.round()
71 71
72 -
    def set_cell_anchors(self, dtype, device):
73 -
        # type: (int, Device) -> None  # noqa: F821
72 +
    def set_cell_anchors(self, dtype: torch.dtype, device: torch.device):
74 73
        if self.cell_anchors is not None:
75 74
            cell_anchors = self.cell_anchors
76 75
            assert cell_anchors is not None
@@ -95,8 +94,7 @@
Loading
95 94
96 95
    # For every combination of (a, (g, s), i) in (self.cell_anchors, zip(grid_sizes, strides), 0:2),
97 96
    # output g[i] anchors that are s[i] distance apart in direction i, with the same dimensions as a.
98 -
    def grid_anchors(self, grid_sizes, strides):
99 -
        # type: (List[List[int]], List[List[Tensor]]) -> List[Tensor]
97 +
    def grid_anchors(self, grid_sizes: List[List[int]], strides: List[List[Tensor]]) -> List[Tensor]:
100 98
        anchors = []
101 99
        cell_anchors = self.cell_anchors
102 100
        assert cell_anchors is not None
@@ -134,8 +132,7 @@
Loading
134 132
135 133
        return anchors
136 134
137 -
    def cached_grid_anchors(self, grid_sizes, strides):
138 -
        # type: (List[List[int]], List[List[Tensor]]) -> List[Tensor]
135 +
    def cached_grid_anchors(self, grid_sizes: List[List[int]], strides: List[List[Tensor]]) -> List[Tensor]:
139 136
        key = str(grid_sizes) + str(strides)
140 137
        if key in self._cache:
141 138
            return self._cache[key]
@@ -143,8 +140,7 @@
Loading
143 140
        self._cache[key] = anchors
144 141
        return anchors
145 142
146 -
    def forward(self, image_list, feature_maps):
147 -
        # type: (ImageList, List[Tensor]) -> List[Tensor]
143 +
    def forward(self, image_list: ImageList, feature_maps: List[Tensor]) -> List[Tensor]:
148 144
        grid_sizes = list([feature_map.shape[-2:] for feature_map in feature_maps])
149 145
        image_size = image_list.tensors.shape[-2:]
150 146
        dtype, device = feature_maps[0].dtype, feature_maps[0].device
@@ -153,10 +149,8 @@
Loading
153 149
        self.set_cell_anchors(dtype, device)
154 150
        anchors_over_all_feature_maps = self.cached_grid_anchors(grid_sizes, strides)
155 151
        anchors = torch.jit.annotate(List[List[torch.Tensor]], [])
156 -
        for i, (image_height, image_width) in enumerate(image_list.image_sizes):
157 -
            anchors_in_image = []
158 -
            for anchors_per_feature_map in anchors_over_all_feature_maps:
159 -
                anchors_in_image.append(anchors_per_feature_map)
152 +
        for i in range(len(image_list.image_sizes)):
153 +
            anchors_in_image = [anchors_per_feature_map for anchors_per_feature_map in anchors_over_all_feature_maps]
160 154
            anchors.append(anchors_in_image)
161 155
        anchors = [torch.cat(anchors_per_image) for anchors_per_image in anchors]
162 156
        # Clear the cache in case that memory leaks.

@@ -1,8 +1,13 @@
Loading
1 1
from typing import Union, Optional, List, Tuple, Text, BinaryIO
2 -
import io
3 2
import pathlib
4 3
import torch
5 4
import math
5 +
import numpy as np
6 +
from PIL import Image, ImageDraw
7 +
from PIL import ImageFont
8 +
9 +
__all__ = ["make_grid", "save_image", "draw_bounding_boxes"]
10 +
6 11
irange = range
7 12
8 13
@@ -121,10 +126,64 @@
Loading
121 126
            If a file object was used instead of a filename, this parameter should always be used.
122 127
        **kwargs: Other arguments are documented in ``make_grid``.
123 128
    """
124 -
    from PIL import Image
125 129
    grid = make_grid(tensor, nrow=nrow, padding=padding, pad_value=pad_value,
126 130
                     normalize=normalize, range=range, scale_each=scale_each)
127 131
    # Add 0.5 after unnormalizing to [0, 255] to round to nearest integer
128 132
    ndarr = grid.mul(255).add_(0.5).clamp_(0, 255).permute(1, 2, 0).to('cpu', torch.uint8).numpy()
129 133
    im = Image.fromarray(ndarr)
130 134
    im.save(fp, format=format)
135 +
136 +
137 +
@torch.no_grad()
138 +
def draw_bounding_boxes(
139 +
    image: torch.Tensor,
140 +
    boxes: torch.Tensor,
141 +
    labels: Optional[List[str]] = None,
142 +
    colors: Optional[List[Union[str, Tuple[int, int, int]]]] = None,
143 +
    width: int = 1,
144 +
    font: Optional[str] = None,
145 +
    font_size: int = 10
146 +
) -> torch.Tensor:
147 +
148 +
    """
149 +
    Draws bounding boxes on given image.
150 +
    The values of the input image should be uint8 between 0 and 255.
151 +
152 +
    Args:
153 +
        image (Tensor): Tensor of shape (C x H x W)
154 +
        bboxes (Tensor): Tensor of size (N, 4) containing bounding boxes in (xmin, ymin, xmax, ymax) format. Note that
155 +
            the boxes are absolute coordinates with respect to the image. In other words: `0 <= xmin < xmax < W` and
156 +
            `0 <= ymin < ymax < H`.
157 +
        labels (List[str]): List containing the labels of bounding boxes.
158 +
        colors (List[Union[str, Tuple[int, int, int]]]): List containing the colors of bounding boxes. The colors can
159 +
            be represented as `str` or `Tuple[int, int, int]`.
160 +
        width (int): Width of bounding box.
161 +
        font (str): A filename containing a TrueType font. If the file is not found in this filename, the loader may
162 +
            also search in other directories, such as the `fonts/` directory on Windows or `/Library/Fonts/`,
163 +
            `/System/Library/Fonts/` and `~/Library/Fonts/` on macOS.
164 +
        font_size (int): The requested font size in points.
165 +
    """
166 +
167 +
    if not isinstance(image, torch.Tensor):
168 +
        raise TypeError(f"Tensor expected, got {type(image)}")
169 +
    elif image.dtype != torch.uint8:
170 +
        raise ValueError(f"Tensor uint8 expected, got {image.dtype}")
171 +
    elif image.dim() != 3:
172 +
        raise ValueError("Pass individual images, not batches")
173 +
174 +
    ndarr = image.permute(1, 2, 0).numpy()
175 +
    img_to_draw = Image.fromarray(ndarr)
176 +
177 +
    img_boxes = boxes.to(torch.int64).tolist()
178 +
179 +
    draw = ImageDraw.Draw(img_to_draw)
180 +
181 +
    for i, bbox in enumerate(img_boxes):
182 +
        color = None if colors is None else colors[i]
183 +
        draw.rectangle(bbox, width=width, outline=color)
184 +
185 +
        if labels is not None:
186 +
            txt_font = ImageFont.load_default() if font is None else ImageFont.truetype(font=font, size=font_size)
187 +
            draw.text((bbox[0], bbox[1]), labels[i], fill=color, font=txt_font)
188 +
189 +
    return torch.from_numpy(np.array(img_to_draw)).permute(2, 0, 1)

@@ -1,6 +1,7 @@
Loading
1 1
import math
2 2
import numbers
3 3
import warnings
4 +
from enum import Enum
4 5
from typing import Any, Optional
5 6
6 7
import numpy as np
@@ -19,6 +20,41 @@
Loading
19 20
from . import functional_tensor as F_t
20 21
21 22
23 +
class InterpolationMode(Enum):
24 +
    """Interpolation modes
25 +
    """
26 +
    NEAREST = "nearest"
27 +
    BILINEAR = "bilinear"
28 +
    BICUBIC = "bicubic"
29 +
    # For PIL compatibility
30 +
    BOX = "box"
31 +
    HAMMING = "hamming"
32 +
    LANCZOS = "lanczos"
33 +
34 +
35 +
# TODO: Once torchscript supports Enums with staticmethod
36 +
# this can be put into InterpolationMode as staticmethod
37 +
def _interpolation_modes_from_int(i: int) -> InterpolationMode:
38 +
    inverse_modes_mapping = {
39 +
        0: InterpolationMode.NEAREST,
40 +
        2: InterpolationMode.BILINEAR,
41 +
        3: InterpolationMode.BICUBIC,
42 +
        4: InterpolationMode.BOX,
43 +
        5: InterpolationMode.HAMMING,
44 +
        1: InterpolationMode.LANCZOS,
45 +
    }
46 +
    return inverse_modes_mapping[i]
47 +
48 +
49 +
pil_modes_mapping = {
50 +
    InterpolationMode.NEAREST: 0,
51 +
    InterpolationMode.BILINEAR: 2,
52 +
    InterpolationMode.BICUBIC: 3,
53 +
    InterpolationMode.BOX: 4,
54 +
    InterpolationMode.HAMMING: 5,
55 +
    InterpolationMode.LANCZOS: 1,
56 +
}
57 +
22 58
_is_pil_image = F_pil._is_pil_image
23 59
_parse_fill = F_pil._parse_fill
24 60
@@ -293,7 +329,7 @@
Loading
293 329
    return tensor
294 330
295 331
296 -
def resize(img: Tensor, size: List[int], interpolation: int = Image.BILINEAR) -> Tensor:
332 +
def resize(img: Tensor, size: List[int], interpolation: InterpolationMode = InterpolationMode.BILINEAR) -> Tensor:
297 333
    r"""Resize the input image to the given size.
298 334
    The image can be a PIL Image or a torch Tensor, in which case it is expected
299 335
    to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions
@@ -307,17 +343,31 @@
Loading
307 343
            :math:`\left(\text{size} \times \frac{\text{height}}{\text{width}}, \text{size}\right)`.
308 344
            In torchscript mode size as single int is not supported, use a tuple or
309 345
            list of length 1: ``[size, ]``.
310 -
        interpolation (int, optional): Desired interpolation enum defined by `filters`_.
311 -
            Default is ``PIL.Image.BILINEAR``. If input is Tensor, only ``PIL.Image.NEAREST``, ``PIL.Image.BILINEAR``
312 -
            and ``PIL.Image.BICUBIC`` are supported.
346 +
        interpolation (InterpolationMode): Desired interpolation enum defined by
347 +
            :class:`torchvision.transforms.InterpolationMode`.
348 +
            Default is ``InterpolationMode.BILINEAR``. If input is Tensor, only ``InterpolationMode.NEAREST``,
349 +
            ``InterpolationMode.BILINEAR`` and ``InterpolationMode.BICUBIC`` are supported.
350 +
            For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable.
313 351
314 352
    Returns:
315 353
        PIL Image or Tensor: Resized image.
316 354
    """
355 +
    # Backward compatibility with integer value
356 +
    if isinstance(interpolation, int):
357 +
        warnings.warn(
358 +
            "Argument interpolation should be of type InterpolationMode instead of int. "
359 +
            "Please, use InterpolationMode enum."
360 +
        )
361 +
        interpolation = _interpolation_modes_from_int(interpolation)
362 +
363 +
    if not isinstance(interpolation, InterpolationMode):
364 +
        raise TypeError("Argument interpolation should be a InterpolationMode")
365 +
317 366
    if not isinstance(img, torch.Tensor):
318 -
        return F_pil.resize(img, size=size, interpolation=interpolation)
367 +
        pil_interpolation = pil_modes_mapping[interpolation]
368 +
        return F_pil.resize(img, size=size, interpolation=pil_interpolation)
319 369
320 -
    return F_t.resize(img, size=size, interpolation=interpolation)
370 +
    return F_t.resize(img, size=size, interpolation=interpolation.value)
321 371
322 372
323 373
def scale(*args, **kwargs):
@@ -424,7 +474,8 @@
Loading
424 474
425 475
426 476
def resized_crop(
427 -
        img: Tensor, top: int, left: int, height: int, width: int, size: List[int], interpolation: int = Image.BILINEAR
477 +
        img: Tensor, top: int, left: int, height: int, width: int, size: List[int],
478 +
        interpolation: InterpolationMode = InterpolationMode.BILINEAR
428 479
) -> Tensor:
429 480
    """Crop the given image and resize it to desired size.
430 481
    The image can be a PIL Image or a Tensor, in which case it is expected
@@ -439,9 +490,12 @@
Loading
439 490
        height (int): Height of the crop box.
440 491
        width (int): Width of the crop box.
441 492
        size (sequence or int): Desired output size. Same semantics as ``resize``.
442 -
        interpolation (int, optional): Desired interpolation enum defined by `filters`_.
443 -
            Default is ``PIL.Image.BILINEAR``. If input is Tensor, only ``PIL.Image.NEAREST``, ``PIL.Image.BILINEAR``
444 -
            and ``PIL.Image.BICUBIC`` are supported.
493 +
        interpolation (InterpolationMode): Desired interpolation enum defined by
494 +
            :class:`torchvision.transforms.InterpolationMode`.
495 +
            Default is ``InterpolationMode.BILINEAR``. If input is Tensor, only ``InterpolationMode.NEAREST``,
496 +
            ``InterpolationMode.BILINEAR`` and ``InterpolationMode.BICUBIC`` are supported.
497 +
            For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable.
498 +
445 499
    Returns:
446 500
        PIL Image or Tensor: Cropped image.
447 501
    """
@@ -502,7 +556,7 @@
Loading
502 556
        img: Tensor,
503 557
        startpoints: List[List[int]],
504 558
        endpoints: List[List[int]],
505 -
        interpolation: int = 2,
559 +
        interpolation: InterpolationMode = InterpolationMode.BILINEAR,
506 560
        fill: Optional[int] = None
507 561
) -> Tensor:
508 562
    """Perform perspective transform of the given image.
@@ -515,8 +569,10 @@
Loading
515 569
            ``[top-left, top-right, bottom-right, bottom-left]`` of the original image.
516 570
        endpoints (list of list of ints): List containing four lists of two integers corresponding to four corners
517 571
            ``[top-left, top-right, bottom-right, bottom-left]`` of the transformed image.
518 -
        interpolation (int): Interpolation type. If input is Tensor, only ``PIL.Image.NEAREST`` and
519 -
            ``PIL.Image.BILINEAR`` are supported. Default, ``PIL.Image.BILINEAR`` for PIL images and Tensors.
572 +
        interpolation (InterpolationMode): Desired interpolation enum defined by
573 +
            :class:`torchvision.transforms.InterpolationMode`. Default is ``InterpolationMode.BILINEAR``.
574 +
            If input is Tensor, only ``InterpolationMode.NEAREST``, ``InterpolationMode.BILINEAR`` are supported.
575 +
            For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable.
520 576
        fill (n-tuple or int or float): Pixel fill value for area outside the rotated
521 577
            image. If int or float, the value is used for all bands respectively.
522 578
            This option is only available for ``pillow>=5.0.0``. This option is not supported for Tensor
@@ -528,10 +584,22 @@
Loading
528 584
529 585
    coeffs = _get_perspective_coeffs(startpoints, endpoints)
530 586
587 +
    # Backward compatibility with integer value
588 +
    if isinstance(interpolation, int):
589 +
        warnings.warn(
590 +
            "Argument interpolation should be of type InterpolationMode instead of int. "
591 +
            "Please, use InterpolationMode enum."
592 +
        )
593 +
        interpolation = _interpolation_modes_from_int(interpolation)
594 +
595 +
    if not isinstance(interpolation, InterpolationMode):
596 +
        raise TypeError("Argument interpolation should be a InterpolationMode")
597 +
531 598
    if not isinstance(img, torch.Tensor):
532 -
        return F_pil.perspective(img, coeffs, interpolation=interpolation, fill=fill)
599 +
        pil_interpolation = pil_modes_mapping[interpolation]
600 +
        return F_pil.perspective(img, coeffs, interpolation=pil_interpolation, fill=fill)
533 601
534 -
    return F_t.perspective(img, coeffs, interpolation=interpolation, fill=fill)
602 +
    return F_t.perspective(img, coeffs, interpolation=interpolation.value, fill=fill)
535 603
536 604
537 605
def vflip(img: Tensor) -> Tensor:
@@ -801,8 +869,9 @@
Loading
801 869
802 870
803 871
def rotate(
804 -
        img: Tensor, angle: float, resample: int = 0, expand: bool = False,
805 -
        center: Optional[List[int]] = None, fill: Optional[int] = None
872 +
        img: Tensor, angle: float, interpolation: InterpolationMode = InterpolationMode.NEAREST,
873 +
        expand: bool = False, center: Optional[List[int]] = None,
874 +
        fill: Optional[int] = None, resample: Optional[int] = None
806 875
) -> Tensor:
807 876
    """Rotate the image by angle.
808 877
    The image can be a PIL Image or a Tensor, in which case it is expected
@@ -811,9 +880,10 @@
Loading
811 880
    Args:
812 881
        img (PIL Image or Tensor): image to be rotated.
813 882
        angle (float or int): rotation angle value in degrees, counter-clockwise.
814 -
        resample (``PIL.Image.NEAREST`` or ``PIL.Image.BILINEAR`` or ``PIL.Image.BICUBIC``, optional):
815 -
            An optional resampling filter. See `filters`_ for more information.
816 -
            If omitted, or if the image has mode "1" or "P", it is set to ``PIL.Image.NEAREST``.
883 +
        interpolation (InterpolationMode): Desired interpolation enum defined by
884 +
            :class:`torchvision.transforms.InterpolationMode`. Default is ``InterpolationMode.NEAREST``.
885 +
            If input is Tensor, only ``InterpolationMode.NEAREST``, ``InterpolationMode.BILINEAR`` are supported.
886 +
            For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable.
817 887
        expand (bool, optional): Optional expansion flag.
818 888
            If true, expands the output image to make it large enough to hold the entire rotated image.
819 889
            If false or omitted, make the output image the same size as the input image.
@@ -825,6 +895,8 @@
Loading
825 895
            Defaults to 0 for all bands. This option is only available for ``pillow>=5.2.0``.
826 896
            This option is not supported for Tensor input. Fill value for the area outside the transform in the output
827 897
            image is always 0.
898 +
        resample (int, optional): deprecated argument and will be removed since v0.10.0.
899 +
            Please use `arg`:interpolation: instead.
828 900
829 901
    Returns:
830 902
        PIL Image or Tensor: Rotated image.
@@ -832,14 +904,32 @@
Loading
832 904
    .. _filters: https://pillow.readthedocs.io/en/latest/handbook/concepts.html#filters
833 905
834 906
    """
907 +
    if resample is not None:
908 +
        warnings.warn(
909 +
            "Argument resample is deprecated and will be removed since v0.10.0. Please, use interpolation instead"
910 +
        )
911 +
        interpolation = _interpolation_modes_from_int(resample)
912 +
913 +
    # Backward compatibility with integer value
914 +
    if isinstance(interpolation, int):
915 +
        warnings.warn(
916 +
            "Argument interpolation should be of type InterpolationMode instead of int. "
917 +
            "Please, use InterpolationMode enum."
918 +
        )
919 +
        interpolation = _interpolation_modes_from_int(interpolation)
920 +
835 921
    if not isinstance(angle, (int, float)):
836 922
        raise TypeError("Argument angle should be int or float")
837 923
838 924
    if center is not None and not isinstance(center, (list, tuple)):
839 925
        raise TypeError("Argument center should be a sequence")
840 926
927 +
    if not isinstance(interpolation, InterpolationMode):
928 +
        raise TypeError("Argument interpolation should be a InterpolationMode")
929 +
841 930
    if not isinstance(img, torch.Tensor):
842 -
        return F_pil.rotate(img, angle=angle, resample=resample, expand=expand, center=center, fill=fill)
931 +
        pil_interpolation = pil_modes_mapping[interpolation]
932 +
        return F_pil.rotate(img, angle=angle, interpolation=pil_interpolation, expand=expand, center=center, fill=fill)
843 933
844 934
    center_f = [0.0, 0.0]
845 935
    if center is not None:
@@ -850,12 +940,13 @@
Loading
850 940
    # due to current incoherence of rotation angle direction between affine and rotate implementations
851 941
    # we need to set -angle.
852 942
    matrix = _get_inverse_affine_matrix(center_f, -angle, [0.0, 0.0], 1.0, [0.0, 0.0])
853 -
    return F_t.rotate(img, matrix=matrix, resample=resample, expand=expand, fill=fill)
943 +
    return F_t.rotate(img, matrix=matrix, interpolation=interpolation.value, expand=expand, fill=fill)
854 944
855 945
856 946
def affine(
857 947
        img: Tensor, angle: float, translate: List[int], scale: float, shear: List[float],
858 -
        resample: int = 0, fillcolor: Optional[int] = None
948 +
        interpolation: InterpolationMode = InterpolationMode.NEAREST, fill: Optional[int] = None,
949 +
        resample: Optional[int] = None, fillcolor: Optional[int] = None
859 950
) -> Tensor:
860 951
    """Apply affine transformation on the image keeping image center invariant.
861 952
    The image can be a PIL Image or a Tensor, in which case it is expected
@@ -869,17 +960,41 @@
Loading
869 960
        shear (float or tuple or list): shear angle value in degrees between -180 to 180, clockwise direction.
870 961
            If a tuple of list is specified, the first value corresponds to a shear parallel to the x axis, while
871 962
            the second value corresponds to a shear parallel to the y axis.
872 -
        resample (``PIL.Image.NEAREST`` or ``PIL.Image.BILINEAR`` or ``PIL.Image.BICUBIC``, optional):
873 -
            An optional resampling filter. See `filters`_ for more information.
874 -
            If omitted, or if the image is PIL Image and has mode "1" or "P", it is set to ``PIL.Image.NEAREST``.
875 -
            If input is Tensor, only ``PIL.Image.NEAREST`` and ``PIL.Image.BILINEAR`` are supported.
876 -
        fillcolor (int): Optional fill color for the area outside the transform in the output image (Pillow>=5.0.0).
963 +
        interpolation (InterpolationMode): Desired interpolation enum defined by
964 +
            :class:`torchvision.transforms.InterpolationMode`. Default is ``InterpolationMode.NEAREST``.
965 +
            If input is Tensor, only ``InterpolationMode.NEAREST``, ``InterpolationMode.BILINEAR`` are supported.
966 +
            For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable.
967 +
        fill (int): Optional fill color for the area outside the transform in the output image (Pillow>=5.0.0).
877 968
            This option is not supported for Tensor input. Fill value for the area outside the transform in the output
878 969
            image is always 0.
970 +
        fillcolor (tuple or int, optional): deprecated argument and will be removed since v0.10.0.
971 +
            Please use `arg`:fill: instead.
972 +
        resample (int, optional): deprecated argument and will be removed since v0.10.0.
973 +
            Please use `arg`:interpolation: instead.
879 974
880 975
    Returns:
881 976
        PIL Image or Tensor: Transformed image.
882 977
    """
978 +
    if resample is not None:
979 +
        warnings.warn(
980 +
            "Argument resample is deprecated and will be removed since v0.10.0. Please, use interpolation instead"
981 +
        )
982 +
        interpolation = _interpolation_modes_from_int(resample)
983 +
984 +
    # Backward compatibility with integer value
985 +
    if isinstance(interpolation, int):
986 +
        warnings.warn(
987 +
            "Argument interpolation should be of type InterpolationMode instead of int. "
988 +
            "Please, use InterpolationMode enum."
989 +
        )
990 +
        interpolation = _interpolation_modes_from_int(interpolation)
991 +
992 +
    if fillcolor is not None:
993 +
        warnings.warn(
994 +
            "Argument fillcolor is deprecated and will be removed since v0.10.0. Please, use fill instead"
995 +
        )
996 +
        fill = fillcolor
997 +
883 998
    if not isinstance(angle, (int, float)):
884 999
        raise TypeError("Argument angle should be int or float")
885 1000
@@ -895,6 +1010,9 @@
Loading
895 1010
    if not isinstance(shear, (numbers.Number, (list, tuple))):
896 1011
        raise TypeError("Shear should be either a single value or a sequence of two values")
897 1012
1013 +
    if not isinstance(interpolation, InterpolationMode):
1014 +
        raise TypeError("Argument interpolation should be a InterpolationMode")
1015 +
898 1016
    if isinstance(angle, int):
899 1017
        angle = float(angle)
900 1018
@@ -920,12 +1038,12 @@
Loading
920 1038
        # otherwise image rotated by 90 degrees is shifted vs output image of torch.rot90 or F_t.affine
921 1039
        center = [img_size[0] * 0.5, img_size[1] * 0.5]
922 1040
        matrix = _get_inverse_affine_matrix(center, angle, translate, scale, shear)
923 -
924 -
        return F_pil.affine(img, matrix=matrix, resample=resample, fillcolor=fillcolor)
1041 +
        pil_interpolation = pil_modes_mapping[interpolation]
1042 +
        return F_pil.affine(img, matrix=matrix, interpolation=pil_interpolation, fill=fill)
925 1043
926 1044
    translate_f = [1.0 * t for t in translate]
927 1045
    matrix = _get_inverse_affine_matrix([0.0, 0.0], angle, translate_f, scale, shear)
928 -
    return F_t.affine(img, matrix=matrix, resample=resample, fillcolor=fillcolor)
1046 +
    return F_t.affine(img, matrix=matrix, interpolation=interpolation.value, fill=fill)
929 1047
930 1048
931 1049
@torch.jit.unused

@@ -757,7 +757,7 @@
Loading
757 757
    return img
758 758
759 759
760 -
def resize(img: Tensor, size: List[int], interpolation: int = 2) -> Tensor:
760 +
def resize(img: Tensor, size: List[int], interpolation: str = "bilinear") -> Tensor:
761 761
    r"""PRIVATE METHOD. Resize the input Tensor to the given size.
762 762
763 763
    .. warning::
@@ -774,8 +774,8 @@
Loading
774 774
            :math:`\left(\text{size} \times \frac{\text{height}}{\text{width}}, \text{size}\right)`.
775 775
            In torchscript mode padding as a single int is not supported, use a tuple or
776 776
            list of length 1: ``[size, ]``.
777 -
        interpolation (int, optional): Desired interpolation. Default is bilinear (=2). Other supported values:
778 -
            nearest(=0) and bicubic(=3).
777 +
        interpolation (str): Desired interpolation. Default is "bilinear". Other supported values:
778 +
            "nearest" and "bicubic".
779 779
780 780
    Returns:
781 781
        Tensor: Resized image.
@@ -785,16 +785,10 @@
Loading
785 785
786 786
    if not isinstance(size, (int, tuple, list)):
787 787
        raise TypeError("Got inappropriate size arg")
788 -
    if not isinstance(interpolation, int):
788 +
    if not isinstance(interpolation, str):
789 789
        raise TypeError("Got inappropriate interpolation arg")
790 790
791 -
    _interpolation_modes = {
792 -
        0: "nearest",
793 -
        2: "bilinear",
794 -
        3: "bicubic",
795 -
    }
796 -
797 -
    if interpolation not in _interpolation_modes:
791 +
    if interpolation not in ["nearest", "bilinear", "bicubic"]:
798 792
        raise ValueError("This interpolation mode is unsupported with Tensor input")
799 793
800 794
    if isinstance(size, tuple):
@@ -822,16 +816,14 @@
Loading
822 816
        if (w <= h and w == size_w) or (h <= w and h == size_h):
823 817
            return img
824 818
825 -
    mode = _interpolation_modes[interpolation]
826 -
827 819
    img, need_cast, need_squeeze, out_dtype = _cast_squeeze_in(img, [torch.float32, torch.float64])
828 820
829 821
    # Define align_corners to avoid warnings
830 -
    align_corners = False if mode in ["bilinear", "bicubic"] else None
822 +
    align_corners = False if interpolation in ["bilinear", "bicubic"] else None
831 823
832 -
    img = interpolate(img, size=[size_h, size_w], mode=mode, align_corners=align_corners)
824 +
    img = interpolate(img, size=[size_h, size_w], mode=interpolation, align_corners=align_corners)
833 825
834 -
    if mode == "bicubic" and out_dtype == torch.uint8:
826 +
    if interpolation == "bicubic" and out_dtype == torch.uint8:
835 827
        img = img.clamp(min=0, max=255)
836 828
837 829
    img = _cast_squeeze_out(img, need_cast=need_cast, need_squeeze=need_squeeze, out_dtype=out_dtype)
@@ -842,9 +834,9 @@
Loading
842 834
def _assert_grid_transform_inputs(
843 835
        img: Tensor,
844 836
        matrix: Optional[List[float]],
845 -
        resample: int,
846 -
        fillcolor: Optional[int],
847 -
        _interpolation_modes: Dict[int, str],
837 +
        interpolation: str,
838 +
        fill: Optional[int],
839 +
        supported_interpolation_modes: List[str],
848 840
        coeffs: Optional[List[float]] = None,
849 841
):
850 842
    if not (isinstance(img, torch.Tensor) and _is_tensor_a_torch_image(img)):
@@ -859,11 +851,11 @@
Loading
859 851
    if coeffs is not None and len(coeffs) != 8:
860 852
        raise ValueError("Argument coeffs should have 8 float values")
861 853
862 -
    if fillcolor is not None:
863 -
        warnings.warn("Argument fill/fillcolor is not supported for Tensor input. Fill value is zero")
854 +
    if fill is not None and not (isinstance(fill, (int, float)) and fill == 0):
855 +
        warnings.warn("Argument fill is not supported for Tensor input. Fill value is zero")
864 856
865 -
    if resample not in _interpolation_modes:
866 -
        raise ValueError("Resampling mode '{}' is unsupported with Tensor input".format(resample))
857 +
    if interpolation not in supported_interpolation_modes:
858 +
        raise ValueError("Interpolation mode '{}' is unsupported with Tensor input".format(interpolation))
867 859
868 860
869 861
def _cast_squeeze_in(img: Tensor, req_dtypes: List[torch.dtype]) -> Tuple[Tensor, bool, bool, torch.dtype]:
@@ -931,7 +923,7 @@
Loading
931 923
932 924
933 925
def affine(
934 -
        img: Tensor, matrix: List[float], resample: int = 0, fillcolor: Optional[int] = None
926 +
        img: Tensor, matrix: List[float], interpolation: str = "nearest", fill: Optional[int] = None
935 927
) -> Tensor:
936 928
    """PRIVATE METHOD. Apply affine transformation on the Tensor image keeping image center invariant.
937 929
@@ -943,28 +935,21 @@
Loading
943 935
    Args:
944 936
        img (Tensor): image to be rotated.
945 937
        matrix (list of floats): list of 6 float values representing inverse matrix for affine transformation.
946 -
        resample (int, optional): An optional resampling filter. Default is nearest (=0). Other supported values:
947 -
            bilinear(=2).
948 -
        fillcolor (int, optional): this option is not supported for Tensor input. Fill value for the area outside the
938 +
        interpolation (str): An optional resampling filter. Default is "nearest". Other supported values: "bilinear".
939 +
        fill (int, optional): this option is not supported for Tensor input. Fill value for the area outside the
949 940
            transform in the output image is always 0.
950 941
951 942
    Returns:
952 943
        Tensor: Transformed image.
953 944
    """
954 -
    _interpolation_modes = {
955 -
        0: "nearest",
956 -
        2: "bilinear",
957 -
    }
958 -
959 -
    _assert_grid_transform_inputs(img, matrix, resample, fillcolor, _interpolation_modes)
945 +
    _assert_grid_transform_inputs(img, matrix, interpolation, fill, ["nearest", "bilinear"])
960 946
961 947
    dtype = img.dtype if torch.is_floating_point(img) else torch.float32
962 948
    theta = torch.tensor(matrix, dtype=dtype, device=img.device).reshape(1, 2, 3)
963 949
    shape = img.shape
964 950
    # grid will be generated on the same device as theta and img
965 951
    grid = _gen_affine_grid(theta, w=shape[-1], h=shape[-2], ow=shape[-1], oh=shape[-2])
966 -
    mode = _interpolation_modes[resample]
967 -
    return _apply_grid_transform(img, grid, mode)
952 +
    return _apply_grid_transform(img, grid, interpolation)
968 953
969 954
970 955
def _compute_output_size(matrix: List[float], w: int, h: int) -> Tuple[int, int]:
@@ -993,7 +978,8 @@
Loading
993 978
994 979
995 980
def rotate(
996 -
        img: Tensor, matrix: List[float], resample: int = 0, expand: bool = False, fill: Optional[int] = None
981 +
    img: Tensor, matrix: List[float], interpolation: str = "nearest",
982 +
    expand: bool = False, fill: Optional[int] = None
997 983
) -> Tensor:
998 984
    """PRIVATE METHOD. Rotate the Tensor image by angle.
999 985
@@ -1006,8 +992,7 @@
Loading
1006 992
        img (Tensor): image to be rotated.
1007 993
        matrix (list of floats): list of 6 float values representing inverse matrix for rotation transformation.
1008 994
            Translation part (``matrix[2]`` and ``matrix[5]``) should be in pixel coordinates.
1009 -
        resample (int, optional): An optional resampling filter. Default is nearest (=0). Other supported values:
1010 -
            bilinear(=2).
995 +
        interpolation (str): An optional resampling filter. Default is "nearest". Other supported values: "bilinear".
1011 996
        expand (bool, optional): Optional expansion flag.
1012 997
            If true, expands the output image to make it large enough to hold the entire rotated image.
1013 998
            If false or omitted, make the output image the same size as the input image.
@@ -1021,21 +1006,14 @@
Loading
1021 1006
    .. _filters: https://pillow.readthedocs.io/en/latest/handbook/concepts.html#filters
1022 1007
1023 1008
    """
1024 -
    _interpolation_modes = {
1025 -
        0: "nearest",
1026 -
        2: "bilinear",
1027 -
    }
1028 -
1029 -
    _assert_grid_transform_inputs(img, matrix, resample, fill, _interpolation_modes)
1009 +
    _assert_grid_transform_inputs(img, matrix, interpolation, fill, ["nearest", "bilinear"])
1030 1010
    w, h = img.shape[-1], img.shape[-2]
1031 1011
    ow, oh = _compute_output_size(matrix, w, h) if expand else (w, h)
1032 1012
    dtype = img.dtype if torch.is_floating_point(img) else torch.float32
1033 1013
    theta = torch.tensor(matrix, dtype=dtype, device=img.device).reshape(1, 2, 3)
1034 1014
    # grid will be generated on the same device as theta and img
1035 1015
    grid = _gen_affine_grid(theta, w=w, h=h, ow=ow, oh=oh)
1036 -
    mode = _interpolation_modes[resample]
1037 -
1038 -
    return _apply_grid_transform(img, grid, mode)
1016 +
    return _apply_grid_transform(img, grid, interpolation)
1039 1017
1040 1018
1041 1019
def _perspective_grid(coeffs: List[float], ow: int, oh: int, dtype: torch.dtype, device: torch.device):
@@ -1072,7 +1050,7 @@
Loading
1072 1050
1073 1051
1074 1052
def perspective(
1075 -
        img: Tensor, perspective_coeffs: List[float], interpolation: int = 2, fill: Optional[int] = None
1053 +
        img: Tensor, perspective_coeffs: List[float], interpolation: str = "bilinear", fill: Optional[int] = None
1076 1054
) -> Tensor:
1077 1055
    """PRIVATE METHOD. Perform perspective transform of the given Tensor image.
1078 1056
@@ -1084,7 +1062,7 @@
Loading
1084 1062
    Args:
1085 1063
        img (Tensor): Image to be transformed.
1086 1064
        perspective_coeffs (list of float): perspective transformation coefficients.
1087 -
        interpolation (int): Interpolation type. Default, ``PIL.Image.BILINEAR``.
1065 +
        interpolation (str): Interpolation type. Default, "bilinear".
1088 1066
        fill (n-tuple or int or float): this option is not supported for Tensor input. Fill value for the area
1089 1067
            outside the transform in the output image is always 0.
1090 1068
@@ -1094,26 +1072,19 @@
Loading
1094 1072
    if not (isinstance(img, torch.Tensor) and _is_tensor_a_torch_image(img)):
1095 1073
        raise TypeError('Input img should be Tensor Image')
1096 1074
1097 -
    _interpolation_modes = {
1098 -
        0: "nearest",
1099 -
        2: "bilinear",
1100 -
    }
1101 -
1102 1075
    _assert_grid_transform_inputs(
1103 1076
        img,
1104 1077
        matrix=None,
1105 -
        resample=interpolation,
1106 -
        fillcolor=fill,
1107 -
        _interpolation_modes=_interpolation_modes,
1078 +
        interpolation=interpolation,
1079 +
        fill=fill,
1080 +
        supported_interpolation_modes=["nearest", "bilinear"],
1108 1081
        coeffs=perspective_coeffs
1109 1082
    )
1110 1083
1111 1084
    ow, oh = img.shape[-1], img.shape[-2]
1112 1085
    dtype = img.dtype if torch.is_floating_point(img) else torch.float32
1113 1086
    grid = _perspective_grid(perspective_coeffs, ow=ow, oh=oh, dtype=dtype, device=img.device)
1114 -
    mode = _interpolation_modes[interpolation]
1115 -
1116 -
    return _apply_grid_transform(img, grid, mode)
1087 +
    return _apply_grid_transform(img, grid, interpolation)
1117 1088
1118 1089
1119 1090
def _get_gaussian_kernel1d(kernel_size: int, sigma: float) -> Tensor:

@@ -6,7 +6,6 @@
Loading
6 6
from typing import Tuple, List, Optional
7 7
8 8
import torch
9 -
from PIL import Image
10 9
from torch import Tensor
11 10
12 11
try:
@@ -15,21 +14,14 @@
Loading
15 14
    accimage = None
16 15
17 16
from . import functional as F
17 +
from .functional import InterpolationMode, _interpolation_modes_from_int
18 +
18 19
19 20
__all__ = ["Compose", "ToTensor", "PILToTensor", "ConvertImageDtype", "ToPILImage", "Normalize", "Resize", "Scale",
20 21
           "CenterCrop", "Pad", "Lambda", "RandomApply", "RandomChoice", "RandomOrder", "RandomCrop",
21 22
           "RandomHorizontalFlip", "RandomVerticalFlip", "RandomResizedCrop", "RandomSizedCrop", "FiveCrop", "TenCrop",
22 23
           "LinearTransformation", "ColorJitter", "RandomRotation", "RandomAffine", "Grayscale", "RandomGrayscale",
23 -
           "RandomPerspective", "RandomErasing", "GaussianBlur"]
24 -
25 -
_pil_interpolation_to_str = {
26 -
    Image.NEAREST: 'PIL.Image.NEAREST',
27 -
    Image.BILINEAR: 'PIL.Image.BILINEAR',
28 -
    Image.BICUBIC: 'PIL.Image.BICUBIC',
29 -
    Image.LANCZOS: 'PIL.Image.LANCZOS',
30 -
    Image.HAMMING: 'PIL.Image.HAMMING',
31 -
    Image.BOX: 'PIL.Image.BOX',
32 -
}
24 +
           "RandomPerspective", "RandomErasing", "GaussianBlur", "InterpolationMode"]
33 25
34 26
35 27
class Compose:
@@ -242,18 +234,30 @@
Loading
242 234
            (size * height / width, size).
243 235
            In torchscript mode padding as single int is not supported, use a tuple or
244 236
            list of length 1: ``[size, ]``.
245 -
        interpolation (int, optional): Desired interpolation enum defined by `filters`_.
246 -
            Default is ``PIL.Image.BILINEAR``. If input is Tensor, only ``PIL.Image.NEAREST``, ``PIL.Image.BILINEAR``
247 -
            and ``PIL.Image.BICUBIC`` are supported.
237 +
        interpolation (InterpolationMode): Desired interpolation enum defined by
238 +
            :class:`torchvision.transforms.InterpolationMode`. Default is ``InterpolationMode.BILINEAR``.
239 +
            If input is Tensor, only ``InterpolationMode.NEAREST``, ``InterpolationMode.BILINEAR`` and
240 +
            ``InterpolationMode.BICUBIC`` are supported.
241 +
            For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable.
242 +
248 243
    """
249 244
250 -
    def __init__(self, size, interpolation=Image.BILINEAR):
245 +
    def __init__(self, size, interpolation=InterpolationMode.BILINEAR):
251 246
        super().__init__()
252 247
        if not isinstance(size, (int, Sequence)):
253 248
            raise TypeError("Size should be int or sequence. Got {}".format(type(size)))
254 249
        if isinstance(size, Sequence) and len(size) not in (1, 2):
255 250
            raise ValueError("If size is a sequence, it should have 1 or 2 values")
256 251
        self.size = size
252 +
253 +
        # Backward compatibility with integer value
254 +
        if isinstance(interpolation, int):
255 +
            warnings.warn(
256 +
                "Argument interpolation should be of type InterpolationMode instead of int. "
257 +
                "Please, use InterpolationMode enum."
258 +
            )
259 +
            interpolation = _interpolation_modes_from_int(interpolation)
260 +
257 261
        self.interpolation = interpolation
258 262
259 263
    def forward(self, img):
@@ -267,7 +271,7 @@
Loading
267 271
        return F.resize(img, self.size, self.interpolation)
268 272
269 273
    def __repr__(self):
270 -
        interpolate_str = _pil_interpolation_to_str[self.interpolation]
274 +
        interpolate_str = self.interpolation.value
271 275
        return self.__class__.__name__ + '(size={0}, interpolation={1})'.format(self.size, interpolate_str)
272 276
273 277
@@ -659,18 +663,28 @@
Loading
659 663
        distortion_scale (float): argument to control the degree of distortion and ranges from 0 to 1.
660 664
            Default is 0.5.
661 665
        p (float): probability of the image being transformed. Default is 0.5.
662 -
        interpolation (int): Interpolation type. If input is Tensor, only ``PIL.Image.NEAREST`` and
663 -
            ``PIL.Image.BILINEAR`` are supported. Default, ``PIL.Image.BILINEAR`` for PIL images and Tensors.
666 +
        interpolation (InterpolationMode): Desired interpolation enum defined by
667 +
            :class:`torchvision.transforms.InterpolationMode`. Default is ``InterpolationMode.BILINEAR``.
668 +
            If input is Tensor, only ``InterpolationMode.NEAREST``, ``InterpolationMode.BILINEAR`` are supported.
669 +
            For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable.
664 670
        fill (n-tuple or int or float): Pixel fill value for area outside the rotated
665 671
            image. If int or float, the value is used for all bands respectively. Default is 0.
666 672
            This option is only available for ``pillow>=5.0.0``. This option is not supported for Tensor
667 673
            input. Fill value for the area outside the transform in the output image is always 0.
668 -
669 674
    """
670 675
671 -
    def __init__(self, distortion_scale=0.5, p=0.5, interpolation=Image.BILINEAR, fill=0):
676 +
    def __init__(self, distortion_scale=0.5, p=0.5, interpolation=InterpolationMode.BILINEAR, fill=0):
672 677
        super().__init__()
673 678
        self.p = p
679 +
680 +
        # Backward compatibility with integer value
681 +
        if isinstance(interpolation, int):
682 +
            warnings.warn(
683 +
                "Argument interpolation should be of type InterpolationMode instead of int. "
684 +
                "Please, use InterpolationMode enum."
685 +
            )
686 +
            interpolation = _interpolation_modes_from_int(interpolation)
687 +
674 688
        self.interpolation = interpolation
675 689
        self.distortion_scale = distortion_scale
676 690
        self.fill = fill
@@ -744,12 +758,15 @@
Loading
744 758
            made. If provided a tuple or list of length 1, it will be interpreted as (size[0], size[0]).
745 759
        scale (tuple of float): scale range of the cropped image before resizing, relatively to the origin image.
746 760
        ratio (tuple of float): aspect ratio range of the cropped image before resizing.
747 -
        interpolation (int): Desired interpolation enum defined by `filters`_.
748 -
            Default is ``PIL.Image.BILINEAR``. If input is Tensor, only ``PIL.Image.NEAREST``, ``PIL.Image.BILINEAR``
749 -
            and ``PIL.Image.BICUBIC`` are supported.
761 +
        interpolation (InterpolationMode): Desired interpolation enum defined by
762 +
            :class:`torchvision.transforms.InterpolationMode`. Default is ``InterpolationMode.BILINEAR``.
763 +
            If input is Tensor, only ``InterpolationMode.NEAREST``, ``InterpolationMode.BILINEAR`` and
764 +
            ``InterpolationMode.BICUBIC`` are supported.
765 +
            For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable.
766 +
750 767
    """
751 768
752 -
    def __init__(self, size, scale=(0.08, 1.0), ratio=(3. / 4., 4. / 3.), interpolation=Image.BILINEAR):
769 +
    def __init__(self, size, scale=(0.08, 1.0), ratio=(3. / 4., 4. / 3.), interpolation=InterpolationMode.BILINEAR):
753 770
        super().__init__()
754 771
        self.size = _setup_size(size, error_msg="Please provide only two dimensions (h, w) for size.")
755 772
@@ -760,6 +777,14 @@
Loading
760 777
        if (scale[0] > scale[1]) or (ratio[0] > ratio[1]):
761 778
            warnings.warn("Scale and ratio should be of kind (min, max)")
762 779
780 +
        # Backward compatibility with integer value
781 +
        if isinstance(interpolation, int):
782 +
            warnings.warn(
783 +
                "Argument interpolation should be of type InterpolationMode instead of int. "
784 +
                "Please, use InterpolationMode enum."
785 +
            )
786 +
            interpolation = _interpolation_modes_from_int(interpolation)
787 +
763 788
        self.interpolation = interpolation
764 789
        self.scale = scale
765 790
        self.ratio = ratio
@@ -824,7 +849,7 @@
Loading
824 849
        return F.resized_crop(img, i, j, h, w, self.size, self.interpolation)
825 850
826 851
    def __repr__(self):
827 -
        interpolate_str = _pil_interpolation_to_str[self.interpolation]
852 +
        interpolate_str = self.interpolation.value
828 853
        format_string = self.__class__.__name__ + '(size={0}'.format(self.size)
829 854
        format_string += ', scale={0}'.format(tuple(round(s, 4) for s in self.scale))
830 855
        format_string += ', ratio={0}'.format(tuple(round(r, 4) for r in self.ratio))
@@ -1122,9 +1147,10 @@
Loading
1122 1147
        degrees (sequence or float or int): Range of degrees to select from.
1123 1148
            If degrees is a number instead of sequence like (min, max), the range of degrees
1124 1149
            will be (-degrees, +degrees).
1125 -
        resample (int, optional): An optional resampling filter. See `filters`_ for more information.
1126 -
            If omitted, or if the image has mode "1" or "P", it is set to PIL.Image.NEAREST.
1127 -
            If input is Tensor, only ``PIL.Image.NEAREST`` and ``PIL.Image.BILINEAR`` are supported.
1150 +
        interpolation (InterpolationMode): Desired interpolation enum defined by
1151 +
            :class:`torchvision.transforms.InterpolationMode`. Default is ``InterpolationMode.NEAREST``.
1152 +
            If input is Tensor, only ``InterpolationMode.NEAREST``, ``InterpolationMode.BILINEAR`` are supported.
1153 +
            For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable.
1128 1154
        expand (bool, optional): Optional expansion flag.
1129 1155
            If true, expands the output to make it large enough to hold the entire rotated image.
1130 1156
            If false or omitted, make the output image the same size as the input image.
@@ -1136,13 +1162,31 @@
Loading
1136 1162
            Defaults to 0 for all bands. This option is only available for Pillow>=5.2.0.
1137 1163
            This option is not supported for Tensor input. Fill value for the area outside the transform in the output
1138 1164
            image is always 0.
1165 +
        resample (int, optional): deprecated argument and will be removed since v0.10.0.
1166 +
            Please use `arg`:interpolation: instead.
1139 1167
1140 1168
    .. _filters: https://pillow.readthedocs.io/en/latest/handbook/concepts.html#filters
1141 1169
1142 1170
    """
1143 1171
1144 -
    def __init__(self, degrees, resample=False, expand=False, center=None, fill=None):
1172 +
    def __init__(
1173 +
        self, degrees, interpolation=InterpolationMode.NEAREST, expand=False, center=None, fill=None, resample=None
1174 +
    ):
1145 1175
        super().__init__()
1176 +
        if resample is not None:
1177 +
            warnings.warn(
1178 +
                "Argument resample is deprecated and will be removed since v0.10.0. Please, use interpolation instead"
1179 +
            )
1180 +
            interpolation = _interpolation_modes_from_int(resample)
1181 +
1182 +
        # Backward compatibility with integer value
1183 +
        if isinstance(interpolation, int):
1184 +
            warnings.warn(
1185 +
                "Argument interpolation should be of type InterpolationMode instead of int. "
1186 +
                "Please, use InterpolationMode enum."
1187 +
            )
1188 +
            interpolation = _interpolation_modes_from_int(interpolation)
1189 +
1146 1190
        self.degrees = _setup_angle(degrees, name="degrees", req_sizes=(2, ))
1147 1191
1148 1192
        if center is not None:
@@ -1150,7 +1194,7 @@
Loading
1150 1194
1151 1195
        self.center = center
1152 1196
1153 -
        self.resample = resample
1197 +
        self.resample = self.interpolation = interpolation
1154 1198
        self.expand = expand
1155 1199
        self.fill = fill
1156 1200
@@ -1173,11 +1217,12 @@
Loading
1173 1217
            PIL Image or Tensor: Rotated image.
1174 1218
        """
1175 1219
        angle = self.get_params(self.degrees)
1176 -
        return F.rotate(img, angle, self.resample, self.expand, self.center, self.fill)
1220 +
        return F.rotate(img, angle, self.interpolation, self.expand, self.center, self.fill)
1177 1221
1178 1222
    def __repr__(self):
1223 +
        interpolate_str = self.interpolation.value
1179 1224
        format_string = self.__class__.__name__ + '(degrees={0}'.format(self.degrees)
1180 -
        format_string += ', resample={0}'.format(self.resample)
1225 +
        format_string += ', interpolation={0}'.format(interpolate_str)
1181 1226
        format_string += ', expand={0}'.format(self.expand)
1182 1227
        if self.center is not None:
1183 1228
            format_string += ', center={0}'.format(self.center)
@@ -1208,19 +1253,47 @@
Loading
1208 1253
            range (shear[0], shear[1]) will be applied. Else if shear is a tuple or list of 4 values,
1209 1254
            a x-axis shear in (shear[0], shear[1]) and y-axis shear in (shear[2], shear[3]) will be applied.
1210 1255
            Will not apply shear by default.
1211 -
        resample (int, optional): An optional resampling filter. See `filters`_ for more information.
1212 -
            If omitted, or if the image has mode "1" or "P", it is set to ``PIL.Image.NEAREST``.
1213 -
            If input is Tensor, only ``PIL.Image.NEAREST`` and ``PIL.Image.BILINEAR`` are supported.
1214 -
        fillcolor (tuple or int): Optional fill color (Tuple for RGB Image and int for grayscale) for the area
1256 +
        interpolation (InterpolationMode): Desired interpolation enum defined by
1257 +
            :class:`torchvision.transforms.InterpolationMode`. Default is ``InterpolationMode.NEAREST``.
1258 +
            If input is Tensor, only ``InterpolationMode.NEAREST``, ``InterpolationMode.BILINEAR`` are supported.
1259 +
            For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable.
1260 +
        fill (tuple or int): Optional fill color (Tuple for RGB Image and int for grayscale) for the area
1215 1261
            outside the transform in the output image (Pillow>=5.0.0). This option is not supported for Tensor
1216 1262
            input. Fill value for the area outside the transform in the output image is always 0.
1263 +
        fillcolor (tuple or int, optional): deprecated argument and will be removed since v0.10.0.
1264 +
            Please use `arg`:fill: instead.
1265 +
        resample (int, optional): deprecated argument and will be removed since v0.10.0.
1266 +
            Please use `arg`:interpolation: instead.
1217 1267
1218 1268
    .. _filters: https://pillow.readthedocs.io/en/latest/handbook/concepts.html#filters
1219 1269
1220 1270
    """
1221 1271
1222 -
    def __init__(self, degrees, translate=None, scale=None, shear=None, resample=0, fillcolor=0):
1272 +
    def __init__(
1273 +
        self, degrees, translate=None, scale=None, shear=None, interpolation=InterpolationMode.NEAREST, fill=0,
1274 +
        fillcolor=None, resample=None
1275 +
    ):
1223 1276
        super().__init__()
1277 +
        if resample is not None:
1278 +
            warnings.warn(
1279 +
                "Argument resample is deprecated and will be removed since v0.10.0. Please, use interpolation instead"
1280 +
            )
1281 +
            interpolation = _interpolation_modes_from_int(resample)
1282 +
1283 +
        # Backward compatibility with integer value
1284 +
        if isinstance(interpolation, int):
1285 +
            warnings.warn(
1286 +
                "Argument interpolation should be of type InterpolationMode instead of int. "
1287 +
                "Please, use InterpolationMode enum."
1288 +
            )
1289 +
            interpolation = _interpolation_modes_from_int(interpolation)
1290 +
1291 +
        if fillcolor is not None:
1292 +
            warnings.warn(
1293 +
                "Argument fillcolor is deprecated and will be removed since v0.10.0. Please, use fill instead"
1294 +
            )
1295 +
            fill = fillcolor
1296 +
1224 1297
        self.degrees = _setup_angle(degrees, name="degrees", req_sizes=(2, ))
1225 1298
1226 1299
        if translate is not None:
@@ -1242,8 +1315,8 @@
Loading
1242 1315
        else:
1243 1316
            self.shear = shear
1244 1317
1245 -
        self.resample = resample
1246 -
        self.fillcolor = fillcolor
1318 +
        self.resample = self.interpolation = interpolation
1319 +
        self.fillcolor = self.fill = fill
1247 1320
1248 1321
    @staticmethod
1249 1322
    def get_params(
@@ -1294,7 +1367,7 @@
Loading
1294 1367
        img_size = F._get_image_size(img)
1295 1368
1296 1369
        ret = self.get_params(self.degrees, self.translate, self.scale, self.shear, img_size)
1297 -
        return F.affine(img, *ret, resample=self.resample, fillcolor=self.fillcolor)
1370 +
        return F.affine(img, *ret, interpolation=self.interpolation, fill=self.fill)
1298 1371
1299 1372
    def __repr__(self):
1300 1373
        s = '{name}(degrees={degrees}'
@@ -1304,13 +1377,13 @@
Loading
1304 1377
            s += ', scale={scale}'
1305 1378
        if self.shear is not None:
1306 1379
            s += ', shear={shear}'
1307 -
        if self.resample > 0:
1308 -
            s += ', resample={resample}'
1309 -
        if self.fillcolor != 0:
1310 -
            s += ', fillcolor={fillcolor}'
1380 +
        if self.interpolation != InterpolationMode.NEAREST:
1381 +
            s += ', interpolation={interpolation}'
1382 +
        if self.fill != 0:
1383 +
            s += ', fill={fill}'
1311 1384
        s += ')'
1312 1385
        d = dict(self.__dict__)
1313 -
        d['resample'] = _pil_interpolation_to_str[d['resample']]
1386 +
        d['interpolation'] = self.interpolation.value
1314 1387
        return s.format(name=self.__class__.__name__, **d)
1315 1388
1316 1389

@@ -107,21 +107,16 @@
Loading
107 107
            # determine only the foreground
108 108
            foreground_idxs_per_image = matched_idxs_per_image >= 0
109 109
            num_foreground = foreground_idxs_per_image.sum()
110 -
            # no matched_idxs means there were no annotations in this image
111 -
            # TODO: enable support for images without annotations that works on distributed
112 -
            if False:  # matched_idxs_per_image.numel() == 0:
113 -
                gt_classes_target = torch.zeros_like(cls_logits_per_image)
114 -
                valid_idxs_per_image = torch.arange(cls_logits_per_image.shape[0])
115 -
            else:
116 -
                # create the target classification
117 -
                gt_classes_target = torch.zeros_like(cls_logits_per_image)
118 -
                gt_classes_target[
119 -
                    foreground_idxs_per_image,
120 -
                    targets_per_image['labels'][matched_idxs_per_image[foreground_idxs_per_image]]
121 -
                ] = 1.0
122 -
123 -
                # find indices for which anchors should be ignored
124 -
                valid_idxs_per_image = matched_idxs_per_image != self.BETWEEN_THRESHOLDS
110 +
111 +
            # create the target classification
112 +
            gt_classes_target = torch.zeros_like(cls_logits_per_image)
113 +
            gt_classes_target[
114 +
                foreground_idxs_per_image,
115 +
                targets_per_image['labels'][matched_idxs_per_image[foreground_idxs_per_image]]
116 +
            ] = 1.0
117 +
118 +
            # find indices for which anchors should be ignored
119 +
            valid_idxs_per_image = matched_idxs_per_image != self.BETWEEN_THRESHOLDS
125 120
126 121
            # compute the classification loss
127 122
            losses.append(sigmoid_focal_loss(
@@ -191,23 +186,12 @@
Loading
191 186
192 187
        for targets_per_image, bbox_regression_per_image, anchors_per_image, matched_idxs_per_image in \
193 188
                zip(targets, bbox_regression, anchors, matched_idxs):
194 -
            # no matched_idxs means there were no annotations in this image
195 -
            # TODO enable support for images without annotations with distributed support
196 -
            # if matched_idxs_per_image.numel() == 0:
197 -
            #     continue
198 -
199 -
            # get the targets corresponding GT for each proposal
200 -
            # NB: need to clamp the indices because we can have a single
201 -
            # GT in the image, and matched_idxs can be -2, which goes
202 -
            # out of bounds
203 -
            matched_gt_boxes_per_image = targets_per_image['boxes'][matched_idxs_per_image.clamp(min=0)]
204 -
205 189
            # determine only the foreground indices, ignore the rest
206 -
            foreground_idxs_per_image = matched_idxs_per_image >= 0
207 -
            num_foreground = foreground_idxs_per_image.sum()
190 +
            foreground_idxs_per_image = torch.where(matched_idxs_per_image >= 0)[0]
191 +
            num_foreground = foreground_idxs_per_image.numel()
208 192
209 193
            # select only the foreground boxes
210 -
            matched_gt_boxes_per_image = matched_gt_boxes_per_image[foreground_idxs_per_image, :]
194 +
            matched_gt_boxes_per_image = targets_per_image['boxes'][matched_idxs_per_image[foreground_idxs_per_image]]
211 195
            bbox_regression_per_image = bbox_regression_per_image[foreground_idxs_per_image, :]
212 196
            anchors_per_image = anchors_per_image[foreground_idxs_per_image, :]
213 197
@@ -403,7 +387,7 @@
Loading
403 387
        matched_idxs = []
404 388
        for anchors_per_image, targets_per_image in zip(anchors, targets):
405 389
            if targets_per_image['boxes'].numel() == 0:
406 -
                matched_idxs.append(torch.empty((0,), dtype=torch.int32))
390 +
                matched_idxs.append(torch.full((anchors_per_image.size(0),), -1, dtype=torch.int64))
407 391
                continue
408 392
409 393
            match_quality_matrix = box_ops.box_iou(targets_per_image['boxes'], anchors_per_image)
Files Coverage
torchvision 72.85%
Project Totals (99 files) 72.85%
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading