android - 尝试在 Android 上将双线性插值代码从 Java 转换为 C/C++

标签 android c++ image-processing java-native-interface argb

背景

我制作了一个小型 Android 库,用于使用 JNI 处理位图(链接 here)

在很久以前,我编写了一些双线性插值代码作为图像缩放的可能算法。该算法有点复杂,使用周围的像素构成目标像素。

问题

即使没有错误(没有编译错误也没有运行时错误),输出图像看起来像这样(宽度缩放 x2):

enter image description here

代码

基本上原始的Java代码使用SWT并且只支持RGB,但是Alpha channel 是一样的。它之前工作得非常完美(尽管现在我看了一下,它似乎在途中创建了很多对象)。

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

这是我尝试从中创建的 C/C++ 代码:

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

问题

我做错了什么?

是否也可以使代码更具可读性?我对 C/C++ 有点生疏,我更像是 C 开发人员而不是 C++ 开发人员。

编辑:现在它工作正常。我已经编辑并修复了代码。

你们唯一能提供帮助的就是提供有关如何让它变得更好的提示。

最佳答案

好吧,这一切都是从颜色的错误转换开始的,然后是指针的使用,然后是基本的像素放置位置。

我编写的代码现在可以正常工作(添加了所有需要的修复)。

很快你们就可以在 Github 项目上使用新代码了。

关于android - 尝试在 Android 上将双线性插值代码从 Java 转换为 C/C++,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23230047/

相关文章:

java - 从 onItemClick 检索选定的菜单项文本 - Android?

c++ - 关于 C++ 中的 const 指针的问题?

c++ - 打印树显示奇怪的字符

C++ 如何避免尾随零?

image-processing - 如何仅在整个对象完全位于感兴趣区域内后才放置边界框?

c# - 非 ui 线程上的过程图像

c++ - 如何使用 vlfeat 和 opencv 在 C++ 中确定图像的 PHOW 特征?

android - Android Studio 2.0 Gradle错误[NoClassDefFoundError:无法初始化类org.gradle.StartParameter]

android - Looper 线程上的 CoroutineDispatcher

android - 如何在代码中引用 strings.xml 中的字符串?