Skip to content

WIP Add PIL 4.1.1 to CI checks #3638

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .circleci/unittest/linux/scripts/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,11 @@ fi
printf "Installing PyTorch with %s\n" "${cudatoolkit}"
conda install -y -c "pytorch-${UPLOAD_CHANNEL}" -c conda-forge "pytorch-${UPLOAD_CHANNEL}"::pytorch "${cudatoolkit}"

if [ $PYTHON_VERSION == "3.6" ]; then
printf "Installing minimal PILLOW version\n"
# Install the minimal PILLOW version. Otherwise, let setup.py install the latest
pip install pillow==4.1.1
fi

printf "* Installing torchvision\n"
python setup.py develop
4 changes: 3 additions & 1 deletion test/common_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,14 @@
from _utils_internal import get_relative_path

import numpy as np
from PIL import Image
from PIL import Image, __version__ as PILLOW_VERSION

IS_PY39 = sys.version_info.major == 3 and sys.version_info.minor == 9
PY39_SEGFAULT_SKIP_MSG = "Segmentation fault with Python 3.9, see https://github.com/pytorch/vision/issues/3367"
PY39_SKIP = unittest.skipIf(IS_PY39, PY39_SEGFAULT_SKIP_MSG)

PILLOW_VERSION = tuple(int(x) for x in PILLOW_VERSION.split('.'))


@contextlib.contextmanager
def get_tmp_dir(src=None, **kwargs):
Expand Down
5 changes: 4 additions & 1 deletion test/test_functional_tensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import torchvision.transforms.functional as F
from torchvision.transforms import InterpolationMode

from common_utils import TransformsTester
from common_utils import TransformsTester, PILLOW_VERSION

from typing import Dict, List, Sequence, Tuple

Expand Down Expand Up @@ -624,6 +624,7 @@ def _test_affine_all_ops(self, tensor, pil_img, scripted_affine):
)
)

@unittest.skipIf(PILLOW_VERSION < (5, 0, 0), "affine requires PIL >= 5.0.0")
def test_affine(self):
# Tests on square and rectangular images
scripted_affine = torch.jit.script(F.affine)
Expand Down Expand Up @@ -712,6 +713,7 @@ def _test_rotate_all_options(self, tensor, pil_img, scripted_rotate, centers):
)
)

@unittest.skipIf(PILLOW_VERSION < (5, 2, 0), "rotate requires PIL >= 5.2.0")
def test_rotate(self):
# Tests on square image
scripted_rotate = torch.jit.script(F.rotate)
Expand Down Expand Up @@ -788,6 +790,7 @@ def _test_perspective(self, tensor, pil_img, scripted_transform, test_configs):
)
)

@unittest.skipIf(PILLOW_VERSION < (5, 0, 0), "perspective requires PIL >= 5.0.0")
def test_perspective(self):

from torchvision.transforms import RandomPerspective
Expand Down
10 changes: 8 additions & 2 deletions test/test_transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
except ImportError:
stats = None

from common_utils import cycle_over, int_dtypes, float_dtypes
from common_utils import cycle_over, int_dtypes, float_dtypes, PILLOW_VERSION


