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

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

  • 一、题目背景
  • 二、灰度BMP的读写
    • 1.读入lena.bmp文件
      • 1.1 编写打印文件头信息与信息头数据的函数
    • 2 通过文件内容得到灰度bmp数据信息
      • 2.1 打印信息结果
    • 3.提取bmp位图数据,另存为 lena.raw, 并通过 photoshop 查看
      • 3.1 原理介绍
      • 3.2 编写灰度bmp转raw函数
      • 3.3 Photoshop查看结果
    • 4.读取原始图像左上角1/4 的数据,另存一个 lenas.bmp 图像,在 photoshop 查看
      • 4.1 编写裁剪函数
      • 4.2 Photoshop查看结果
  • 三、彩色BMP图像的读写
    • 1. 读入lena _C.bmp文件;
    • 2. 通过文件内容得到彩色bmp数据信息;
    • 3. 提取bmp的位图数据,另存为lena_C.raw,并通过Photoshop查看
      • 3.1 原理介绍
      • 3.2 编写彩色bmp转raw函数
      • 3.3 Photoshop查看生成的lena_C.raw
    • 4.读取原始图像左上角1/4 的数据,另存一个 lena_Cs.bmp 图像,在 photoshop 查看
      • 4.1 编写裁剪函数
      • 4.2 Photoshop查看生成的lena_Cs.bmp文件
  • 四、总结
  • 五、参考文章

一、题目背景

1.灰度 BMP 图像的读写

  1. 读入 lena.bmp 文件;
  2. 通过文件内容得出文件大小,位图数据起始字节,图像长、宽以及每像素的位数等信息;
  3. 提取出原图像中的位图数据,另存为 lena.raw, 并通过 photoshop 打开该文件,查看所读取的数据。
  4. 仅取原始图像左上角1/4 的数据,另存一个 lenas.bmp 图像,在 photoshop 中打开查看效果。

2.彩色 BMP 图像的读写:

  1. 读入文件 lena_C.bmp 文件;
  2. 通过文件内容得出文件大小,位图数据起始字节,图像长、宽以及每像素的位数等信息;
  3. 提取出原图像中的位图数据,另存为 lena_C.raw, 并通过photoshop打开该文件,查看所读取的数据。
  4. 仅取原始图像左上角 1/4 的数据,另存一个 lena_Cs.bmp 图像,在photoshop 中打开查看效果。

二、灰度BMP的读写

1.读入lena.bmp文件

  • 首先自定义类型名称,eg:将unsigned short int 自定义为 WORD,为了与课件中类型匹配
//自定义类型名称,eg:将unsigned short int 自定义为 WORD,为了与课件中类型匹配
typedef unsigned short int WORD;
typedef unsigned int DWORD;
typedef unsigned char BYTE;
typedef long LONG;
  • 紧接着定义三个结构体,用于存放位图数据,它一般由4部分组成:BMP文件头、BMP信息头、颜色表(在真彩色模式无颜色表)和位图数据区组成。
