本来是打算弄个BadApple玩玩,不过不满足于简单地读取文本文件并输出,所以最后变成了研究如何用C语言读取位图文件并通过二维数组来存储像素信息。

第一步自然是弄清楚bmp的文件格式。在各种位图格式中,bmp因为数据块通常没有压缩,每个像素都由独立的几个或者几组bits来表示,读写方式都比较简单,只需要按照格式要求通过fread读取二进制文件就行。

bmp文件大概有四部分组成,第一部分是文件类型数据,存放跟bmp文件类型有关的信息,共占用14字节。第二部分是图像信息数据,保存位图图像相关的信息,共40字节,第三部分是一个可选的调色板,最后是像素信息区。

各部分中具体的字段含义见下表:

按照上面的格式说明,我们定义几个结构体来描述他们:

#pragma once#include<stdio.h>
typedef unsigned int DWORD;  // 4bytes
typedef unsigned short WORD;  // 2bytes
typedef signed long LONG;  // 4bytes
typedef unsigned char BYTE;  // 1bytes#pragma pack(push)
#pragma pack(1)// 修改默认对齐值
/*位图文件文件头结构体*/
typedef struct tagBITMAPFILEHEADER {WORD bFileType;DWORD bFileSize;WORD bReserved1;WORD bReserved2;DWORD bPixelDataOffset;
}BITMAPFILEHEADER; //14bytes
#pragma pack(pop)/*位图文件信息头结构体*/
typedef struct tagBITMAPINFOHEADER {DWORD bHeaderSize;  // 图像信息头总大小(40bytes)LONG bImageWidth;  // 图像宽度(像素)LONG bImageHeight;  // 图像高度WORD bPlanes;  // 应该是0WORD bBitsPerPixel;  // 像素位数DWORD bCompression;  // 图像压缩方法DWORD bImageSize;  // 图像大小(字节)LONG bXpixelsPerMeter;  // 横向每米像素数LONG bYpixelsPerMeter;  // 纵向每米像素数DWORD bTotalColors;  // 使用的颜色总数,如果像素位数大于8,则该字段没有意义DWORD bImportantColors;  // 重要颜色数,一般没什么用
}BITMAPINFOHEADER; //40bytes/*位图文件调色板结构体*/
typedef struct tagRGBQUAD {BYTE rgbBlue;BYTE    rgbGreen;BYTE   rgbRed;BYTE rgbReserved;
}RGBQUAD;/*像素点RGB结构体*/
typedef struct tagRGB {BYTE blue;BYTE green;BYTE red;
}RGBDATA;

#pragma pack(push)
#pragma pack(1)    
用于取消结构体的自动对齐,如果不指定文件头结构体可能占据的大小不是14字节,关于机构体的大小是否正确可以通过输出 sizeof(BITMAPFILEHEADER) 来得到。

事实上这些机构体在wingdi.h文件中也有定义,如果使用windows也可以直接使用gdi中定义的结构。

定义好这些结构体之后就是编写读取位图的函数了,基本方法就是通过fread来从文件读取指定大小,指定个数的元素,比如下面这条语句用于读取信息头:

fread(infoHead, sizeof(BITMAPINFOHEADER), 1, fp)

从fp文件流中读取14字节的元素一次,并存放到infoHead指针所指向的内存区域,infoHead是BITMAPINFOHEADER类型的结构指针,该内存可以通过malloc在堆中动态分配也可以直接定义相应变量。

下面贴上用到的源码,原谅我没有写多少注释:

