运用C++对文件操作——数字图像处理实验的一个方向

本文介绍了如何使用C++基本函数读取BMP文件,特别是位图文件头的信息。通过理解BMP文件的结构,包括BITMAPFILEHEADER和BITMAPINFOHEADER,以及使用fstream、fopen_s、fseek、fread和fclose等函数,实现了读取BMP文件头的过程。此外,还解释了位图和矢量图的区别,以及调色板在非真彩色图中的作用。
摘要由CSDN通过智能技术生成

前言

这一次数字图像处理实验的内容虽然有OpenCV环境的配置,但是重头戏在于BMP文件的读取,对于编程基础欠佳的同学有一定的挑战性,一开始我也觉得这个任务很难,但是一步步自己查找资料下来,不断地debug也硬是做出来了(笑)。
本次实验有很多种实现方式,在OpenCV库里有直接能够调用的函数,但是为了能深度理解BMP位图在计算机中的存储方式,这里介绍的思路是用C++的基本函数读取BMP文件。

理论准备

图像的文件格式

  • 图像文件格式即图像文件存放的格式
  • 软硬件商提出并广泛采用的格式: BMP、 GIF、 PSD 等
  • 国际标准组织提出的格式: JPEG、 TIFF、 DICOM、 MPEG等
  • BMP文件格式:(包括DIB、 DDB格式) Windows操作系统支持的文件格式。不经过压缩、按位存储。
  1. DIB格式(Device Independent Bitmap):与设备无关,存储未压缩图像,是很多图像处理程序的内 存图像数据格式。
  2. DDB格式(Device Dependent Bitmap) :与设备有关,存储显示和打印设备兼容的未压缩图像。

位图和矢量图的区别

  • 位图图像:由称作像素(图片元素)的单个点组成的,对图像进行缩放会改变图像辨识度。
  • 矢量图形:根据几何特性来绘制图形,矢量可以是一个点或一条线,对图像进行缩放操作不会改变图像辩认度。
    在这里插入图片描述

BMP文件格式

BMP图像文件包括四个部分:

  • 位图文件头(BITMAPFILEHEADER):长度14字节,记录了文件类型、大小、数据起始位置。
  • 位图信息头(BITMAPINFOHEADER):长度40字节,位图宽度、高度,每个像素需要位数,压缩类型(最常用不压缩), 位平面数、颜色索引等信息。
  • 位图调色板(Palette):可选,使用索引来表示图像颜色,调色板可索引与其对应的颜色的映射表。有些位 图需要调色板(如16色, 256色),真彩色(24或32位RGB)不需要调色板。
  • 位图数据(Pixel data):每个像素占一个字节,取得字节后,以该字节为索引查询相应的颜色。真彩色不需要 索引,位图数据就是像素颜色值。

接下来将具体介绍BMP文件四个部分的内容。

位图文件头(BITMAPFILEHEADER)

Offset Member Introduction
0 bfType 位图文件类型,必须为“BM”
2 bfSize 文件大小,单位Byte
6 bfReserved1 保留字节,必须为0
8 bfReserved2 保留字节,必须为0
10 bfOffBits 位图实际数据起始位置

文件头部分所包含的图像信息:

  • bfType:指定文件类型,必须是0x424D,即字符串“BM”,也就是说所有.bmp文件的头两个字节都是“BM”。
  • bfSize:指定文件大小,包括这14个字节。
  • bfReserved1,bfReserved2 :为保留字,不用考虑。
  • bfOffBits:为从文件头到实际的位图数据的偏移字节数,即前三个部分的长度之和。

位图信息头(BITMAPINFOHEADER)

