Chuyển đổi mảng byte sang hình ảnh


100

Tôi muốn chuyển đổi một mảng byte thành một hình ảnh.

Đây là mã cơ sở dữ liệu của tôi từ nơi tôi lấy mảng byte:

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;
}

Mã chuyển đổi của tôi:

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;
}

Khi tôi đến dòng có nhận xét, ngoại lệ sau sẽ xảy ra: Parameter is not valid.

Làm cách nào để khắc phục bất cứ điều gì gây ra ngoại lệ này?


Bạn đã kiểm tra xem các byte hình ảnh trong truy vấn của mình có hợp lệ không? Bạn có thể thực hiện một File.WriteAllBytes ("myimage.jpg", byteArrayIn) để xác minh.
Holstebroe

Câu trả lời:


109

Bạn đang ghi vào luồng bộ nhớ của mình hai lần, bạn cũng không hủy luồng sau khi sử dụng. Bạn cũng đang yêu cầu bộ giải mã hình ảnh áp dụng hiệu chỉnh màu được nhúng.

Hãy thử cái này thay thế:

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

Bạn cũng có thể thực hiện một cách rõ ràng dòng ký ức không thể ghi sau khi khởi tạo: MemoryStream mới (byteArrayIn, false)
Holstebroe

28
Điều này vi phạm một thông số kỹ thuật trong MSDN cho Image.FromStream (), trong đó nó nói "Bạn phải mở luồng trong suốt thời gian tồn tại của Hình ảnh." Xem thêm stackoverflow.com/questions/3290060/…
RenniePet

81

Có thể tôi đang thiếu một cái gì đó, nhưng đối với tôi một lớp lót này hoạt động tốt với một mảng byte chứa hình ảnh của tệp JPEG.

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

BIÊN TẬP:

Xem tại đây để biết phiên bản cập nhật của câu trả lời này: Cách chuyển đổi hình ảnh trong mảng byte


AAAh cảm ơn, cuối cùng là một câu trả lời tốt. Tại sao rất nhiều câu trả lời với dòng bộ nhớ, nó gây ra cho tôi rất nhiều vấn đề. cảm ơn rất nhiều !
Julian50

Câu trả lời tốt! điều này có thể được sử dụng trong một chức năng riêng biệt trong khi tất cả các đề xuất khác sử dụng MemoryStream không thể (Luồng phải được mở trong suốt thời gian tồn tại của Hình ảnh)
A_L

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

Mặc dù đây không phải là một ý tưởng hay, nhưng tôi đặt GC.Collect () trước dòng Memory. Tôi đã hết bộ nhớ khi tải trước rất nhiều tệp đồ họa lớn dưới dạng bytearrays vào bộ nhớ và sau đó chuyển chúng thành hình ảnh trong khi xem.
Kayot

9

Tôi muốn lưu ý rằng có một lỗi trong giải pháp do @ isaias-b cung cấp.

Giải pháp đó giả sử rằng stridebằng chiều dài hàng. Nhưng nó không phải lúc nào cũng đúng. Do việc căn chỉnh bộ nhớ được thực hiện bởi GDI, sải chân có thể lớn hơn độ dài hàng. Điều này phải được tính đến. Nếu không, hình ảnh bị dịch chuyển không hợp lệ sẽ được tạo ra. Các byte đệm trong mỗi hàng sẽ bị bỏ qua.

Sải chân là chiều rộng của một hàng pixel (một dòng quét), được làm tròn đến ranh giới bốn byte.

Mã cố định:

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;
    }
}

Để minh họa những gì nó có thể dẫn đến, hãy tạo PixelFormat.Format24bppRgbhình ảnh gradient 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);
}

Nếu chúng ta sao chép toàn bộ mảng đến địa chỉ được trỏ đến bmpData.Scan0, chúng ta sẽ nhận được hình ảnh sau. Thay đổi hình ảnh vì một phần hình ảnh được ghi vào các byte đệm, điều đó đã bị bỏ qua. Ngoài ra, đó là lý do tại sao hàng cuối cùng không đầy đủ:

