ไบต์อาร์เรย์เพื่อการแปลงรูปภาพ


104

ฉันต้องการแปลงอาร์เรย์ไบต์เป็นรูปภาพ

นี่คือรหัสฐานข้อมูลของฉันที่ฉันได้รับอาร์เรย์ไบต์:

public void Get_Finger_print()
{
    try
    {
        using (SqlConnection thisConnection = new SqlConnection(@"Data Source=" + System.Environment.MachineName + "\\SQLEXPRESS;Initial Catalog=Image_Scanning;Integrated Security=SSPI "))
        {
            thisConnection.Open();
            string query = "select pic from Image_tbl";// where Name='" + name + "'";
            SqlCommand cmd = new SqlCommand(query, thisConnection);
            byte[] image =(byte[]) cmd.ExecuteScalar();
            Image newImage = byteArrayToImage(image);
            Picture.Image = newImage;
            //return image;
        }
    }
    catch (Exception) { }
    //return null;
}

รหัสการแปลงของฉัน:

public Image byteArrayToImage(byte[] byteArrayIn)
{
    try
    {
        MemoryStream ms = new MemoryStream(byteArrayIn,0,byteArrayIn.Length);
        ms.Write(byteArrayIn, 0, byteArrayIn.Length);
        returnImage = Image.FromStream(ms,true);//Exception occurs here
    }
    catch { }
    return returnImage;
}

เมื่อฉันมาถึงบรรทัดพร้อมความคิดเห็นข้อยกเว้นต่อไปนี้จะเกิดขึ้น: Parameter is not valid.

ฉันจะแก้ไขสิ่งที่ทำให้เกิดข้อยกเว้นนี้ได้อย่างไร


คุณตรวจสอบว่าไบต์ของรูปภาพในแบบสอบถามของคุณถูกต้องหรือไม่ คุณสามารถทำ File.WriteAllBytes ("myimage.jpg", byteArrayIn) เพื่อตรวจสอบ
Holstebroe

คำตอบ:


112

คุณกำลังเขียนไปยังสตรีมหน่วยความจำของคุณสองครั้งและคุณจะไม่ทิ้งสตรีมหลังการใช้งาน คุณกำลังขอให้ตัวถอดรหัสภาพใช้การแก้ไขสีแบบฝัง

ลองสิ่งนี้แทน:

using (var ms = new MemoryStream(byteArrayIn))
{
    return Image.FromStream(ms);
}

คุณอาจทำให้สตรีมหน่วยความจำไม่สามารถเขียนได้อย่างชัดเจนหลังจากการเริ่มต้น: MemoryStream ใหม่ (byteArrayIn, false)
Holstebroe

28
ซึ่งละเมิดข้อกำหนดใน MSDN สำหรับ Image.FromStream () โดยระบุว่า "คุณต้องเปิดสตรีมไว้ตลอดอายุการใช้งานของรูปภาพ" ดูstackoverflow.com/questions/3290060/…
RenniePet

82

บางทีฉันอาจจะขาดอะไรไป แต่สำหรับฉันหนึ่งซับนี้ใช้งานได้ดีกับอาร์เรย์ไบต์ที่มีรูปภาพของไฟล์ JPEG

Image x = (Bitmap)((new ImageConverter()).ConvertFrom(jpegByteArray));

แก้ไข:

ดูคำตอบฉบับปรับปรุงที่นี่: วิธีการแปลงรูปภาพในไบต์อาร์เรย์


AAAh ขอบคุณในที่สุดคำตอบที่ดี ทำไมคำตอบมากมายกับสตรีมหน่วยความจำมันทำให้ฉันมีปัญหามาก ขอบคุณมาก !
Julian50

คำตอบที่ดี! สิ่งนี้สามารถใช้ในฟังก์ชันแยกต่างหากในขณะที่ข้อเสนออื่น ๆ ทั้งหมดที่ใช้ MemoryStream ไม่สามารถทำได้ (ต้องเปิดสตรีมไว้ตลอดอายุการใช้งานของรูปภาพ)
A_L

23
public Image byteArrayToImage(byte[] bytesArr)
{
    using (MemoryStream memstr = new MemoryStream(bytesArr))
    {
        Image img = Image.FromStream(memstr);
        return img;
    }
}

แม้ว่าจะไม่ใช่ความคิดที่ดี แต่ฉันก็ใส่ GC.Collect () ก่อนสตรีมหน่วยความจำ หน่วยความจำของฉันกำลังจะหมดเมื่อฉันโหลดไฟล์กราฟิกขนาดใหญ่จำนวนมากไว้ล่วงหน้าเป็น bytearrays ในหน่วยความจำแล้วเปลี่ยนเป็นภาพในระหว่างการดู
Kayot

