快速,高质量的像素插值,可实现极端的图像缩小

时间:2019-06-04 20:36:10

标签: c++ image-processing visual-studio-2012 interpolation

在我的程序中,我将500px或更大的图像缩小到大约16px-32px的极限水平。源图像是用户指定的,因此我无法控制其大小。您可以想象,几乎没有像素插值,结果不可避免地会出现严重的锯齿。

我尝试了双线性,双三次和平方平均采样。平方平均采样实际上提供了最令人满意的结果,但是得到的越小,采样半径就必须越大。结果,它变得相当慢-比其他插值方法要慢。

我还尝试了自适应平方平均采样,以使其采样半径越大,采样半径越大,而采样半径越接近其原始大小,则采样半径越小。但是,这会产生问题,我不相信这是最好的方法。

问题是:推荐的像素插值类型是什么?在这种极端的缩小比例下,像素插值是快速的并且效果很好吗?

我不想使用一个库,所以我需要一些可以手工编写的代码,并且不太复杂。我正在VS 2012中使用C ++。

这是我按要求尝试的一些示例代码(希望不会因我的伪代码剪切和粘贴而出错)。这样可实现平均7x7的缩小效果,尽管比双线性插值或双三次插值效果更好,但也会带来很大的冲击:

    // Sizing control
    ctl(0): "Resize",Range=(0,800),Val=100

    // Variables
    float fracx,fracy;
    int Xnew,Ynew,p,q,Calc;
    int x,y,p1,q1,i,j;

    //New image dimensions
    Xnew=image->width*ctl(0)/100;
    Ynew=image->height*ctl(0)/100;

    for (y=0; y<image->height; y++){ // rows
    for (x=0; x<image->width; x++){ // columns

        p1=(int)x*image->width/Xnew;
        q1=(int)y*image->height/Ynew;

        for (z=0; z<3; z++){ // channels

             for (i=-3;i<=3;i++) {
             for (j=-3;j<=3;j++) {
             Calc += (int)(src(p1-i,q1-j,z)); 
             } //j
             } //i

             Calc /= 49;

             pset(x, y, z, Calc);

         }  // channels         


    }  // columns
    }  // rows

谢谢!

2 个答案:

答案 0 :(得分:1)

第一点是使用指向数据的指针。切勿在每个像素上使用索引。当您写时:src(p1-i,q1-j,z)pset(x, y, z, Calc)正在进行多少计算?使用数据指针并进行操作。

第二:您的算法错误。您不需要平均过滤器,但希望在源图像上建立一个网格,并为每个网格单元计算平均值并将其放入输出图像的相应像素中。

应根据您的数据表示量身定制特定的解决方案,但这可能是这样的:

    std::vector<uint32_t> accum(Xnew);
    std::vector<uint32_t> count(Xnew);
    uint32_t *paccum, *pcount;
    uint8_t* pin = /*pointer to input data*/;
    uint8_t* pout = /*pointer to output data*/;
    for (int dr = 0, sr = 0, w = image->width, h = image->height; sr < h; ++dr) {
        memset(paccum = accum.data(), 0, Xnew*4);
        memset(pcount = count.data(), 0, Xnew*4);
        while (sr * Ynew / h == dr) {
            paccum = accum.data();
            pcount = count.data();
            for (int dc = 0, sc = 0; sc < w; ++sc) {
                *paccum += *i;
                *pcount += 1;
                ++pin;
                if (sc * Xnew / w > dc) {
                    ++dc;
                    ++paccum;
                    ++pcount;
                }
            }
            sr++;
        }
        std::transform(begin(accum), end(accum), begin(count), pout, std::divides<uint32_t>());
        pout += Xnew;
    }

这是使用我自己的库(仍在开发中)编写的,并且似乎可以正常工作,但是后来我更改了变量名称以使其在此处更简单,所以我不做任何保证!

这个想法是要有一个32位整数的局部缓冲区,该缓冲区可以保存输出图像的一行中行中所有像素的部分和。然后,将其除以单元格计数,然后将输出保存到最终图像。

您应该做的第一件事就是建立一个绩效评估系统,以衡量任何变化对绩效的影响。

答案 1 :(得分:0)

如前所述,您不应使用索引,而应使用(可能)大量的指针。 加快而不是简单地平均,因为像素的基本平均基本上是模糊滤镜。

我强烈建议您重新编写代码以使用“内核”。这是代表每个像素使用比例的矩阵。这样,您将能够测试不同的策略并优化质量。

内核示例: https://en.wikipedia.org/wiki/Kernel_(image_processing)

上采样/下采样内核: http://www.johncostella.com/magic/

注意,从代码中看,您似乎应用了3x3内核,但最初是在7x7内核上完成的。发布的等效3x3内核为:

[1 1 1]
[1 1 1] * 1/9
[1 1 1]