PIL:图像调整大小:类似于Firefox的算法

11

我从PIL的四种算法中得到了大致相同的低质量的缩放效果。

>>> data = utils.fetch("http://wavestock.com/images/beta-icon.gif")
>>> image = Image.open(StringIO.StringIO(data)); image.save("/home/ptarjan/www/tmp/metaward/original.png")
>>>
>>> image = Image.open(StringIO.StringIO(data)); image.resize((36,36), Image.ANTIALIAS).save("/home/ptarjan/www/tmp/metaward/antialias.png")
>>> image = Image.open(StringIO.StringIO(data)); image.resize((36,36), Image.BILINEAR).save("/home/ptarjan/www/tmp/metaward/bilinear.png")
>>> image = Image.open(StringIO.StringIO(data)); image.resize((36,36), Image.BICUBIC).save("/home/ptarjan/www/tmp/metaward/bicubic.png")
>>> image = Image.open(StringIO.StringIO(data)); image.resize((36,36), Image.NEAREST).save("/home/ptarjan/www/tmp/metaward/nearest.png")
>>>
>>> image = Image.open(StringIO.StringIO(data)); image.thumbnail((36,36), Image.ANTIALIAS); image.save("/home/ptarjan/www/tmp/metaward/antialias-thumb.png")
>>> image = Image.open(StringIO.StringIO(data)); image.thumbnail((36,36), Image.BILINEAR); image.save("/home/ptarjan/www/tmp/metaward/bilinear-thumb.png")
>>> image = Image.open(StringIO.StringIO(data)); image.thumbnail((36,36), Image.BICUBIC); image.save("/home/ptarjan/www/tmp/metaward/bicubic-thumb.png")
>>> image = Image.open(StringIO.StringIO(data)); image.thumbnail((36,36), Image.NEAREST); image.save("/home/ptarjan/www/tmp/metaward/nearest-thumb.png")
>>>
>>> image = Image.open(StringIO.StringIO(data)); image.convert("RGB").resize((36,36), Image.ANTIALIAS).save("/home/ptarjan/www/tmp/metaward/antialias-rgb.png")
>>> image = Image.open(StringIO.StringIO(data)); image.convert("RGB").resize((36,36), Image.BILINEAR).save("/home/ptarjan/www/tmp/metaward/bilinear-rgb.png")
>>> image = Image.open(StringIO.StringIO(data)); image.convert("RGB").resize((36,36), Image.BICUBIC).save("/home/ptarjan/www/tmp/metaward/bicubic-rgb.png")
>>> image = Image.open(StringIO.StringIO(data)); image.convert("RGB").resize((36,36), Image.NEAREST).save("/home/ptarjan/www/tmp/metaward/nearest-rgb.png")

但结果比在Firefox中调整大小要糟糕得多。

http://paulisageek.com/tmp/metaward/images.html

我该如何使用PIL(或其他Python图像库)获得类似于Firefox结果的效果?

编辑:将鼠标悬停在每个图像上以查看其内容

看起来RGB和ANTIALIS效果最好。还有其他推荐吗?

供参考,这是看起来最好的:

>>> image = Image.open(StringIO.StringIO(data)); 
>>> image.convert("RGB").resize((36,36), Image.ANTIALIAS)

你使用的PIL版本是哪个? - rob
PIL版本:/usr/lib/python2.5/site-packages/PIL/PIL-1.1.6.egg-info - Paul Tarjan
Gimp 产生与 PIL 相同的结果。 - Nadia Alramli
您的帖子中的图片无法显示。 - knarf
4个回答

10

我使用Python调整了“原始”大小,并得到了与您相同的结果。我还使用GIMP调整了“原始”的大小,结果质量相同(甚至不如Python调整的效果好)。这让我怀疑Firefox在欺骗我们。可能它将图片转换为RGB格式(而“原始”模式是索引颜色)。因此,以下是代码:

import Image
im=Image.open("beta-icon.gif")
im = im.convert("RGB")
im=im.resize((36,36), Image.ANTIALIAS)
im.save("q5.png")

这个结果几乎和Firefox的一样好。


这是否是获取良好缩略图的通用策略?如果您想要查看我的语料库示例,我有大约10k张图片:http://metaward.com/awards - Paul Tarjan
2
肯定的。使用索引颜色调整图像大小通常不会提供良好的结果。如果您不关心保留调色板,则应始终转换为RGB,然后调整大小并(在必要时)再次减少颜色深度。 - rob
谢谢Roberto。如果这是一种已知的方法,为什么PIL不默认执行呢? - Paul Tarjan
4
因为它不再是之前那样带有256色调色板的索引图像。改变模式(甚至调色板)通常都不会是默认行为。@Paul说的就是这个。 - Lennart Regebro

1

看起来RGB和ANTIALIS效果最好。还有其他建议吗?

不,那确实是预期的结果。在原始有限调色板模式下进行的任何调整大小都可能会产生锯齿状垃圾,因为调色板中缺少可用的中间颜色;而ANTIALIAS是唯一旨在用于缩小的调整大小滤镜:BILINEAR和BICUBIC确实只取每个轴的两个像素并在它们之间混合,这对于放大很好,但当一个或两个轴被缩小时根本不起作用。

不幸的是,thumbnail()从来没有真正正常工作过,所以你必须自己处理。


如果可以的话,我希望将 .thumbnail 删除并替换为 .thumbnail(size),它会在需要时转换调色板,然后使用 resize。作为 PIL 的新手,.thumbnail 看起来正是我所需要的。 - Paul Tarjan
确实,这是PIL中一个可疑的角落,需要改进,但对于新用户来说也是一个看起来很有吸引力的陷阱。 - bobince
实际上,使用RGB时我丢失了Alpha通道。有什么方法可以保留它吗? - Paul Tarjan

0

尝试使用resize()方法代替thumbnail()。根据我的经验,它们表现非常不同。

另外,你的代码显示读取.gif文件,但原始文件是.png格式的。在开始缩小之前确保你真正拥有所有原始数据。


抱歉,我删掉了我原来写的那行代码。但是没错,这只是一个转换。我会尝试调整大小并进行比较。 - Paul Tarjan
示例页面现在全部使用resize(),但它们看起来仍然比FF的方式差得多。还有其他建议吗? - Paul Tarjan

0
发布的图像是索引图像。如果您通过PIL (Python Imaging Library)读取它,其模式为'P'。有关Image.resize()的PIL文档指出:

如果图像的模式为“1”或“P”,则始终将其设置为PIL.Image.NEAREST

因此,对于调整大小的索引图像,PIL将使用最近邻过滤器,这是一种低质量的过滤器

正确的方法是将图像模式转换为RGB,然后使用高质量滤镜(如Image.LANCZOS)进行调整大小。顺便提一下,Image.ANTIALIAS现在与Image.LANCZOS相同,源代码在此处

import Image
img = Image.open("beta-icon.gif").convert("RGB")
img = img.resize((36,36), Image.LANCZOS)
img.save("img-resized.png")

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