BMP文件读写 数字图像处理【C++】

BMP文件的读写

一、实验题目

用C/C++语言编程实现以下功能:

1.1 灰度BMP图像读写

(1)读入lena.bmp文件;

(2)通过文件内容得出文件大小、位图数据起始字节、图像长宽及每像素的位数等信息;

(3)提取出原图像中的位图数据,另存为lena.raw,并通过Photoshop打开改文件,查看所读取的数据;

(4)仅取原始图像左上角1/4的数据,另存一个lenas.bmp文件,在Photoshop中打开查看效果。

1.2 彩色BMP图像读写

(1)读入lena_C.bmp文件;

(2)通过文件内容得出文件大小、位图数据起始字节、图像长宽及每像素的位数等信息;

(3)提取出原图像中的位图数据,另存为lena_C.raw,并通过Photoshop打开改文件,查看所读取的数据;

(4)仅取原始图像左上角1/4的数据,另存一个lena_Cs.bmp文件,在Photoshop中打开查看效果。

二、实验内容和代码实现

2.1 分析实验内容并设计相关函数

本实验考察我们对bmp文件格式,所以首先需要在程序中以结构体形式,定义bmp格式的部分结构。以BITMAPFILEHEADER定义文件头,BITMAPINFOHEADER定义信息头,RGBQUAD定义调色板,没有定义的是位图数据区。

其次,实验要求读入灰度或彩色图像,分析文件头和信息头得出文件大小、位图数据起始字节、图像长宽及每像素的位数等信息,于是定义函数bmpInfo(const char *path),通过输入文件路径,在函数内输出相关信息。

然后,实验要求将bmp文件中的数据部分读出并存成raw格式,于是定义函数bmp2raw(const char *path, const char *raw),输入待读取的文件名称和待写入的文件名称,在函数内实现上述格式转换。

最后,实验要求将原始图像中左上角1/4的数据切割出来,并存成另一个bmp文件,于是定义函数cutbmp(const char *path, const char *cut),输入待读取的文件名称和待写入的文件名称,在函数内实现图像切割。

需要注意的是,输入上述函数的图像不需要区分是否是灰度图像或彩色图像,函数内部会自行判断并实现该函数的功能。

2.2 Bmp文件格式定义

2.2.1 自定义类

课件中将各个字节数变量定义了相应名称,此处采用相同名称。

typedef unsigned char BYTE;		//1个字节
typedef unsigned short WORD;	//2个字节
typedef unsigned int DWORD;		//4个字节
typedef unsigned long LONG;		//4个字节
2.2.2 Bmp文件头

位图文件头有14个字节,具体包含信息如下注释。

/*位图文件头定义 14个字节*/
typedef struct tagBITMAPFILEHEADER {
    WORD bfType;				//位图文件的类型,必须为BM(0-1字节)
    DWORD bfSize;				//位图文件的大小,以字节为单位(2-5字节) 
    WORD bfReserved1;			//位图文件保留字,必须为0(6-7字节)
    WORD bfReserved2;			//位图文件保留字,必须为0(8-9字节)
    DWORD bfOffBits;			//位图数据的起始位置,以相对于位图(10-13字节)
} BITMAPFILEHEADER;
2.2.3 Bmp信息头

位图信息头有40个字节,具体包含信息如下注释。

/*位图信息头定义 40字节*/
typedef struct tagBITMAPINFOHEADER {
    DWORD biSize;				//本结构所占用字节数(14-17字节) 
    LONG biWidth;				//位图的宽度,以像素为单位(18-21字节) 
    LONG biHeight;				//位图的高度,以像素为单位(22-25字节) 
    WORD biPlanes;				//目标设备的级别,必须为1(26-27字节) 
    WORD biBitCount;			//每个像素所需的位数,必须是1(双色)、4(16色)、8(256色)、24(真彩色),(28-29字节)
    DWORD biCompression;		//位图压缩类型,必须是0(不压缩)、1(BI_RLE8压缩类型)、2(BI_RLE4压缩类型),(30-33字节)
    DWORD biSizeImage;			//位图的大小,以字节为单位(34-37字节)
    LONG biXPelsPerMeter;		//位图水平分辨率,每米像素数(38-41字节)
    LONG biYPelsPerMeter;		//位图垂直分辨率,每米像素数(42-45字节)
    DWORD biClrUsed;			//位图实际使用的颜色表中的颜色数(46-49字节)
    DWORD biClrImportant;		//位图中重要的色彩数,如果该值为零,则认为所有的颜色都是重要的(50-53字节)
} BITMAPINFOHEADER; 
2.2.4 调色板

