索引位图制作、C++读写流程

和常见的16、24、32位色BMP格式不同,索引位图是一种使用调色板 + 8位色的位图格式,每个像素只占一个字节,像素值为0~255索引,其颜色保存在调色板对应位置数据

Photoshop制作索引位图

使用Photoshop打开已有的图片,再选择菜单【图像】、【模式】、【索引颜色…】
随后弹出的对话框中可选择对应的算法进行索引颜色整理,将当前图像的颜色归纳的256色之中
在这里插入图片描述确定之后图像就变成索引位图了,再次通过菜单【图像】、【模式】、【颜色表…】,可以修改调色板内容
在这里插入图片描述然后【存储为…】,保存为8位色BMP图片格式

GIMP制作索引位图

通过菜单【图像】、【模式】、【索引…】
弹出对话框将图像转换为索引颜色
在这里插入图片描述
再通过菜单【窗口】、【可停靠对话框】、【颜色表】
打开颜色表面板,就能在后续操作中指定颜色、及索引值
在这里插入图片描述
编辑完成后,需要通过菜单【文件】、【导出…】,选择Windows BMP图像,就能生成8位索引位图

Imagine查看索引位图及颜色表

Imagine是一个轻量级免费图像浏览器,可方便查看各种图片格式,官网:https://www.nyam.pe.kr/dev/imagine/
当打开索引位图时,可通过工具栏上的【编辑调色板】按扭在这里插入图片描述就能打开调色板窗口
在这里插入图片描述

索引位图文件格式

参考msdn文档:https://docs.microsoft.com/zh-cn/windows/win32/gdi/bitmap-storage,可知位图的存储格式为
在这里插入图片描述

BITMAPFILEHEADER格式

在头文件Wingdi.h中声明

成员说明
bfType0x4D42,表示BM
bfSize文件总的大小,包括BITMAPFILEHEADER size
bfReserved10
bfReserved20
bfOffBits图像数据(Color-index array)相对于文件头的起始位置

BITMAPINFOHEADER格式

在头文件Wingdi.h中声明

成员说明
biSize结构体BITMAPINFOHEADER的大小40
biWidth图像宽度
biHeight图像高度
biPlanes1
biBitCount索引位图为8
biCompressionBI_RGB表示未压缩
biSizeImage图像数据大小,通常是pitch x biHeight,
pitch是:biWidth * sizeof(pixel)的4字节对齐版本
biXPelsPerMeterpixels-per-meter
biYPelsPerMeterpixels-per-meter
biClrUsed索引位图为0,表示调色板有256色
biClrImportant0

C++中读取索引位图

假设需要将位图读到二维数组boost::multi_array<byte, 2> img中

std::ifstream ifs("aaa.bmp", std::ios::binary);

//-		bfh	{bfType=19778 bfSize=787512 bfReserved1=0 ...}	tagBITMAPFILEHEADER
//		bfType	19778	unsigned short
//		bfSize	787512	unsigned long
//		bfReserved1	0	unsigned short
//		bfReserved2	0	unsigned short
//		bfOffBits	1078	unsigned long
BITMAPFILEHEADER bfh;
ifs.read((char*)&bfh, sizeof(bfh));
if (bfh.bfType != 0x4D42)
	return;

//-		bih	{biSize=40 biWidth=1024 biHeight=768 ...}	tagBITMAPINFOHEADER
//		biSize	40	unsigned long
//		biWidth	1024	long
//		biHeight	768	long
//		biPlanes	1	unsigned short
//		biBitCount	8	unsigned short
//		biCompression	0	unsigned long
//		biSizeImage	786434	unsigned long
//		biXPelsPerMeter	11808	long
//		biYPelsPerMeter	11808	long
//		biClrUsed	0	unsigned long
//		biClrImportant	0	unsigned long
BITMAPINFOHEADER bih;
ifs.read((char*)&bih, sizeof(bih));
if (bih.biCompression != BI_RGB || bih.biBitCount != 8)
	return;

// 位图每一行的数据字节长度是4的倍数
int pitch = (bih.biWidth * bih.biBitCount + 31) / 32 * 4;

// 逐行将数据读入二维数组
boost::multi_array<unsigned char, 2> img(boost::extents[bih.biHeight][bih.biWidth]);
for (int i = 0; i < bih.biHeight; i++)
{
	// Windows位图数据从下往上存储,所以需要反着读取
	long offset = bfh.bfOffBits + (bih.biHeight - i - 1) * pitch;
	ifs.seekg(offset, SEEK_SET);

	ifs.read((char*)&img[i][0], bih.biWidth * sizeof(unsigned char));
}

C++中写入索引位图

由于之前读取位图的bfh、bih之后,内存中就已经看到数据,所以依样画葫芦,再把数据写回文件,但由于位图宽度数据需要4的倍数,所以建议将原来的数组进行相应扩大

// boost::multi_array<unsigned char, 2> img(...);
// 需要将二维数组img的宽度扩大到4的倍数
img.resize(boost::extents[img.shape()[0]][boost::alignment::align_up(img.shape()[1], 4)]);

std::ofstream ofs(path, std::ios::binary, _SH_DENYRW);

BITMAPFILEHEADER bfh = { 0 };
BITMAPINFOHEADER bih = { 0 };
bfh.bfType = 0x4D42;
bfh.bfOffBits = sizeof(bfh) + sizeof(bih) + sizeof(RGBQUAD) * 256;
bfh.bfSize = bfh.bfOffBits + img.num_elements() * sizeof(unsigned char);
bih.biSize = sizeof(bih);
bih.biWidth = img.shape()[1];
bih.biHeight = img.shape()[0];
bih.biPlanes = 1;
bih.biBitCount = 8;
bih.biCompression = BI_RGB;
bih.biSizeImage = img.num_elements() * sizeof(unsigned char);
bih.biXPelsPerMeter = 11808;
bih.biYPelsPerMeter = 11808;
bih.biClrUsed = 0;
bih.biClrImportant = 0;

ofs.write((char*)&bfh, sizeof(bfh));
ofs.write((char*)&bih, sizeof(bih));

// 这里写入灰度数据到调色板数据,可在Photoshop面板中更改
for (int p = 0; p < 256; p++)
{
	RGBQUAD q = { p, p, p, 0 };
	ofs.write((char*)&q, sizeof(q));
}

for (int i = 0; i < img.shape()[0]; i++)
{
	ofs.write((char*)&img[img.shape()[0] - i - 1][0], img.shape()[1] * sizeof(unsigned char));
}

参考:
https://docs.microsoft.com/zh-cn/windows/win32/gdi/bitmap-storage
https://blog.csdn.net/Wintalen/article/details/1014820
https://sunriver2000.blog.csdn.net/article/details/104251831

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值