Перетворити растровий файл в байтовий масив


233

Використовуючи C #, чи є кращий спосіб перетворити Windows Bitmapна файл, byte[]ніж збереження у тимчасовий файл та зчитування результату за допомогою FileStream?

Відповіді:


418

Є кілька способів.

ImageConverter

public static byte[] ImageToByte(Image img)
{
    ImageConverter converter = new ImageConverter();
    return (byte[])converter.ConvertTo(img, typeof(byte[]));
}

Цей варіант зручний тим, що для нього не потрібно багато коду.

Потік пам'яті

public static byte[] ImageToByte2(Image img)
{
    using (var stream = new MemoryStream())
    {
        img.Save(stream, System.Drawing.Imaging.ImageFormat.Png);
        return stream.ToArray();
    }
}

Цей еквівалент тому, що ви робите, за винятком того, що файл зберігається в пам'яті замість диска. Хоча більше коду, ви маєте можливість ImageFormat, і його можна легко змінити між збереженням на пам'яті або диску.

Джерело: http://www.vcskicks.com/image-to-byte.php


2
Ви також можете використовувати bitmap.Lockbits та Marshal.Copy для простої швидкої копії без проміжних
потоків

4
Будь ласка, майте на увазі, що ImageConverterметод збереже зображення у форматі Png, що призведе до ВЕЛИЧЕЗНИХ файлів.
skolima

8
вам не потрібно закривати потік при використанні "використання"
LastTribunal

2
Хтось може дати мені трохи більше інформації про структурування цього масиву? Починається це з x = 0 і проходить через кожне y, а потім збільшується x? А потім зберігати його так: [0,0,1,1,1,1,0,0,1,1]?
Реймонд Тіммерманс

1
ImageConverterне є .net стандартом, який ви можете використовуватиMemoryStream
Олександр

95

Для цього може допомогти MemoryStream . Ви можете вкласти його в метод розширення:

public static class ImageExtensions
{
    public static byte[] ToByteArray(this Image image, ImageFormat format)
    {
        using(MemoryStream ms = new MemoryStream())
        {
            image.Save(ms, format);
            return ms.ToArray();
        }
    }
}

Ви можете просто використовувати його так:

var image = new Bitmap(10, 10);
// Draw your image
byte[] arr = image.ToByteArray(ImageFormat.Bmp);

Я частково не погоджуюся з відповіддю престоманіфто щодо ImageConverter. Не використовуйте ImageConverter. З цим нічого погано технічно, але просто той факт, що він використовує бокс / розблокування від об'єкта, говорить про те, що це код зі старих темних місць рамки .NET і його не ідеально використовувати для обробки зображень (це надмірність для перетворення в байт [] принаймні), особливо якщо врахувати наступне.

Я поглянув на ImageConverterкод, який використовується .Net Framework, і внутрішньо він використовує код, майже ідентичний тому, який я подав вище. Він створює новий MemoryStream, зберігає Bitmapв будь-якому форматі він був, коли ви його надали, і повертає масив. Пропустіть додаткові накладні витрати на створення ImageConverterкласу за допомогоюMemoryStream


Прекрасна. Це зробимо! Я вважаю, що ви хочете утилізувати MemoryStream, хоча - хочете оновити?
Джеремі Макгі

Я оновив свою відповідь деякою дискусією про те, чому б не використовувати ImageConverter, як підказує обрана відповідь, а також додавання утилізації.
Крістофер Керренс

Гарне використання методу розширення, мені це подобається!
Чувак Паскалу

3
+1 за перегляд ImageConverter та звітування про результати свого дослідження. Але я не думаю, що те, що ви виявили, вимагає твердження "Не використовуйте ImageConverter". Він безумовно надає корисні послуги в іншому напрямку - від байтового масиву до зображення, наприклад, встановлення роздільної здатності зображення (dpi). І "зайві витрати на створення класу ImageConverter", мабуть, незначні, і їх потрібно зробити лише один раз, незалежно від того, скільки разів ви ним користуєтеся.
RenniePet

1
ImageConverter виявився корисним і в тому, що він може визначати тип зображення автоматично - наприклад, якщо у вас є байтний масив зображення, але не знаєте формату - ви можете спробувати прочитати заголовки та отримати підказки звідти, але, ну, ... використовуючи (Image img = (Зображення) myImgConverter.ConvertFrom (byteImage)), а потім перевірити img.PixelFormat і т.д. просто простіше .. - звичайно залежно від того, що ви хочете зробити
hello_earth

45

Ви також можете просто маршал. Скопіюйте дані растрових зображень. Немає посередницької пам’яті тощо та швидкої копії пам’яті. Це має працювати як на 24-бітній, так і на 32-бітній растрових картах.

public static byte[] BitmapToByteArray(Bitmap bitmap)
{

    BitmapData bmpdata = null;

    try
    {
        bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat);
        int numbytes = bmpdata.Stride * bitmap.Height;
        byte[] bytedata = new byte[numbytes];
        IntPtr ptr = bmpdata.Scan0;

        Marshal.Copy(ptr, bytedata, 0, numbytes);

        return bytedata;
    }
    finally
    {
        if (bmpdata != null)
            bitmap.UnlockBits(bmpdata);
    }

}

