【问题标题】:Bi-Cubic Interpolation Algorithm for Image Scaling用于图像缩放的双三次插值算法
【发布时间】:2013-02-17 02:09:25
【问题描述】:

我正在尝试编写一个基本的双三次调整大小算法来调整 24 位 RGB 位图的大小。我对所涉及的 the math 有一个大致的了解,并且我使用来自 Google Code 的this implementation 作为指导。我在这里没有使用任何外部库——我只是在试验算法本身。位图表示为普通的std::vector<unsigned char>

inline unsigned char getpixel(const std::vector<unsigned char>& in, 
    std::size_t src_width, std::size_t src_height, unsigned x, unsigned y, int channel)
{
    if (x < src_width && y < src_height)
        return in[(x * 3 * src_width) + (3 * y) + channel];

    return 0;
}

std::vector<unsigned char> bicubicresize(const std::vector<unsigned char>& in, 
    std::size_t src_width, std::size_t src_height, std::size_t dest_width, std::size_t dest_height)
{
    std::vector<unsigned char> out(dest_width * dest_height * 3);

    const float tx = float(src_width) / dest_width;
    const float ty = float(src_height) / dest_height;
    const int channels = 3;
    const std::size_t row_stride = dest_width * channels;

    unsigned char C[5] = { 0 };

    for (int i = 0; i < dest_height; ++i)
    {
        for (int j = 0; j < dest_width; ++j)
        {
            const int x = int(tx * j);
            const int y = int(ty * i);
            const float dx = tx * j - x;
            const float dy = ty * i - y;

            for (int k = 0; k < 3; ++k)
            {
                for (int jj = 0; jj < 4; ++jj)
                {
                    const int z = y - 1 + jj;
                    unsigned char a0 = getpixel(in, src_width, src_height, z, x, k);
                    unsigned char d0 = getpixel(in, src_width, src_height, z, x - 1, k) - a0;
                    unsigned char d2 = getpixel(in, src_width, src_height, z, x + 1, k) - a0;
                    unsigned char d3 = getpixel(in, src_width, src_height, z, x + 2, k) - a0;
                    unsigned char a1 = -1.0 / 3 * d0 + d2 - 1.0 / 6 * d3;
                    unsigned char a2 = 1.0 / 2 * d0 + 1.0 / 2 * d2;
                    unsigned char a3 = -1.0 / 6 * d0 - 1.0 / 2 * d2 + 1.0 / 6 * d3;
                    C[jj] = a0 + a1 * dx + a2 * dx * dx + a3 * dx * dx * dx;

                    d0 = C[0] - C[1];
                    d2 = C[2] - C[1];
                    d3 = C[3] - C[1];
                    a0 = C[1];
                    a1 = -1.0 / 3 * d0 + d2 -1.0 / 6 * d3;
                    a2 = 1.0 / 2 * d0 + 1.0 / 2 * d2;
                    a3 = -1.0 / 6 * d0 - 1.0 / 2 * d2 + 1.0 / 6 * d3;
                    out[i * row_stride + j * channels + k] = a0 + a1 * dy + a2 * dy * dy + a3 * dy * dy * dy;
                }
            }
        }
    }

    return out;
}

问题:当我使用此算法缩小图像时,除了输出图像由于某种原因在右侧包含所有黑色像素外,它可以正常工作,看起来它已被“裁剪”。

示例:

输入图像:

输出图像:



问题查看算法,我不明白为什么会发生这种情况。有人看到这里的缺陷吗?

【问题讨论】:

  • 嗯,根据该输出图像,它看起来完全就像您只计算了一个正方形的输出像素值。这应该是一个足够大的线索让您自己调试和诊断......

标签: c++ image algorithm image-processing bicubic


【解决方案1】:

