调整图像大小以保持纵横比并使纵向和横向图像尺寸完全相同?

Posted

技术标签:

【中文标题】调整图像大小以保持纵横比并使纵向和横向图像尺寸完全相同?【英文标题】:Resize image maintaining aspect ratio AND making portrait and landscape images exact same size? 【发布时间】:2012-02-24 13:43:39 【问题描述】:

目前我正在使用:

    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 如果他想合身,他可以让它们的尺寸相同,这样可以用恒定的颜色填充额外的区域。这将允许 4x3 或 16x9 或任何方面的图像适合 80x80 的给定尺寸 不幸的是,没有标准的方法来定义裁剪的“合理”。听起来您总是想从图像中裁剪出一个正方形,这意味着您可能会切掉您真正想要保留的部分。 @MarkRansom - 我同意。如果您的源图像恰好具有非常非方形的外观,您将面临丢失大部分图像的风险。我认为合适的填充物将是最好的。看我的回答。 感谢大家的回答,但我更喜欢剪切图像并丢失其中的一部分而不是添加填充。请参阅上面的更新。 【参考方案1】:

显然,您需要裁剪或填充图像。您可以执行以下操作,根据缩略图的纵横比(未经测试)获得最大的居中裁剪:

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 的参数需要是一个盒子元组,所以你需要将它们包装在 () 中。您正在将花车传递给作物。需要做int(width)和int(height)。此外,所有这些都不是必需的,因为有一个内置方法:im = ImageOps.fit(im, size, Image.ANTIALIAS, (0.5, 0.5)) 可以做同样的事情。【参考方案2】:

这是我对图像填充的看法:

#!/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,谢谢!我如何将其更改为切碎而不插入填充? (甚至丢失了一大块图像) 完美,正是我需要的!谢谢@jdi!【参考方案3】:

如果您使用简单缩略图,则需要将 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

【讨论】:

以上是关于调整图像大小以保持纵横比并使纵向和横向图像尺寸完全相同?的主要内容,如果未能解决你的问题,请参考以下文章

如何按比例调整图像大小/保持纵横比?

具有自动布局和最大尺寸保持纵横比的 UIImageView

在不保持纵横比的情况下使用 mogrify 调整图像大小

布局约束将图像大小调整为屏幕宽度保持纵横比

为纵向和横向模式调整 UIImage 的大小

iMessage App如何调整图像大小并保持纵横比