//图像文件头(14个字节)
typedef struct readBITMAPFILEHEADER
{
	//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 readBITMAPINFOHEADER
{
	DWORD biSize;			 // 本结构所占用字节数(14-17字节)
	LONG biWidth;			 // 位图的宽度,以像素为单位(18-21字节)
	LONG biHeight;			 // 位图的高度,以像素为单位(22-25字节)
	WORD biPlanes;			 // 目标设备的级别,必须为1(26-27字节)
	WORD biBitCount;		 // 每个像素所需的位数,必须是1(双色),(28-29字节)
							 // 4(16色),8(256色)或24(真彩色)之一
	DWORD biCompression;	 // 位图压缩类型,必须是 0(不压缩),(30-33字节)
							 // 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
	DWORD biSizeImage;		 // 位图的大小,以字节为单位(34-37字节)
	LONG biXPelsPerMeter;    // 位图水平分辨率,每米像素数(38-41字节)
	LONG biYPelsPerMeter;    // 位图垂直分辨率,每米像素数(42-45字节)
	DWORD biClrUsed;		 // 位图实际使用的颜色表中的颜色数(46-49字节)
	DWORD biClrImportant;	 // 位图显示过程中重要的颜色数(50-53字节)

}BITMAPINFOHEADER;

//调色板(颜色表)
typedef struct readRGBQUAD
{
	BYTE rgbBlue;       // 蓝色的亮度(值范围为0-255)
	BYTE rgbGreen;      // 绿色的亮度(值范围为0-255)
	BYTE rgbRed;        // 红色的亮度(值范围为0-255)
	BYTE rgbReserved;   // 保留字,必须为0

} RGBQUAD;

把WORD bfType注释掉,这是因为C语言结构体Sizeof运算规则——整体大于部分之和,如果不单独将bfType在结构体外部读取,将导致读文件错位,即打印:sizeof(readBITMAPFILEHEADER)=16≠14
你需要单独在程序里将其读进来,如下所示:(这里仅仅告知,后续程序会写出来)

//如果不先读取bfType,根据C语言结构体Sizeof运算规则——整体大于部分之和,从而导致读文件错位
unsigned short  fileType;	
fread(&fileType, 1, sizeof(unsigned short), f_opener);

1.1 编写打印文件头信息与信息头数据的函数

其中展示的数据并不是文件头和信息头的全部数据,但绝对多于作业要求的数据。

//展示图像文件头函数
void showBmpFileHead(BITMAPFILEHEADER p_FileHead)
{
	printf("----------------位图文件头----------------\n");
	printf("BMP文件大小:%dKB\n", FileHeader.bfSize/ 1024);
	printf("位图保留字:%d\n", FileHeader.bfReserved1);
	printf("位图保留字:%d\n", FileHeader.bfReserved2);
	printf("位图数据的起始位置: %d\n", FileHeader.bfOffBits);
}

//展示图像信息头函数
void showBmpInfoHead(BITMAPINFOHEADER p_InfoHead)
{
	printf("----------------位图信息头----------------\n");
	printf("位图宽度:%d\n", InfoHeader.biWidth);
	printf("位图高度:%d\n", InfoHeader.biHeight);
	printf("每个像素的位数:%d\n", InfoHeader.biBitCount);
	printf("压缩方式:%d\n", InfoHeader.biCompression);
	printf("图像的大小:%d\n", InfoHeader.biSizeImage);
	printf("水平方向分辨率:%d\n", InfoHeader.biXPelsPerMeter);
	printf("垂直方向分辨率:%d\n", InfoHeader.biYPelsPerMeter);
	printf("使用的颜色数:%d\n", InfoHeader.biClrUsed);
}

2 通过文件内容得到灰度bmp数据信息

采用C/C++语言,借助部分内置函数,读取到bmp文件信息

//读入bmp文件,可以选择灰度,也可以选择真彩色
void readBmp(const char* bmp_ptah)
{
	FILE *f_opener;
	f_opener = fopen(bmp_ptah, "rb+");//打开一个二进制文件,文件必须存在,只允许读
	if (f_opener == NULL)
	{
		printf("SaveFile:open failed");
	}
	//如果不先读取bfType,根据C语言结构体Sizeof运算规则——整体大于部分之和,从而导致读文件错位
	unsigned short  fileType;	
	fread(&fileType, 1, sizeof(unsigned short), f_opener);
	if (fileType = 0x4d42)
	{
		printf("该文件是bmp格式文件!");
		//19778,必须是BM字符串,对应的十六进制为0x4d42,十进制为19778,否则不是bmp格式文件
		printf("\n文件标识符:%d\n", fileType);
		fread(&FileHeader, 1, sizeof(BITMAPFILEHEADER), f_opener);
		showBmpFileHead(FileHeader);
		fread(&InfoHeader, 1, sizeof(BITMAPINFOHEADER), f_opener);
		showBmpInfoHead(InfoHeader);		
		fclose(f_opener);
	}
}
int main()
{
	const char *bmp_path = "lena.bmp";//读入的灰度bmp原图
	readBmp(bmp_path);//调用函数,打印信息
	return 0;
}

2.1 打印信息结果

运行上述程序,得出文件大小、位图数据起始字节、图像长宽及每像素的位数等信息,控制台打印结果如下:
数字图像处理大作业-BMP文件的读写_第1张图片

为了验证输出信息的正确性,将lena.bmp文件转为二进制数据,进行验证。我采用UltraEdit软件,进行二进制数据的查看,经过对比,证明了输出数据的准确性。
数字图像处理大作业-BMP文件的读写_第2张图片

3.提取bmp位图数据,另存为 lena.raw, 并通过 photoshop 查看

3.1 原理介绍

这里需要说明的是,bmp图像的位图数据:也就是一个像素所表示的数据,一张图片的第一个像素点在坐下角,然后往右一个个存取,最后一个在右上角。
数字图像处理大作业-BMP文件的读写_第3张图片
而raw则是正常的像素排列方式,因此需要将读到的bmp图像作镜像处理。

3.2 编写灰度bmp转raw函数

因此对直接读取到的bmp数据,存储到raw前,进行了如下的变换(我定义三个逐像素处理方式,使得最终得到的raw不是镜像翻转的)

void bmp2raw(const char *bmp_path, const char *raw_path)
{
	FILE *f_opener;//定义文件指针,打开bmp文件
	f_opener = fopen(bmp_path, "rb+");
	FILE *f_saver;//定义文件指针,存储raw文件
	f_saver = fopen(raw_path, "wb+");

	unsigned short fileType;
	fread(&fileType, 1, sizeof(unsigned short), f_opener);
	fread(&FileHeader, 1, sizeof(BITMAPFILEHEADER), f_opener);
	fread(&InfoHeader, 1, sizeof(BITMAPINFOHEADER), f_opener);

	int width = InfoHeader.biWidth;//获得图像的宽度
	int height = InfoHeader.biHeight;//获得图像的长度
	if (InfoHeader.biBitCount == 8)
	{
		printf("8位图像!!!\n");
	}

	//开辟内存空间存放bmp源图像数据
	unsigned char *PixDataBmp = (unsigned char *)malloc(height*width);
	//初始化PixDataBmp,里面的内容是0,大小为height*width
	memset(PixDataBmp, 0, height*width);

	//开辟内存空间存储raw图像处理之后数据
	unsigned char *PixDataRaw = (unsigned char *)malloc(height*width);
	//初始化PixDataRaw,里面的内容是0,大小为height*width
	memset(PixDataRaw, 0, height*width);

	//开辟内存空间存储备份raw图像处理之后数据
	unsigned char *PixDataRawClone = (unsigned char *)malloc(height*width);
	//初始化PixDataRawClone,里面的内容是0,大小为height*width
	memset(PixDataRawClone, 0, height*width);

	long nData = height * width;//得到数据的长度

	//把bmp位图数据信息读到数组里,这里面存的就是像素值,PixDataBmp类似于摊平的矩阵
	fread(PixDataBmp, 1, nData, f_opener);


	//将bmp的像素赋值放到raw里,但此时是左右镜像
	for (int i =0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			int index = i * width + j;
			PixDataRaw[index] = PixDataBmp[nData - index];
		}
	}
	//将bmp的像素赋值放到备份的raw里,但此时依然是左右镜像
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			int index = i * width + j;
			PixDataRawClone[index] = PixDataBmp[nData - index];
		}
	}
	//将备份的raw的左右两边的像素,分别赋值给将要存储的raw
	for (int i = 0; i < height; i++) {
		for (int j = 0; j < width; j++) {

			int index = i * width + j;      //像素索引
			int exchange_x = width - j - 1; //水平镜像后的x坐标
			int exchange_y = i;             //水平镜像后的y坐标
			PixDataRaw[exchange_y * width + exchange_x] = PixDataRawClone[index ];
			PixDataRaw[index] = PixDataRawClone[exchange_y * width + exchange_x];

		}
	}

	//将得到的数据存入raw中,此时将读到的bmp镜像回来,我们看到的就是正视图
	fwrite(PixDataRaw, 1, nData, f_saver);

	fclose(f_opener);//关闭文件指针
	fclose(f_saver);
	free(PixDataRaw);//释放堆空间内存
	free(PixDataBmp);
}
int main()
{
	//8位bmp转raw
	const char *bmp_path = "lena.bmp";//读入的灰度bmp原图
	const char *raw_path = "lena.raw";//生成的灰度raw格式图
	bmp2raw(bmp_path, raw_path);//8位的bmp转raw
	return 0;
}

