当前位置: 首页 > news >正文

文件IO——bmp图像处理

目录

  • 前言
  • 一、BMP图片原理
    • (1)位图文件头
    • (2)位图信息头
    • (3)调色板
    • (4)位图数据
      • **对齐说明**
      • 存储顺序
      • 像素结构
  • 二、练习
    • 1.生成一个新的图片,将图片等比例缩小为原来的1/2
      • 代码实现
    • 2.生成一个新图片,只显示左半部分
      • 代码实现

前言

学文件IO不仅仅只学文件打开关闭,读写操作,还要懂的一些常用的图片格式的文件存储结构。这对我们后期学习图像处理有很大的帮助。

在这里插入图片描述

一、BMP图片原理

(1)位图文件头

位图文件头(Bitmap-File Header)包含了图像类型、图像大小、两个保留字以及位图数据存放地址。
在这里插入图片描述

(2)位图信息头

位图信息头(Bitmap-Information Header)包含了位图信息头的大小、图像的宽和高、图像的色深、压缩说明、图像数据的大小和其他一些参数。
在这里插入图片描述

(3)调色板

bmp位图按照像素深度分类可以分为:1bit位图(2色)、4bit位图(16色)、8bit位图(256色)、16bit位图(65536色-高彩色)、24bit位图(1670万色-真彩色)、32bit位图(1670万色-增强型真彩色)。

调色板是为了让一些颜色深度比较小(1bit、4bit、8bit)的位图可以表示颜色而设置的。调色板存储颜色,后面的位图数据存储颜色索引,这样调色板+位图数据就可以表示颜色了。

需要注意的是:16bit、24bit、32bit的位图一般没有调色板,因为从16bit开始就直接使用位图数据表示颜色了。

在这里插入图片描述

(4)位图数据

位图数据分两种情况,如果带调色板,则位图数据存放的是调色板的颜色索引,如果不带调色板,则位图数据存放实际的argb值。由于24位bmp图片不带调色板,所以文件开头的54字节为图片信息,从第55个字节开始就为bmp图片的颜色数据。

对齐说明

在这里插入图片描述

计算机的CPU为了提高工作效率,所以读取数据的时候会进行字节对齐,一般图像刷新都是行为单位,所以CPU每次都是读取一行的大小,所以32bit系统下如果一行字节大小不是4的倍数,就会进行字节补齐,目的是提高效率,是典型的用空间来换时间的案例!

分辨率是10*10,每行10个像素点,每个像素点3字节,所以一行是30个字节,不够4的倍数,所以需要在每行的末尾补2个字节。

由于每行必须是4的倍数,所以需要补齐的字节数有4种情况:0、1、2、3,所以需要设计算法来计算每行需要自动补齐的字节数。 公式: byte = ( 4 - (width*3 % 4) ) % 4

存储顺序

如果打算了解BMP图片内部数据的存储顺序(大端 or 小端),应该去查看BMP文件内部数据结构,需要使用专业工具(HEX十六进制查看器)。
在这里插入图片描述

像素结构

在这里插入图片描述

二、练习

1.生成一个新的图片,将图片等比例缩小为原来的1/2

请添加图片描述

代码实现

