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


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 имеют метод удаления, убедитесь, что вы удаляете их, поскольку это может вызвать MemoryLeaks.
abc123

3
@ abc123: вам не нужно выбрасывать MemoryStream; это полностью управляемый ресурс, если вы не используете его для удаленного взаимодействия. В обоих этих случаях было бы неуместно избавляться от ресурса.
Джон Скит,

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

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

2
какое-нибудь окончательное решение с полным исходным кодом?
Kiquenet

Ответы:


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 Понятно. И процедура состоит в том, чтобы поместить любую функцию, которую я хочу выполнить, в using () {}?
Халил Халаф

2
@FirstStep Не совсем так. Точнее: если вы используете объект, в котором реализован IDisposable, вы должны обязательно вызвать Dispose (), когда закончите с ним, чтобы он очистил все ресурсы, которые он связал. Оператор using () {} просто вызывает его, когда объект выходит за рамки этого оператора. Таким образом, вы можете сделать myObject.Dispose()или using(myObject){}- оба делают одно и то же, но оператор using в основном создает область видимости, которая очищает вас.
Sinaesthetic

14

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

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

Невозможно привести объект типа System.Byte [] к типу System.Drawing.Image.
user123

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

Это для файлов в байты, но OP хотел, чтобы объект рисования был преобразован в байты. Объекты чертежа могут храниться в базах данных, не обязательно в файловой системе, в виде массива байтов, и поэтому должны преобразовываться взад и вперед ... но не как файлы в 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.