图像下采样算法

发布于 2024-11-09 15:36:23 字数 284 浏览 4 评论 0原文

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

我原本打算跳过像素。这是最好的方法吗?据我所知,像素跳跃生成的图像太锐利。尝试过此评论的人可以吗?我的图像包含类似于 这个。

What's the best re-sampling algorithm I can use to divide an image into half its original size. Speed is of primary importance but it shouldn't degrade quality too bad. I'm basically trying to generate an image pyramid.

I was originally planning to skip pixels. Is this the best way to go? From what I've read the image produced by pixel skipping is too sharp. Could someone who has tried this comment. My images contain map data sort of like this.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

梦境 2024-11-16 15:36:23

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

在不出现锯齿的情况下将尺寸缩小一半的最快方法是将 2x2 像素平均为单个像素。使用更复杂的缩减内核可以获得更好的结果,但它们会以牺牲速度为代价。

以下是迄今为止讨论的技术的一些示例。

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

跳过每隔一个像素

对每个 2x2 网格进行平均 - 文本现在清晰易读:

Average 2x2

高斯模糊,如 R. - 有点模糊,但在某种程度上更具可读性。可以调整模糊量以获得不同的结果:

高斯模糊,然后选择像素

R. 关于伽玛曲线影响结果的说法也是正确的,但这应该只在最苛刻的应用程序中可见。我的例子是在没有伽马校正的情况下完成的。

编辑:这是一个更复杂但速度较慢的内核的示例,Lanczos- 5在线性(非伽玛调整)色彩空间中执行。

线性空间中的Lanczos-5

字体的对比度较低,可能是由于颜色空间转换的原因。但看看海岸线的细节。

Skipping pixels will result in aliasing, where high frequency changes (such as alternating light/dark bands) will convert to low frequencies (such as constant light or dark).

The quickest way to downsize to half without aliasing is to average 2x2 pixels into a single pixel. Better results can be had with more sophisticated reduction kernels, but they will come at the expense of speed.

Here are some examples of the techniques discussed so far.

Skipping every other pixel - you can see that the results aren't very good by looking at the legend on the left side. It's almost unreadable:

Skipping every other pixel

Averaging every 2x2 grid - The text is now sharp and readable:

Average 2x2

Gaussian blur, as suggested by R. - a little blurrier, but more readable up to a point. The amount of blur can be adjusted to give different results:

Gaussian blur followed by pixel selection

R. is also correct about the Gamma curve affecting the results, but this should only be visible in the most demanding applications. My examples were done without gamma correction.

Edit: And here is an example of a more sophisticated but slow kernel, a Lanczos-5 performed in a linear (not gamma-adjusted) color space.

Lanczos-5 in linear space

The contrast in the lettering is lower, probably because of the conversion of color spaces. But look at the coastline detail.

哽咽笑 2024-11-16 15:36:23

对于缩小规模,面积平均(参见马克的答案)接近您所能获得的最佳效果。

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

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

另一个需要考虑的问题是伽玛。除非伽玛是线性的,否则强度 k 的两个像素的总强度将比强度 2*k 的单个像素少得多。如果您的滤镜执行了足够的模糊,那么这可能并不重要,但对于普通区域平均滤镜来说,这可能是一个主要问题。我知道的唯一解决方法是在缩放之前和之后应用和反转伽玛曲线......

For downscaling, area-averaging (see Mark's answer) is close to the best you'll get.

The main other contender is gaussian, with a slightly larger radius. This will increase blurring a little bit, which could be seen as a disadvantage, but would make the blurring more uniform rather than dependent on the alignment of pixels mod 2.

In case it's not immediately clear what I mean, consider the pixel patterns 0,0,2,2,0,0 and 0,0,0,2,2,0. With area-averaging, they'd downscale to 0,2,0 and 0,1,1, respectively - that is, one will be sharp and bright while the other will be blurred and dim. Using a longer filter, both will be blurred, but they'll appear more similar, which presumably matters to human observers.

Another issue to consider is gamma. Unless gamma is linear, two pixels of intensity k will have much less total intensity than a single pixel of intensity 2*k. If your filter performs sufficient blurring, it might not matter so much, but with the plain area-average filter it can be a major issue. The only work-around I know is to apply and reverse the gamma curve before and after scaling...

蘑菇王子 2024-11-16 15:36:23

如果速度是一个问题,如上所述,我建议采用 2x2 块并计算平均值作为结果像素。质量不是可以达到的最好,但接近。您可以激发该算法以显示其弱点,但在大多数图像上,您不会看到任何差异,无法证明计算时间要高出许多倍。
您也没有任何内存开销。
如果颜色分辨率可以降低到每通道 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,文件大小会变小。
它是这样的:
使用上述算法缩小的测试图像

If speed is an issue, as mentioned, I recommend to take a 2x2 Block and calculate the average as the resulting pixel. The quality is not the best that can be achieved, but close to. You can provoke this algorithm to show its weaknesses, but on most images you won't see a difference that would justify the many times higher computation time.
You also dont have any memory overhead.
If color resolution can be lowered to 6bit per channel, here is a pretty fast way that prevents you from decomposing the ARGB channels (here assuming 32bit 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);

Side effect of this alogrithm is, that if saved as PNG, the file size gets smaller.
This is how it looks like:
Test image downscaled with the above algorithm

北凤男飞 2024-11-16 15:36:23

我尝试概括 Thilo Kö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 值缩放则不起作用。是否可以概括这一点?

顺便说一句,对于非 Pythonistas,上面的 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 值。

I tried to generalise Thilo Köhler's solution (but in 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

This works fine for scaling by 2 (quarter size result), but doesn't work for scaling by 3 or 4 or other int values. Is it possible to generalise this?

BTW for non-Pythonistas the for loop above is equivalent to this (except that the first version is scalable by changing the STRIDE):

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

I'm using 32-bit ARGB values.

北恋 2024-11-16 15:36:23

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

The NetPBM suite includes a utility called pamscale, which provides a few options for downsampling. It is open source, so you can try the various options and then copy the algorithm you like best (or just use libnetpbm).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文