การแปลง Java bitmap เป็นอาร์เรย์ไบต์


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();

9
สิ่งนี้จะทำให้เกิดปัญหาหรือไม่หากภาพไม่ใช่ประเภท PNG?
pgsandstrom

7
ไม่ใช่เพราะบิตแมปเป็นภาพที่ถอดรหัสแล้วไม่ว่าจะเป็นอะไรเช่นอาเรย์พิกเซล มันจะบีบอัดเป็น PNG ซึ่งจะไม่สูญเสียคุณภาพในการบีบอัด

5
ดีกว่าเป็นตัวเลือกที่ย้อนกลับจาก @Ted Hopp - การบีบอัดจะเสีย CPU เว้นแต่เป้าหมายของคุณคือภาพที่เข้ารหัส ....
ดินขาวไฟ

38
จากประสบการณ์ของฉันในระบบหน่วยความจำต่ำเช่น Android ต้องให้ความสนใจในการเพิ่ม bitmap.recycle (); หลังการบีบอัดและปิดสตรีมเพื่อหลีกเลี่ยงข้อยกเว้นการรั่วไหลของหน่วยความจำ
Son Huy TRAN

10
วิธีนี้เป็นวิธีการจัดสรรที่ไม่สิ้นเปลืองจริง ๆ คุณByteArrayOutputStreamจะจัดสรรbyte[]ขนาดเท่ากับการbyte[]สำรองข้อมูลของคุณBitmapจากนั้นByteArrayOutputStream.toByteArray()จะจัดสรรอีกbyte[]ขนาดที่เท่ากัน
zyamys

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
automaton

อาจจะพิจารณา endian ใหญ่ / เล็ก
NeoWang

หากคุณต้องการบันทึกอาร์เรย์ไบต์ในฐานข้อมูลท้องถิ่น (Sqlite, Room) คุณควรบีบอัดเหมือนคำตอบด้านบน!
J.Dragon

อย่างไรก็ตามโปรดทราบว่าหากไม่มีการบีบอัดความแตกต่างของขนาดนั้นน่าทึ่ง สำหรับทฤษฎีคุณสามารถอ่านวิกิพีเดียได้ แต่ตัวอย่างในกรณีของฉันผลการบีบอัด (ตามคำตอบที่ 1) คือ 20 MB อีกอันหนึ่ง (คำตอบนี้) คือ 48 MB
Kirill Starostin

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

ใช้ฟังก์ชั่นด้านล่างเพื่อเข้ารหัสบิตแมปเป็นไบต์ [] และในทางกลับกัน

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;
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.