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() | ||||||
|  |  | ||||||