调色板有4个字节,具体包含信息如下注释。

/*调色板定义 4字节*/
typedef struct tagRGBQUAD {
    BYTE rgbBlue;				//蓝色的亮度(值范围为0-255)
    BYTE rgbGreen;				//绿色的亮度(值范围为0-255)
    BYTE rgbRed;				//红色的亮度(值范围为0-255)
    BYTE rgbReserved;			//保留,必须为0 
} RGBQUAD;

2.3 打印灰度图像或彩色图像中文件头和信息头信息的函数

在函数中将灰度图像或彩色图像的文件头和信息头读入结构体变量,通过结构体变量将相应信息展示到控制台。

/*展示图像文件头函数*/
void showBmpFileHead(BITMAPFILEHEADER FileHeader)
{
	cout<<endl<<"---------位图文件头---------"<<endl;
	cout<<"位图文件的类型:"<<FileHeader.bfType<<endl;
	cout<<"位图文件的大小:"<<FileHeader.bfSize<<" B"<<endl;
	cout<<"位图文件保留字:"<<FileHeader.bfReserved1<<endl;
	cout<<"位图保留字:"<<FileHeader.bfReserved2<<endl;
	cout<<"位图数据的起始位置:"<<FileHeader.bfOffBits<<endl;
}

/*展示图像信息头函数*/
void showBmpInfoHead(BITMAPINFOHEADER InfoHeader)
{
	cout<<endl<<"---------位图信息头---------"<<endl;
	cout<<"位图的宽度:"<<InfoHeader.biWidth<<endl;
	cout<<"位图的高度:"<<InfoHeader.biHeight<<endl;
	cout<<"每个像素的位数:"<<InfoHeader.biBitCount<<endl;
	cout<<"位图的压缩类型:"<<InfoHeader.biCompression<<endl;
	cout<<"位图的大小:"<<InfoHeader.biSizeImage<<endl;
	cout<<"位图的水平分辨率:"<<InfoHeader.biXPelsPerMeter<<endl;
	cout<<"位图的垂直分辨率:"<<InfoHeader.biYPelsPerMeter<<endl;
	cout<<"使用的颜色数:"<<InfoHeader.biClrUsed<<endl;
}

/*控制台输出BMP文件信息*/
void bmpInfo(const char *path)
{
	FILE *f;
	BITMAPFILEHEADER FileHeader; 
	BITMAPINFOHEADER InfoHeader;
	f = fopen(path, "rb+");	//只读打开一个二进制文件
	if (f == NULL)
	{
		cout<<"文件打开失败。"<<endl;
	}
	
	cout<<endl<<"#############位图文件:"<<path<<"#############"<<endl;
	fread(&FileHeader, 1, sizeof(BITMAPFILEHEADER), f);
	showBmpFileHead(FileHeader);
	fread(&InfoHeader, 1, sizeof(BITMAPINFOHEADER), f);
	showBmpInfoHead(InfoHeader);
	
	cout<<endl;
	fclose(f);
}

2.4 Bmp转raw格式的函数

将bmp格式转成raw格式需要先取出位图数据,当读入文件为灰度图像时,开辟存储位图数据的空间,由于位图数据存储方式是由下至上、由左至右,所以要通过索引的地址变化从而还原数据顺序从上至下、从左至右,写入raw文件;同理当读入文件为彩色图像时,需要对3个通道BGR的宽度为3倍的数据执行由下至上、由左至右的查找,最终还原数据顺序从上至下、从左至右,写入raw文件。

