conversão de bitmap Java em matriz de bytes

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[]

Quando olho para o buffer depois que a chamada para copyPixelsToBufferos bytes é 0 ... O bitmap retornado da câmera é imutável ... mas isso não deve importar, pois está fazendo uma cópia.

O que poderia estar errado com esse código?

Tom Fobear
fonte

Respostas:

652

Tente algo como isto:

Bitmap bmp = intent.getExtras().get("data");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
bmp.recycle();
Mezm
fonte
9
Isso não causará problemas se a imagem não for do tipo PNG?
pgsandstrom
7
não porque o Bitmap é uma imagem decodificada, independentemente do que foi, como uma matriz de pixels. Ele irá comprimir como um PNG, que vai qualidade não perder a compressão
5
melhor é a opção de rebobinagem de @Ted Hopp - comprimi-lo é um desperdício de CPU menos que seu objetivo é uma imagem codificada ....
Caulim Fogo
38
Na minha experiência, em sistemas com pouca memória, como o Android, deve-se ter atenção para adicionar bitmap.recycle (); logo após a compactação e feche o fluxo para evitar a exceção de vazamento de memória.
Son Huy TRAN
10
Essa abordagem é realmente inútil de alocações. Você ByteArrayOutputStreamalocará um byte[]tamanho igual ao byte[]respetivo suporte e Bitmap, em seguida ByteArrayOutputStream.toByteArray(), alocará novamente outro byte[]tamanho do mesmo tamanho.
Zyamys 20/05
70

CompressFormat está muito lento ...

Experimente o ByteBuffer.

※※※ Bitmap para byte ※※※

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

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

※※※ byte para bitmap ※※※

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);
朱 西西
fonte
5
Uma vez que esta questão tem a marca Android, convertendo bytes de volta para um Bitmap também pode ser feito com um one-liner: Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length) onde bytesé a sua matriz de bytes
autômato
Talvez endian grande / pequeno deva ser considerado?
NeoWang
Se você deseja salvar a matriz de bytes no banco de dados local (Sqlite, Room), deve compactar como a resposta superior!
J.Dragon
Observe, no entanto, que sem a compressão, a diferença de tamanho é dramática. Para a teoria, você pode ler a Wikipedia, mas, por exemplo, no meu caso, o resultado compactado (conforme a 1ª resposta) é de 20 MB, o outro (essa resposta) é de 48 MB
Kirill Starostin
19

Aqui está a extensão de bitmap .convertToByteArrayescrita no 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
}
Tomas Ivan
fonte
18

Você precisa rebobinar o buffer, talvez?

Além disso, isso pode acontecer se o passo (em bytes) do bitmap for maior que o comprimento da linha em pixels * bytes / pixel. Torne o comprimento dos bytes b.remaining () em vez do tamanho.

Ted Hopp
fonte
6
rewind()É a chave. Eu estava recebendo o mesmo BufferUnderflowExceptione rebobinando o buffer depois de preenchê-lo, resolvi isso.
tstuts 27/02
9

Use as funções abaixo para codificar bitmap em byte [] e vice-versa

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);
}
Amol Suryawanshi
fonte
6

Sua matriz de bytes é muito pequena. Cada pixel ocupa 4 bytes, não apenas 1, então multiplique seu tamanho * 4 para que a matriz seja grande o suficiente.

MindJuice
fonte
4
Sua matriz de bytes é grande o suficiente. getRowBytes()leva em consideração os 4 bytes por pixel.
tstuts 27/02
3

Ted Hopp está correto, na documentação da API:

public void copyPixelsToBuffer (Buffer dst)

"... Depois que esse método retorna, a posição atual do buffer é atualizada: a posição é incrementada pelo número de elementos gravados no buffer."

e

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

"Lê bytes da posição atual na matriz de bytes especificada, iniciando no deslocamento especificado e aumenta a posição pelo número de bytes lidos."

CaptainCrunch
fonte
2

Para evitar OutOfMemoryerros em arquivos maiores, eu resolvia a tarefa dividindo um bitmap em várias partes e mesclando os bytes de suas partes.

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();
}
Ayaz Alifov
fonte
0

Tente isso para converter String-Bitmap ou 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;
    }
}
Mohammad nabil
fonte