Offset Member Introduction
14 biSize 该结构长度,必须为40
18 biWidth 图像实际宽度,单位是像素
22 biHeight 图像实际高度`,单位是像素
26 biPlanes 必须为1
28 biBiCount 每像素所需bit位数,常用的值是1(黑白图),4(16色图),8(256色图),24(真彩色图)
30 biCompression 非压缩为0
34 biSizeImage 指实际的位图数据所占用的字节,非压缩时可为0
40 biXPelsPerMeter 水平分辨率,pixel/m
44 biYPelsPerMeter 垂直分辨率,pixel/m
48 biClrUsed 实际用到的颜色数,若为0,则用到的颜 色数为2的biBitCount次方种
52 biClrImportant 重要颜色数,0指代所有元素都重要

位图调色板(Palette)

针对非真彩色图,其颜色显示方式并不是直接显示像素块的RGB值,需要一个数组来保存需要使用的颜色,以便于在位图文件中调用,这个保存颜色的数组就是调色板,而真彩色图的位图数据直接显示的是像素块的RGB值因此不需要调色板。

调色板数据结构定义:

typedef struct tagRGBQUAD{
   
    BYTE rgbBlue;
    Byte rgbGreen;
    BYTE rgbRed;
    BYTE rgbReserved;
}RGBQUAD;

调色板数组中的每一个元素占四个字节,分别保存颜色的RGB变量和一个字节的保留值。

位图数据(Pixel data)

对于用到调色板的位图,图像数据就是该像素在调色板中的索引值。对于真彩色图,图像数据就是实际的R、G、B值。

对于2色位图,用1位就可以表示该像素的颜色(一般0表示黑,1表示白),所以一个 字节可以表示8个像素。

对于16色位图,用4位可以表示一个像素的颜色,所以一个字节可以表示2个像素。

对于256色位图,一个字节刚好可以表示1个像素。

对于真彩色图,三个字节才能表示1个像素。

一般来说,BMP文件的数据从下到上,从左到右的。也就是说,从文件中最先读到的是图象最下面一行的左边第一个像素, 然后是左边第二个像素……接下来是倒数第二行左边第一个像素,左边第二个像素……依次类推,最后得到的是最上面一行的最右一个像素。

fstream头文件

头文件fstream包含了ifstream、ofstream、fstream三个类,可以通过定义这三个类的对象来实现相对应的文件操作。

#include <fstream>
ofstream     //文件写操作,内存写入存储设备
ifstream     //文件读操作,存储设备读取到内存中
fstream      //读写操作,对打开的文件可进行读写操作

计算机中的文件是按二进制的形式存储的,它一律把数据看成是字节构成的序列,即字节流,对文件的存取也是以字节为单位的,输入/输出的的数据流仅受程序控制而不受物理符号(如回车换行符)的控制,所以说C语言文件为流式文件。在该实验中,由于需要读取并解析文件数据,因此需要用到FILE,fopen_s,fseek,fread,fclose。

文件类型指针

可以用该结构体类型来定义文件类型的指针变量:

FILE *fp;

FILE是在stdio.h中定义的结构体类型,封装了与文件有关的信息,如文件句柄、位置指针及缓冲区等,缓冲文件系统为每个被使用的文件在内存中开辟一个缓冲区,

用来存放文件的有关信息,这些信息被保存在一个FILE结构类型的变量中,fp是一个指向FILE结构体类型的指针变量。

fopen_s函数

在高版本的visual sdudio中使用fopen会报错,因此这里只介绍fopen_s函数,该函数的功能是将文件打开,并指定使用文件的方式。

fopen_s函数声明如下:

errno_t  fopen_s(
            FILE**      fp,
            char const* _FileName,
            char const* _Mode
            );

该函数有三个参数:指针、文件名、使用文件的方式。

前文说到fp是一个指向FILE结构体类型的指针变量,现在定义的新指针该指针是用来接收fp的指针,即fp的地址。

文件名就是待打开文件的文件名。

如果文件打开成功,则函数返回值为0,若返回了正整数,可以根据errno_t类的定义查询具体的错误。

文件使用方式有十二种,如下表格:

符号 功能
“r” = “rt” 打开一个文本文件,文件必须存在,只允许读
“r+” = “rt+” 打开一个文本文件,文件必须存在,允许读写
“rb” 打开一个二进制文件,文件必须存在,只允许读
“rb+” 打开一个二进制文件,文件必须存在,允许读写
“w” = “wt” 新建一个文本文件,已存在的文件将内容清空,只允许写
“w+” =“wt+” 新建一个文本文件,已存在的文件将内容清空,允许读写
“wb” 新建一个二进制文件,已存在的文件将内容清空,只允许写
“wb+” 新建一个二进制文件,已存在的文件将内容清空,允许读写
“a” = “at” 打开或新建一个文本文件,只允许在文件末尾追写
“a+” = “at+” 打开或新建一个文本文件,可以读,但只允许在文件末尾追写
“ab” 打开或新建一个二进制文件,只允许在文件末尾追写
“ab+” 打开或新建一个二进制文件,可以读,但只允许在文件末尾追写

fseek

fseek函数的声明如下:

int fseek(FILE *stream, long offset, int fromwhere);

其功能是重新设置文件指针stream的位置。其中stream也就是上文提到的fp,offset偏移量,值为正数表示指针在fromwhere后多少位,值为负数表示指针在fromwhere前多少位。fromwhere是指针位置的基准,可以使用以下三个常量:

变量 意义
SEEK_SET 文件起始位置
SEEK_CUR 指针当前指向位置
SEEK_END 文件结束位置

由于fstream中对文件的具体操作都要基于stream的位置,因此fseek的使用显得尤其重要。
例:如果我们要把fp的指针位置移到文件开头后的四个字节的位置,则需输入如下代码:

 fseek(fp,4,SEEK_SET);

fread函数

fread函数的作用是从文件流中读取数据到提前定义好的数组中,其声明如下:

size_t fread( void *restrict buffer, size_t size, size_t count, FILE *restrict stream );

其四个参数的定义如下表所示:

参数 定义
buffer 指向要读取的数组中首个对象的指针
size 每个对象的大小(单位是字节)
count 要读取的对象个数
stream 输入流

需要特别注意的是,在使用fread后若还需对文件流进行操作,需再次使用fseek函数设置文件流指针的新位置。

fclose函数

fclose的功能是关闭一个文件流,函数声明如下:

int fclose( FILE *fp );

如果流成功关闭,fclose 返回 0,否则返回EOF(-1)。(如果流为NULL,而且程序可以继续执行,fclose设定error number给EINVAL,并返回EOF。)

typedef

在该实验中需要使用typedef来声明位图文件头、信息头、调色板,typedef是在计算机编程语言中用来为复杂的声明定义简单的别名。在该实验中需要频繁地用到一个字节、两个字节、四个字节的定义,因此可声明如下变量:

typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef char BYTE;

同时也给出typedef声明结构体的思路,将文件头,信息头,调色板的类型用typedef作声明,然后在main函数里直接使用,不仅能加深对BMP位图文件的理解,同时也能更加方便地解析调用位图相关的参数。
如,位图文件头可声明如下:

typedef struct BITMAP_FILE_HEADER {
   
    WORD bfType;
    DWORD bfSize;
    WORD bfReserved1;
    WORD bfReserved2;
    DWORD bfOffBits;
}BITMPFILEHEADER;

实战—读取位图文件头

接下来我将以读取位图文件的文件头并输出前两个字符为例(假设输入的文件确实是bmp文件)利用理论准备中的方法,首先我们得定义文件头的结构体:

typedef struct BITMAP_FILE_HEADER {
   
    WORD bfType;
    DWORD bfSize;
    WORD bfReserved1;
    WORD bfReserved2;
    DWORD bfOffBits;
}BITMPFILEHEADER;

接下来读入文件:

int main(){
   
FILE* fp;
errno_t err;
err = fopen_s(&fp, "test1.bmp", "rb");
unsigned char a[2];//定义数组来保存文件的头两位
memset(a, 0, sizeof(a));
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值