/** Copyright (c)* * date: 2025-7-26* * author: Charles* * function name : halfBMP.c** function: 把一张任意尺寸的 BMP 图片等比例且不失真的缩小为原来的 1/2并生成一张新的 BMP 图片*/#include <stdio.h>
#include <stdlib.h>
#include <string.h>// BMP文件信息头(14字节)
typedef struct {char bfType[2];      // 文件类型,必须为0x4D42unsigned int   bfSize;      // 文件大小(字节)unsigned short bfReserved1; // 保留,必须为0unsigned short bfReserved2; // 保留,必须为0unsigned int   bfOffBits;   // 位图数据偏移量
} __attribute__((packed)) BITMAPFILEHEADER;		//__attribute__((packed))防止字节对齐// BMP文件数据头(40字节)
typedef struct {unsigned int   biSize;          // 结构体大小,40字节int            biWidth;         // 图像宽度(像素)int            biHeight;        // 图像高度(像素)unsigned short biPlanes;        // 颜色平面数,必须为1unsigned short biBitCount;      // 每像素位数unsigned int   biCompression;   // 压缩类型unsigned int   biSizeImage;     // 图像数据大小(字节)int            biXPelsPerMeter; // 水平分辨率int            biYPelsPerMeter; // 垂直分辨率unsigned int   biClrUsed;       // 实际使用的颜色数unsigned int   biClrImportant;  // 重要颜色数
} __attribute__((packed)) BITMAPINFOHEADER;		//__attribute__((packed))防止字节对齐int main(int argc, char const *argv[])
{BITMAPFILEHEADER fileheader_src, fileheader_dest;	//源图片的文件头和信息头BITMAPINFOHEADER infoheader_src, infoheader_dest;	//目标图片的文件头和信息头if(argc != 3){		//如果参数不为3就输入错误printf("argument is invailed!\n");exit(-1);}FILE* file_src = fopen(argv[1], "rb");	//以二进制只读的方式打开源图片if(!file_src){		//打开源图片的错误处理printf("file: %s open failed\n",argv[1]);exit(-1);}fread(&fileheader_src, sizeof(fileheader_src), 1, file_src);		//将源图片的文件头和信息头读入源图片文件头和信息头的结构体变量中fread(&infoheader_src, sizeof(infoheader_src), 1, file_src);if(fileheader_src.bfType[0] != 'B' || fileheader_src.bfType[1] != 'M'){		//判断源文件是否为BMP图片printf("file: %s is not BMP\n", argv[1]);fclose(file_src);exit(-1);}printf("file_src %s size is %d byte\n", argv[1], fileheader_src.bfSize);	//输出源图片大小,以及像素宽度和高度printf("file_src %s width is %d px\n", argv[1], infoheader_src.biWidth);printf("file_src %s height is %d px\n", argv[1], infoheader_src.biHeight);fileheader_dest = fileheader_src;		//将源文件文件头和信息头信息赋值给目标图片infoheader_dest = infoheader_src;int new_width = infoheader_src.biWidth / 2;		//目标图片新的像素宽度和高度int new_height = infoheader_src.biHeight / 2;int src_stride = ((infoheader_src.biWidth * 3 + 3) & ~3);	//源图片与目标图片的行对齐字节数int dest_stride = ((new_width * 3 + 3) & ~3);infoheader_dest.biWidth = new_width;		//目标图片新的数据信息infoheader_dest.biHeight = new_height;infoheader_dest.biSizeImage = dest_stride * new_height;fileheader_dest.bfSize = dest_stride * new_height + 54;fileheader_dest.bfOffBits = 54;FILE* file_dest = fopen(argv[2], "wb");		//以二进制写的方式打开目标图片,如果不存在该图片就创建if(!file_dest){printf("file: %s open failed\n",argv[2]);fclose(file_src);exit(-1);}fwrite(&fileheader_dest, sizeof(fileheader_dest), 1, file_dest);		//将目标图片结构体信息写入目标图片中fwrite(&infoheader_dest, sizeof(infoheader_dest), 1, file_dest);unsigned char* src_line = (unsigned char*)malloc(src_stride);		//存储原图片与目标图片每行的数据缓冲区unsigned char* dest_line = (unsigned char*)malloc(dest_stride);if(!src_line || !dest_line){		//分配内存错误处理printf("malloc is failed!\n");fclose(file_src);fclose(file_dest);exit(-1);}fseek(file_src, fileheader_src.bfOffBits, SEEK_SET);for(int y = 0; y < new_height; y++){fread(src_line, 1, src_stride, file_src);		//读源图片每行的像素点BGR,BMP图片是以小端的形式存储if(y < infoheader_src.biHeight)fseek(file_src, src_stride, SEEK_CUR);			//每读一行隔一行for(int x = 0; x < new_width; x++){		int src_index = x * 3 * 2;	//因为目标图片缩小为源图片1/2, 所以这里2倍偏移int dest_index = x * 3;dest_line[dest_index + 0] = src_line[src_index + 0];	//Bdest_line[dest_index + 1] = src_line[src_index + 1];	//Gdest_line[dest_index + 2] = src_line[src_index + 2];	//R}fwrite(dest_line, 1, dest_stride, file_dest);	//将像素点写入目标图片中memset(dest_line, 0, dest_stride);	//清空数据}printf("file_dest %s size is %d byte\n", argv[2], fileheader_dest.bfSize);printf("file_dest %s width is %d px\n", argv[2], infoheader_dest.biWidth);printf("file_dest %s height is %d px\n", argv[2], infoheader_dest.biHeight);free(src_line);free(dest_line);fclose(file_src);fclose(file_dest);return 0;
}

