什么是最好的图像缩小算法(质量方面)?

时间:2008-12-21 21:40:56

标签: algorithm image resize

我想找出哪种算法最适合用于缩小光栅图片的尺寸。最好的我指的是给出最好看结果的那个。我知道bicubic,但还有更好的东西吗?例如,我从一些人那里听说Adobe Lightroom有一些专有算法,它产生的结果比我使用的标准双三次更好。不幸的是,我想在我的软件中自己使用这个算法,因此Adobe小心谨慎的商业秘密是不会的。

加了:

我检查了Paint.NET,令我惊讶的是,在缩小图片时,Super Sampling似乎比bicubic更好。这让我想知道插值算法是否可行。

它还让我想起了我自己“发明”但从未实现过的算法。我想它也有一个名字(因为这个琐碎的东西不能仅仅是我的想法),但我在流行的那些中找不到它。超级采样是最接近的。

这个想法是这样的 - 对于目标图片中的每个像素,计算它在源图片中的位置。它可能会覆盖一个或多个其他像素。然后可以计算这些像素的面积和颜色。然后,为了获得目标像素的颜色,可以简单地计算这些颜色的平均值,将它们的区域添加为“权重”。因此,如果目标像素覆盖1/3的黄色源像素和1/4的绿色源像素,我会得到(1/3 *黄色+ 1/4 *绿色)/(1/3 + 1/4)。

这自然是计算密集型的,但它应该尽可能接近理想,不是吗?

这个算法有名称吗?

7 个答案:

答案 0 :(得分:66)

不幸的是,我找不到原始调查的链接,但随着好莱坞电影摄影师从电影转向数字图像,这个问题出现了很多,所以有人(也许是SMPTE,也许是ASC)聚集了一堆专业摄影师和向他们展示了使用一堆不同算法重新调整的镜头。结果是,对于那些看着巨大电影的专业人士来说,共识是 Mitchell (也称为高质量的Catmull-Rom)最适合放大和 sinc 最适合缩小规模。但是,sinc是一个理论上的过滤器,无法完成,因此无法完全实现,所以我不知道'sinc'实际上是什么意思。它可能是指sinc的截断版本。 Lanczos 是sinc的几种实用变体之一,试图改进它只是截断它并且可能是缩小静止图像的最佳默认选择。但是像往常一样,它取决于图像和你想要的东西:缩小线条图以保留线条是一种情况,例如,你可能更喜欢强调保留在缩小花朵照片时不受欢迎的边缘。

Cambridge in Color有各种算法结果的好例子。

fxguide 的人们将a lot of information放在缩放算法(以及关于合成和其他图像处理的许多其他内容)上,值得一看。它们还包括可能对您自己的测试有用的测试图像。

如果你真的想进入它,现在ImageMagick有一个extensive guide on resampling filters

有一点具有讽刺意味的是,关于缩小图像存在更多争议,理论上可以完美地做到这一点,因为你只丢弃信息,而不是扩大规模,你试图添加信息那不存在。但是从Lanczos开始吧。

答案 1 :(得分:19)

Lanczos sampling比双三次慢,但会产生更高质量的图像。

答案 2 :(得分:14)

(Bi-)线性和(双)立方重采样不仅丑陋,而且在缩小小于1/2的因子时非常不正确。它们将导致非常糟糕的混叠,类似于如果您将采样率降低1/2,然后使用最近邻下采样,则会得到的结果。

我个人建议(区域)平均样本用于大多数下采样任务。它非常简单,快速且接近最佳。高斯重采样(半径选择与因子的倒数成比例,例如半径为5,用于下采样1/5)可以提供更好的结果,计算开销更多,并且在数学上更具声音。

使用高斯重采样的一个可能原因是,与大多数其他算法不同,只要您选择适合重采样因子的半径,它就可以正常工作(不会引入伪像/混叠)进行上采样和下采样。否则,为了支持两个方向,您需要两个单独的算法 - 用于下采样的区域平均(对于上采样会降级到最近邻居),以及类似(bi)立方体用于上采样(对于下采样会降级到最近邻居)。在数学上看到高斯重采样的这种优良特性的一种方法是具有非常大半径的高斯近似于面积平均,而具有非常小半径的高斯近似(双)线性插值。

答案 3 :(得分:7)

我刚才看到一篇关于Slashdot关于Seam Carving的文章,可能值得研究。

  

Seam雕刻是一个调整大小的图像   由Shai Avidan开发的算法   阿里尔沙米尔。该算法改变了   图像的尺寸不是   缩放或裁剪,而是通过   智能地从(或   添加像素)携带的图像   不太重要。

答案 4 :(得分:4)

您描述的算法称为线性插值,是最快的算法之一,但在图像上不是最好的。

答案 5 :(得分:2)

  

这个算法有名称吗?

它可能在文献中被称为“盒子”或“窗口”重新取样。 事实上它的计算成本更低。

它还可以用于创建一个中间位图,随后由双三次插值使用,以避免在采样率超过1/2时出现混叠。

答案 6 :(得分:-1)

如果有人感兴趣,这是我的面积平均缩放算法的C ++实现:

void area_averaging_image_scale(uint32_t *dst, int dst_width, int dst_height, const uint32_t *src, int src_width, int src_height)
{
    // 1. Scale horizontally (src -> mid)
    int mid_width  = dst_width,
        mid_height = src_height;
    float src_width_div_by_mid_width = float(src_width) / mid_width;
    float mid_width_div_by_src_width = 1.f / src_width_div_by_mid_width;
    std::vector<uint32_t> mid(mid_width * mid_height);
    for (int y=0; y<mid_height; y++)
        for (int x=0; x<mid_width; x++)
            for (int c=0; c<4; c++) {
                float f = x * src_width_div_by_mid_width;
                int i = int(f);
                float d = ((uint8_t*)&src[i + y*src_width])[c] * (float(i) + 1 - f);
                float end = f + src_width_div_by_mid_width;
                int endi = int(end);
                if (end - float(endi) > 1e-4f) {
                    assert(endi < src_width);
                    d += ((uint8_t*)&src[endi + y*src_width])[c] * (end - float(endi));
                }
                for (i++; i < endi; i++)
                    d += ((uint8_t*)&src[i + y*src_width])[c];
                int r = int(d * mid_width_div_by_src_width + 0.5f);
                assert(r <= 255);
                ((uint8_t*)&mid[x + y*mid_width])[c] = r;
            }

    // 2. Scale vertically (mid -> dst)
    float mid_height_div_by_dst_height = float(mid_height) / dst_height;
    float dst_height_div_by_mid_height = 1.f / mid_height_div_by_dst_height;
    for (int y=0; y<dst_height; y++)
        for (int x=0; x<dst_width; x++)
            for (int c=0; c<4; c++) {
                float f = y * mid_height_div_by_dst_height;
                int i = int(f);
                float d = ((uint8_t*)&mid[x + i*mid_width])[c] * (float(i) + 1 - f);
                float end = f + mid_height_div_by_dst_height;
                int endi = int(end);
                if (end - float(endi) > 1e-4f) {
                    assert(endi < mid_height);
                    d += ((uint8_t*)&mid[x + endi*mid_width])[c] * (end - float(endi));
                }
                for (i++; i < endi; i++)
                    d += ((uint8_t*)&mid[x + i*mid_width])[c];
                int r = int(d * dst_height_div_by_mid_height + 0.5f);
                assert(r <= 255);
                ((uint8_t*)&dst[x + y*dst_width])[c] = r;
            }
}