Accesso ai dati grezzi in ARGB_8888 Android Bitmap
-
14-11-2019 - |
Domanda
Sto provando ad accedere ai dati grezzi di una bitmap in formato ARGB_8888 su Android, utilizzando il file copyPixelsToBuffer
E copyPixelsFromBuffer
metodi.Tuttavia, l'invocazione di tali chiamate sembra applicare sempre il canale alfa ai canali RGB.Ho bisogno dei dati grezzi in un byte[] o simile (per passare attraverso JNI;sì, conosco bitmap.h in Android 2.2, non posso usarlo).
Ecco un esempio:
// 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)));
Il registro quindi mostra
hasAlpha() = true
pixel before = ef234567
value before = 214161ef
value after = ffffff61
pixel after = 619e9e9e
Capisco che l'ordine dei canali argb sia diverso;va bene.Ma non voglio che il canale Alpha venga applicato su ogni copia (che è quello che sembra fare).
È così? copyPixelsToBuffer
E copyPixelsFromBuffer
dovrebbero funzionare?È lì Qualunque modo per ottenere i dati grezzi in un byte[]?
Aggiunto in risposta alla risposta di seguito:
Mettere dentro buffer.order(ByteOrder.nativeOrder());
prima di copyPixelsToBuffer
cambia il risultato, ma non ancora nel modo in cui lo voglio:
pixel before = ef234567
value before = ef614121
value after = ffffff41
pixel after = ff41ffff
Sembra soffrire essenzialmente dello stesso problema (l'alfa viene applicato su ciascuno copyPixelsFrom/ToBuffer
).
Soluzione
Mi rendo conto che questo è molto stantio e probabilmente non ti aiuterà ora, ma ho incontrato di recente questo nel tentativo di ottenere copyPixelsFromBuffer
per lavorare nella mia app.(Grazie per aver fatto questa domanda, btw! Mi hai salvato tonnellate di tempo nel debug.) Sto aggiungendo questa risposta nella speranza Aiuta gli altri come me andare avanti ...
Sebbene non abbia ancora usato questo per assicurarmi che funzioni, sembra che, a partire dal livello 19 API, avremo finalmente un modo per specificare di non "applicare l'alfa" (AKA Premultaliply) all'interno di Bitmap
.Stanno aggiungendo un setPremultiplied(boolean)
Metodo che dovrebbeAiutare in situazioni come questa andando avanti permettendoci di specificare false
.
Spero che questo aiuti!
Altri suggerimenti
Un modo per accedere ai dati in bitmap è utilizzare il metodo GetPixels ().Di seguito è possibile trovare un esempio che ho usato per ottenere un'immagine in scala di grigi da dati di argb e poi torna da byte array a bitmap (ovviamente se hai bisogno di RGB prenoti 3x byte e salvali tutti ...):
/*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;
}
}
. La mia ipotesi è che questo potrebbe dover fare con l'ordine dei byte del bytebuffer che stai utilizzando.Bytebuffer utilizza Big Endian per impostazione predefinita. Impostare Endianess sul buffer con
buffer.order(ByteOrder.nativeOrder());
.
Vedi se aiuta.
Inoltre, CopypixelsFruffuffer / CopypixelStoBuffer non modifica i dati dei pixel in alcun modo.Sono copiati crudi.
Questa è una vecchia domanda, ma ho riscontrato lo stesso problema e ho appena scoperto che i byte bitmap sono premoltiplicati, puoi impostare la bitmap (a partire dall'API 19) per non premoltiplicare il buffer, ma nell'API non danno alcuna garanzia.
Da i documenti:
public final void setPremultiplied(boolean premultiplied)
Imposta se la bitmap deve considerare i propri dati come premoltiplicati.Le bitmap vengono sempre trattate come premoltiplicate dal sistema di visualizzazione e dal Canvas per motivi di prestazioni.Memorizzazione di dati non premoltiplicati in una bitmap (attraverso
setPixel
,setPixels
, OBitmapFactory.Options.inPremultiplied
) può portare a una fusione errata se disegnata dal quadro.Questo metodo non influenzerà il comportamento di una bitmap senza canale alfa o se
hasAlpha()
restituisce falso.Chiamando
createBitmap
OcreateScaledBitmap
con una bitmap di origine i cui colori non sono premoltiplicati può risultare in aRuntimeException
, poiché tali funzioni richiedono il disegno della sorgente, che non è supportata per le bitmap non premoltiplicate.