convertir el mapa de bits de Java a una 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[]

Cuando miro el búfer después de que la llamada a copyPixelsToBufferlos bytes son todos 0 ... El mapa de bits devuelto por la cámara es inmutable ... pero eso no debería importar ya que está haciendo una copia.

¿Qué podría estar mal con este código?

Tom Fobear
fuente

Respuestas:

652

Intenta algo como esto:

Bitmap bmp = intent.getExtras().get("data");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
bmp.recycle();
Mezm
fuente
99
¿Esto no causará problemas si la imagen no es del tipo PNG?
pgsandstrom
77
no porque el mapa de bits es una imagen decodificada independientemente de lo que era, como una matriz de píxeles. Se comprimirá como PNG, lo que no perderá calidad en la compresión
55
mejor es la opción de rebobinado de @Ted Hopp: comprimirlo es un desperdicio de CPU a menos que su objetivo sea una imagen codificada ...
Kaolin Fire
38
En mi experiencia, en un sistema de poca memoria como Android, debe prestar atención para agregar bitmap.recycle (); justo después de la compresión, y cierre la transmisión para evitar la excepción de pérdida de memoria.
Son Huy TRAN
10
Este enfoque es realmente un desperdicio de asignaciones. Su ByteArrayOutputStreamasignará un byte[]tamaño igual a la byte[]copia de su Bitmap, a continuación, ByteArrayOutputStream.toByteArray()sin embargo, volverá a asignar otro byte[]del mismo tamaño.
zyamys
70

CompressFormat es demasiado lento ...

Prueba ByteBuffer.

※※※ Mapa de bits al 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 a mapa de bits ※※※

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);
朱 西西
fuente
55
Dado que esta pregunta tiene la etiqueta de Android, la conversión de bytes nuevamente a un mapa de bits también se puede hacer con una línea: ¿ Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length) dónde bytesestá su matriz de bytes?
Autómata
¿Quizás debería considerarse el endian grande / pequeño?
NeoWang
Si desea guardar la matriz de bytes en la base de datos local (Sqlite, Room), debe comprimir como respuesta superior.
J.Dragon
Tenga en cuenta, sin embargo, que sin la compresión, la diferencia de tamaño es dramática. Para la teoría, podría leer Wikipedia, pero por ejemplo, en mi caso, el resultado comprimido (según la primera respuesta) es de 20 MB, el otro (esta respuesta) es de 48 MB
Kirill Starostin
19

Aquí está la extensión de mapa de bits .convertToByteArrayescrita en 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
}
Tomás Ivan
fuente
18

¿Necesitas rebobinar el búfer, tal vez?

Además, esto podría suceder si el paso (en bytes) del mapa de bits es mayor que la longitud de la fila en píxeles * bytes / píxel. Haga la longitud de bytes b.remaining () en lugar de tamaño.

Ted Hopp
fuente
66
rewind()es la llave. Estaba obteniendo lo mismo BufferUnderflowExceptiony rebobinando el búfer después de llenarlo resolvió esto.
tstuts
9

Use las siguientes funciones para codificar el mapa de bits en byte [] y viceversa

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
fuente
6

Su conjunto de bytes es demasiado pequeño. Cada píxel ocupa 4 bytes, no solo 1, así que multiplique su tamaño * 4 para que la matriz sea lo suficientemente grande.

MindJuice
fuente
44
Su conjunto de bytes es lo suficientemente grande. getRowBytes()toma en cuenta los 4 bytes por píxel.
tstuts
3

Ted Hopp es correcto, de la documentación de la API:

public void copyPixelsToBuffer (Buffer dst)

"... Después de que este método regrese, la posición actual del búfer se actualiza: la posición se incrementa por el número de elementos escritos en el búfer".

y

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

"Lee los bytes de la posición actual en la matriz de bytes especificada, comenzando en el desplazamiento especificado, y aumenta la posición en la cantidad de bytes leídos".

CapitánCrunch
fuente
2

Para evitar OutOfMemoryerrores en archivos más grandes, resolvería la tarea dividiendo un mapa de bits en varias partes y fusionando los bytes de sus 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
fuente
0

Pruebe esto para convertir String-Bitmap o 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
fuente