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;
}