2.生成一个新图片,只显示左半部分

代码实现

/** Copyright (c)* * date: 2025-7-26* * author: Charles* * function name : halfBMP.c** function: 把一张任意尺寸的 BMP 图片生成一张只显示原来图片的左半部分*/#include <stdio.h>
#include <stdlib.h>
#include <string.h>// BMP文件信息头(14字节)
typedef struct {char bfType[2];      // 文件类型,必须为0x4D42unsigned int   bfSize;      // 文件大小(字节)unsigned short bfReserved1; // 保留,必须为0unsigned short bfReserved2; // 保留,必须为0unsigned int   bfOffBits;   // 位图数据偏移量
} __attribute__((packed)) BITMAPFILEHEADER;     //__attribute__((packed))防止字节对齐// BMP文件数据头(40字节)
typedef struct {unsigned int   biSize;          // 结构体大小,40字节int            biWidth;         // 图像宽度(像素)int            biHeight;        // 图像高度(像素)unsigned short biPlanes;        // 颜色平面数,必须为1unsigned short biBitCount;      // 每像素位数unsigned int   biCompression;   // 压缩类型unsigned int   biSizeImage;     // 图像数据大小(字节)int            biXPelsPerMeter; // 水平分辨率int            biYPelsPerMeter; // 垂直分辨率unsigned int   biClrUsed;       // 实际使用的颜色数unsigned int   biClrImportant;  // 重要颜色数
} __attribute__((packed)) BITMAPINFOHEADER;     //__attribute__((packed))防止字节对齐int main(int argc, char const *argv[])
{BITMAPFILEHEADER fileheader_src, fileheader_dest;   //源图片的文件头和信息头BITMAPINFOHEADER infoheader_src, infoheader_dest;   //目标图片的文件头和信息头if(argc != 3){      //如果参数不为3就输入错误printf("argument is invailed!\n");exit(-1);}FILE* file_src = fopen(argv[1], "rb");  //以二进制只读的方式打开源图片if(!file_src){      //打开源图片的错误处理printf("file: %s open failed\n",argv[1]);exit(-1);}fread(&fileheader_src, sizeof(fileheader_src), 1, file_src);        //将源图片的文件头和信息头读入源图片文件头和信息头的结构体变量中fread(&infoheader_src, sizeof(infoheader_src), 1, file_src);if(fileheader_src.bfType[0] != 'B' || fileheader_src.bfType[1] != 'M'){     //判断源文件是否为BMP图片printf("file: %s is not BMP\n", argv[1]);fclose(file_src);exit(-1);}printf("file_src %s size is %d byte\n", argv[1], fileheader_src.bfSize);    //输出源图片大小,以及像素宽度和高度printf("file_src %s width is %d px\n", argv[1], infoheader_src.biWidth);printf("file_src %s height is %d px\n", argv[1], infoheader_src.biHeight);fileheader_dest = fileheader_src;       //将源文件文件头和信息头信息赋值给目标图片infoheader_dest = infoheader_src;int new_width = infoheader_src.biWidth / 2;     //目标图片新的像素宽度int height = infoheader_src.biHeight;int src_stride = ((infoheader_src.biWidth * 3 + 3) & ~3);   //源图片与目标图片的行对齐字节数int dest_stride = ((new_width * 3 + 3) & ~3);infoheader_dest.biWidth = new_width;        //目标图片新的数据信息infoheader_dest.biSizeImage = dest_stride * height;fileheader_dest.bfSize = dest_stride * height + 54;fileheader_dest.bfOffBits = 54;FILE* file_dest = fopen(argv[2], "wb");     //以二进制写的方式打开目标图片,如果不存在该图片就创建if(!file_dest){printf("file: %s open failed\n",argv[2]);fclose(file_src);exit(-1);}fwrite(&fileheader_dest, sizeof(fileheader_dest), 1, file_dest);        //将目标图片结构体信息写入目标图片中fwrite(&infoheader_dest, sizeof(infoheader_dest), 1, file_dest);unsigned char* src_line = (unsigned char*)malloc(src_stride);       //存储原图片与目标图片每行的数据缓冲区unsigned char* dest_line = (unsigned char*)malloc(dest_stride);if(!src_line || !dest_line){        //分配内存错误处理printf("malloc is failed!\n");fclose(file_src);fclose(file_dest);exit(-1);}fseek(file_src, fileheader_src.bfOffBits, SEEK_SET);for(int y = 0; y < height; y++){fread(src_line, 1, src_stride, file_src);       //读源图片每行的像素点BGR,BMP图片是以小端的形式存储for(int x = 0; x < new_width; x++){     int src_index = x * 3 ;  //因为目标图片缩小为源图片1/2, 所以这里2倍偏移int dest_index = x * 3;dest_line[dest_index + 0] = src_line[src_index + 0];    //Bdest_line[dest_index + 1] = src_line[src_index + 1];    //Gdest_line[dest_index + 2] = src_line[src_index + 2];    //R}fwrite(dest_line, 1, dest_stride, file_dest);   //将像素点写入目标图片中memset(dest_line, 0, dest_stride);  //清空数据}printf("file_dest %s size is %d byte\n", argv[2], fileheader_dest.bfSize);printf("file_dest %s width is %d px\n", argv[2], infoheader_dest.biWidth);printf("file_dest %s height is %d px\n", argv[2], infoheader_dest.biHeight);free(src_line);free(dest_line);fclose(file_src);fclose(file_dest);return 0;
}

