BMP全称(BITMAP)是微软WINDOWS系统默认使用的一种通用图片数据存储格式,特点是结构清晰,解析简单和多平台支持广泛。其文件结构由文件头结构体,文件信息头结构体,调色板(可选),以及图片数据(压缩编码可选)组成。一个BMP文件的解析,从二进制低位开始,层层递进,每一层包含了下一层的信息,非常适合C语言进行操作。

首先介绍一下从BMP文件二进制数据前两字节,一般为‘B’ 'M'的ASCII码,其他的标识在微软未公开的OS使用,我们暂时忽略。

【1】首先读取一张BMP图像的头两个字节,一般情况下是0x42 0x4D,就是‘B’, 'M'的ASCII码,如果是两个字节一起读取就是0x4D42(lsb),十进制是19778。

【2】紧跟着就是12字节的文件头包含文件大小,有无调色板以及图像数据在偏移多少字节可以获取。

【3】在12字节的文件头后面是BMP的信息头文件。一共40字节,包含着BMP图像的宽,高,单像素大小,压缩模式,分辨率尺寸等具体的图像信息。

【4】如果有调色板的情况下在这个40字节的信息头文件之后直接存储调色板信息,每个调色板是4个字节,分别是B,G,R,A的映射值。

【5】最后的存储空间就是图像数据了,需要注意几个地方。第一,图像是按行反着存储的,第一行被存储到了最后一行,第二行存储在了最后第二行,以此类推。第二,图像如果是多通道的按BGRA方式存储。比如24位的图像单像素尺寸是已BGR顺序存储的。第三,图像每一行的大小必须是四字节对齐,比如8位图像511x511的BMP图像,每一行是511字节的图像数据自动扩展为512字节一行,用0x00补齐。

下面是12字节文件头结构体:

上图是BMP文件头结构体

bfSize是32位的元素,表示整个BMP文件的大小,单位是字节。

bfOffBits是32位的元素,表示从BMP二进制起始地址开始偏移多少字节是图像存储数据。根据之前讲解的内容,在文件存储数据前有2字节的’BM‘,12字节的文件头,40字节的信息头和调色板(如果有的话),所以总结下来bfOffBits 为54字节时没有调色板,大于54字节时,多出来的字节数就是调色板大小。

下面是40字节的信息头

其中biSize是整个信息头的大小为40字节

biWidth和biHeight代表了图像的像素分辨率,比如640x480

biPlanes这里默认为1,大部分情况都是1个plane。

biBitCount代表单个像素的位数,比如24是三字节的像素一般是BGR,32位一般是BGRA。

biCompression指的是压缩模式,0代表不压缩,1代表BGR555,2代表BGR565,3是其他模式。

biSizeImage这个变量蕴含着整个图像存储文件的大小,程序可以根据这个大小开辟空间。

biXPelsPerMeter和biYPelsPerMeter是用来描述原图的真实尺寸的,可以忽略。

biClrUsed和biClrImportant可以默认设置为0.

接下来就用C语言简单实现一下BMP文件的读和存

上图展示了读取BMP图像的过程:

【1】打开文件流,读取前两个字节

    FILE * fp = fopen(path,"rb");uint16_t bfType;  fread(&bfType,sizeof(bfType),1,fp);

【2】 读取文件头和信息头,计算调色板大小

    fread(bmpFileHeader_p,sizeof(BMP_FILE_HEADER),1,fp);fread(bmpInfoHeader_p,sizeof(BMP_INFO_HEADER),1,fp);size_t colorTableSize = bmpFileHeader_p->bfSize - bmpInfoHeader_p->biSizeImage -2 - sizeof(BMP_FILE_HEADER) - sizeof(BMP_INFO_HEADER);printf("colorTableSize=%d\n",colorTableSize);

【3】 首先讨论没有调色板的情况,根据读到的信息头确定图像宽高和大小,开辟空间并按行反着读取到内存空间。

    printf("bfType: %d %d [%c%c] \n",bfType&0xff,bfType>>8,bfType&0xff,bfType>>8);uint32_t channel = bmpInfoHeader_p->biBitCount>>3;uint32_t dataSize=bmpInfoHeader_p->biSizeImage;*dataPtr=(uint8_t *)malloc(dataSize);for(uint32_t i=0;i<bmpInfoHeader_p->biHeight;i++){fread((*dataPtr) + (bmpInfoHeader_p->biHeight-i-1)*bmpInfoHeader_p->biWidth*channel,sizeof(uint8_t),bmpInfoHeader_p->biWidth*channel,fp);}

