实验目的
掌握BMP文件的读写方法;
掌握数组的使用方法;
数组的遍历和数组元素的操作;
动态分配一维和二维数组;
实验内容
后面的实验需要从图片中读入数据,进行处理。我们使用格式比较简单的BMP图像文件。我们先仔细学习一下BMP文件格式的定义和容易出错的地方,下面是关于BMP文件的说明。
BMP文件的读写
BMP文件格式是Windows操作系统推荐和支持的标准图像文件格式,是一种将内存或显示器的图像数据不经过压缩而直接按位存盘的文件格式,故称位图(bitmap),其扩展名为BMP。BMP图像通常有4个部分组成:文件头、信息头、颜色表、数据。
实验提供的图像是24位真彩色图像,即biBitCount=24,每个像素有3个值(RGB三个通道)来表示,因此没有颜色表那部分数据。为简单起见,我们接下来的实验是要把图像读入二维数组后再做其他处理,因此要把这个三维的数据(行、列、通道)转换为二维数据(只有行、列)。因此,实验要求在读取BMP文件的时候,把每个像素的三个像素值转换成一个值,一个简单的做法是取RGB三个通道的平均数。
本次实验读入一幅图像,比如“Fruits.bmp”。将图像数据读入到内存中动态分配的二维数组中。图像的大小是height行,width列像素,每个像素占3个字节,类型是unsigned char的整数(即在区间[0,255]内)。在读取的时候把每个像素的3个通道的值求平均,存入二维数组对应的位置。按照下面的任务处理完后,再写入一幅新的BMP文件。注意,因为在读取的时候计算3通道的平均值,丢失了颜色信息,我们是无法再还原成彩色图像了,因此在把结果保存成BMP图像文件的时候,需要把二维数组的每一个值赋值到新图像的3个通道,结果图像虽然内部有RGB三个通道的数据,但是由于都是同一个值,因此图像没有颜色,称为灰度图。
请编程实现:
- 读取给定的BMP图像。通过读取文件信息头里的biSizeImage的值,验证BMP每行像素所占字节数是否补0达到4的整数倍。
- 动态分配二维数组a,将图像数据“降维”后读入a中。这里的“降维”指的是把3通道的彩色图像读取到单通道的二维数组中。
- 将数组a的元素上下翻转。即第一行变为最后一行。即实现函数FlipImageUpDown。
- 将变换后的数组a写入一幅新的BMP图像文件。如果以上步骤正确,新的图像是原图翻转后的图像。
- 将数组a的元素左右翻转。即第一列变为最后一列。即实现函数FlipImageLeftRight。
- 将变换后的数组a写入一幅新的BMP图像文件。如果以上步骤正确,会显示翻转后的图像。
- 将图像缩小为原来尺寸的一半,存入动态分配内存的二维数组b。一个简单的做法是将a中的属于奇数行和奇数列的元素读取写入到b中。操作完成后,将b写回硬盘一个新的BMP文件中。
#include <iostream>
#include <stdio.h>
using namespace std;
typedef unsigned char BYTE;
#pragma pack(push,1)
struct BITMAPFILEHEADER
{
unsigned short bfType;
unsigned int bfSize;
unsigned short bfReserved1;
unsigned short bfReserved2;
unsigned int bfOffBits;
};
#pragma pack(pop)
struct BITMAPINFOHEADER
{
unsigned long biSize; //本结构所占用字节数 40字节
long biWidth; //位图的宽度,以像素为单位
long biHeight; //位图的高度,以像素为单位
unsigned short biPlanes; //目标设备的级别,必须为1
unsigned short biBitCount; //每个像素所需的位数,必须是1(双色)、
//4(16色)、8(256色)或24(真彩色)之一
unsigned long biCompression; //位图压缩类型,必须是 0(BI_RGB不压缩)、
//1(BI_RLE8压缩类型)
//2(BI_RLE压缩类型)之一
unsigned long biSizeImage; //位图的大小,以字节为单位
long biXPelsPerMeter; //位图水平分辨率,每米像素数
long biYPelsPerMeter; //位图垂直分辨率,每米像素数
unsigned long biClrUsed; //位图实际使用的颜色表中的颜色数
unsigned long biClrImportant; //位图显示过程中重要的颜色数
};
BYTE** readfile(char *filename,BITMAPFILEHEADER *fileHeader,BITMAPINFOHEADER *infoHeader)
{
FILE* pfin = fopen(filename, "rb");
if(pfin==NULL)
{
printf("文件打开失败");
exit(0);
}
fread(fileHeader, sizeof(BITMAPFILEHEADER), 1, pfin);
fread(infoHeader, sizeof(BITMAPINFOHEADER), 1, pfin);
//int LineByte1 = (infoHeader->biWidth * 8 / 8 + 3) / 4 * 4;
int height, weight;
height = infoHeader->biHeight;
weight = infoHeader->biWidth;
BYTE **p=new BYTE *[height];//动态内存开辟
for(int i=0; i<height; i++)
{
p[i]=new BYTE[weight];
}
BYTE a,b,c;
int k;
//fread(img, sizeof(RGB), size, pfin);
for (int i = 0; i < height; i++)
{
for (int j = 0; j < weight; j++)
{
fread(&a, sizeof(BYTE), 1, pfin);
fread(&b, sizeof(BYTE), 1, pfin);
fread(&c, sizeof(BYTE), 1, pfin);//读出三通道
p[i][j]=(a+b+c)/3;//转为灰度图,存入数组
}
}
fclose(pfin);
return p;
}
void writefile(BITMAPFILEHEADER fileHeader,BITMAPINFOHEADER infoHeader,BYTE **p)
{
FILE* pfout = fopen("imgSave.bmp", "wb");
if(pfout==NULL)
{
printf("文件打开失败");
exit(0);
}
fwrite(&fileHeader, sizeof(fileHeader), 1, pfout);
fwrite(&infoHeader, sizeof(infoHeader), 1, pfout);
// fwrite(img, sizeof(RGB), size, pfout);
int height, weight;
height = infoHeader.biHeight;
weight = infoHeader.biWidth;
int i,j,k;
for (i = 0; i < height; i++)
{
for (j = 0; j < weight; j++)
{
for(k=0; k < 3; k++)
fwrite(&p[i][j], sizeof(BYTE), 1, pfout);
}
}
fclose(pfout);
for (i = 0; i < height; i++)//释放空间
delete [] p[i];
delete p;
}
BYTE** imageupdown(int height,int weight,BYTE **p)
{
int i,j;
int a[height][weight]= {0};
for(i=0; i<height; i++)
{
for(j=0; j<weight; j++)
{
a[height-1-i][j]=p[i][j];
}
}
for(i=0; i<height; i++)
{
for(j=0; j<weight; j++)
{
p[i][j]=a[i][j];
}
}
return p;
}
BYTE** imageleftright(int height,int weight,BYTE **p)
{
int i,j;
int a[height][weight]= {0};
for(i=0; i<height; i++)
{
for(j=0; j<weight; j++)
{
a[height-i-1][weight-j-1]=p[i][j];
}
}
for(i=0; i<height; i++)
{
for(j=0; j<weight; j++)
{
p[i][j]=a[i][j];
}
}
return p;
}
BYTE** imageresize(int height,int weight,BYTE **p)
{
int i,j;
int a[height/2][weight/2]= {0};
for(i=0; i*2<height; i++)
{
for(j=0; j*2<weight; j++)
{
a[i][j]=p[i*2][j*2];
}
}
for(i=0; i<height/2; i++)
{
for(j=0; j<weight/2; j++)
{
p[i][j]=a[i][j];
}
}
return p;
}
void writefile2(BITMAPFILEHEADER fileHeader,BITMAPINFOHEADER infoHeader,BYTE **p)//图片缩小操作的读入函数
{
FILE* pfout = fopen("imgSave.bmp", "wb");
if(pfout==NULL)
{
printf("文件打开失败");
exit(0);
}
infoHeader.biHeight/=2;
infoHeader.biWidth/=2; //缩小
fwrite(&fileHeader, sizeof(fileHeader), 1, pfout);
fwrite(&infoHeader, sizeof(infoHeader), 1, pfout);
// fwrite(img, sizeof(RGB), size, pfout);
int height, weight;
height = infoHeader.biHeight;
weight = infoHeader.biWidth;
int i,j,k;
for (i = 0; i < height; i++)
{
for (j = 0; j < weight; j++)
{
for(k=0; k < 3; k++)
fwrite(&p[i][j], sizeof(BYTE), 1, pfout);
}
}
fclose(pfout);
for (int i = 0; i < height; i++)
delete [] p[i];
delete p;
}
int main()
{
BITMAPFILEHEADER fileHeader;
BITMAPINFOHEADER infoHeader;
char filename[100];
printf("请输入你要处理的bmp文件名称:\n");
cin>>filename;
printf("\n");
BYTE **p=readfile(filename,&fileHeader,&infoHeader);//先读出文件
int height = infoHeader.biHeight;
int weight = infoHeader.biWidth;
printf("/****数组操作****/\n");
printf("请选择相应操作:\n");
printf("“1”:上下翻转图像;\n");
printf("“2”:左右翻转图像;\n");
printf("“3”:缩小为原来一半;\n");//菜单
int n;
scanf("%d",&n);
printf("\n");
switch (n)
{
case 1:p=imageupdown(height,weight,p);
writefile(fileHeader,infoHeader,p);
printf("上下翻转图像完成\n");
break;
case 2:p=imageleftright(height,weight,p);
writefile(fileHeader,infoHeader,p);
printf("左右翻转图像完成\n");
break;
case 3:p=imageresize(height,weight,p);
writefile2(fileHeader,infoHeader,p);
printf("图像缩小完成\n");
break;
default:printf("输入错误!!!\n");break;
}
return 0;
}