/*将bmp格式转成raw格式*/
void bmp2raw(const char *path, const char *raw)
{
	FILE *fo;			//打开bmp文件
	FILE *fs;			//存储raw文件
	
	BITMAPFILEHEADER FileHeader;		//bmp的文件头 
	BITMAPINFOHEADER InfoHeader;		//bmp的信息头 
	
	fo = fopen(path, "rb+");			//读bmp文件 
	fs = fopen(raw, "wb+");				//写raw文件 
	
	fread(&FileHeader, 1, sizeof(BITMAPFILEHEADER), fo);		//把bmp文件头读出 
	fread(&InfoHeader, 1, sizeof(BITMAPINFOHEADER), fo);		//把bmp信息头读出 
	
	if(InfoHeader.biBitCount == 8){					//当为灰度图像时 
		
		int width = InfoHeader.biWidth;			//位图的宽度 
		int height = InfoHeader.biHeight;		//位图的高度 
		
		unsigned char *PixDataBmp = (unsigned char *)malloc(height * width);		//存bmp数据的空间 
		unsigned char *PixDataRaw = (unsigned char *)malloc(height * width);		//存raw数的空间 
		unsigned char *PixDatawid = (unsigned char *)malloc(width);					//镜像翻转后的数据存储空间 
		
		memset(PixDataBmp, 0, height * width);			//置空 
		memset(PixDataRaw, 0, height * width);			//置空 
		
		fread(PixDataBmp, 1, height * width, fo);		//从bmp文件中读取数据 
		
		for(int i = 0; i < height; i++){
			for(int j = 0; j < width; j++){
				int index = i * width + j;
				PixDataRaw[index] = PixDataBmp[height * width - index];		//bmp数据从下至上从存储到raw空间  
			}
		}
		
		for(int i = 0; i < height; i++){
			memset(PixDatawid, 0, width);			//循环置空 
			
			for(int j = 0; j < width; j++){
				PixDatawid[j] = PixDataRaw[(i + 1) * width - j];		//bmp数据从左至右存储到raw空间 
			}
			
			fwrite(PixDatawid, 1, width, fs);		//将raw空间每一行写入raw文件 
		}

		free(PixDataRaw);			//释放空间 
		free(PixDataBmp);
		free(PixDatawid);
		
		cout<<"灰度bmp图像转raw图像成功!"<<endl<<endl; 

	}
	else if(InfoHeader.biBitCount == 24){			//当为彩色图像时 
		
		int width = InfoHeader.biWidth;
		int height = InfoHeader.biHeight;
		int width3 = width * 3;				//BGR三通道所以宽度需要乘以3 
		
		unsigned char *PixDataBmp = (unsigned char *)malloc(height * width3);
		unsigned char *PixDataRaw = (unsigned char *)malloc(height * width3);
		unsigned char *PixDatawid = (unsigned char *)malloc(width3);
		
		memset(PixDataBmp, 0, height * width3);
		memset(PixDataRaw, 0, height * width3);
		
		fread(PixDataBmp, 1, height * width3, fo);
		
		for(int i = 0; i < height; i++){
			for(int j = 0; j < width; j++){
				int index = i * width3 + j * 3;
				PixDataRaw[index + 2] = PixDataBmp[height * width3 - index];		//R
				PixDataRaw[index + 1] = PixDataBmp[height * width3 - index + 1];	//G 
				PixDataRaw[index] = PixDataBmp[height * width3 - index + 2];		//B 
			}
		}
		
		for(int i = 0; i < height; i++){
			memset(PixDatawid, 0, width3);
			
			for(int j = 0; j < width; j++){
				int index = j * 3;
				PixDatawid[index] = PixDataRaw[(i + 1) * width3 - index];
				PixDatawid[index + 1] = PixDataRaw[(i + 1) * width3 - index + 1];
				PixDatawid[index + 2] = PixDataRaw[(i + 1) * width3 - index + 2];
			}
			
			fwrite(PixDatawid, 1, width3, fs);
		}

		free(PixDataRaw);
		free(PixDataBmp);
		free(PixDatawid);
		
		cout<<"彩色bmp图像转raw图像成功!"<<endl<<endl; 
		
	}
	else{
		cout<<"这不是bmp格式的灰度或彩色图像。"<<endl<<endl; 
	}
	
	fclose(fo);			//关闭文件 
	fclose(fs);
}

2.5 裁剪左上角1/4的数据

该函数读入文件时,分割文件头、信息头、颜色表(若为灰度图像)和数据区,将裁剪后的宽、高、位图数据大小等数据在相应结构中进行修改,同时按照索引查找图像bmp数据的左下角块复制到新开辟的存储空间,写入新的文件中。