【4】当步骤【2】读到的调色板大小超过0,就先读取调色板,再按行反着读取图像数据,然后根据调色板的映射还原图像的色彩信息。

    if(colorTableSize>0){uint8_t * table = (uint8_t *)malloc(colorTableSize);fread(table,1,colorTableSize,fp);// for(int i=0;i<colorTableSize;i++)// {//     printf("colortable[%3d]: %3d\n",i,table[i]);// }printf("bfType: %d %d [%c%c] \n",bfType&0xff,bfType>>8,bfType&0xff,bfType>>8);uint32_t channel = bmpInfoHeader_p->biBitCount>>3;uint32_t raw_dataSize=bmpInfoHeader_p->biSizeImage;uint32_t raw_channel = channel;channel =3;uint32_t dataSize    =bmpInfoHeader_p->biWidth * bmpInfoHeader_p->biHeight * channel;*dataPtr=(uint8_t *)malloc(dataSize);uint8_t * raw=(uint8_t *)malloc(raw_dataSize);// fread(raw,sizeof(uint8_t),raw_dataSize,fp);for(uint32_t i=0;i<bmpInfoHeader_p->biHeight;i++){fread((raw) + (bmpInfoHeader_p->biHeight-i-1)*bmpInfoHeader_p->biWidth*raw_channel,sizeof(uint8_t),bmpInfoHeader_p->biWidth*raw_channel,fp);}BMP_COLOR_TBL * tbl_array = (BMP_COLOR_TBL*)table;for(uint32_t i=0;i<bmpInfoHeader_p->biHeight;i++){uint8_t * write_line_ptr = (*dataPtr) + (bmpInfoHeader_p->biHeight-i-1)*bmpInfoHeader_p->biWidth*channel;uint8_t * raw_line_ptr   = (raw) + (bmpInfoHeader_p->biHeight-i-1)*bmpInfoHeader_p->biWidth*raw_channel;for(uint32_t j=0;j<bmpInfoHeader_p->biWidth;j++){uint8_t v = raw_line_ptr[j];BMP_COLOR_TBL tbl_array_ = tbl_array[v];write_line_ptr[j*channel+0] = tbl_array_.B;write_line_ptr[j*channel+1] = tbl_array_.G;write_line_ptr[j*channel+2] = tbl_array_.R;}// fread((*dataPtr) + (bmpInfoHeader_p->biHeight-i-1)*bmpInfoHeader_p->biWidth*channel,sizeof(uint8_t),bmpInfoHeader_p->biWidth*channel,fp);}free(raw);bmpInfoHeader_p->biBitCount=channel<<3;bmpInfoHeader_p->biSizeImage = bmpInfoHeader_p->biWidth * bmpInfoHeader_p->biHeight * channel;free(table);}

【5】关闭文件流,完成BMP文件读取

fclose(fp);

以下是完整读取BMP文件函数

void readBmpFromFile(char* path,BMP_FILE_HEADER * bmpFileHeader_p,BMP_INFO_HEADER * bmpInfoHeader_p,uint8_t ** dataPtr)
{FILE * fp = fopen(path,"rb");uint16_t bfType;  fread(&bfType,sizeof(bfType),1,fp);fread(bmpFileHeader_p,sizeof(BMP_FILE_HEADER),1,fp);fread(bmpInfoHeader_p,sizeof(BMP_INFO_HEADER),1,fp);size_t colorTableSize = bmpFileHeader_p->bfSize - bmpInfoHeader_p->biSizeImage -2 -sizeof(BMP_FILE_HEADER) - sizeof(BMP_INFO_HEADER);printf("colorTableSize=%d\n",colorTableSize);if(colorTableSize>0){uint8_t * table = (uint8_t *)malloc(colorTableSize);fread(table,1,colorTableSize,fp);// for(int i=0;i<colorTableSize;i++)// {//     printf("colortable[%3d]: %3d\n",i,table[i]);// }printf("bfType: %d %d [%c%c] \n",bfType&0xff,bfType>>8,bfType&0xff,bfType>>8);uint32_t channel = bmpInfoHeader_p->biBitCount>>3;uint32_t raw_dataSize=bmpInfoHeader_p->biSizeImage;uint32_t raw_channel = channel;channel =3;uint32_t dataSize    =bmpInfoHeader_p->biWidth * bmpInfoHeader_p->biHeight * channel;*dataPtr=(uint8_t *)malloc(dataSize);uint8_t * raw=(uint8_t *)malloc(raw_dataSize);// fread(raw,sizeof(uint8_t),raw_dataSize,fp);for(uint32_t i=0;i<bmpInfoHeader_p->biHeight;i++){fread((raw) + (bmpInfoHeader_p->biHeight-i-1)*bmpInfoHeader_p->biWidth*raw_channel,sizeof(uint8_t),bmpInfoHeader_p->biWidth*raw_channel,fp);}BMP_COLOR_TBL * tbl_array = (BMP_COLOR_TBL*)table;for(uint32_t i=0;i<bmpInfoHeader_p->biHeight;i++){uint8_t * write_line_ptr = (*dataPtr) + (bmpInfoHeader_p->biHeight-i-1)*bmpInfoHeader_p->biWidth*channel;uint8_t * raw_line_ptr   = (raw) + (bmpInfoHeader_p->biHeight-i-1)*bmpInfoHeader_p->biWidth*raw_channel;for(uint32_t j=0;j<bmpInfoHeader_p->biWidth;j++){uint8_t v = raw_line_ptr[j];BMP_COLOR_TBL tbl_array_ = tbl_array[v];write_line_ptr[j*channel+0] = tbl_array_.B;write_line_ptr[j*channel+1] = tbl_array_.G;write_line_ptr[j*channel+2] = tbl_array_.R;}// fread((*dataPtr) + (bmpInfoHeader_p->biHeight-i-1)*bmpInfoHeader_p->biWidth*channel,sizeof(uint8_t),bmpInfoHeader_p->biWidth*channel,fp);}free(raw);bmpInfoHeader_p->biBitCount=channel<<3;bmpInfoHeader_p->biSizeImage = bmpInfoHeader_p->biWidth * bmpInfoHeader_p->biHeight * channel;free(table);}else{printf("bfType: %d %d [%c%c] \n",bfType&0xff,bfType>>8,bfType&0xff,bfType>>8);uint32_t channel = bmpInfoHeader_p->biBitCount>>3;uint32_t dataSize=bmpInfoHeader_p->biSizeImage;*dataPtr=(uint8_t *)malloc(dataSize);for(uint32_t i=0;i<bmpInfoHeader_p->biHeight;i++){fread((*dataPtr) + (bmpInfoHeader_p->biHeight-i-1)*bmpInfoHeader_p->biWidth*channel,sizeof(uint8_t),bmpInfoHeader_p->biWidth*channel,fp);}}fclose(fp);
}

存BMP文件也是类似的操作,

一下是没有调色板的BMP文件存储

void saveBmpDataToFile(uint8_t * data_p,uint32_t pixelW,uint32_t pixelH,uint32_t pixelS,char * path,int compLevel)
{//s0 compute key parameters infouint32_t imageSize = pixelW * pixelH * pixelS;uint32_t bitsPerPixel = pixelS <<3;uint32_t imageDataOffsetFromHead       = 2+sizeof(BMP_INFO_HEADER) + sizeof(BMP_FILE_HEADER);uint32_t imageDataOffsetFromFileHeader = sizeof(BMP_INFO_HEADER);uint32_t fileSize                      = 2+imageSize + sizeof(BMP_INFO_HEADER) + sizeof(BMP_FILE_HEADER);FILE * fp = fopen(path,"wb");//s1 save 'B' 'M'char Format_BM[2]={'B','M'};fwrite(Format_BM,sizeof(char),2,fp);//s2 save 12 bytes File HeaderBMP_FILE_HEADER fileHeader;fileHeader.bfOffBits  = imageDataOffsetFromHead;fileHeader.bfSize     = fileSize;fileHeader.bfReserved1=0;fileHeader.bfReserved2=0;fwrite(&fileHeader,sizeof(BMP_FILE_HEADER),1,fp);//s3 save 40 bytes Info HeaderBMP_INFO_HEADER infoHeader;infoHeader.biSize           = imageDataOffsetFromFileHeader;infoHeader.biWidth          = pixelW;infoHeader.biHeight         = pixelH;infoHeader.biPlanes         = 1;infoHeader.biBitCount       = bitsPerPixel;infoHeader.biCompression    = 0;infoHeader.biSizeImage      = imageSize;infoHeader.biXPelsPerMeter  = 37795;infoHeader.biYPelsPerMeter  = 37795;infoHeader.biClrUsed        = 0;infoHeader.biClrImportant   = 0;fwrite(&infoHeader,sizeof(BMP_INFO_HEADER),1,fp);//s4 save image data with inversed linesfor(uint32_t i = 0;i<pixelH;i++){fwrite(data_p+(pixelH-i-1)*(pixelW*pixelS),sizeof(uint8_t),pixelW*pixelS,fp);}fclose(fp);
}

以下是有调色板情况下的BMP文件存储

void saveBmpDataToFile_colorTBL(uint8_t * data_p,uint32_t pixelW,uint32_t pixelH,uint32_t pixelS,uint8_t * colorTbl,uint32_t colorTblSize,char * path)
{if(colorTblSize==0) {printf("error! colorTblSize could not be zero!!!\n");return;};//s0 compute key parameters infouint32_t imageSize = pixelW * pixelH * pixelS;uint32_t bitsPerPixel = pixelS <<3;uint32_t imageDataOffsetFromHead       = 2+colorTblSize+sizeof(BMP_INFO_HEADER) + sizeof(BMP_FILE_HEADER);uint32_t imageDataOffsetFromFileHeader = sizeof(BMP_INFO_HEADER);uint32_t fileSize                      = 2+colorTblSize+imageSize+ sizeof(BMP_INFO_HEADER) + sizeof(BMP_FILE_HEADER);FILE * fp = fopen(path,"wb");//s1 save 'B' 'M'char Format_BM[2]={'B','M'};fwrite(Format_BM,sizeof(char),2,fp);//s2 save 12 bytes File HeaderBMP_FILE_HEADER fileHeader;fileHeader.bfOffBits  = imageDataOffsetFromHead;fileHeader.bfSize     = fileSize;fileHeader.bfReserved1=0;fileHeader.bfReserved2=0;fwrite(&fileHeader,sizeof(BMP_FILE_HEADER),1,fp);//s3 save 40 bytes Info HeaderBMP_INFO_HEADER infoHeader;infoHeader.biSize           = imageDataOffsetFromFileHeader;infoHeader.biWidth          = pixelW;infoHeader.biHeight         = pixelH;infoHeader.biPlanes         = 1;infoHeader.biBitCount       = bitsPerPixel;infoHeader.biCompression    = 0;infoHeader.biSizeImage      = pixelW*pixelH*1;infoHeader.biXPelsPerMeter  = 37795;infoHeader.biYPelsPerMeter  = 37795;infoHeader.biClrUsed        = 0;infoHeader.biClrImportant   = 0;fwrite(&infoHeader,sizeof(BMP_INFO_HEADER),1,fp);//s4 save color tablefwrite(colorTbl,sizeof(uint8_t),colorTblSize,fp);//s5 save image data with inversed linesfor(uint32_t i = 0;i<pixelH;i++){fwrite(data_p+(pixelH-i-1)*(pixelW*pixelS),sizeof(uint8_t),pixelW*pixelS,fp);}fclose(fp);
}

该工程已经同步到GIHUB,

下载地址:

https://github.com/leonard73/LeoCPorintg.git

视频讲解地址:

纯C语言手撸BMP读写_哔哩哔哩_bilibili

C语言读写BMP图片(附Github下载链接和视频讲解地址)相关推荐

  1. UWB定位matlab代码及详细解析(附github下载链接)

    UWB 此处说的UWB是超宽带,代码是matlab的一个简单的函数,函数输入若干个待定位点的横纵坐标,输出经UWB定位后的位置信息(坐标).默认2维.任意数量的待定位点,4个基站,基站数量和误差在程序 ...

  2. 10 个超炫酷后台控制面板(附 GitHub下载链接)

    点击上方"朱小厮的博客",选择"设为星标" 做积极的人,而不是积极废人 Web 开发中几乎的平台都需要一个后台管理,但是从零开发一套后台控制面板并不容易,幸运的 ...

  3. C语言实现BMP图片的放大缩小

    C语言实现BMP图片的放大缩小 BMP图片简介:BMP图片是windows操作系统中的标准图像文件格式,可以分为两类:设备相关位图(DDB)和设备无关位图(DIB),使用广泛.它采用位映射存储格式,除 ...

  4. C语言实现bmp图片裁剪

    C语言实现bmp图片裁剪 bmp图片如何储存的,在这篇文章中有很好的介绍:https://blog.csdn.net/weixin_46987028/article/details/109171867 ...

  5. CVPR2020| 最新CVPR2020论文抢先看,附全部下载链接!

    持续更新Github: https://github.com/Sophia-11/Awesome-CVPR-Paper 2021持续论文集锦百度云请在[计算机视觉联盟]后台回复  CVPR2021 往 ...

  6. html 文件、图片、txt 下载 链接和按钮

    html 文件.图片.txt下载 链接和按钮 <!DOCTYPE html> <html lang="en"> <head><meta c ...

  7. 【华为推荐】基于反事实学习的推荐系统研究.pdf(附pdf下载链接)

    今天给大家分享的是华为诺亚方舟实验室董振华博士在2019中国大数据技术大会(BDTC)上做的分享<基于反事实学习的推荐系统研究.pdf>,之前公众号曾给大家推送过该分享对应的论文,感兴趣的 ...

  8. 安装Windows11【附镜像下载链接】

    虚拟机VMware中安装Windows 11[附镜像下载链接] 前言: 如果你想体验Windows 11,又担心玩坏物理机,不愿使用自己的笔记本尝试,那么使用虚拟机试水 is a good idea. ...

  9. GitHub下载链接

    GITHUB 下载链接 https://gh.api.99988866.xyz/ https://hub.fastgit.org/

最新文章

  1. Qt5.8 在windows下mingw静态编译
  2. 移动医疗未来还有多少红利?
  3. juqery 获取radio选中的值
  4. 解决html2canvas截取页面部分div黑屏问题
  5. 中html倒入css那么套路,CSS常用套路
  6. 提高代码可读性: 命名技术
  7. KDD'21 | FaceBook :基于图的负采样方法
  8. java ipv4和ipv6通信_IPv6与IPv4连接负载
  9. 数据结构笔记(三十三)--二叉排序树的插入与生成
  10. 一步步学习SPD2010--第十四章节--在Web页面使用控件(2)--使用标准ASP.NET服务器控件...
  11. Python基础笔记(四)
  12. 【Chinapub读书会第9期】5月28日赵鑫磊带你深入解析Linux
  13. 中国天气预报API城市编号
  14. MacOSX安装OpenCC实现繁体字转简体字
  15. XCTF easyCpp
  16. Java modifier
  17. 新品国产C2000,独立双核32位CPU,主频高达400MHz,QX320F280049
  18. 【algorithm】源码详解中国大陆新身份证号码算法
  19. 基于遗传算法的配电网重构研究(Matlab代码实现)
  20. Windows下Git服务(Bonobo)安装

热门文章

  1. Oracle RAC 12.1.0.2 High CPU Usage
  2. ExecutorExecutorService
  3. 用vscode编写matlab
  4. 屏幕录像制作gif动态图
  5. 毕业设计:电子/通信/物联网/计算机专业选题目参考(嵌入式linux/单片机STM32/web/图像)
  6. java 线性回归_Java线性回归
  7. HAproxy正向代理配置
  8. 74LS85的IP核设计
  9. Python电影爬虫,用Excel存储并进行数据可视化分析
  10. 【技术教程】RTSP视频流媒体智能分析平台EasyNVR中的H264及H265编码视频存储计算方法介绍