.


1
Привіт, я використовував цей метод, але я не можу знову перетворити цей байтовий масив у зображення. Bitmap bitmap1 = новий Bitmap (новий MemoryStream (масив)); він викидає виняток недійсних параметрів. Чи є якась помилка?
Говард

1
Привіт, розділ коментарів для мене занадто малий, щоб розміщувати повний чистий код. Найпростіший спосіб - прикріпити масив: GCHandle handle = GCHandle.Alloc (bufferarray, GCHandleType.Pinned); отримати покажчик на масив IntPtr iptr = Marshal.UnsafeAddrOfPinnedArrayElement (bufferarray, 0); потім створіть нову растрову карту за допомогою IntPtr: bitmap = new Bitmap (ширина, висота, (ширина * 4), PixelFormat.Format32bppArgb, iptr); але вам доведеться використовувати відповідні параметри створення растрових зображень для вашого формату растрових зображень. Обов’язково очистіть: iptr = IntPtr.Zero; ручка.Безкоштовна ();
deegee

3
Що таке помилка? Багато програмістів, включаючи мене, використовують цей стиль коду, і він прекрасно працює.
deegee

4
@ mini-me - рекомендую шукати крок бітової карти проти ширини растрових зображень. Це не помилка в моєму коді, ось як Windows обробляє растрові карти. Stride може містити додаткові дані в кінці кожного рядка (ширини) для того, щоб кожен рядок закінчувався і починався на 32-бітній межі для вирівнювання пам'яті та продуктивності.
deegee

3
Це не помилка. msdn.microsoft.com/en-us/library/…
deegee

16

Збережіть зображення в MemoryStream, а потім захопіть байтовий масив.

http://msdn.microsoft.com/en-us/library/ms142148.aspx

  Byte[] data;

  using (var memoryStream = new MemoryStream())
  {
    image.Save(memoryStream, ImageFormat.Bmp);

    data = memoryStream.ToArray();
  }

Існує метод збереження на зображенні.
Прескотт Шартьє

@PrescottChartier Вищенаведений приклад передбачає, що ви працюєте з типу, похідного від System.Drawing.Image (див .: docs.microsoft.com/en-us/dotnet/api/… )
Кріс Бакстер

Так, і я отримую System.Drawing.Image does not exist. Отже .. ні, не працює :(
Прескотт Шартьє

Ви можете побачити, що я намагаюся зробити тут: stackoverflow.com/questions/55084620/…
Prescott Chartier

10

Використовуйте MemoryStreamзамість FileStream, наприклад:

MemoryStream ms = new MemoryStream();
bmp.Save (ms, ImageFormat.Jpeg);
byte[] bmpBytes = ms.ToArray();

Ви, мабуть, хочете ToArray, ні GetBuffer.
vcsjones

GetBuffer повертає масив непідписаних байтів (байтовий масив)
Jeff Reddy,

4
Він може мати дані наповнювача, які не є частиною зображення. З Документів: Note that the buffer contains allocated bytes which might be unused. For example, if the string "test" is written into the MemoryStream object, the length of the buffer returned from GetBuffer is 256, not 4, with 252 bytes unused. To obtain only the data in the buffer, use the ToArray method.Тепер байтовий масив від GetBufferповерне зображення плюс невикористані байти, що, ймовірно, призведе до пошкодження зображення.
vcsjones

1
Добре знати. Дякуємо за коментар vcs!
Джефф Редді

Такий підхід зберігає зображення як jpeg із налаштуваннями стиснення за замовчуванням, що введе артефакти стиснення, які можуть помітно погіршити ваше зображення.
Річард Єв

8

Спробуйте наступне:

MemoryStream stream = new MemoryStream();
Bitmap bitmap = new Bitmap();
bitmap.Save(stream, ImageFormat.Jpeg);

byte[] byteArray = stream.GetBuffer();

Переконайтеся, що ви використовуєте:

System.Drawing & using System.Drawing.Imaging;

1
Такий підхід зберігає зображення як jpeg із налаштуваннями стиснення за замовчуванням, що введе артефакти стиснення, які можуть помітно погіршити ваше зображення.
Річард Єв

Знову ж, жоден метод збереження на растровій карті.
Прескотт Шартьє

7

Я вірю, що ви можете просто зробити:

ImageConverter converter = new ImageConverter();
var bytes = (byte[])converter.ConvertTo(img, typeof(byte[]));

6
MemoryStream ms = new MemoryStream();
yourBitmap.Save(ms, ImageFormat.Bmp);
byte[] bitmapData = ms.ToArray();


-3

Дуже просто використовувати це лише в одному рядку:

byte[] imgdata = File.ReadAllBytes(@"C:\download.png");

op сказав, що не зацікавлений у цьому варіанті
Мауро Сампьетро
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.