преобразование растрового изображения Java в байтовый массив

292
  Bitmap bmp   = intent.getExtras().get("data");
  int size     = bmp.getRowBytes() * bmp.getHeight();
  ByteBuffer b = ByteBuffer.allocate(size);

  bmp.copyPixelsToBuffer(b);

  byte[] bytes = new byte[size];

  try {
     b.get(bytes, 0, bytes.length);
  } catch (BufferUnderflowException e) {
     // always happens
  }
  // do something with byte[]

Когда я смотрю на буфер после вызова copyPixelsToBufferвсех байтов равны 0 ... Растровое изображение, возвращаемое с камеры, является неизменным ... но это не должно иметь значения, так как он делает копию.

Что может быть не так с этим кодом?

Том Фобер
источник

Ответы:

652

Попробуйте что-то вроде этого:

Bitmap bmp = intent.getExtras().get("data");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
bmp.recycle();
Mezm
источник
9
Не вызовет ли это проблем, если изображение не имеет тип PNG?
pgsandstrom
7
не будет, потому что растровое изображение является декодированным изображением независимо от того, что было, как массив пикселей. Он будет сжиматься как PNG, который не будет терять качество при сжатии
5
лучше вариант перемотки от @Ted Hopp - сжатие - это пустая трата ЦП, если ваша цель не является закодированным изображением ....
Kaolin Fire
38
По моему опыту, на системах с низким объемом памяти, таких как Android, необходимо обратить внимание на добавление bitmap.recycle (); сразу после сжатия и закройте поток, чтобы избежать исключения утечки памяти.
Сын Хай ТРАН
10
Этот подход действительно бесполезен для распределения. Вы ByteArrayOutputStreamвыделите byte[]размер, равный byte[]сумме поддержки Bitmap, затем ByteArrayOutputStream.toByteArray()снова выделите еще один byte[]такой же размер.
Зямис
70

CompressFormat работает слишком медленно ...

Попробуйте ByteBuffer.

※※※ Растровое изображение в байт ※※※

width = bitmap.getWidth();
height = bitmap.getHeight();

int size = bitmap.getRowBytes() * bitmap.getHeight();
ByteBuffer byteBuffer = ByteBuffer.allocate(size);
bitmap.copyPixelsToBuffer(byteBuffer);
byteArray = byteBuffer.array();

※※※ байт в растровое изображение ※※※

Bitmap.Config configBmp = Bitmap.Config.valueOf(bitmap.getConfig().name());
Bitmap bitmap_tmp = Bitmap.createBitmap(width, height, configBmp);
ByteBuffer buffer = ByteBuffer.wrap(byteArray);
bitmap_tmp.copyPixelsFromBuffer(buffer);
朱 西西
источник
5
Поскольку в этом вопросе есть тег Android, преобразование байтов обратно в растровое изображение также можно выполнить с помощью однострочного: Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length) где bytesнаходится ваш байтовый массив
автомат
Возможно, следует рассмотреть big / small endian?
NeoWang
Если вы хотите сохранить байтовый массив в локальной БД (Sqlite, Room), вы должны сжать как верхний ответ!
Дж. Драгон
Обратите внимание, однако, что без сжатия разница в размерах будет существенной. Для теории вы могли бы прочитать Википедию, но, например, в моем случае сжатый результат (согласно 1-му ответу) составляет 20 МБ, другой (этот ответ) - 48 МБ
Кирилл Старостин
19

Вот растровое расширение, .convertToByteArrayнаписанное на Kotlin.

/**
 * Convert bitmap to byte array using ByteBuffer.
 */
fun Bitmap.convertToByteArray(): ByteArray {
    //minimum number of bytes that can be used to store this bitmap's pixels
    val size = this.byteCount

    //allocate new instances which will hold bitmap
    val buffer = ByteBuffer.allocate(size)
    val bytes = ByteArray(size)

    //copy the bitmap's pixels into the specified buffer
    this.copyPixelsToBuffer(buffer)

    //rewinds buffer (buffer position is set to zero and the mark is discarded)
    buffer.rewind()

    //transfer bytes from buffer into the given destination array
    buffer.get(bytes)

    //return bitmap's pixels
    return bytes
}
Томас Иван
источник
18

Возможно, вам нужно перемотать буфер?

Кроме того, это может произойти, если шаг (в байтах) растрового изображения превышает длину строки в пикселях * байт / пиксель. Сделайте длину байтов b.remaining () вместо размера.

