在Android中访问ARGB_8888位图的原始数据

12

我正尝试在Android上使用copyPixelsToBuffercopyPixelsFromBuffer方法获取以ARGB_8888格式表示的位图的原始数据。 但是,调用这些方法似乎总是将Alpha通道应用于RGB通道。 我需要以byte []或类似形式获取原始数据(通过JNI传递; 是的,我知道Android 2.2中有bitmap.h,但无法使用它)。

以下是一个示例:

    // Create 1x1 Bitmap with alpha channel, 8 bits per channel
    Bitmap one = Bitmap.createBitmap(1,1,Bitmap.Config.ARGB_8888);
    one.setPixel(0,0,0xef234567);
    Log.v("?","hasAlpha() = "+Boolean.toString(one.hasAlpha()));
    Log.v("?","pixel before = "+Integer.toHexString(one.getPixel(0,0)));

    // Copy Bitmap to buffer
    byte[] store = new byte[4];
    ByteBuffer buffer  = ByteBuffer.wrap(store);
    one.copyPixelsToBuffer(buffer);

    // Change value of the pixel
    int value=buffer.getInt(0);
    Log.v("?", "value before = "+Integer.toHexString(value));
    value = (value >> 8) | 0xffffff00;
    buffer.putInt(0, value);
    value=buffer.getInt(0);
    Log.v("?", "value after = "+Integer.toHexString(value));

    // Copy buffer back to Bitmap
    buffer.position(0);
    one.copyPixelsFromBuffer(buffer);
    Log.v("?","pixel after = "+Integer.toHexString(one.getPixel(0,0)));

记录显示

hasAlpha() = true
pixel before = ef234567
value before = 214161ef
value after = ffffff61
pixel after = 619e9e9e
我理解argb通道的顺序不同,这没关系。但我不希望alpha通道在每次复制时都被应用(这似乎是正在发生的)。
这是copyPixelsToBuffer和copyPixelsFromBuffer的预期工作方式吗?有没有任何方法可以获取byte[]中的原始数据?
添加响应下面答案的内容:
在copyPixelsToBuffer之前放置buffer.order(ByteOrder.nativeOrder());的确会改变结果,但仍不是我想要的方式:
pixel before = ef234567
value before = ef614121
value after = ffffff41
pixel after = ff41ffff

似乎遭受基本相同的问题(alpha被应用于每个copyPixelsFrom/ToBuffer)。


嗨,我也遇到了这个问题。你找到解决方法了吗?也许将数据转换回真正的RGB值是解决的方法? - Luke Vo
您可能还想检查“Bitmap.isPremultiplied()”。猜测当存在 alpha 通道时,默认情况下设置为“true”。 - harism
你尝试过使用 Bitmap.getPixels() 吗?文档明确说明它返回未预乘的 ARGB 像素。下面也有关于 getPixels() 的答案,但你没有评论它。 - Andreas
4个回答

3

访问位图数据的一种方式是使用getPixels()方法。下面是我用于从argb数据获取灰度图像并从字节数组返回到位图的示例(当然,如果需要rgb,您可以保留3倍字节并保存它们全部...):

/*Free to use licence by Sami Varjo (but nice if you retain this line)*/

public final class BitmapConverter {

    private BitmapConverter(){};

   /**
    * Get grayscale data from argb image to byte array
    */
   public static byte[] ARGB2Gray(Bitmap img)
   {

       int width = img.getWidth();
       int height = img.getHeight();

       int[] pixels = new int[height*width];
       byte grayIm[] = new byte[height*width];

       img.getPixels(pixels,0,width,0,0,width,height);

       int pixel=0;
       int count=width*height;

       while(count-->0){
           int inVal = pixels[pixel];

           //Get the pixel channel values from int 
           double r = (double)( (inVal & 0x00ff0000)>>16 );
           double g = (double)( (inVal & 0x0000ff00)>>8  );
           double b = (double)(  inVal & 0x000000ff)      ;

           grayIm[pixel++] = (byte)( 0.2989*r + 0.5870*g + 0.1140*b );
       }

       return grayIm;
   }

   /**
    * Create a gray scale bitmap from byte array
    */
   public static Bitmap gray2ARGB(byte[] data, int width, int height)
   {
       int count = height*width;
       int[] outPix = new int[count];
       int pixel=0;
       while(count-->0){
           int val = data[pixel] & 0xff; //convert byte to unsigned
           outPix[pixel++] = 0xff000000 | val << 16 | val << 8 | val ;
       }

       Bitmap out =  Bitmap.createBitmap(outPix,0,width,width, height, Bitmap.Config.ARGB_8888);
       return out;
   }

}

1

我猜这可能与您使用的ByteBuffer的字节顺序有关。 ByteBuffer默认使用大端字节序。 请使用以下命令在缓冲区上设置字节顺序

buffer.order(ByteOrder.nativeOrder());

看看是否有帮助。

此外,copyPixelsFromBuffer/copyPixelsToBuffer 不会以任何方式更改像素数据。它们是原样复制的。


这改变了结果,但并没有解决主要问题:每个副本仍然应用了 alpha 通道(请参见我问题中添加的注释)。 - Kasper Peeters
确实,copyPixelsToBuffer 似乎将 alpha 通道应用于 rgb 通道。当您设置 setPixel(0,0.0x7f224466) 时,可以清楚地看到这一点 - 当读入缓冲区时,它将变为 0x7f332211。 这可能是一个错误。考虑报告此问题。 - misiu_mp

0

这是一个老问题,但我遇到了同样的问题,并且刚刚发现位图字节是预乘的,你可以将位图(从API 19开始)设置为不预乘缓冲区,但在API中,他们不做任何保证。

来自文档

public final void setPremultiplied(boolean premultiplied)

设置位图是否将其数据视为预乘。

出于性能原因,位图在视图系统和画布中始终被视为预乘。如果通过setPixelsetPixelsBitmapFactory.Options.inPremultiplied将未预乘的数据存储在位图中,当由框架绘制时可能会导致错误的混合。

此方法不会影响没有 alpha 通道的位图的行为,或者如果hasAlpha()返回 false。

使用颜色未预乘的源位图调用createBitmapcreateScaledBitmap可能会导致RuntimeException,因为这些函数需要绘制源位图,而不支持对未预乘位图进行绘制。


0

我知道这篇文章可能已经过时了,现在可能对你没有帮助,但最近我在尝试让我的应用程序中的copyPixelsFromBuffer正常工作时遇到了这个问题(顺便感谢你提出这个问题!你为我节省了大量调试时间)。我写下这篇答案是希望它能帮助像我一样的其他人...

虽然我还没有使用过这个方法来确保它是否有效,但从API Level 19开始,我们似乎终于有了一种方法来指定不要在Bitmap内“应用alpha”(也称为预乘)。他们正在添加一个setPremultiplied(boolean)方法,通过允许我们指定false,应该有助于像这样的情况。

希望这可以帮到你!


@KasperPeeters 嘿嘿,谢谢!我希望这能解决我们的问题! - Turix

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接