如何保持宽高比并使竖向和横向图片尺寸完全相同?

12

目前我在使用:

    os.chdir(album.path)
    images = glob.glob('*.*')

    # thumbs size
    size = 80,80

    for image in images:
        #create thumb
        file, ext = os.path.splitext(image)
        im = Image.open(os.path.join(album.path,image))
        im.thumbnail(size, Image.ANTIALIAS)
        thumb_path = os.path.join(album.path, 'thumbs', file + ".thumb" + ".jpeg")
        im.save(thumb_path)
尽管这样可以工作,但我最终得到的图像大小不同(有些是纵向的,有些是横向的),但我希望所有的图像都具有精确的尺寸。也许需要进行合理的裁剪?

更新:


我不介意裁剪图像的一小部分。当我说合理的裁剪时,我的意思是像这样的算法:

if image is portrait:
    make width 80px
    crop the height (will be more than 80px)
else if image is landscape:
    make height 80px
    crop the width to 80px (will be more than 80px)

请考虑阅读手册。thumnail方法旨在保留图像的宽高比。显然,您不能同时拥有两者,因为不是所有图像都具有相同的宽高比。如果您想重新调整大小而不保留比例,请考虑使用resize - Has QUIT--Anony-Mousse
1
如果他想要进行适应操作,他可以使它们大小相同,这将使用恒定的颜色填充额外的区域。这将允许4x3或16x9或任何长宽比的图像适合于给定的80x80尺寸。 - jdi
很遗憾,目前没有一种标准的方法来定义“合理”的裁剪方式。听起来你想从图像中始终裁剪出一个正方形,这意味着你可能会切掉你真正想要保留的部分。 - Mark Ransom
@MarkRansom - 我同意。如果您的源图像具有非常不正方形的方面,您将面临失去大部分图像的风险。我认为最好使用适合的填充。请参见我的答案。 - jdi
谢谢大家的回答,但我更喜欢裁剪图片并失去一部分而不是添加填充。请参见我的更新。 - avlnx
3个回答

19

这是我的方法来对图像进行填充适应:

#!/usr/bin/env python

from PIL import Image, ImageChops

F_IN = "/path/to/image_in.jpg"
F_OUT = "/path/to/image_out.jpg"

size = (80,80)

image = Image.open(F_IN)
image.thumbnail(size, Image.ANTIALIAS)
image_size = image.size

thumb = image.crop( (0, 0, size[0], size[1]) )

offset_x = max( (size[0] - image_size[0]) / 2, 0 )
offset_y = max( (size[1] - image_size[1]) / 2, 0 )

thumb = ImageChops.offset(thumb, offset_x, offset_y)
thumb.save(F_OUT)

该方法首先使用缩略图操作将图像缩小到原始边界内并保持其纵横比。然后将其裁剪回以实际填充您的边界大小(因为除非原始图像是正方形,否则现在它会更小),并找到正确的偏移量使图像居中。图像被偏移到中心,因此最终会出现黑色填充但不会裁剪图像。

除非您可以在不丢失可能重要的图像数据的情况下对适当的中心裁剪进行明智的猜测,否则填充式的适配方法效果更好。

更新

这里是一个可以执行中心裁剪或填充适配的版本。

#!/usr/bin/env python

from PIL import Image, ImageChops, ImageOps

def makeThumb(f_in, f_out, size=(80,80), pad=False):

    image = Image.open(f_in)
    image.thumbnail(size, Image.ANTIALIAS)
    image_size = image.size

    if pad:
        thumb = image.crop( (0, 0, size[0], size[1]) )

        offset_x = max( (size[0] - image_size[0]) / 2, 0 )
        offset_y = max( (size[1] - image_size[1]) / 2, 0 )

        thumb = ImageChops.offset(thumb, offset_x, offset_y)

    else:
        thumb = ImageOps.fit(image, size, Image.ANTIALIAS, (0.5, 0.5))

    thumb.save(f_out)


source = "/path/to/source/image.JPG"

makeThumb(source, "/path/to/source/image_padded.JPG", pad=True)
makeThumb(source, "/path/to/source/image_centerCropped.JPG", pad=False)

这非常接近我需要的@jdi,谢谢!我如何修改它以剪切而不是插入填充?(即使失去图像的一部分) - avlnx

2

很明显,你需要裁剪或填充图像。你可以采用以下方法根据缩略图的宽高比来实现最大中心裁剪(未经测试):

aspect = lambda size: float(size[0]) / float(size[1])
sa = aspect(size)
if aspect(im.size) > sa:
    width = int(sa * im.size[1])
    left = (im.size[0] - width) / 2
    im = im.crop((left, 0, left + width, im.size[1]))
else:
    height = int(im.size[0] / sa)
    top = (im.size[1] - height) / 2
    im = im.crop((0, top, im.size[0], top + height))
im.thumbnail(size, Image.ANTIALIAS)

这个例子有一些错误。im.crop()不能直接原地剪裁,需要将图像对象返回给自身。crop的参数需要是一个元组盒子,所以需要用()括起来。你正在传递浮点数给crop。需要使用int(width)和int(height)。而且,所有这些都不必要,因为有一个内置的方法: im = ImageOps.fit(im, size, Image.ANTIALIAS, (0.5, 0.5)) 可以做同样的事情。 - jdi

0
如果您使用easy-thumbnails,您需要将crop设置为True,并将upscale设置为True,以始终填充空间(具有完全相同的尺寸)。

例如:使image_2适合于image_1的尺寸:

    thumbnailer = get_thumbnailer(image_2)
    thumbnail = thumbnailer.generate_thumbnail(thumbnail_options={
        'crop': True,
        'upscale': True,
        'size': image_1.size
    })
    image_2 = thumbnail.image

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接