在Android上实现双线性插值,支持Alpha值

时间:2014-04-22 22:17:28

标签: java android image-processing alpha

背景

我试图弄清楚如何最好地将一些旧的Java代码转换为使用双线性插值调整位图大小的C / C ++(here)。

为此,我决定使用Java(在Android上)使代码在Android上可用,以检查它是否仍然有用,因为我已经离开它,也许还允许其他人使用它

问题

即使一切看起来效果都很好,但在调整大小时我可以看到一些奇怪的黑色文物。我几乎可以肯定他们出现的原因:

enter image description here

注意黄色框左侧和红色框右侧的灰色。

我假设第一种情况下颜色应该介于黄色和白色之间,第二种情况下颜色介于红色和白色之间。

但是,我认为我得到的颜色是因为没有正确处理alpha值(白色区域实际上是透明的)。

那是因为如果我为位图设置真正的白色,我会得到正确的结果。

原始代码(适用于PC)

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

转换后的代码(适用于Android)

/** 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 Bitmap input,final Bitmap output)
    {
    final int oldHeight=input.getHeight(),oldWidth=input.getWidth();
    final int newHeight=output.getHeight(),newWidth=output.getWidth();
    // 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;
    int rgbTopLeft=0,rgbTopRight=0,rgbBottomLeft=0,rgbBottomRight=0,rgbTopMiddle=0,rgbBottomMiddle=0;
    // 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=input.getPixel(xTopLeft,yTopLeft+1);
          rgbBottomRight=input.getPixel(xTopLeft+1,yTopLeft+1);
          rgbBottomMiddle=Color.argb((int)(Color.alpha(rgbBottomLeft)*xcRatio2+Color.alpha(rgbBottomRight)*xcratio1),//
              (int)(Color.red(rgbBottomLeft)*xcRatio2+Color.red(rgbBottomRight)*xcratio1),//
              (int)(Color.green(rgbBottomLeft)*xcRatio2+Color.green(rgbBottomRight)*xcratio1),//
              (int)(Color.blue(rgbBottomLeft)*xcRatio2+Color.blue(rgbBottomRight)*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=input.getPixel(xTopLeft,yTopLeft);
          rgbTopRight=input.getPixel(xTopLeft+1,yTopLeft);
          rgbTopMiddle=Color.argb((int)(Color.alpha(rgbTopLeft)*xcRatio2+Color.alpha(rgbTopRight)*xcratio1),//
              (int)(Color.red(rgbTopLeft)*xcRatio2+Color.red(rgbTopRight)*xcratio1),//
              (int)(Color.green(rgbTopLeft)*xcRatio2+Color.green(rgbTopRight)*xcratio1),//
              (int)(Color.blue(rgbTopLeft)*xcRatio2+Color.blue(rgbTopRight)*xcratio1));
          rgbBottomLeft=input.getPixel(xTopLeft,yTopLeft+1);
          rgbBottomRight=input.getPixel(xTopLeft+1,yTopLeft+1);
          rgbBottomMiddle=Color.argb((int)(Color.alpha(rgbBottomLeft)*xcRatio2+Color.alpha(rgbBottomRight)*xcratio1),//
              (int)(Color.red(rgbBottomLeft)*xcRatio2+Color.red(rgbBottomRight)*xcratio1),//
              (int)(Color.green(rgbBottomLeft)*xcRatio2+Color.green(rgbBottomRight)*xcratio1),//
              (int)(Color.blue(rgbBottomLeft)*xcRatio2+Color.blue(rgbBottomRight)*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
        output.setPixel(x,y,Color.argb(//
            (int)(Color.alpha(rgbTopMiddle)*ycRatio2+Color.alpha(rgbBottomMiddle)*ycRatio1),//
            (int)(Color.red(rgbTopMiddle)*ycRatio2+Color.red(rgbBottomMiddle)*ycRatio1),//
            (int)(Color.green(rgbTopMiddle)*ycRatio2+Color.green(rgbBottomMiddle)*ycRatio1),//
            (int)(Color.blue(rgbTopMiddle)*ycRatio2+Color.blue(rgbBottomRight)*ycRatio1)));
        }
      }
    }
  }

问题:

我应该如何处理alpha通道?

在这种情况下应该对算法做些什么?

我应该在输出的每个像素上复用(组合)Alpha通道(并除以255)吗?

顺便说一句,我知道已经有一个使用该框架的内置解决方案,但这里的目的是从我的错误中吸取教训,并提供适用于C / C ++解决方案的东西。制作。

0 个答案:

没有答案