C #: Как загрузить изображение RAW (формат: rgb565) в растровое изображение? - PullRequest
0 голосов
/ 19 апреля 2019

Моя цель:
Отобразите изображение в необработанном формате rgb565 в моей программе Windows Forms. (не по теме: данные получены из модуля камеры OV7670)

Мой подход:
Сначала я создаю пустой растр. Затем я вставляю данные изображения (необработанный формат: rgb565) в раздел полезной нагрузки пустого растрового изображения. Наконец, я отображаю измененное изображение в PictureBox.

Моя проблема:
Все работает нормально, но показания отображаются с диагональными полосами вместо вертикальных полос (см. Ссылки ниже).

Исходное необработанное изображение rgb565: Исходное исходное изображение rgb565
Снимок экрана PictureBox (с диагональными полосами): Снимок экрана PictureBox

Мне удалось отобразить изображение, извлекая R, G, B и используя SetPixel (), но это слишком медленно для меня. Вот почему я хотел бы получить код для правильного отображения изображения.

Мое Свидетельство можно найти в выпадающем списке здесь: Свидетельство: Свидетельство


MemoryStream memoryStream = new MemoryStream(1000000);

    // Read raw image into byte array
    string imgpath = "rgb565_LSB-first_313x240.raw";
    FileStream fs = new FileStream(imgpath, FileMode.Open);
    fs.CopyTo(memoryStream);
    Byte[] buffer = memoryStream.ToArray();

    // Create empty Bitmep and inject byte arrays data into bitmap's data area
    Bitmap bmp = new Bitmap(313, 240, PixelFormat.Format16bppRgb565);

    // Lock the bitmap's bits.  
    Rectangle rect = new Rectangle(0, 0, 313, 240);
    BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite,
                                      PixelFormat.Format16bppRgb565);
    IntPtr ptrToFirstPixel = bmpData.Scan0;

    // Inject the rgb565 data (stored in the buffer array)
    Marshal.Copy(buffer, 0, ptrToFirstPixel, buffer.Length);
    bmp.UnlockBits(bmpData);

    // Diplay Bitmap in my PictureBox
    pbImage.Image = bmp;

Ожидаемый результат: вертикальные полосы: -)
Фактический результат: диагональные полосы :-(

1 Ответ

0 голосов
/ 21 апреля 2019

Через 10 часов ковыряясь в стоге сена, я наконец-то смог разыскать причина, которая была определенно не банальна (по крайней мере, для меня).

Вот оно: Для спецификации растрового изображения размер строки должен быть кратен 4 байтам! https://upload.wikimedia.org/wikipedia/commons/c/c4/BMPfileFormat.png

Поскольку у моего colorbar-testimage была ширина линии 313 пикселей, а каждый пиксель был закодирован как rgb565, я получил 626 байт на строку.

Но 626 не является кратным 4. Вот почему я должен был добавить еще 2 "байта заполнения" в конце каждой строки. И это было причиной моих диагональных полос.

После добавления этих 2 байтов заполнения (0x00 0x00) я получил растровое изображение, в котором заголовок говорит вам: это изображение имеет ширину 313 пикселей, но реальные данные изображения состоят из 314 пикселей на строку - это немного странно, но это определяется спецификацией.

Как только я изменил свое растровое изображение, чтобы оно соответствовало этому требованию спецификации, диагональные полосы исчезли, а ожидаемая вертикальная полоса вышла из темноты.

Поскольку в 99% всех примеров кода в Интернете предполагается, что их изображения кратны 4 ширинам линии (например, форматам изображения 320x240 или 680x480), все они не сталкиваются с моей проблемой - но большинство из них будут, если вы подадите им изображения rgb565 с нечетным количеством строк-пикселей, как я должен был сделать.

Несколько дополнительных строк (помеченных "// ***") было достаточно для добавления "трюка с отступами". (Приведенный ниже код только для пояснения, в производительном коде вы можете добавить некоторые оптимизации)

MemoryStream memoryStream = new MemoryStream(1000000);

// Read raw image into byte array
string imgpath = "rgb565_LSB-first_313x240.raw";
FileStream fs = new FileStream(imgpath, FileMode.Open);
fs.CopyTo(memoryStream);
Byte[] buffer = memoryStream.ToArray();

// Create empty Bitmep and inject byte arrays data into bitmap's data area
Bitmap bmp = new Bitmap(313, 240, PixelFormat.Format16bppRgb565);
// Lock the bitmap's bits.  
Rectangle rect = new Rectangle(0, 0, 313, 240);
BitmapData bmpData = bmp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format16bppRgb565);
IntPtr ptrToFirstPixel = bmpData.Scan0;


// *** Attention:  Bitmap specification requires, to pad row size to a multiple of 4 Bytes 
// *** See:        https://upload.wikimedia.org/wikipedia/commons/c/c4/BMPfileFormat.png
// *** Solution:   Copy buffer[] to buffer2[] and pay attention to padding (!!) at the end of each row
Byte[] buffer2 = new Byte[240 * bmpData.Stride];
for (int y = 0; y < 240; y++)
{
    Buffer.BlockCopy(buffer, y * 313 * 2, buffer2, y * bmpData.Stride, 313 * 2);
}

Marshal.Copy(buffer2, 0, ptrToFirstPixel, buffer2.Length);  // *** Use padded buffer2 instead of buffer1
bmp.UnlockBits(bmpData);
// Diplay Bitmap in my PictureBox
pbImage.Image = bmp;
...