多个双线性插值的快速方法?

时间:2017-08-31 16:57:54

标签: c++ performance image-processing optimization bilinear-interpolation

我正在用C ++编写一个程序,用于从一组投影的2D图像重建一个3D对象,其中计算密集度最高的部分涉及通过双线性插值放大和移动每个图像。我目前有一对这项任务的功能; “blnSetup”定义了循环外的一些参数,然后“双线性”在循环中逐点应用插值:

(注意:'我'是包含有序行图像数据的一维数组)

//Pre-definition structure (in header)
struct blnData{
    float* X;
    float* Y;
    int* I;
    float X0;
    float Y0;
    float delX;
    float delY;
};

//Pre-definition function (outside the FOR loop)
extern inline blnData blnSetup(float* X, float* Y, int* I)
{
    blnData bln;
    //Create pointers to X, Y, and I vectors
    bln.X = X;
    bln.Y = Y;
    bln.I = I;

    //Store offset and step values for X and Y
    bln.X0 = X[0];
    bln.delX = X[1] - X[0];
    bln.Y0 = Y[0];
    bln.delY = Y[1] - Y[0];

    return bln;
}

//Main interpolation function (inside the FOR loop)
extern inline float bilinear(float x, float y, blnData bln)
{
    float Ixy;

    //Return -1 if the target point is outside the image matrix
    if (x < bln.X[0] || x > bln.X[-1] || y < bln.Y[0] || y > bln.Y[-1])
        Ixy = 0;
    //Otherwise, apply bilinear interpolation
    else
    {
        //Define known image width
        int W = 200;

        //Find nearest indices for interpolation
        int i = floor((x - bln.X0) / bln.delX);
        int j = floor((y - bln.Y0) / bln.delY);

        //Interpolate I at (xi, yj)
        Ixy = 1 / ((bln.X[i + 1] - bln.X[i])*(bln.Y[j + 1] - bln.Y[j])) *
            (
            bln.I[W*j + i] * (bln.X[i + 1] - x) * (bln.Y[j + 1] - y) +
            bln.I[W*j + i + 1] * (x - bln.X[i]) * (bln.Y[j + 1] - y) +
            bln.I[W*(j + 1) + i] * (bln.X[i + 1] - x) * (y - bln.Y[j]) +
            bln.I[W*(j + 1) + i + 1] * (x - bln.X[i]) * (y - bln.Y[j])
            );
    }

    return Ixy;
}

编辑:函数调用如下。 'flat.imgdata'是包含输入图像数据的std :: vector,'proj.imgdata'是包含转换后图像的std :: vector。

int Xs = flat.dim[0];
int Ys = flat.dim[1];

int* Iarr = flat.imgdata.data();
float II, x, y;

bln = blnSetup(X, Y, Iarr);

for (int j = 0; j < flat.imgdata.size(); j++)
{
    x = 1.2*X[j % Xs];
    y = 1.2*Y[j / Xs];
    II = bilinear(x, y, bln);
    proj.imgdata[j] = (int)II;
}

自从我开始优化以来,我已经能够通过在插值函数中从std :: vectors切换到C数组来减少~50x(!)的计算时间,并且通过清理冗余计算/类型转换/减少大约2x左右等等,但假设O(n)的n为处理像素的总数,则完全重建(~7e10像素)仍然需要40分钟左右 - 大约比我的目标<5分钟大一个数量级。

根据Visual Studio的性能分析器,插值函数调用(“II = bilinear(x,y,bln);”)不出所料仍然是我的大部分计算负荷。我还没有找到任何快速多重插值的线性代数方法,所以我的问题是:这基本上和我的代码一样快,不能将更多或更快的CPU应用到任务中吗?或者是否有一种不同的方法可以加快速度?

P.S。我现在也只用C ++编写了大约一个月的代码,所以请随意指出我可能犯的任何初学者错误。

2 个答案:

答案 0 :(得分:2)

我写了一篇很长的答案,建议查看OpenCV(opencv.org),或使用Halide(http://halide-lang.org/),并了解如何优化图像变形,但我认为较短的答案可能会更好。如果你真的只是缩放和翻译整个图像,OpenCV有代码可以做到这一点,我们也有一个在Halide中调整大小的例子(https://github.com/halide/Halide/blob/master/apps/resize/resize.cpp)。

如果你真的有一个算法需要使用浮点坐标来索引图像,浮点坐标是一个无法在整数坐标上变成一个中等简单函数的计算结果,那么你真的想要在一个上使用过滤纹理采样GPU。大多数用于优化CPU的技术依赖于利用算法中的一些常规访问模式,并从寻址中移除浮点数到整数的转换。 (对于调整大小,一个使用两个整数变量,一个用于索引图像的像素坐标,另一个是坐标的小数部分,它为权重内核编制索引。)如果不可能,则加速有限在CPU上。 OpenCV确实提供了相当一般的重映射支持,但它可能并不是那么快。

这里可能适用的两个优化是尝试将边界条件移出循环并使用两遍方法,其中水平和垂直维度分别处理。如果图像非常大,后者可能获胜也可能不获胜,并且需要平铺数据以适应高速缓存。平铺通常对于大图像非常重要,但目前尚不清楚这是第一阶性能问题,并且根据输入中的值,缓存行为可能不够常规。

答案 1 :(得分:2)

“矢量比数组慢50倍”。这是一个死的赠品,你处于调试模式,其中vector::operator[]没有内联。只需切换到发布模式,您可能会获得必要的加速,以及更多。

作为奖励,vector.back()方法,因此您可以正确替换[-1]。指向数组开头的指针不包含数组大小,因此无法以这种方式找到数组的后面。