Hoo*_*pes 44 python python-imaging-library
我试图拍摄大(巨大)图像(来自数码相机),并将它们转换成我可以在网络上显示的内容.这似乎很简单,也许应该是.但是,当我尝试使用PIL创建缩略图版本时,如果我的源图像高于它的宽度,则生成的图像旋转90度,使得源图像的顶部位于结果图像的左侧.如果源图像宽度高于高图像,则生成的图像是正确的(原始)方向.它可能与我发送的2元组大小有关吗?我正在使用缩略图,因为它似乎是为了保持纵横比.或者我只是完全失明,做一些愚蠢的事情?大小元组是1000,1000,因为我希望最长边缩小到1000像素,同时保持AR保持.
代码似乎很简单
img = Image.open(filename)
img.thumbnail((1000,1000), Image.ANTIALIAS)
img.save(output_fname, "JPEG")
Run Code Online (Sandbox Code Playgroud)
在此先感谢您的帮助.
sto*_*_to 62
我赞同"unutbu"和Ignacio Vazquez-Abrams所回答的几乎所有内容,但......
EXIF方向标志的值可以在1到8之间,具体取决于相机的握持方式.
可以在左侧或右侧边缘使用相机顶部拍摄人像照片,可以将照片上下颠倒拍摄.
这是考虑到这一点的代码(使用DSLR尼康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()
Run Code Online (Sandbox Code Playgroud)
Mai*_*fel 32
xilvar的答案非常好,但有两个小缺点,我想在被拒绝的编辑中修复,所以我会将其作为答案发布.
首先,如果文件不是JPEG或者没有exif数据,xilvar的解决方案将失败.另一方面,它总是旋转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()
Run Code Online (Sandbox Code Playgroud)
Rom*_*sky 31
感觉被迫分享我的版本,这在功能上与其他答案中建议的相同,但在我看来,更清洁:
import Image
import functools
def image_transpose_exif(im):
"""
Apply Image.transpose to ensure 0th row of pixels is at the visual
top of the image, and 0th column is the visual left-hand side.
Return the original image if unable to determine the orientation.
As per CIPA DC-008-2012, the orientation field contains an integer,
1 through 8. Other values are reserved.
"""
exif_orientation_tag = 0x0112
exif_transpose_sequences = [ # Val 0th row 0th col
[], # 0 (reserved)
[], # 1 top left
[Image.FLIP_LEFT_RIGHT], # 2 top right
[Image.ROTATE_180], # 3 bottom right
[Image.FLIP_TOP_BOTTOM], # 4 bottom left
[Image.FLIP_LEFT_RIGHT, Image.ROTATE_90], # 5 left top
[Image.ROTATE_270], # 6 right top
[Image.FLIP_TOP_BOTTOM, Image.ROTATE_90], # 7 right bottom
[Image.ROTATE_90], # 8 left bottom
]
try:
seq = exif_transpose_sequences[im._getexif()[exif_orientation_tag]]
except Exception:
return im
else:
return functools.reduce(type(im).transpose, seq, im)
Run Code Online (Sandbox Code Playgroud)
Dob*_*eer 20
这是一个适用于所有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
Run Code Online (Sandbox Code Playgroud)
Jac*_*ing 15
Pillow 有一个 API可以自动处理 EXIF 方向标签:
from PIL import Image, ImageOps
original_image = Image.open(filename)
fixed_image = ImageOps.exif_transpose(original_image)
Run Code Online (Sandbox Code Playgroud)
unu*_*tbu 13
请注意,下面有更好的答案.
当图片高于宽度时,表示相机已旋转.有些相机可以检测到这一点并在图片的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")
Run Code Online (Sandbox Code Playgroud)
但请注意,上述代码可能不适用于所有相机.
最简单的解决方案可能是使用其他程序来制作缩略图.
phatch是一个用Python编写的批处理照片编辑器,可以处理/保存EXIF元数据.您可以使用此程序制作缩略图,也可以查看其源代码以了解如何在Python中执行此操作.我相信它使用pyexiv2来处理EXIF元数据.pyexiv2可能比PIL的ExifTags模块更好地处理EXIF.
imagemagick是制作批量缩略图的另一种可能性.
Has*_*aig 10
我需要一个解决所有方向的解决方案,而不仅仅是3
,6
和8
。
我尝试了 Roman Odaisky 的解决方案- 它看起来全面而干净。然而,与有时会导致错误的结果(如各个方位值实际图像测试它这样一个有方向设置为0
)。
另一个可行的解决方案可能是 Dobes Vandermeer's。但是我没有尝试过,因为我觉得可以更简单地编写逻辑(我更喜欢)。
所以不用多说,这是一个更简单、更易于维护(在我看来)的版本:
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
Run Code Online (Sandbox Code Playgroud)
经过测试,发现可以处理具有所有提到的 exif 方向的图像。但是,也请自己进行测试。
小智 6
Hoope的答案很好,但使用转置方法而不是旋转效率要高得多.旋转对每个像素执行实际的滤波计算,实际上是整个图像的复杂大小调整.此外,当前的PIL库似乎有一个错误,其中黑色线被添加到旋转图像的边缘.转置速度更快,并且缺少该错误.我只是调整了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()
Run Code Online (Sandbox Code Playgroud)
我是编程,Python和PIL的菜鸟,所以前面的答案中的代码示例对我来说似乎很复杂.我没有遍历标签,而是直接找到标签的钥匙.在python shell中,您可以看到方向的键是274.
>>>from PIL import ExifTags
>>>ExifTags.TAGS
Run Code Online (Sandbox Code Playgroud)
我使用该image._getexif()
函数来获取图像中的ExifTags.如果不存在方向标记,则会抛出错误,因此我使用try/except.
Pillow的文档说旋转和转置之间的性能或结果没有区别.我通过计算两个函数来确认它.我使用旋转因为它更简洁.
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
Run Code Online (Sandbox Code Playgroud)