我的目标:
在我的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;
预期结果:竖条纹:-)
实际结果:对角条纹:-(
答案 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;