直方图均衡化
用VC++实现位图直方图均衡化和显示
1、首先建立一个工程。打开VC++6.0,单击 文件【files 】→新建【new 】→工程【projects 】在打开的projects 下选择MFC App Wizard [exe]→在project name 下输入自己的工程名例如(Zhifangtujunhenghua )→单击【ok 】→在打开的对话框中选择基于单文档【single document】→在第四步 “MFC App Wizard step 4 of 6”面板中删掉【隐藏工具栏】和【打印和打印预览】两个选项,之后的全部选择默认,单击finish ,出现一个“New Project Information”窗口,单击【ok 】。一个简单的工程框架就建好了。
2、现在我们正式开始在新建工程Zhifangtujunhenghua 中进行编程实现bmp 位图的直方图均衡化。点击左边框中的【ResourceView 】框找到【Menu 】点开,双击Menu 下的图标,在右面的显示框中创建两个菜单:
打开 ID: ID_FILE_OPEN
直方图均衡化 ID : ID_ZHIFANGTU
3、给这两个菜单建立类向导。在右边的窗口中右击“打开”,选择“建立类向导”,然后在打开的对话框中按下图1操作,“Class name”选择“CZhifangtujunhenghuaView ” →”Object IDs”中选择
“ID_FILE_OPEN” →“Message ”中选择“COMMAND ”, 点击“Add Function ”键就会在“Member functions”中如下图1显示,然后点击“Edit Code”,在自动生成的OnFileOpen 函数中编写代码:
void CZhifangtujunhenghuaView::OnFileOpen()
{
// TODO: Add your command handler code here
CFileDialog fileDlg(true);
fileDlg.m_ofn.lpstrTitle="图片打开对话框";
fileDlg.m_ofn.lpstrFilter="BMP Files(*.bmp)\0*.bmp\0\0";
if(IDOK==fileDlg.DoModal())
{
m_fileName.Format ("%s",fileDlg.GetPathName());
m_Dib.LoadFile(m_fileName);
Invalidate();
}
}
图1
同理,操作“直方图均衡化” ,如下图2显示
图2
选择“CZhifangtujunhenghuaView ”。点击“Edit Code”,在自动生成的“OnZhifangtu ”
函数中编写代码:
void CZhifangtujunhenghuaView::OnZhifangtu()
{
// TODO: Add your command handler code here
if (m_Dib.IsValid())
{
m_Dib.fenbujunhenghua();
Invalidate();
}
}4、创建一个处理位图的类CDib ,且CDib 类是由CObject 类派生出来的。点击工程菜单栏中的插入【insert 】→【insert class】→类类型选“Generic Class”, →类名称填“CDib ” →【ok 】。可以看见工程窗口的Class View 中多了一个 CDib 类;点开CDib 类的头文件,输
入以下代码,注意变量可以直接复制,声明的函数不要直接复制(在CDib 类右击选择“Add member function”,输入函数类型和函数名)。 class CDib: public CObject
{
public:
RGBQUAD * m_pRGB;
BYTE * m_pData;
UINT m_numberOfColors;
bool m_valid;
BITMAPFILEHEADER bitmapFileHeader;//定义了一个文件头结构体的对象
BITMAPINFOHEADER * m_pBitmapInfoHeader;//定义了一个指向信息头的结构体指针 BITMAPINFO * m_pBitmapInfo;//定义了一个结构体指针,BITMAPINFO 是一个包含有信息头,和调色板的
BYTE * pDib;
DWORD size;
char m_fileName[256];
public:
int dwWidthBytes;
int byBitCount;
void fenbujunhenghua();
void zhifangtu(float *tongji);
void SaveFile(const CString filename);
void LoadFile(const char * dibFileName);
WORD PaletteSize(LPBYTE lpDIB);
WORD DIBNumColors(LPBYTE lpDIB);
BITMAPINFO * GetInfo();
BYTE * GetData();
RGBQUAD * GetRGB();
UINT GetWidth();
UINT GetHeight();
UINT GetNumberOfColors();
DWORD GetSize();
bool IsValid();
char * GetFileName();
CDib();
virtual ~CDib();};
5、对CDib 类中的函数定义,找到Dib.cpp 输入代码:
CDib::CDib()
{
m_numberOfColors=0;
size = 0;
m_valid=0;
byBitCount=0;
dwWidthBytes=0;
}
CDib::~CDib()
{
GlobalFreePtr(m_pBitmapInfo);
}
char * CDib::GetFileName()
{
return m_fileName;
}
bool CDib::IsValid()
{
return m_valid;
}
DWORD CDib::GetSize()
{
if (m_pBitmapInfoHeader->biSizeImage!=0)
{
return m_pBitmapInfoHeader->biSizeImage;
}
else
{
DWORD height = (DWORD)GetHeight();
DWORD width = (DWORD)GetWidth();
return height * width;
}
}
UINT CDib::GetNumberOfColors()
{
int numberOfColors;
if ((m_pBitmapInfoHeader->biClrUsed==0)&&(m_pBitmapInfoHeader->biBitCount
//biBitCount为用到的颜色的位数,小于9则表示最大为8位,那么之多为256色 {
switch(m_pBitmapInfoHeader->biBitCount)
{
case 1: numberOfColors = 2;break;
case 4: numberOfColors = 16;break;
case 8: numberOfColors = 256;break;
}
}
else//若不是上面的情况,则直接返回颜色数
{
numberOfColors=(int)m_pBitmapInfoHeader->biClrUsed;
}
return numberOfColors;
}
UINT CDib::GetHeight()
{
return (UINT)m_pBitmapInfoHeader->biHeight;
}
UINT CDib::GetWidth()
{
return (UINT)m_pBitmapInfoHeader->biWidth;
}
RGBQUAD * CDib::GetRGB()
{
return m_pRGB;
}
BYTE * CDib::GetData()
{
return m_pData;
}
BITMAPINFO * CDib::GetInfo()
{
return m_pBitmapInfo;
}
WORD CDib::DIBNumColors(LPBYTE lpDIB)
{
WORD wBitCount; //DIB bit count
wBitCount = ((LPBITMAPCOREHEADER)lpDIB)->bcBitCount;
switch(wBitCount)
{
case 1:
return 2;
case 4:
return 16;
case 8:
return 256;
default:
return 0;
}
}
WORD CDib::PaletteSize(LPBYTE lpDIB)
{
return(DIBNumColors(lpDIB)*sizeof(RGBTRIPLE));
}
void CDib::LoadFile(const char *dibFileName)
{
strcpy(m_fileName,dibFileName);//将路径名称拷贝到m_fileName之中
CFile dibFile(m_fileName,CFile::modeRead);//创建CFile 类对象,只读方式
dibFile.Read((void *)&bitmapFileHeader,sizeof(BITMAPFILEHEADER));//读取文件头的内容
if(bitmapFileHeader.bfType == 0x4d42)//判断是否为bmp 格式,单步调试你会发现,此时的bfType 值
{
DWORD fileLength = dibFile.GetLength();//读取文件的大小,你可以试试跟踪此值来看看它是否和你要打开的图片大小一致
size = fileLength - sizeof(BITMAPFILEHEADER);;//文件大小-文件头结构体的大小,此时你会发现,文件头的大小的确是14字节
pDib = (BYTE *)GlobalAllocPtr(GMEM_MOVEABLE,size);//详见说明(2)
dibFile.Read((void *)pDib,size);//通过读取,把读出的数据存入刚才分配的内存之中 dibFile.Close();//文件操作完成之后关闭文件
m_pBitmapInfo=(BITMAPINFO *)pDib;//BITMAPINFO结构体指针指向该内存 m_pBitmapInfoHeader = (BITMAPINFOHEADER *)pDib;//信息头指向该内存
m_pRGB = (RGBQUAD *)(pDib + m_pBitmapInfoHeader->biSize);//调色板指针指向该内存的调色板部分。因为pDib 原本指向信息头,偏移40字节(信息头结构体的大小)之后便到了调色板部分,因此用加法来实现指针的偏移
int m_numberOfColors = GetNumberOfColors();//调用GetNumberOfColors 函数来得到颜色数
if(m_pBitmapInfoHeader->biClrUsed == 0)
{
m_pBitmapInfoHeader->biClrUsed=m_numberOfColors;//把颜色数赋予biClrUsed 之中
}
DWORD colorTableSize=m_numberOfColors * sizeof(RGBQUAD);//用每个调色板结
构体大小乘以颜色数量,得到调色板的大小
m_pData = pDib + m_pBitmapInfoHeader->biSize+ colorTableSize;//这时候代表把m_pData指针指向实际图像数据了
if (m_pRGB == (RGBQUAD *)m_pData) // 如果调色板指针位置和实际图像位置指针指向位置相同,那就代表没有调色板
{
m_pRGB = NULL;//指针赋予空
}
m_pBitmapInfoHeader->biSizeImage = GetSize();//赋予实际位图的大小
m_valid = true;
}
else//如果不是bmp 位图则失败
{
m_valid = false;
AfxMessageBox("This isn't a bitmap file!");
}
}
void CDib::SaveFile(const CString filename)
{
strcpy(m_fileName,filename);
CFile dibFile(m_fileName,CFile::modeCreate|CFile::modeWrite);
dibFile.Write((void *)&bitmapFileHeader,sizeof(BITMAPFILEHEADER));
dibFile.Write((void *)pDib,size);
dibFile.Close();
}
void CDib::zhifangtu(float *tongji)
{
int i;
int j;
int huidu[256]; //灰度计数
int wide,height; //原图长、宽
//变量初始化
memset(huidu,0,sizeof(huidu));
wide=this->GetWidth();
height=this->GetHeight();
LPBYTE temp1=new BYTE[wide*height]; //新图像缓冲区
//复制原图像到缓存 图像
memcpy(temp1,m_pData,wide*height);
for(i=0;i
{
for(j=0;j
{
unsigned char temp=temp1[wide*i+j];
//灰度统计计数
huidu[temp]++;
}
}
//计算灰度分布密度
for(i=0;i
tongji[i]=huidu[i]/(height*wide*1.0f);
}
void CDib::fenbujunhenghua()
{
long i;
long j;
float fps_R[256];
float temp_r[256];
int nNs_R[256];
memset(temp_r,0,sizeof(temp_r));
LPBYTE p_data;
p_data=this->GetData();
zhifangtu(fps_R);
for(i=0;i
{
if(i==0)
{
temp_r[0]=fps_R[0];
}
else
{
temp_r[i]=temp_r[i-1]+fps_R[i];
}
nNs_R[i]=(int)(255.0f*temp_r[i]+0.5f);
}
LONG wide=GetWidth();
LONG height=GetHeight();
for(i=0;i
{
for(j=0;j
{
unsigned char temp=*((unsigned char*)p_data+wide*i+j);
*((unsigned char*)p_data+wide*i+j)=nNs_R[temp];
}
}
}
6、找到类CZhifangtujunhenghuaView 中的OnDraw(CDC* pDC)函数输入以下代码:
void CZhifangtujunhenghuaView::OnDraw(CDC* pDC)
{
CZhifangtujunhenghuaDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
if (m_Dib.IsValid())
{
int m_scale=1;
BYTE * pBitmapData=m_Dib.GetData();
LPBITMAPINFO pBitmapInfo = m_Dib.GetInfo();
int bitmapHeight = m_Dib.GetHeight();
int bitmapWidth=m_Dib.GetWidth();
int scaleWidth=(int)(bitmapWidth*m_scale);
int scaleHeight=(int)(bitmapHeight*m_scale);
::StretchDIBits(pDC->GetSafeHdc(),0,0,scaleWidth,scaleHeight,0,0,bitmapWidth,bitmapHeight,pBitmapData,pBitmapInfo,DIB_RGB_COLORS,SRCCOPY); }
}