代码看起来很简单。
img = Image.open(filename)
img.thumbnail((1000,1000), Image.ANTIALIAS)
img.save(output_fname, "JPEG")
提前感谢任何帮助。
img = Image.open(filename)
img.thumbnail((1000,1000), Image.ANTIALIAS)
img.save(output_fname, "JPEG")
提前感谢任何帮助。
我同意“unutbu”和Ignacio Vazquez-Abrams的几乎所有回答,然而...
EXIF方向标志可以具有1到8之间的值,具体取决于相机的拍摄方式。
竖直照片可以在左侧或右侧将相机顶部朝上拍摄,水平照片可以倒置拍摄。
这是一段考虑到这一点的代码(已经通过了DSLR Nikon D80的测试)。
import Image, ExifTags
try :
image=Image.open(os.path.join(path, fileName))
for orientation in ExifTags.TAGS.keys() :
if ExifTags.TAGS[orientation]=='Orientation' : break
exif=dict(image._getexif().items())
if exif[orientation] == 3 :
image=image.rotate(180, expand=True)
elif exif[orientation] == 6 :
image=image.rotate(270, expand=True)
elif exif[orientation] == 8 :
image=image.rotate(90, expand=True)
image.thumbnail((THUMB_WIDTH , THUMB_HIGHT), Image.ANTIALIAS)
image.save(os.path.join(path,fileName))
except:
traceback.print_exc()
与本问题中提出的每个函数(包括我的原始函数)不同,它会注意从EXIF中删除方向字段(因为图像不再以奇怪的方式定位),并确保返回值是全新的图像对象,因此对其所做的更改不会影响原始对象。
import functools
,但仍然应该被接受作为Python 2.7中可直接使用的答案。 - Kapitein Witbaardrotate
而不是transpose
,要么缺少所有八个可能的状态。JPEG的退出方向是用于转置而不是旋转。http://jpegclub.org/exif_orientation.html - OdraEncoded0
):https://imgur.com/a/053MR - Hassan Baigxilvar的答案很好,但有两个小缺陷需要修正,在一次被拒绝的编辑中我已经解决了这些问题,所以我将其发表为答案。
首先,xilvar的解决方案在文件不是JPEG格式或没有exif数据时会失败。另外,它总是旋转180度而非适当的角度。
import Image, ExifTags
try:
image=Image.open(os.path.join(path, fileName))
if hasattr(image, '_getexif'): # only present in JPEGs
for orientation in ExifTags.TAGS.keys():
if ExifTags.TAGS[orientation]=='Orientation':
break
e = image._getexif() # returns None if no EXIF data
if e is not None:
exif=dict(e.items())
orientation = exif[orientation]
if orientation == 3: image = image.transpose(Image.ROTATE_180)
elif orientation == 6: image = image.transpose(Image.ROTATE_270)
elif orientation == 8: image = image.transpose(Image.ROTATE_90)
image.thumbnail((THUMB_WIDTH , THUMB_HIGHT), Image.ANTIALIAS)
image.save(os.path.join(path,fileName))
except:
traceback.print_exc()
orientation = exif.get(orientation, None)
然后if orientation is None: return
,并添加一些日志,说明图像的exif可能无效。我不是说它会对每个人都造成错误,但它曾经发生过在我身上,而且可能非常罕见。 - Bartosz Dabrowskiorientation = next(k for k, v in ExifTags.TAGS.items() if v == 'Orientation')
,因为该脚本依赖于此标签,而 PIL 的 ExifTags.py
库似乎已经包含了它。 - kirpitPillow有一个API可以自动处理EXIF方向标签:(参见)
from PIL import Image, ImageOps
original_image = Image.open(filename)
fixed_image = ImageOps.exif_transpose(original_image)
请注意,下面有更好的答案。
当一张图片的高度大于宽度时,这意味着相机被旋转了。一些相机可以检测到这种情况,并在图片的EXIF元数据中写入该信息。一些查看器会注意到这些元数据并适当地显示图像。
PIL可以读取图片的元数据,但在保存图像时不会写入/复制元数据。因此,您的智能图像查看器将无法像以前那样旋转图像。
继@Ignacio Vazquez-Abrams的评论后,您可以使用PIL这种方式读取元数据,然后根据需要旋转:
import ExifTags
import Image
img = Image.open(filename)
print(img._getexif().items())
exif=dict((ExifTags.TAGS[k], v) for k, v in img._getexif().items() if k in ExifTags.TAGS)
if not exif['Orientation']:
img=img.rotate(90, expand=True)
img.thumbnail((1000,1000), Image.ANTIALIAS)
img.save(output_fname, "JPEG")
请注意,上述代码可能不适用于所有相机。
最简单的解决方案可能是使用其他程序来制作缩略图。
phatch 是一个用Python编写的批量照片编辑器,可以处理/保留EXIF元数据。你可以使用这个程序来制作缩略图,或者查看其源代码以了解如何在Python中实现。我相信它使用pyexiv2 来处理EXIF元数据。pyexiv2 可能比PIL 的ExifTags模块更好地处理EXIF。
imagemagick 是另一种制作批量缩略图的可能性。
这里有一个适用于所有8个方向的版本:
def flip_horizontal(im): return im.transpose(Image.FLIP_LEFT_RIGHT)
def flip_vertical(im): return im.transpose(Image.FLIP_TOP_BOTTOM)
def rotate_180(im): return im.transpose(Image.ROTATE_180)
def rotate_90(im): return im.transpose(Image.ROTATE_90)
def rotate_270(im): return im.transpose(Image.ROTATE_270)
def transpose(im): return rotate_90(flip_horizontal(im))
def transverse(im): return rotate_90(flip_vertical(im))
orientation_funcs = [None,
lambda x: x,
flip_horizontal,
rotate_180,
flip_vertical,
transpose,
rotate_270,
transverse,
rotate_90
]
def apply_orientation(im):
"""
Extract the oritentation EXIF tag from the image, which should be a PIL Image instance,
and if there is an orientation tag that would rotate the image, apply that rotation to
the Image instance given to do an in-place rotation.
:param Image im: Image instance to inspect
:return: A possibly transposed image instance
"""
try:
kOrientationEXIFTag = 0x0112
if hasattr(im, '_getexif'): # only present in JPEGs
e = im._getexif() # returns None if no EXIF data
if e is not None:
#log.info('EXIF data found: %r', e)
orientation = e[kOrientationEXIFTag]
f = orientation_funcs[orientation]
return f(im)
except:
# We'd be here with an invalid orientation value or some random error?
pass # log.exception("Error applying EXIF Orientation tag")
return im
我需要一个能处理所有方向而不仅仅是 3
, 6
和8
的解决方案。
我尝试了 Roman Odaisky 的解决方案 - 它看起来全面而干净。但是,用实际图像测试它的多种方向值有时会导致错误结果(例如这个设置为方向0
的)。
另一个可行的解决方案可能是Dobes Vandermeer提供的。但我没有尝试过,因为我觉得可以更简单地编写逻辑(这是我喜欢的)。
所以,不再拖延,以下是一个更简单、更易维护(在我看来)的版本:
from PIL import Image
def reorient_image(im):
try:
image_exif = im._getexif()
image_orientation = image_exif[274]
if image_orientation in (2,'2'):
return im.transpose(Image.FLIP_LEFT_RIGHT)
elif image_orientation in (3,'3'):
return im.transpose(Image.ROTATE_180)
elif image_orientation in (4,'4'):
return im.transpose(Image.FLIP_TOP_BOTTOM)
elif image_orientation in (5,'5'):
return im.transpose(Image.ROTATE_90).transpose(Image.FLIP_TOP_BOTTOM)
elif image_orientation in (6,'6'):
return im.transpose(Image.ROTATE_270)
elif image_orientation in (7,'7'):
return im.transpose(Image.ROTATE_270).transpose(Image.FLIP_TOP_BOTTOM)
elif image_orientation in (8,'8'):
return im.transpose(Image.ROTATE_90)
else:
return im
except (KeyError, AttributeError, TypeError, IndexError):
return im
已经测试并发现在所有提到的Exif方向的图像上都可以正常工作。但是,请您也进行自己的测试。
PIL==6.2.2
中有一个小问题,im
没有_getexif()
,但有getexif()
。 - jkhadka我是一个编程新手,对Python和PIL都不熟悉,前面答案中的代码示例对我来说很复杂。所以,我直接找到了标签的键值,而没有遍历标签。在Python shell中,您可以看到方向的键值是274。
>>>from PIL import ExifTags
>>>ExifTags.TAGS
image._getexif()
函数获取图像中的 ExifTags。如果方向标签不存在,则会引发错误,因此我使用 try/except。rotate(90)
逆时针旋转,该函数似乎接受负度数。from PIL import Image, ExifTags
# Open file with Pillow
image = Image.open('IMG_0002.jpg')
#If no ExifTags, no rotating needed.
try:
# Grab orientation value.
image_exif = image._getexif()
image_orientation = image_exif[274]
# Rotate depending on orientation.
if image_orientation == 3:
rotated = image.rotate(180)
if image_orientation == 6:
rotated = image.rotate(-90)
if image_orientation == 8:
rotated = image.rotate(90)
# Save rotated image.
rotated.save('rotated.jpg')
except:
pass
Hoopes的回答很好,但使用转置方法比旋转更有效率。旋转对每个像素进行实际的过滤计算,实际上是整个图像的复杂调整大小。另外,当前的PIL库似乎存在一个bug,旋转图像的边缘会添加一条黑线。转置要快得多,而且没有那个bug。我只是稍微修改了hoopes的答案,改用了转置。
import Image, ExifTags
try :
image=Image.open(os.path.join(path, fileName))
for orientation in ExifTags.TAGS.keys() :
if ExifTags.TAGS[orientation]=='Orientation' : break
exif=dict(image._getexif().items())
if exif[orientation] == 3 :
image=image.transpose(Image.ROTATE_180)
elif exif[orientation] == 6 :
image=image.rotate(Image.ROTATE_180)
elif exif[orientation] == 8 :
image=image.rotate(Image.ROTATE_180)
image.thumbnail((THUMB_WIDTH , THUMB_HIGHT), Image.ANTIALIAS)
image.save(os.path.join(path,fileName))
except:
traceback.print_exc()
除了其他答案之外,我遇到了问题,因为在运行函数之前我会使用im.copy()
-- 这会剥离必要的exif数据。确保在运行im.copy()
之前保存exif数据:
try:
exif = im._getexif()
except Exception:
exif = None
# ...
# im = im.copy() somewhere
# ...
if exif:
im = transpose_im(im, exif)
.thumbnail()
不会旋转 - 我使用img.show()
进行了验证。实际上是.save()
方法将其写入文件。我的尝试是:- 尝试将其写入内存文件而不是磁盘文件from io import BytesIO; buffer = BytesIO; img.save(buffer, "JPEG"); Image.open(buffer).show()
- Grijesh Chauhan