【问题标题】:Faster Method for Multiple Bilinear Interpolation?多个双线性插值的更快方法?
【发布时间】:2018-02-09 17:14:36
【问题描述】:

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

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

//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 数组,我已经能够将计算时间减少约 50 倍(!),并通过清理冗余计算/类型转换/再减少 2 倍左右等等,但假设 O(n) 和 n 是处理像素的总数,完整的重建(~7e10 像素)仍然需要 40 分钟左右——比我的目标

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

附:我现在也只用 C++ 编码大约一个月,所以请随时指出我可能犯的任何初学者错误。

【问题讨论】:

  • 你能显示你调用blnSetupbilinear的代码吗?
  • 将双线性中的调用更改为引用以避免堆叠和取消堆叠参数。例如,使用const float &amp;x
  • bln.X[-1]?认真的吗?
  • 就像我说的,我最近开始使用 C++(来自 MATLAB);检查数组的最后一个元素有什么更好的方法?
  • 请注意,一行中的每个输出像素将分别计算相同的 Y 权重因子,并且每列计算相同的 X 权重因子。我首先在一个单独的结构中计算权重因子。

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


【解决方案1】:

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

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

【讨论】:

  • 我其实是在发布模式;当我将“I”变量从向量切换到数组时,我得到了 50 倍的改进——所以函数调用变量的方式似乎存在问题。
【解决方案2】:

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

如果您确实有一个算法需要使用浮点坐标索引图像,而浮点坐标的计算结果无法转化为整数坐标上的中等简单函数,那么您真的希望在显卡。大多数在 CPU 上进行优化的技术都依赖于利用算法中的一些常规访问模式,并从寻址中删除浮点到整数的转换。 (对于调整大小,一个使用两个整数变量,一个索引图像的像素坐标,另一个是坐标的小数部分,它索引一个权重内核。)如果这是不可能的,那么加速就会受到一定的限制在 CPU 上。 OpenCV 确实提供了相当通用的重新映射支持,但它可能不是那么快。

此处可能适用的两个优化是尝试将边界条件移出循环并使用两遍方法,其中水平和垂直尺寸分别处理。后者可能会赢也可能不会赢,如果图像非常大,则需要平铺数据以适应缓存。一般来说,平铺对于大图像非常重要,但不清楚这是这里的一阶性能问题,并且根据输入中的值,缓存行为可能无论如何都不够规则。

【讨论】:

    猜你喜欢
    • 2018-05-05
    • 2015-11-04
    • 1970-01-01
    • 2010-10-22
    • 1970-01-01
    • 2014-08-08
    • 2015-01-19
    • 2018-04-12
    • 1970-01-01
    相关资源
    最近更新 更多