Added ImageOps exif_transpose method
BIN
Tests/images/hopper_orientation_2.jpg
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
Tests/images/hopper_orientation_2.webp
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
Tests/images/hopper_orientation_3.jpg
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
Tests/images/hopper_orientation_3.webp
Normal file
After Width: | Height: | Size: 3.7 KiB |
BIN
Tests/images/hopper_orientation_4.jpg
Normal file
After Width: | Height: | Size: 4.6 KiB |
BIN
Tests/images/hopper_orientation_4.webp
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
Tests/images/hopper_orientation_5.jpg
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
Tests/images/hopper_orientation_5.webp
Normal file
After Width: | Height: | Size: 3.5 KiB |
BIN
Tests/images/hopper_orientation_6.jpg
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
Tests/images/hopper_orientation_6.webp
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
Tests/images/hopper_orientation_7.jpg
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
Tests/images/hopper_orientation_7.webp
Normal file
After Width: | Height: | Size: 3.6 KiB |
BIN
Tests/images/hopper_orientation_8.jpg
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
Tests/images/hopper_orientation_8.webp
Normal file
After Width: | Height: | Size: 3.6 KiB |
|
@ -3,6 +3,12 @@ from .helper import PillowTestCase, hopper
|
||||||
from PIL import ImageOps
|
from PIL import ImageOps
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
try:
|
||||||
|
from PIL import _webp
|
||||||
|
HAVE_WEBP = True
|
||||||
|
except ImportError:
|
||||||
|
HAVE_WEBP = False
|
||||||
|
|
||||||
|
|
||||||
class TestImageOps(PillowTestCase):
|
class TestImageOps(PillowTestCase):
|
||||||
|
|
||||||
|
@ -62,6 +68,9 @@ class TestImageOps(PillowTestCase):
|
||||||
ImageOps.solarize(hopper("L"))
|
ImageOps.solarize(hopper("L"))
|
||||||
ImageOps.solarize(hopper("RGB"))
|
ImageOps.solarize(hopper("RGB"))
|
||||||
|
|
||||||
|
ImageOps.exif_transpose(hopper("L"))
|
||||||
|
ImageOps.exif_transpose(hopper("RGB"))
|
||||||
|
|
||||||
def test_1pxfit(self):
|
def test_1pxfit(self):
|
||||||
# Division by zero in equalize if image is 1 pixel high
|
# Division by zero in equalize if image is 1 pixel high
|
||||||
newimg = ImageOps.fit(hopper("RGB").resize((1, 1)), (35, 35))
|
newimg = ImageOps.fit(hopper("RGB").resize((1, 1)), (35, 35))
|
||||||
|
@ -218,3 +227,22 @@ class TestImageOps(PillowTestCase):
|
||||||
(0, 127, 0),
|
(0, 127, 0),
|
||||||
threshold=1,
|
threshold=1,
|
||||||
msg='white test pixel incorrect')
|
msg='white test pixel incorrect')
|
||||||
|
|
||||||
|
def test_exif_transpose(self):
|
||||||
|
exts = [".jpg"]
|
||||||
|
if HAVE_WEBP and _webp.HAVE_WEBPANIM:
|
||||||
|
exts.append(".webp")
|
||||||
|
for ext in exts:
|
||||||
|
base_im = Image.open("Tests/images/hopper"+ext)
|
||||||
|
|
||||||
|
orientations = [base_im]
|
||||||
|
for i in range(2, 9):
|
||||||
|
im = Image.open("Tests/images/hopper_orientation_"+str(i)+ext)
|
||||||
|
orientations.append(im)
|
||||||
|
for im in orientations:
|
||||||
|
transposed_im = ImageOps.exif_transpose(im)
|
||||||
|
self.assert_image_similar(base_im, transposed_im, 17)
|
||||||
|
|
||||||
|
# Repeat the operation, to test that it does not keep transposing
|
||||||
|
transposed_im2 = ImageOps.exif_transpose(transposed_im)
|
||||||
|
self.assert_image_equal(transposed_im2, transposed_im)
|
||||||
|
|
|
@ -522,3 +522,31 @@ def solarize(image, threshold=128):
|
||||||
else:
|
else:
|
||||||
lut.append(255-i)
|
lut.append(255-i)
|
||||||
return _lut(image, lut)
|
return _lut(image, lut)
|
||||||
|
|
||||||
|
|
||||||
|
def exif_transpose(image):
|
||||||
|
"""
|
||||||
|
If an image has an EXIF Orientation tag, return a new image that is
|
||||||
|
transposed accordingly. Otherwise, return a copy of the image.
|
||||||
|
|
||||||
|
:param image: The image to transpose.
|
||||||
|
:return: An image.
|
||||||
|
"""
|
||||||
|
if not hasattr(image, '_exif_transposed') and hasattr(image, '_getexif'):
|
||||||
|
exif = image._getexif()
|
||||||
|
if exif:
|
||||||
|
orientation = exif.get(0x0112)
|
||||||
|
method = {
|
||||||
|
2: Image.FLIP_LEFT_RIGHT,
|
||||||
|
3: Image.ROTATE_180,
|
||||||
|
4: Image.FLIP_TOP_BOTTOM,
|
||||||
|
5: Image.TRANSPOSE,
|
||||||
|
6: Image.ROTATE_270,
|
||||||
|
7: Image.TRANSVERSE,
|
||||||
|
8: Image.ROTATE_90
|
||||||
|
}.get(orientation)
|
||||||
|
if method is not None:
|
||||||
|
transposed_image = image.transpose(method)
|
||||||
|
transposed_image._exif_transposed = True
|
||||||
|
return transposed_image
|
||||||
|
return image.copy()
|
||||||
|
|