/*将bmp裁剪左上角1/4的数据*/
void cutbmp(const char *path, const char *cut)
{
	FILE *fo;			//打开bmp文件
	FILE *fs;			//存储bmp文件 
	
	BITMAPFILEHEADER FileHeader;		//bmp的文件头 
	BITMAPINFOHEADER InfoHeader;		//bmp的信息头 
	
	fo = fopen(path, "rb+");			//读bmp文件 
	fs = fopen(cut, "wb+");				//写bmp文件 
	
	fread(&FileHeader, 1, sizeof(BITMAPFILEHEADER), fo);		//把bmp文件头读出 
	fread(&InfoHeader, 1, sizeof(BITMAPINFOHEADER), fo);		//把bmp信息头读出 
	
	if(InfoHeader.biBitCount == 8){					//当为灰度图像时 
		
		int width = InfoHeader.biWidth;			//位图的宽度 
		int height = InfoHeader.biHeight;		//位图的高度 
		int cutwidth = InfoHeader.biWidth / 2;			//位图切割后的宽度 
		int cutheight = InfoHeader.biHeight / 2;		//位图切割后的高度 
		
		InfoHeader.biWidth = cutwidth;
		InfoHeader.biHeight = cutheight;
		InfoHeader.biSizeImage = cutwidth * cutheight;
		FileHeader.bfSize = 54 + InfoHeader.biSizeImage;
		
		RGBQUAD color[256];		//8bit的有调色板 
		fread(&color[0], sizeof(RGBQUAD), 256, fo);
		
		unsigned char *pColorData = (unsigned char *)malloc(height * width);		//原始图像数据的空间 
		unsigned char *pColorDataWrite = (unsigned char *)malloc(cutheight * cutwidth);		//切割后图像数据的空间 
		
		memset(pColorData, 0, height * width);					//置空 
		memset(pColorDataWrite, 0, cutheight * cutwidth);	//置空 
		
		fread(pColorData, 1, height * width, fo);		//从bmp文件中读取数据 
	
		for (int i = height - cutheight; i < height; i++){
			for (int j = 0; j < cutwidth; j++){
				
				int index = i * width + j;
				int index2 = (i - height + cutheight) * cutwidth + j;
				pColorDataWrite[index2] = pColorData[index];			//切割数据放到新空间 
			}
		}
		
		fwrite(&FileHeader, 1, sizeof(BITMAPFILEHEADER), fs);		//写文件头 
		fwrite(&InfoHeader, 1, sizeof(BITMAPINFOHEADER), fs);		//写信息头 
		fwrite(&color[0], sizeof(RGBQUAD), 256, fs);				//写调色板 
		fwrite(pColorDataWrite, 1, cutheight * cutwidth, fs);		//写切割后的数据 
		
		free(pColorData);			//释放空间
		free(pColorDataWrite);
		
		cout<<"灰度bmp图像的左上角1/4裁剪成功!"<<endl<<endl; 
		
	}
	else if(InfoHeader.biBitCount == 24){			//当为彩色图像时 
		
		int width = InfoHeader.biWidth;			//位图的宽度 
		int width3 = width * 3;					//位图的RGB宽度
		int height = InfoHeader.biHeight;		//位图的高度 
		int cutwidth = InfoHeader.biWidth / 2;			//位图切割后的宽度 
		int cutwidth3 = cutwidth * 3;					//位图切割后的RGB宽度 
		int cutheight = InfoHeader.biHeight / 2;		//位图切割后的高度 
		
		InfoHeader.biWidth = cutwidth;
		InfoHeader.biHeight = cutheight;
		InfoHeader.biSizeImage = cutwidth3 * cutheight;
		FileHeader.bfSize = 54 + InfoHeader.biSizeImage;
		
		unsigned char *pColorData = (unsigned char *)malloc(height * width3);			//原始图像数据的空间 
		unsigned char *pColorDataWrite = (unsigned char *)malloc(cutheight * cutwidth3);		//切割后图像数据的空间 
		
		memset(pColorData, 0, height * width3);					//置空 
		memset(pColorDataWrite, 0, cutheight * cutwidth3);	//置空 
		
		fread(pColorData, 1, height * width3, fo);		//从bmp文件中读取数据 
	
		for (int i = height - cutheight; i < height; i++){
			for (int j = 0; j < cutwidth; j++){
				
				int index = i * width3 + j * 3;
				int index2 = (i - height + cutheight) * cutwidth3 + j * 3;
				pColorDataWrite[index2] = pColorData[index];			//切割数据放到新空间 
				pColorDataWrite[index2 + 1] = pColorData[index + 1];
				pColorDataWrite[index2 + 2] = pColorData[index + 2];
			}
		}
		
		fwrite(&FileHeader, 1, sizeof(BITMAPFILEHEADER), fs);		//写文件头 
		fwrite(&InfoHeader, 1, sizeof(BITMAPINFOHEADER), fs);		//写信息头 
		fwrite(pColorDataWrite, 1, cutheight * cutwidth3, fs);		//写切割后的数据 
		
		free(pColorData);			//释放空间
		free(pColorDataWrite);
		
		cout<<"彩色bmp图像的左上角1/4裁剪成功!"<<endl<<endl; 
		
	}
	else{
		cout<<"这不是bmp格式的灰度或彩色图像。"<<endl<<endl; 
	}
	
	fclose(fo);			//关闭文件 
	fclose(fs);
}

