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

3

我的目标:
在我的Windows Forms程序中显示一个RGB565原始格式的图像。(离题:数据来自OV7670相机模块)

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

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

原始rgb565原始图像:原始rgb565原始图像
PictureBox截图(带有对角线条纹):PictureBox截图

我曾经通过提取R、G、B并使用SetPixel()来显示图像,但这对我来说太慢了。这就是为什么我想获取底层代码以正确地显示图像。

我的测试图像可以在Dropbox上找到:
测试图像:测试图像


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个回答

4

经过10个小时的搜索,我终于找到了原因,这绝不是一件平凡的事情(至少对我来说不是)。

原因在于:位图规范要求将行尺寸填充为4字节的倍数!https://upload.wikimedia.org/wikipedia/commons/c/c4/BMPfileFormat.png

由于我的色带测试图像有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;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接