一步一步使用标c编写跨平台图像处理库
这一系列的文章都将以BMP格式图像为主做处理,主要是为了让大家能对图像处理有一个良好的认知,后面会教大家写一个能支持BMP,JPG,JPEG,PNG等格式的图像处理库。
首先你要对图像处理以及图像理论有一个基本的认识,如果没有任何基本的图像知识的话我建议先看一下博主的这篇文章:Opencv学习笔记_计算机视觉是什么?Opencv的起源 虽然说这篇文章是介绍计算机视觉和opencv的但是其中包含了很多图像理论知识。
首先第一步要对BMP图像文件格式要有一个基本的认识:
BMP图像格式详解
数据段名称 | 大小(byte) | 开始地址 | 结束地址 |
位图文件头(bitmap-file header)
|
14 | 0000h | 000Dh |
位图信息头(bitmap-information header)
|
40 | 000Eh | 0035h |
调色板(color table)
|
由biBitCount决定 | 0036h | 未知 |
图片点阵数据(bitmap data)
|
由图片大小和颜色定 | 未知 | 未知 |
变量名 | 地址偏移 | 大小 | 作用说明 |
bfType | 0000h | 2Bytes |
文件标识符,必须为"BM",即0x424D 才是Windows位图文件
‘BM’:Windows 3.1x, 95, NT,… ‘BA’:OS/2 Bitmap Array ‘CI’:OS/2 Color Icon
‘CP’:OS/2 Color Pointer ‘IC’:OS/2 Icon
‘PT’:OS/2 Pointer
因为OS/2系统并没有被普及开,所以在编程时,你只需判断第一个标识“BM”就行
|
bfSize | 0002h | 4Bytes | 整个BMP文件的大小(以位B为单位) |
bfReserved1 | 0006h | 2Bytes | 保留,必须设置为0 |
bfReserved2 | 0008h | 2Bytes | 保留,必须设置为0 |
bfOffBits | 000Ah | 4Bytes | 说明从文件头0000h开始到图像像素数据的字节偏移量(以字节Bytes为单位),以为位图的调色板长度根据位图格式不同而变化,可以用这个偏移量快速从文件中读取图像数据 |
变量名
|
地址偏移
|
大小
|
作用说明
|
biSize
|
000Eh
|
4Bytes
|
BNP信息头即BMP_INFOHEADER结构体所需要的字节数(以字节为单位)
|
biWidth
|
0012h
|
4Bytes
|
说明图像的宽度(以像素为单位)
|
biHeight
|
0016h
|
4Bytes
|
说明图像的高度(以像素为单位)。这个值还有一个用处,指明图像是正向的位图还是倒向的位图,该值是正数说明图像是倒向的即图像存储是由下到上;该值是负数说明图像是倒向的即图像存储是由上到下。大多数BMP位图是倒向的位图,所以此值是正值。
|
biPlanes
|
001Ah
|
2Bytes
|
为目标设备说明位面数,其值总设置为1
|
biBitCount
|
001Ch
|
2Bytes
|
说明一个像素点占几位(以比特位/像素位单位),其值可为1,4,8,16,24或32
|
biCompression
|
001Eh
|
4Bytes
|
说明图像数据的压缩类型,取值范围为:
0 BI_RGB 不压缩(最常用)
1 BI_RLE8 8比特游程编码(BLE),只用于8位位图
2 BI_RLE4 4比特游程编码(BLE),只用于4位位图
3 BI_BITFIELDS比特域(BLE),只用于16/32位位图
4
|
biSizeImage
|
0022h
|
4Bytes
|
说明图像的大小,以字节为单位。当用BI_RGB格式时,总设置为0
|
biXPelsPerMeter
|
0026h
|
4Bytes
|
说明水平分辨率,用像素/米表示,有符号整数
|
biYPelsPerMeter
|
002Ah
|
4Bytes
|
说明垂直分辨率,用像素/米表示,有符号整数
|
biClrUsed | 002Eh | 4Bytes | 说明位图实际使用的调色板索引数,0:使用所有的调色板索引 |
biClrImportant | 0032h | 4Bytes | 说明对图像显示有重要影响的颜色索引的数目,如果是0,表示都重要。 |
通过上面的描述,我们基本上对BMP图像文件格式有一个基本的认识了,那么我们可以通过c语言的结构体的内存对齐机制来依次读取这些头文件信息,并转化成可识别的十进制信息!
首先第一步,对c语言的基本类型定义别名,方便维护。
//8bit
typedef char UINT_8;
//16bit
typedef char UINT_16[2];
//22bit
typedef char UINT_22[3];
//256bit
typedef char UINT_256[256];
//32bit
typedef int UINT_32;
//data
typedef char* DATA;
//64 fbit
typedef double FUINT_64;
//16bit int
typedef short SUINT_16;
然后根据头信息在文件中的字节偏移地址来定义结构体:
变量名 | 地址偏移 | 大小 | 作用说明 |
bfType | 0000h | 2Bytes |
文件标识符,必须为"BM",即0x424D 才是Windows位图文件
‘BM’:Windows 3.1x, 95, NT,… ‘BA’:OS/2 Bitmap Array ‘CI’:OS/2 Color Icon
‘CP’:OS/2 Color Pointer ‘IC’:OS/2 Icon
‘PT’:OS/2 Pointer
因为OS/2系统并没有被普及开,所以在编程时,你只需判断第一个标识“BM”就行
|
bfSize | 0002h | 4Bytes | 整个BMP文件的大小(以位B为单位) |
bfReserved1 | 0006h | 2Bytes | 保留,必须设置为0 |
bfReserved2 | 0008h | 2Bytes | 保留,必须设置为0 |
bfOffBits | 000Ah | 4Bytes | 说明从文件头0000h开始到图像像素数据的字节偏移量(以字节Bytes为单位),以为位图的调色板长度根据位图格式不同而变化,可以用这个偏移量快速从文件中读取图像数据 |
变量名
|
地址偏移
|
大小
|
作用说明
|
biSize
|
000Eh
|
4Bytes
|
BNP信息头即BMP_INFOHEADER结构体所需要的字节数(以字节为单位)
|
biWidth
|
0012h
|
4Bytes
|
说明图像的宽度(以像素为单位)
|
biHeight
|
0016h
|
4Bytes
|
说明图像的高度(以像素为单位)。这个值还有一个用处,指明图像是正向的位图还是倒向的位图,该值是正数说明图像是倒向的即图像存储是由下到上;该值是负数说明图像是倒向的即图像存储是由上到下。大多数BMP位图是倒向的位图,所以此值是正值。
|
biPlanes
|
001Ah
|
2Bytes
|
为目标设备说明位面数,其值总设置为1
|
biBitCount
|
001Ch
|
2Bytes
|
说明一个像素点占几位(以比特位/像素位单位),其值可为1,4,8,16,24或32
|
biCompression
|
001Eh
|
4Bytes
|
说明图像数据的压缩类型,取值范围为:
0 BI_RGB 不压缩(最常用)
1 BI_RLE8 8比特游程编码(BLE),只用于8位位图
2 BI_RLE4 4比特游程编码(BLE),只用于4位位图
3 BI_BITFIELDS比特域(BLE),只用于16/32位位图
4
|
biSizeImage
|
0022h
|
4Bytes
|
说明图像的大小,以字节为单位。当用BI_RGB格式时,总设置为0
|
biXPelsPerMeter
|
0026h
|
4Bytes
|
说明水平分辨率,用像素/米表示,有符号整数
|
biYPelsPerMeter
|
002Ah
|
4Bytes
|
说明垂直分辨率,用像素/米表示,有符号整数
|
biClrUsed | 002Eh | 4Bytes | 说明位图实际使用的调色板索引数,0:使用所有的调色板索引 |
biClrImportant | 0032h | 4Bytes | 说明对图像显示有重要影响的颜色索引的数目,如果是0,表示都重要。 |
//结构体
//图像结构体,防止结构体对齐否则读取文件流时出错,所以用pragma命令强制对齐
#pragma pack(1)
typedef struct image_struct{UINT_16 image_pil; //文件标识符 UINT_32 image_Size; //文件大小 UINT_16 image_Reserved1; //无需过问 UINT_16 image_Reserved2; //无需过问 UINT_32 image_Offbits; //头文件到图像数据偏移量 UINT_32 image_Stsize; //结构体所需大小 UINT_32 image_Width; //图像宽度 UINT_32 image_Height; //图像高度 UINT_16 image_Planes; //目标设备位面数 SUINT_16 image_Bitcount; //像素点占用多少bit位 UINT_32 image_Compression; //图像压缩类型 UINT_32 image_Sizeimage; //图像的大小 UINT_32 image_Xpelspermeter; //水平分辨率 UINT_32 image_Ypelspermeter; //垂直分辨率 UINT_32 image_ClrUsed; //调色板索引数 UINT_32 image_Clrlmportant; //图像显示重要颜色索引数目 DATA image_Data; //图像数据 UINT_32 image_Data_Size; //图像数据大小
}image;
#pragma pack()
//函数
//加载图像
int image_load(struct image_struct** im, char *path){FILE *image_path_fp;image_path_fp = fopen(path, "rb");if (image_path_fp == NULL){return -1;}//取文件大小fseek(image_path_fp, 0, SEEK_END); //定位到文件末 int nFileLen = ftell(image_path_fp); //文件长度fseek(image_path_fp, 0, SEEK_SET); //恢复到文件头,防止从文件尾读取数据//读取头信息if (fread((*im), (sizeof(struct image_struct) - ((sizeof(DATA/*image_Data*/)+(sizeof(UINT_32)/*image_Data_Size*/)))/*暂不读取数据,无法从头文件中获取数据偏移量,防止数据混乱*/), 1, image_path_fp) == 0){return -2;}//给data变量分配内存(*im)->image_Data = (DATA)malloc(nFileLen-(*im)->image_Offbits/*完整的数据大小*/);//判断是否分配成功if ((*im)->image_Data == NULL){ //如果没有可用堆内存则malloc返回NULLreturn -3;}//读取数据//读取前将文件指针挪移到文件头信息后,找到正确的数据存储区fseek(image_path_fp, 0, SEEK_SET); //恢复到文件头,因为已经fread一次了,所以数据文件指针发生变更fseek(image_path_fp, (*im)->image_Offbits, SEEK_CUR); //忽略头数据if (fread((*im)->image_Data, (nFileLen - (*im)->image_Offbits/*file - off = 实际大小*/), 1, image_path_fp) == 0){return -4;}//保存文件大小,方便读写操作(*im)->image_Data_Size = (nFileLen - (*im)->image_Offbits/*file - off = 实际大小*/);//文件指针释放,防止占用文件内核的临界资源fclose(image_path_fp);image_path_fp == NULL;return 0;
}
//给图像数据分配内存
int image_malloc(struct image_struct** im){*im = (struct image_struct*)malloc(sizeof(struct image_struct));if (*im == NULL){return -1;}return 0;
}
//保存图像数据到文件
int image_save_file(struct image_struct** im, char *path){FILE* file_fp = fopen(path, "wb+"); //以二进制可读写方式打开if (file_fp == NULL){ //判断文件指针是否为空return -1;}//写入头信息fwrite((*im), (*im)->image_Offbits/*直接写入头文件到数据的偏移量大小即可*/, 1, file_fp);//写入文件数据fwrite((*im)->image_Data, (*im)->image_Data_Size, 1, file_fp);return 0;}
int main(){image *imga;image_malloc(&imga);image_load(&imga, "test.bmp");printf("图像宽度:%d\n", imga->image_Width); printf("图像高度:%d\n", imga->image_Height);printf("图像文件占用字节:%d\n", imga->image_Size);printf("图像每个像素占用bit位:%d\n", imga->image_Bitcount);getchar();
}
/*Robust图像处理库
*版本:v1.0
*作者:周志豪
*4.17 19:09
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//类型定义
//8bit
typedef char UINT_8;
//16bit
typedef char UINT_16[2];
//22bit
typedef char UINT_22[3];
//256bit
typedef char UINT_256[256];
//32bit
typedef int UINT_32;
//data
typedef char* DATA;
//64 fbit
typedef double FUINT_64;
//16bit int
typedef short SUINT_16;
//结构体
//图像结构体,防止结构体对齐否则读取文件流时出错,所以用pragma命令强制对齐
#pragma pack(1)
typedef struct image_struct{UINT_16 image_pil; //文件标识符 UINT_32 image_Size; //文件大小 UINT_16 image_Reserved1; //无需过问 UINT_16 image_Reserved2; //无需过问 UINT_32 image_Offbits; //头文件到图像数据偏移量 UINT_32 image_Stsize; //结构体所需大小 UINT_32 image_Width; //图像宽度 UINT_32 image_Height; //图像高度 UINT_16 image_Planes; //目标设备位面数 SUINT_16 image_Bitcount; //像素点占用多少bit位 UINT_32 image_Compression; //图像压缩类型 UINT_32 image_Sizeimage; //图像的大小 UINT_32 image_Xpelspermeter; //水平分辨率 UINT_32 image_Ypelspermeter; //垂直分辨率 UINT_32 image_ClrUsed; //调色板索引数 UINT_32 image_Clrlmportant; //图像显示重要颜色索引数目 DATA image_Data; //图像数据 UINT_32 image_Data_Size; //图像数据大小
}image;
#pragma pack()
//函数
//加载图像
int image_load(struct image_struct** im, char *path){FILE *image_path_fp;image_path_fp = fopen(path, "rb");if (image_path_fp == NULL){return -1;}//取文件大小fseek(image_path_fp, 0, SEEK_END); //定位到文件末 int nFileLen = ftell(image_path_fp); //文件长度fseek(image_path_fp, 0, SEEK_SET); //恢复到文件头,防止从文件尾读取数据//读取头信息if (fread((*im), (sizeof(struct image_struct) - ((sizeof(DATA/*image_Data*/)+(sizeof(UINT_32)/*image_Data_Size*/)))/*暂不读取数据,无法从头文件中获取数据偏移量,防止数据混乱*/), 1, image_path_fp) == 0){return -2;}//给data变量分配内存(*im)->image_Data = (DATA)malloc(nFileLen-(*im)->image_Offbits/*完整的数据大小*/);//判断是否分配成功if ((*im)->image_Data == NULL){ //如果没有可用堆内存则malloc返回NULLreturn -3;}//读取数据//读取前将文件指针挪移到文件头信息后,找到正确的数据存储区fseek(image_path_fp, 0, SEEK_SET); //恢复到文件头,因为已经fread一次了,所以数据文件指针发生变更fseek(image_path_fp, (*im)->image_Offbits, SEEK_CUR); //忽略头数据if (fread((*im)->image_Data, (nFileLen - (*im)->image_Offbits/*file - off = 实际大小*/), 1, image_path_fp) == 0){return -4;}//保存文件大小,方便读写操作(*im)->image_Data_Size = (nFileLen - (*im)->image_Offbits/*file - off = 实际大小*/);//文件指针释放,防止占用文件内核的临界资源fclose(image_path_fp);image_path_fp == NULL;return 0;
}
//给图像数据分配内存
int image_malloc(struct image_struct** im){*im = (struct image_struct*)malloc(sizeof(struct image_struct));if (*im == NULL){return -1;}return 0;
}
//将图像转换成反向图_该方法只能用于真彩图
int image_reverse_rgb(struct image_struct** im){if ((*im) == NULL){ //判断传递进来的图像指针是否为空return -1;}//转换成反向图很简单只需要将每个图像里的像素点转换成负的就可以了,注意在一个24位的图像文件中一个字节对应一个颜色值三个字节则为一个完整的像素点,所以我们一个一个像素点的转换就可以了//算法公式为:S=-R-G-Bfor (int i = 0; i < (*im)->image_Data_Size; ++i){if ((*im)->image_Data[i] == (int)0){ //如果等于0则不处理continue; //开始下一次循环}//i*(*im)->image_Width + j(*im)->image_Data[i] = -(*im)->image_Data[i]; //调用宏函数转换}return 0;
}
//保存图像数据到文件
int image_save_file(struct image_struct** im, char *path){FILE* file_fp = fopen(path, "wb+"); //以二进制可读写方式打开if (file_fp == NULL){ //判断文件指针是否为空return -1;}//写入头信息fwrite((*im), (*im)->image_Offbits/*直接写入头文件到数据的偏移量大小即可*/, 1, file_fp);//写入文件数据fwrite((*im)->image_Data, (*im)->image_Data_Size, 1, file_fp);return 0;}
int main(){image *imga;image_malloc(&imga);image_load(&imga, "test.bmp");printf("图像宽度:%d\n", imga->image_Width); printf("图像高度:%d\n", imga->image_Height);printf("图像文件占用字节:%d\n", imga->image_Size);printf("图像每个像素占用bit位:%d\n", imga->image_Bitcount);getchar();
}
一步一步使用标c编写跨平台图像处理库相关推荐
- 一步一步使用标c编写跨平台图像处理库_让一个图像变成反向图像
接着上一章 一步一步使用标c编写跨平台图像处理库,本章将介绍如何让一个图像变成反向图,其原理非常简单,只需要让每个像素点上的RGB三色取反即可!变成负数! 其公式为: s = -r-g-b 转换方式: ...
- [教程]JS从糊涂到明白:一步一步编写计算器2 – 简化代码
[文章原始发表:This Is WWW : http://www.plrsoft.cn/blog/?p=69 转载请注明出处] 我在上一篇文章"一步一步编写计算器 – 构建和兼容&qu ...
- 一步一步实现STM32-FOTA系列教程之Bootloader编写
一步一步实现STM32-FOTA系列教程之Bootloader编写 文章系列链接 <一步一步实现STM32-FOTA系列教程之bin文件生成> <一步一步实现STM32-FOTA系列 ...
- 【深度学习基础】一步一步讲解卷积神经网络
点击上方"小白学视觉",选择加"星标"或"置顶" 重磅干货,第一时间送 本文转自:一步一步讲解卷积神经网络 卷积神经网络(Convoluti ...
- 一步一步学Silverlight 2系列(10):使用用户控件
概述 Silverlight 2 Beta 1版本发布了,无论从Runtime还是Tools都给我们带来了很多的惊喜,如支持框架语言Visual Basic, Visual C#, IronRuby, ...
- 通过脚本案例学习shell(五) 通过创建DNS脚本一步一步教你将一个普通脚本规范到一个生产环境脚本...
通过脚本案例学习shell(五) 通过创建DNS脚本一步一步教你将一个普通脚本规范到一个生产环境脚本 版权声明: 本文遵循"署名非商业性使用相同方式共享 2.5 中国大陆"协议 ...
- 一步一步学Silverlight 2系列(24):与浏览器交互相关辅助方法
概述 Silverlight 2 Beta 1版本发布了,无论从Runtime还是Tools都给我们带来了很多的惊喜,如支持框架语言Visual Basic, Visual C#, IronRuby, ...
- 一步一步写算法(之prim算法 中)
原文:一步一步写算法(之prim算法 中) [ 声明:版权所有,欢迎转载,请勿用于商业用途. 联系信箱:feixiaoxing @163.com] C)编写最小生成树,涉及创建.挑选和添加过程 MI ...
- Python之美[从菜鸟到高手]--一步一步动手给Python写扩展(异常处理和引用计数)
我们将继续一步一步动手给Python写扩展,通过上一篇我们学习了如何写扩展,本篇将介绍一些高级话题,如异常,引用计数问题等.强烈建议先看上一篇,Python之美[从菜鸟到高手]--一步一步动手给Pyt ...
最新文章
- 基于html5海贼王单页视差滚动特效
- 使用FFT来计算IFFT
- 学习编写用例是开发者走向项目经理的必经之路(《编写有效用例》书评) ——“Jolt大奖精选丛书”有奖征文...
- 洛谷1522牛的旅行
- 《Exchange Server 2010 SP1/SP2管理实践》一2.2 部署域名解析网络环境
- 海信电视服务器暂时不可用,一线也实惠 微星H61M-E35(B3)主板评测
- 实战演练:PostgreSQL在线扩容
- 概要设计 英文_JavaScript 中的位运算和权限设计
- 装配图中齿轮的画法_机械制图如何从入门到精通,金属结构件的表达画法,你会吗?...
- iOS Socket 客户端 基本使用
- 文化学刊杂志文化学刊杂志社文化学刊编辑部2022年第3期目录
- 计算机网络ppt背景,教大家使用ppt设计出高逼格的背景图
- mysql_stmt_precheck_COM_STMT_PREPARE 1
- Python向已有数据的Excel表写入数据
- 回顾过去。。展望未来
- wlop作品集_【图包】【wlop】作品合集
- 微软面试题:红帽子与黑帽子
- 毕节职业技术学院计算机网络技术专业,毕节职业技术学院计算机网络技术9.顶岗实习安排...
- VTM1.0代码阅读:CU、PU、TU
- InetAddress类常用方法
热门文章
- mysql主从复制原理详解_深入研究MySQL(三)、主从复制原理及演示
- xilinx set up debug
- gitee创建ssh公钥
- str python3_python3.4.3如何转换str字符串?
- python读写excel模块pandas_python3 基于pandas读写Excel
- springMVC+hibernate + layui分页
- object转换成Integer
- dist包编译html_npm package开发指南-包内容篇
- py2exe打包python_Python打包-py2exe使用
- python3d绘图代码_python机器学习之3D Matplotlib绘图