c++ - 有没有机会使双线性插值更快?

标签 c++ c image performance image-scaling

首先,我想为您提供一些背景信息。

我有两种图像需要合并。第一张图片是背景图片,格式为 8BppGrey,分辨率为 320x240。第二张图片是前景图片,格式为 32BppRGBA,分辨率为 64x48。

更新 带有 MVP 的 github 存储库位于问题的底部。

为此,我使用双线性插值将第二张图像调整为与第一张图像相同的大小,然后使用混合将两者合并为一张图像。仅当第二张图像的 alpha 值大于 0 时才会发生混合。

我需要尽快完成,所以我的想法是结合调整大小和合并/混合过程。

为了实现这一点,我使用了 writeablebitmapex repository 中的调整大小功能并添加了合并/混合。

一切都按预期进行,但我想减少执行时间。

这是当前的调试时间:

// CPU: Intel(R) Core(TM) i7-4810MQ CPU @ 2.80GHz

MediaServer: Execution time in c++ 5 ms
MediaServer: Resizing took 4 ms.
MediaServer: Execution time in c++ 5 ms
MediaServer: Resizing took 5 ms.
MediaServer: Execution time in c++ 4 ms
MediaServer: Resizing took 4 ms.
MediaServer: Execution time in c++ 3 ms
MediaServer: Resizing took 3 ms.
MediaServer: Execution time in c++ 4 ms
MediaServer: Resizing took 4 ms.
MediaServer: Execution time in c++ 5 ms
MediaServer: Resizing took 4 ms.
MediaServer: Execution time in c++ 6 ms
MediaServer: Resizing took 6 ms.
MediaServer: Execution time in c++ 3 ms
MediaServer: Resizing took 3 ms.

我有机会提高性能并缩短调整大小/合并/混合过程的执行时间吗?

是否有一些部分我可以并行化?

我是否有机会使用某些处理器功能?

巨大的性能损失是嵌套循环,但我不知道如何才能更好地编写它。

我希望整个过程达到 1 或 2 毫秒。这可能吗?

这是我使用的修改后的 visual c++ 函数。

  • pd 是我用来显示的可写位图的后备缓冲区 结果是 wpf。我使用的格式是默认的 32BppRGBA。
  • pixels 是 64x48 32BppRGBA 图像的 int[] 数组
  • widthSource 和 heightSource 是像素图像的大小
  • width和height是输出图片的目标尺寸
  • baseImage 是 320x240 8BppGray 图像的 int[] 数组

VC++代码:

unsigned int Resize(int* pd, int* pixels, int widthSource, int heightSource, int width, int height, byte* baseImage)
{
    unsigned int start = clock();

    float xs = (float)widthSource / width;
    float ys = (float)heightSource / height;

    float fracx, fracy, ifracx, ifracy, sx, sy, l0, l1, rf, gf, bf;
    int c, x0, x1, y0, y1;
    byte c1a, c1r, c1g, c1b, c2a, c2r, c2g, c2b, c3a, c3r, c3g, c3b, c4a, c4r, c4g, c4b;
    byte a, r, g, b;

    // Bilinear
    int srcIdx = 0;

    for (int y = 0; y < height; y++)
    {
        for (int x = 0; x < width; x++)
        {
            sx = x * xs;
            sy = y * ys;
            x0 = (int)sx;
            y0 = (int)sy;

            // Calculate coordinates of the 4 interpolation points
            fracx = sx - x0;
            fracy = sy - y0;
            ifracx = 1.0f - fracx;
            ifracy = 1.0f - fracy;
            x1 = x0 + 1;
            if (x1 >= widthSource)
            {
                x1 = x0;
            }
            y1 = y0 + 1;
            if (y1 >= heightSource)
            {
                y1 = y0;
            }

            // Read source color
            c = pixels[y0 * widthSource + x0];
            c1a = (byte)(c >> 24);
            c1r = (byte)(c >> 16);
            c1g = (byte)(c >> 8);
            c1b = (byte)(c);

            c = pixels[y0 * widthSource + x1];
            c2a = (byte)(c >> 24);
            c2r = (byte)(c >> 16);
            c2g = (byte)(c >> 8);
            c2b = (byte)(c);

            c = pixels[y1 * widthSource + x0];
            c3a = (byte)(c >> 24);
            c3r = (byte)(c >> 16);
            c3g = (byte)(c >> 8);
            c3b = (byte)(c);

            c = pixels[y1 * widthSource + x1];
            c4a = (byte)(c >> 24);
            c4r = (byte)(c >> 16);
            c4g = (byte)(c >> 8);
            c4b = (byte)(c);

            // Calculate colors
            // Alpha
            l0 = ifracx * c1a + fracx * c2a;
            l1 = ifracx * c3a + fracx * c4a;
            a = (byte)(ifracy * l0 + fracy * l1);

            // Write destination
            if (a > 0)
            {
                // Red
                l0 = ifracx * c1r + fracx * c2r;
                l1 = ifracx * c3r + fracx * c4r;
                rf = ifracy * l0 + fracy * l1;

                // Green
                l0 = ifracx * c1g + fracx * c2g;
                l1 = ifracx * c3g + fracx * c4g;
                gf = ifracy * l0 + fracy * l1;

                // Blue
                l0 = ifracx * c1b + fracx * c2b;
                l1 = ifracx * c3b + fracx * c4b;
                bf = ifracy * l0 + fracy * l1;

                // Cast to byte
                float alpha = a / 255.0f;
                r = (byte)((rf * alpha) + (baseImage[srcIdx] * (1.0f - alpha)));
                g = (byte)((gf * alpha) + (baseImage[srcIdx] * (1.0f - alpha)));
                b = (byte)((bf * alpha) + (baseImage[srcIdx] * (1.0f - alpha)));

                pd[srcIdx++] = (255 << 24) | (r << 16) | (g << 8) | b;
            }
            else
            {
                // Alpha, Red, Green, Blue                          
                pd[srcIdx++] = (255 << 24) | (baseImage[srcIdx] << 16) | (baseImage[srcIdx] << 8) | baseImage[srcIdx];
            }
        }
    }

    unsigned int end = clock() - start;
    return end;
}

Github repo

最佳答案

可以加快代码速度的一个操作是避免从整数到 float 的类型转换,反之亦然。这可以通过在合适的范围内使用 int 值而不是 0..1 范围内的 float 来实现

像这样:

for (int y = 0; y < height; y++)
{
    for (int x = 0; x < width; x++)
    {
        int sx1 = x * widthSource ;
        int x0 = sx1 / width;
        int fracx = (sx1 % width) ; // range 0..width - 1

变成类似的东西

        l0 = (fracx * c2a + (width - fracx) * c1a) / width ;

等等。有点棘手但可行

关于c++ - 有没有机会使双线性插值更快?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47416295/

相关文章:

c++ - 在每次调用中使用 namespace::function 比在 namespace::前面加前缀更好?

c - 在循环中创建一个新指针 C

c - 解析csv文件,填充结构并写入二进制数据文件

image - 在图像上使用特定大小的单元格绘制网格

c++ - 在不超过时间限制的情况下找到素数

C++继承问题

c++ - 是否通过名称和定义明确的引用在表达式中多次修改对象?

c - 没有初始化的for循环中的for循环

c++ - opencv 中的 cv::imshow 仅显示合成图像的一部分,但可以单独显示这些部分。为什么?

java - 检测图像中子图像的所有出现