#define _CRT_SECURE_NO_DEPRECATE
#include"bmp.h"
#include<string.h>
#include<stdlib.h>
#include<stdio.h>FILE* openBmpImage(char* fileName, char* mode) {FILE* fp;if (strcmp(mode, "r") == 0) {mode = "rb";}else if (strcmp(mode,"w") == 0) {mode = "ab";}else {//输出错误信息fprintf(stderr,"文件打开模式:%s使用错误\n",mode);//文件打开失败,返回空指针return NULL;}if ((fp = fopen(fileName,mode)) == NULL) {fprintf(stderr, "打开文件:%s失败\n", fileName);return NULL; }return fp;
}void closeBmpImage(FILE* fp) {//关闭文件fclose(fp);//printf("已关闭文件\n");//释放文件指针free(fp);//printf("已释放文件指针\n");
}BITMAPFILEHEADER* readBmpFileHead(FILE* fp) {//printf("%d\n", sizeof(BITMAPFILEHEADER));//这个大小是16Bytes没错BITMAPFILEHEADER* fileHead = (BITMAPFILEHEADER*)malloc(sizeof(BITMAPFILEHEADER));if (fileHead == NULL) {fprintf(stderr,"内存分配失败");}if (fread(fileHead, sizeof(BITMAPFILEHEADER), 1, fp) != 1) {fprintf(stderr,"文件头读取失败");}return fileHead;
}BITMAPINFOHEADER* readBmpInfoHead(FILE* fp) {//printf("%d\n", sizeof(BITMAPINFOHEADER));BITMAPINFOHEADER* infoHead = (BITMAPINFOHEADER*)malloc(sizeof(BITMAPINFOHEADER));if (infoHead == NULL) {fprintf(stderr, "内存分配失败");}if (fread(infoHead, sizeof(BITMAPINFOHEADER), 1, fp) != 1) {fprintf(stderr, "信息头读取失败");}printf("信息头大小:%d字节\n", infoHead->bHeaderSize);printf("图片宽度:%d像素\n", infoHead->bImageWidth);printf("图片高度:%d像素\n", infoHead->bImageHeight);printf("颜色位数:%d位\n", infoHead->bBitsPerPixel);printf("横向每米像素数:%d个\n", infoHead->bXpixelsPerMeter);printf("纵向每米像素数:%d个\n", infoHead->bYpixelsPerMeter);printf("数据块大小:%d字节\n", infoHead->bImageSize);printf("位面数:%d\n", infoHead->bPlanes);printf("使用颜色总数:%d个\n", infoHead->bTotalColors);printf("重要颜色总数:%d个\n", infoHead->bImportantColors);printf("压缩算法:%d\n", infoHead->bCompression);return infoHead;
}RGBDATA** readBmpDataToArr(FILE* fp) {int i = 0, j = 0;int width = 0, height = 0;BITMAPFILEHEADER* fileHead = readBmpFileHead(fp);BITMAPINFOHEADER* infoHead = readBmpInfoHead(fp);width = infoHead->bImageWidth;height = infoHead->bImageHeight;RGBDATA** data = createMatrix(width,height);//如果位数小于8则调色板有效if (infoHead->bBitsPerPixel < 8) {RGBQUAD* rgbQuad = (RGBQUAD*)malloc(sizeof(RGBQUAD));if(rgbQuad == NULL){printf("内存分配失败");}if (fread(rgbQuad, sizeof(rgbQuad), 1, fp) != 1) {printf("调色板读入失败");}}for (i = 0; i < height; i++) {for (j = 0; j < width; j++) {fread(&data[i][j], sizeof(RGBDATA), 1, fp);}}return data;
}RGBDATA** createMatrix(int width,int height) {//动态创建二维数组RGBDATA** Matrix;int i;Matrix = (RGBDATA **)malloc(sizeof(RGBDATA*) * height);if (Matrix == NULL) {fprintf(stderr,"内存分配失败");return NULL;}for (i = 0; i < height; i++) {Matrix[i] = (RGBDATA *)malloc(sizeof(RGBDATA) * width);if (Matrix[i] == NULL) {fprintf(stderr, "内存分配失败");return NULL;}}return(Matrix);
}

没有太多内容值得细说,后面是测试代码:

#include<stdio.h>
#include"bmp.h"
int main() {//printf("%d",sizeof(unsigned short));//printf("%d",sizeof(unsigned int));//printf("%d",sizeof(unsigned long));//printf("%d",sizeof(unsigned char));FILE* fp = openBmpImage("lena.bmp","r");//BITMAPFILEHEADER* fileHead = readBmpFileHead(fp);//BITMAPINFOHEADER* infoHead = readBmpInfoHead(fp);RGBDATA ** data = readBmpDataToArr(fp);//谨慎,避免下标越界for (int i = 0; i < 512; i++) {for (int j = 0; j < 512; j++) {printf("第(%d,%d)像素:[%d,%d,%d] \n ", 511-i,j+1,data[i][j].blue, data[i][j].green, data[i][j].red);}printf("\n");}closeBmpImage(fp);getchar();return 0;
}

运行之后可以得到这样的结果:

注意每个像素得到的三个数值分别代表b,g,r分量,和习惯的rgb是反着的。

最后要注意,大多数位图的像素信息是自下而上的,也就是说代码中得到的数组的最后一行对应图片的第一行,以此类推。。

位图读取就是这样了,下一步再考虑写入bmp文件和像素数组的处理操作。。。