10

ฉันต้องการทราบว่ามีข้อบกพร่องในการแก้ปัญหาโดย @ isaias-b

วิธีแก้ปัญหานั้นถือว่าstrideเท่ากับความยาวแถว แต่มันไม่จริงเสมอไป เนื่องจากการจัดตำแหน่งหน่วยความจำดำเนินการโดย GDI การก้าวย่างอาจมากกว่าความยาวของแถว สิ่งนี้จะต้องนำมาพิจารณา มิฉะนั้นรูปภาพที่เลื่อนไม่ถูกต้องจะถูกสร้างขึ้น การเติมไบต์ในแต่ละแถวจะถูกละเว้น

การก้าวย่างคือความกว้างของพิกเซลแถวเดียว (เส้นสแกน) โดยปัดขึ้นเป็นขอบเขตสี่ไบต์

รหัสคงที่:

using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

public static class ImageExtensions
{
    public static Image ImageFromRawBgraArray(this byte[] arr, int width, int height, PixelFormat pixelFormat)
    {
        var output = new Bitmap(width, height, pixelFormat);
        var rect = new Rectangle(0, 0, width, height);
        var bmpData = output.LockBits(rect, ImageLockMode.ReadWrite, output.PixelFormat);

        // Row-by-row copy
        var arrRowLength = width * Image.GetPixelFormatSize(output.PixelFormat) / 8;
        var ptr = bmpData.Scan0;
        for (var i = 0; i < height; i++)
        {
            Marshal.Copy(arr, i * arrRowLength, ptr, arrRowLength);
            ptr += bmpData.Stride;
        }

        output.UnlockBits(bmpData);
        return output;
    }
}

เพื่อแสดงให้เห็นถึงสิ่งที่สามารถนำไปสู่เรามาสร้างPixelFormat.Format24bppRgbภาพไล่ระดับ 101x101:

var width = 101;
var height = 101;
var gradient = new byte[width * height * 3 /* bytes per pixel */];
for (int i = 0, pixel = 0; i < gradient.Length; i++, pixel = i / 3)
{
    var x = pixel % height;
    var y = (pixel - x) / width;
    gradient[i] = (byte)((x / (double)(width - 1) + y / (double)(height - 1)) / 2d * 255);
}

หากเราจะคัดลอกอาร์เรย์ทั้งหมดตามที่อยู่ที่ชี้โดยbmpData.Scan0เราจะได้ภาพต่อไปนี้ การเปลี่ยนภาพเนื่องจากส่วนหนึ่งของภาพเขียนเป็นไบต์ช่องว่างซึ่งถูกละเว้น นั่นคือสาเหตุที่แถวสุดท้ายไม่สมบูรณ์:

ไม่สนใจก้าวย่าง

แต่ถ้าเราจะคัดลอกตัวชี้ปลายทางการเปลี่ยนทีละแถวตามbmpData.Strideค่าระบบจะสร้างภาพที่ถูกต้อง:

ก้าวย่างคำนึงถึง

Stride อาจเป็นลบ:

หากก้าวย่างเป็นบวกบิตแมปจะอยู่บนลงล่าง หากก้าวย่างเป็นลบบิตแมปจะอยู่ล่างขึ้นบน

แต่ฉันไม่ได้ทำงานกับภาพดังกล่าวและสิ่งนี้อยู่นอกเหนือบันทึกของฉัน

คำตอบที่เกี่ยวข้อง: C # - RGB Buffer จาก Bitmap แตกต่างจาก C ++


6

คำตอบที่นำเสนอทั้งหมดถือว่าไบต์อาร์เรย์มีข้อมูลในรูปแบบไฟล์ที่รู้จักเช่น gif, png หรือ jpg แต่เมื่อเร็ว ๆ นี้ฉันมีปัญหาในการพยายามแปลงbyte[]s ที่มีข้อมูล BGRA เชิงเส้นเป็นImageวัตถุอย่างมีประสิทธิภาพ รหัสต่อไปนี้แก้ไขโดยใช้Bitmapวัตถุ

using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
public static class Extensions
{
    public static Image ImageFromRawBgraArray(
        this byte[] arr, int width, int height)
    {
        var output = new Bitmap(width, height);
        var rect = new Rectangle(0, 0, width, height);
        var bmpData = output.LockBits(rect, 
            ImageLockMode.ReadWrite, output.PixelFormat);
        var ptr = bmpData.Scan0;
        Marshal.Copy(arr, 0, ptr, arr.Length);
        output.UnlockBits(bmpData);
        return output;
    }
}

นี่เป็นรูปแบบเล็กน้อยของโซลูชันที่โพสต์บนไซต์นี้


