Pillow/Tests/test_image_transform.py

283 lines
9.5 KiB
Python
Raw Normal View History

2016-07-10 21:47:59 +03:00
import math
from .helper import PillowTestCase, hopper
from PIL import Image
2013-10-01 08:57:19 +04:00
2014-06-10 13:10:47 +04:00
class TestImageTransform(PillowTestCase):
def test_sanity(self):
from PIL import ImageTransform
im = Image.new("L", (100, 100))
seq = tuple(range(10))
transform = ImageTransform.AffineTransform(seq[:6])
im.transform((100, 100), transform)
transform = ImageTransform.ExtentTransform(seq[:4])
im.transform((100, 100), transform)
transform = ImageTransform.QuadTransform(seq[:8])
im.transform((100, 100), transform)
transform = ImageTransform.MeshTransform([(seq[:4], seq[:8])])
im.transform((100, 100), transform)
2014-06-10 13:10:47 +04:00
def test_extent(self):
im = hopper('RGB')
2014-06-10 13:10:47 +04:00
(w, h) = im.size
transformed = im.transform(im.size, Image.EXTENT,
(0, 0,
w//2, h//2), # ul -> lr
Image.BILINEAR)
scaled = im.resize((w*2, h*2), Image.BILINEAR).crop((0, 0, w, h))
# undone -- precision?
2014-09-14 22:21:16 +04:00
self.assert_image_similar(transformed, scaled, 23)
2014-06-10 13:10:47 +04:00
def test_quad(self):
# one simple quad transform, equivalent to scale & crop upper left quad
im = hopper('RGB')
2014-06-10 13:10:47 +04:00
(w, h) = im.size
transformed = im.transform(im.size, Image.QUAD,
(0, 0, 0, h//2,
# ul -> ccw around quad:
w//2, h//2, w//2, 0),
Image.BILINEAR)
scaled = im.transform((w, h), Image.AFFINE,
(.5, 0, 0, 0, .5, 0),
Image.BILINEAR)
2013-10-01 08:57:19 +04:00
2014-06-10 13:10:47 +04:00
self.assert_image_equal(transformed, scaled)
2017-11-11 19:31:37 +03:00
def test_fill(self):
for mode, pixel in [
['RGB', (255, 0, 0)],
['RGBA', (255, 0, 0, 255)],
['LA', (76, 0)]
]:
im = hopper(mode)
(w, h) = im.size
transformed = im.transform(im.size, Image.EXTENT,
(0, 0,
w*2, h*2),
Image.BILINEAR,
fillcolor='red')
self.assertEqual(transformed.getpixel((w-1, h-1)), pixel)
2017-11-11 19:31:37 +03:00
2014-06-10 13:10:47 +04:00
def test_mesh(self):
# this should be a checkerboard of halfsized hoppers in ul, lr
im = hopper('RGBA')
2014-06-10 13:10:47 +04:00
(w, h) = im.size
transformed = im.transform(im.size, Image.MESH,
[((0, 0, w//2, h//2), # box
(0, 0, 0, h,
w, h, w, 0)), # ul -> ccw around quad
((w//2, h//2, w, h), # box
(0, 0, 0, h,
w, h, w, 0))], # ul -> ccw around quad
Image.BILINEAR)
scaled = im.transform((w//2, h//2), Image.AFFINE,
(2, 0, 0, 0, 2, 0),
Image.BILINEAR)
2013-10-01 09:01:23 +04:00
2014-06-10 13:10:47 +04:00
checker = Image.new('RGBA', im.size)
checker.paste(scaled, (0, 0))
checker.paste(scaled, (w//2, h//2))
2013-10-01 09:10:19 +04:00
2014-06-10 13:10:47 +04:00
self.assert_image_equal(transformed, checker)
2013-10-01 09:01:23 +04:00
2014-06-10 13:10:47 +04:00
# now, check to see that the extra area is (0, 0, 0, 0)
blank = Image.new('RGBA', (w//2, h//2), (0, 0, 0, 0))
2014-06-10 13:10:47 +04:00
self.assert_image_equal(blank, transformed.crop((w//2, 0, w, h//2)))
self.assert_image_equal(blank, transformed.crop((0, h//2, w//2, h)))
2013-10-01 09:01:23 +04:00
2014-06-10 13:10:47 +04:00
def _test_alpha_premult(self, op):
# create image with half white, half black,
# with the black half transparent.
# do op,
# there should be no darkness in the white section.
im = Image.new('RGBA', (10, 10), (0, 0, 0, 0))
im2 = Image.new('RGBA', (5, 10), (255, 255, 255, 255))
im.paste(im2, (0, 0))
2013-10-01 09:10:19 +04:00
2014-06-10 13:10:47 +04:00
im = op(im, (40, 10))
im_background = Image.new('RGB', (40, 10), (255, 255, 255))
im_background.paste(im, (0, 0), im)
2013-10-01 09:01:23 +04:00
2014-06-10 13:10:47 +04:00
hist = im_background.histogram()
self.assertEqual(40*10, hist[-1])
2014-06-10 13:10:47 +04:00
def test_alpha_premult_resize(self):
2014-06-10 13:10:47 +04:00
def op(im, sz):
2016-07-10 14:59:36 +03:00
return im.resize(sz, Image.BILINEAR)
2014-06-10 13:10:47 +04:00
self._test_alpha_premult(op)
2014-06-10 13:10:47 +04:00
def test_alpha_premult_transform(self):
2014-06-10 13:10:47 +04:00
def op(im, sz):
(w, h) = im.size
return im.transform(sz, Image.EXTENT,
(0, 0,
w, h),
Image.BILINEAR)
2014-06-10 13:10:47 +04:00
self._test_alpha_premult(op)
2014-06-10 13:10:47 +04:00
def test_blank_fill(self):
# attempting to hit
# https://github.com/python-pillow/Pillow/issues/254 reported
#
# issue is that transforms with transparent overflow area
# contained junk from previous images, especially on systems with
# constrained memory. So, attempt to fill up memory with a
# pattern, free it, and then run the mesh test again. Using a 1Mp
# image with 4 bands, for 4 megs of data allocated, x 64. OMM (64
# bit 12.04 VM with 512 megs available, this fails with Pillow <
# a0eaf06cc5f62a6fb6de556989ac1014ff3348ea
#
# Running by default, but I'd totally understand not doing it in
# the future
pattern = [
Image.new('RGBA', (1024, 1024), (a, a, a, a))
for a in range(1, 65)
]
2014-06-10 13:10:47 +04:00
# Yeah. Watch some JIT optimize this out.
pattern = None # noqa: F841
2014-06-10 13:10:47 +04:00
self.test_mesh()
def test_missing_method_data(self):
2017-09-01 14:05:40 +03:00
im = hopper()
self.assertRaises(ValueError, im.transform, (100, 100), None)
def test_unknown_resampling_filter(self):
im = hopper()
(w, h) = im.size
for resample in (Image.BOX, "unknown"):
self.assertRaises(ValueError, im.transform, (100, 100), Image.EXTENT,
(0, 0,
w, h),
resample)
2013-10-01 09:01:23 +04:00
2016-07-10 14:59:36 +03:00
class TestImageTransformAffine(PillowTestCase):
transform = Image.AFFINE
2016-07-10 14:59:36 +03:00
def _test_image(self):
im = hopper('RGB')
return im.crop((10, 20, im.width - 10, im.height - 20))
2016-07-11 00:47:58 +03:00
def _test_rotate(self, deg, transpose):
2016-07-10 14:59:36 +03:00
im = self._test_image()
2016-07-11 00:47:58 +03:00
angle = - math.radians(deg)
2016-07-10 21:47:59 +03:00
matrix = [
round(math.cos(angle), 15), round(math.sin(angle), 15), 0.0,
round(-math.sin(angle), 15), round(math.cos(angle), 15), 0.0,
2016-07-11 00:47:58 +03:00
0, 0]
2016-07-10 21:47:59 +03:00
matrix[2] = (1 - matrix[0] - matrix[1]) * im.width / 2
matrix[5] = (1 - matrix[3] - matrix[4]) * im.height / 2
if transpose is not None:
transposed = im.transpose(transpose)
else:
transposed = im
2016-07-11 00:47:58 +03:00
2016-07-10 14:59:36 +03:00
for resample in [Image.NEAREST, Image.BILINEAR, Image.BICUBIC]:
transformed = im.transform(transposed.size, self.transform,
2016-07-10 21:47:59 +03:00
matrix, resample)
2016-07-10 14:59:36 +03:00
self.assert_image_equal(transposed, transformed)
2016-07-10 21:47:59 +03:00
def test_rotate_0_deg(self):
self._test_rotate(0, None)
def test_rotate_90_deg(self):
self._test_rotate(90, Image.ROTATE_90)
2016-07-10 14:59:36 +03:00
def test_rotate_180_deg(self):
2016-07-10 21:47:59 +03:00
self._test_rotate(180, Image.ROTATE_180)
2016-07-10 14:59:36 +03:00
def test_rotate_270_deg(self):
2016-07-10 21:47:59 +03:00
self._test_rotate(270, Image.ROTATE_270)
2016-07-10 14:59:36 +03:00
2016-07-11 00:47:58 +03:00
def _test_resize(self, scale, epsilonscale):
2016-07-10 16:07:31 +03:00
im = self._test_image()
2016-07-11 00:47:58 +03:00
size_up = int(round(im.width * scale)), int(round(im.height * scale))
matrix_up = [
2016-07-10 21:47:59 +03:00
1 / scale, 0, 0,
0, 1 / scale, 0,
2016-07-11 00:47:58 +03:00
0, 0]
matrix_down = [
2016-07-10 21:47:59 +03:00
scale, 0, 0,
0, scale, 0,
2016-07-11 00:47:58 +03:00
0, 0]
for resample, epsilon in [(Image.NEAREST, 0),
(Image.BILINEAR, 2), (Image.BICUBIC, 1)]:
transformed = im.transform(
size_up, self.transform, matrix_up, resample)
transformed = transformed.transform(
im.size, self.transform, matrix_down, resample)
self.assert_image_similar(transformed, im, epsilon * epsilonscale)
2016-07-10 16:07:31 +03:00
def test_resize_1_1x(self):
2016-07-11 00:47:58 +03:00
self._test_resize(1.1, 6.9)
2016-07-10 16:07:31 +03:00
def test_resize_1_5x(self):
2016-07-11 00:47:58 +03:00
self._test_resize(1.5, 5.5)
2016-07-10 16:07:31 +03:00
def test_resize_2_0x(self):
2016-07-11 00:47:58 +03:00
self._test_resize(2.0, 5.5)
2016-07-10 16:07:31 +03:00
def test_resize_2_3x(self):
2016-07-11 00:47:58 +03:00
self._test_resize(2.3, 3.7)
2016-07-10 16:07:31 +03:00
def test_resize_2_5x(self):
2016-07-11 00:47:58 +03:00
self._test_resize(2.5, 3.7)
2016-07-10 21:47:59 +03:00
def _test_translate(self, x, y, epsilonscale):
im = self._test_image()
2016-07-11 00:47:58 +03:00
2016-07-10 21:47:59 +03:00
size_up = int(round(im.width + x)), int(round(im.height + y))
matrix_up = [
1, 0, -x,
0, 1, -y,
2016-07-11 00:47:58 +03:00
0, 0]
2016-07-10 21:47:59 +03:00
matrix_down = [
1, 0, x,
0, 1, y,
2016-07-11 00:47:58 +03:00
0, 0]
2016-07-10 21:47:59 +03:00
for resample, epsilon in [(Image.NEAREST, 0),
(Image.BILINEAR, 1.5), (Image.BICUBIC, 1)]:
transformed = im.transform(
size_up, self.transform, matrix_up, resample)
2016-07-10 21:47:59 +03:00
transformed = transformed.transform(
im.size, self.transform, matrix_down, resample)
2016-07-10 21:47:59 +03:00
self.assert_image_similar(transformed, im, epsilon * epsilonscale)
def test_translate_0_1(self):
self._test_translate(.1, 0, 3.7)
def test_translate_0_6(self):
self._test_translate(.6, 0, 9.1)
def test_translate_50(self):
self._test_translate(50, 50, 0)
2016-07-10 16:07:31 +03:00
2016-07-10 14:59:36 +03:00
class TestImageTransformPerspective(TestImageTransformAffine):
2016-08-31 13:12:07 +03:00
# Repeat all tests for AFFINE transformations with PERSPECTIVE
transform = Image.PERSPECTIVE