Бикубическая интерполяция двухмерного массива Java

Недавно я играл с бикубической интерполяцией, так как хочу сгенерировать землю на основе реальных карт высот внутри Minecraft. Причина, по которой я использую интерполяцию, заключается в том, что я хотел бы сделать мир более детализированным. После долгих исследований и множества проб и ошибок я решил спросить здесь. :)

Из-за ограниченной памяти я не могу масштабировать изображение при запуске и держать его загруженным, мне приходится делать интерполяцию на лету.

Кажется, я заставил кубическую интерполяцию работать, как показано здесь: Визуализация интерполяции Однако , Я не могу заставить бикубическую интерполяцию работать. В целях тестирования я использую небольшое изображение и масштабирую его на 4. Вот что делает код: Ввод -> Вывод

Это мой текущий код:

 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))];
}

Спасибо за помощь :)


person Gegy    schedule 01.12.2015    source источник


Ответы (2)


Насколько я понимаю, ваша реализация обрабатывает заданные координаты x и y совершенно по-другому, что не приводит к желаемому результату. Вы должны в основном сделать следующее.

Во-первых, вам необходимо определить четыре точки (пары координат) в сетке, которые будут составлять основу интерполяции, а также расстояния в обоих направлениях от сетки до места интерполяции. Это можно сделать следующим образом.

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

Методы интерполяции также обсуждаются здесь.

person Codor    schedule 01.12.2015

В вашем методе бикубической интерполяции вы пишете:

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

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

Как легко заметить, inBetweenPoint будет в интервале [0, 1) при вызове cubicInterpolate. Это означает, что интерполяция будет между beforePoint1 и point1. не, как хотелось бы, между точкой 1 и точкой 2.

Простое решение - написать

return cubicInterpolate(new double[]{beforePoint1, point1, point2, afterPoint2}, inBetweenPoint + 1, scale);
person WorldSEnder    schedule 01.12.2015
comment
Привет! Спасибо за помощь, но у меня все еще есть проблемы с выходным изображением, это изображение, которое я сейчас получаю: prntscr.com /9990d6 - person Gegy; 02.12.2015
comment
Это может быть проблема с кодом, генерирующим изображение, а не с самой интерполяцией. Можете ли вы опубликовать соответствующий код? - person WorldSEnder; 02.12.2015