读取bmp格式位图文件到二维数组(C语言)相关推荐

  1. node + ts读取csv文件为二维数组

    node + TypeScript读取csv文件为二维数组 CSDN用户名:jcLee95 邮箱:291148484@163.com 原创不易,感谢点赞支持. 在数据分析的时候经常需要读取csv格式的 ...

  2. 使用fstream在C++工程中读取文件到二维数组

    首先为类CCalculateCenterDlg定义一个较大的二维数组成员m_DataArry[100][100],实际读取数据中存放在该数组中,但是肯定用不完该数组的所有空间,实际使用的数组长度和宽度 ...

  3. c语言 复制二维数组,C语言 二维数组复制、清零及打印显示(示例代码)

    #include #include#include //二维整型数组打印显示 void printarr2d(int (*a)[3],int row,intcol) {inti,j;for(i=0; ...

  4. c语言动态生成二维数组,C语言 动态创建二维数组

    /*C语言 如何动态创建二维数组 转化为一维数组申请数组,创建和释放都比较简单 */ #include #include #include #define RANK 10 #define COLUMN ...

  5. c语言的整形二维数组,C语言入门 — 数组,二维数组

    1.C语言入门 - 数组,结合上一篇文章<C语言入门 - 数组,一维数组>, 我们这里讲如何使用二维数组, 二维数组可以理解成多个一维数组组成的数组, 比如定义两个a[10]的数组可以这样 ...

  6. 二维数组 C语言优先级记忆Tips

    引入: 一维数组的定义: 动态创建一维数组 :int * arr=(int * )malloc(10* sizeof(int)); 1.二维数组的定义: 如创建3行4列的数组 第一种方法: int m ...

  7. c语言N*N的二维数组,c语言高手帮个忙(请先看问题,好解答

    程序定义了N×N的二维数组,并在主函数中自动赋值.请编写函数fun(int a[ ][ N ],int n),该函数的功能是使数组左下半三角元素中的值加上n.例如:若n的值为3,a数组中的值为a=2 ...

  8. 二维数组c语言矩阵加法,C 语言实例 – 两个矩阵相加 - C 语言基础教程

    C 语言实例 使用多维数组将两个矩阵相加. #include int main(){ int r, c, a[100][100], b[100][100], sum[100][100], i, j; ...

  9. 顺时针打印二维数组C语言递归,按顺时针打印矩阵

    存在二种解题思路: 一种是递归解法,一种是层层递进解法 图解递归解法 如图所示, 一个5*5的矩阵 先打印最外层的圈, 然后剩余最里层3*3的矩阵, 如图. 将3*3的矩阵继续打印最外层,思路与打印最 ...

最新文章

  1. Webpack 4 学习09(打包生成html)
  2. android报错res\drawable-xhdpi\ic_Dianhua.png: Invalid file name: must contain only [a-z0-9_.]
  3. 《Hadoop大明白》【1】Hadoop的核心组件
  4. 前端学习(2241):react打卡倒计时十五天之react出现背景
  5. dropdownlist三级联动怎么实现_简单三步,轻松搞定一级、二级、三级下拉菜单
  6. 云计算-大数据-云安全高等教育改革示范教材
  7. PHP设计模式——备忘录模式
  8. 计算机专业寒假打工大一,大一学生寒假打工心得3篇
  9. C++头文件和cpp文件的原理
  10. 详解物理学四大神兽————麦克斯韦妖
  11. ZJYYC LSH去年买了个表(也是LZY去年买了个表) DFS
  12. LINGO进行灵敏度分析为何总提醒92/237错误
  13. Vue学习笔记(一) 基础+指令+侦听器+计算属性+vue-cli
  14. 递归--组合数计算--委员会问题--抽人问题
  15. Lucene.Net的简单练习
  16. The following entrypoint(s) combined asset size exceeds the recommended limit (244 KiB).
  17. 师范大学计算机专业研究生,2019考研调剂:福建师范大学计算机类学硕研究生调剂生...
  18. JavaScript:实现Sudoku数独游戏算法(附完整源码)
  19. 这是你的新计算机房吗英语咋写,2017年pep四年级下册英语作业题
  20. 先转行从零基础入门学编程可以吗?

热门文章

  1. 查看笔记本电脑相应电池参数
  2. Video_based_ReID_01
  3. c++中istringstream及ostringstream超详细说明
  4. 解决所有浏览器被“hao 123”拦截的终极大法,试过很多很多方法都不管用,最终这个管用。
  5. 关于这半年,我在干些什么,有一些想和你分享的话题
  6. 《北漂的京城巡回礼》
  7. [kali]--攻击浏览器漏洞
  8. 勒索软件利用IE漏洞挂马传播,腾讯零信任iOA、腾讯电脑管家支持检测拦截
  9. 联诚发led屏助力深圳文博会打造光显科技盛宴!
  10. shell 脚本之 echo