Подтвердить что ты не робот

Как преобразовать массив байтов в экземпляр Bitmap (.NET)?

Я работаю с камерами (в данном случае монохромными) в VB.NET. API камер находится на С++, и производитель предоставляет обертки. Как типично в этих случаях, изображение возвращается как массив Byte. В моем конкретном случае это 8 бит на пиксель, поэтому нет необходимости в бинарнике для отмены.

Я был flabbergasted, что .NET так мало поддерживает этот тип вещей. Поиск в Интернете я нашел различные темы, но это не помогает, что во многих случаях у людей есть массив байтов, соответствующий данным изображения (то есть, считывание файла изображения в массив байтов, а затем .NET интерпретирует массив как файл, с заголовком и всем).

В этом случае мне нужно преобразовать массив значений исходного пикселя в, в частности, что-то, что я могу отобразить на экране.

Кажется, что нет встроенного способа сделать это, потому что формат 8bpp, встроенный в .NET, индексируется (требуется палитра), но не кажется, что настройка палитры поддерживается. Опять же, это меня действительно удивило. Это является наиболее перспективной темой, которую я нашел.

Таким образом, единственный способ, которым я нашел это, - создать Bitmap, а затем скопировать значения пикселей по пикселям. В моем случае изображение с 8-мегапиксельной камерой заняло несколько секунд на самом быстром i7 при использовании SetPixel.

Альтернативой, которую я нашел для использования SetPixel, является LockBits, который затем дает вам доступ к (неуправляемому) массиву байтов, который составляет изображение. Это кажется расточительным, потому что я должен манипулировать массивом в .NET, а затем скопировать его обратно в неуправляемое пространство. Я уже копирую исходные данные изображения один раз (поэтому оригинал может быть повторно использован или отброшен остальной частью того, что происходит), поэтому, хотя значительно быстрее, чем при использовании SetPixel, это все еще кажется расточительным.

Вот код VB, который у меня есть:

Public Function GetBitmap(ByRef TheBitmap As System.Drawing.Bitmap) As Integer
    Dim ImageData() As Byte
    Dim TheWidth, TheHeight As Integer
    'I've omitted the code here (too specific for this question) which allocates
    'ImageData and then uses Array.Copy to store a copy of the pixel values
    'in it. It also assigns the proper values to TheWidth and TheHeight.

    TheBitmap = New System.Drawing.Bitmap(TheWidth, TheHeight, System.Drawing.Imaging.PixelFormat.Format24bppRgb)
    Dim TheData As System.Drawing.Imaging.BitmapData = TheBitmap.LockBits(
        New System.Drawing.Rectangle(0, 0, TheWidth, TheHeight), Drawing.Imaging.ImageLockMode.ReadWrite, TheBitmap.PixelFormat)
    Dim Image24(Math.Abs(TheData.Stride) * TheHeight) As Byte
    'Then we have two choices: to do it in parallel or not. I tried both; the sequential version is commented out below.
    System.Threading.Tasks.Parallel.For(0, ImageData.Length, Sub(i)
                                                                 Image24(i * 3 + 0) = ImageData(i)
                                                                 Image24(i * 3 + 1) = ImageData(i)
                                                                 Image24(i * 3 + 2) = ImageData(i)
                                                             End Sub)
    'Dim i As Integer
    'For i = 0 To ImageData.Length - 1
    '    Image24(i * 3 + 0) = ImageData(i)
    '    Image24(i * 3 + 1) = ImageData(i)
    '    Image24(i * 3 + 2) = ImageData(i)
    'Next
    System.Runtime.InteropServices.Marshal.Copy(Image24, 0, TheData.Scan0, Image24.Length)
    TheBitmap.UnlockBits(TheData)

    'The above based on
    'http://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.aspx

    Return 1

End Function

Для 8-мегапиксельного изображения последовательная версия потребляет около 220 миллисекунд от создания TheBitmap до (и включая) вызова до TheBitmap.UnlockBits(). Параллельная версия гораздо более противоречива, но колеблется от 60 до 80 миллисекунд. Учитывая, что я одновременно приобретаю четыре камеры и потоки на диск с большим количеством времени, это довольно разочаровывает. API поставляется с образцом кода на С++, который может отображаться со всех четырех камер одновременно с полной частотой кадров (17 кадров в секунду). Конечно, он никогда не имеет дело с Bitmap, поскольку GDI обращается к массивам исходных значений пикселей.

В заключение я должен пройти через управляемый/неуправляемый барьер просто потому, что нет прямого способа взять массив значений пикселей и "заполнить" его в экземпляр Bitmap. В этом случае я также копирую значения пикселей в 24bpp Bitmap, потому что я не могу "установить" палитру в индексированном изображении, поэтому 8bpp не является жизнеспособным форматом для отображения.

Нет ли лучшего способа?

4b9b3361

Ответ 1

Создайте допустимый заголовок растрового изображения и предоставьте его в массив байтов. Вам нужно будет вручную создать 56-128 байт данных.

Во-вторых, создайте поток из массива байтов.

Далее создать образ или растровый класс с помощью Image.FromStream()/Bitmap.FromStream()

Я бы не подумал, что в .NET есть какие-то необработанные функции обработки изображений. Существует так много необходимой информации для изображения, это был бы чрезвычайно длинный список параметров для метода для приема необработанных байтов и создания воображения (это растровое изображение, jpeg, gif, png и т.д. И все дополнительные данные, каждый из которых для этого требуется создать правильный образ), это не имеет смысла.

Ответ 2

Попробуйте это

var img = new Bitmap(new MemoryStream(yourByteArray));

Ответ 3

Кажется, что все ваши камеры одного типа, поэтому данные изображений. Я не знаю, возможно ли это в вашем случае, но вы можете создать это "полное растровое изображение" из любого изображения, а затем просто использовать заголовок в качестве шаблона (поскольку он будет таким же для всех изображений).