sải bước bị bỏ qua

Nhưng nếu chúng ta sao chép con trỏ đích dịch chuyển từng hàng theo bmpData.Stridegiá trị, ảnh hợp lệ sẽ được tạo:

sải bước có tính đến

Stride cũng có thể là tiêu cực:

Nếu sải chân là dương, bitmap là từ trên xuống. Nếu sải chân là âm, bitmap là từ dưới lên.

Nhưng tôi đã không làm việc với những hình ảnh như vậy và điều này nằm ngoài khả năng ghi nhận của tôi.

Câu trả lời liên quan: C # - Bộ đệm RGB từ Bitmap khác với C ++


6

Tất cả các câu trả lời được trình bày giả định rằng mảng byte chứa dữ liệu ở dạng biểu diễn định dạng tệp đã biết, như: gif, png hoặc jpg. Nhưng gần đây tôi đã gặp sự cố khi cố gắng chuyển đổi byte[]s, chứa thông tin BGRA tuyến tính hóa, thành Imagecác đối tượng một cách hiệu quả . Đoạn mã sau giải quyết nó bằng cách sử dụng một Bitmapđối tượng.

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;
    }
}

Đây là một biến thể nhỏ của một giải pháp đã được đăng trên trang web này .


dưới dạng tham chiếu MSDN: Bitmap (Int32, Int32): "Hàm tạo này tạo một Bitmap với giá trị liệt kê PixelFormat của Format32bppArgb.", có nghĩa là byte [0] là màu xanh lam, byte [1] là màu xanh lá cây, byte [2] là màu đỏ , byte [3] là alpha, byte [4] là màu xanh lam, v.v.
brk

1
CẢM ƠN BẠN đã thêm tất cả các "using" cần thiết. Hầu hết mọi người đều quên điều đó và thật khó để tìm ra tất cả.
Casey Crookston

6

có một cách tiếp cận đơn giản như dưới đây, bạn có thể sử dụng FromStreamphương pháp của một hình ảnh để thực hiện thủ thuật, Chỉ cần nhớ sử dụng 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

thử (CẬP NHẬT)

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

2
Sẽ rất ít khi ghi mảng byte vào luồng bộ nhớ sau khi nó đã được khởi tạo với cùng một mảng byte. Trên thực tế, tôi không chắc MemoryStream cho phép ghi vượt quá độ dài được chỉ định trong hàm tạo.
Holstebroe

2

Bạn chưa khai báo returnImage là bất kỳ loại biến nào :)

Điều này sẽ giúp:

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
Mã hữu ích như một ý tưởng, nhưng tất nhiên sẽ không hoạt động như được viết. returnImage phải được khai báo bên ngoài phần try / catch. Ngoài ra returnImage phải được khởi tạo trong 'catch' - Tôi tạo một bitmap pixel duy nhất: var image = new Bitmap (1, 1); Dòng MemoryStream = new MemoryStream (); image.Save (stream, ImageFormat.Jpeg); stream.Position = 0;
Reid

2

Điều này được lấy cảm hứng từ câu trả lời của Holstebroe, cộng với các nhận xét ở đây: Lấy một đối tượng Hình ảnh từ một mảng byte

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

1

Lót:

Image bmp = (Bitmap)((new ImageConverter()).ConvertFrom(imageBytes));

0

Hầu hết khi điều này xảy ra, đó là dữ liệu xấu trong cột SQL. Đây là cách thích hợp để chèn vào cột hình ảnh:

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

Hầu hết mọi người đều làm sai theo cách này:

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

0

Lần đầu tiên cài đặt gói này:

Gói cài đặt SixLabors.ImageSharp -Version 1.0.0-beta0007

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

Sau đó, sử dụng mã dưới đây để truyền mảng Byte sang hình ảnh:

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

Để có được ImageFormat, hãy sử dụng mã dưới đây:

IImageFormat format = Image.DetectFormat(byteArray);

Để thay đổi hình ảnh sử dụng mã bên dưới:

image.Mutate(x => x.Resize(new Size(1280, 960)));
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.