Accès aux données brutes dans Argb_8888 Android Bitmap
-
14-11-2019 - |
Question
J'essaie d'accéder aux données brutes d'un bitmap au format argb_8888 sur Android, en utilisant le copyPixelsToBuffer
et copyPixelsFromBuffer
Méthodes. Cependant, l'invocation de ces appels semble toujours appliquer le canal alpha aux canaux RVB. J'ai besoin des données brutes dans un octet [] ou similaire (pour passer par JNI; oui, je sais que Bitmap.h dans Android 2.2, ne peut pas l'utiliser).
Voici un échantillon:
// 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)));
Le journal affiche alors
hasAlpha() = true
pixel before = ef234567
value before = 214161ef
value after = ffffff61
pixel after = 619e9e9e
Je comprends que l'ordre des canaux Argb est différent; C'est très bien. Mais je ne veux pas que le canal Alpha soit appliqué à chaque copie (ce qu'il semble faire).
Est-ce comme ça copyPixelsToBuffer
et copyPixelsFromBuffer
sont censés fonctionner? Y a-t-il n'importe quel moyen d'obtenir les données brutes dans un octet []?
Ajouté en réponse à la réponse ci-dessous:
Mettre dans buffer.order(ByteOrder.nativeOrder());
avant le copyPixelsToBuffer
Change le résultat, mais toujours pas dans la façon dont je le veux:
pixel before = ef234567
value before = ef614121
value after = ffffff41
pixel after = ff41ffff
Semble souffrir essentiellement du même problème (Alpha est appliqué à chaque copyPixelsFrom/ToBuffer
).
La solution
Je me rends compte que c'est très vicié et ne vous aidera probablement pas maintenant, mais je l'ai rencontré récemment en essayant d'obtenir copyPixelsFromBuffer
pour travailler dans mon application. (Merci d'avoir posé cette question, btw! Vous m'avez sauvé des tonnes de temps dans le débogage.) J'ajoute cette réponse dans l'espoir que cela aide d'autres comme moi à l'avenir ...
Bien que je n'ai pas encore utilisé cela pour m'assurer que cela fonctionne, il semble que, au niveau de l'API 19, nous aurons enfin un moyen de spécifier de ne pas "appliquer l'alpha" (aka prémultiply) Bitmap
. Ils ajoutent un setPremultiplied(boolean)
Méthode qui devrait aider dans des situations comme celle-ci à l'avenir en nous permettant de spécifier false
.
J'espère que ça aide!
Autres conseils
Une façon d'accéder aux données dans Bitmap consiste à utiliser la méthode getPixels (). Ci-dessous, vous pouvez trouver un exemple que j'ai utilisé pour obtenir l'image en niveaux de gris à partir des données ArgB, puis de retour du tableau d'octets à Bitmap (bien sûr, si vous avez besoin de RGB, vous réservez 3x octets et les enregistrez tous ...):
/*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;
}
}
Je suppose que cela pourrait avoir à voir avec l'ordre des octets du bytebuffer que vous utilisez. ByteBuffer utilise Big Endian par défaut. Réglez les endettes sur le tampon avec
buffer.order(ByteOrder.nativeOrder());
Voyez si cela aide.
De plus, CopyPixelsFrombUffer / CopyPixelStobuffer ne change en aucun cas les données de pixels. Ils sont copiés crus.
C'est une vieille question, mais je suis arrivé au même problème, et j'ai juste compris que l'octet bitmap est pré-multiplié, vous pouvez définir le bitmap (à partir de l'API 19) pour ne pas pré-multiplier le tampon, mais dans l'API Ils ne garantissent pas.
De les documents:
public final void setPremultiplied(boolean premultiplied)
Définit si le bitmap doit traiter ses données comme pré-multipliées. Les bitmaps sont toujours traités comme pré-multipliés par le système de vue et la toile pour des raisons de performance. Stockage des données non pré-reproduies dans un bitmap (à travers
setPixel
,setPixels
, ouBitmapFactory.Options.inPremultiplied
) peut entraîner un mélange incorrect s'il est dessiné par le cadre.Cette méthode n'affectera pas le comportement d'un bitmap sans canal alpha, ou si
hasAlpha()
Renvoie false.Appel
createBitmap
oucreateScaledBitmap
avec un bitmap source dont les couleurs ne sont pas pré-multipliées peuvent entraîner unRuntimeException
, comme ces fonctions nécessitent de dessiner la source, qui n'est pas prise en charge pour les bitmaps non pré-multipliés.