Преобразовать растровое изображение в байтовый массив

233

Используя C #, есть ли лучший способ преобразовать Windows Bitmapв a, byte[]чем сохранить во временный файл и прочитать результат, используя FileStream?

Джереми МакГи
источник

Ответы:

419

Есть несколько способов.

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

prestomanifesto
источник
2
Вы также можете использовать bitmap.Lockbits и Marshal.Copy для простого быстрого копирования без промежуточных
потоков
4
Помните, что ImageConverterметод сохранит изображение в формате Png, что приведет к огромным файлам.
Сколима
8
вам не нужно закрывать поток при использовании 'using'
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);

Я частично не согласен с ответом Prestomanifto относительно ImageConverter. Не используйте ImageConverter. В этом нет ничего технически неправильного, но просто тот факт, что он использует упаковку / распаковку из объекта, говорит мне, что это код из старых темных мест .NET Framework, и он не идеален для использования с обработкой изображений (это избыточно для преобразования в байт [] по крайней мере), особенно если учесть следующее.

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

Кристофер Токов
источник
Прекрасный. Это сработает! Насколько я понимаю, вы захотите избавиться от MemoryStream, хотя - хотите обновить?
Джереми МакГи
Я обновил свой ответ, обсудив, почему не следует использовать ImageConverter, как предлагает выбранный вами ответ, а также добавить утилизацию.
Кристофер Уркс
Хорошее использование метода расширения, мне это нравится!
Чувак Паскало
3
+1 за просмотр ImageConverter и сообщение о результатах вашего исследования. Но я не думаю, что то, что вы обнаружили, оправдывает утверждение «Не используйте ImageConverter». Он определенно предоставляет полезные сервисы, идущие в другую сторону, от байтового массива до изображения, например, установка разрешения изображения (dpi). А «дополнительные затраты на создание класса ImageConverter», по-видимому, незначительны, и их нужно выполнять только один раз, независимо от того, сколько раз вы его используете.
RenniePet
1
Я нашел ImageConverter полезным также и в том, что он может автоматически определять тип изображения - например, если у вас есть байтовый массив изображения, но вы не знаете формат - вы можете попробовать прочитать заголовки и получить оттуда подсказки, но, ну ... использовать (Image img = (Image) myImgConverter.ConvertFrom (byteImage)) и затем проверять img.PixelFormat и т. д. просто проще ... - конечно, в зависимости от того, что вы хотите сделать
hello_earth
46

Вы также можете просто Marshal.Copy данных растрового изображения. Нет промежуточного потока памяти и т. Д. И быстрая копия памяти. Это должно работать как для 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);
    }

}

,

deegee
источник
1
Привет, я использовал этот метод, но я не могу снова преобразовать этот байтовый массив обратно в изображение. Bitmap bitmap1 = новый точечный рисунок (новый 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; handle.Free ();
Диджи
3
В чем ошибка? Многие программисты, включая меня, используют этот стиль кода, и он отлично работает.
Диджеи
4
@ mini-me - я рекомендую посмотреть, что такое шаг растрового изображения по сравнению с шириной растрового изображения. Это не ошибка в моем коде, это то, как Windows обрабатывает растровые изображения. Stride может включать в себя дополнительные данные в конце каждой строки (ширины), чтобы каждый конец строки начинался на 32-битной границе для выравнивания памяти и производительности.
Диджеи
3
Это не ошибка. msdn.microsoft.com/en-us/library/…
Диджея
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();
  }
Крис Бакстер
источник
На изображении нет метода Save.
Прескотт Шартье
@PrescottChartier В приведенном выше примере предполагается, что вы работаете с типом, производным от System.Drawing.Image (см .: docs.microsoft.com/en-us/dotnet/api/… )
Крис Бакстер,
Да, и я получаю System.Drawing.Image does not exist. Так что .. нет, не работает :(
Прескотт Шартье
Вы можете увидеть, что я пытаюсь сделать здесь: stackoverflow.com/questions/55084620/…
Прескотт Шартье
10

Используйте MemoryStreamвместо a FileStream, вот так:

MemoryStream ms = new MemoryStream();
bmp.Save (ms, ImageFormat.Jpeg);
byte[] bmpBytes = ms.ToArray();
Джефф Редди
источник
Вы, вероятно, хотите ToArray, нет GetBuffer.
vcsjones
GetBuffer возвращает массив байтов без знака (байтовый массив)
Джефф Редди
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.Итак, теперь массив байтов from 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[]));
Kevek
источник
6
MemoryStream ms = new MemoryStream();
yourBitmap.Save(ms, ImageFormat.Bmp);
byte[] bitmapData = ms.ToArray();
62071072SP
источник
5

Более простой:

return (byte[])System.ComponentModel.TypeDescriptor.GetConverter(pImagen).ConvertTo(pImagen, typeof(byte[]))
Моизес Конехо
источник
-4

Очень просто использовать это только в одной строке:

byte[] imgdata = File.ReadAllBytes(@"C:\download.png");
Каран
источник
оп сказал, что он не заинтересован в этом варианте
Мауро Сампьетро