Python PIL缩略图正在旋转我的图像?

Python PIL缩略图正在旋转我的图像?,python,python-imaging-library,Python,Python Imaging Library,我正在尝试(从数码相机)拍摄大(巨大)图像,并将它们转换成我可以在网上显示的东西。这似乎很简单,也许应该如此。但是,当我尝试使用PIL创建缩略图版本时,如果源图像的高度大于宽度,则生成的图像将旋转90度,从而使源图像的顶部位于生成图像的左侧。如果源图像的宽度大于其高度,则生成的图像是正确的(原始)方向。这可能与我发送的2元组大小有关吗?我使用缩略图,因为它似乎是为了保持纵横比。还是我完全瞎了眼,做了些傻事?大小元组是10001000,因为我希望最长的边缩小到1000像素,同时保持AR不变

我正在尝试(从数码相机)拍摄大(巨大)图像,并将它们转换成我可以在网上显示的东西。这似乎很简单,也许应该如此。但是,当我尝试使用PIL创建缩略图版本时,如果源图像的高度大于宽度,则生成的图像将旋转90度,从而使源图像的顶部位于生成图像的左侧。如果源图像的宽度大于其高度,则生成的图像是正确的(原始)方向。这可能与我发送的2元组大小有关吗?我使用缩略图,因为它似乎是为了保持纵横比。还是我完全瞎了眼,做了些傻事?大小元组是10001000,因为我希望最长的边缩小到1000像素,同时保持AR不变

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
代码似乎很简单

img = Image.open(filename)
img.thumbnail((1000,1000), Image.ANTIALIAS)
img.save(output_fname, "JPEG")
    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)

提前感谢您的帮助。

请注意,下面有更好的答案。

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)

当图片的高度大于宽度时,表示相机已旋转。一些相机可以检测到这一点,并将该信息写入图片的EXIF元数据中。一些查看者会注意到此元数据并适当地显示图像

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
PIL可以读取图片的元数据,但在保存图像时不会写入/复制元数据。因此,您的智能图像查看器不会像以前那样旋转图像

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
在@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")
    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
但请注意,上述代码可能不适用于所有摄像头

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
最简单的解决方案可能是使用其他程序制作缩略图

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
是一个用Python编写的批处理照片编辑器,可以处理/保留EXIF元数据。您可以使用此程序制作缩略图,也可以查看其源代码,了解如何在Python中实现这一点。我相信它使用了来处理EXIF元数据。pyexiv2可能比PIL的ExifTags模块更好地处理EXIF

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)

是制作批量缩略图的另一种可能性。

我同意“unutbu”和Ignacio Vazquez Abrams回答的几乎所有问题,但是

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
EXIF方向标志的值可以介于1和8之间,具体取决于相机的固定方式

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
肖像照片可以在相机顶部位于左侧或右侧拍摄,风景照片可以倒置拍摄

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
下面是考虑到这一点的代码(使用DSLR尼康D80测试)

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)

Hoopes的答案很好,但使用转置方法比旋转方法更有效。旋转对每个像素进行实际的过滤计算,有效地调整整个图像的复杂大小。此外,当前的PIL库似乎有一个缺陷,在旋转图像的边缘添加了一条黑线。转置速度快得多,而且没有那个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()
    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)

Xivar的答案很好,但有两个小缺点,我想在一个被拒绝的编辑中修复,所以我将把它作为一个答案发布

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
首先,如果文件不是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()
    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)

以下是一个适用于所有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
    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)

您好,我试图实现图像的旋转,多亏了这篇文章前面的答案,我做到了。但我升级了解决方案,并希望与大家分享。我希望有人会觉得这很有用

def get_rotation_code(img):
    """
    Returns rotation code which say how much photo is rotated.
    Returns None if photo does not have exif tag information. 
    Raises Exception if cannot get Orientation number from python 
    image library.
    """
    if not hasattr(img, '_getexif') or img._getexif() is None:
        return None

    for code, name in ExifTags.TAGS.iteritems():
        if name == 'Orientation':
            orientation_code = code
            break
    else:
        raise Exception('Cannot get orientation code from library.')

    return img._getexif().get(orientation_code, None)