3.3 Photoshop查看结果

数字图像处理大作业-BMP文件的读写_第4张图片

4.读取原始图像左上角1/4 的数据,另存一个 lenas.bmp 图像,在 photoshop 查看

4.1 编写裁剪函数

即对原始bmp进行裁剪,其核心就是对像素处理时,需要限定迭代索引i和j的位置,整个裁剪函数的实现如下:

void GrayCutBmp(const char *bmp_path, const char *cutBmp_path)
{

	BITMAPFILEHEADER writebitHead;
	BITMAPINFOHEADER writebitInfoHead;

	int Cut_height = 256;//想要截取的长度
	int Cut_width = 256;//想要截取的宽度
	int x_left = 0;//想要截取的左上角坐标x值
	int y_left = 0;//想要截取的左上角坐标y值

	FILE *f_opener;
	f_opener = fopen(bmp_path, "rb+");
	FILE *f_saver;
	f_saver = fopen(cutBmp_path, "wb+");

	//读取位图文件头信息
	unsigned short  fileType;
	fread(&fileType, 1, sizeof(unsigned short), f_opener);
	fwrite(&fileType, 1, sizeof(unsigned short), f_saver);
	if (fileType != 0x4d42)
	{
		printf("该文件不是bmp格式文件!");
		return;
	}
	//读取位图文件头信息
	fread(&FileHeader, 1, sizeof(BITMAPFILEHEADER), f_opener);
	writebitHead = FileHeader;//由于截取图像头和源文件头相似,所以先将源文件头数据赋予截取文件头
	//读取位图信息头信息
	fread(&InfoHeader, 1, sizeof(BITMAPINFOHEADER), f_opener);
	writebitInfoHead = InfoHeader;//同位图文件头相似

	writebitInfoHead.biHeight = Cut_height;//为截取文件重写位图高度
	writebitInfoHead.biWidth = Cut_width;//为截取文件重写位图宽度
	int mywritewidth = writebitInfoHead.biWidth;
	writebitInfoHead.biSizeImage = mywritewidth * writebitInfoHead.biHeight;//计算位图实际数据区大小

	writebitHead.bfSize = 54 + writebitInfoHead.biSizeImage;//位图文件头大小为位图数据区大小加上54byte
	fwrite(&writebitHead, 1, sizeof(BITMAPFILEHEADER), f_saver);//写回位图文件头信息到输出文件
	fwrite(&writebitInfoHead, 1, sizeof(BITMAPINFOHEADER), f_saver);//写回位图信息头信息到输出文件

	RGBQUAD wRggList[256];//8bit的有颜色表,所以要读进来,写出去
	fread(&wRggList[0], sizeof(RGBQUAD), 256, f_opener);
	fwrite(&wRggList[0], sizeof(RGBQUAD), 256, f_saver);

	int width = InfoHeader.biWidth;
	int height = InfoHeader.biHeight;

	unsigned char *pColorData = (unsigned char *)malloc(height*width);//开辟内存空间存储图像数据
	memset(pColorData, 0, height*width);

	unsigned char *pColorDataWrite = (unsigned char *)malloc(mywritewidth*Cut_height);//开辟内存空间存储图像处理之后数据
	memset(pColorDataWrite, 0, mywritewidth*Cut_height);

	long nData = height * width;
	long write_nData = mywritewidth * Cut_height;//截取的位图数据区长度定义

	//把位图数据信息读到数组里   
	fread(pColorData, 1, nData, f_opener);//图像处理可通过操作这部分数据加以实现

	//由于BMP图像的数据存储格式起点是图像的左下角,所以需要进行坐标换算操作
	for (int i = height - y_left - Cut_height; i < height - y_left; i++)
	{
		for (int j = 0; j < x_left + Cut_width; j++)
		{
			int index = i * width + j;//像素索引
			int write_index = (i - height + y_left + Cut_height)*mywritewidth + (j - x_left);
			pColorDataWrite[write_index] = pColorData[index];
		}
	}
		

	fwrite(pColorDataWrite, 1, write_nData, f_saver);   //将处理完图像数据区写回文件

	fclose(f_opener);//释放文件指针
	fclose(f_saver);
	free(pColorData);//回收内存
	free(pColorDataWrite);
}
int main()
{
    //8位bmp图像的裁剪
	const char *bmp_path = "lena.bmp";//读入的灰度bmp原图
	const char *cutBmp_path = "lenas.bmp";//裁剪之后的图像
	GrayCutBmp(bmp_path, cutBmp_path);
    return 0;
}