三、实验结果与收获

3.1 灰度bmp图像读写

3.1.1 图像信息展示

图1 灰度图像位图文件头和信息头信息

3.1.2 Bmp转raw

图2 灰度图像位图转raw并用photoshop打开

3.1.3 Bmp裁剪左上角1/4数据

图3 灰度图像位图裁剪左上角1/4并用photoshop打开

3.2 彩色bmp图像读写

3.2.1 图像信息展示

图4 彩色图像位图文件头和信息头信息

3.2.2 Bmp转raw

图5 彩色图像位图转raw并用photoshop打开

3.2.3 Bmp裁剪左上角1/4数据

图6 彩色图像位图裁剪左上角1/4并用photoshop打开

3.3 实验收获

在本次实验中,我不仅更深入的理解了bmp位图格式和数据存储方向,对位图的数据结构和文件的读写也有丰富的认识。

同时,在实验中遇到了如结构体读取文件出错等问题,我通过设定变量以n字节对齐方式解决,并记住了字节对齐的预处理指令“#pragma pack(1)”。

四、完整代码

#include <iostream>
#include <fstream>
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#pragma pack(1)					//设定变量以n字节对齐方式

using namespace std;

typedef unsigned char BYTE;		//1个字节
typedef unsigned short WORD;	//2个字节
typedef unsigned int DWORD;		//4个字节
typedef unsigned long LONG;		//4个字节

/*位图文件头定义 14个字节*/
typedef struct tagBITMAPFILEHEADER {
    WORD bfType;				//位图文件的类型,必须为BM(0-1字节)
    DWORD bfSize;				//位图文件的大小,以字节为单位(2-5字节) 
    WORD bfReserved1;			//位图文件保留字,必须为0(6-7字节)
    WORD bfReserved2;			//位图文件保留字,必须为0(8-9字节)
    DWORD bfOffBits;			//位图数据的起始位置,以相对于位图(10-13字节)
} BITMAPFILEHEADER;

/*位图信息头定义 40字节*/
typedef struct tagBITMAPINFOHEADER {
    DWORD biSize;				//本结构所占用字节数(14-17字节) 
    LONG biWidth;				//位图的宽度,以像素为单位(18-21字节) 
    LONG biHeight;				//位图的高度,以像素为单位(22-25字节) 
    WORD biPlanes;				//目标设备的级别,必须为1(26-27字节) 
    WORD biBitCount;			//每个像素所需的位数,必须是1(双色)、4(16色)、8(256色)、24(真彩色),(28-29字节)
    DWORD biCompression;		//位图压缩类型,必须是0(不压缩)、1(BI_RLE8压缩类型)、2(BI_RLE4压缩类型),(30-33字节)
    DWORD biSizeImage;			//位图的大小,以字节为单位(34-37字节)
    LONG biXPelsPerMeter;		//位图水平分辨率,每米像素数(38-41字节)
    LONG biYPelsPerMeter;		//位图垂直分辨率,每米像素数(42-45字节)
    DWORD biClrUsed;			//位图实际使用的颜色表中的颜色数(46-49字节)
    DWORD biClrImportant;		//位图中重要的色彩数,如果该值为零,则认为所有的颜色都是重要的(50-53字节)
} BITMAPINFOHEADER; 

/*调色板定义 4字节*/
typedef struct tagRGBQUAD {
    BYTE rgbBlue;				//蓝色的亮度(值范围为0-255)
    BYTE rgbGreen;				//绿色的亮度(值范围为0-255)
    BYTE rgbRed;				//红色的亮度(值范围为0-255)
    BYTE rgbReserved;			//保留,必须为0 
} RGBQUAD;

