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

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

Я был ошеломлен тем, что.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 поставляется с образцом кода на C++, который может отображать изображение со всех четырех камер одновременно с полной частотой кадров (17 кадров в секунду). Конечно, он никогда не имеет дело с Bitmap, поскольку GDI обращается к массивам необработанных значений пикселей.

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

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

12
задан Community 23 May 2017 в 12:02
поделиться