Байтовий масив для перетворення зображення


101

Я хочу перетворити байтовий масив у зображення.

Це мій код бази даних, звідки я отримую байтовий масив:

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

Відповіді:


110

Ви записуєте у свій потік пам'яті двічі, також ви не розпоряджаєтесь потоком після використання. Ви також просите декодер зображення застосувати вбудовану корекцію кольору.

Спробуйте це замість цього:

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

Ви також можете явно зробити потік пам’яті непридатним для запису після ініціалізації: новий MemoryStream (byteArrayIn, false)
Holstebroe

28
Це порушує специфікацію MSDN для Image.FromStream (), де написано "Ви повинні тримати потік відкритим протягом життя". Дивіться також stackoverflow.com/questions/3290060 / ...
RenniePet

81

Можливо, мені чогось не вистачає, але для мене цей однолінійний файл відмінно працює з байтовим масивом, який містить зображення файлу JPEG.

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

Редагувати:

Дивіться тут оновлену версію цієї відповіді: Як перетворити зображення в байтовий масив


AAAh дякую, нарешті гарна відповідь. Чому так багато відповідей з потоком пам'яті, це викликає у мене стільки проблем. дуже дякую !
Julian50

Хороша відповідь! це може використовуватися в окрему функцію, тоді як усі інші пропозиції, що використовують MemoryStream, не можуть (Потік повинен залишатися відкритим протягом усього часу зображення)
A_L

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

Хоча це зазвичай не є хорошою ідеєю, я поставив GC.Collect () перед потоком пам'яті. У мене не вистачало пам’яті, коли я попередньо завантажував у пам’ять цілу велику кількість графічних файлів у вигляді байтарів, а потім перетворював їх на зображення під час перегляду.
Кайот

9

Я хотів би зазначити, що у вирішенні, який надає @ 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 градієнтне зображення 101х101:

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значенням, буде створено дійсне зображення:

кроки, враховані

Крок також може бути негативним:

Якщо крок позитивний, растрова карта зверху вниз. Якщо крок негативний, растрова карта знизу вгору.

Але я не працював з такими образами, і це поза моїми відомостями.

Відповідна відповідь: C # - буфер RGB від 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] - синій тощо.
брк

1
ДЯКУЙТЕ за те, що додали всі необхідні "використання" s. Більшість людей це забувають, і їх боляче знайти.
Кейсі Крукстон

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

спробуйте (ОНОВЛЕНО)

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 повинен бути оголошений поза розділом спробувати. Також returnImage має бути створений у "catch" - я створюю растрову піксель з єдиним пікселем: var image = new Bitmap (1, 1); MemoryStream stream = новий MemoryStream (); image.Save (потік, ImageFormat.Jpeg); stream.Position = 0;
Рейд



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 для зображення:

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

Для отримання ImageFormat використовуйте нижче код:

IImageFormat format = Image.DetectFormat(byteArray);

Для вимкнення зображення використовуйте нижче коду:

image.Mutate(x => x.Resize(new Size(1280, 960)));
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.