【问题标题】:Loading an 8bpp grayscale BMP in C在 C 中加载 8bpp 灰度 BMP
【发布时间】:2012-06-03 06:59:20
【问题描述】:

我无法理解 BMP 格式,我知道它应该很简单,但不知何故我遗漏了一些东西。我以为是 2 个标头,后跟定义图像的实际字节,但数字不加起来。

例如,我只是尝试将此 BMP 文件加载到内存中(640x480 8bpp 灰度),然后将其写回另一个文件。据我了解,有两个不同的标题 BITMAPFILEHEADER 和 BITMAPINFOHEADER。 BITMAPFILEHEADER 是 14 字节,而 BITMAPINFOHEADER 是 40 字节(这取决于 BMP,我怎么能说这是另一个故事)。无论如何,BITMAPFILEHEADER 通过其参数 bfOffBits 表示位图位从偏移量 1078 开始。这意味着还有 1024 ( 1078 - (40+14) ) 个其他字节,携带更多信息。这些字节是什么,我如何读取它们,这就是问题所在。还是有更正确的方法来加载 BMP 并将其写入磁盘?

这里是我使用的代码供参考(顺便说一句,我在 windows 下做所有这些。)

#include <windows.h>
#include <iostream>
#include <stdio.h>


HANDLE hfile;
DWORD written;

BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;

int main()
    hfile = CreateFile("image.bmp",GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
ReadFile(hfile,&bfh,sizeof(bfh),&written,NULL);

ReadFile(hfile,&bih,sizeof(bih),&written,NULL);

int imagesize = bih.biWidth * bih.biHeight;

image = (unsigned char*) malloc(imagesize);

ReadFile(hfile,image,imagesize*sizeof(char),&written,NULL);

CloseHandle(hfile);

然后我正在做与写入文件完全相反的操作,

hfile = CreateFile("imageout.bmp",GENERIC_WRITE,FILE_SHARE_WRITE,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);

WriteFile(hfile,&bfh,sizeof(bfh),&written,NULL);
WriteFile(hfile,&bih,sizeof(bih),&written,NULL);
WriteFile(hfile,image,imagesize*sizeof(char),&written,NULL);

CloseHandle(hfile);

编辑 --- 已解决

好吧,我终于做对了,毕竟它并不复杂。正如 Viktor 指出的,这 1024 个字节代表调色板。

我在代码中添加了以下内容:

RGBQUAD palette[256];
// [...] previous declarations [...] int main() [...] then read two headers
ReadFile(hfile,palette,sizeof(palette),&written,NULL);

然后当我回信时,我添加了以下内容,

WriteFile(hfile,palette,sizeof(palette),&written,NULL);

【问题讨论】:

  • 从图像中读取 biBitCount 时设置为什么?如果如你所说,它是一个 8 位图像,它应该有一个你需要处理的调色板。
  • 你为什么用 C++ 标签标记 C 问题?

标签: c windows image bmp


【解决方案1】:

“这些字节是什么,如何读取它们,这就是问题所在。”

正如 Retired Ninja 在评论中提到的那样,这些字节是 Palette(或 .BMP 格式术语中的 ColorTable)。基本上,它是一个表格,指定位图数据中遇到的每个 8bpp 值使用什么颜色。

对于灰度,调色板是微不足道的(我不是在谈论颜色模型和 RGB -> 灰度转换):

for(int i = 0 ; i < 256 ; i++)
{
    Palette[i].R = i;
    Palette[i].G = i;
    Palette[i].B = i;
}

但是,ColorTable 的条目中有一些填充,因此它需要 4 * 256 字节,而不是您需要的 256 * 3。 ColorTable 条目中的第四个组件(RGBQUAD 结构)不是“alpha 通道”,它只是“保留”的东西。请参阅 RGBQUAD (MSDN, RGBQUAD) 上的 MSDN。

详细的格式说明可以在维基页面找到:Wiki, bmp format

关于 RGBQUAD 结构的 SO 也有这个链接问题:Writing BMP image in pure c/c++ without other libraries

【讨论】:

  • there's some padding in the ColorTable's entries - 嗯,你确定吗?也许是 Alpha 通道。
  • 我认为“微不足道”的灰色调色板实际上并不理想。我现在找不到证实这一点的参考资料,但我相信有一种更好的方法可以将感知亮度考虑在内(绿色 100 像素被认为比蓝色 100 像素更亮)。
  • 对于 CRT 显示器来说,像 0.12 (R)、0.59(G)、0.29(B) 这样的值是可以的,但必须针对 LCD 显示器进行调整。你是对的,但我只是没有过多地谈论颜色模型 - 这取决于原始海报,他的文件格式有问题。
  • 查看 MSDN/RGBQUAD 文档 - 它不是 RGBQUAD 中的 alpha 字段,它只是保留的内容
【解决方案2】:

正如维克托在他的回答中所说,这些位就是托盘。至于你应该如何阅读它们,看看这个header-only bitmap class。特别要查看对ColorTable 的引用,了解它如何根据给出的 BMP 类型处理托盘位。

【讨论】:

    猜你喜欢
    • 2019-12-03
    • 2011-07-18
    • 2023-03-03
    • 2017-03-11
    • 1970-01-01
    • 1970-01-01
    • 2019-02-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多