วิธีที่เร็วที่สุดในการแปลง Image เป็น Byte array


106

ฉันกำลังสร้างแอปพลิเคชันการแชร์เดสก์ท็อประยะไกลซึ่งฉันจับภาพของเดสก์ท็อปและบีบอัดและส่งไปยังเครื่องรับ ในการบีบอัดภาพฉันต้องแปลงเป็นไบต์ []

ตอนนี้ฉันกำลังใช้สิ่งนี้:

public byte[] imageToByteArray(System.Drawing.Image imageIn)
{
    MemoryStream ms = new MemoryStream();
    imageIn.Save(ms,System.Drawing.Imaging.ImageFormat.Gif);
    return  ms.ToArray();
}

public Image byteArrayToImage(byte[] byteArrayIn)
{
     MemoryStream ms = new MemoryStream(byteArrayIn);
     Image returnImage = Image.FromStream(ms);
     return returnImage;
}

แต่ฉันไม่ชอบเพราะฉันต้องบันทึกใน ImageFormat และอาจใช้ทรัพยากร (ช้าลง) รวมทั้งให้ผลลัพธ์การบีบอัดที่แตกต่างกันฉันได้อ่านเกี่ยวกับการใช้ Marshal Copy และ memcpy แต่ฉันไม่สามารถทำได้ เข้าใจพวกเขา

แล้วมีวิธีอื่นอีกไหมที่จะบรรลุเป้าหมายนี้?


ทั้ง MemoryStream และ Image มีวิธีการกำจัดตรวจสอบให้แน่ใจว่าคุณกำลังกำจัดสิ่งเหล่านี้เนื่องจากอาจทำให้เกิด MemoryLeaks ได้
abc123

3
@ abc123: คุณไม่จำเป็นต้องทิ้ง a MemoryStream; เป็นทรัพยากรที่มีการจัดการทั้งหมดเว้นแต่คุณจะใช้ในระยะไกล ในทั้งสองกรณีนี้การทิ้งทรัพยากรจะไม่เหมาะสม
Jon Skeet

1
@JonSkeet น่าสนใจคุณได้ทำเกณฑ์มาตรฐานแล้วหรือยัง? เพื่อดูความเร็วที่. net ปล่อยวัตถุ? ฉันรู้ว่ามีอาร์กิวเมนต์ที่คล้ายกันสำหรับ DataTable และยังมีความแตกต่างที่เห็นได้ชัดในความเร็วที่ GarbageCollector รวบรวมหน่วยความจำที่จัดสรรเมื่อมีการใช้การกำจัด
abc123

@ abc123: ฉันไม่คาดคิดว่าจะมี - การกำจัดสตรีมไม่ได้ทำอะไรกับอาร์เรย์และ MemoryStream ไม่มี Finalizer (ไม่เหมือนกับ DataTable ซึ่งสืบทอดมาจาก MarshalByValueComponent)
Jon Skeet

2
โซลูชันสุดท้ายที่มีซอร์สโค้ดแบบเต็มหรือไม่
Kiquenet

คำตอบ:


39

แล้วมีวิธีอื่นอีกไหมที่จะบรรลุเป้าหมายนี้?

ไม่ในการแปลงรูปภาพเป็นอาร์เรย์ไบต์คุณต้องระบุรูปแบบภาพเช่นเดียวกับที่คุณต้องระบุการเข้ารหัสเมื่อคุณแปลงข้อความเป็นอาร์เรย์ไบต์

หากคุณกังวลเกี่ยวกับสิ่งประดิษฐ์การบีบอัดให้เลือกรูปแบบที่ไม่สูญเสีย หากคุณกังวลเกี่ยวกับทรัพยากรของ CPU ให้เลือกรูปแบบที่ไม่รบกวนการบีบอัดเช่นพิกเซล ARGB ดิบ แต่แน่นอนว่าจะนำไปสู่อาร์เรย์ไบต์ที่ใหญ่ขึ้น

โปรดทราบว่าหากคุณเลือกรูปแบบที่ไม่รวมถึงการบีบอัดมีจุดในการบีบอัดแล้วแถว byte หลังจากนั้นไม่ - ก็เกือบบางอย่างที่จะไม่มีผลประโยชน์


12
แทนที่จะ "เลือกรูปแบบที่ไม่สูญเสีย" คุณสามารถเลือกได้imageIn.RawFormatว่าจะพยายามบันทึกไบต์ภาพดิบใดโดยไม่ต้องเข้ารหัสอีกครั้ง
Chris F Carroll

52

มีคุณสมบัติ RawFormat ของพารามิเตอร์ Image ซึ่งส่งคืนรูปแบบไฟล์ของรูปภาพ คุณอาจลองทำสิ่งต่อไปนี้:

// extension method
public static byte[] imageToByteArray(this System.Drawing.Image image)
{
    using(var ms = new MemoryStream())
    {
        image.Save(ms, image.RawFormat);
        return ms.ToArray();
    }
}