Тед Хопп
источник
6
rewind()это ключ. Я получал то же самое BufferUnderflowExceptionи перематывал буфер после заполнения, это решило это.
tstuts
9

Используйте следующие функции для кодирования растрового изображения в byte [] и наоборот

public static String encodeTobase64(Bitmap image) {
    Bitmap immagex = image;
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    immagex.compress(Bitmap.CompressFormat.PNG, 90, baos);
    byte[] b = baos.toByteArray();
    String imageEncoded = Base64.encodeToString(b, Base64.DEFAULT);
    return imageEncoded;
}

public static Bitmap decodeBase64(String input) {
    byte[] decodedByte = Base64.decode(input, 0);
    return BitmapFactory.decodeByteArray(decodedByte, 0, decodedByte.length);
}
Амол Суряванши
источник
6

Ваш байтовый массив слишком мал. Каждый пиксель занимает 4 байта, а не только 1, поэтому умножьте ваш размер * 4, чтобы массив был достаточно большим.

MindJuice
источник
4
Его байтовый массив достаточно велик. getRowBytes()принимает на учет 4 байта на пиксель.
tstuts
3

Тед Хопп прав, из документации API:

public void copyPixelsToBuffer (Buffer dst)

«... После возврата этого метода текущая позиция буфера обновляется: позиция увеличивается на количество элементов, записанных в буфере».

и

public ByteBuffer get (byte[] dst, int dstOffset, int byteCount)

Msgstr "Считывает байты из текущей позиции в указанный байтовый массив, начиная с указанного смещения, и увеличивает позицию на количество прочитанных байтов."

CaptainCrunch
источник
2

Чтобы избежать OutOfMemoryошибок для больших файлов, я решил бы эту задачу, разбив растровое изображение на несколько частей и объединив байты их частей.

private byte[] getBitmapBytes(Bitmap bitmap)
{
    int chunkNumbers = 10;
    int bitmapSize = bitmap.getRowBytes() * bitmap.getHeight();
    byte[] imageBytes = new byte[bitmapSize];
    int rows, cols;
    int chunkHeight, chunkWidth;
    rows = cols = (int) Math.sqrt(chunkNumbers);
    chunkHeight = bitmap.getHeight() / rows;
    chunkWidth = bitmap.getWidth() / cols;

    int yCoord = 0;
    int bitmapsSizes = 0;

    for (int x = 0; x < rows; x++)
    {
        int xCoord = 0;
        for (int y = 0; y < cols; y++)
        {
            Bitmap bitmapChunk = Bitmap.createBitmap(bitmap, xCoord, yCoord, chunkWidth, chunkHeight);
            byte[] bitmapArray = getBytesFromBitmapChunk(bitmapChunk);
            System.arraycopy(bitmapArray, 0, imageBytes, bitmapsSizes, bitmapArray.length);
            bitmapsSizes = bitmapsSizes + bitmapArray.length;
            xCoord += chunkWidth;

            bitmapChunk.recycle();
            bitmapChunk = null;
        }
        yCoord += chunkHeight;
    }

    return imageBytes;
}


private byte[] getBytesFromBitmapChunk(Bitmap bitmap)
{
    int bitmapSize = bitmap.getRowBytes() * bitmap.getHeight();
    ByteBuffer byteBuffer = ByteBuffer.allocate(bitmapSize);
    bitmap.copyPixelsToBuffer(byteBuffer);
    byteBuffer.rewind();
    return byteBuffer.array();
}
Аяз Алифов
источник
0

Попробуйте это преобразовать String-Bitmap или Bitmap-String

/**
 * @param bitmap
 * @return converting bitmap and return a string
 */
public static String BitMapToString(Bitmap bitmap){
    ByteArrayOutputStream baos=new ByteArrayOutputStream();
    bitmap.compress(Bitmap.CompressFormat.PNG,100, baos);
    byte [] b=baos.toByteArray();
    String temp=Base64.encodeToString(b, Base64.DEFAULT);
    return temp;
}

/**
 * @param encodedString
 * @return bitmap (from given string)
 */
public static Bitmap StringToBitMap(String encodedString){
    try{
        byte [] encodeByte=Base64.decode(encodedString,Base64.DEFAULT);
        Bitmap bitmap= BitmapFactory.decodeByteArray(encodeByte, 0, encodeByte.length);
        return bitmap;
    }catch(Exception e){
        e.getMessage();
        return null;
    }
}
Мухаммед Набиль
источник