Java 2D阵列双立方插值

时间:2015-12-01 12:10:14

标签: java interpolation terrain cubic bicubic

我最近一直在玩Bicubic Interpolation,因为我想根据Minecraft中的真实高度图来生成地球。我使用插值的原因是因为我想让世界有更多的细节。经过大量的研究,以及大量的反复试验,我决定来这里问一下。 :)

由于内存有限,我无法在启动时缩放图像,并保持加载,我必须动态插值。

我似乎已经进行了Cubic插值工作,如下所示: Visualisation of the interpolation 但是,我不能让Bicubic Interpolation工作。出于测试目的,我使用的是一个小图像,并将其缩放为4.这就是代码的作用:Input -> Output

这是我目前的代码:

 public static double cubicInterpolate(double[] points, double x, double scale)
{
    x /= scale;

    double inBetweenPoint = x;
    int xInHeightmap = (int) x;
    inBetweenPoint -= xInHeightmap;

    double beforePoint1 = safe(points, xInHeightmap - 1);
    double point1 = safe(points, xInHeightmap);
    double point2 = safe(points, xInHeightmap + 1);
    double afterPoint2 = safe(points, xInHeightmap + 2);

    double p = (afterPoint2 - point2) - (beforePoint1 - point1);
    double q = (beforePoint1 - point1) - p;
    double r = point2 - beforePoint1;
    double s = point1;

    return (p * Math.pow(inBetweenPoint, 3)) + (q * Math.pow(inBetweenPoint, 2)) + (r * inBetweenPoint) + s;
}

public static double bicubicInterpolate(double[][] points, double x, double y, double scale)
{
    x /= scale;

    double inBetweenPoint = x;
    int xInHeightmap = (int) x;
    inBetweenPoint -= xInHeightmap;

    double beforePoint1 = cubicInterpolate(safe(points, xInHeightmap - 1), y, scale);
    double point1 = cubicInterpolate(safe(points, xInHeightmap), y, scale);
    double point2 = cubicInterpolate(safe(points, xInHeightmap + 1), y, scale);
    double afterPoint2 = cubicInterpolate(safe(points, xInHeightmap + 2), y, scale);

    return cubicInterpolate(new double[]{beforePoint1, point1, point2, afterPoint2}, inBetweenPoint + 1, scale);
}

public static double[] safe(double[][] p, int i)
{
    return p[Math.max(0, Math.min(i, p.length - 1))];
}

public static double safe(double[] p, int i)
{
    return p[Math.max(0, Math.min(i, p.length - 1))];
}

感谢您的帮助:)

2 个答案:

答案 0 :(得分:3)

据我了解,您的实现以完全不同的方式处理给定的xy坐标,这不会产生预期的结果。您基本上应该执行以下操作。

首先,您需要确定网格中的四个点(坐标对),它们将构成插值的基础,以及从网格到插值的两个方向上的距离。这可以按如下方式完成。

int xfloor = (int)x;
int yfloor = (int)y;
int xdelta = x - (double)xfloor;
int ydelta = y - (double)yfloor;

然后是所需的坐标对(取决于轴的方向)

P1 = (xfloor,     yfloor    ) // left upper corner
P2 = (xfloor,     yfloor + 1) // left lower corner
P3 = (xfloor + 1 ,yfloor + 1) // right lower corner
P4 = (xfloor + 1, yfloor    ) // left upper corner

最后你首先要沿平行轴插入,然后在中间插入,可以使用中间值按如下方式进行插补。

val1 = cubic(value(P1), value(P2), deltay) // interpolate cubically on the left edge
val2 = cubic(value(P4), value(P3), deltay) // interpolate cubically on the right edge
val = cubic (val1, val2, deltax) // interpolate cubically between the intermediates

还讨论了插值方法here

答案 1 :(得分:0)

在您的双三次插值方法中,您可以写:

//double inBetweenPoint = x;
//int xInHeightmap = (int) x;
//inBetweenPoint -= xInHeightmap;

return cubicInterpolate(new double[]{beforePoint1, point1, point2, afterPoint2}, inBetweenPoint, scale);

正如您可以轻松看到的那样,inBetweenPoint会在[0, 1)来电时间隔cubicInterpolate。这意味着插值将在before1和point1之间。不需要,在point1和point2之间。

简单的解决方法是写

return cubicInterpolate(new double[]{beforePoint1, point1, point2, afterPoint2}, inBetweenPoint + 1, scale);