使用不同的方法进行降采样时,图像渐变会变得不准确。

4
我们有一个相当复杂的Python图像处理脚本,使用了PIL和numpy。其中一个步骤需要使用非常敏感的多通道梯度查找表。一旦创建完成,就会保存到多个不同的较小分辨率中。然而,当这样做时,绿色通道(从左到右具有渐变)似乎突然失去了精度。它应该每50个像素左右失去255个值中的1个。但实际上,它开始以每100个像素2个值的速度下降。这会导致巨大的问题,我无法弄清楚PIL为什么会这样做。然而,我发现在地图的其他部分也有1个单位的跳跃,因此我认为问题并不简单,可能不只是缺少一个位的精度。我还注意到在另一个通道上,整个地图似乎被移动了1个值。即使使用“最近”滤镜,一旦缩放,整个地图看起来都不准确。
对于全尺寸图像,我们使用以下代码从numpy数组创建它:
image = Image.fromarray(imageIn.astype(np.uint8))

我们接着将其缩小:
new_image = image.resize(new_size, scaleFilter)

这个比例总是最大值的一半,我已经尝试了所有可用的比例选项。

然后我们按以下方式将其保存为PNG:

new_image.save(file_name, 'PNG')

我们使用相同的保存命令直接保存第一步后的大文件,这样做是没问题的。在缩放之后,我们在绿色通道上出现了问题。任何帮助都将不胜感激!
编辑:
现在看来很可能是SciPy的问题。以下内容仍会导致问题:
    new_array = misc.imresize(imageIn, (x_size, y_size, 4), interp='nearest')
    misc.imsave(file_name,new_array)

我不理解为什么即使使用最近邻算法也会出现扭曲问题。我将此数组分配为float64,但其中必须涉及代码中的四舍五入问题。
编辑#2:
我进一步尝试了使用OSX内置程序sips下载它,并得到了同样的扭曲!然后我尝试使用Adobe After Effects进行操作,操作正常。然后我安装了imagemagick,现在可以正常工作。如果有人能够解释所有这些方法中发生的原因,我仍将授予奖励。
编辑#3:
根据请求,这里是一个缩放和未缩放的雪碧图片段。在创建它们期间,我发现OSX内置的“预览”应用程序在缩小时也会导致缩放问题,因此我实际上必须使用Photoshop获取原始剪辑。
原始剪辑: enter image description here 带有失真的缩放。请尝试查看沿水平轴的绿色通道: enter image description here 请注意,这些裁剪不是完全相同的像素,但是从形状上可以看出它们是从同一区域裁剪出来的。
编辑#4:
我现在尝试通过应用程序内的OpenGL进行此缩放,并发现它也会发生!这必须涉及使用固定位数进行双线性插值的一些基本问题?

1
什么是 scaleFilter - user2379410
1
imresize()imsave()都内部调用toimage(),它似乎会将像素值重新缩放为0-255。 - Reti43
1
你在使用SciPy时遇到的问题是它内部调用了PIL来进行调整大小,这就是为什么行为是相同的原因。我更喜欢OpenCV,特别是当我使用numpy时,因为没有来回转换(不像PIL,使用cv2加载的图像存储为numpy数组)。 - bconstanzo
David,你可以发布一个渐变图像的示例(在缩放之前)和/或生成它的代码吗?此外,您可以发布可运行的缩放代码示例吗?我想知道发生了什么,但在完整的示例上展示会更容易。 - Alex I
3
PIL(参见来源)在“预乘alpha”空间中调整大小,导致一些透明信息渗入颜色通道。其思想是不混合透明和不透明像素的颜色,我猜这对于普通图像是需要的,但对于查找表来说就不好了。由于使用了uint8中介,这个问题在PIL中被夸大了。 - user2379410
显示剩余6条评论
1个回答

2
以下代码使用skimage在缩放50%时似乎做得很好:
import numpy
import skimage
import skimage.io

img = skimage.io.imread('uY173.png')

import skimage.transform

img50_order0 = skimage.img_as_ubyte( skimage.transform.rescale(img, 0.5, order=0, clip=True) )
img50_order1 = skimage.img_as_ubyte( skimage.transform.rescale(img, 0.5, order=1, clip=True) )

img50_lm = numpy.rint( skimage.transform.downscale_local_mean(img, (2,2,1), clip=True) )

import scipy.ndimage.interpolation

img50_nd = scipy.ndimage.interpolation.zoom(img, (0.5, 0.5, 1))

# plot section of green channel along horizontal axis
plot(img50_order0[50, :, 1])
plot(img50_order1[50, :, 1])
plot(img50_lm[50, :, 1])
plot(img50_nd[50, :, 1])

据我所知,这似乎并不依赖于 PIL。源图像被读取为 uint8,在每个图像中以微妙的不同方式进行处理和舍入,最终得到 uint8 输出。尽管所有这些之间的差异从未超过 1,步骤也从未达到大小 2。


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