PIL和pytorch中的双线性图像缩放为什么会产生不同的结果?

7
为了将图像输入到pytorch网络中,我首先需要将其缩小到一定的固定大小。起初,我使用PIL.Image.resize()方法进行操作,插值模式设置为BILINEAR。然后我认为,先将一批图像转换为pytorch张量,然后在GPU上一次性使用torch.nn.functional.interpolate()函数对整个张量进行缩放(也使用“bilinear”插值模式)会更方便。但是这导致模型准确性下降,因为现在推理期间所使用的缩放类型(torch)与训练期间使用的不同(PIL)。之后,我通过视觉比较两种缩小方法并发现它们产生了不同的结果。Pillow缩小似乎更加平滑。虽然两者都是双线性的,但它们在背后执行的操作是否不同呢?如果是这样,我也很好奇是否有办法通过torch张量缩放来实现与Pillow图像缩放相同的结果?
原始图片链接: Original image Pillow缩放后的图片:

Pillow scaled image

火炬缩放图像:

Torch scaled image

平均通道绝对差分图:

Mean channel absolute difference map

演示代码:
import numpy as np
from PIL import Image
import torch
import torch.nn.functional as F
from torchvision import transforms
import matplotlib.pyplot as plt

pil_to_torch = transforms.ToTensor()
res_shape = (128, 128)


pil_img = Image.open('Lenna.png')
torch_img = pil_to_torch(pil_img)

pil_image_scaled = pil_img.resize(res_shape, Image.BILINEAR)
torch_img_scaled = F.interpolate(torch_img.unsqueeze(0), res_shape, mode='bilinear').squeeze(0)

pil_image_scaled_on_torch = pil_to_torch(pil_image_scaled)
relative_diff = torch.abs((pil_image_scaled_on_torch - torch_img_scaled) / pil_image_scaled_on_torch).mean().item()
print('relative pixel diff:', relative_diff)

pil_image_scaled_numpy = pil_image_scaled_on_torch.cpu().numpy().transpose([1, 2, 0])
torch_img_scaled_numpy = torch_img_scaled.cpu().numpy().transpose([1, 2, 0])
plt.imsave('pil_scaled.png', pil_image_scaled_numpy)
plt.imsave('torch_scaled.png', torch_img_scaled_numpy)
plt.imsave('mean_diff.png', np.abs(pil_image_scaled_numpy - torch_img_scaled_numpy).mean(-1))

Python 3.6.6,要求:

cycler==0.10.0
kiwisolver==1.1.0
matplotlib==3.2.1
numpy==1.18.2
Pillow==7.0.0
pyparsing==2.4.6
python-dateutil==2.8.1
six==1.14.0
torch==1.4.0
torchvision==0.5.0
2个回答

8
“双线性插值”是插值方法之一。
但缩小图像并不一定只能使用插值来完成。
可以将图像简单地重新采样为较低的采样率,使用插值方法来计算新的样本,这些样本与旧的样本不重合。但这会导致混叠效应(当图像中的高频成分无法以较低的采样密度表示时,就会在较低频率成分上“混叠”这些高频能量;也就是说,在重新采样后,图像中会出现新的低频成分)。
为了避免混叠效应,一些库在重新采样之前应用低通滤波器(删除无法用较低采样频率表示的高频),这些库的子采样算法远不止插值。
你看到的差异是因为这两个库采取了不同的方法,一个通过低通滤波来避免混叠效应,而另一个则没有。
要在Torch中获得与Pillow相同的结果,您需要明确地对图像进行低通滤波。要获得相同的结果,您需要确定Pillow如何过滤图像,有不同的方法和不同的参数设置。查看源代码是找出它们做了什么的最好方式。

这并没有真正回答问题,问题的结尾是如何在Torch中实现这一点(据我所知,目前不可能以一种好的方式在torch中缩小图像...)。请注意,您在最后一段中有一个错误,应该是“为了在Pillow中获得相同的结果”。 - BjornW
@BjornW:您的更正与原文相同。但我发现我在那段落中交换了“Pillow”和“Torch”的位置。谢谢。// 关于这个问题:它并没有说“如何做到这一点?”,而是说“为什么会这样?”和“有办法吗?”我相信我已经回答了这两个问题。 - Cris Luengo

2

来自文档
使用torch.nn.functional.interpolateantialias=True一起使用可以得到与Pillow实现相匹配的结果。


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