4.2 Photoshop查看结果

数字图像处理大作业-BMP文件的读写_第5张图片

三、彩色BMP图像的读写

1. 读入lena _C.bmp文件;

与读入lena.bmp文件类似,这里我将其读入文件的功能封装成了一个函数,无论是读入灰度图还是真彩色图,直接传入路径即可完成文件的读入。文件头、信息头等结构体数据与第一问定义相同,这里不再做展示。

只需要将图片名字更改一下即可

int main()
{
	const char *bmp_path = "lena_C.bmp";//读入的彩色bmp原图
	readBmp(bmp_path);//调用函数,打印信息
	return 0;
}

2. 通过文件内容得到彩色bmp数据信息;

通过读入lena_C.bmp文件,打印出文件大小,位图数据起始字节,图像长宽及位数等信息,见下图控制台输出:
数字图像处理大作业-BMP文件的读写_第6张图片

同理,为了验证数据的准确性,与源文件的二进制数据进行比对:
数字图像处理大作业-BMP文件的读写_第7张图片

3. 提取bmp的位图数据,另存为lena_C.raw,并通过Photoshop查看

3.1 原理介绍

由于读入的lena_C.bmp是彩色图像,涉及到通道数的增加,由原来的单通道增加至三通道,因此代码实现与灰度bmp不同。同时bmp赋值给raw时需要将三通道都赋值,由于BMP的RGB和raw的BGR的不同,通道传输需要更改,否则得到的图像偏蓝色。