尽量不要交换宽度和高度。

   for (int i = 0; i < dest_width; ++i)
    {
        for (int j = 0; j < dest_height; ++j)

【讨论】:

  • 以他的方式遍历是缓存友好的,它会执行得更快。与您的建议相反,该建议会导致大图像的缓存未命中。
  • 问题出在索引的范围内。并且缓存未命中取决于缓存大小和编译器设置...
【解决方案2】:

我建议不要使用这个函数,因为它写得很糟糕。您需要进行两次卷积:首先按 X 坐标,然后按 Y。在此函数中,所有这些卷积同时进行,这会导致工作非常缓慢。如果您查看 jj 循环主体,您会注意到主体的所有第二部分都从“d0 = C[0] - C[1];”开始可以移到 jj 循环之外,因为只有此循环的最后一次迭代对 out[] 数组生效(所有先前的迭代结果都将被覆盖)。

【讨论】:

【解决方案3】:

您应该在调用getpixel 时切换xz,在getpixel 中您应该使用以下方法索引数组:

[(y * 3 * src_width) + (3 * x) + channel]

【讨论】:

    【解决方案4】:

    getpixel(in, src_width, src_height, z, x, k):

    z mean horizontal offset
    x mean vertical offset
    

    所以只需要修补getpixel函数,下面是修补后的代码:

    inline unsigned char getpixel(const std::vector<unsigned char>& in, 
        std::size_t src_width, std::size_t src_height, unsigned y, unsigned x, int channel)
    {
        if (x < src_width && y < src_height)
            return in[(y * 3 * src_width) + (3 * x) + channel];
    
        return 0;
    }
    
    std::vector<unsigned char> bicubicresize(const std::vector<unsigned char>& in, 
        std::size_t src_width, std::size_t src_height, std::size_t dest_width, std::size_t dest_height)
    {
        std::vector<unsigned char> out(dest_width * dest_height * 3);
    
        const float tx = float(src_width) / dest_width;
        const float ty = float(src_height) / dest_height;
        const int channels = 3;
        const std::size_t row_stride = dest_width * channels;
    
        unsigned char C[5] = { 0 };
    
        for (int i = 0; i < dest_height; ++i)
        {
            for (int j = 0; j < dest_width; ++j)
            {
                const int x = int(tx * j);
                const int y = int(ty * i);
                const float dx = tx * j - x;
                const float dy = ty * i - y;
    
                for (int k = 0; k < 3; ++k)
                {
                    for (int jj = 0; jj < 4; ++jj)
                    {
                        const int z = y - 1 + jj;
                        unsigned char a0 = getpixel(in, src_width, src_height, z, x, k);
                        unsigned char d0 = getpixel(in, src_width, src_height, z, x - 1, k) - a0;
                        unsigned char d2 = getpixel(in, src_width, src_height, z, x + 1, k) - a0;
                        unsigned char d3 = getpixel(in, src_width, src_height, z, x + 2, k) - a0;
                        unsigned char a1 = -1.0 / 3 * d0 + d2 - 1.0 / 6 * d3;
                        unsigned char a2 = 1.0 / 2 * d0 + 1.0 / 2 * d2;
                        unsigned char a3 = -1.0 / 6 * d0 - 1.0 / 2 * d2 + 1.0 / 6 * d3;
                        C[jj] = a0 + a1 * dx + a2 * dx * dx + a3 * dx * dx * dx;
    
                        d0 = C[0] - C[1];
                        d2 = C[2] - C[1];
                        d3 = C[3] - C[1];
                        a0 = C[1];
                        a1 = -1.0 / 3 * d0 + d2 -1.0 / 6 * d3;
                        a2 = 1.0 / 2 * d0 + 1.0 / 2 * d2;
                        a3 = -1.0 / 6 * d0 - 1.0 / 2 * d2 + 1.0 / 6 * d3;
                        out[i * row_stride + j * channels + k] = a0 + a1 * dy + a2 * dy * dy + a3 * dy * dy * dy;
                    }
                }
            }
        }
    
        return out;
    }
    

    【讨论】:

      猜你喜欢
      • 2018-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-27
      • 1970-01-01
      • 2015-11-14
      • 1970-01-01
      • 2014-02-28
      相关资源
      最近更新 更多