摘要:
本文档为数字图像处理程序的技术报告。程序主要功能为读入8位或24位位图,并可选择性对其进行直方图均衡、灰度线性变换、灰度拉伸等操作。该文档从算法原理和算法实现两方面,通过对算法的文字表述、函数的功能介绍以及主要代码分析注释,阐述了该程序进行图像处理时的方法,并通过实验测试和分析实验结果,验证了程序的正确性和可靠性。
关键词:直方图均衡 灰度线性变换 灰度拉伸
1、任务说明
⏹打开一幅图像,进行直方图均衡。将灰度线性变化,将灰度拉伸。
⏹用C语言或JAVA编程序读出图像文件,并利用算法进行灰度拉伸。
2、算法原理(背景意义,基本算法,扩展算法)
1
2
2.1直方图均衡
直方图均衡化是将原图像通过某种变换,得到一幅灰度直方图为均匀分布的新图像的方法。设图像均衡化处理后,图像的直方图是平直的,即各灰度级具有相同的出现频数,那么由于灰度级具有均匀的概率分布,图像看起来就更清晰了。
在离散情况下,共有L个灰度级,其中第k个灰度级rk出现的像素个数为nk,图像的总像素个数为N,则第k个灰度级出现的概率为:
进行均匀化处理的变换函数为:
2.2灰度线性变换
灰度的线性变换就是将图像中所有的点按照线性灰度变换函数进行变换。该线性灰度变换函数f(x)是一个一维线性函数:
f(x)=fA*x+fB
灰度变换方程为:
D2=f(D1)=k*D+d
式中参数k为线性函数的斜率,d为线性函数的在y轴上的截距,D1为输入图像的灰度,D2为输出的图像灰度。
当k>1时,输出的图像的对比度增大,图像的像素值在变换后全部增大,整体显示效果增强;当0 灰度拉伸又叫对比度拉伸,它是最基本的一种灰度变换,使用的是最简单的分段线性变换函数,它的主要思想是提高图像处理时灰度级的动态范围。 可以有选择的拉伸某段灰度区间以改善输出图像。如图,所示的变换函数的运算结果是将原图在a到b之间的灰度拉伸到c到d之间。如果一幅图像的灰度集中在较暗的区域而导致图像偏暗,可以用灰度拉伸功能来拉伸(斜率>1)物体灰度区间以改善图像;同样如果图像灰度集中在较亮的区域而导致图像偏亮,也可以用灰度拉伸功能来压缩(斜率<1)物体灰度区间以改善图像质量。 图2.1 灰度拉伸 3、算法实现 3 3.1使用语言 C++ 3.2编译环境 VC++6.0 3.3数据结构 1. 2. 3. 3.2 3.3 3.3.1文件头 typedef struct tagBITMAPFILEHEADER { WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits; } BITMAPFILEHEADER, *PBITMAPFILEHEADER; ●bfType代表的是文件格式,所以,从BMP文件的前两个字节我们就可以判断我们要打开的图片是不是位图。 ●bfSize代表的是该位图文件的大小(包含文件头、信息头、调色板(如果有)、像素数据)。它是一个DWORD类型,占4个字节。 ●bfReserved1和bfReserved2都是保留量,因此它们应该都为0。它们合起来占4个字节 ●bfOffBits代表的是像素数据距的偏移地址。如果我们想直接读像素数据的话,可以把文件指针偏移bfOffBits个字节,然后读取就可以了。 ●在保存BMP文件的时候,保存的顺序也是按照bfType、bfSize、bfReserved1、bfReserved2、bfOffBits的顺序的。 1. 2. 3. 3.2 3.3 3.3.1 3.3.2信息头 typedef struct tagBITMAPINFOHEADER{ DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER, *PBITMAPINFOHEADER; ●biSize代表的是该结构体的大小,即40字节。 ●biWidth代表的是位图的宽度,以像素为单位。 ●biHeight代表的是位图的高度,以像素为单位。 ●biPlanes代表的位图的平面数。 ●biBitCount代表的是位图的位数。 ●biCompression代表是位图的压缩方法,由于BMP位图是没有压缩的,所以通常情况下它的值都为0。 ●biSizeImage代表的是位图像素数据的大小,就是高度乘上每行像素所占的字节数。 ●biXPelsPerMeter代表的是水平方向的分辨率,以米为单位。 ●biYPelsPerMeter代表的是垂直方向的分辨率,以米为单位。 ●biClrUsed代表的是位图用到的颜色数,可以设置为0,表示全部用到了。 ●biClrImportant代表的是位图中重要的颜色数,可以设置为0,表示全部都很重要。 1. 2. 3. 3.2 3.3 3.3.1 3.3.2 3.3.3调色板 typedef struct tagRGBQUAD { BYTE rgbBlue; BYTE rgbGreen; BYTE rgbRed; BYTE rgbReserved; } RGBQUAD ●只有8位位图具有调色板。 ●调色板是有若干项(最多256项)构成的,而每一项都是一个RGBQUAD的结构体,也就是说每一项都有一个蓝色、绿色、红色和保留量。 3.4函数说明 3.4 3.4 3.4.1自定义函数说明 bmfh:位图文件头指针 bmih:位图信息头指针 imgData:像素数据指针 bitCount:位图位数 dwWidth:位图宽度; dwHeight:位图高度; wBitCount:位图位数 dwWidth:位图宽度; dwHeight:位图高度; wBitCount:位数; k:线性变换斜率; d:线性变换截距 dwWidth:位图宽度; dwHeight:位图高度; wBitCount:位数; pt1x,pt1y,pt2x,pt2y:两个参考点的坐标 dwWidth:位图宽度; dwHeight:位图高度; wBitCount:位数 size:单个元素的大小,单位是字节指针; nitems:元素个数; stream:提供数据的文件指针 size:单个元素的大小,单位是字节指针; nitems:元素个数; stream:目标文件指针 offset:偏移量; fromwhere:起始地址 source:源内存区; n:预复制字节数 这里只说明处理8位位图的算法,24位位图的处理基本相同,只是分别处理R、G、B三个。 3.5 3.5.1直方图均衡函数 int GrayEqualize(BYTE *image,DWORD dwWidth,DWORD dwHeight,WORD wBitCount) if (bitCount==8) { for (i=0; i grayNum[g]++; //计算各个灰度级个数 } } for (n=0; n<256; n++) { //重新定义灰度级 c=0; for (m=0; m<=n; m++) c+=grayNum[m]; grayMap[n]=(BYTE)(c*255/height/width); } for (i=0; i *(temp+lineBytes*(height-1-i)+j)=grayMap[g]; //给像素点赋上新灰度级 } } } 3.5.2线性变换函数 int LineTranslation(BYTE *image,DWORD dwWidth,DWORD dwHeight,WORD,wBitCount,double k,double d) if (wBitCount==8) { for (i=0; i f=g*k+d; //对灰度级进行线性变换 if(f>255) f=255; if(f<0) f=0; *(image+dwLineBytes*(dwHeight-1-i)+j)=(BYTE)f; //重新为像素点赋上灰度级 } } } 3.5.3 灰度拉伸函数 int GrayStretch(BYTE* image,DWORD dwWidth,DWORD dwHeight,WORD wBitCount,int pt1x,int pt1y,int pt2x,int pt2y) for (n=0; n<=pt1x; n++) { if (pt1x>0) map[n]=(BYTE)pt1y*n/pt1x; //定义0点到点pt1之间新的灰度级,线性拉伸 else map[n]=0; //定义0点之前的灰度级全为0 } for (; n<=pt2x; n++) { if (pt2x!=pt1x) map[n]=pt1y+(BYTE)((pt2y-pt1y)*(n-pt1x)/(pt2x-pt1x)); //定义点pt1到点pt2之间新的灰度级,线性拉伸 else map[n]=pt1y; } for (; n<256; n++) { if (pt2x!=255) map[n]=pt2y+(BYTE)((255-pt2y)*(n-pt2x)/(255-pt2x)); //定义点pt2到点(255,255)之间新的灰度级,线性拉伸 else map[n]=255; } if (wBitCount==8) { for (i=0; i *(image+dwLineBytes*(dwHeight-1-i)+j)=map[g]; //为像素点赋上新的灰度级 } } } 4、实验 4 4.1直方图均衡 图4.1 直方图均衡对比图 直方图均衡后,图像得到了锐化,对比度变得强烈,图像更加清晰。 4.2灰度线性变换 图4.2 灰度线性变换对比图1 图4.3 灰度线性变换对比图2 由图4.2和图4.3可知,在截距不变的情况下,斜率越大,图像灰度越弱,反之亦然。 图4.4 灰度线性变换对比图3 图4.5 灰度线性变换对比图4 由图4.4和图4.5可知,当斜率不变时,截距越大,图像的灰度越弱,反之亦然。 4.3灰度拉伸 图4.6 灰度拉伸对比图 5、结论 通过这次试验,我自学会了用C++进行简单的数字图像处理,基本掌握了对于位图文件的读写和简单操作。实验过程中也遇到很多困难,在老师推荐的一些优秀书籍以及网络的帮助之下都一一解决了,从而充分感受到了数字图像处理的乐趣所在,也得到了充沛的知识。 参考文献 •MSDN2001 •刘海波,沈晶,郭耸. 《Visual C++数字图像处理技术详解》. 机械工业出版社. 2010 •冈萨雷斯. 阮秋琦(译). 《数字图像处理(第2版)》. 电子工业出版社. 2007 •章毓晋. 图像处理和分析. 清华大学出版社. 1990下载本文
3.4.2库函数说明函数 功能说明 输入参数说明 输出参数说明 void PrintMenu() 打印菜单选项 int CheckFilePath(char *filepath) 检查路径是否合法:文件能打开;以bmp为后缀名 filepath:预处理位图路径 路径合法返回0,否则返回-1 void ShowImage(char * filepath) 显示位图 filepath:预处理位图路径 void ReadFileHeader(char *filepath,BITMAPFILEHEADER *bmfh) 读入位图的文件头 filepath:预处理位图路径; void ReadInfoHeader(char *filepath,BITMAPINFOHEADER *bmih) 读入位图的信息头 filepath:预处理位图路径; void ReadPixelData(char *filepath,BYTE *imgData) 读入位图的像素数据 filepath:预处理位图路径; int CreatePalette(RGBQUAD pal[]) 创建8位位图的调色板 pal:调色板指针 创建成功返回0,否则返回-1 LONG GetLineBytes(int imgWidth,int bitCount) 计算每行像素所占的字节数 imgWidth:位图宽度; 返回每行像素所占字节数 int GrayEqualize(BYTE *image,DWORD dwWidth,DWORD dwHeight,WORD wBitCount) 直方图均衡 image:位图像素数据指针; 处理成功返回0,否则返回-1 int LineTranslation(BYTE *image,DWORD dwWidth,DWORD dwHeight,WORD wBitCount,double k,double d) 线性灰度变换 image:位图像素数据指针; 处理成功返回0,否则返回-1 int GrayStretch(BYTE* image,DWORD dwWidth,DWORD dwHeight,WORD wBitCount,int pt1x,int pt1y,int pt2x,int pt2y) 灰度拉伸 image:位图像素数据指针; 处理成功返回0,否则返回-1 void PrintFileHeader(BITMAPFILEHEADER *bmfh) 打印位图的文件头 bmfh:文件头指针 void PrintInfoHeader(BITMAPINFOHEADER *bmih) 打印位图的信息头 bmih:信息头指针 void PrintPixelData(BYTE *imgData,int width,int height,int bitCount) 打印位图的像素数据 image:位图像素数据指针; int SaveAsImage(char *filepath) 另存为位图 filepath:预保存图片指定路径 保存成功返回0,否则返回-1
3.4.3函数依赖关系函数 功能说明 输入参数说明 输出参数说明 int fread(void *ptr, int size, int nitems, FILE *stream) 从一个流中读数据 ptr:接收数据的地址; 返回成功读取的元素个数 int fwrite(void *ptr, int size, int nitems, FILE *stream) 向文件读入写入一个数据块 ptr:输出数据的地址; 返回实际写入的数据块数目 int fseek(FILE *stream, long offset, int fromwhere) 移动文件指针 stream:文件指针; 指针移动成功返回0,否则返回其他值 void *memcpy(void *destin, void *source, unsigned n) 由指定内存区域复制指定个字节到指定内存区域 destin:目标内存区;
3.5核心函数算法流程调用函数 被调用的函数 ReadPixelData ReadFileHeader,ReadInfoHeader,GetLineBytes PrintPixelData GetLineBytes SaveAsImage GetLineBytes