C语言读写BMP图片(附Github下载链接和视频讲解地址)
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下载链接和视频讲解地址)相关推荐
- UWB定位matlab代码及详细解析(附github下载链接)
UWB 此处说的UWB是超宽带,代码是matlab的一个简单的函数,函数输入若干个待定位点的横纵坐标,输出经UWB定位后的位置信息(坐标).默认2维.任意数量的待定位点,4个基站,基站数量和误差在程序 ...
- 10 个超炫酷后台控制面板(附 GitHub下载链接)
点击上方"朱小厮的博客",选择"设为星标" 做积极的人,而不是积极废人 Web 开发中几乎的平台都需要一个后台管理,但是从零开发一套后台控制面板并不容易,幸运的 ...
- C语言实现BMP图片的放大缩小
C语言实现BMP图片的放大缩小 BMP图片简介:BMP图片是windows操作系统中的标准图像文件格式,可以分为两类:设备相关位图(DDB)和设备无关位图(DIB),使用广泛.它采用位映射存储格式,除 ...
- C语言实现bmp图片裁剪
C语言实现bmp图片裁剪 bmp图片如何储存的,在这篇文章中有很好的介绍:https://blog.csdn.net/weixin_46987028/article/details/109171867 ...
- CVPR2020| 最新CVPR2020论文抢先看,附全部下载链接!
持续更新Github: https://github.com/Sophia-11/Awesome-CVPR-Paper 2021持续论文集锦百度云请在[计算机视觉联盟]后台回复 CVPR2021 往 ...
- html 文件、图片、txt 下载 链接和按钮
html 文件.图片.txt下载 链接和按钮 <!DOCTYPE html> <html lang="en"> <head><meta c ...
- 【华为推荐】基于反事实学习的推荐系统研究.pdf(附pdf下载链接)
今天给大家分享的是华为诺亚方舟实验室董振华博士在2019中国大数据技术大会(BDTC)上做的分享<基于反事实学习的推荐系统研究.pdf>,之前公众号曾给大家推送过该分享对应的论文,感兴趣的 ...
- 安装Windows11【附镜像下载链接】
虚拟机VMware中安装Windows 11[附镜像下载链接] 前言: 如果你想体验Windows 11,又担心玩坏物理机,不愿使用自己的笔记本尝试,那么使用虚拟机试水 is a good idea. ...
- GitHub下载链接
GITHUB 下载链接 https://gh.api.99988866.xyz/ https://hub.fastgit.org/
最新文章
- Qt5.8 在windows下mingw静态编译
- 移动医疗未来还有多少红利?
- juqery 获取radio选中的值
- 解决html2canvas截取页面部分div黑屏问题
- 中html倒入css那么套路,CSS常用套路
- 提高代码可读性: 命名技术
- KDD'21 | FaceBook :基于图的负采样方法
- java ipv4和ipv6通信_IPv6与IPv4连接负载
- 数据结构笔记(三十三)--二叉排序树的插入与生成
- 一步步学习SPD2010--第十四章节--在Web页面使用控件(2)--使用标准ASP.NET服务器控件...
- Python基础笔记(四)
- 【Chinapub读书会第9期】5月28日赵鑫磊带你深入解析Linux
- 中国天气预报API城市编号
- MacOSX安装OpenCC实现繁体字转简体字
- XCTF easyCpp
- Java modifier
- 新品国产C2000,独立双核32位CPU,主频高达400MHz,QX320F280049
- 【algorithm】源码详解中国大陆新身份证号码算法
- 基于遗传算法的配电网重构研究(Matlab代码实现)
- Windows下Git服务(Bonobo)安装
热门文章
- Oracle RAC 12.1.0.2 High CPU Usage
- ExecutorExecutorService
- 用vscode编写matlab
- 屏幕录像制作gif动态图
- 毕业设计:电子/通信/物联网/计算机专业选题目参考(嵌入式linux/单片机STM32/web/图像)
- java 线性回归_Java线性回归
- HAproxy正向代理配置
- 74LS85的IP核设计
- Python电影爬虫,用Excel存储并进行数据可视化分析
- 【技术教程】RTSP视频流媒体智能分析平台EasyNVR中的H264及H265编码视频存储计算方法介绍