DIP-图像增强
第二节:图像增强(Digital Image Enhancement)前言
遇到一些朦胧图片显示效果不够清晰怎么办?如大雾天气下细节缺失的图片,或夜间成像过于黑暗而丢失很多细节的图片等。图像增强可以有效解决这个问题。一、图像增强是什么?
图像增强是在处理前对原始数据进行质量和信息量的提高。
常见的做法包括对比度增强、空间滤波、密度切片和FCC。
对比度增强或拉伸是通过线性变换扩大原有的灰度范围来实现的。
空间滤波改进了自然发生的线性特征,如断层、剪切带和轮廓线。
密度切片将连续的灰色调范围转换为一系列密度间隔,用单独的颜色或符号来表示不同的特征。
这里我们着重讲解对比度增强以及其中常用的三种方法:线性对比度展宽、非线性动态范围调整、灰度直方图均衡化。
二、方法介绍
1.线性对比度展宽
目的:通过将亮暗差异(即对比度)扩大,来把人所关心的部分强调出来。
算法思想:f(i,j)为旧的位图矩阵函数,g(i,j)为新的位图矩阵函数。他们的对应关系如图所示。
代码如下(示例):
void linearContrastStretch(RGBQUAD** dataOfBmp_src, RGBQUAD** dataOfBmp_dst, DWORD width, DWORD height)
{
double ka = 0.2,kb = 8,kc = 0.2;
int a = 60,b = 120;//范围不应过小
double ga = a*ka;
double gb = b*kb+ga;
for(DWORD i=0;i<height;i++)
for(DWORD j=0;j<width;j++)
{
//printf("%d ",dataOfBmp_dst[i][j].rgbRed);
if(dataOfBmp_src[i][j].rgbRed<a)
dataOfBmp_dst[i][j].rgbRed = (dataOfBmp_dst[i][j].rgbRed * ka + 0.5);
if(dataOfBmp_src[i][j].rgbGreen<a)
dataOfBmp_dst[i][j].rgbGreen = (dataOfBmp_dst[i][j].rgbGreen * ka + 0.5);
if(dataOfBmp_src[i][j].rgbBlue<a)
dataOfBmp_dst[i][j].rgbBlue = (dataOfBmp_dst[i][j].rgbBlue * ka + 0.5);
if(dataOfBmp_src[i][j].rgbRed >= b)
dataOfBmp_dst[i][j].rgbRed = (kc*(dataOfBmp_src[i][j].rgbRed-b) + gb + 0.5);
if(dataOfBmp_src[i][j].rgbGreen >= b)
dataOfBmp_dst[i][j].rgbGreen = (kc*(dataOfBmp_src[i][j].rgbGreen-b) + gb + 0.5);
if(dataOfBmp_src[i][j].rgbBlue >= b)
dataOfBmp_dst[i][j].rgbBlue = (kc*(dataOfBmp_src[i][j].rgbBlue-b) + gb + 0.5);
if(dataOfBmp_src[i][j].rgbRed >= a && dataOfBmp_src[i][j].rgbRed < b)
dataOfBmp_dst[i][j].rgbRed = (kb*(dataOfBmp_src[i][j].rgbRed-a) + ga + 0.5);
if(dataOfBmp_src[i][j].rgbGreen >= a && dataOfBmp_src[i][j].rgbGreen < b)
dataOfBmp_dst[i][j].rgbGreen = (kb*(dataOfBmp_src[i][j].rgbGreen-a) + ga + 0.5);
if(dataOfBmp_src[i][j].rgbBlue >= a && dataOfBmp_src[i][j].rgbBlue < b)
dataOfBmp_dst[i][j].rgbBlue = (kb*(dataOfBmp_src[i][j].rgbBlue-a) + ga + 0.5);
}
}}
效果图:
2.非线性动态范围调整
目的:将暗的部分扩展,而将亮的部分抑制。
算法思想:通过动态范围的压缩,可以将所关心部分的灰度级的变化范围扩大。
动态范围调整方法分为以下两种:
1.线性动态范围调整
2.非线性动态范围调整
线性动态范围调整与上文第一种方法大同小异,这里着重介绍非线性动态范围调整方法
代码如下:
void dynamicRangeAdjustment(RGBQUAD** dataOfBmp_src, RGBQUAD** dataOfBmp_dst, DWORD width, DWORD height)
{
double c = 105.8865;//数值较低时,将暗的部分扩展,将亮的部分抑制 数值高的时候相反
for(DWORD i=0;i<height;i++)
for(DWORD j=0;j<width;j++)
{
dataOfBmp_dst[i][j].rgbRed = (c*log10(dataOfBmp_src[i][j].rgbRed + 1) + 0.5);
dataOfBmp_dst[i][j].rgbGreen = (c*log10(dataOfBmp_src[i][j].rgbGreen + 1) + 0.5);
dataOfBmp_dst[i][j].rgbBlue = (c*log10(dataOfBmp_src[i][j].rgbBlue + 1) + 0.5);
}
}
效果图:
3.非线性动态范围调整灰度直方图均衡化
算法思想:对图像中像素个数多的灰度级进行展宽,而对像素个数少的灰度级进行缩减,从而达到清晰图像的目的。
void histogramEqualization(RGBQUAD** dataOfBmp_src, RGBQUAD** dataOfBmp_dst, DWORD width, DWORD height)
{
printf("%d x %d\n",width,height);
DWORD Nf = width*height;//图像总体像素个数
printf("%d\n",Nf);
DWORD greyRange_R[256] = {
0};//灰度分级 0-255 起初设置int 造成溢出
DWORD greyRange_G[256] = {
0};
DWORD greyRange_B[256] = {
0};
for(DWORD i=0;i<height;i++)
for(DWORD j=0;j<width;j++)
{
greyRange_R[dataOfBmp_src[i][j].rgbRed]++;//统计所有灰度分级的频数
greyRange_G[dataOfBmp_src[i][j].rgbGreen]++;
greyRange_B[dataOfBmp_src[i][j].rgbBlue]++;
}
double hsR[256] = {
0.0};//每个灰度级的分布概率 R G B
double hsG[256] = {
0.0};
double hsB[256] = {
0.0};
for(int i = 0;i<256;i++){
hsR[i] = greyRange_R[i] /(Nf*1.0);
hsG[i] = greyRange_G[i]/(Nf*1.0);
hsB[i] = greyRange_B[i]/(Nf*1.0);
}
//求直方图
double temp1 = 0,temp2 = 0,temp3 = 0;
for(int i=0;i<256;i++){
hsR[i] += temp1;
temp1 = hsR[i];
hsG[i] += temp2;
temp2 = hsG[i];
hsB[i] += temp3;
temp3 = hsB[i];
}
//hs中存放R G B各灰度级的累积分布hp
for(DWORD i=0;i<height;i++)
for(DWORD j=0;j<width;j++)
{
if(dataOfBmp_src[i][j].rgbRed == 0)
dataOfBmp_dst[i][j].rgbRed = 0;
else if(dataOfBmp_src[i][j].rgbRed != 0)
dataOfBmp_dst[i][j].rgbRed = (255*hsR[dataOfBmp_src[i][j].rgbRed] + 0.5);
if(dataOfBmp_src[i][j].rgbGreen == 0)
dataOfBmp_dst[i][j].rgbGreen = 0;
else if(dataOfBmp_src[i][j].rgbGreen != 0)
dataOfBmp_dst[i][j].rgbGreen = (255*hsG[dataOfBmp_src[i][j].rgbGreen] + 0.5);
if(dataOfBmp_src[i][j].rgbBlue == 0)
dataOfBmp_dst[i][j].rgbBlue = 0;
else if(dataOfBmp_src[i][j].rgbBlue != 0)
dataOfBmp_dst[i][j].rgbBlue = (255*hsB[dataOfBmp_src[i][j].rgbBlue] + 0.5);
}
}
效果图:
完整代码:
//ReadBitMap
//
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <math.h>
#define WIDTHBYTES(bits) (((bits)+31)/32*4)
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef long LONG;
#pragma pack(1) //强制内存以1字节为单位对齐
//位图文件头信息结构定义
//其中不包含文件类型信息(由于结构体的内存结构决定,要是加了的话将不能正确读取文件信息)
typedef struct tagBITMAPFILEHEADER {
WORD bfType;
DWORD bfSize; //文件大小
WORD bfReserved1; //保留字,不考虑
WORD bfReserved2; //保留字,同上
DWORD bfOffBits; //实际位图数据的偏移字节数,即前三个部分长度之和
} BITMAPFILEHEADER;
//信息头BITMAPINFOHEADER,也是一个结构,其定义如下:
typedef struct tagBITMAPINFOHEADER{
//public:
DWORD biSize; //指定此结构体的长度,为40
LONG biWidth; //位图宽
LONG biHeight; //位图高
WORD biPlanes; //平面数,为1
WORD biBitCount; //采用颜色位数,可以是1,2,4,8,16,24,新的可以是32
DWORD biCompression; //压缩方式,可以是0,1,2,其中0表示不压缩
DWORD biSizeImage; //实际位图数据占用的字节数
LONG biXPelsPerMeter; //X方向分辨率
LONG biYPelsPerMeter; //Y方向分辨率
DWORD biClrUsed; //使用的颜色数,如果为0,则表示默认值(2^颜色位数)
DWORD biClrImportant; //重要颜色数,如果为0,则表示所有颜色都是重要的
} BITMAPINFOHEADER;
//调色板Palette,当然,这里是对那些需要调色板的位图文件而言的。24位和32位是不需要调色板的。
//(似乎是调色板结构体个数等于使用的颜色数。)
typedef struct tagRGBQUAD {
//public:
BYTE rgbBlue; //该颜色的蓝色分量
BYTE rgbGreen; //该颜色的绿色分量
BYTE rgbRed; //该颜色的红色分量
BYTE rgbReserved; //保留值
} RGBQUAD;
/*函数功能:位图文件头显示
输入参数:BITMAPFILEHEADER* pBmpHead 指向位图文件头结构的指针
(即内存地址,实现主调函数实参和被调函数形参的内存共享)
输出值:没有
*/
void showBmpHead(BITMAPFILEHEADER* pBmpHead)
{
printf("位图文件头:\n");
printf("文件类型:%x\n",pBmpHead->bfType);
printf("文件大小:%d\n",pBmpHead->bfSize);
printf("保留字:%d\n",pBmpHead->bfReserved1);
printf("保留字:%d\n",pBmpHead->bfReserved2);
printf("实际位图数据的偏移字节数:%d\n",pBmpHead->bfOffBits);
}
/*函数功能:位图信息头显示
输入参数:BITMAPINFOHEADER* pBmpInforHead 指向位图文件信息头结构的指针
(即内存地址,实现主调函数实参和被调函数形参的内存共享)
输出值:没有
*/
void showBmpInforHead(BITMAPINFOHEADER* pBmpInforHead)
{
printf("位图信息头:\n");
printf("结构体的长度:%d\n",pBmpInforHead->biSize);
printf("位图宽:%d\n",pBmpInforHead->biWidth);
printf("位图高:%d\n",pBmpInforHead->biHeight);
printf("biPlanes平面数:%d\n",pBmpInforHead->biPlanes);
printf("biBitCount采用颜色位数:%d\n",pBmpInforHead->biBitCount);
printf("压缩方式:%d\n",pBmpInforHead->biCompression);
printf("biSizeImage实际位图数据占用的字节数:%d\n",pBmpInforHead->biSizeImage);
printf("X方向分辨率:%d\n",pBmpInforHead->biXPelsPerMeter);
printf("Y方向分辨率:%d\n",pBmpInforHead->biYPelsPerMeter);
printf("使用的颜色数:%d\n",pBmpInforHead->biClrUsed);
printf("重要颜色数:%d\n",pBmpInforHead->biClrImportant);
}
/*函数功能:颜色表(索引模式)或位图数据区各像素RGB值(真彩模式)显示
输入参数:RGBQUAD* pRGB --- RGBAlpha 四元素结构体指针变量
DWORD num --- 要显示的RGBAlpha 四元素结构体个数,
即颜色表长度或要显示的像素个数
输出值: 没有
*/
void showRgbQuan(RGBQUAD* pRGB, DWORD num)
{
for (DWORD i=0; i<num; i++)
{
if (i%5==0)
{
printf("\n");
}
printf("(%-3d,%-3d,%-3d) ",(pRGB+i)->rgbRed,(pRGB+i)->rgbGreen,(pRGB+i)->rgbBlue);
}
printf("\n");
}
/*函数功能:图像反色
输入参数:RGBQUAD* dataOfBmp_src --- 原图像所有像素(以行为序)对应的
RGBAlpha 四元素结构体指针;
RGBQUAD* dataOfBmp_dst --- 反色后的图像所有像素(以行为序)对应的
RGBAlpha 四元素结构体指针;
DWORD width, DWORD height --- 原图像和输出图像的宽度和高度
(以像素为单位)
输出值: 没有
*/
void reverseColor(RGBQUAD** dataOfBmp_src, RGBQUAD** dataOfBmp_dst, DWORD width, DWORD height)
{
for(DWORD i=0;i<height;i++)
for(DWORD j=0;j<width;j++)
{
dataOfBmp_dst[i][j].rgbRed = 255-dataOfBmp_src[i][j].rgbRed;
dataOfBmp_dst[i][j].rgbGreen = 255-dataOfBmp_src[i][j].rgbGreen;
dataOfBmp_dst[i][j].rgbBlue = 255-dataOfBmp_src[i][j].rgbBlue;
}
}
/*函数功能:彩色图像转化为灰度图象
输入参数:RGBQUAD** dataOfBmp_src --- 原图像所有像素(以行为序)对应的
RGBAlpha 四元素结构体指针;
RGBQUAD** dataOfBmp_dst --- 转化为灰度图象后的图像所有像素(以行为序)对应的RGBAlpha 四元素结构体指针;
BYTE** dataOfBmp_gray --- 转化为灰度图象后的图像所有像素(以行为序)对应的灰度值;
DWORD width, DWORD height --- 原图像和输出图像的宽度和高度
(以像素为单位)
输出值: 没有
*/
void RGB2Gray(RGBQUAD** dataOfBmp_src, RGBQUAD** dataOfBmp_dst, BYTE** dataOfBmp_gray, DWORD width, DWORD height)
{
double gray;
for(DWORD i=0;i<height;i++)
{
for(DWORD j=0;j<width;j++)
{
gray = 0.299*dataOfBmp_src[i][j].rgbRed+0.587*dataOfBmp_src[i][j].rgbGreen+0.114*dataOfBmp_src[i][j].rgbBlue;
dataOfBmp_gray[i][j] = (BYTE)gray;
dataOfBmp_dst[i][j].rgbRed = (BYTE)gray;
dataOfBmp_dst[i][j].rgbGreen = (BYTE)gray;
dataOfBmp_dst[i][j].rgbBlue = (BYTE)gray;
}
}
}
/*函数功能:灰度图象四近邻(flag=0)或八近邻(flag=1)对比度
输入参数:BYTE* dataOfBmp_gray --- 灰度图像所有像素(以行为序)对应的灰度值;
DWORD width, DWORD height --- 原图像和输出图像的宽度和高度
(以像素为单位)
bool flag --- 四近邻或八近邻标志, flag=0为四近邻,flag=1为八近邻
输出值: 四近邻(flag=0)或八近邻(flag=1)对比度
*/
double contrast(BYTE** dataOfBmp_gray, DWORD width, DWORD height, bool flag)
{
DWORD i, j;
double contrast_sum = 0;
int tmp0 = 2, tmp1 = 3, tmp2 = 4;
int num = 0;
if(flag)
{
tmp0 = 3;
tmp1 = 5;
tmp2 = 8;
}
num = 4*tmp0+((width-2)+(height-2))*2*tmp1+((width-2)*(height-2))*tmp2;
for(i=0;i<height;i++)
{
for(j=0;j<width;j++)
{
if(i>0)
{
contrast_sum += pow((dataOfBmp_gray[i-1][j]-dataOfBmp_gray[i][j]),2.0);
if(flag)
{
if(j>0)
{
contrast_sum += pow((dataOfBmp_gray[i-1][j-1]-dataOfBmp_gray[i][j]),2.0);
}
if(j<width-1)
{
contrast_sum += pow((dataOfBmp_gray[i-1][j+1]-dataOfBmp_gray[i][j]),2.0);
}
}
}
if(i<height-1)
{
contrast_sum += pow((dataOfBmp_gray[i+1][j]-dataOfBmp_gray[i][j]),2.0);
if(flag)
{
if(j>0)
{
contrast_sum += pow((dataOfBmp_gray[i+1][j-1]-dataOfBmp_gray[i][j]),2.0);
}
if(j<width-1)
{
contrast_sum += pow((dataOfBmp_gray[i+1][j+1]-dataOfBmp_gray[i][j]),2.0);
}
}
}
if(j>0)
{
contrast_sum += pow((dataOfBmp_gray[i][j-1]-dataOfBmp_gray[i][j]),2.0);
}
if(j<width-1)
{
contrast_sum += pow((dataOfBmp_gray[i][j+1]-dataOfBmp_gray[i][j]),2.0);
}
}
}
return contrast_sum/num;
}
/*函数功能:写(32位)位图(即保存)
输入参数:RGBQUAD* dataOfBmp --- 待存储图像所有像素(以行为序)对应的
RGBAlpha 四元素结构体指针;
DWORD width, DWORD height --- 待存储图像的宽度和高度
(以像素为单位)
输出值: 没有
*/
void saveBmp(RGBQUAD** dataOfBmp, DWORD width, DWORD height)
{
DWORD i, j;
BITMAPFILEHEADER bitHead;
BITMAPINFOHEADER bitInfoHead;
WORD biBitCount = 32;
FILE* pfile;
char strFile[50];
printf("please input the .bmp destination file name:\n");
scanf("%s",strFile);
pfile = fopen(strFile,"wb");//打开文件
if(pfile!=NULL)
{
printf("file %s open success.\n", strFile);
//写位图文件头信息
bitHead.bfType = 0x4d42;
bitHead.bfSize = 0;
bitHead.bfReserved1 = 0;
bitHead.bfReserved2 = 0;
bitHead.bfOffBits = 54;
if(biBitCount<=8)
bitHead.bfOffBits += (DWORD)pow(2, biBitCount)*4;
fwrite(&bitHead,1,sizeof(tagBITMAPFILEHEADER),pfile);
bitInfoHead.biSize = 40;
bitInfoHead.biWidth = width;
bitInfoHead.biHeight = height;
bitInfoHead.biPlanes = 1;
bitInfoHead.biBitCount = biBitCount;
bitInfoHead.biCompression = 0;
bitInfoHead.biSizeImage = 0;
bitInfoHead.biXPelsPerMeter = 0;
bitInfoHead.biYPelsPerMeter = 0;
bitInfoHead.biClrImportant = 0;
bitInfoHead.biClrUsed = 0;
fwrite(&bitInfoHead,1,sizeof(tagBITMAPINFOHEADER),pfile);
if(biBitCount<=8)
{
BYTE tmp=0;
for(i=0; i<(DWORD)pow(2, biBitCount); i++)
{
tmp = (BYTE)i;
fwrite(&tmp,1,4,pfile);
}
}
int l_width = WIDTHBYTES(width * biBitCount)-width*4;//计算为确保位图数据区的实际宽度为32字节的整数倍需添加的0字节个数
for( i=0; i<height; i++)
{
for( j=0; j<width; j+=1)
{
fwrite(&dataOfBmp[height-i-1][j],1,4,pfile);
}
BYTE tmp=0;
for(j=0; j<l_width; j++)
fwrite(&tmp,1,1,pfile);
}
}
fclose(pfile);
}
/*函数功能:图像线性对比度展宽
输入参数:RGBQUAD* dataOfBmp_src --- 原图像所有像素(以行为序)对应的
RGBAlpha 四元素结构体指针;
RGBQUAD* dataOfBmp_dst --- 线性展宽后的图像所有像素(以行为序)对应的
RGBAlpha 四元素结构体指针;
DWORD width, DWORD height --- 原图像和输出图像的宽度和高度
(以像素为单位)
输出值: 没有
*/
void linearContrastStretch(RGBQUAD** dataOfBmp_src, RGBQUAD** dataOfBmp_dst, DWORD width, DWORD height)
{
double ka = 0.4,kb = 2.3,kc = 0.5;
int a = 75,b = 150;
double ga = a*ka;
double gb = b*kb+ga;
for(DWORD i=0;i<height;i++)
for(DWORD j=0;j<width;j++)
{
if(dataOfBmp_src[i][j].rgbRed<a)
dataOfBmp_dst[i][j].rgbRed *= ka;
if(dataOfBmp_src[i][j].rgbGreen<a)
dataOfBmp_dst[i][j].rgbGreen *= ka;
if(dataOfBmp_src[i][j].rgbBlue<a)
dataOfBmp_dst[i][j].rgbBlue *= ka;
if(dataOfBmp_src[i][j].rgbRed >= b)
dataOfBmp_dst[i][j].rgbRed = kc*(dataOfBmp_src[i][j].rgbRed-b) + gb;
if(dataOfBmp_src[i][j].rgbGreen >= b)
dataOfBmp_dst[i][j].rgbGreen = kc*(dataOfBmp_src[i][j].rgbGreen-b) + gb;
if(dataOfBmp_src[i][j].rgbBlue >= b)
dataOfBmp_dst[i][j].rgbBlue = kc*(dataOfBmp_src[i][j].rgbBlue-b) + gb;
if(dataOfBmp_src[i][j].rgbRed >= a && dataOfBmp_src[i][j].rgbRed < b)
dataOfBmp_dst[i][j].rgbRed = kb*(dataOfBmp_src[i][j].rgbRed-a) + ga;
if(dataOfBmp_src[i][j].rgbGreen >= a && dataOfBmp_src[i][j].rgbGreen < b)
dataOfBmp_dst[i][j].rgbGreen = kb*(dataOfBmp_src[i][j].rgbGreen-a) + ga;
if(dataOfBmp_src[i][j].rgbBlue >= a && dataOfBmp_src[i][j].rgbBlue < b)
dataOfBmp_dst[i][j].rgbBlue = kb*(dataOfBmp_src[i][j].rgbBlue-a) + ga;
}
}
/*函数功能:图像动态范围非线性调整
输入参数:RGBQUAD* dataOfBmp_src --- 原图像所有像素(以行为序)对应的
RGBAlpha 四元素结构体指针;
RGBQUAD* dataOfBmp_dst --- 线性展宽后的图像所有像素(以行为序)对应的
RGBAlpha 四元素结构体指针;
DWORD width, DWORD height --- 原图像和输出图像的宽度和高度
(以像素为单位)
输出值: 没有
*/
void dynamicRangeAdjustment(RGBQUAD** dataOfBmp_src, RGBQUAD** dataOfBmp_dst, DWORD width, DWORD height)
{
double c = 105.8865;
for(DWORD i=0;i<height;i++)
for(DWORD j=0;j<width;j++)
{
dataOfBmp_dst[i][j].rgbRed = (c*log10(dataOfBmp_src[i][j].rgbRed + 1) + 0.5);
dataOfBmp_dst[i][j].rgbGreen = (c*log10(dataOfBmp_src[i][j].rgbGreen + 1) + 0.5);
dataOfBmp_dst[i][j].rgbBlue = (c*log10(dataOfBmp_src[i][j].rgbBlue + 1) + 0.5);
}
}
/*函数功能:图像直方图均衡化
输入参数:RGBQUAD* dataOfBmp_src --- 原图像所有像素(以行为序)对应的
RGBAlpha 四元素结构体指针;
RGBQUAD* dataOfBmp_dst --- 线性展宽后的图像所有像素(以行为序)对应的
RGBAlpha 四元素结构体指针;
DWORD width, DWORD height --- 原图像和输出图像的宽度和高度
(以像素为单位)
输出值: 没有
*/
void histogramEqualization(RGBQUAD** dataOfBmp_src, RGBQUAD** dataOfBmp_dst, DWORD width, DWORD height)
{
printf("%d x %d\n",width,height);
DWORD Nf = width*height;//图像总体像素个数
printf("%d\n",Nf);
DWORD greyRange_R[256] = {
0};//灰度分级 0-255 起初设置int 造成溢出
DWORD greyRange_G[256] = {
0};
DWORD greyRange_B[256] = {
0};
for(DWORD i=0;i<height;i++)
for(DWORD j=0;j<width;j++)
{
greyRange_R[dataOfBmp_src[i][j].rgbRed]++;//统计所有灰度分级的频数
greyRange_G[dataOfBmp_src[i][j].rgbGreen]++;
greyRange_B[dataOfBmp_src[i][j].rgbBlue]++;
}
double hsR[256] = {
0.0};//每个灰度级的分布概率 R G B
double hsG[256] = {
0.0};
double hsB[256] = {
0.0};
for(int i = 0;i<256;i++){
hsR[i] = greyRange_R[i] /(Nf*1.0);
hsG[i] = greyRange_G[i]/(Nf*1.0);
hsB[i] = greyRange_B[i]/(Nf*1.0);
}
//求直方图
double temp1 = 0,temp2 = 0,temp3 = 0;
for(int i=0;i<256;i++){
hsR[i] += temp1;
temp1 = hsR[i];
hsG[i] += temp2;
temp2 = hsG[i];
hsB[i] += temp3;
temp3 = hsB[i];
}
//hs中存放R G B各灰度级的累积分布hp
for(DWORD i=0;i<height;i++)
for(DWORD j=0;j<width;j++)
{
if(dataOfBmp_src[i][j].rgbRed == 0)
dataOfBmp_dst[i][j].rgbRed = 0;
else if(dataOfBmp_src[i][j].rgbRed != 0)
dataOfBmp_dst[i][j].rgbRed = (255*hsR[dataOfBmp_src[i][j].rgbRed] + 0.5);
if(dataOfBmp_src[i][j].rgbGreen == 0)
dataOfBmp_dst[i][j].rgbGreen = 0;
else if(dataOfBmp_src[i][j].rgbGreen != 0)
dataOfBmp_dst[i][j].rgbGreen = (255*hsG[dataOfBmp_src[i][j].rgbGreen] + 0.5);
if(dataOfBmp_src[i][j].rgbBlue == 0)
dataOfBmp_dst[i][j].rgbBlue = 0;
else if(dataOfBmp_src[i][j].rgbBlue != 0)
dataOfBmp_dst[i][j].rgbBlue = (255*hsB[dataOfBmp_src[i][j].rgbBlue] + 0.5);
}
}
int main()
{
BITMAPFILEHEADER bitHead;
BITMAPINFOHEADER bitInfoHead;
int i, j;
FILE* pfile;
char strFile[50];
printf("please input the .bmp source file name:\n");
scanf("%s",strFile);
pfile = fopen(strFile,"rb");//打开文件
if(pfile!=NULL)
{
printf("file %s open success.\n", strFile);
//读取位图文件头信息
fread(&bitHead,1,sizeof(BITMAPFILEHEADER),pfile);
if(bitHead.bfType != 0x4d42)
{
printf("file is not .bmp file!");
return 0;
}
showBmpHead(&bitHead);
printf("\n\n");
//读取位图信息头信息
fread(&bitInfoHead,1,sizeof(BITMAPINFOHEADER),pfile);
showBmpInforHead(&bitInfoHead);
printf("\n");
}
else
{
printf("file open fail!\n");
return 0;
}
RGBQUAD* pRgb=NULL;
if(bitInfoHead.biBitCount < 24)//有调色板
{
//读取调色盘结信息
long nPlantNum = bitInfoHead.biClrUsed;
if(!nPlantNum)
nPlantNum = long(pow(2,double(bitInfoHead.biBitCount))); // Mix color Plant Number;
pRgb=new RGBQUAD[nPlantNum*sizeof(RGBQUAD)];
memset(pRgb,0,nPlantNum*sizeof(RGBQUAD));
int num = fread(pRgb,4,nPlantNum,pfile);
printf("Color Plate Number: %d\n",nPlantNum);
printf("颜色板信息:\n");
showRgbQuan(pRgb, nPlantNum);
}
int width = bitInfoHead.biWidth;
int height = bitInfoHead.biHeight;
//分配内存空间把源图存入内存
int l_width = WIDTHBYTES(width* bitInfoHead.biBitCount);//计算位图的实际宽度并确保它为32的倍数
long nData = height*l_width;
BYTE *pColorData= new BYTE[nData];
memset(pColorData,0,nData);
//把位图数据信息读到数组里
fread(pColorData,1,nData,pfile);
//将位图数据转化为RGB数据
RGBQUAD** dataOfBmp_src=NULL; //用于保存各像素对应的RGB数据
dataOfBmp_src = new RGBQUAD*[height];
for(i=0; i < height;i++)
dataOfBmp_src[i] =new RGBQUAD[width];
if(bitInfoHead.biBitCount<24)//有调色板,即位图为非真彩色
{
int k;
if(bitInfoHead.biBitCount <= 8 && !bitInfoHead.biCompression)
{
int pnum = 8/bitInfoHead.biBitCount;
int mbnum = 8-bitInfoHead.biBitCount;
for(int i=0;i<height;i++)
{
int k0 = (height-i-1)*l_width;//k:取得该像素颜色数据在实际数据数组中的序号
for(int j=0;j<width;j++)
{
BYTE mixIndex= 0;
k = k0+(j/pnum);
mixIndex = pColorData[k];
//mixIndex:提取当前像素的颜色的在颜色表中的索引值
if(bitInfoHead.biBitCount < 8)
{
mixIndex = mixIndex<<((j%pnum)*bitInfoHead.biBitCount);
mixIndex = mixIndex>>mbnum;
}
//将像素颜色数据(RGBA)保存到数组中对应的位置
dataOfBmp_src[i][j].rgbRed = pRgb[mixIndex].rgbRed;
dataOfBmp_src[i][j].rgbGreen = pRgb[mixIndex].rgbGreen;
dataOfBmp_src[i][j].rgbBlue = pRgb[mixIndex].rgbBlue;
dataOfBmp_src[i][j].rgbReserved = pRgb[mixIndex].rgbReserved;
}
}
}
if(bitInfoHead.biBitCount == 16)
{
if(!bitInfoHead.biCompression)
{
for( i=0;i<height;i++)
{
int k0 = (height-i-1)*l_width;
for( j=0;j<width;j++)
{
WORD mixIndex= 0;
k = k0+j*2;
WORD shortTemp;
shortTemp = pColorData[k+1];
shortTemp = shortTemp<<8;
mixIndex = pColorData[k] + shortTemp;
dataOfBmp_src[i][j].rgbRed = pRgb[mixIndex].rgbRed;
dataOfBmp_src[i][j].rgbGreen = pRgb[mixIndex].rgbGreen;
dataOfBmp_src[i][j].rgbBlue = pRgb[mixIndex].rgbBlue;
dataOfBmp_src[i][j].rgbReserved = pRgb[mixIndex].rgbReserved;
}
}
}
}
}
else//位图为24/32位真彩色
{
int k;
int index = 0;
if(bitInfoHead.biBitCount == 16)
{
for( i=0;i<height;i++)
{
int k0 = (height-i-1)*l_width;
for( j=0;j<width;j++)
{
k = k0+j*2;
if(!bitInfoHead.biCompression)//555格式
{
dataOfBmp_src[i][j].rgbBlue=pColorData[k]&0x1F;
dataOfBmp_src[i][j].rgbGreen=(((pColorData[k+1]<<6)&0xFF)>>3)+(pColorData[k]>>5);
dataOfBmp_src[i][j].rgbRed=(pColorData[k+1]<<1)>>3;
dataOfBmp_src[i][j].rgbReserved = 0;
}
}
}
}
if(bitInfoHead.biBitCount == 24 && !bitInfoHead.biCompression)
{
for( i=0;i<height;i++)
{
int k0 = (height-i-1)*l_width;
for( j=0;j<width;j++)
{
k = k0+(j*3);
dataOfBmp_src[i][j].rgbRed = pColorData[k+2];
dataOfBmp_src[i][j].rgbGreen = pColorData[k+1];
dataOfBmp_src[i][j].rgbBlue = pColorData[k];
dataOfBmp_src[i][j].rgbReserved = 0;
}
}
}
if(bitInfoHead.biBitCount == 32 && !bitInfoHead.biCompression)
{
for( i=0;i<height;i++)
{
int k0 = (height-i-1)*l_width;
for( j=0;j<width;j++)
{
k = k0+(j*4);
dataOfBmp_src[i][j].rgbRed = pColorData[k+2];
dataOfBmp_src[i][j].rgbGreen = pColorData[k+1];
dataOfBmp_src[i][j].rgbBlue = pColorData[k];
dataOfBmp_src[i][j].rgbReserved = pColorData[k+3];
}
}
}
}
//printf("像素数据信息:\n");
RGBQUAD** dataOfBmp_dst=NULL;
dataOfBmp_dst = new RGBQUAD*[height]; //反色后的图象中每个像素的RGBAlpha
for(i=0; i<height; i++)
dataOfBmp_dst[i] = new RGBQUAD[width];
BYTE** dataOfBmp_gray=NULL;
dataOfBmp_gray = new BYTE*[height];
for(i=0; i<height; i++)
dataOfBmp_gray[i] = new BYTE[width];
//reverseColor(dataOfBmp_src, dataOfBmp_dst, width, height);
//RGB2Gray(dataOfBmp_src, dataOfBmp_dst, dataOfBmp_gray, width, height);
//double contrast8nn = contrast(dataOfBmp_gray, width, height, 1);
linearContrastStretch(dataOfBmp_src, dataOfBmp_dst, width, height);
//dynamicRangeAdjustment(dataOfBmp_src, dataOfBmp_dst, width, height);
//histogramEqualization(dataOfBmp_src, dataOfBmp_dst, width, height);
//printf("the 8nn contrast of the gray image is %f\n", contrast8nn);
saveBmp(dataOfBmp_dst, width, height);
fclose(pfile);
if (bitInfoHead.biBitCount<24 && pRgb)
{
delete []pRgb;
}
for(i=0; i<height; i++)
if(dataOfBmp_src[i])
delete dataOfBmp_src[i];
if(dataOfBmp_src)
delete dataOfBmp_src;
for(i=0; i<height; i++)
if(dataOfBmp_dst[i])
delete dataOfBmp_dst[i];
if(dataOfBmp_dst)
delete dataOfBmp_dst;
for(i=0; i<height; i++)
if(dataOfBmp_gray[i])
delete dataOfBmp_gray[i];
if(dataOfBmp_gray)
delete dataOfBmp_gray;
if(pColorData)
delete []pColorData;
}