Найшвидший спосіб перетворення зображення в масив байтів


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 мають спосіб утилізації, переконайтесь, що ви їх утилізуєте, оскільки це може спричинити пам'ять пам'яті.
abc123

3
@ Абв123: Вам не потрібно утилізувати MemoryStream; це повністю керований ресурс, якщо ви не використовуєте його для видалення. В обох цих випадках було б недоцільно розпоряджатися ресурсом.
Джон Скіт

1
@JonSkeet цікаво, ти зробив орієнтир на цьому? щоб побачити швидкість, з якою .net звільняє об'єкт? Я знаю, що існує аналогічний аргумент для DataTable, але все ж є помітна різниця у швидкості, з якою GarbageCollector збирає пам'ять, виділену при використанні розпорядження.
abc123

@ abc123: Я дійсно не сподівався би на те, що таке буде - розміщення потоку нічого не робить для масиву, а MemoryStream не має фіналізатора (на відміну від DataTable, який успадковує один від MarshalByValueComponent).
Джон Скіт

2
будь-яке остаточне рішення з повним вихідним кодом?
Кікенет

Відповіді:


39

То чи є ще якийсь метод для досягнення цієї мети?

Ні. Для перетворення зображення в байтовий масив необхідно вказати формат зображення - так само, як ви повинні вказати кодування при перетворенні тексту в байтовий масив.

Якщо ви переживаєте за артефакти стиснення, виберіть формат без втрат. Якщо ви переживаєте за ресурси процесора, виберіть формат, який не турбує стискання - наприклад, необроблені пікселі ARGB. Але це, звичайно, призведе до збільшення масиву байтів.

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


12
замість "вибору формату без втрат" ви можете вибрати, imageIn.RawFormatякі намагаються зберегти байти необробленого зображення без подальшого повторного кодування.
Кріс Ф Керролл

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, або загортати тіло цього методу в оператор using () {}
Neil.Allen

@ Neil.Allen Я новачок тут, скажіть, будь ласка, чому?
Халил Халаф

3
@FirstStep Тому що прибирай за собою :)
Sinaesthetic

@Sinaesthetic я бачу. І звичайним є введення будь-якої функції, яку я хочу виконати, у користуванні () {}?
Халил Халаф

2
@FirstStep Не зовсім. Точніше: якщо ви використовуєте об'єкт, який реалізував IDisposable, вам слід обов’язково зателефонувати Dispose (), коли ви закінчите з ним, щоб він очистив усі пов'язані з ним ресурси. Оператор using () {} просто викликає його, коли об'єкт виходить за межі цього оператора. Таким чином, ви можете зробити myObject.Dispose()або using(myObject){}- і те й інше зробити те ж саме, але використання оператора в основному створює область, яка очистить для вас.
Сінестетик

14

Я не впевнений, якщо ви отримаєте якісь величезні вигоди з причин, які вказував Джон Скіт. Однак ви можете спробувати і порівняти метод TypeConvert.ConvertTo і побачити, як він порівнюється з використанням поточного методу.

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

Неможливо передати об’єкт типу 'System.Byte []', щоб ввести 'System.Drawing.Image'.
користувач123

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

Це для файлів у байти, але ОП захотів об'єкт малювання, перетворений у байти. Об'єкти малювання можуть зберігатися в базах даних, не обов'язково у файловій системі, як масив байтів, і тому їх потрібно трансформувати вперед і назад ..., але не як файли в FileStream, які повинні бути перетворені в байти - якщо, можливо, під час початкове завантаження.
vapcguy

Це допомогло мені, коли я шукав це зробити з файлами. Добре мати навколо себе пов'язане.
Джастін

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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.