Доступ к необработанным данным в растровом изображении Android ARGB_8888
-
14-11-2019 - |
Вопрос
Я пытаюсь получить доступ к необработанным данным растрового изображения в формате ARGB_8888 на Android, используя команду copyPixelsToBuffer
и copyPixelsFromBuffer
методы.Однако вызов этих вызовов, похоже, всегда применяет альфа-канал к каналам RGB.Мне нужны необработанные данные в формате byte[] или аналогичном (для прохождения через 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
должны работать?Есть любой способ получить необработанные данные в байте []?
Добавлено в ответ на ответ ниже:
Установка buffer.order(ByteOrder.nativeOrder());
перед copyPixelsToBuffer
меняет результат, но все равно не так, как я хочу:
pixel before = ef234567
value before = ef614121
value after = ffffff41
pixel after = ff41ffff
Кажется, страдает по существу от одной и той же проблемы (альфа применяется к каждому copyPixelsFrom/ToBuffer
).
Решение
Я понимаю, что это очень устарело и, вероятно, сейчас вам не поможет, но я недавно наткнулся на это, пытаясь получить copyPixelsFromBuffer
работать в моем приложении.(Спасибо, что задали этот вопрос, кстати!Вы сэкономили мне массу времени при отладке.) Я добавляю этот ответ в надежде, что он поможет другим, таким как я, двигаться вперед...
Хотя я еще не использовал это, чтобы убедиться, что оно работает, похоже, что начиная с уровня API 19, у нас наконец-то появится способ указать не «применять альфу» (т.предварительно умножить) внутри Bitmap
.Они добавляют setPremultiplied(boolean)
метод, который должен помочь в подобных ситуациях в будущем, позволяя нам указать false
.
Надеюсь, это поможет!
Другие советы
Один из способов доступа к данным в Bitmap — использовать метод 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;
}
}
Я предполагаю, что это может быть связано с порядком байтов используемого вами ByteBuffer.ByteBuffer по умолчанию использует обратный порядок байтов.Установите порядок байтов в буфере с помощью
buffer.order(ByteOrder.nativeOrder());
Посмотрите, поможет ли это.
Более того, copyPixelsFromBuffer/copyPixelsToBuffer никак не меняет данные пикселей.Они копируются в сыром виде.
Это старый вопрос, но я столкнулся с той же проблемой и только что понял, что байты растрового изображения предварительно умножаются, вы можете установить растровое изображение (начиная с API 19) так, чтобы оно не умножало предварительно буфер, а в API они не дают никаких гарантий.
От документы:
public final void setPremultiplied(boolean premultiplied)
Устанавливает, должно ли растровое изображение обрабатывать свои данные как предварительно умноженные.Растровые изображения всегда обрабатываются системой просмотра и Canvas как предварительно умноженные из соображений производительности.Хранение непредварительно умноженных данных в растровом изображении (через
setPixel
,setPixels
, илиBitmapFactory.Options.inPremultiplied
) может привести к неправильному смешиванию, если оно нарисовано фреймворком.Этот метод не повлияет на поведение растрового изображения без альфа-канала или если
hasAlpha()
возвращает ложь.Вызов
createBitmap
илиcreateScaledBitmap
с исходным растровым изображением, цвета которого не умножаются предварительно, может привести кRuntimeException
, поскольку эти функции требуют рисования источника, который не поддерживается для растровых изображений без предварительного умножения.