将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 后查看缓冲区时,从相机返回的位图是不可变的……但这无关紧要,因为它正在复制。

此代码可能有什么问题?

Answers:


652

尝试这样的事情:

Bitmap bmp = intent.getExtras().get("data");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
bmp.recycle();

9
如果图像不是PNG类型,这不会引起问题吗?
pgsandstrom,2012年

7
不会因为位图是一个已解码的图像而不管它是什么,例如像素阵列。它将作为PNG压缩,压缩时不会失去质量

5
更好的是从@Ted霍普倒带选项-压缩是CPU的浪费,除非你的目标是建立一个编码图像....
高岭土火

38
以我的经验,在诸如Android之类的低内存系统上,必须注意添加bitmap.recycle();。压缩之后,关闭流,以避免内存泄漏异常。
孙惠TRAN

10
这种方法确实浪费了分配。您ByteArrayOutputStream将分配一个byte[]byte[]您的后备大小相等的大小Bitmap,然后ByteArrayOutputStream.toByteArray()再次分配另一个byte[]相同大小的大小。
zyamys 2014年

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
自动机

也许应该考虑大/小字节序?
NeoWang19年

如果要将字节数组保存在本地数据库(Sqlite,Room)中,则应像上一个答案一样进行压缩!
J.Dragon

但是请注意,没有压缩,大小差异会很大。从理论上讲,您可以阅读Wikipedia,但是例如,在我的情况下,压缩结果(根据第一个答案)为20 MB,另一个(此答案)为48 MB
Kirill Starostin

19

这是.convertToByteArrayKotlin中写的位图扩展。

/**
 * 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 2013年

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乘以数组足够大。


4
他的字节数组足够大。 getRowBytes()考虑到每个像素4个字节。
tstuts

3

Ted Hopp是正确的,来自API文档:

public void copyPixelsToBuffer (Buffer dst)

“ ...返回此方法后,将更新缓冲区的当前位置:该位置将增加写入缓冲区的元素数。”

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

“从指定的偏移量开始,将当前位置的字节读取到指定的字节数组中,并将位置增加读取的字节数。”


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;
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.