Доступ к необработанным данным в ARGB_8888 Android Bitmap - PullRequest
9 голосов
/ 16 февраля 2011

Я пытаюсь получить доступ к необработанным данным растрового изображения в формате 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?Есть ли какой-либо способ получить необработанные данные в байтах []?

Добавлено в ответ на ответ ниже:

Ввод buffer.order(ByteOrder.nativeOrder()); до того, как copyPixelsToBuffer изменит результат, но все же не так, как я хочу:

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

Кажется, что страдает по существу от той же проблемы (альфа применяется к каждому copyPixelsFrom/ToBuffer).

Ответы [ 4 ]

2 голосов
/ 23 октября 2014

Одним из способов доступа к данным в Bitmap является использование метода getPixels ().Ниже вы можете найти пример, который я использовал для получения изображения в градациях серого из данных argb, а затем обратно из байтового массива в Bitmap (конечно, если вам нужен 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;
   }

}
1 голос
/ 16 марта 2014

Я понимаю, что это очень устарело и, вероятно, не поможет вам сейчас, но я недавно столкнулся с этим, пытаясь заставить copyPixelsFromBuffer работать в моем приложении. (Спасибо, что задали этот вопрос, кстати! Вы сэкономили мне кучу времени на отладку.) Я добавляю этот ответ в надежде, что он поможет другим, таким как я, идти вперед ...

Хотя я еще не использовал это, чтобы убедиться, что он работает, похоже, что на уровне API 19 мы наконец-то сможем указать, что нужно «не применять альфа» (или предварительно умножать) внутри Bitmap. Они добавляют метод setPremultiplied(boolean), который должен помочь в подобных ситуациях, позволяя нам указать false.

Надеюсь, это поможет!

1 голос
/ 16 февраля 2011

Я предполагаю, что это может быть связано с порядком байтов байтового буфера, который вы используете. ByteBuffer по умолчанию использует байты с прямым порядком байтов. Установить endianess в буфере с помощью

buffer.order(ByteOrder.nativeOrder());

Посмотрите, поможет ли это.

Кроме того, copyPixelsFromBuffer / copyPixelsToBuffer никоим образом не изменяет данные пикселей. Они копируются в сыром виде.

0 голосов
/ 19 августа 2016

Это старый вопрос, но я столкнулся с той же проблемой, и только что выяснил, что бит растрового изображения предварительно умножен, вы можете установить битовый массив (с API 19), чтобы не предварительно умножать буфер, нов API они не дают никаких гарантий.

С документы :

public final void setPremultiplied(boolean premultiplied)

Устанавливает, должен ли растровое изображение обрабатывать свои данныекак предварительно умножено.Растровые изображения всегда обрабатываются как предварительно умноженные системой представления и Canvas по соображениям производительности.Хранение предварительно не умноженных данных в растровом изображении (через setPixel, setPixels или BitmapFactory.Options.inPremultiplied) может привести к неправильному смешиванию, если нарисовано платформой.

Этот метод не повлияет на поведениерастровое изображение без альфа-канала, или если hasAlpha() возвращает false.

Вызов createBitmap или createScaledBitmap с исходным растровым изображением, цвета которого предварительно не умножены, может привести к RuntimeException, посколькуфункции требуют рисования источника, который не поддерживается для предварительно не умноженных растровых изображений.

...