下图展示了彩色bmp读取像素值的示意图,存放在一个向量里,每一个像素值的三个通道分量依次排列

数字图像处理大作业-BMP文件的读写_第8张图片

3.2 编写彩色bmp转raw函数

//24位图像bmp转raw
void Cbmp2Craw(const char *bmp_path, const char *raw_path)
{
	FILE *f_opener;
	f_opener = fopen(bmp_path, "rb+");
	FILE *f_saver;
	f_saver = fopen(raw_path, "wb+");

	unsigned short fileType;
	fread(&fileType, 1, sizeof(unsigned short), f_opener);
	fread(&FileHeader, 1, sizeof(BITMAPFILEHEADER), f_opener);
	fread(&InfoHeader, 1, sizeof(BITMAPINFOHEADER), f_opener);

	//fwrite(&fileType, sizeof(fileType), 1, f_saver);//位图类型单独读取
	//fwrite(&FileHeader, sizeof(BITMAPFILEHEADER), 1, f_saver);//文件头
	//fwrite(&InfoHeader, sizeof(BITMAPINFOHEADER), 1, f_saver);//信息头

	printf("24位图像!!!\n");
	int width = InfoHeader.biWidth;
	int height = InfoHeader.biHeight;
	//三个通道横着拼在一起,width就变成了512*3=1536
	//int real_width = width * 3;
	int real_width = WIDTHBYTES(width* InfoHeader.biBitCount);//计算位图的实际宽度并确保它为4byte的倍数  

	//开辟内存空间存放bmp源图像数据
	unsigned char *PixDataBmp = (unsigned char *)malloc(height*real_width);
	//初始化pColorData,里面的内容是0,大小为height*real_width
	memset(PixDataBmp, 0, height*real_width);

	//开辟内存空间存储raw图像处理之后数据
	unsigned char *PixDataRaw = (unsigned char *)malloc(height*real_width);
	//初始化pColorData,里面的内容是0,大小为height*real_width
	memset(PixDataRaw, 0, height*real_width);

	//开辟内存空间存储raw图像处理之后数据
	unsigned char *PixDataRawClone = (unsigned char *)malloc(height*real_width);
	//初始化pColorData,里面的内容是0,大小为height*real_width
	memset(PixDataRawClone, 0, height*real_width);

	long nData = height * real_width;

	//把bmp位图数据信息读到数组里,这里面存的就是像素值,pColorData类似于摊平的矩阵
	fread(PixDataBmp, 1, nData, f_opener);

	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			//数组位置偏移量,对应于图像的各像素点RGB的起点  i * real_width的作用相当于另起一行
			int pixel_index = i * real_width + j * 3;
			PixDataRaw[pixel_index+2] = PixDataBmp[nData-pixel_index];
			PixDataRaw[pixel_index + 1] = PixDataBmp[nData-pixel_index + 1];
			PixDataRaw[pixel_index] = PixDataBmp[nData-pixel_index + 2];
		}
	}

	//clone数据PixDataRaw
	for (int i = 0; i < height; i++)
	{
		for (int j = 0; j < width; j++)
		{
			//数组位置偏移量,对应于图像的各像素点RGB的起点  i * real_width的作用相当于另起一行
			int pixel_index = i * real_width + j * 3;
			PixDataRawClone[pixel_index + 2] = PixDataBmp[nData - pixel_index];
			PixDataRawClone[pixel_index + 1] = PixDataBmp[nData - pixel_index + 1];
			PixDataRawClone[pixel_index] = PixDataBmp[nData - pixel_index + 2];
		}
	}


	for (int i = 0; i < height; i++) 
	{
		for (int j = 0; j < width; j++) 
		{
			int index = i * real_width + j * 3;
			int exchange_x = width - j - 1;
			int exchange_y = i;

			for (int channels = 0; channels < 3; channels++) 
			{
				PixDataRaw[exchange_y * real_width + exchange_x * 3 + channels] = PixDataRawClone[index + channels] ;
			    PixDataRaw[index + channels] = PixDataRawClone[exchange_y * real_width + exchange_x * 3 + channels];
			}
		}
	}


	fwrite(PixDataRaw, 1, nData, f_saver);//将获取到的像素值写入raw文件
	
	fclose(f_opener);
	fclose(f_saver);
	free(PixDataRaw);
	free(PixDataBmp);
}