class IncorrectRotationCode(Exception):
    pass


def rotate_image(img, rotation_code):
    """
    Returns rotated image file.

    img: PIL.Image file.
    rotation_code: is rotation code retrieved from get_rotation_code.
    """
    if rotation_code == 1:
        return img
    if rotation_code == 3:
        img = img.transpose(Image.ROTATE_180)
    elif rotation_code == 6:
        img = img.transpose(Image.ROTATE_270)
    elif rotation_code == 8:
        img = img.transpose(Image.ROTATE_90)
    else:
        raise IncorrectRotationCode('{} is unrecognized '
                                    'rotation code.'
                                    .format(rotation_code))
    return img
    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
使用:

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)

我对编程、Python和PIL一无所知,所以前面答案中的代码示例对我来说似乎很复杂。我没有遍历标记,而是直接找到了标记的键。在pythonshell中,可以看到方向键是274

>>>from PIL import ExifTags
>>>ExifTags.TAGS
    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
我使用
图像。_getexif()
函数获取图像中的ExitTag。如果方向标记不存在,它会抛出一个错误,所以我使用try/except

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
Pillow的文档说,旋转和转置在性能或结果上没有区别。我已经通过对这两个功能进行计时来确认这一点。我使用rotate是因为它更简洁

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
旋转(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
    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
只需从枕头上使用。
与回答此问题时提出的每个函数(包括我的原始函数)不同,它会小心地从EXIF中删除方向字段(因为图像不再以奇怪的方式定向)另外,为了确保返回值是一个全新的图像对象,因此对其所做的更改不会影响原始图像对象。

这里有一些很好的答案,我只想发布一个经过清理的版本。。。
    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
该函数假定您已经在某个位置执行了Image.open(),并将在其他位置执行Image.save(),只需要一个可以插入以修复旋转的函数

def _fix_image_rotation(image):
 orientation_to_rotation_map = {
     3: Image.ROTATE_180,
     6: Image.ROTATE_270,
     8: Image.ROTATE_90,
 }
 try:
     exif = _get_exif_from_image(image)
     orientation = _get_orientation_from_exif(exif)
     rotation = orientation_to_rotation_map.get(orientation)
     if rotation:
         image = image.transpose(rotation)

 except Exception as e:
     # Would like to catch specific exceptions, but PIL library is poorly documented on Exceptions thrown
     # Log error here

 finally:
     return image


def _get_exif_from_image(image):
 exif = {}

 if hasattr(image, '_getexif'):  # only jpegs have _getexif
     exif_or_none = image._getexif()
     if exif_or_none is not None:
         exif = exif_or_none

 return exif


def _get_orientation_from_exif(exif):
 ORIENTATION_TAG = 'Orientation'
 orientation_iterator = (
     exif.get(tag_key) for tag_key, tag_value in ExifTags.TAGS.items()
     if tag_value == ORIENTATION_TAG
 )
 orientation = next(orientation_iterator, None)
 return orientation
    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)

我需要一个能兼顾所有方向的解决方案,而不仅仅是
3
6
8

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
我试过Roman Odaisky的——它看起来既全面又干净。但是,使用具有不同方向值的实际图像进行测试有时会导致错误结果(例如,方向设置为
0

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
另一个可能是Dobes Vandermer的。但我没有尝试过,因为我觉得可以更简单地编写逻辑(我更喜欢这样)

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
因此,不用多说,这里有一个更简单、更易于维护(在我看来)的版本:

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)

经过测试,发现可以处理所有提到的exif方向的图像。但是,也请自己做测试。

除了其他答案之外,我遇到了一些问题,因为在运行函数之前,我会使用
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)
    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)

有一种更简单的方法来实现这一切:

    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)
我希望它能有所帮助:)

枕头必须处理进出口方向
    from PIL import image
    im1 = Image.open(path_to_image)
    im1.thumbnail(size1, Image.ANTIALIAS)
    y, z = im1.size
    d = z * 1.5
    if y > d:
         im1.rotate(90, expand=1)