【问题标题】:Reducing time complexity of bilinear interpolation降低双线性插值的时间复杂度
【发布时间】:2016-04-14 22:43:49
【问题描述】:

我在我的 android 应用程序中使用双线性插值。它运行完美,但需要很长时间才能得到结果。

我在 xi = 259,920 并且 yi = 259,920 时测试它。响应时间为:Galaxy Note 4 需要 3 秒, HTC One M8 大约需要 8 秒!那么我可以更改或使用什么来减少时间?!

我用于双线性插值的代码:

public static double[] BiInterp(Mat z, ArrayList < Double > xi, ArrayList < Double > yi) {
    // Declare matrix indeces
    int xi_i, yi_i;
    // Initialize output vector
    double zi[] = new double[xi.size()];
    double s00, s01, s10, s11;
    for (int i = 0; i < xi.size(); i++) { // Note: xi.length = yi.length !
        xi_i = xi.get(i).intValue(); // X index without round
        yi_i = yi.get(i).intValue(); // Y index without round
        if (xi_i < z.rows() - 1 && yi_i < z.cols() - 1 && xi_i >= 0 && yi_i >= 0) {
            // Four neighbors of sample pixel
                s00 = z.get(xi_i,yi_i)[0]; s01 = z.get(xi_i,yi_i + 1)[0];
                s10 = z.get(xi_i + 1,yi_i)[0];s11 = z.get(xi_i + 1,yi_i + 1)[0];

            int neighbor_no = 4; // As bilinear interpolation take 4 neighbors
            double A[][] = new double[neighbor_no][neighbor_no];

            A[0][0]=xi_i; A[0][1]=yi_i; A[0][2]=xi_i*yi_i; A[0][3]=1;
            A[1][0]=xi_i; A[1][1]=yi_i+1; A[1][2]=xi_i*(yi_i+1); A[1][3]=1;
            A[2][0]=xi_i+1; A[2][1]=yi_i; A[2][2]=(xi_i+1)*yi_i; A[2][3]=1;
            A[3][0]=xi_i+1; A[3][1]=yi_i+1; A[3][2]=(xi_i+1)*(yi_i+1); A[3][3]=1;

            GaussianElimination solveE = new GaussianElimination();
            double b[] = {s00,s01,s10,s11};
            double x[] = solveE.solve(A, b);
            zi[i] = xi.get(i)*x[0] + yi.get(i)*x[1] + xi.get(i)*yi.get(i)*x[2] + x[3];
        }
    }
    return zi;
}

我用高斯消元法求解4个未知数的方程

private static final double EPSILON = 1e-10;

// Gaussian elimination with partial pivoting
public static double[] solve(double[][] A, double[] b) {
    int N = b.length;

    for (int p = 0; p < N; p++) {

        // find pivot row and swap
        int max = p;
        for (int i = p + 1; i < N; i++) {
            if (Math.abs(A[i][p]) > Math.abs(A[max][p])) {
                max = i;
            }
        }
        double[] temp = A[p];
        A[p] = A[max];
        A[max] = temp;
        double t = b[p];
        b[p] = b[max];
        b[max] = t;

        // singular or nearly singular
        if (Math.abs(A[p][p]) <= EPSILON) {
            throw new RuntimeException("Matrix is singular or nearly singular");
        }

        // pivot within A and b
        for (int i = p + 1; i < N; i++) {
            double alpha = A[i][p] / A[p][p];
            b[i] -= alpha * b[p];
            for (int j = p; j < N; j++) {
                A[i][j] -= alpha * A[p][j];
            }
        }
    }

    // back substitution
    double[] x = new double[N];
    for (int i = N - 1; i >= 0; i--) {
        double sum = 0.0;
        for (int j = i + 1; j < N; j++) {
            sum += A[i][j] * x[j];
        }
        x[i] = (b[i] - sum) / A[i][i];
    }
    return x;
}

正如您在双线性代码中看到的,我立即从 Mat 对象中获取像素强度。但是,当我使用矩阵时,它需要的时间要少得多,例如注释 4 需要 1 秒。

但是从 Mat 图像转换为矩阵需要 4 秒。所以我更喜欢使用Mat。

【问题讨论】:

    标签: java android algorithm opencv time-complexity


    【解决方案1】:

    这里有一个更简单的双线性插值算法:

    • 找到 yi 的小数部分并用它在 s00 和 s01 之间进行插值以找到 s0,在 s10 和 s11 之间进行插值以找到 s1
    • 找到 xi 的小数部分并用它在 s0 和 s1 之间进行插值以找到 zi

    基本上,您将其分解为三个简单的线性插值。您可以将其可视化为 H 形。首先,您在 H 的左右柱子上进行插值,以获取各自向下的值。然后你沿着横梁插值得到中间的最终值。

    代码是这样的:

        xi_i = xi.get(i).intValue(); // X index without round
        yi_i = yi.get(i).intValue(); // Y index without round
        if (xi_i < z.rows() - 1 && yi_i < z.cols() - 1 && xi_i >= 0 && yi_i >= 0) {
            // Four neighbors of sample pixel
            s00 = z.get(xi_i,yi_i)[0]; s01 = z.get(xi_i,yi_i + 1)[0];
            s10 = z.get(xi_i + 1,yi_i)[0];s11 = z.get(xi_i + 1,yi_i + 1)[0];
    
            // find fractional part of yi:
            double yi_frac = yi.get(i) - (double)yi_i;
    
            // interpolate between s00 and s01 to find s0:
            double s0 = s00 + ((s01 - s00) * yi_frac);
            // interpolate between s10 and s11 to find s1:
            double s1 = s10 + ((s11 - s10) * yi_frac);
    
            // find fractional part of xi:
            double xi_frac = xi.get(i) - (double)xi_i;
    
            // interpolate between s0 and s1 to find zi:
            zi[i] =  s0 + ((s1 - s0) * xi_frac);
        }
    

    您还可以通过使用定点整数而不是双精度数来加快整个过程(以准确性为代价)。

    【讨论】:

    • 谢谢,您的修改对我帮助很大。它减少了大约一半的时间,在 Note 4 上是 2902 毫秒,现在是 1920 毫秒。我还尝试了您建议的其他解决方案并给了我 1844 毫秒,但我不喜欢使用它。我还能做些什么来加快响应时间吗?
    • 我简化了公式,希望能稍微加快速度。你调用这个函数多少次?尝试在函数外只分配一次 zi 并传入,内存分配可能会很慢。
    • 我只调用了一次,但是巨大的输入向量 xi 和 yi 使得响应时间很差。再次感谢,我暂时保留您的解决方案。
    • @Sheikhah Maasher 你的输入向量 xi 和 yi 是否以某种方式排序?
    猜你喜欢
    • 2011-06-15
    • 2012-07-28
    • 2016-10-07
    • 2020-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-06
    相关资源
    最近更新 更多