3.3 Photoshop查看生成的lena_C.raw

数字图像处理大作业-BMP文件的读写_第9张图片

4.读取原始图像左上角1/4 的数据,另存一个 lena_Cs.bmp 图像,在 photoshop 查看

与灰度bmp实现相似,不同之处在彩色图是三通道,需要BGR三个通道操作,而灰度图只需要操作一个通道。

4.1 编写裁剪函数

//24位bmp裁剪为1/4
void ColorCutBmp(const char *bmp_path, const char *cutBmp_path) 
{

	BITMAPFILEHEADER writebitHead;
	BITMAPINFOHEADER writebitInfoHead;

	int Cut_height = 256;
	int Cut_width  = 256;
	int x_left = 0;
	int y_left = 0;

	FILE *f_opener;
	f_opener = fopen(bmp_path, "rb+");
	FILE *f_saver;
	f_saver = fopen(cutBmp_path, "wb+");

	//读取位图文件头信息
	unsigned short  fileType;
	fread(&fileType, 1, sizeof(unsigned short), f_opener);
	fwrite(&fileType, 1, sizeof(unsigned short), f_saver);
	if (fileType != 0x4d42)
	{
		printf("该文件不是bmp格式文件!");
		return;
	}
	//读取位图文件头信息
	fread(&FileHeader, 1, sizeof(BITMAPFILEHEADER), f_opener);
	writebitHead = FileHeader;//由于截取图像头和源文件头相似,所以先将源文件头数据赋予截取文件头
	//读取位图信息头信息
	fread(&InfoHeader, 1, sizeof(BITMAPINFOHEADER), f_opener);
	writebitInfoHead = InfoHeader;//同位图文件头相似

	writebitInfoHead.biHeight = Cut_height;//为截取文件重写位图高度
	writebitInfoHead.biWidth = Cut_width;//为截取文件重写位图宽度
	int mywritewidth = WIDTHBYTES(writebitInfoHead.biWidth*writebitInfoHead.biBitCount);//BMP图像实际位图数据区的宽度为4byte的倍数,在此计算实际数据区宽度
	writebitInfoHead.biSizeImage = mywritewidth * writebitInfoHead.biHeight;//计算位图实际数据区大小

	writebitHead.bfSize = 54 + writebitInfoHead.biSizeImage;//位图文件头大小为位图数据区大小加上54byte
	fwrite(&writebitHead, 1, sizeof(BITMAPFILEHEADER), f_saver);//写回位图文件头信息到输出文件
	fwrite(&writebitInfoHead, 1, sizeof(BITMAPINFOHEADER), f_saver);//写回位图信息头信息到输出文件

	int width = InfoHeader.biWidth;
	int height = InfoHeader.biHeight;
	//分配内存空间把源图存入内存   
	int l_width = WIDTHBYTES(width*InfoHeader.biBitCount);//计算位图的实际宽度并确保它为4byte的倍数
	int write_width = WIDTHBYTES(writebitInfoHead.biWidth*writebitInfoHead.biBitCount);//计算写位图的实际宽度并确保它为4byte的倍数

	unsigned char *pColorData = (unsigned char *)malloc(height*l_width);//开辟内存空间存储图像数据
	memset(pColorData, 0, height*l_width);

	unsigned char *pColorDataWrite = (unsigned char *)malloc(mywritewidth*Cut_height);//开辟内存空间存储图像处理之后数据
	memset(pColorDataWrite, 0, mywritewidth*Cut_height);

	long nData = height * l_width;
	long write_nData = mywritewidth * Cut_height;//截取的位图数据区长度定义

	//把位图数据信息读到数组里   
	fread(pColorData, 1, nData, f_opener);//图像处理可通过操作这部分数据加以实现

	//由于BMP图像的数据存储格式起点是图像的左下角,所以需要进行坐标换算操作
	for (int i = height - y_left - Cut_height; i < height - y_left; i++)
	{
		for (int j = 0; j < x_left + Cut_width; j++)
		{
			//24位真彩图像素赋值格式
			int index = i * l_width + j * 3;//数组位置偏移量,对应于图像的各像素点RGB的起点
			int write_index = (i - height + y_left + Cut_height)*mywritewidth + (j - x_left) * 3;
			pColorDataWrite[write_index] = pColorData[index];
			pColorDataWrite[write_index + 1] = pColorData[index + 1];
			pColorDataWrite[write_index + 2] = pColorData[index + 2];//不要转换通道,因为这不是转为raw

		}
	}
		

	fwrite(pColorDataWrite, 1, write_nData, f_saver);   //将处理完图像数据区写回文件

	fclose(f_opener);
	fclose(f_saver);
	free(pColorData);
	free(pColorDataWrite);
}

4.2 Photoshop查看生成的lena_Cs.bmp文件

数字图像处理大作业-BMP文件的读写_第10张图片

四、总结

所有生成的图像都在下方
数字图像处理大作业-BMP文件的读写_第11张图片

五、参考文章

利用C语言读取BMP文件
C/C++ BMP(24位真彩色)图像处理(1)------图像の打开与数据区处理
C++实现BMP转RAW

你可能感兴趣的:(数字图像处理,图像处理,计算机视觉,c++)