diff --git a/Tests/images/g4_orientation_1.tif b/Tests/images/g4_orientation_1.tif new file mode 100755 index 000000000..8ab0f1d0d Binary files /dev/null and b/Tests/images/g4_orientation_1.tif differ diff --git a/Tests/images/g4_orientation_2.tif b/Tests/images/g4_orientation_2.tif new file mode 100755 index 000000000..4ab085641 Binary files /dev/null and b/Tests/images/g4_orientation_2.tif differ diff --git a/Tests/images/g4_orientation_3.tif b/Tests/images/g4_orientation_3.tif new file mode 100755 index 000000000..ca0d0fe29 Binary files /dev/null and b/Tests/images/g4_orientation_3.tif differ diff --git a/Tests/images/g4_orientation_4.tif b/Tests/images/g4_orientation_4.tif new file mode 100755 index 000000000..166381fb7 Binary files /dev/null and b/Tests/images/g4_orientation_4.tif differ diff --git a/Tests/images/g4_orientation_5.tif b/Tests/images/g4_orientation_5.tif new file mode 100755 index 000000000..9fecaad65 Binary files /dev/null and b/Tests/images/g4_orientation_5.tif differ diff --git a/Tests/images/g4_orientation_6.tif b/Tests/images/g4_orientation_6.tif new file mode 100755 index 000000000..6abc001eb Binary files /dev/null and b/Tests/images/g4_orientation_6.tif differ diff --git a/Tests/images/g4_orientation_7.tif b/Tests/images/g4_orientation_7.tif new file mode 100755 index 000000000..0babc9108 Binary files /dev/null and b/Tests/images/g4_orientation_7.tif differ diff --git a/Tests/images/g4_orientation_8.tif b/Tests/images/g4_orientation_8.tif new file mode 100755 index 000000000..3216a3725 Binary files /dev/null and b/Tests/images/g4_orientation_8.tif differ diff --git a/Tests/test_imageops.py b/Tests/test_imageops.py index 85529a708..d55e6da36 100644 --- a/Tests/test_imageops.py +++ b/Tests/test_imageops.py @@ -70,6 +70,9 @@ class TestImageOps(PillowTestCase): ImageOps.exif_transpose(hopper("L")) ImageOps.exif_transpose(hopper("RGB")) + ImageOps.auto_transpose(hopper("L")) + ImageOps.auto_transpose(hopper("RGB")) + def test_1pxfit(self): # Division by zero in equalize if image is 1 pixel high newimg = ImageOps.fit(hopper("RGB").resize((1, 1)), (35, 35)) @@ -250,28 +253,47 @@ class TestImageOps(PillowTestCase): exts = [".jpg"] if HAVE_WEBP and _webp.HAVE_WEBPANIM: exts.append(".webp") - for ext in exts: - base_im = Image.open("Tests/images/hopper" + ext) + for transpose in [ImageOps.exif_transpose, ImageOps.auto_transpose]: + 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 i, orientation_im in enumerate(orientations): - for im in [orientation_im, orientation_im.copy()]: # ImageFile # Image - if i == 0: - self.assertNotIn("exif", im.info) - else: - original_exif = im.info["exif"] - transposed_im = ImageOps.exif_transpose(im) - self.assert_image_similar(base_im, transposed_im, 17) - if i == 0: - self.assertNotIn("exif", im.info) - else: - self.assertNotEqual(transposed_im.info["exif"], original_exif) + orientations = [base_im] + for i in range(2, 9): + im = Image.open("Tests/images/hopper_orientation_" + str(i) + ext) + orientations.append(im) + for i, orientation_im in enumerate(orientations): + for im in [ + orientation_im, + orientation_im.copy(), + ]: # ImageFile # Image + if i == 0: + self.assertNotIn("exif", im.info) + else: + original_exif = im.info["exif"] + transposed_im = transpose(im) + self.assert_image_similar(base_im, transposed_im, 17) + if i == 0: + self.assertNotIn("exif", im.info) + else: + self.assertNotEqual( + transposed_im.info["exif"], original_exif + ) - self.assertNotIn(0x0112, transposed_im.getexif()) + self.assertNotIn(0x0112, transposed_im.getexif()) - # 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) + # Repeat the operation, + # to test that it does not keep transposing + transposed_im2 = transpose(transposed_im) + self.assert_image_equal(transposed_im2, transposed_im) + + def test_auto_transpose(self): + base_im = Image.open("Tests/images/g4_orientation_1.tif") + + for i in range(2, 9): + im = Image.open("Tests/images/g4_orientation_" + str(i) + ".tif") + transposed_im = ImageOps.auto_transpose(im) + self.assert_image_similar(base_im, transposed_im, 0.7) + + # Repeat the operation, to test that it does not keep transposing + transposed_im2 = ImageOps.auto_transpose(transposed_im) + self.assert_image_equal(transposed_im2, transposed_im) diff --git a/src/PIL/ImageOps.py b/src/PIL/ImageOps.py index cfdd5c02a..586854050 100644 --- a/src/PIL/ImageOps.py +++ b/src/PIL/ImageOps.py @@ -520,16 +520,7 @@ def solarize(image, threshold=128): 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. - """ - exif = image.getexif() - orientation = exif.get(0x0112) +def _transpose(image, orientation, exif=None): method = { 2: Image.FLIP_LEFT_RIGHT, 3: Image.ROTATE_180, @@ -541,7 +532,40 @@ def exif_transpose(image): }.get(orientation) if method is not None: transposed_image = image.transpose(method) - del exif[0x0112] - transposed_image.info["exif"] = exif.tobytes() + if exif: + del exif[0x0112] + transposed_image.info["exif"] = exif.tobytes() return transposed_image return image.copy() + + +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. + """ + exif = image.getexif() + orientation = exif.get(0x0112) + return _transpose(image, orientation, exif) + + +def auto_transpose(image): + """ + If an image has an EXIF or TIFF 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. + """ + exif = image.getexif() + orientation = exif.get(0x0112) + if orientation and orientation != 1: + return _transpose(image, orientation, exif) + elif hasattr(image, "tag_v2"): + orientation = image.tag_v2.get(0x0112) + return _transpose(image, orientation) + else: + return image.copy()