C#:如何将RAW图像(格式:rgb565)加载到位图中?

时间:2019-04-19 18:09:57

标签: c# bitmap pixelformat

我的目标:
在我的Windows Forms程序中显示以rgb565原始格式附带的图像。 (主题为:数据来自OV7670摄像机模块)

我的方法:
首先,我创建一个空的位图。接下来,我将图像数据(原始格式:rgb565)插入到空位图的有效负载部分。最后,我在PictureBox中显示修改后的位图。

我的问题:
一切正常,但测试图像显示为斜条纹而不是垂直条纹(请参见下面的链接)。

原始的rgb565原始图像:Original rgb565 raw image
PictureBox的屏幕截图(带有斜条纹):Screenshot of PictureBox

我确实通过提取R,G,B并使用SetPixel()来显示图像,但这对我来说太慢了。这就是为什么我想在下面的代码以正确的方式显示图像。

我的Testimage可以在此处的保管箱中找到:
测试图像:Testimage


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 :(得分:0)

在干草堆里闲逛了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;