/*展示图像文件头函数*/
void showBmpFileHead(BITMAPFILEHEADER FileHeader)
{
	cout<<endl<<"---------位图文件头---------"<<endl;
	cout<<"位图文件的类型:"<<FileHeader.bfType<<endl;
	cout<<"位图文件的大小:"<<FileHeader.bfSize<<" B"<<endl;
	cout<<"位图文件保留字:"<<FileHeader.bfReserved1<<endl;
	cout<<"位图保留字:"<<FileHeader.bfReserved2<<endl;
	cout<<"位图数据的起始位置:"<<FileHeader.bfOffBits<<endl;
}

/*展示图像信息头函数*/
void showBmpInfoHead(BITMAPINFOHEADER InfoHeader)
{
	cout<<endl<<"---------位图信息头---------"<<endl;
	cout<<"位图的宽度:"<<InfoHeader.biWidth<<endl;
	cout<<"位图的高度:"<<InfoHeader.biHeight<<endl;
	cout<<"每个像素的位数:"<<InfoHeader.biBitCount<<endl;
	cout<<"位图的压缩类型:"<<InfoHeader.biCompression<<endl;
	cout<<"位图的大小:"<<InfoHeader.biSizeImage<<endl;
	cout<<"位图的水平分辨率:"<<InfoHeader.biXPelsPerMeter<<endl;
	cout<<"位图的垂直分辨率:"<<InfoHeader.biYPelsPerMeter<<endl;
	cout<<"使用的颜色数:"<<InfoHeader.biClrUsed<<endl;
}

/*控制台输出BMP文件信息*/
void bmpInfo(const char *path)
{
	FILE *f;
	BITMAPFILEHEADER FileHeader; 
	BITMAPINFOHEADER InfoHeader;
	f = fopen(path, "rb+");	//只读打开一个二进制文件
	if (f == NULL)
	{
		cout<<"文件打开失败。"<<endl;
	}
	
	cout<<endl<<"#############位图文件:"<<path<<"#############"<<endl;
	fread(&FileHeader, 1, sizeof(BITMAPFILEHEADER), f);
	showBmpFileHead(FileHeader);
	fread(&InfoHeader, 1, sizeof(BITMAPINFOHEADER), f);
	showBmpInfoHead(InfoHeader);
	
	cout<<endl;
	fclose(f);
}