9
ฉันขอแนะนำให้ทิ้ง MemoryStream หรือห่อเนื้อความของวิธีนี้ในคำสั่ง
use

@ นีลอัลเลนฉันเป็นคนใหม่ที่นี่คุณช่วยบอกเหตุผลได้ไหม
Khalil Khalaf

3
@FirstStep เพราะทำความสะอาดหลังจากตัวเอง :)
Sinaesthetic

@Sinaesthetic ฉันเห็น. และรูทีนคือใส่ฟังก์ชันอะไรก็ได้ที่ฉันต้องการเรียกใช้โดยใช้ () {}?
Khalil Khalaf

2
@FirstStep ไม่ค่อยนะ ถูกต้องมากขึ้น: หากคุณใช้วัตถุที่ติดตั้ง IDisposable คุณควรเรียก Dispose () เมื่อคุณทำเสร็จแล้วเพื่อล้างทรัพยากรใด ๆ ที่เกี่ยวข้อง คำสั่ง using () {} จะเรียกมันให้คุณเมื่อวัตถุอยู่นอกขอบเขตของคำสั่งนั้น ดังนั้นคุณสามารถทำmyObject.Dispose()หรือusing(myObject){}- ทั้งคู่ทำสิ่งเดียวกัน แต่คำสั่งใช้โดยพื้นฐานแล้วจะสร้างขอบเขตที่จะล้างข้อมูลให้คุณ
Sinaesthetic

14

ฉันไม่แน่ใจว่าคุณจะได้รับผลกำไรมหาศาลหรือไม่ด้วยเหตุผลที่ Jon Skeet ชี้ให้เห็น อย่างไรก็ตามคุณสามารถลองเปรียบเทียบเมธอดTypeConvert.ConvertToและดูว่าเปรียบเทียบกับวิธีการปัจจุบันของคุณอย่างไร

ImageConverter converter = new ImageConverter();
byte[] imgArray = (byte[])converter.ConvertTo(imageIn, typeof(byte[]));

ไม่สามารถแคสต์ออบเจ็กต์ประเภท 'System Byte []' เพื่อพิมพ์ 'System.Drawing.Image'
user123

14
public static byte[] ReadImageFile(string imageLocation)
    {
        byte[] imageData = null;
        FileInfo fileInfo = new FileInfo(imageLocation);
        long imageFileLength = fileInfo.Length;
        FileStream fs = new FileStream(imageLocation, FileMode.Open, FileAccess.Read);
        BinaryReader br = new BinaryReader(fs);
        imageData = br.ReadBytes((int)imageFileLength);
        return imageData;
    }

5
ยินดีต้อนรับสู่ stackoverflow.com คุณสามารถเพิ่มรายละเอียดเล็กน้อยเพื่ออธิบายว่าเหตุใดตัวอย่างโค้ดด้านบนจึงช่วยได้ สำหรับผู้ใช้ SO รายอื่นที่อาจไม่เข้าใจทั้งหมด ... stackoverflow.com/help/how-to-answer
Mack

นี่คือไฟล์สำหรับไบต์ แต่ OP ต้องการให้ออบเจ็กต์รูปวาดที่แปลงเป็นไบต์ ออบเจ็กต์การวาดสามารถเก็บไว้ในฐานข้อมูลไม่จำเป็นต้องเป็นระบบไฟล์เป็นอาร์เรย์ของไบต์ดังนั้นจึงต้องถูกแปลงไปมา ... แต่ไม่ใช่ไฟล์ใน FileStream ที่จะถูกแปลงเป็นไบต์ - เว้นแต่บางทีในระหว่าง อัพโหลดครั้งแรก
vapcguy

สิ่งนี้ช่วยฉันได้ในขณะที่ฉันต้องการทำสิ่งนี้กับไฟล์ ดีที่จะมีรอบที่เกี่ยวข้อง
Justin

5
public static class HelperExtensions
{
    //Convert Image to byte[] array:
    public static byte[] ToByteArray(this Image imageIn)
    {
        var ms = new MemoryStream();
        imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
        return ms.ToArray();
    }

    //Convert byte[] array to Image:
    public static Image ToImage(this byte[] byteArrayIn)
    {
        var ms = new MemoryStream(byteArrayIn);
        var returnImage = Image.FromStream(ms);
        return returnImage;
    }
}

2

วิธีที่เร็วที่สุดที่ฉันจะหาได้คือ:

var myArray = (byte[]) new ImageConverter().ConvertTo(InputImg, typeof(byte[]));

หวังว่าจะเป็นประโยชน์


ระวังเรื่องนี้โดยเฉพาะอย่างยิ่งถ้าใช้ WPF ซึ่งคุณจะมีSystem.Windows.Controls.Imageวัตถุ หากคุณต้องการแปลงหนึ่งในนั้นเป็นไบต์และคุณส่งต่อไปยังบรรทัดนี้เป็นInputImgสิ่งนี้จะไม่ได้ผล คาดว่าSystem.Drawing.Imageวัตถุ
vapcguy
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.