Доступ к необработанным данным в растровом изображении ARGB 8888 Android
Я пытаюсь получить доступ к необработанным данным растрового изображения в формате ARGB_8888 на Android, используя copyPixelsToBuffer и copyPixelsFromBuffer методы. Однако вызов этих вызовов, похоже, всегда применяет альфа-канал к каналам rgb. Мне нужны необработанные данные в байте[] или аналогичном (для прохождения через JNI; да, я знаю о bitmap.h в Android 2.2, не может использовать это).
вот пример:
// 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 отличается; это нормально. Но я не хотите, чтобы альфа-канал применялся к каждой копии (что, по-видимому, и происходит).
это как copyPixelsToBuffer и copyPixelsFromBuffer должны работать? Есть ли любой способ получить необработанные данные в массив Byte[]?
добавил в ответ на ответ ниже:
указав buffer.order(ByteOrder.nativeOrder()); до copyPixelsToBuffer изменяет результат, но все же не так, как я хочу это:
pixel before = ef234567
value before = ef614121
value after = ffffff41
pixel after = ff41ffff
кажется, страдает от по существу той же проблемы (Альфа применяется к каждому copyPixelsFrom/ToBuffer).
4 ответов
Я понимаю, что это очень черствый и, вероятно, не поможет вам сейчас, но я столкнулся с этим недавно в попытке получить copyPixelsFromBuffer для работы в моем приложении. (Спасибо, что задали этот вопрос, кстати! Ты сэкономил мне кучу времени на отладке.) Я добавляю этот ответ в надежде, что он поможет другим, таким как я, идти вперед...
хотя я еще не использовал это, чтобы убедиться, что он работает, похоже, что, начиная с уровня API 19, у нас, наконец, будет способ указать, чтобы не "применять Альфа" (a.к. a. premultiply) в пределах Bitmap. Они добавляют setPremultiplied(boolean) метод, который должен помочь в таких ситуациях, как это происходит вперед, позволяя нам указать false.
надеюсь, это поможет!
Я предполагаю, что это может иметь отношение к порядку байтов ByteBuffer, который вы используете. ByteBuffer по умолчанию использует big endian. Установите endianess на буфер с
buffer.order(ByteOrder.nativeOrder());
посмотрите, поможет ли это.
кроме того, copyPixelsFromBuffer/copyPixelsToBuffer никак не изменяет пиксельные данные. Они копируются raw.
один из способов доступа к данным в растровом изображении-использовать метод getPixels (). Ниже вы можете найти пример, который я использовал для получения изображения в оттенках серого из данных argb, а затем обратно из массива байтов в растровое изображение (конечно, если вам нужен rgb, вы резервируете 3x байта и сохраняете их все...):
/*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;
}
}
это старый вопрос, но я добрался до той же проблемы и просто выяснил, что байт растрового изображения предварительно умножается, вы можете установить растровое изображение (начиная с API 19), Чтобы не предварительно умножать буфер, но в API они не дают никакой гарантии.
С документы:
public final void setPremultiplied(boolean premultiplied)задает, должно ли растровое изображение обрабатывать свои данные как предварительно умноженные. Растровые изображения всегда обрабатываются как предварительно умноженные на систему просмотра и холст для производительности. Хранение данных без предварительного умножения в растровом изображении (через
setPixel,setPixelsилиBitmapFactory.Options.inPremultiplied) может привести к неправильному смешиванию, если он нарисован фреймворком.этот метод не повлияет на поведение растрового изображения без альфа-канала, или если
hasAlpha()возвращает false.вызов
createBitmapилиcreateScaledBitmapс исходным растровым изображением, цвета которого не предварительно умножены, может привести кRuntimeException, так как эти функции требуют рисования источника, который не является поддерживается для растровых изображений без предварительного умножения.