将Java位图转换为字节数组

321
  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...从相机返回的位图是不可变的...但这并不重要,因为它正在进行复制。

这段代码可能有什么问题?

10个回答

707

试试这样做:

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

11
如果图像不是PNG类型,这会引起问题吗? - pgsandstrom
10
不管Bitmap原来是什么样子,它已经是一张解码后的图片了,因此不会受到压缩的影响,就像一个像素数组。它会被压缩为PNG格式,这种格式在压缩时不会失去质量。 - user529543
5
@Ted Hopp 提出的倒带选项更好——压缩只有在你的目标是编码图像时才会浪费 CPU。 - Kaolin Fire
44
根据我的经验,在低内存系统如Android上,务必要注意在压缩后立即添加bitmap.recycle()代码,并关闭流以避免内存泄漏异常。 - Son Huy TRAN
12
这种方法会导致大量的内存分配浪费。你的ByteArrayOutputStream将分配一个与你的Bitmap后备byte[]大小相同的byte[],然后ByteArrayOutputStream.toByteArray()又会再次分配一个相同大小的byte[] - zyamys
显示剩余8条评论

88

CompressFormat太慢了...

试试ByteBuffer。

※※※Bitmap转字节数组※※※

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

// Somehow save (and later load) these as well:
int width = bitmap.getWidth();
int height = bitmap.getHeight();
String format = bitmap.getConfig().name();

※※※字节数组转位图※※※
Bitmap.Config configBmp = Bitmap.Config.valueOf(format);
Bitmap bitmap_tmp = Bitmap.createBitmap(width, height, configBmp);
ByteBuffer buffer = ByteBuffer.wrap(byteArray);
bitmap_tmp.copyPixelsFromBuffer(buffer);

8
因为这个问题带有Android标签,所以将字节转换回位图也可以用一行代码完成:Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length),其中bytes是您的字节数组。 - automaton
也许应该考虑大/小端? - NeoWang
如果您想将字节数组保存在本地数据库(Sqlite、Room)中,应像上面的答案一样进行压缩! - J.Dragon
1
请注意,如果没有压缩,文件大小的差异是非常明显的。你可以在维基百科上了解相关理论知识,但以我的案例为例,第一个答案给出的压缩结果是20MB,而这个答案则是48MB。 - Kirill Starostin

27

这里是用 Kotlin 写的位图扩展 .convertToByteArray

/**
 * 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
}

A/Bitmap: 错误,无法在此处访问无效/已释放的位图! - nicolas asinovich
这对我没有起作用。 - kc_dev
@kc_dev,你遇到了一些错误,或者有什么我们可以查看的东西吗? - Tomas Ivan

20

你需要倒回缓冲区吗?

此外,如果位图的跨度(以字节为单位)大于每行像素*每像素字节的行长度,则可能会出现此问题。将字节长度设置为 b.remaining() 而不是 size。


6
rewind()是关键。我之前遇到了相同的BufferUnderflowException错误,通过在填充缓冲区后倒带缓冲区解决了这个问题。 - tstuts

13
使用以下函数将位图编码为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

4

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

public void copyPixelsToBuffer (Buffer dst)

"...该方法返回后,缓冲区的当前位置会被更新:该位置会根据写入缓冲区的元素数量进行增加。"

以及

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

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


3
为了避免处理更大文件时出现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();
}

2
我想这样做可以 -
public static byte[] convertBitmapToByteArray(Bitmap bitmap){
        ByteBuffer byteBuffer = ByteBuffer.allocate(bitmap.getByteCount());
        bitmap.copyPixelsToBuffer(byteBuffer);
        byteBuffer.rewind();
        return byteBuffer.array();
    }

0

尝试使用以下方法将字符串位图或位图字符串进行转换

/**
 * @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;
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接