/*将bmp格式转成raw格式*/
void bmp2raw(const char *path, const char *raw)
{
	FILE *fo;			//打开bmp文件
	FILE *fs;			//存储raw文件
	
	BITMAPFILEHEADER FileHeader;		//bmp的文件头 
	BITMAPINFOHEADER InfoHeader;		//bmp的信息头 
	
	fo = fopen(path, "rb+");			//读bmp文件 
	fs = fopen(raw, "wb+");				//写raw文件 
	
	fread(&FileHeader, 1, sizeof(BITMAPFILEHEADER), fo);		//把bmp文件头读出 
	fread(&InfoHeader, 1, sizeof(BITMAPINFOHEADER), fo);		//把bmp信息头读出 
	
	if(InfoHeader.biBitCount == 8){					//当为灰度图像时 
		
		int width = InfoHeader.biWidth;			//位图的宽度 
		int height = InfoHeader.biHeight;		//位图的高度 
		
		unsigned char *PixDataBmp = (unsigned char *)malloc(height * width);		//存bmp数据的空间 
		unsigned char *PixDataRaw = (unsigned char *)malloc(height * width);		//存raw数的空间 
		unsigned char *PixDatawid = (unsigned char *)malloc(width);					//镜像翻转后的数据存储空间 
		
		memset(PixDataBmp, 0, height * width);			//置空 
		memset(PixDataRaw, 0, height * width);			//置空 
		
		fread(PixDataBmp, 1, height * width, fo);		//从bmp文件中读取数据 
		
		for(int i = 0; i < height; i++){
			for(int j = 0; j < width; j++){
				int index = i * width + j;
				PixDataRaw[index] = PixDataBmp[height * width - index];		//bmp数据从下至上从存储到raw空间  
			}
		}
		
		for(int i = 0; i < height; i++){
			memset(PixDatawid, 0, width);			//循环置空 
			
			for(int j = 0; j < width; j++){
				PixDatawid[j] = PixDataRaw[(i + 1) * width - j];		//bmp数据从左至右存储到raw空间 
			}
			
			fwrite(PixDatawid, 1, width, fs);		//将raw空间每一行写入raw文件 
		}

		free(PixDataRaw);			//释放空间 
		free(PixDataBmp);
		free(PixDatawid);
		
		cout<<"灰度bmp图像转raw图像成功!"<<endl<<endl; 

	}
	else if(InfoHeader.biBitCount == 24){			//当为彩色图像时 
		
		int width = InfoHeader.biWidth;
		int height = InfoHeader.biHeight;
		int width3 = width * 3;				//BGR三通道所以宽度需要乘以3 
		
		unsigned char *PixDataBmp = (unsigned char *)malloc(height * width3);
		unsigned char *PixDataRaw = (unsigned char *)malloc(height * width3);
		unsigned char *PixDatawid = (unsigned char *)malloc(width3);
		
		memset(PixDataBmp, 0, height * width3);
		memset(PixDataRaw, 0, height * width3);
		
		fread(PixDataBmp, 1, height * width3, fo);
		
		for(int i = 0; i < height; i++){
			for(int j = 0; j < width; j++){
				int index = i * width3 + j * 3;
				PixDataRaw[index + 2] = PixDataBmp[height * width3 - index];		//R
				PixDataRaw[index + 1] = PixDataBmp[height * width3 - index + 1];	//G 
				PixDataRaw[index] = PixDataBmp[height * width3 - index + 2];		//B 
			}
		}
		
		for(int i = 0; i < height; i++){
			memset(PixDatawid, 0, width3);
			
			for(int j = 0; j < width; j++){
				int index = j * 3;
				PixDatawid[index] = PixDataRaw[(i + 1) * width3 - index];
				PixDatawid[index + 1] = PixDataRaw[(i + 1) * width3 - index + 1];
				PixDatawid[index + 2] = PixDataRaw[(i + 1) * width3 - index + 2];
			}
			
			fwrite(PixDatawid, 1, width3, fs);
		}

		free(PixDataRaw);
		free(PixDataBmp);
		free(PixDatawid);
		
		cout<<"彩色bmp图像转raw图像成功!"<<endl<<endl; 
		
	}
	else{
		cout<<"这不是bmp格式的灰度或彩色图像。"<<endl<<endl; 
	}
	
	fclose(fo);			//关闭文件 
	fclose(fs);
}

