我正在尝试编写用于将各种格式的图像读取到字节数组中的原始算法。在我设法解析 PPM 和 TGA 图纸后,我决定启动 BMP 格式。目前,我已经学习了如何在以下情况下将 BMP 图像转换为 RGB 序列数组:
- 在图片中,有关特定像素的信息使用四位或八位进行编码,并且文件包含一个颜色表。
- 在绘图中,有关特定像素的信息使用 24 位进行编码,并且文件不包含颜色表。这是最简单的选择。
32 位 BMP 图像出现问题。我使用 Paint dot NET 图形编辑器生成 BMP 文件。在带有保存选项的窗口中,您可以选择图片的位深度。我的 32 位图像是 2*2 像素,看起来像这样:
像素 #1 是 RGB(11, 12, 13),像素 #2 是 RGB(51, 52, 53),像素 #3 是 RGB(101, 102, 103),像素 #4 是 RGB(151, 152, 153) )。
使用类std::ifstream
及其方法get()
,我将文件的内容显示到控制台(首先是字节数,然后是具有该数字的字节的内容):
相同的输出,但在文本中:
1) 66 2) 77 3) 166 4) 0 5) 0 6) 0 7) 0 8) 0 9) 0 10) 0 11) 150 12) 0 13) 0 14) 0 15) 124 16) 0 17) 0 18) 0 19) 2 20) 0 21) 0 22) 0 23) 2 24) 0 25) 0 26) 0 27) 1 28) 0 29) 32 30) 0 31) 3 32) 0 33) 0 34) 0 35) 0 36) 0 37) 0 38) 0 39) 196 40) 14 41) 0 42) 0 43) 196 44) 14 45) 0 46) 0 47) 0 48) 0 49) 0 50) 0 51) 0 52) 0 53) 0 54) 0 55) 0 56) 0 57) 255 58) 0 59) 0 60) 255
61) 0 62) 0 63) 255 64) 0 65) 0 66) 0 67) 0 68) 0 69) 0 70) 255 71) 32 72) 110 73) 105 74) 87 75) 0 76) 0 77) 0 78) 0 79) 0 80) 0 81) 0 82) 0 83) 0 84) 0 85) 0 86) 0 87) 0 88) 0 89) 0 90) 0 91) 0 92) 0 93) 0 94) 0 95) 0 96) 0 97) 0 98) 0 99) 0 100) 0 101) 0 102) 0 103) 0 104) 0 105) 0 106) 0 107) 0 108) 0 109) 0 110) 0 111) 0 112) 0 113) 0 114) 0 115) 0 116) 0 117) 0 118) 0 119) 0 120) 0 121) 0 122) 0 123) 0 124) 0 125) 0 126) 0 127) 0 128) 0 129) 0 130) 0 131) 0 132) 0 133) 0 134) 0 135) 0 136) 0 137) 0 138) 0 139) 255 140) 0 141) 0 142) 0 143) 0 144) 255 145) 0 146) 0 147) 0 148) 0 149) 255 150) 0 151) 103 152) 102 153) 101 154) 255 155) 153 156) 152 157) 151 158) 255 159) 13 160) 12 161) 11 162) 255 163) 53 164) 52 165) 51 166) 255
从第 15 到第 18 字节占用的字段(取决于 BMP 版本称为 bcSize/biSize/bV4Size/bV5Size)的值为 124,对应于 BMP 版本 5。这表明图片可能包含“bitmasks to提取通道值:红色、绿色、蓝色强度和 alpha 通道值”(来源:https ://ru.wikipedia.org/wiki/BMP#BITMAPINFO )。压缩字段的值(从第 31 到第 34 个字节)也指示掩码的存在,因为它等于数字 3。包含掩码的字段占用 4 个字节,并从第 55 个字节开始相互跟随. 从控制台输出来看,在我的图像中,掩码具有以下值(十进制):
红色掩码:(0 0 255 0
第 55 到第 58 个字节)
绿色掩码:(0 255 0 0
从第 59 到第 62 字节)
蓝色掩码:(255 0 0 0
从第 63 到第 66 字节)
Alpha 通道掩码:(0 0 0 255
从第 67 到第 70 个字节)
我不知道如何解释这些值并将它们应用于位于文件末尾的像素颜色。如果我忽略这些值并直接提取颜色(从有时称为图像数据或像素数据的数据块中),我会得到正确的颜色。这一定是因为我没有在图形编辑器中调整蒙版,程序为它们写下了不会扭曲像素颜色的默认值。
如果手动更正蒙版的值并覆盖文件,每个像素的颜色都会发生变化。例如,我将红色蒙版更改为0 0 60 0
(十进制),在图形编辑器中打开图像,得到以下结果:
像素 #1 现在是 RGB(34, 12, 13),像素 #2 是 RGB(204, 52, 53),像素 #3 是 RGB(153, 102, 103),像素 #4 是 RGB(85, 152) , 153)。
确实,每个像素的红色值都发生了变化。可能,编辑器使用数字的二进制表示执行了一些操作,但我不知道哪些操作。在我看过的有关 BMP 的文章中,出于某种原因顺便提到了掩码。位于图像数据之前的数据块也提出了问题。它占据文件的第 139 到第 150 个字节(在我的情况下),根据维基百科,它的名称是 GAP1(https://commons.wikimedia.org/wiki/File:BMPfileFormat.svg?uselang=ru) . 只有在我在保存选项中将位深度设置为 32 后,GAP1 才会出现在我的 BMP 绘图中。这个块的数据和颜色掩码非常相似,但我不确定它是否有任何联系,因为我找不到关于 GAP1 的信息。我可能做错了什么。
请告诉我如何理解蒙版在 BMP 图纸中的工作原理?
#include <iostream>
#include <fstream>
#include <string>
void ReadImage_BMP(const char* file_path_c_str);
int main()
{
ReadImage_BMP(".../image.bmp");
return 0;
}
void ReadImage_BMP(const char* file_path_c_str)
{
std::string file_path = file_path_c_str;
std::ifstream input_fs;
input_fs.open(file_path, std::ios_base::binary);
if (input_fs.is_open() == false)
{
perror(("Error while opening file " + file_path).c_str());
return;
}
char current_byte;
int count = 1;
while ((bool)input_fs.get(current_byte) == true)
{
std::cout << count << ") " << (int)(unsigned char)current_byte << std::endl;
count++;
}
if (input_fs.eof())
{
std::cout << "End of file reached." << std::endl;
}
else if (input_fs.fail())
{
std::cout << "Type error." << std::endl;
}
else
{
std::cout << "Unknown error" << std::endl;
}
input_fs.close();
}