Билинейная интерполяция байтового ролловера Java
Так что я работал над программным рендером 3d и мне нужен метод билинейной интерполяции. Я работаю с 3-байтовым растром BGR для скорости, и я не могу понять, как мой код функционально отличается от чего-то работающего, что я собрал вместе, непосредственно обращаясь к самому изображению. Я сначала выложу код перед результатами.
float lerp(float x1, float x2, float a) {
return x1 * (1 - a) + x2 * a;
}
Основной линейный интерполяционный алгоритм
void berp(float sourceX, float sourceY, int destPos, byte[] source, byte[] dest, int sourceWidth) {
float ax = sourceX - 0.5f;
float bx = sourceX + 0.5f;
float xa = ax - (int) (ax);
float ay = sourceY - 0.5f;
float by = sourceY + 0.5f;
float ya = ay - (int) (ay);
int pos1 = (((int) ay) * sourceWidth + ((int) ax)) * 3;
int pos2 = (((int) ay) * sourceWidth + ((int) bx)) * 3;
int pos3 = (((int) by) * sourceWidth + ((int) ax)) * 3;
int pos4 = (((int) by) * sourceWidth + ((int) bx)) * 3;
Просто установите его так, чтобы позиции от pos1 до pos4 располагались в исходном массиве из 4 ближайших пикселей к заданным картам (sourceX & sourceY). xa и ya - это расстояние от карт до центра пикселя сверху и пикселя слева, соответственно, поэтому, в основном, сколько нужно выбрать из каждого пикселя.
dest[destPos] = (byte) lerp(lerp(source[pos1], source[pos2], xa), lerp(source[pos3], source[pos4], xa), ya);
dest[destPos + 1] = (byte) lerp(lerp(source[pos1 + 1], source[pos2 + 1], xa), lerp(source[pos3 + 1], source[pos4 + 1], xa), ya);
dest[destPos + 2] = (byte) lerp(lerp(source[pos1 + 2], source[pos2 + 2], xa), lerp(source[pos3 + 2], source[pos4 + 2], xa), ya);
}
Здесь я интерполирую между rgb1 и rgb2, затем rgb3 и rgb4, используя xa. Затем я беру их результаты и интерполирую их, используя ya. Повторите для зеленого и красного, и это должно получиться золотой.
КРОМЕ:
Просто посмотрите на это изображение, нет способа описать его словами.
Я уверен, что вы видите мое затруднительное положение.
Любая помощь на этом этапе была бы желательна, я пытался часами безрезультатно, похоже, что это перенос с использованием броска с плавающей запятой, но я не очень понимаю, как можно получить, что lerp даст только когда-либо значение между x1 и x2 и только байты входят так...
*
*
*
*
*
Мне пришло в голову, что может быть полезно добавить код, который выдает правильное изображение. Некоторые имена и прочее могут отличаться, но суть всего этого есть:)
private int getColorBilinear (float x, float y, BufferedImage s) {
float nx = x - 0.5f;
float xa = nx - (int) (nx);
float ny = y - 0.5f;
float ya = ny - (int) (ny);
int rgb1 = s.getRGB((int) (x - 0.5), (int) (y - 0.5));
int rgb2 = s.getRGB((int) (x + 0.5), (int) (y - 0.5));
int rgb3 = s.getRGB((int) (x - 0.5), (int) (y + 0.5));
int rgb4 = s.getRGB((int) (x + 0.5), (int) (y + 0.5));
return (int) (interpolate(
interpolate(((rgb1 >> 16) & 0x000000FF), ((rgb2 >> 16) & 0x000000FF), xa),
interpolate(((rgb3 >> 16) & 0x000000FF), ((rgb4 >> 16) & 0x000000FF), xa),
ya)) << 16
| (int) (interpolate(
interpolate(((rgb1 >> 8) & 0x000000FF), ((rgb2 >> 8) & 0x000000FF), xa),
interpolate(((rgb3 >> 8) & 0x000000FF), ((rgb4 >> 8) & 0x000000FF), xa),
ya)) << 8
| (int) (interpolate(interpolate((rgb1 & 0x000000FF), (rgb2 & 0x000000FF), xa),
interpolate((rgb3 & 0x000000FF), (rgb4 & 0x000000FF), xa), ya));
}
1 ответ
Фактический процесс таков:
- Значение пикселя выбирается как байт без знака
- Байты (предполагается, что они подписаны) интерполируются
- Интерполированное значение используется в целевом изображении в качестве байта без знака
Теперь шокирующе это на самом деле работает для большинства случаев. Проблема возникает, когда одно из значений источника выше 127, а другое - ниже. Например, метод интерполяции будет видеть (129,125) как (-127,125) и пытается интерполировать как таковой. Одним из способов борьбы с этой проблемой было бы сначала преобразовать в int, а затем интерполировать; реализация которого приведена ниже.
float lerp(byte x1, byte x2, float a) {
return unsignedByteToInt(x1) * (1 - a) + unsignedByteToInt(x2) * a;
}
int unsignedByteToInt(byte x) {return (((int) x) + 255) % 255;}