2011-02-15 13 views
8

Estoy tratando de acceder a los datos brutos de un mapa de bits en formato ARGB_8888 en Android, utilizando los métodos copyPixelsToBuffer y copyPixelsFromBuffer. Sin embargo, la invocación de esas llamadas parece aplicar siempre el canal alfa a los canales rgb. Necesito los datos sin procesar en un byte [] o similar (para pasar por JNI; sí, sé sobre bitmap.h en Android 2.2, no puedo usar eso).Acceso a datos brutos en ARGB_8888 Android Bitmap

Este es un ejemplo:

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

El registro muestra entonces

hasAlpha() = true 
pixel before = ef234567 
value before = 214161ef 
value after = ffffff61 
pixel after = 619e9e9e 

entiendo que el orden de los canales ARGB es diferente; esta bien. Pero no deseo que se aplique el canal alfa en cada copia (que es lo que parece estar haciendo).

¿Es así como se supone que copyPixelsToBuffer y copyPixelsFromBuffer funcionan? ¿Hay alguna forma de para obtener los datos sin formato en un byte []?

añadido en respuesta a contestar a continuación:

Poner en buffer.order(ByteOrder.nativeOrder()); antes de la copyPixelsToBuffer sí cambia el resultado, pero aún no en la forma que yo quiero:

pixel before = ef234567 
value before = ef614121 
value after = ffffff41 
pixel after = ff41ffff 

parece sufrir de esencia el mismo problema (alfa se aplica en cada copyPixelsFrom/ToBuffer).

+0

Hola, también encuentro este problema. ¿Has encontrado una manera de resolver esto? Tal vez la forma de convertir los datos a valores reales RGB? –

+0

Es posible que desee comprobar '' Bitmap.isPremultiplied() '' también. Supongo que está configurado en '' true'' de forma predeterminada cuando el canal alfa está presente. – harism

Respuesta

1

Me doy cuenta de que esto es muy rancio y probablemente no lo ayude ahora, pero me encontré con esto recientemente al tratar de obtener copyPixelsFromBuffer para que funcione en mi aplicación. (Gracias por hacer esta pregunta, por cierto, me ahorraste mucho tiempo en la depuración.) Estoy agregando esta respuesta con la esperanza de que ayude a otros a seguir adelante ...

Aunque no he usado esto para asegurarnos de que funciona, parece que, a partir del nivel 19 de la API, finalmente tendremos una forma de especificar que no se "aplique el alfa" (también conocido como premultiplicado) dentro de Bitmap. Están agregando un método setPremultiplied(boolean) que debería ayudar en situaciones como esta en adelante al permitirnos especificar false.

Espero que esto ayude!

+0

Puedo aceptar esto como la mejor respuesta ;-) –

+0

@KasperPeeters jeje, gracias! Yo * espero * ¡esto resuelve nuestros problemas! – Turix

1

Supongo que esto podría tener que ver con el orden de bytes del ByteBuffer que está utilizando. ByteBuffer usa big endian por defecto. Conjunto endianess en el buffer con

buffer.order(ByteOrder.nativeOrder()); 

ver si ayuda.

Además, copyPixelsFromBuffer/copyPixelsToBuffer no cambia los datos de píxeles de ninguna manera. Se copian en crudo

+0

Eso cambia el resultado, pero no el problema principal: el canal alfa todavía se está aplicando para cada copia (vea la nota agregada en mi pregunta). –

+0

De hecho, copyPixelsToBuffer parece estar aplicando el canal alfa en canales rgb. Esto se puede ver claramente cuando configura Pixel (0,0. 0x7f224466); esto será 0x7f332211 cuando se lea en el búfer. Esto podría ser un error. Considera reportar esto. –

1

Una forma de acceder a los datos en Bitmap es utilizar el método getPixels(). A continuación puede encontrar un ejemplo que utilicé para obtener una imagen en escala de grises de los datos de argb y luego de vuelta de una matriz de bytes a un mapa de bits (por supuesto, si necesita rgb, reserve 3x bytes y guárdelos todos ...):

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

} 
0

Ésta es una vieja pregunta, pero llegué a la misma cuestión, y acabo de descubrir que el byte de mapa de bits son pre-multiplicado, se puede establecer el mapa de bits (a partir de la API 19) a no pre -multiplicar el buffer, pero en la API no hacen ninguna garantía.

De the docs:

public final void setPremultiplied(boolean premultiplied)

Establece si el mapa de bits debe tratar sus datos como pre-multiplicado. Los mapas de bits siempre se tratan como pre-multiplicados por el sistema de visualización y Canvas por motivos de rendimiento. Almacenar datos no multiplicados previamente en un mapa de bits (a través de setPixel, setPixels o BitmapFactory.Options.inPremultiplied) puede provocar una mezcla incorrecta si el marco los dibuja.

Este método no afectará el comportamiento de un mapa de bits sin un canal alfa, o si hasAlpha() devuelve falso.

Calling createBitmap o createScaledBitmap con un mapa de bits fuente cuyos colores no son pre-multiplicados puede resultar en una RuntimeException, ya que sus funciones requieren la elaboración de la fuente, que no se admite para mapas de bits multiplicado-un-pre.

Cuestiones relacionadas