Larik byte ke konversi gambar

101

Saya ingin mengubah array byte menjadi gambar.

Ini adalah kode database saya dari mana saya mendapatkan array 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;
}

Kode konversi saya:

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

Ketika saya mencapai baris dengan komentar, pengecualian berikut terjadi: Parameter is not valid.

Bagaimana cara memperbaiki apa pun yang menyebabkan pengecualian ini?

Tanzeel ur Rahman
sumber
Sudahkah Anda memeriksa bahwa byte gambar dalam kueri Anda valid? Anda bisa melakukan File.WriteAllBytes ("myimage.jpg", byteArrayIn) untuk memverifikasi.
Holstebroe

Jawaban:

110

Anda menulis ke aliran memori Anda dua kali, juga Anda tidak akan membuang aliran setelah digunakan. Anda juga meminta dekoder gambar untuk menerapkan koreksi warna tersemat.

Coba ini sebagai gantinya:

using (var ms = new MemoryStream(byteArrayIn))
{
    return Image.FromStream(ms);
}
Holstebroe
sumber
Anda juga dapat secara eksplisit membuat aliran memori tidak dapat ditulis setelah inisialisasi: MemoryStream baru (byteArrayIn, false)
Holstebroe
28
Hal ini melanggar spesifikasi dalam MSDN untuk Image.FromStream (), yang menyatakan "Anda harus tetap membuka streaming selama Image tersebut seumur hidup." Lihat juga stackoverflow.com/questions/3290060/…
RenniePet
81

Mungkin saya melewatkan sesuatu, tetapi bagi saya baris satu ini berfungsi dengan baik dengan array byte yang berisi gambar file JPEG.

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

EDIT:

Lihat di sini untuk versi terbaru dari jawaban ini: Bagaimana mengkonversi gambar dalam array byte

RenniePet
sumber
AAAh terima kasih, Akhirnya jawaban yang bagus. Mengapa begitu banyak jawaban dengan aliran memori, itu menyebabkan banyak masalah bagi saya. Terima kasih banyak !
Julian50
Jawaban yang bagus! ini dapat digunakan dalam fungsi terpisah sedangkan semua proposal lain yang menggunakan MemoryStream tidak dapat (Aliran harus tetap terbuka untuk seumur hidup Gambar)
A_L
20
public Image byteArrayToImage(byte[] bytesArr)
{
    using (MemoryStream memstr = new MemoryStream(bytesArr))
    {
        Image img = Image.FromStream(memstr);
        return img;
    }
}
Ali Gh
sumber
Meskipun biasanya bukan ide yang bagus, saya meletakkan GC.Collect () sebelum aliran Memori. Saya kehabisan memori ketika saya melakukan pramuat banyak file grafik besar sebagai bytearrays ke dalam memori dan kemudian mengubahnya menjadi gambar selama menonton.
Kayot
9

Saya ingin mencatat bahwa ada bug dalam solusi yang disediakan oleh @ isaias-b.

Solusi tersebut mengasumsikan bahwa stridesama dengan panjang baris. Tapi itu tidak selalu benar. Karena penyelarasan memori yang dilakukan oleh GDI, langkah bisa lebih besar dari panjang baris. Ini harus diperhitungkan. Jika tidak, gambar bergeser yang tidak valid akan dihasilkan. Byte pengisi di setiap baris akan diabaikan.

Langkahnya adalah lebar satu baris piksel (garis pindai), dibulatkan ke batas empat byte.

Kode tetap:

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

Untuk mengilustrasikan apa yang bisa ditimbulkannya, mari buat PixelFormat.Format24bppRgbgambar gradien 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);
}

Jika kita akan menyalin seluruh array apa adanya ke alamat yang ditunjuk bmpData.Scan0, kita akan mendapatkan gambar berikut. Gambar bergeser karena bagian gambar ditulis ke padding byte, yang diabaikan. Juga itulah mengapa baris terakhir tidak lengkap:

langkah diabaikan

