【学习图像处理】之实验一——处理BMP图片

BMP格式

bmp是一种常见的未压缩图像格式,也是大多数图像处理入门课会用到的一种引路格式。具体的BMP图像格式解析请见:BMP图像格式详解

实验内容

  1. 反白图像
  2. 改变调色板的颜色值,看对图像的影响
  3. 将彩色图像变为灰度图像
  4. 将一灰度图像数据变为文本格式存入bmp.txt中,txt文件的一行对应图像文件的一行。将bmp.txt导入到excel中,并用至少三种可视化工具将其图形化显示。

老师已经给出了用于处理BMP格式的类的头文件以及部分函数实现代码(C/C++)。由于函数实现为老师的成果,所以我就不贴出代码了,不过我们可以一起分析一下头文件,看一看这个类有什么数据成员,能完成什么功能。

头文件分析

class BMPFILE {
	BYTE *Imagedata;					//位图数据域
public:
	int imagew, imageh;					//图片的宽度和高度
	int iYRGBnum;						//1:灰度,3:彩色
	RGBQUAD palette[256];				//调色板
	BYTE *pDataAt(int h, int Y0R0G1B2 = 0);	
	//指向图像第h行的位置,Y0R0G1B2表示:灰度(Y)=0,R=0,G=1,B=2
	BOOL AllocateMem();					//为图像分配内存
	BOOL LoadBMPFILE(const char *fname);//从硬盘加载图像
	BOOL SaveBMPFILE(const char *fname);//将图像保存至硬盘
	BMPFILE();							//构造函数,初始化
	~BMPFILE();							//析构函数
};

这里唯一需要解释的可能是pDataAt这个函数,尤其是形参中Y0R0G1B2这个让人摸不着头脑的命名。
首先说功能,这个函数的功能是获取第h行行首像素的位置,也就是求偏移量w=imagew*h,然后再用Imagedata+w去定位。
但是这里要补充一个点,那就是BMP数据在内存中的存储方式。我们知道,对24bit的彩图而言,在硬盘中每位像素会以BGR的格式排列,但这不意味着在内存中也是同样连续存储BGR的。相反,在内存中的真实状态是R、G、B被分别存储于3个矩阵中,因此我们要找到第h行的行首像素时,要分别找到其R、G、B的位置。因此实际偏移量应为:

w=imagew*h+Y0R0G1B2*imagew*imageh

第一部分:图像处理

我们把实验分为两部分,因为bmp2txt并用excel作图那部分卡了我很久,而前面那三个却很简单,我们就先说这简单的部分吧。

1、反白图像

这里所谓的反白其实就是反色,只是对于灰度图而言我们看到的效果是黑白颠倒。我们选用的颜色量级为0-255,因此反色就是用255减去当前的颜色。下面贴出部分代码(只给了大家灰度图的反白,如何做彩图的反色呢?留给读者自行解决)

void Reverse(BMPFILE &src,BMPFILE &des)	//反白函数
{
	for (int i = 0; i < src.imageh; i++)
		for (int j = 0; j < src.imagew; j++)
		{
			//反白图	
			des.pDataAt(i)[j] = 255 - src.pDataAt(i)[j];
		}
}

效果如下

原图:原图
反色效果:
效果

2、改变调色板的数值

在没有要求的情况下,随意修改即可,内容没什么可讲的那我们就来说说调色板吧。

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

上面是RGBQUAD,也就是调色板所用的结构体的定义。可以看到每一个RGBQUAD中定义了蓝、绿、红和一个保留位。如果调色板中R=G=B,则显示的是灰度图;相反,如果RGB值各不相同,那么一共可以有256x256x256种颜色,不过注意,由于调色板是一个有256个RGBQUAD的数组,所以最终的图像最多也就256种颜色。对于rgbReserved这一项,stackoverflow上的这份问答大家可以读一下,相信你会有比较深刻的理解。What is rgbReserved?

3、彩图变灰图

彩图变灰图其实也没什么可说的,因为经过历代程序员的尝试我们已经有了非常完美的转换公式,Y=0.299R + 0.587G + 0.114B。我们的要求只是显示效果的彩转灰,如果真的是要变成灰度图,那么除了让像素颜色按照公式转换,我们还应该对信息头、文件头以及调色板作出修改,这里就不做了。

上个效果吧
原图:
lena
转灰效果图:
lena_gray

第二部分:bmp2txt

1、输出txt函数

说实话这个把bmp数据存入txt的题目要求让我疑惑了很久,很长一段时间大概一天我都在修改类似下面的代码:

bool bmp2txt(const char *cFilename)
{
	FILE *fin,*fout;
	fin=fopen(cFilename, "r+b");
	fout = fopen("bmp.txt", "w+");
	int rc;
	unsigned char buf[1024];
	fseek(fin, sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER), SEEK_SET);
	while ((rc = fread(buf, sizeof(unsigned char), 1, fin)) != 0)
	{
		fwrite(buf, sizeof(unsigned char), rc, fout);
	}
	fclose(fin);
	fclose(fout);
}

为什么卡那么久,主要是这个代码如果把w+改成w+b,再把fseek删掉,写出的二进制文件改成bmp图像是可以正常显示的。这样就让我一度认为方法是没问题的,细节上出错了。包括改为fprintf(fout,"%4d",buf)试图转换为10进制输出也没有解决问题(输出的是重复值,原因未明)。
终于,我决定重新读题,然后我发现了这句话“txt 文件的一行对应图像的一行,按图像显示的顺序存储。
突然间我就悟了,下面这段正确代码很快就写出来了:

bool bmp2txt(const char *cFilename)	//将bmp数据转存入txt,路径为形参
{
	//将像素数据存入TXT文件。
	BMPFILE bmpfile;
	if (!bmpfile.LoadBMPFILE(cFilename))
		return false;
	FILE *outfile;
	if ((outfile = fopen("bmp.txt", "w+")) == NULL)
	{
		printf("ERROR\n");
		return false;
	}
	for (int i = 0; i < bmpfile.imageh; i++)	//按行写入
	{
		for (int j = 0; j < bmpfile.imagew; j++)
		{
			fprintf(outfile, "%4d", bmpfile.pDataAt(i)[j]);
		}
		fprintf(outfile, "\n");
	}
	fclose(outfile);
	return true;
}

这告诉我们还是要认真读题啊。。。

2、Excel作图

这里只给出我认为有意义的一种可视化,因为老师说他只是让我们看看数据在不同表现形式下的效果,理解“有数就有图”。但实际上直方图、折线图都完全没有意义,下面介绍下色阶表示。
我们首先将bmp.txt导入到excel中穷人只能用wps了呜呜呜,如图:
bmp.txt
之后全选(ctrl+a),然后在开始——条件格式——色阶,随意选一个之后缩放到最小,就能看到神奇的画面:
变成图片了!
当然这个灰度图的显示效果是自定义的,你只需要在其他规则中把最低值改为黑色,最高值改为白色即可。这个原理其实还是个调色板,按照数值的大小去显示介于最小值和最大值之间的一种颜色罢了。不过做出来的那一刻还是很有成就感的。

结语

这学期图像处理的第一次实验,说实话就这点内容竟然给了三周时间,不由得吐个槽。。。希望这次疫情能快点结束吧,网课上的太累了!!!

发布了2 篇原创文章 · 获赞 0 · 访问量 29

猜你喜欢

转载自blog.csdn.net/weixin_44151650/article/details/105063237