เป็นข้อมูลอ้างอิง MSDN: Bitmap (Int32, Int32): "ตัวสร้างนี้สร้างบิตแมปที่มีค่าการแจกแจง PixelFormat ของ Format32bppArgb" ซึ่งหมายถึงไบต์ [0] เป็นสีน้ำเงินไบต์ [1] เป็นสีเขียวไบต์ [2] เป็นสีแดง ไบต์ [3] คืออัลฟาไบต์ [4] เป็นสีน้ำเงินและอื่น ๆ
brk

1
ขอบคุณสำหรับการเพิ่ม "โดยใช้" ที่จำเป็นทั้งหมด คนส่วนใหญ่ลืมสิ่งนั้นไปและเป็นความเจ็บปวดที่ต้องพบพวกเขาทั้งหมด
Casey Crookston

6

มีวิธีง่ายๆดังต่อไปนี้คุณสามารถใช้FromStreamวิธีการของภาพเพื่อทำเคล็ดลับเพียงอย่าลืมใช้System.Drawing;

// using image object not file 
public byte[] imageToByteArray(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;
}

5

ลอง(UPDATE)

MemoryStream ms = new MemoryStream(byteArrayIn,0,byteArrayIn.Length);
ms.Position = 0; // this is important
returnImage = Image.FromStream(ms,true);

2
ไม่มีเหตุผลที่จะเขียนอาร์เรย์ไบต์ไปยังสตรีมหน่วยความจำหลังจากเริ่มต้นด้วยอาร์เรย์ไบต์เดียวกัน อันที่จริงฉันไม่แน่ใจว่า MemoryStream อนุญาตให้เขียนเกินความยาวที่ระบุไว้ในตัวสร้าง
Holstebroe

2

คุณยังไม่ได้ประกาศ returnImage เป็นตัวแปรใด ๆ :)

สิ่งนี้จะช่วยได้:

public Image byteArrayToImage(byte[] byteArrayIn)
{
    try
    {
        MemoryStream ms = new MemoryStream(byteArrayIn,0,byteArrayIn.Length);
        ms.Write(byteArrayIn, 0, byteArrayIn.Length);
        Image returnImage = Image.FromStream(ms,true);
    }
    catch { }
    return returnImage;
}

1
โค้ดมีประโยชน์เป็นไอเดีย แต่แน่นอนว่าใช้ไม่ได้ตามที่เขียนไว้ ต้องมีการประกาศ returnImage นอกส่วน try / catch ต้องมีการสร้างอินสแตนซ์ returnImage ด้วยใน 'catch' - ฉันสร้างบิตแมปพิกเซลเดียว: var image = bitmap ใหม่ (1, 1); สตรีม MemoryStream = MemoryStream ใหม่ (); image.Save (สตรีม ImageFormat.Jpeg); กระแสตำแหน่ง = 0;
Reid

2

สิ่งนี้ได้รับแรงบันดาลใจจากคำตอบของ Holstebroe รวมถึงความคิดเห็นที่นี่: รับวัตถุรูปภาพจากอาร์เรย์ไบต์

Bitmap newBitmap;
using (MemoryStream memoryStream = new MemoryStream(byteArrayIn))
    using (Image newImage = Image.FromStream(memoryStream))
        newBitmap = new Bitmap(newImage);
return newBitmap;



0

เวลาส่วนใหญ่ที่เกิดเหตุการณ์นี้เป็นข้อมูลที่ไม่ถูกต้องในคอลัมน์ SQL นี่เป็นวิธีที่เหมาะสมในการแทรกลงในคอลัมน์รูปภาพ:

INSERT INTO [TableX] (ImgColumn) VALUES (
(SELECT * FROM OPENROWSET(BULK N'C:\....\Picture 010.png', SINGLE_BLOB) as tempimg))

คนส่วนใหญ่ทำไม่ถูกต้องด้วยวิธีนี้:

INSERT INTO [TableX] (ImgColumn) VALUES ('C:\....\Picture 010.png'))

0

ติดตั้งแพ็คเกจนี้ก่อน:

ติดตั้งแพ็กเกจ SixLabors ImageSharp -Version 1.0.0-beta0007

[SixLabors.ImageSharp] [1] [1]: https://www.nuget.org/packages/SixLabors.ImageSharp

จากนั้นใช้โค้ดด้านล่างสำหรับ Cast Byte Array To Image:

Image<Rgba32> image = Image.Load(byteArray); 

สำหรับรับ ImageFormat ใช้รหัสด้านล่าง:

IImageFormat format = Image.DetectFormat(byteArray);

สำหรับภาพกลายพันธุ์ให้ใช้รหัสด้านล่าง:

image.Mutate(x => x.Resize(new Size(1280, 960)));
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.