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


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

    使用Photoshop制作索引位图

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

    索引位图文件格式

    参考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 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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    C++中写入索引位图

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

    // boost::multi_array 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));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    参考:
    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

  • 相关阅读:
    Apollo planning之PathAssessmentDecider
    spring-boot-starter-data-redis 引发的一系列惨案
    油溶性硫化镉量子点,CdS 量子点,CdS QDS
    计算机毕业设计django基于python药房药品管理系统(源码+系统+mysql数据库+Lw文档)
    知识图谱01——ubuntu22.04 neo4j安装配置
    探索IP地址定位工具:解读IP数据云的功能与优势
    Python对接海康威视机器视觉工业相机
    Sentinel 系统规则 (SystemRule)
    企业电子招标采购系统源码Spring Boot + Mybatis + Redis + Layui + 前后端分离 构建企业电子招采平台之立项流程图
    计算机毕业设计(附源码)python在线答题系统
  • 原文地址:https://blog.csdn.net/tangyin025/article/details/126175940