图像下采样算法

什么是我可以用来将图像分成原始大小的一半的最佳重采样算法。 速度是最重要的,但它不应该降低质量太差。 我基本上试图生成图像金字塔。

我本来打算跳过像素。 这是最好的方式吗? 从我所看到的,像素跳过产生的图像太尖锐了。 可能有人试过这个评论。 我的图片包含这样的地图数据。

跳过像素将导致混叠,其中高频变化(例如交替的亮/暗带)将转换为低频(例如恒定亮或暗)。

在没有混叠的情况下缩小到一半的最快方法是将2×2像素平均为单个像素。 使用更复杂的还原内核可以获得更好的结果,但它们将以牺牲速度为代价。

编辑:以下是目前讨论的技术的一些例子。

跳过所有其他像素 – 通过查看左侧的图例,您可以看到结果不是很好。 这几乎是不可读的:

跳过每隔一个像素

平均每2×2网格 – 文本现在清晰可读:

平均2x2

高斯模糊,正如R.所暗示的那样 – 有点模糊,但在某一点上更具可读性。 可以调整模糊量以得到不同的结果:

在此处输入图像描述

R.对于影响结果的Gamma曲线也是正确的,但这应该只在最苛刻的应用中可见。 我的例子没有伽马校正。

对于缩小尺寸,区域平均(参见Mark的答案)接近于您将获得的最佳值。

主要的其他竞争者是高斯,半径稍大。 这将稍微增加模糊,这可能被视为缺点,但会使模糊更均匀,而不是依赖于像素mod 2的对齐。

如果我不清楚我的意思,请考虑像素模式0,0,2,2,0,0和0,0,0,2,2,0。 通过面积平均,它们分别缩小到0,2,0和0,1,1 – 也就是说,一个将是锐利和明亮的,而另一个将是模糊和暗淡的。 使用较长的滤镜,两者都会模糊,但它们看起来会更相似,这对人类观察者来说很重要。

需要考虑的另一个问题是gamma。 除非伽马是线性的,否则强度k两个像素将具有比强度为2*k的单个像素小得多的总强度。 如果你的filter表现出足够的模糊,那么它可能并不重要,但是使用普通的区域平均filter可能是一个主要问题。 我所知道的唯一解决方法是在缩放之前和之后应用和反转伽玛曲线……

如果速度是一个问题,如上所述,我建议采用2×2块并计算平均值作为结果像素。 质量不是最好的,但接近。 您可以激发此算法以显示其弱点,但在大多数图像上,您将看不到可以certificate计算时间多倍的差异。 你也没有任何内存开销。 如果颜色分辨率可以降低到每通道6位,这是一种非常快速的方法,可以防止你分解ARGB通道(这里假设32位ARGB):

 destPixel[x,y] = ((sourcePixel[2*x ,2*y ]>>2)&0x3f3f3f3f) + ((sourcePixel[2*x+1,2*y ]>>2)&0x3f3f3f3f) + ((sourcePixel[2*x ,2*y+1]>>2)&0x3f3f3f3f) + ((sourcePixel[2*x+1,2*y+1]>>2)&0x3f3f3f3f); 

这种算法的副作用是,如果保存为PNG,文件大小会变小。 这是它的样子: 使用上述算法缩小测试图像

我试图概括ThiloKöhler的解决方案(但在Python中):

 STRIDE = 2 MASK = 0x3F3F3F3F color = 0 for Δx, Δy in itertools.product(range(STRIDE), repeat=2): color += (get_pixel(x + Δx, y + Δy) // STRIDE) & MASK 

这适用于缩放2(四分之一大小结果),但不适用于按3或4或其他int值缩放。 有可能推广这个吗?

对于非Pythonist的BTW,上面的for循环等同于此(除了第一个版本可通过更改STRIDE进行扩展):

 for Δx, Δy in [(0, 0), (0, 1), (1, 0), (1, 1)]: color += (get_pixel(x + Δx, y + Δy) // STRIDE) & MASK 

我正在使用32位ARGB值。

NetPBM套件包括一个名为pamscale的实用程序,它为下采样提供了一些选项。 它是开源的,因此您可以尝试各种选项,然后复制您最喜欢的算法(或者只使用libnetpbm)。