GRACE_HOPPER = get_file_path_2(
Expand Down Expand Up @@ -238,6 +238,7 @@ def test_randomperspective(self):
self.assertGreater(torch.nn.functional.mse_loss(tr_img, F.to_tensor(img)) + 0.3,
torch.nn.functional.mse_loss(tr_img2, F.to_tensor(img)))

@unittest.skipIf(PILLOW_VERSION < (5, 0, 0), "fill background requires PIL >= 5.0.0")
def test_randomperspective_fill(self):

# assert fill being either a Sequence or a Number
Expand Down Expand Up @@ -506,6 +507,7 @@ def test_lambda(self):
trans.__repr__()

@unittest.skipIf(stats is None, 'scipy.stats not available')
@unittest.skipIf(PILLOW_VERSION < (5, 2, 0), "fill background requires PIL >= 5.2.0")
def test_random_apply(self):
random_state = random.getstate()
random.seed(42)
Expand Down Expand Up @@ -1264,7 +1266,7 @@ def test_adjust_contrast(self):
y_ans = np.array(y_ans, dtype=np.uint8).reshape(x_shape)
self.assertTrue(np.allclose(y_np, y_ans))

@unittest.skipIf(Image.__version__ >= '7', "Temporarily disabled")
@unittest.skipIf(PILLOW_VERSION >= (7,), "Temporarily disabled")
def test_adjust_saturation(self):
x_shape = [2, 2, 3]
x_data = [0, 5, 13, 54, 135, 226, 37, 8, 234, 90, 255, 1]
Expand Down Expand Up @@ -1322,6 +1324,7 @@ def test_adjust_hue(self):
y_ans = np.array(y_ans, dtype=np.uint8).reshape(x_shape)
self.assertTrue(np.allclose(y_np, y_ans))

@unittest.skipIf(PILLOW_VERSION < (7,), "Lower PIL versions lead to slightly different results")
def test_adjust_sharpness(self):
x_shape = [4, 4, 3]
x_data = [75, 121, 114, 105, 97, 107, 105, 32, 66, 111, 117, 114, 99, 104, 97, 0,
Expand Down Expand Up @@ -1489,6 +1492,7 @@ def test_rotate(self):

self.assertTrue(np.all(np.array(result_a) == np.array(result_b)))

@unittest.skipIf(PILLOW_VERSION < (5, 2, 0), "fill background requires PIL >= 5.2.0")
def test_rotate_fill(self):
img = F.to_pil_image(np.ones((100, 100, 3), dtype=np.uint8) * 255, "RGB")

Expand Down Expand Up @@ -1943,6 +1947,7 @@ def test_random_solarize(self):
)

@unittest.skipIf(stats is None, 'scipy.stats not available')
@unittest.skipIf(PILLOW_VERSION < (7,), "Lower PIL versions lead to slightly different results")
def test_random_adjust_sharpness(self):
self._test_randomness(
F.adjust_sharpness,
Expand All @@ -1966,6 +1971,7 @@ def test_random_equalize(self):
[{}]
)

@unittest.skipIf(PILLOW_VERSION < (5, 2, 0), "fill background requires PIL >= 5.2.0")
def test_autoaugment(self):
for policy in transforms.AutoAugmentPolicy:
for fill in [None, 85, (128, 128, 128)]:
Expand Down
3 changes: 2 additions & 1 deletion test/test_transforms_tensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import unittest
from typing import Sequence

from common_utils import TransformsTester, get_tmp_dir, int_dtypes, float_dtypes
from common_utils import TransformsTester, get_tmp_dir, int_dtypes, float_dtypes, PILLOW_VERSION


NEAREST, BILINEAR, BICUBIC = InterpolationMode.NEAREST, InterpolationMode.BILINEAR, InterpolationMode.BICUBIC
Expand Down Expand Up @@ -105,6 +105,7 @@ def test_random_solarize(self):
'solarize', 'RandomSolarize', fn_kwargs=fn_kwargs, meth_kwargs=meth_kwargs
)

@unittest.skipIf(PILLOW_VERSION < (7,), "Lower PIL versions lead to slightly different results")
def test_random_adjust_sharpness(self):
fn_kwargs = meth_kwargs = {"sharpness_factor": 2.0}
self._test_op(
Expand Down
7 changes: 4 additions & 3 deletions test/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@
import unittest
from io import BytesIO
import torchvision.transforms.functional as F
from PIL import Image, __version__ as PILLOW_VERSION
from PIL import Image
from common_utils import PILLOW_VERSION


PILLOW_VERSION = tuple(int(x) for x in PILLOW_VERSION.split('.'))

boxes = torch.tensor([[0, 0, 20, 20], [0, 0, 0, 0],
[10, 15, 30, 35], [23, 35, 93, 95]], dtype=torch.float)

Expand Down Expand Up @@ -110,6 +109,7 @@ def test_save_image_single_pixel_file_object(self):
self.assertTrue(torch.equal(F.to_tensor(img_orig), F.to_tensor(img_bytes)),
'Pixel Image not stored in file object')

@unittest.skipIf(PILLOW_VERSION < (5, 3, 0), "draw_bounding_box is only available for PIL >= 5.3.0")
def test_draw_boxes(self):
img = torch.full((3, 100, 100), 255, dtype=torch.uint8)
img_cp = img.clone()
Expand All @@ -132,6 +132,7 @@ def test_draw_boxes(self):
self.assertTrue(torch.all(torch.eq(boxes, boxes_cp)).item())
self.assertTrue(torch.all(torch.eq(img, img_cp)).item())

@unittest.skipIf(PILLOW_VERSION < (5, 3, 0), "draw_bounding_box is only available for PIL >= 5.3.0")
def test_draw_boxes_vanilla(self):
img = torch.full((3, 100, 100), 0, dtype=torch.uint8)
img_cp = img.clone()
Expand Down
6 changes: 5 additions & 1 deletion torchvision/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import math
import warnings
import numpy as np
from PIL import Image, ImageDraw, ImageFont, ImageColor
from PIL import Image, ImageDraw, ImageFont, ImageColor, __version__ as PILLOW_VERSION

__all__ = ["make_grid", "save_image", "draw_bounding_boxes", "draw_segmentation_masks"]

Expand Down Expand Up @@ -187,6 +187,10 @@ def draw_bounding_boxes(
elif image.dim() != 3:
raise ValueError("Pass individual images, not batches")

pillow_version = tuple(int(x) for x in PILLOW_VERSION.split('.'))
if pillow_version < (5, 3, 0):
raise ValueError("draw_bounding_boxes requires Pillow >= 5.3.0")

ndarr = image.permute(1, 2, 0).numpy()
img_to_draw = Image.fromarray(ndarr)

Expand Down