Android-представление изображений - PullRequest
3 голосов
/ 22 ноября 2011

Я пытаюсь получить доступ к необработанным пиксельным данным изображения в Android.
Код выглядит примерно так:

   Bitmap bitmap =  BitmapFactory.decodeFile("image.png"); 
   // assert valid input
   if ((bitmap.getConfig() == null) || bitmap.getConfig() == Config.ARGB_8888)
      throw new Exception("bad config");

  ByteBuffer buffer = ByteBuffer.allocate(4 * bitmap.getWidth() * bitmap.getHeight());
  bitmap.copyPixelsToBuffer(buffer);        
  return buffer.array();

Как хранятся пиксели в линейном 1D buffer.array()?

  1. Первый элемент - это верхний левый пиксель или нижний левый пиксель (или что-то еще)?
  2. Основной ряд (строка за строкой) или основной столбец (столбец за столбцом))?
  3. Порядок каналов ARGB или BGRA?
  4. Основной ряд или основной столбец для каждого канала отдельно?
  5. Что-то еще

1 Ответ

5 голосов
/ 24 ноября 2011

Чтобы получить смещение в buffer.array() для данного пикселя x, y в изображении размером width x height с bytesPerPixel байтами на пиксель, используйте следующую формулу:

offsetForPixel = (y * width + x) * bytesPerPixel

Другими словами, первый элемент в массиве - это верхний левый пиксель, а следующие элементы - основные строки. Все данные для пикселя хранятся в соседних байтах и ​​не распределяются по каналам. Это ответ на 1, 2 и 4 выше. Теперь давайте обсудим 3, где все становится сложнее.

Что вы получаете с Bitmap.copyPixelsToBuffer () - это представление необработанных растровых данных, используемое низкоуровневой библиотекой рисования Android skia . Это имеет три существенных последствия:

  • Порядок каналов зависит от порядка байтов
  • Каналы предварительно умножаются на альфа
  • Способ упаковки каналов в содержащие типы данных настраивается

Последний пункт делает неудобным использование Bitmap.copyPixelsToBuffer() вообще, если вы хотите проверить отдельные пиксели, потому что вы просто не можете знать, как Skia была настроена для упаковки каналов. В качестве эксперимента попробуйте этот код:

int inputPixel = 0x336699cc;
int[] pixels = new int[] { inputPixel };
Bitmap bm = Bitmap.createBitmap(pixels, 1, 1, Config.ARGB_8888);
ByteBuffer bb = ByteBuffer.allocate(4);
bm.copyPixelsToBuffer(bb);
Log.i("TAG", "inputPixel = 0x" + Integer.toHexString(inputPixel));
for (int i = 0; i < 4; i++) {
    String byteString = "0x" + Integer.toHexString(bb.array()[i] & 0xff);
    Log.i("TAG", "outputPixel byte " + i + " = " + byteString);
}

Когда я запускаю это, я получаю этот вывод:

I/TAG ( 1995): inputPixel = 0x336699cc
I/TAG ( 1995): outputPixel byte 0 = 0x14
I/TAG ( 1995): outputPixel byte 1 = 0x1f
I/TAG ( 1995): outputPixel byte 2 = 0x29
I/TAG ( 1995): outputPixel byte 3 = 0x33

Мы можем видеть, что мы имеем дело с прямым порядком байтов, что представление в памяти предварительно умножено и что каналы были переупорядочены с ARGB на RGBA (мотивированные в исходном коде skia тем, что в представление памяти как OpenGL).

Если вы хотите прочитать данные пикселей, я предлагаю вам использовать Bitmap.getPixels () . Существует некоторое копирование, но, по крайней мере, API указывает, как возвращаемые данные форматируются.

...