(Py)Vips与Pillow的图像放大质量比较

6

将图像调整大小到200%会导致Pillow和pyvips之间的质量差异。

虽然Pillow在重现方面非常准确,但vips夸大了噪声和对比度。

我需要使用vips处理非常大的图像,但质量令人不满意。有没有办法从vips获得更好的放大效果?(从文档中我了解到,对于vips来说,放大并不是真正重要的,大部分思考都集中在缩小上)。

示例:

from PIL import Image
import pyvips
import numpy as np

#Vips
img = pyvips.Image.new_from_file("mypic.jpg", access='sequential')
out = img.resize(2, kernel = "linear")
out.write_to_file("mypic_vips_resized.tif")

#Pillow
img = np.array(Image.open("mypic.jpg"))
h, w = img.shape[:2]
out = Image.fromarray(img,mode="RGB")
out = out.resize((w*2,h*2), Image.BILINEAR)
out.save("mypic_PIL_resized.tif", format='TIFF', compression='None')

原图:
original
Pillow:
Pillow
Vips:
Vips

抽象示例(10*10像素)

原图:
Original
Pillow双线性插值:
Pillow Bilinear
Vips线性插值:
Vips linear


1
您正在使用不同的升尺寸方法——对于Pillow是双线性,对于Pyvips是立方。请尝试将Vips设置为线性,它们应该匹配。您可以尝试其他Pyvips内核:最近邻、线性、三次样条、Mitchell、Lanczos2、Lanczos3。 - jcupitt
@jcupitt 这个图像实际上来自于vips linear(我尝试了所有方法来获得最接近的匹配),这只是我的简化代码中的一个错误。 - Horst
我让代码片段可用,如果您将原始文件保存为mypic.jpg,则应该能够看到差异(但我不知道SO是否会干扰上传的jpg)。无论如何,适用于任何图像。 - Horst
1
你说得对,libvips的“resize”操作符在放大时使用了简单的插值双线性方法,而Pillow则使用三角形滤波器。你可以通过放大前进行模糊处理来获得三角形滤波器的效果。我会写一个正式的答案。 - jcupitt
1个回答

3

看起来Pillow正在使用三角形滤波器进行线性放大,而对于放大,libvips则采用简单插值。libvips在缩小时使用三角形滤波器。

如果你想象一下像素:

A 
B 
C 

然后Pillow会计算A和B之间的新像素、B位置上的像素以及B和C之间的新像素,如下:

(A + B) / 2
(A + B) / 4 + B / 2 + (B + C) / 4
(B + C) / 2

与此同时,libvips正在进行计算:

(A + B) / 2
B
(B + C) / 2

您可以通过先进行轻微模糊来获得三角形滤波器的效果。如果我将您的程序更改为:

img = pyvips.Image.new_from_file('mypic.png', access='sequential')
img = img.gaussblur(0.45, precision='float', min_ampl=0.01).cast('uchar')
out = img.resize(2, kernel='linear')
out.write_to_file('mypic_vips_resized_blur.png')

例如,先进行小半径、高精度的gaussblur,我得到了以下结果:

enter image description here

从左到右,这些图像分别是:1)简单的x2像素双倍,2)Pillow LINEAR,3)libvips linear,4)libvips gaussblur + linear。您可能需要点击图片,否则您的浏览器会缩小和模糊它。

2)和4)在我(不太好的)眼中看来相当接近。 3)似乎更符合原始要求,因为原始要求中存在的振铃和噪声没有被平滑掉。


非常感谢详细的解释。我已经尝试了建议的高斯模糊,它确实改善了结果。即使对于单个图像,我仍然觉得Pillow版本更加令人愉悦,即使再现不是那么忠实。但是,由于我正在处理约15张图像的图像堆栈并计算平均值和中位数,因此pillow的输出对我来说看起来更好(与gauss-vips相比,噪声更少,对比度更好)。是否有可能在libvips中实现pillow的线性滤波变体?我知道升级不是优先考虑的问题,但仍符合pillow的原始目标。 - Horst
在最后一句话中,我当然是指vips的原始目标。 - Horst
啊,好的,是的,我已经处理过类似的事情了。我会使用libvips插值将比例扩大到200%-您不希望在处理完成之前进行任何额外的平滑处理。 我认为您会发现平均/中位数足够了。 libvips在图像堆栈上有一个排名过滤器操作符 https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-bandrank - jcupitt
bandmean 找到图像波段的平均值 https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-bandmean 如果您想对一组图像进行平均处理,只需将它们相加并除以数量即可。还有 sum 可以快速对图像数组进行求和 https://libvips.github.io/libvips/API/current/libvips-arithmetic.html#vips-sum 因此例如 pyvips.Image.sum(imagearray) / len(imagearray) - jcupitt
当然是求和。我有点陷入了花哨函数的泥潭。对于其他人来说,使用pyvips rint和cast之后可以将图像转换为基于整数而不是除法浮点输出的形式。 - Horst
显示剩余4条评论

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