Tetapi jika kita akan menyalin pointer tujuan bergeser baris demi baris dengan bmpData.Stridenilai, gambar yang valid akan dihasilkan:

langkah diperhitungkan

Langkah juga bisa negatif:

Jika langkahnya positif, bitmapnya adalah top-down. Jika langkahnya negatif, bitmapnya adalah bottom-up.

Tetapi saya tidak bekerja dengan gambar seperti itu dan ini di luar catatan saya.

Jawaban terkait: C # - Buffer RGB dari Bitmap berbeda dari C ++

lorond
sumber
6

Semua jawaban yang disajikan mengasumsikan bahwa array byte berisi data dalam representasi format file yang dikenal, seperti: gif, png atau jpg. Tetapi saya baru-baru ini mengalami masalah saat mencoba mengubah byte[]s, yang berisi informasi BGRA yang dilinearisasi, secara efisien menjadi Imageobjek. Kode berikut menyelesaikannya menggunakan Bitmapobjek.

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

Ini adalah sedikit variasi dari solusi yang telah diposting di situs ini .

isaias-b
sumber
sebagai referensi MSDN: Bitmap (Int32, Int32): "Konstruktor ini membuat Bitmap dengan nilai enumerasi PixelFormat dari Format32bppArgb.", yang berarti byte [0] berwarna biru, byte [1] hijau, byte [2] berwarna merah , byte [3] adalah alpha, byte [4] berwarna biru, dan seterusnya.
brk
1
TERIMA KASIH telah menambahkan semua "menggunakan" yang dibutuhkan. Kebanyakan orang melupakan itu dan sangat menyebalkan untuk menemukan mereka semua.
Casey Crookston
6

Ada pendekatan sederhana seperti di bawah ini, Anda dapat menggunakan FromStreammetode gambar untuk melakukan triknya, Ingatlah untuk menggunakan 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;
}
Ali
sumber
5

coba (UPDATE)

MemoryStream ms = new MemoryStream(byteArrayIn,0,byteArrayIn.Length);
ms.Position = 0; // this is important
returnImage = Image.FromStream(ms,true);
Yahia
sumber
2
Tidak masuk akal untuk menulis array byte ke aliran memori setelah diinisialisasi dengan array byte yang sama. Sebenarnya saya tidak yakin MemoryStream memungkinkan penulisan melebihi panjang yang ditentukan dalam konstruktor.
Holstebroe
2

Anda belum mendeklarasikan returnImage sebagai variabel apa pun :)

Ini akan membantu:

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;
}
LeeCambl
sumber
1
Kode berguna sebagai ide, tetapi tentu saja tidak akan berfungsi seperti yang tertulis. returnImage harus dideklarasikan di luar bagian coba / tangkap. Juga returnImage harus dibuat di 'catch' - Saya membuat bitmap piksel tunggal: var image = new Bitmap (1, 1); Arus MemoryStream = MemoryStream baru (); image.Save (stream, ImageFormat.Jpeg); aliran.Posisi = 0;
Reid
2

Ini terinspirasi oleh jawaban Holstebroe, ditambah komentar di sini: Mendapatkan objek Gambar dari array byte

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

Satu liner:

Image bmp = (Bitmap)((new ImageConverter()).ConvertFrom(imageBytes));
Arvin Amir
sumber
0

Sebagian besar waktu ketika ini terjadi itu adalah data yang buruk di kolom SQL. Ini adalah cara yang tepat untuk memasukkan ke dalam kolom gambar:

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

Kebanyakan orang salah melakukannya dengan cara ini:

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

Instal Pertama Paket Ini:

Instal-Paket SixLabors.ImageSharp -Version 1.0.0-beta0007

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

Kemudian gunakan Kode Di Bawah Ini Untuk Cast Byte Array To Image:

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

Untuk Mendapatkan ImageFormat Gunakan Kode Di Bawah Ini:

IImageFormat format = Image.DetectFormat(byteArray);

Untuk Mutasi Gambar Gunakan Kode Di Bawah Ini:

image.Mutate(x => x.Resize(new Size(1280, 960)));
AminGolmahalle
sumber