在这里插入图片描述

希望各位靓仔靓女点赞,收藏,关注多多支持,我们共同进步,后续我会更新更多的面试真题,你们的支持将是我前进最大的动力

在这里插入图片描述

http://www.dtcms.com/a/300213.html

相关文章:

  • GRE、MGRE实验
  • 机器学习(重学版)基础篇(概念与评估)
  • 算子推理是什么
  • GStreamer中Pipeline(管道)
  • 速通python加密之base64
  • 《C++》模板详解
  • odoo代码分析(二)
  • 一键实现推送服务:Docker部署ntfy开源通知系统指南
  • STM32-定时器的基本定时/计数功能实现配置教程(寄存器版)
  • 数据结构习题--删除排序数组中的重复项
  • Linux内核设计与实现 - 第13章 虚拟文件系统(VFS)
  • TCP如何解决网络切换问题
  • Flutter开发实战之原生平台集成
  • FROM stakater/java8-alpine 构建cocker镜像
  • React入门学习——指北指南(第三节)
  • 【星野AI】minimax非活动时间充值优惠漏洞
  • 数据存储:OLAP vs OLTP
  • java基础(day16)set-map
  • 从0到1学Pandas(七):Pandas 在机器学习中的应用
  • AI Agent开发学习系列 - langchain之LCEL(5):如何创建一个Agent?
  • React入门学习——指北指南(第五节)
  • java集合框架面试点(2)
  • [2025CVPR-图象合成、生成方向]ODA-GAN:由弱监督学习辅助的正交解耦比对GAN 虚拟免疫组织化学染色
  • 《每日AI-人工智能-编程日报》--2025年7月26日
  • 四大主流AI Agent框架选型梳理
  • 零基础学习性能测试第三章:jmeter性能组件应用(事件,并发,定时器)
  • DriverManager在rt.jar里,凭什么能加载到classpath下的驱动?
  • CPA战略-4.1-公司战略与组织结构
  • 人形机器人_双足行走动力学:弹性势能存储和步态能量回收
  • 聚类里面的一些相关概念介绍阐述