实验目的
1.理解图像文件的基本组成。
2.掌握结构体作为复杂数据对象的用法。进一步熟悉由问题到程序的解决方案,并掌握
编程细节:如内存分配、倒序读写、字节序、文件读写过程等。
方法解释
文件格式
注释:
1 WORD = 2 BYTE
1 DWORD= 2 WORD = 4 BYTE
1 LONG = 2 DWORD = 8 BYTE
BMP,全称为“bitmap”,中文译为“位图”
BMP文件格式
补充说明24位BMP文件,每个像素的RGB分量可以直接用24位的三个字节表示,所以没有调色板
位图文件头格式
补充说明:
bfType 表示位图类别,根据不同的操作系统而不同,在Windows中,此字段的值总为‘BM’(424d)
bfSize 说明的是整个bmp文件的大小,以字节为单位
bfOffBits 说明的是位图文件头+位图信息头+调色板的大小
BMP文件头固定长度为14字节(0x28)
位图文件头格式
补充说明:
bitCompression 表示压缩方式,0表示不压缩,1表示RLE8压缩,2表示RLE4压缩,3表示每个像素值由指定的掩码决定
BMP信息头固定长度为40字节
调色板
补充说明:
以8位位图为例,每个像素由一个8bit字节存储,一个8bit字节能表示256个数(索引值 或 数组下标),所以文件信息头之后,图像数据之前,需要存放256*4字节长度的调色板,依次按照颜色对应的索引值顺序,4字节4字节存放,每四个字节按照B、G、R、保留值(为0)顺序存储。显示图片时,按照图像数据所存储的索引值,在调色板中查找对应的BGR分量,还原该像素颜色。
图像数据
比如,一字节可以表示8个1位彩色深度像素,2个4位,或1个8位
若不是4的整数倍,需要合理补齐,比如末位补0等,否则无法正常读取文件
24位BMP文件格式
24位BMP文件没有调色板,数据图像用3个字节表示一个像素
8位BMP文件格式
8位BMP文件,在信息头后面存放了256*4长度的调色板,数据图像用1个字节表示一个像素
算法说明
8位BMP转24位BMP
创建pRGB结构体数组读取8位BMP调色板信息
bool MakePalette(FILE* pFile, BITMAPFILEHEADER& file_h, BITMAPINFOHEADER& info_h, RGBQUAD* pRGB_out)
{
if ((file_h.bfOffBits - sizeof(BITMAPFILEHEADER) - info_h.biSize) == sizeof(RGBQUAD) * pow(2, (double)info_h.biBitCount))
{
fseek(pFile, sizeof(BITMAPFILEHEADER) + info_h.biSize, 0);
fread(pRGB_out, sizeof(RGBQUAD), (unsigned int)pow(2, (double)info_h.biBitCount), pFile);
return true;
}
else
return false;
}
每读取一个字节,根据该字节去查找调色板,转换成BGR,直到fread读到文件末,返还0结束循环
while(fread(clrIndex, sizeof(uint8_t), 1, bmpFile))
{
*clr = pRGB[*clrIndex].rgbBlue;
clr++;
*clr = pRGB[*clrIndex].rgbGreen;
clr++;
*clr = pRGB[*clrIndex].rgbRed;
clr++;
}
改文件头和信息头
图像数据的大小 改为 24bit图像数据大小
偏移量删除调色板长度 改为 文件头+信息头
重置文件大小
像素深度 从8改为24
info_h.biSizeImage = info_h.biSizeImage * 3;
file_h.bfOffBits = 14 + info_h.biSize;
file_h.bfSize = file_h.bfOffBits + info_h.biSizeImage;
info_h.biBitCount = 24;
24位BMP转8位BMP
调色板设计
8bit彩色深度可以表示256种颜色,若想rgb三种像素值在(0~255)的颜色组合表示256中颜色,假设每种颜色有相同种类的像素值,那么63=216<256<343=73,若每种颜色有6种像素值等级,那么会有许多像素值不好分配。
所以想能否改变其中一种颜色像素值等级数,若其中一个改成7,那么能组合出来的颜色组合有6x6x7=252种,非常接近255种。人眼对绿色的敏感度在色谱中会较高,所以这里采取把绿色分成7个等级的方式,去设计调色板。
将256个像素平分成6个等级,则每个等级代表的的区间长度为256/6=42,采取用中间值代表整个区间的方式,以42/2=21作为第一等级像素值代表[0,41]区间的像素值,后四个等级区间依次递增42。绿色则同理,每个等级区间长度为256/7=36,以18为代表第一像素等级区间[0,35]的值,后六个等级依次递增36,最后剩下的256-252=4种颜色,让rgb同时递增小值,接近最高值255
int index = 0;
int r;
int g;
int b;
for (r = 0; r < 6; r++) {
for (g = 0; g < 7; g++) {
for (b = 0; b < 6; b++) {
Palette[index].rgbRed = 21 + 42 * r;
Palette[index].rgbBlue = 21 + 42 * b;
Palette[index].rgbGreen = 18