interpolacion - Intentando convertir código de interpolación bilineal de Java a C/C++ en Android
interpolacion opencv (1)
Fondo
Creé una pequeña biblioteca de Android para manejar mapas de bits usando JNI (enlace aquí )
En el pasado, hice un código de interpolación bilineal como posible algoritmo para escalar imágenes. El algoritmo es un poco complejo y usa píxeles para formar el píxel objetivo.
El problema
Aunque no hay errores (sin errores de compilación y sin errores de tiempo de ejecución), la imagen de salida se ve así (escalado el ancho en x2):
El código
Básicamente, el código original de Java usaba SWT y solo soportaba RGB, pero es lo mismo para el canal Alpha. Funcionó antes perfectamente (aunque ahora que lo miro, parece crear muchos objetos en el camino).
Aquí está el código de Java:
/** class for resizing imageData using the Bilinear Interpolation method */
public class BilinearInterpolation
{
/** the method for resizing the imageData using the Bilinear Interpolation algorithm */
public static void resize(final ImageData inputImageData,final ImageData newImageData,final int oldWidth,final int oldHeight,final int newWidth,final int newHeight)
{
// position of the top left pixel of the 4 pixels to use interpolation on
int xTopLeft,yTopLeft;
int x,y,lastTopLefty;
final float xRatio=(float)newWidth/(float)oldWidth,yratio=(float)newHeight/(float)oldHeight;
// Y color ratio to use on left and right pixels for interpolation
float ycRatio2=0,ycRatio1=0;
// pixel target in the src
float xt,yt;
// X color ratio to use on left and right pixels for interpolation
float xcRatio2=0,xcratio1=0;
// copy data from source image to RGB values:
RGB rgbTopLeft,rgbTopRight,rgbBottomLeft=null,rgbBottomRight=null,rgbTopMiddle=null,rgbBottomMiddle=null;
RGB[][] startingImageData;
startingImageData=new RGB[oldWidth][oldHeight];
for(x=0;x<oldWidth;++x)
for(y=0;y<oldHeight;++y)
{
rgbTopLeft=inputImageData.palette.getRGB(inputImageData.getPixel(x,y));
startingImageData[x][y]=new RGB(rgbTopLeft.red,rgbTopLeft.green,rgbTopLeft.blue);
}
// do the resizing:
for(x=0;x<newWidth;x++)
{
xTopLeft=(int)(xt=x/xRatio);
// when meeting the most right edge, move left a little
if(xTopLeft>=oldWidth-1)
xTopLeft--;
if(xt<=xTopLeft+1)
{
// we are between the left and right pixel
xcratio1=xt-xTopLeft;
// color ratio in favor of the right pixel color
xcRatio2=1-xcratio1;
}
for(y=0,lastTopLefty=Integer.MIN_VALUE;y<newHeight;y++)
{
yTopLeft=(int)(yt=y/yratio);
// when meeting the most bottom edge, move up a little
if(yTopLeft>=oldHeight-1)
yTopLeft--;
// we went down only one rectangle
if(lastTopLefty==yTopLeft-1)
{
rgbTopLeft=rgbBottomLeft;
rgbTopRight=rgbBottomRight;
rgbTopMiddle=rgbBottomMiddle;
rgbBottomLeft=startingImageData[xTopLeft][yTopLeft+1];
rgbBottomRight=startingImageData[xTopLeft+1][yTopLeft+1];
rgbBottomMiddle=new RGB((int)(rgbBottomLeft.red*xcRatio2+rgbBottomRight.red*xcratio1),(int)(rgbBottomLeft.green*xcRatio2+rgbBottomRight.green*xcratio1),(int)(rgbBottomLeft.blue*xcRatio2+rgbBottomRight.blue*xcratio1));
}
else if(lastTopLefty!=yTopLeft)
{
// we went to a totally different rectangle (happens in every loop start,and might happen more when making the picture smaller)
rgbTopLeft=startingImageData[xTopLeft][yTopLeft];
rgbTopRight=startingImageData[xTopLeft+1][yTopLeft];
rgbTopMiddle=new RGB((int)(rgbTopLeft.red*xcRatio2+rgbTopRight.red*xcratio1),(int)(rgbTopLeft.green*xcRatio2+rgbTopRight.green*xcratio1),(int)(rgbTopLeft.blue*xcRatio2+rgbTopRight.blue*xcratio1));
rgbBottomLeft=startingImageData[xTopLeft][yTopLeft+1];
rgbBottomRight=startingImageData[xTopLeft+1][yTopLeft+1];
rgbBottomMiddle=new RGB((int)(rgbBottomLeft.red*xcRatio2+rgbBottomRight.red*xcratio1),(int)(rgbBottomLeft.green*xcRatio2+rgbBottomRight.green*xcratio1),(int)(rgbBottomLeft.blue*xcRatio2+rgbBottomRight.blue*xcratio1));
}
lastTopLefty=yTopLeft;
if(yt<=yTopLeft+1)
{
// color ratio in favor of the bottom pixel color
ycRatio1=yt-yTopLeft;
ycRatio2=1-ycRatio1;
}
// prepared all pixels to look at, so finally set the new pixel data
newImageData.setPixel(x,y,inputImageData.palette.getPixel(new RGB((int)(rgbTopMiddle.red*ycRatio2+rgbBottomMiddle.red*ycRatio1),(int)(rgbTopMiddle.green*ycRatio2+rgbBottomMiddle.green*ycRatio1),(int)(rgbTopMiddle.blue*ycRatio2+rgbBottomMiddle.blue*ycRatio1))));
}
}
}
}
Y aquí está el código C / C ++ que he intentado hacer con él:
typedef struct
{
uint8_t alpha, red, green, blue;
} ARGB;
int32_t convertArgbToInt(ARGB argb)
{
return (argb.alpha) | (argb.red << 16) | (argb.green << 8)
| (argb.blue << 24);
}
void convertIntToArgb(uint32_t pixel, ARGB* argb)
{
argb->red = ((pixel >> 24) & 0xff);
argb->green = ((pixel >> 16) & 0xff);
argb->blue = ((pixel >> 8) & 0xff);
argb->alpha = (pixel & 0xff);
}
...
/**scales the image using a high-quality algorithm called "Bilinear Interpolation" */ //
JNIEXPORT void JNICALL Java_com_jni_bitmap_1operations_JniBitmapHolder_jniScaleBIBitmap(
JNIEnv * env, jobject obj, jobject handle, uint32_t newWidth,
uint32_t newHeight)
{
JniBitmap* jniBitmap = (JniBitmap*) env->GetDirectBufferAddress(handle);
if (jniBitmap->_storedBitmapPixels == NULL)
return;
uint32_t oldWidth = jniBitmap->_bitmapInfo.width;
uint32_t oldHeight = jniBitmap->_bitmapInfo.height;
uint32_t* previousData = jniBitmap->_storedBitmapPixels;
uint32_t* newBitmapPixels = new uint32_t[newWidth * newHeight];
// position of the top left pixel of the 4 pixels to use interpolation on
int xTopLeft, yTopLeft;
int x, y, lastTopLefty;
float xRatio = (float) newWidth / (float) oldWidth, yratio =
(float) newHeight / (float) oldHeight;
// Y color ratio to use on left and right pixels for interpolation
float ycRatio2 = 0, ycRatio1 = 0;
// pixel target in the src
float xt, yt;
// X color ratio to use on left and right pixels for interpolation
float xcRatio2 = 0, xcratio1 = 0;
ARGB rgbTopLeft, rgbTopRight, rgbBottomLeft, rgbBottomRight, rgbTopMiddle,
rgbBottomMiddle, result;
for (x = 0; x < newWidth; ++x)
{
xTopLeft = (int) (xt = x / xRatio);
// when meeting the most right edge, move left a little
if (xTopLeft >= oldWidth - 1)
xTopLeft--;
if (xt <= xTopLeft + 1)
{
// we are between the left and right pixel
xcratio1 = xt - xTopLeft;
// color ratio in favor of the right pixel color
xcRatio2 = 1 - xcratio1;
}
for (y = 0, lastTopLefty = -30000; y < newHeight; ++y)
{
yTopLeft = (int) (yt = y / yratio);
// when meeting the most bottom edge, move up a little
if (yTopLeft >= oldHeight - 1)
--yTopLeft;
if (lastTopLefty == yTopLeft - 1)
{
// we went down only one rectangle
rgbTopLeft = rgbBottomLeft;
rgbTopRight = rgbBottomRight;
rgbTopMiddle = rgbBottomMiddle;
//rgbBottomLeft=startingImageData[xTopLeft][yTopLeft+1];
convertIntToArgb(
previousData[((yTopLeft + 1) * oldWidth) + xTopLeft],
&rgbBottomLeft);
//rgbBottomRight=startingImageData[xTopLeft+1][yTopLeft+1];
convertIntToArgb(
previousData[((yTopLeft + 1) * oldWidth)
+ (xTopLeft + 1)], &rgbBottomRight);
rgbBottomMiddle.alpha = rgbBottomLeft.alpha * xcRatio2
+ rgbBottomRight.alpha * xcratio1;
rgbBottomMiddle.red = rgbBottomLeft.red * xcRatio2
+ rgbBottomRight.red * xcratio1;
rgbBottomMiddle.green = rgbBottomLeft.green * xcRatio2
+ rgbBottomRight.green * xcratio1;
rgbBottomMiddle.blue = rgbBottomLeft.blue * xcRatio2
+ rgbBottomRight.blue * xcratio1;
}
else if (lastTopLefty != yTopLeft)
{
// we went to a totally different rectangle (happens in every loop start,and might happen more when making the picture smaller)
//rgbTopLeft=startingImageData[xTopLeft][yTopLeft];
convertIntToArgb(previousData[(yTopLeft * oldWidth) + xTopLeft],
&rgbTopLeft);
//rgbTopRight=startingImageData[xTopLeft+1][yTopLeft];
convertIntToArgb(
previousData[((yTopLeft + 1) * oldWidth) + xTopLeft],
&rgbTopRight);
rgbTopMiddle.alpha = rgbTopLeft.alpha * xcRatio2
+ rgbTopRight.alpha * xcratio1;
rgbTopMiddle.red = rgbTopLeft.red * xcRatio2
+ rgbTopRight.red * xcratio1;
rgbTopMiddle.green = rgbTopLeft.green * xcRatio2
+ rgbTopRight.green * xcratio1;
rgbTopMiddle.blue = rgbTopLeft.blue * xcRatio2
+ rgbTopRight.blue * xcratio1;
//rgbBottomLeft=startingImageData[xTopLeft][yTopLeft+1];
convertIntToArgb(
previousData[((yTopLeft + 1) * oldWidth) + xTopLeft],
&rgbBottomLeft);
//rgbBottomRight=startingImageData[xTopLeft+1][yTopLeft+1];
convertIntToArgb(
previousData[((yTopLeft + 1) * oldWidth)
+ (xTopLeft + 1)], &rgbBottomRight);
rgbBottomMiddle.alpha = rgbBottomLeft.alpha * xcRatio2
+ rgbBottomRight.alpha * xcratio1;
rgbBottomMiddle.red = rgbBottomLeft.red * xcRatio2
+ rgbBottomRight.red * xcratio1;
rgbBottomMiddle.green = rgbBottomLeft.green * xcRatio2
+ rgbBottomRight.green * xcratio1;
rgbBottomMiddle.blue = rgbBottomLeft.blue * xcRatio2
+ rgbBottomRight.blue * xcratio1;
}
lastTopLefty = yTopLeft;
if (yt <= yTopLeft + 1)
{
// color ratio in favor of the bottom pixel color
ycRatio1 = yt - yTopLeft;
ycRatio2 = 1 - ycRatio1;
}
// prepared all pixels to look at, so finally set the new pixel data
result.alpha = rgbTopMiddle.alpha * ycRatio2
+ rgbBottomMiddle.alpha * ycRatio1;
result.blue = rgbTopMiddle.blue * ycRatio2
+ rgbBottomMiddle.blue * ycRatio1;
result.red = rgbTopMiddle.red * ycRatio2
+ rgbBottomMiddle.red * ycRatio1;
result.green = rgbTopMiddle.green * ycRatio2
+ rgbBottomMiddle.green * ycRatio1;
newBitmapPixels[(y * newWidth) + x] = convertArgbToInt(result);
}
}
//get rid of old data, and replace it with new one
delete[] previousData;
jniBitmap->_storedBitmapPixels = newBitmapPixels;
jniBitmap->_bitmapInfo.width = newWidth;
jniBitmap->_bitmapInfo.height = newHeight;
}
La pregunta
¿Qué estoy haciendo mal?
¿También es posible hacer que el código sea un poco más legible? Estoy un poco oxidado en C / C ++ y era más un desarrollador C que un desarrollador C ++.
EDITAR: ahora funciona bien. He editado y reparado el código.
Lo único que ustedes pueden ayudar es dar consejos sobre cómo mejorarlo.
OK, todo comenzó con una mala conversión de los colores, luego pasó al uso de punteros y luego al básico de dónde colocar los píxeles.
El código que he escrito ahora funciona bien (agregó todas las correcciones necesarias).
Pronto todos podrán usar el nuevo código en el proyecto Github.