/*将bmp裁剪左上角1/4的数据*/
void cutbmp(const char *path, const char *cut)
{
	FILE *fo;			//打开bmp文件
	FILE *fs;			//存储bmp文件 
	
	BITMAPFILEHEADER FileHeader;		//bmp的文件头 
	BITMAPINFOHEADER InfoHeader;		//bmp的信息头 
	
	fo = fopen(path, "rb+");			//读bmp文件 
	fs = fopen(cut, "wb+");				//写bmp文件 
	
	fread(&FileHeader, 1, sizeof(BITMAPFILEHEADER), fo);		//把bmp文件头读出 
	fread(&InfoHeader, 1, sizeof(BITMAPINFOHEADER), fo);		//把bmp信息头读出 
	
	if(InfoHeader.biBitCount == 8){					//当为灰度图像时 
		
		int width = InfoHeader.biWidth;			//位图的宽度 
		int height = InfoHeader.biHeight;		//位图的高度 
		int cutwidth = InfoHeader.biWidth / 2;			//位图切割后的宽度 
		int cutheight = InfoHeader.biHeight / 2;		//位图切割后的高度 
		
		InfoHeader.biWidth = cutwidth;
		InfoHeader.biHeight = cutheight;
		InfoHeader.biSizeImage = cutwidth * cutheight;
		FileHeader.bfSize = 54 + InfoHeader.biSizeImage;
		
		RGBQUAD color[256];		//8bit的有调色板 
		fread(&color[0], sizeof(RGBQUAD), 256, fo);
		
		unsigned char *pColorData = (unsigned char *)malloc(height * width);		//原始图像数据的空间 
		unsigned char *pColorDataWrite = (unsigned char *)malloc(cutheight * cutwidth);		//切割后图像数据的空间 
		
		memset(pColorData, 0, height * width);					//置空 
		memset(pColorDataWrite, 0, cutheight * cutwidth);	//置空 
		
		fread(pColorData, 1, height * width, fo);		//从bmp文件中读取数据 
	
		for (int i = height - cutheight; i < height; i++){
			for (int j = 0; j < cutwidth; j++){
				
				int index = i * width + j;
				int index2 = (i - height + cutheight) * cutwidth + j;
				pColorDataWrite[index2] = pColorData[index];			//切割数据放到新空间 
			}
		}
		
		fwrite(&FileHeader, 1, sizeof(BITMAPFILEHEADER), fs);		//写文件头 
		fwrite(&InfoHeader, 1, sizeof(BITMAPINFOHEADER), fs);		//写信息头 
		fwrite(&color[0], sizeof(RGBQUAD), 256, fs);				//写调色板 
		fwrite(pColorDataWrite, 1, cutheight * cutwidth, fs);		//写切割后的数据 
		
		free(pColorData);			//释放空间
		free(pColorDataWrite);
		
		cout<<"灰度bmp图像的左上角1/4裁剪成功!"<<endl<<endl; 
		
	}
	else if(InfoHeader.biBitCount == 24){			//当为彩色图像时 
		
		int width = InfoHeader.biWidth;			//位图的宽度 
		int width3 = width * 3;					//位图的RGB宽度
		int height = InfoHeader.biHeight;		//位图的高度 
		int cutwidth = InfoHeader.biWidth / 2;			//位图切割后的宽度 
		int cutwidth3 = cutwidth * 3;					//位图切割后的RGB宽度 
		int cutheight = InfoHeader.biHeight / 2;		//位图切割后的高度 
		
		InfoHeader.biWidth = cutwidth;
		InfoHeader.biHeight = cutheight;
		InfoHeader.biSizeImage = cutwidth3 * cutheight;
		FileHeader.bfSize = 54 + InfoHeader.biSizeImage;
		
		unsigned char *pColorData = (unsigned char *)malloc(height * width3);			//原始图像数据的空间 
		unsigned char *pColorDataWrite = (unsigned char *)malloc(cutheight * cutwidth3);		//切割后图像数据的空间 
		
		memset(pColorData, 0, height * width3);					//置空 
		memset(pColorDataWrite, 0, cutheight * cutwidth3);	//置空 
		
		fread(pColorData, 1, height * width3, fo);		//从bmp文件中读取数据 
	
		for (int i = height - cutheight; i < height; i++){
			for (int j = 0; j < cutwidth; j++){
				
				int index = i * width3 + j * 3;
				int index2 = (i - height + cutheight) * cutwidth3 + j * 3;
				pColorDataWrite[index2] = pColorData[index];			//切割数据放到新空间 
				pColorDataWrite[index2 + 1] = pColorData[index + 1];
				pColorDataWrite[index2 + 2] = pColorData[index + 2];
			}
		}
		
		fwrite(&FileHeader, 1, sizeof(BITMAPFILEHEADER), fs);		//写文件头 
		fwrite(&InfoHeader, 1, sizeof(BITMAPINFOHEADER), fs);		//写信息头 
		fwrite(pColorDataWrite, 1, cutheight * cutwidth3, fs);		//写切割后的数据 
		
		free(pColorData);			//释放空间
		free(pColorDataWrite);
		
		cout<<"彩色bmp图像的左上角1/4裁剪成功!"<<endl<<endl; 
		
	}
	else{
		cout<<"这不是bmp格式的灰度或彩色图像。"<<endl<<endl; 
	}
	
	fclose(fo);			//关闭文件 
	fclose(fs);
}


int main()
{
    const char *path1 = "lena.bmp";			//灰度图像bmp格式
    const char *raw1 = "lena.raw";			//灰度图像生成raw格式 
    const char *cut1 = "lenas.bmp";			//灰度图像裁剪1/4 
    
    const char *path2 = "lena_C.bmp";		//彩色图像bmp格式
    const char *raw2 = "lena_C.raw";		//彩色图像生成raw格式 
    const char *cut2 = "lenas_C.bmp";		//彩色图像裁剪1/4 
    
	cout<<"1.灰度图像读写:"<<endl; 
	bmpInfo(path1);
	bmp2raw(path1, raw1); 
	cutbmp(path1, cut1); 
	system("pause");
	
	cout<<endl<<"2.彩色图像读写:"<<endl; 
	bmpInfo(path2);
	bmp2raw(path2, raw2);
	cutbmp(path2, cut2); 
	system("pause");

	return 0;
}

五、参考文章

数字图像处理大作业-BMP文件的读写

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秋千的千秋

希望你喜欢

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值