Unverified Commit b030e936 authored by Nicolas Hug's avatar Nicolas Hug Committed by GitHub
Browse files

Change default of antialias parameter from None to 'warn' (#7160)


Co-authored-by: default avatarPhilip Meier <github.pmeier@posteo.de>
Co-authored-by: default avatarvfdev <vfdev.5@gmail.com>
parent 8fdaeb03
...@@ -81,9 +81,10 @@ plot(img1_batch) ...@@ -81,9 +81,10 @@ plot(img1_batch)
######################### #########################
# The RAFT model accepts RGB images. We first get the frames from # The RAFT model accepts RGB images. We first get the frames from
# :func:`~torchvision.io.read_video` and resize them to ensure their # :func:`~torchvision.io.read_video` and resize them to ensure their dimensions
# dimensions are divisible by 8. Then we use the transforms bundled into the # are divisible by 8. Note that we explicitly use ``antialias=False``, because
# weights in order to preprocess the input and rescale its values to the # this is how those models were trained. Then we use the transforms bundled into
# the weights in order to preprocess the input and rescale its values to the
# required ``[-1, 1]`` interval. # required ``[-1, 1]`` interval.
from torchvision.models.optical_flow import Raft_Large_Weights from torchvision.models.optical_flow import Raft_Large_Weights
...@@ -93,8 +94,8 @@ transforms = weights.transforms() ...@@ -93,8 +94,8 @@ transforms = weights.transforms()
def preprocess(img1_batch, img2_batch): def preprocess(img1_batch, img2_batch):
img1_batch = F.resize(img1_batch, size=[520, 960]) img1_batch = F.resize(img1_batch, size=[520, 960], antialias=False)
img2_batch = F.resize(img2_batch, size=[520, 960]) img2_batch = F.resize(img2_batch, size=[520, 960], antialias=False)
return transforms(img1_batch, img2_batch) return transforms(img1_batch, img2_batch)
......
...@@ -455,7 +455,11 @@ class Resize(torch.nn.Module): ...@@ -455,7 +455,11 @@ class Resize(torch.nn.Module):
INTERP_MODE = self._interpolation_mode_strategy() INTERP_MODE = self._interpolation_mode_strategy()
for img in images: for img in images:
resized_images += (F.resize(img, self.resize_size, interpolation=INTERP_MODE),) # We hard-code antialias=False to preserve results after we changed
# its default from None to True (see
# https://github.com/pytorch/vision/pull/7160)
# TODO: we could re-train the stereo models with antialias=True?
resized_images += (F.resize(img, self.resize_size, interpolation=INTERP_MODE, antialias=False),)
for dsp in disparities: for dsp in disparities:
if dsp is not None: if dsp is not None:
......
...@@ -196,8 +196,12 @@ class RandomResizeAndCrop(torch.nn.Module): ...@@ -196,8 +196,12 @@ class RandomResizeAndCrop(torch.nn.Module):
if torch.rand(1).item() < self.resize_prob: if torch.rand(1).item() < self.resize_prob:
# rescale the images # rescale the images
img1 = F.resize(img1, size=(new_h, new_w)) # We hard-code antialias=False to preserve results after we changed
img2 = F.resize(img2, size=(new_h, new_w)) # its default from None to True (see
# https://github.com/pytorch/vision/pull/7160)
# TODO: we could re-train the OF models with antialias=True?
img1 = F.resize(img1, size=(new_h, new_w), antialias=False)
img2 = F.resize(img2, size=(new_h, new_w), antialias=False)
if valid_flow_mask is None: if valid_flow_mask is None:
flow = F.resize(flow, size=(new_h, new_w)) flow = F.resize(flow, size=(new_h, new_w))
flow = flow * torch.tensor([scale_x, scale_y])[:, None, None] flow = flow * torch.tensor([scale_x, scale_y])[:, None, None]
......
...@@ -15,7 +15,11 @@ class VideoClassificationPresetTrain: ...@@ -15,7 +15,11 @@ class VideoClassificationPresetTrain:
): ):
trans = [ trans = [
transforms.ConvertImageDtype(torch.float32), transforms.ConvertImageDtype(torch.float32),
transforms.Resize(resize_size), # We hard-code antialias=False to preserve results after we changed
# its default from None to True (see
# https://github.com/pytorch/vision/pull/7160)
# TODO: we could re-train the video models with antialias=True?
transforms.Resize(resize_size, antialias=False),
] ]
if hflip_prob > 0: if hflip_prob > 0:
trans.append(transforms.RandomHorizontalFlip(hflip_prob)) trans.append(transforms.RandomHorizontalFlip(hflip_prob))
...@@ -31,7 +35,11 @@ class VideoClassificationPresetEval: ...@@ -31,7 +35,11 @@ class VideoClassificationPresetEval:
self.transforms = transforms.Compose( self.transforms = transforms.Compose(
[ [
transforms.ConvertImageDtype(torch.float32), transforms.ConvertImageDtype(torch.float32),
transforms.Resize(resize_size), # We hard-code antialias=False to preserve results after we changed
# its default from None to True (see
# https://github.com/pytorch/vision/pull/7160)
# TODO: we could re-train the video models with antialias=True?
transforms.Resize(resize_size, antialias=False),
transforms.Normalize(mean=mean, std=std), transforms.Normalize(mean=mean, std=std),
transforms.CenterCrop(crop_size), transforms.CenterCrop(crop_size),
ConvertBCHWtoCBHW(), ConvertBCHWtoCBHW(),
......
...@@ -2,6 +2,7 @@ import colorsys ...@@ -2,6 +2,7 @@ import colorsys
import itertools import itertools
import math import math
import os import os
import warnings
from functools import partial from functools import partial
from typing import Sequence from typing import Sequence
...@@ -483,8 +484,8 @@ def test_resize(device, dt, size, max_size, interpolation): ...@@ -483,8 +484,8 @@ def test_resize(device, dt, size, max_size, interpolation):
tensor = tensor.to(dt) tensor = tensor.to(dt)
batch_tensors = batch_tensors.to(dt) batch_tensors = batch_tensors.to(dt)
resized_tensor = F.resize(tensor, size=size, interpolation=interpolation, max_size=max_size) resized_tensor = F.resize(tensor, size=size, interpolation=interpolation, max_size=max_size, antialias=True)
resized_pil_img = F.resize(pil_img, size=size, interpolation=interpolation, max_size=max_size) resized_pil_img = F.resize(pil_img, size=size, interpolation=interpolation, max_size=max_size, antialias=True)
assert resized_tensor.size()[1:] == resized_pil_img.size[::-1] assert resized_tensor.size()[1:] == resized_pil_img.size[::-1]
...@@ -509,10 +510,12 @@ def test_resize(device, dt, size, max_size, interpolation): ...@@ -509,10 +510,12 @@ def test_resize(device, dt, size, max_size, interpolation):
else: else:
script_size = size script_size = size
resize_result = script_fn(tensor, size=script_size, interpolation=interpolation, max_size=max_size) resize_result = script_fn(tensor, size=script_size, interpolation=interpolation, max_size=max_size, antialias=True)
assert_equal(resized_tensor, resize_result) assert_equal(resized_tensor, resize_result)
_test_fn_on_batch(batch_tensors, F.resize, size=script_size, interpolation=interpolation, max_size=max_size) _test_fn_on_batch(
batch_tensors, F.resize, size=script_size, interpolation=interpolation, max_size=max_size, antialias=True
)
@pytest.mark.parametrize("device", cpu_and_gpu()) @pytest.mark.parametrize("device", cpu_and_gpu())
...@@ -547,7 +550,7 @@ def test_resize_antialias(device, dt, size, interpolation): ...@@ -547,7 +550,7 @@ def test_resize_antialias(device, dt, size, interpolation):
tensor = tensor.to(dt) tensor = tensor.to(dt)
resized_tensor = F.resize(tensor, size=size, interpolation=interpolation, antialias=True) resized_tensor = F.resize(tensor, size=size, interpolation=interpolation, antialias=True)
resized_pil_img = F.resize(pil_img, size=size, interpolation=interpolation) resized_pil_img = F.resize(pil_img, size=size, interpolation=interpolation, antialias=True)
assert resized_tensor.size()[1:] == resized_pil_img.size[::-1] assert resized_tensor.size()[1:] == resized_pil_img.size[::-1]
...@@ -596,6 +599,23 @@ def test_assert_resize_antialias(interpolation): ...@@ -596,6 +599,23 @@ def test_assert_resize_antialias(interpolation):
F.resize(tensor, size=(5, 5), interpolation=interpolation, antialias=True) F.resize(tensor, size=(5, 5), interpolation=interpolation, antialias=True)
def test_resize_antialias_default_warning():
img = torch.randint(0, 256, size=(3, 44, 56), dtype=torch.uint8)
match = "The default value of the antialias"
with pytest.warns(UserWarning, match=match):
F.resize(img, size=(20, 20))
with pytest.warns(UserWarning, match=match):
F.resized_crop(img, 0, 0, 10, 10, size=(20, 20))
# For modes that aren't bicubic or bilinear, don't throw a warning
with warnings.catch_warnings():
warnings.simplefilter("error")
F.resize(img, size=(20, 20), interpolation=NEAREST)
F.resized_crop(img, 0, 0, 10, 10, size=(20, 20), interpolation=NEAREST)
@pytest.mark.parametrize("device", cpu_and_gpu()) @pytest.mark.parametrize("device", cpu_and_gpu())
@pytest.mark.parametrize("dt", [torch.float32, torch.float64, torch.float16]) @pytest.mark.parametrize("dt", [torch.float32, torch.float64, torch.float16])
@pytest.mark.parametrize("size", [[10, 7], [10, 42], [42, 7]]) @pytest.mark.parametrize("size", [[10, 7], [10, 42], [42, 7]])
...@@ -924,7 +944,9 @@ def test_resized_crop(device, mode): ...@@ -924,7 +944,9 @@ def test_resized_crop(device, mode):
# 1) resize to the same size, crop to the same size => should be identity # 1) resize to the same size, crop to the same size => should be identity
tensor, _ = _create_data(26, 36, device=device) tensor, _ = _create_data(26, 36, device=device)
out_tensor = F.resized_crop(tensor, top=0, left=0, height=26, width=36, size=[26, 36], interpolation=mode) out_tensor = F.resized_crop(
tensor, top=0, left=0, height=26, width=36, size=[26, 36], interpolation=mode, antialias=True
)
assert_equal(tensor, out_tensor, msg=f"{out_tensor[0, :5, :5]} vs {tensor[0, :5, :5]}") assert_equal(tensor, out_tensor, msg=f"{out_tensor[0, :5, :5]} vs {tensor[0, :5, :5]}")
# 2) resize by half and crop a TL corner # 2) resize by half and crop a TL corner
...@@ -939,7 +961,14 @@ def test_resized_crop(device, mode): ...@@ -939,7 +961,14 @@ def test_resized_crop(device, mode):
batch_tensors = _create_data_batch(26, 36, num_samples=4, device=device) batch_tensors = _create_data_batch(26, 36, num_samples=4, device=device)
_test_fn_on_batch( _test_fn_on_batch(
batch_tensors, F.resized_crop, top=1, left=2, height=20, width=30, size=[10, 15], interpolation=NEAREST batch_tensors,
F.resized_crop,
top=1,
left=2,
height=20,
width=30,
size=[10, 15],
interpolation=NEAREST,
) )
......
...@@ -1050,5 +1050,25 @@ def test_raft(model_fn, scripted): ...@@ -1050,5 +1050,25 @@ def test_raft(model_fn, scripted):
_assert_expected(flow_pred.cpu(), name=model_fn.__name__, atol=1e-2, rtol=1) _assert_expected(flow_pred.cpu(), name=model_fn.__name__, atol=1e-2, rtol=1)
def test_presets_antialias():
img = torch.randint(0, 256, size=(1, 3, 224, 224), dtype=torch.uint8)
match = "The default value of the antialias parameter"
with pytest.warns(UserWarning, match=match):
models.ResNet18_Weights.DEFAULT.transforms()(img)
with pytest.warns(UserWarning, match=match):
models.segmentation.DeepLabV3_ResNet50_Weights.DEFAULT.transforms()(img)
with warnings.catch_warnings():
warnings.simplefilter("error")
models.ResNet18_Weights.DEFAULT.transforms(antialias=True)(img)
models.segmentation.DeepLabV3_ResNet50_Weights.DEFAULT.transforms(antialias=True)(img)
models.detection.FasterRCNN_ResNet50_FPN_Weights.DEFAULT.transforms()(img)
models.video.R3D_18_Weights.DEFAULT.transforms()(img)
models.optical_flow.Raft_Small_Weights.DEFAULT.transforms()(img, img)
if __name__ == "__main__": if __name__ == "__main__":
pytest.main([__file__]) pytest.main([__file__])
import itertools import itertools
import re import re
import warnings
from collections import defaultdict from collections import defaultdict
import numpy as np import numpy as np
...@@ -94,7 +95,7 @@ def parametrize_from_transforms(*transforms): ...@@ -94,7 +95,7 @@ def parametrize_from_transforms(*transforms):
class TestSmoke: class TestSmoke:
@parametrize_from_transforms( @parametrize_from_transforms(
transforms.RandomErasing(p=1.0), transforms.RandomErasing(p=1.0),
transforms.Resize([16, 16]), transforms.Resize([16, 16], antialias=True),
transforms.CenterCrop([16, 16]), transforms.CenterCrop([16, 16]),
transforms.ConvertDtype(), transforms.ConvertDtype(),
transforms.RandomHorizontalFlip(), transforms.RandomHorizontalFlip(),
...@@ -210,7 +211,7 @@ class TestSmoke: ...@@ -210,7 +211,7 @@ class TestSmoke:
@parametrize( @parametrize(
[ [
( (
transforms.RandomResizedCrop([16, 16]), transforms.RandomResizedCrop([16, 16], antialias=True),
itertools.chain( itertools.chain(
make_images(extra_dims=[(4,)]), make_images(extra_dims=[(4,)]),
make_vanilla_tensor_images(), make_vanilla_tensor_images(),
...@@ -1991,6 +1992,70 @@ class TestUniformTemporalSubsample: ...@@ -1991,6 +1992,70 @@ class TestUniformTemporalSubsample:
assert output.dtype == inpt.dtype assert output.dtype == inpt.dtype
# TODO: remove this test in 0.17 when the default of antialias changes to True
def test_antialias_warning():
pil_img = PIL.Image.new("RGB", size=(10, 10), color=127)
tensor_img = torch.randint(0, 256, size=(3, 10, 10), dtype=torch.uint8)
tensor_video = torch.randint(0, 256, size=(2, 3, 10, 10), dtype=torch.uint8)
match = "The default value of the antialias parameter"
with pytest.warns(UserWarning, match=match):
transforms.Resize((20, 20))(tensor_img)
with pytest.warns(UserWarning, match=match):
transforms.RandomResizedCrop((20, 20))(tensor_img)
with pytest.warns(UserWarning, match=match):
transforms.ScaleJitter((20, 20))(tensor_img)
with pytest.warns(UserWarning, match=match):
transforms.RandomShortestSize((20, 20))(tensor_img)
with pytest.warns(UserWarning, match=match):
transforms.RandomResize(10, 20)(tensor_img)
with pytest.warns(UserWarning, match=match):
transforms.functional.resize(tensor_img, (20, 20))
with pytest.warns(UserWarning, match=match):
transforms.functional.resize_image_tensor(tensor_img, (20, 20))
with pytest.warns(UserWarning, match=match):
transforms.functional.resize(tensor_video, (20, 20))
with pytest.warns(UserWarning, match=match):
transforms.functional.resize_video(tensor_video, (20, 20))
with pytest.warns(UserWarning, match=match):
datapoints.Image(tensor_img).resize((20, 20))
with pytest.warns(UserWarning, match=match):
datapoints.Image(tensor_img).resized_crop(0, 0, 10, 10, (20, 20))
with pytest.warns(UserWarning, match=match):
datapoints.Video(tensor_video).resize((20, 20))
with pytest.warns(UserWarning, match=match):
datapoints.Video(tensor_video).resized_crop(0, 0, 10, 10, (20, 20))
with warnings.catch_warnings():
warnings.simplefilter("error")
transforms.Resize((20, 20))(pil_img)
transforms.RandomResizedCrop((20, 20))(pil_img)
transforms.ScaleJitter((20, 20))(pil_img)
transforms.RandomShortestSize((20, 20))(pil_img)
transforms.RandomResize(10, 20)(pil_img)
transforms.functional.resize(pil_img, (20, 20))
transforms.Resize((20, 20), antialias=True)(tensor_img)
transforms.RandomResizedCrop((20, 20), antialias=True)(tensor_img)
transforms.ScaleJitter((20, 20), antialias=True)(tensor_img)
transforms.RandomShortestSize((20, 20), antialias=True)(tensor_img)
transforms.RandomResize(10, 20, antialias=True)(tensor_img)
transforms.functional.resize(tensor_img, (20, 20), antialias=True)
transforms.functional.resize_image_tensor(tensor_img, (20, 20), antialias=True)
transforms.functional.resize(tensor_video, (20, 20), antialias=True)
transforms.functional.resize_video(tensor_video, (20, 20), antialias=True)
datapoints.Image(tensor_img).resize((20, 20), antialias=True)
datapoints.Image(tensor_img).resized_crop(0, 0, 10, 10, (20, 20), antialias=True)
datapoints.Video(tensor_video).resize((20, 20), antialias=True)
datapoints.Video(tensor_video).resized_crop(0, 0, 10, 10, (20, 20), antialias=True)
@pytest.mark.parametrize("image_type", (PIL.Image, torch.Tensor, datapoints.Image)) @pytest.mark.parametrize("image_type", (PIL.Image, torch.Tensor, datapoints.Image))
@pytest.mark.parametrize("label_type", (torch.Tensor, int)) @pytest.mark.parametrize("label_type", (torch.Tensor, int))
@pytest.mark.parametrize("dataset_return_type", (dict, tuple)) @pytest.mark.parametrize("dataset_return_type", (dict, tuple))
......
...@@ -2,6 +2,7 @@ import math ...@@ -2,6 +2,7 @@ import math
import os import os
import random import random
import re import re
import warnings
from functools import partial from functools import partial
import numpy as np import numpy as np
...@@ -319,7 +320,7 @@ def test_randomresized_params(): ...@@ -319,7 +320,7 @@ def test_randomresized_params():
scale_range = (scale_min, scale_min + round(random.random(), 2)) scale_range = (scale_min, scale_min + round(random.random(), 2))
aspect_min = max(round(random.random(), 2), epsilon) aspect_min = max(round(random.random(), 2), epsilon)
aspect_ratio_range = (aspect_min, aspect_min + round(random.random(), 2)) aspect_ratio_range = (aspect_min, aspect_min + round(random.random(), 2))
randresizecrop = transforms.RandomResizedCrop(size, scale_range, aspect_ratio_range) randresizecrop = transforms.RandomResizedCrop(size, scale_range, aspect_ratio_range, antialias=True)
i, j, h, w = randresizecrop.get_params(img, scale_range, aspect_ratio_range) i, j, h, w = randresizecrop.get_params(img, scale_range, aspect_ratio_range)
aspect_ratio_obtained = w / h aspect_ratio_obtained = w / h
assert ( assert (
...@@ -366,7 +367,7 @@ def test_randomresized_params(): ...@@ -366,7 +367,7 @@ def test_randomresized_params():
def test_resize(height, width, osize, max_size): def test_resize(height, width, osize, max_size):
img = Image.new("RGB", size=(width, height), color=127) img = Image.new("RGB", size=(width, height), color=127)
t = transforms.Resize(osize, max_size=max_size) t = transforms.Resize(osize, max_size=max_size, antialias=True)
result = t(img) result = t(img)
msg = f"{height}, {width} - {osize} - {max_size}" msg = f"{height}, {width} - {osize} - {max_size}"
...@@ -424,7 +425,7 @@ def test_resize_sequence_output(height, width, osize): ...@@ -424,7 +425,7 @@ def test_resize_sequence_output(height, width, osize):
img = Image.new("RGB", size=(width, height), color=127) img = Image.new("RGB", size=(width, height), color=127)
oheight, owidth = osize oheight, owidth = osize
t = transforms.Resize(osize) t = transforms.Resize(osize, antialias=True)
result = t(img) result = t(img)
assert (owidth, oheight) == result.size assert (owidth, oheight) == result.size
...@@ -439,6 +440,16 @@ def test_resize_antialias_error(): ...@@ -439,6 +440,16 @@ def test_resize_antialias_error():
t(img) t(img)
def test_resize_antialias_default_warning():
img = Image.new("RGB", size=(10, 10), color=127)
# We make sure we don't warn for PIL images since the default behaviour doesn't change
with warnings.catch_warnings():
warnings.simplefilter("error")
transforms.Resize((20, 20))(img)
transforms.RandomResizedCrop((20, 20))(img)
@pytest.mark.parametrize("height, width", ((32, 64), (64, 32))) @pytest.mark.parametrize("height, width", ((32, 64), (64, 32)))
def test_resize_size_equals_small_edge_size(height, width): def test_resize_size_equals_small_edge_size(height, width):
# Non-regression test for https://github.com/pytorch/vision/issues/5405 # Non-regression test for https://github.com/pytorch/vision/issues/5405
...@@ -447,7 +458,7 @@ def test_resize_size_equals_small_edge_size(height, width): ...@@ -447,7 +458,7 @@ def test_resize_size_equals_small_edge_size(height, width):
img = Image.new("RGB", size=(width, height), color=127) img = Image.new("RGB", size=(width, height), color=127)
small_edge = min(height, width) small_edge = min(height, width)
t = transforms.Resize(small_edge, max_size=max_size) t = transforms.Resize(small_edge, max_size=max_size, antialias=True)
result = t(img) result = t(img)
assert max(result.size) == max_size assert max(result.size) == max_size
...@@ -1424,11 +1435,11 @@ def test_random_choice(proba_passthrough, seed): ...@@ -1424,11 +1435,11 @@ def test_random_choice(proba_passthrough, seed):
def test_random_order(): def test_random_order():
random_state = random.getstate() random_state = random.getstate()
random.seed(42) random.seed(42)
random_order_transform = transforms.RandomOrder([transforms.Resize(20), transforms.CenterCrop(10)]) random_order_transform = transforms.RandomOrder([transforms.Resize(20, antialias=True), transforms.CenterCrop(10)])
img = transforms.ToPILImage()(torch.rand(3, 25, 25)) img = transforms.ToPILImage()(torch.rand(3, 25, 25))
num_samples = 250 num_samples = 250
num_normal_order = 0 num_normal_order = 0
resize_crop_out = transforms.CenterCrop(10)(transforms.Resize(20)(img)) resize_crop_out = transforms.CenterCrop(10)(transforms.Resize(20, antialias=True)(img))
for _ in range(num_samples): for _ in range(num_samples):
out = random_order_transform(img) out = random_order_transform(img)
if out == resize_crop_out: if out == resize_crop_out:
......
import os import os
import sys import sys
import warnings
import numpy as np import numpy as np
import pytest import pytest
...@@ -371,7 +372,7 @@ class TestResize: ...@@ -371,7 +372,7 @@ class TestResize:
def test_resize_int(self, size): def test_resize_int(self, size):
# TODO: Minimal check for bug-fix, improve this later # TODO: Minimal check for bug-fix, improve this later
x = torch.rand(3, 32, 46) x = torch.rand(3, 32, 46)
t = T.Resize(size=size) t = T.Resize(size=size, antialias=True)
y = t(x) y = t(x)
# If size is an int, smaller edge of the image will be matched to this number. # If size is an int, smaller edge of the image will be matched to this number.
# i.e, if height > width, then image will be rescaled to (size * height / width, size). # i.e, if height > width, then image will be rescaled to (size * height / width, size).
...@@ -394,13 +395,13 @@ class TestResize: ...@@ -394,13 +395,13 @@ class TestResize:
if max_size is not None and len(size) != 1: if max_size is not None and len(size) != 1:
pytest.skip("Size should be an int or a sequence of length 1 if max_size is specified") pytest.skip("Size should be an int or a sequence of length 1 if max_size is specified")
transform = T.Resize(size=size, interpolation=interpolation, max_size=max_size) transform = T.Resize(size=size, interpolation=interpolation, max_size=max_size, antialias=True)
s_transform = torch.jit.script(transform) s_transform = torch.jit.script(transform)
_test_transform_vs_scripted(transform, s_transform, tensor) _test_transform_vs_scripted(transform, s_transform, tensor)
_test_transform_vs_scripted_on_batch(transform, s_transform, batch_tensors) _test_transform_vs_scripted_on_batch(transform, s_transform, batch_tensors)
def test_resize_save_load(self, tmpdir): def test_resize_save_load(self, tmpdir):
fn = T.Resize(size=[32]) fn = T.Resize(size=[32], antialias=True)
_test_fn_save_load(fn, tmpdir) _test_fn_save_load(fn, tmpdir)
@pytest.mark.parametrize("device", cpu_and_gpu()) @pytest.mark.parametrize("device", cpu_and_gpu())
...@@ -424,9 +425,25 @@ class TestResize: ...@@ -424,9 +425,25 @@ class TestResize:
_test_transform_vs_scripted_on_batch(transform, s_transform, batch_tensors) _test_transform_vs_scripted_on_batch(transform, s_transform, batch_tensors)
def test_resized_crop_save_load(self, tmpdir): def test_resized_crop_save_load(self, tmpdir):
fn = T.RandomResizedCrop(size=[32]) fn = T.RandomResizedCrop(size=[32], antialias=True)
_test_fn_save_load(fn, tmpdir) _test_fn_save_load(fn, tmpdir)
def test_antialias_default_warning(self):
img = torch.randint(0, 256, size=(3, 44, 56), dtype=torch.uint8)
match = "The default value of the antialias"
with pytest.warns(UserWarning, match=match):
T.Resize((20, 20))(img)
with pytest.warns(UserWarning, match=match):
T.RandomResizedCrop((20, 20))(img)
# For modes that aren't bicubic or bilinear, don't throw a warning
with warnings.catch_warnings():
warnings.simplefilter("error")
T.Resize((20, 20), interpolation=NEAREST)(img)
T.RandomResizedCrop((20, 20), interpolation=NEAREST)(img)
def _test_random_affine_helper(device, **kwargs): def _test_random_affine_helper(device, **kwargs):
tensor = torch.randint(0, 256, size=(3, 44, 56), dtype=torch.uint8, device=device) tensor = torch.randint(0, 256, size=(3, 44, 56), dtype=torch.uint8, device=device)
......
...@@ -78,7 +78,7 @@ class BoundingBox(Datapoint): ...@@ -78,7 +78,7 @@ class BoundingBox(Datapoint):
size: List[int], size: List[int],
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
max_size: Optional[int] = None, max_size: Optional[int] = None,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> BoundingBox: ) -> BoundingBox:
output, spatial_size = self._F.resize_bounding_box( output, spatial_size = self._F.resize_bounding_box(
self.as_subclass(torch.Tensor), spatial_size=self.spatial_size, size=size, max_size=max_size self.as_subclass(torch.Tensor), spatial_size=self.spatial_size, size=size, max_size=max_size
...@@ -105,7 +105,7 @@ class BoundingBox(Datapoint): ...@@ -105,7 +105,7 @@ class BoundingBox(Datapoint):
width: int, width: int,
size: List[int], size: List[int],
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> BoundingBox: ) -> BoundingBox:
output, spatial_size = self._F.resized_crop_bounding_box( output, spatial_size = self._F.resized_crop_bounding_box(
self.as_subclass(torch.Tensor), self.format, top, left, height, width, size=size self.as_subclass(torch.Tensor), self.format, top, left, height, width, size=size
......
...@@ -145,7 +145,7 @@ class Datapoint(torch.Tensor): ...@@ -145,7 +145,7 @@ class Datapoint(torch.Tensor):
size: List[int], size: List[int],
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
max_size: Optional[int] = None, max_size: Optional[int] = None,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> Datapoint: ) -> Datapoint:
return self return self
...@@ -163,7 +163,7 @@ class Datapoint(torch.Tensor): ...@@ -163,7 +163,7 @@ class Datapoint(torch.Tensor):
width: int, width: int,
size: List[int], size: List[int],
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> Datapoint: ) -> Datapoint:
return self return self
......
...@@ -64,7 +64,7 @@ class Image(Datapoint): ...@@ -64,7 +64,7 @@ class Image(Datapoint):
size: List[int], size: List[int],
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
max_size: Optional[int] = None, max_size: Optional[int] = None,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> Image: ) -> Image:
output = self._F.resize_image_tensor( output = self._F.resize_image_tensor(
self.as_subclass(torch.Tensor), size, interpolation=interpolation, max_size=max_size, antialias=antialias self.as_subclass(torch.Tensor), size, interpolation=interpolation, max_size=max_size, antialias=antialias
...@@ -87,7 +87,7 @@ class Image(Datapoint): ...@@ -87,7 +87,7 @@ class Image(Datapoint):
width: int, width: int,
size: List[int], size: List[int],
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> Image: ) -> Image:
output = self._F.resized_crop_image_tensor( output = self._F.resized_crop_image_tensor(
self.as_subclass(torch.Tensor), self.as_subclass(torch.Tensor),
......
...@@ -55,7 +55,7 @@ class Mask(Datapoint): ...@@ -55,7 +55,7 @@ class Mask(Datapoint):
size: List[int], size: List[int],
interpolation: InterpolationMode = InterpolationMode.NEAREST, interpolation: InterpolationMode = InterpolationMode.NEAREST,
max_size: Optional[int] = None, max_size: Optional[int] = None,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> Mask: ) -> Mask:
output = self._F.resize_mask(self.as_subclass(torch.Tensor), size, max_size=max_size) output = self._F.resize_mask(self.as_subclass(torch.Tensor), size, max_size=max_size)
return Mask.wrap_like(self, output) return Mask.wrap_like(self, output)
...@@ -76,7 +76,7 @@ class Mask(Datapoint): ...@@ -76,7 +76,7 @@ class Mask(Datapoint):
width: int, width: int,
size: List[int], size: List[int],
interpolation: InterpolationMode = InterpolationMode.NEAREST, interpolation: InterpolationMode = InterpolationMode.NEAREST,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> Mask: ) -> Mask:
output = self._F.resized_crop_mask(self.as_subclass(torch.Tensor), top, left, height, width, size=size) output = self._F.resized_crop_mask(self.as_subclass(torch.Tensor), top, left, height, width, size=size)
return Mask.wrap_like(self, output) return Mask.wrap_like(self, output)
......
...@@ -59,7 +59,7 @@ class Video(Datapoint): ...@@ -59,7 +59,7 @@ class Video(Datapoint):
size: List[int], size: List[int],
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
max_size: Optional[int] = None, max_size: Optional[int] = None,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> Video: ) -> Video:
output = self._F.resize_video( output = self._F.resize_video(
self.as_subclass(torch.Tensor), self.as_subclass(torch.Tensor),
...@@ -86,7 +86,7 @@ class Video(Datapoint): ...@@ -86,7 +86,7 @@ class Video(Datapoint):
width: int, width: int,
size: List[int], size: List[int],
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> Video: ) -> Video:
output = self._F.resized_crop_video( output = self._F.resized_crop_video(
self.as_subclass(torch.Tensor), self.as_subclass(torch.Tensor),
......
...@@ -47,7 +47,7 @@ class Resize(Transform): ...@@ -47,7 +47,7 @@ class Resize(Transform):
size: Union[int, Sequence[int]], size: Union[int, Sequence[int]],
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
max_size: Optional[int] = None, max_size: Optional[int] = None,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> None: ) -> None:
super().__init__() super().__init__()
...@@ -95,7 +95,7 @@ class RandomResizedCrop(Transform): ...@@ -95,7 +95,7 @@ class RandomResizedCrop(Transform):
scale: Tuple[float, float] = (0.08, 1.0), scale: Tuple[float, float] = (0.08, 1.0),
ratio: Tuple[float, float] = (3.0 / 4.0, 4.0 / 3.0), ratio: Tuple[float, float] = (3.0 / 4.0, 4.0 / 3.0),
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> None: ) -> None:
super().__init__() super().__init__()
self.size = _setup_size(size, error_msg="Please provide only two dimensions (h, w) for size.") self.size = _setup_size(size, error_msg="Please provide only two dimensions (h, w) for size.")
...@@ -761,7 +761,7 @@ class ScaleJitter(Transform): ...@@ -761,7 +761,7 @@ class ScaleJitter(Transform):
target_size: Tuple[int, int], target_size: Tuple[int, int],
scale_range: Tuple[float, float] = (0.1, 2.0), scale_range: Tuple[float, float] = (0.1, 2.0),
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
): ):
super().__init__() super().__init__()
self.target_size = target_size self.target_size = target_size
...@@ -789,7 +789,7 @@ class RandomShortestSize(Transform): ...@@ -789,7 +789,7 @@ class RandomShortestSize(Transform):
min_size: Union[List[int], Tuple[int], int], min_size: Union[List[int], Tuple[int], int],
max_size: Optional[int] = None, max_size: Optional[int] = None,
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
): ):
super().__init__() super().__init__()
self.min_size = [min_size] if isinstance(min_size, int) else list(min_size) self.min_size = [min_size] if isinstance(min_size, int) else list(min_size)
...@@ -936,7 +936,7 @@ class RandomResize(Transform): ...@@ -936,7 +936,7 @@ class RandomResize(Transform):
min_size: int, min_size: int,
max_size: int, max_size: int,
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> None: ) -> None:
super().__init__() super().__init__()
self.min_size = min_size self.min_size = min_size
......
...@@ -41,10 +41,14 @@ class StereoMatching(torch.nn.Module): ...@@ -41,10 +41,14 @@ class StereoMatching(torch.nn.Module):
def forward(self, left_image: Tensor, right_image: Tensor) -> Tuple[Tensor, Tensor]: def forward(self, left_image: Tensor, right_image: Tensor) -> Tuple[Tensor, Tensor]:
def _process_image(img: PIL.Image.Image) -> Tensor: def _process_image(img: PIL.Image.Image) -> Tensor:
if self.resize_size is not None:
img = F.resize(img, self.resize_size, interpolation=self.interpolation)
if not isinstance(img, Tensor): if not isinstance(img, Tensor):
img = F.pil_to_tensor(img) img = F.pil_to_tensor(img)
if self.resize_size is not None:
# We hard-code antialias=False to preserve results after we changed
# its default from None to True (see
# https://github.com/pytorch/vision/pull/7160)
# TODO: we could re-train the stereo models with antialias=True?
img = F.resize(img, self.resize_size, interpolation=self.interpolation, antialias=False)
if self.use_gray_scale is True: if self.use_gray_scale is True:
img = F.rgb_to_grayscale(img) img = F.rgb_to_grayscale(img)
img = F.convert_image_dtype(img, torch.float) img = F.convert_image_dtype(img, torch.float)
......
...@@ -10,6 +10,7 @@ from torch.nn.functional import grid_sample, interpolate, pad as torch_pad ...@@ -10,6 +10,7 @@ from torch.nn.functional import grid_sample, interpolate, pad as torch_pad
from torchvision.prototype import datapoints from torchvision.prototype import datapoints
from torchvision.transforms import functional_pil as _FP from torchvision.transforms import functional_pil as _FP
from torchvision.transforms.functional import ( from torchvision.transforms.functional import (
_check_antialias,
_compute_resized_output_size as __compute_resized_output_size, _compute_resized_output_size as __compute_resized_output_size,
_get_perspective_coeffs, _get_perspective_coeffs,
InterpolationMode, InterpolationMode,
...@@ -143,14 +144,18 @@ def resize_image_tensor( ...@@ -143,14 +144,18 @@ def resize_image_tensor(
size: List[int], size: List[int],
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
max_size: Optional[int] = None, max_size: Optional[int] = None,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> torch.Tensor: ) -> torch.Tensor:
antialias = _check_antialias(img=image, antialias=antialias, interpolation=interpolation)
assert not isinstance(antialias, str)
antialias = False if antialias is None else antialias antialias = False if antialias is None else antialias
align_corners: Optional[bool] = None align_corners: Optional[bool] = None
if interpolation == InterpolationMode.BILINEAR or interpolation == InterpolationMode.BICUBIC: if interpolation == InterpolationMode.BILINEAR or interpolation == InterpolationMode.BICUBIC:
align_corners = False align_corners = False
elif antialias: else:
raise ValueError("Antialias option is supported for bilinear and bicubic interpolation modes only") # The default of antialias should be True from 0.17, so we don't warn or
# error if other interpolation modes are used. This is documented.
antialias = False
shape = image.shape shape = image.shape
num_channels, old_height, old_width = shape[-3:] num_channels, old_height, old_width = shape[-3:]
...@@ -225,7 +230,7 @@ def resize_video( ...@@ -225,7 +230,7 @@ def resize_video(
size: List[int], size: List[int],
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
max_size: Optional[int] = None, max_size: Optional[int] = None,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> torch.Tensor: ) -> torch.Tensor:
return resize_image_tensor(video, size=size, interpolation=interpolation, max_size=max_size, antialias=antialias) return resize_image_tensor(video, size=size, interpolation=interpolation, max_size=max_size, antialias=antialias)
...@@ -235,7 +240,7 @@ def resize( ...@@ -235,7 +240,7 @@ def resize(
size: List[int], size: List[int],
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
max_size: Optional[int] = None, max_size: Optional[int] = None,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> datapoints.InputTypeJIT: ) -> datapoints.InputTypeJIT:
if not torch.jit.is_scripting(): if not torch.jit.is_scripting():
_log_api_usage_once(resize) _log_api_usage_once(resize)
...@@ -1761,7 +1766,7 @@ def resized_crop_image_tensor( ...@@ -1761,7 +1766,7 @@ def resized_crop_image_tensor(
width: int, width: int,
size: List[int], size: List[int],
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> torch.Tensor: ) -> torch.Tensor:
image = crop_image_tensor(image, top, left, height, width) image = crop_image_tensor(image, top, left, height, width)
return resize_image_tensor(image, size, interpolation=interpolation, antialias=antialias) return resize_image_tensor(image, size, interpolation=interpolation, antialias=antialias)
...@@ -1814,7 +1819,7 @@ def resized_crop_video( ...@@ -1814,7 +1819,7 @@ def resized_crop_video(
width: int, width: int,
size: List[int], size: List[int],
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> torch.Tensor: ) -> torch.Tensor:
return resized_crop_image_tensor( return resized_crop_image_tensor(
video, top, left, height, width, antialias=antialias, size=size, interpolation=interpolation video, top, left, height, width, antialias=antialias, size=size, interpolation=interpolation
...@@ -1829,7 +1834,7 @@ def resized_crop( ...@@ -1829,7 +1834,7 @@ def resized_crop(
width: int, width: int,
size: List[int], size: List[int],
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> datapoints.InputTypeJIT: ) -> datapoints.InputTypeJIT:
if not torch.jit.is_scripting(): if not torch.jit.is_scripting():
_log_api_usage_once(resized_crop) _log_api_usage_once(resized_crop)
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
This file is part of the private API. Please do not use directly these classes as they will be modified on This file is part of the private API. Please do not use directly these classes as they will be modified on
future versions without warning. The classes should be accessed only via the transforms argument of Weights. future versions without warning. The classes should be accessed only via the transforms argument of Weights.
""" """
from typing import Optional, Tuple from typing import Optional, Tuple, Union
import torch import torch
from torch import nn, Tensor from torch import nn, Tensor
...@@ -44,6 +44,7 @@ class ImageClassification(nn.Module): ...@@ -44,6 +44,7 @@ class ImageClassification(nn.Module):
mean: Tuple[float, ...] = (0.485, 0.456, 0.406), mean: Tuple[float, ...] = (0.485, 0.456, 0.406),
std: Tuple[float, ...] = (0.229, 0.224, 0.225), std: Tuple[float, ...] = (0.229, 0.224, 0.225),
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
antialias: Optional[Union[str, bool]] = "warn",
) -> None: ) -> None:
super().__init__() super().__init__()
self.crop_size = [crop_size] self.crop_size = [crop_size]
...@@ -51,9 +52,10 @@ class ImageClassification(nn.Module): ...@@ -51,9 +52,10 @@ class ImageClassification(nn.Module):
self.mean = list(mean) self.mean = list(mean)
self.std = list(std) self.std = list(std)
self.interpolation = interpolation self.interpolation = interpolation
self.antialias = antialias
def forward(self, img: Tensor) -> Tensor: def forward(self, img: Tensor) -> Tensor:
img = F.resize(img, self.resize_size, interpolation=self.interpolation) img = F.resize(img, self.resize_size, interpolation=self.interpolation, antialias=self.antialias)
img = F.center_crop(img, self.crop_size) img = F.center_crop(img, self.crop_size)
if not isinstance(img, Tensor): if not isinstance(img, Tensor):
img = F.pil_to_tensor(img) img = F.pil_to_tensor(img)
...@@ -105,7 +107,11 @@ class VideoClassification(nn.Module): ...@@ -105,7 +107,11 @@ class VideoClassification(nn.Module):
N, T, C, H, W = vid.shape N, T, C, H, W = vid.shape
vid = vid.view(-1, C, H, W) vid = vid.view(-1, C, H, W)
vid = F.resize(vid, self.resize_size, interpolation=self.interpolation) # We hard-code antialias=False to preserve results after we changed
# its default from None to True (see
# https://github.com/pytorch/vision/pull/7160)
# TODO: we could re-train the video models with antialias=True?
vid = F.resize(vid, self.resize_size, interpolation=self.interpolation, antialias=False)
vid = F.center_crop(vid, self.crop_size) vid = F.center_crop(vid, self.crop_size)
vid = F.convert_image_dtype(vid, torch.float) vid = F.convert_image_dtype(vid, torch.float)
vid = F.normalize(vid, mean=self.mean, std=self.std) vid = F.normalize(vid, mean=self.mean, std=self.std)
...@@ -145,16 +151,18 @@ class SemanticSegmentation(nn.Module): ...@@ -145,16 +151,18 @@ class SemanticSegmentation(nn.Module):
mean: Tuple[float, ...] = (0.485, 0.456, 0.406), mean: Tuple[float, ...] = (0.485, 0.456, 0.406),
std: Tuple[float, ...] = (0.229, 0.224, 0.225), std: Tuple[float, ...] = (0.229, 0.224, 0.225),
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
antialias: Optional[Union[str, bool]] = "warn",
) -> None: ) -> None:
super().__init__() super().__init__()
self.resize_size = [resize_size] if resize_size is not None else None self.resize_size = [resize_size] if resize_size is not None else None
self.mean = list(mean) self.mean = list(mean)
self.std = list(std) self.std = list(std)
self.interpolation = interpolation self.interpolation = interpolation
self.antialias = antialias
def forward(self, img: Tensor) -> Tensor: def forward(self, img: Tensor) -> Tensor:
if isinstance(self.resize_size, list): if isinstance(self.resize_size, list):
img = F.resize(img, self.resize_size, interpolation=self.interpolation) img = F.resize(img, self.resize_size, interpolation=self.interpolation, antialias=self.antialias)
if not isinstance(img, Tensor): if not isinstance(img, Tensor):
img = F.pil_to_tensor(img) img = F.pil_to_tensor(img)
img = F.convert_image_dtype(img, torch.float) img = F.convert_image_dtype(img, torch.float)
......
...@@ -393,7 +393,7 @@ def resize( ...@@ -393,7 +393,7 @@ def resize(
size: List[int], size: List[int],
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
max_size: Optional[int] = None, max_size: Optional[int] = None,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> Tensor: ) -> Tensor:
r"""Resize the input image to the given size. r"""Resize the input image to the given size.
If the image is torch Tensor, it is expected If the image is torch Tensor, it is expected
...@@ -429,10 +429,24 @@ def resize( ...@@ -429,10 +429,24 @@ def resize(
smaller edge may be shorter than ``size``. This is only supported smaller edge may be shorter than ``size``. This is only supported
if ``size`` is an int (or a sequence of length 1 in torchscript if ``size`` is an int (or a sequence of length 1 in torchscript
mode). mode).
antialias (bool, optional): antialias flag. If ``img`` is PIL Image, the flag is ignored and anti-alias antialias (bool, optional): Whether to apply antialiasing.
is always used. If ``img`` is Tensor, the flag is False by default and can be set to True for It only affects **tensors** with bilinear or bicubic modes and it is
``InterpolationMode.BILINEAR`` and ``InterpolationMode.BICUBIC`` modes. ignored otherwise: on PIL images, antialiasing is always applied on
This can help making the output for PIL images and tensors closer. bilinear or bicubic modes; on other modes (for PIL images and
tensors), antialiasing makes no sense and this parameter is ignored.
Possible values are:
- ``True``: will apply antialiasing for bilinear or bicubic modes.
Other mode aren't affected. This is probably what you want to use.
- ``False``: will not apply antialiasing for tensors on any mode. PIL
images are still antialiased on bilinear or bicubic modes, because
PIL doesn't support no antialias.
- ``None``: equivalent to ``False`` for tensors and ``True`` for
PIL images. This value exists for legacy reasons and you probably
don't want to use it unless you really know what you are doing.
The current default is ``None`` **but will change to** ``True`` **in
v0.17** for the PIL and Tensor backends to be consistent.
Returns: Returns:
PIL Image or Tensor: Resized image. PIL Image or Tensor: Resized image.
...@@ -462,6 +476,8 @@ def resize( ...@@ -462,6 +476,8 @@ def resize(
if (image_height, image_width) == output_size: if (image_height, image_width) == output_size:
return img return img
antialias = _check_antialias(img, antialias, interpolation)
if not isinstance(img, torch.Tensor): if not isinstance(img, torch.Tensor):
if antialias is not None and not antialias: if antialias is not None and not antialias:
warnings.warn("Anti-alias option is always applied for PIL Image input. Argument antialias is ignored.") warnings.warn("Anti-alias option is always applied for PIL Image input. Argument antialias is ignored.")
...@@ -594,7 +610,7 @@ def resized_crop( ...@@ -594,7 +610,7 @@ def resized_crop(
width: int, width: int,
size: List[int], size: List[int],
interpolation: InterpolationMode = InterpolationMode.BILINEAR, interpolation: InterpolationMode = InterpolationMode.BILINEAR,
antialias: Optional[bool] = None, antialias: Optional[Union[str, bool]] = "warn",
) -> Tensor: ) -> Tensor:
"""Crop the given image and resize it to desired size. """Crop the given image and resize it to desired size.
If the image is torch Tensor, it is expected If the image is torch Tensor, it is expected
...@@ -614,10 +630,24 @@ def resized_crop( ...@@ -614,10 +630,24 @@ def resized_crop(
Default is ``InterpolationMode.BILINEAR``. If input is Tensor, only ``InterpolationMode.NEAREST``, Default is ``InterpolationMode.BILINEAR``. If input is Tensor, only ``InterpolationMode.NEAREST``,
``InterpolationMode.NEAREST_EXACT``, ``InterpolationMode.BILINEAR`` and ``InterpolationMode.BICUBIC`` are ``InterpolationMode.NEAREST_EXACT``, ``InterpolationMode.BILINEAR`` and ``InterpolationMode.BICUBIC`` are
supported. supported.
antialias (bool, optional): antialias flag. If ``img`` is PIL Image, the flag is ignored and anti-alias antialias (bool, optional): Whether to apply antialiasing.
is always used. If ``img`` is Tensor, the flag is False by default and can be set to True for It only affects **tensors** with bilinear or bicubic modes and it is
``InterpolationMode.BILINEAR`` and ``InterpolationMode.BICUBIC`` modes. ignored otherwise: on PIL images, antialiasing is always applied on
This can help making the output for PIL images and tensors closer. bilinear or bicubic modes; on other modes (for PIL images and
tensors), antialiasing makes no sense and this parameter is ignored.
Possible values are:
- ``True``: will apply antialiasing for bilinear or bicubic modes.
Other mode aren't affected. This is probably what you want to use.
- ``False``: will not apply antialiasing for tensors on any mode. PIL
images are still antialiased on bilinear or bicubic modes, because
PIL doesn't support no antialias.
- ``None``: equivalent to ``False`` for tensors and ``True`` for
PIL images. This value exists for legacy reasons and you probably
don't want to use it unless you really know what you are doing.
The current default is ``None`` **but will change to** ``True`` **in
v0.17** for the PIL and Tensor backends to be consistent.
Returns: Returns:
PIL Image or Tensor: Cropped image. PIL Image or Tensor: Cropped image.
""" """
...@@ -1537,3 +1567,28 @@ def elastic_transform( ...@@ -1537,3 +1567,28 @@ def elastic_transform(
if not isinstance(img, torch.Tensor): if not isinstance(img, torch.Tensor):
output = to_pil_image(output, mode=img.mode) output = to_pil_image(output, mode=img.mode)
return output return output
# TODO in v0.17: remove this helper and change default of antialias to True everywhere
def _check_antialias(
img: Tensor, antialias: Optional[Union[str, bool]], interpolation: InterpolationMode
) -> Optional[bool]:
if isinstance(antialias, str): # it should be "warn", but we don't bother checking against that
if isinstance(img, Tensor) and (
interpolation == InterpolationMode.BILINEAR or interpolation == InterpolationMode.BICUBIC
):
warnings.warn(
"The default value of the antialias parameter of all the resizing transforms "
"(Resize(), RandomResizedCrop(), etc.) "
"will change from None to True in v0.17, "
"in order to be consistent across the PIL and Tensor backends. "
"To suppress this warning, directly pass "
"antialias=True (recommended, future default), antialias=None (current default, "
"which means False for Tensors and True for PIL), "
"or antialias=False (only works on Tensors - PIL will still use antialiasing). "
"This also applies if you are using the inference transforms from the models weights: "
"update the call to weights.transforms(antialias=True)."
)
antialias = None
return antialias
...@@ -440,6 +440,8 @@ def resize( ...@@ -440,6 +440,8 @@ def resize(
img: Tensor, img: Tensor,
size: List[int], size: List[int],
interpolation: str = "bilinear", interpolation: str = "bilinear",
# TODO: in v0.17, change the default to True. This will a private function
# by then, so we don't care about warning here.
antialias: Optional[bool] = None, antialias: Optional[bool] = None,
) -> Tensor: ) -> Tensor:
_assert_image_tensor(img) _assert_image_tensor(img)
...@@ -451,7 +453,11 @@ def resize( ...@@ -451,7 +453,11 @@ def resize(
antialias = False antialias = False
if antialias and interpolation not in ["bilinear", "bicubic"]: if antialias and interpolation not in ["bilinear", "bicubic"]:
raise ValueError("Antialias option is supported for bilinear and bicubic interpolation modes only") # We manually set it to False to avoid an error downstream in interpolate()
# This behaviour is documented: the parameter is irrelevant for modes
# that are not bilinear or bicubic. We used to raise an error here, but
# now we don't as True is the default.
antialias = False
img, need_cast, need_squeeze, out_dtype = _cast_squeeze_in(img, [torch.float32, torch.float64]) img, need_cast, need_squeeze, out_dtype = _cast_squeeze_in(img, [torch.float32, torch.float64])
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment