昆明理工大学
图像处理
课程设计报告
1.不要删除行尾的分节符,此行不会被打印
目 录
一. 课程设计任务... 2
二. 课程设计原理及设计方案... 3
2.1灰度直方图原理分析... 3
2.2灰度直方图统计的实现方法... 4
2.3图像的灰度变换原理... 5
三. 课程设计的步骤和结果... 6
3.1类声明与核心算法程序代码... 6
3.2直方图均衡化过程实现步骤... 8
3.3图像的灰度变换实现步骤... 2
3.4设计成果... 18
四. 设计体会... 23
五. 参考文献... 24
编写一个基于VC++ 的图像处理程序/软件,功能按钮和界面布局自己设定,需要实现如下两个功能:
1、绘制灰度直方图,实现直方图均衡化及直方图规定化;
2、图像的灰度变换(图像求反,图像的阈值变换,图像的线性变换);
直方图是对图像每一亮度间隔内像元频数的统计。它描绘了图像的总概貌,给出了图像亮度与颜色范围、每个亮度间隔的频数、像元依亮度分布的形态以及彩色分量的色调。但不知道某一亮度值像元出现的概率处于什么位置。将一幅图像划分成一些子图像,整个图像的直方图等于各个子图像的直方图之和,不同的图像可能具有相同的直方图。利用直方图可检验输入图像亮度值在量化范围内是否分配得恰当,图像数字化后的灰度级与实际占用灰度级的提高了很多,有很好的对比度。
试开发一个图像直方图处理软件,要求如下:
1.菜单式的人机界面。
2.具有文件处理功能。
3.对图像进行灰度变换。
图像直方图处理部分应具有直方图均衡化(非适应性直方图修正)和直方图规定化(适应性直方图修正)两个模块。
图像的灰度变换包括图像求反,图像的阈值变换,图像的线性变换。
1、灰度直方图
直方像图反映了图的像素的灰度分布是反映一幅图像中的灰度级与出现这种灰度级的像素的概率之间关系的图形。
直方图的横坐标为灰度级(用r表示),纵坐标是具有该灰度级的像素个数或出现此灰度级的概率P(rk)。
设N(=a×b)为一幅图像中像素总数,nk为第k级灰度的像素数;r k表示第k个灰度级。则:
P(rk)= nk /N (归一化后k级灰度像素数)
定义:反映各灰度级出现频数的分布情况,进而反映图像对(清晰)度,但不反映各灰度级的空间位置分布。
图像的(灰度统计)直方图是一个一维的离散函数。它的定义为:设sk为图像f(x,y)的第k级灰度值,nk是f(x,y)中具有灰度值sk的象素的个数,n是图像象素总数,则:ps(sk)= nk/n k=0,1,¼,L-1称为图像f(x,y)的直方图。这里ps(sk)代表原始图中第k个灰度级的出现概率。以nk为自变量,以ps(sk)为函数,得到的曲线就是图像的直方图,在实际中常常直接将对第k个灰度级的统计值nk作为图像的直方图。它提供了原图灰度值的分布情况,也可以说给出了一幅图所有灰度值的整体描述。
对灰度图像进行直方图统计的程序流程图如图2-2所示。
图2-2 灰度图像直方图统计流程
2、直方图均衡化
如上面所述,一幅给定的图像的灰度级分布在0≤ r ≤1范围内。可以对[0, 1]区间内的任一个 r 值进行如下变换 也就是说,通过上述变换,每个原始图像的像素灰度值 r 都对应产生一个 s 值。
变换函数T(r)应满足下列条件:
(1)在0≤r≤1区间内,T(r)单值单调增加;
(2)对于0≤r≤1,有0≤T(r)≤1。
这里的第一个条件保证了图像的灰度级从白到黑的次序不变。第二个条件则保证了映射变换后的像素灰度值在允许的范围内。
3、直方图规定化
希望能够有目的地增强某个灰度区间的图像, 即能够人为地修正直方图的形状, 使之与期望的形状相匹配,这就是直方图规定化的基本思想。换句话说,希望可以人为地改变直方图形状,使之成为某个特定的形状,直方图规定化就是针对上述要求提出来的一种增强技术,它可以按照预先设定的某个形状来调整图像的直方图。直方图规定化是在运用均衡化原理的基础上,通过建立原始图像和期望图像之间的关系,选择地控制直方图,使原始图像的直方图变成规定的形状,从而弥补了直方图均衡不具备交互作用的特性。
4、BMP图像文件格式
BMP图像文件是Microsoft Windows所规定的图像文件格式。随着Windows的风行全球,BMP图像文件也就成为PC机上流行的图像文件格式。
BMP图像文件具有下列六项特色。
(1)文件结构只能存放一幅图像。
(2)可以存储单色、16色、256色和全彩色四种图像数据。
(3)图像数据可选择压缩或不压缩处理
(4)Windows设计了两种压缩方式:RLE4和RLE8。RLE4处理16色图像数据;而RLE8则是压缩256图像数据。
(5)图像数据排列顺序,与一般图像文件有所不同。
(6)调色板的数据结构特殊。
直方图均衡化过程(算法):
(1) 列出原始图灰度级rk;
(2) 统计原始直方图各灰度级像素数nk;
(3) 计算原始直方图各概率:pk=nk/N;
(4) 计算累计直方图:sk=Σpk;
(5) 取整Sk=int{(L-1)sk+0.5};
(6) 确定映射对应关系:rk?sk;
(7) 统计新直方图各灰度级像素nk';
(8) 用pk (sk) =nk'/N计算新直方图。
利用Visual C++中提供的MFC框架设计一个简单的应用程序框架,具有图像显示,图像直方图均衡、直方图规定化、直方图、直方图均衡后图像等菜单栏,再利用C++具有面向对象程序设计的性质,编写程序代码实现MFC框架中对应菜单栏中的图像处理的功能,即可得到一个简易的图像处理系统,达到课程考核题目的要求。 本次课程考核设计了一个简单的图片处理系统,主要具有以对话框的形式显示原始图片(bmp格式)、显示原图像的直方图、图像直方图均衡化、图像直方图规定化、显示均衡化和规定化处理后的直方图的功能。
灰度即使用黑色调表示物体。每个灰度对象都具有从0%(白色)到100%(黑色)的亮度值。灰度变换处理是图像增强处理技术中一种非常基础、直接的空间域图像处理方法,也是图像数字化和图像显示的一个重要组成部分。灰度变换主要针对独立的像素点进行处理,通过改变原始图像数据所占有的灰度范围而使图像在视觉上得到改观。
// 灰度均衡
void CBitmapView::OnPointEqua()
{
// 获取文档
CBitmapDoc* pDoc = GetDocument();
// 指向DIB的指针
LPSTR lpDIB;
// 指向DIB象素指针
LPSTR lpDIBBits;
// 锁定DIB
lpDIB = (LPSTR) ::GlobalLock((HGLOBAL) pDoc->GetHDIB());
// 找到DIB图像象素起始位置
lpDIBBits = ::FindDIBBits(lpDIB);
// 判断是否是8-bpp位图(这里为了方便,只处理8-bpp位图的直方图均衡,其它的可以类推)
if (::DIBNumColors(lpDIB) != 256)
{
// 提示用户
MessageBox("目前只支持256色位图的直方图均衡!", "系统提示" ,
MB_ICONINFORMATION | MB_OK);
// 解除锁定
::GlobalUnlock((HGLOBAL) pDoc->GetHDIB());
// 返回
return;
}
// 更改光标形状
BeginWaitCursor();
// 调用InteEqualize()函数进行直方图均衡
InteEqualize(lpDIBBits, ::DIBWidth(lpDIB), ::DIBHeight(lpDIB));
// 设置脏标记
pDoc->SetModifiedFlag(TRUE);
// 更新视图
pDoc->UpdateAllViews(NULL);
// 解除锁定
::GlobalUnlock((HGLOBAL) pDoc->GetHDIB());
// 恢复光标
EndWaitCursor();
}
/********************************************************************
* 函数名称:
* InteEqualize()
*
* 参数:
* LPSTR lpDIBBits - 指向源DIB图像指针
* LONG lWidth - 源图像宽度(象素数)
* LONG lHeight - 源图像高度(象素数)
*
* 返回值:
* BOOL - 成功返回TRUE,否则返回FALSE。
*
* 说明:
* 该函数用来对图像进行直方图均衡。
************************************************************************/
BOOL WINAPI InteEqualize(LPSTR lpDIBBits, LONG lWidth, LONG lHeight)
{// 指向源图像的指针
unsigned char* lpSrc;// 临时变量
LONG lTemp; // 循环变量
LONG i;
LONG j; // 灰度映射表
BYTE bMap[256]; // 灰度映射表
LONG lCount[256]; // 图像每行的字节数
LONG lLineBytes; // 计算图像每行的字节数
lLineBytes = WIDTHBYTES(lWidth * 8); // 重置计数为0
for (i = 0; i < 256; i ++)
{
// 清零
lCount[i] = 0;
}
// 计算各个灰度值的计数
for (i = 0; i < lHeight; i ++)
{
for (j = 0; j < lWidth; j ++)
{
lpSrc = (unsigned char *)lpDIBBits + lLineBytes * i + j;
// 计数加1
lCount[*(lpSrc)]++;
}
}
// 计算灰度映射表
for (i = 0; i < 256; i++)
{// 初始为0
lTemp = 0;
for (j = 0; j <= i ; j++)
{
lTemp += lCount[j];
} // 计算对应的新灰度值
bMap[i] = (BYTE) (lTemp * 255 / lHeight / lWidth);
}
// 每行
for(i = 0; i < lHeight; i++)
{// 每列
for(j = 0; j < lWidth; j++)
{
// 指向DIB第i行,第j个象素的指针
lpSrc = (unsigned char*)lpDIBBits + lLineBytes * (lHeight - 1 - i) + j;
// 计算新的灰度值
*lpSrc = bMap[*lpSrc];
}
}// 返回
return TRUE;
}
根据BMP图像文件的基本格式,利用VC编写8bits无压缩BMP图像文件的打开和存储的程序;编写对灰度图像进行直方图统计的程序,并将结果显示在屏幕上。
1、总体流程图
2、参考步骤和程序
打开VC程序——文件——新建——工程中的MFC AppWizard(exe),在工程下面的框中输入工程名(假定工程名为111),点确定——选多重文档,点下一个——后面都点下一个直到完成确定,基本框架就完成了,下面就加代码。
这时VC界面上左边框的下面有三个按钮:ClassView、ResourceView和FileView,ClassView里面是工程111的类:CAdoutDlg、CChildFrame、CMy111App、CMy111Doc、CMy111View和Globals;点ResourceView里面是资源类:Accelerator、Dialog、Icon、Menu、String Table、Toolbar和Version;点开FileView里面是文件类:Source File、Header Files、Resource Files和ReadMe.txt。
点界面的“工程”按钮——添加工程——新建——选C++ Source File,在文件下面的框里输入文件名(如DIBAPI),点“结束”,这样在FileView中的Source Files里面就多了一个DIBAPI.cpp文件,所有的代码都加在该文件中。再点界面的“工程”按钮——添加工程——新建——选C/C++ Header File,在文件下面的框里输入文件名(和前面的文件名必须一致),点“结束”,这样在FileView中的Header Files里面就多了一个DIBAPI.h文件,该文件是DIBAPI.cpp的头文件。
点开DIBAPI.h文件,里面是空白的,把如下代码考入文件中:
//DIBAPI.h
#ifndef _INC_DIBAPI
#define _INC_DIBAPI
DECLARE_HANDLE(HDIB);
#define PALVERSION 0x300
#define IS_WIN30_DIB(lpbi) ((*(LPDWORD)(lpbi))==sizeof(BITMAPINFOHEADER))
#define RECTWIDTH(lpRect) ((lpRect)->right-(lpRect)->left)
#define RECTHEIGHT(lpRect) ((lpRect)->bottom-(lpRect)->top)
#define WIDTHBYTES(bits) (((bits)+31)/32*4)
#define DIB_HEADER_MARKER ((WORD)('M'<<8)|'B')
BOOL WINAPI PaintDIB(HDC,LPRECT,HDIB,LPRECT,CPalette* pPal);
BOOL WINAPI CreateDIBPalette(HDIB hDIB,CPalette* cPal);
LPSTR WINAPI FindDIBBits(LPSTR lpbi);
DWORD WINAPI DIBWidth(LPSTR lpDIB);
DWORD WINAPI DIBHeight(LPSTR lpDIB);
WORD WINAPI PaletteSize(LPSTR lpbi);
WORD WINAPI DIBNumColors(LPSTR lpbi);
HGLOBAL WINAPI CopyHandle(HGLOBAL h);
BOOL WINAPI SaveDIB(HDIB hDib,CFile& file);
HDIB WINAPI ReadDIBFile(CFile& file);
//在此处输入自己的函数声明
#endif//!_INC_DIBAPI
上面这些函数是实现图像的读取、存储等图像处理的基本功能的,你将自己需要的函数也输入到“//在此处输入自己的函数声明”的下面。
点开DIBAPI.cpp文件,里面是空白的,将如下代码加入其中:
//DIBAPI.cpp
#include "stdafx.h"
#include "DIBAPI.h"
WORD WINAPI DIBNumColors(LPSTR lpbi)
{
WORD wBitCount;
if(IS_WIN30_DIB(lpbi))
{
DWORD dwClrUsed;
dwClrUsed=((LPBITMAPINFOHEADER)lpbi)->biClrUsed;
if(dwClrUsed)
return (WORD)dwClrUsed;
}
if(IS_WIN30_DIB(lpbi))
wBitCount=((LPBITMAPINFOHEADER)lpbi)->biBitCount;
else
wBitCount=((LPBITMAPCOREHEADER)lpbi)->bcBitCount;
switch(wBitCount)
{
case 1:
return 2;
case 4:
return 16;
case 8:
return 256;
default:
return 0;
}
}
WORD WINAPI PaletteSize(LPSTR lpbi)
{
if(IS_WIN30_DIB(lpbi))
return (WORD)(DIBNumColors(lpbi)*sizeof(RGBQUAD));
else
return (WORD)(DIBNumColors(lpbi)*sizeof(RGBTRIPLE));
}
LPSTR WINAPI FindDIBBits(LPSTR lpbi)
{
return (lpbi+*(LPDWORD)lpbi+::PaletteSize(lpbi));
}
DWORD WINAPI DIBWidth(LPSTR lpDIB)
{
LPBITMAPINFOHEADER lpbmi;
LPBITMAPCOREHEADER lpbmc;
lpbmi=(LPBITMAPINFOHEADER)lpDIB;
lpbmc=(LPBITMAPCOREHEADER)lpDIB;
if(IS_WIN30_DIB(lpDIB))
return lpbmi->biWidth;
else
return (DWORD)lpbmc->bcWidth;
}
DWORD WINAPI DIBHeight(LPSTR lpDIB)
{
LPBITMAPINFOHEADER lpbmi;
LPBITMAPCOREHEADER lpbmc;
lpbmi=(LPBITMAPINFOHEADER)lpDIB;
lpbmc=(LPBITMAPCOREHEADER)lpDIB;
if(IS_WIN30_DIB(lpDIB))
return lpbmi->biHeight;
else
return (DWORD)lpbmc->bcHeight;
}
BOOL WINAPI PaintDIB(HDC hDC,LPRECT lpDCRect,HDIB hDIB,LPRECT lpDIBRect,CPalette* pPal)
{
LPSTR lpDIBHdr;
LPSTR lpDIBBits;
BOOL bSuccess=FALSE;
HPALETTE hPal=NULL;
HPALETTE hOldPal=NULL;
if(hDIB==NULL)
return FALSE;
lpDIBHdr=(LPSTR)::GlobalLock((HGLOBAL)hDIB);
lpDIBBits=FindDIBBits(lpDIBHdr);
if(pPal!=NULL)
{
hPal=(HPALETTE)pPal->m_hObject;
hOldPal=::SelectPalette(hDC,hPal,TRUE);
}
::SetStretchBltMode(hDC,COLORONCOLOR);
if((RECTWIDTH(lpDCRect)==RECTWIDTH(lpDIBRect))&&(RECTHEIGHT(lpDCRect)==RECTHEIGHT(lpDIBRect)))
{
bSuccess=::SetDIBitsToDevice(hDC,lpDCRect->left,lpDCRect->top,RECTWIDTH(lpDCRect),RECTHEIGHT(lpDCRect),lpDIBRect->left,\
(int)DIBHeight(lpDIBHdr)-lpDIBRect->top-RECTHEIGHT(lpDIBRect),0,(WORD)DIBHeight(lpDIBHdr),\
lpDIBBits,(LPBITMAPINFO)lpDIBHdr,DIB_RGB_COLORS);
}
else
{
bSuccess=::StretchDIBits(hDC,lpDCRect->left,lpDCRect->top,RECTWIDTH(lpDCRect),RECTHEIGHT(lpDCRect),lpDIBRect->left,\
lpDIBRect->top,RECTWIDTH(lpDIBRect),RECTHEIGHT(lpDIBRect),\
lpDIBBits,(LPBITMAPINFO)lpDIBHdr,DIB_RGB_COLORS,SRCCOPY);
}
::GlobalUnlock((HGLOBAL)hDIB);
if(hOldPal)
::SelectPalette(hDC,hOldPal,TRUE);
GlobalUnlock(hDIB);
return bSuccess;
}
BOOL WINAPI CreateDIBPalette(HDIB hDIB,CPalette* pPal)
{
LPLOGPALETTE lpPal;
HANDLE hLogPal;
HPALETTE hPal=NULL;
LPSTR lpbi;
LPBITMAPINFO lpbmi;
LPBITMAPCOREINFO lpbmc;
BOOL bWinStyleDIB;
int i;
WORD wNumColors;
BOOL bResult=FALSE;
if(hDIB==NULL)
return FALSE;
lpbi=(LPSTR)::GlobalLock((HGLOBAL)hDIB);
lpbmi=(LPBITMAPINFO)lpbi;
lpbmc=(LPBITMAPCOREINFO)lpbi;
wNumColors=DIBNumColors(lpbi);
bWinStyleDIB=IS_WIN30_DIB(lpbi);
if(wNumColors!=0)
{
hLogPal=::GlobalAlloc(GHND,sizeof(LOGPALETTE)+sizeof(PALETTEENTRY)*wNumColors);
if(hLogPal==0)
{
::GlobalUnlock((HGLOBAL)hDIB);
return FALSE;
}
lpPal=(LPLOGPALETTE)::GlobalLock(hLogPal);
lpPal->palVersion=PALVERSION;
lpPal->palNumEntries=(WORD)wNumColors;
bWinStyleDIB=IS_WIN30_DIB(lpbi);
for(i=0;i<(int)wNumColors;i++)
{
if(bWinStyleDIB)
{
lpPal->palPalEntry[i].peRed=lpbmi->bmiColors[i].rgbRed;
lpPal->palPalEntry[i].peGreen=lpbmi->bmiColors[i].rgbGreen;
lpPal->palPalEntry[i].peBlue=lpbmi->bmiColors[i].rgbBlue;
lpPal->palPalEntry[i].peFlags=0;
}
else
{
lpPal->palPalEntry[i].peRed=lpbmc->bmciColors[i].rgbtRed;
lpPal->palPalEntry[i].peGreen=lpbmc->bmciColors[i].rgbtGreen;
lpPal->palPalEntry[i].peBlue=lpbmc->bmciColors[i].rgbtBlue;
lpPal->palPalEntry[i].peFlags=0;
}
}
bResult=pPal->CreatePalette(lpPal);
::GlobalUnlock((HGLOBAL)hLogPal);
::GlobalFree((HGLOBAL)hLogPal);
}
::GlobalUnlock((HGLOBAL)hDIB);
return bResult;
}
HGLOBAL WINAPI CopyHandle(HGLOBAL h)
{
if(h==NULL)
return NULL;
DWORD dwLen=::GlobalSize((HGLOBAL)h);
HGLOBAL hCopy=::GlobalAlloc(GHND,dwLen);
if(hCopy!=NULL)
{
void* lpCopy=::GlobalLock((HGLOBAL)hCopy);
void* lp=::GlobalLock((HGLOBAL)h);
memcpy(lpCopy,lp,dwLen);
::GlobalUnlock(hCopy);
::GlobalUnlock(h);
}
return hCopy;
}
BOOL WINAPI SaveDIB(HDIB hDib,CFile& file)
{
BITMAPFILEHEADER bmfHdr;
LPBITMAPINFOHEADER lpBI;
DWORD dwDIBSize;
if(!hDib)
return FALSE;
lpBI=(LPBITMAPINFOHEADER)::GlobalLock((HGLOBAL)hDib);
if(lpBI==NULL)
return FALSE;
if(!IS_WIN30_DIB(lpBI))
{
::GlobalUnlock((HGLOBAL)hDib);
return FALSE;
}
bmfHdr.bfType=DIB_HEADER_MARKER;
dwDIBSize=*(LPDWORD)lpBI+::PaletteSize((LPSTR)lpBI);
if((lpBI->biCompression==BI_RLE8)||(lpBI->biCompression==BI_RLE4))
dwDIBSize+=lpBI->biSizeImage;
else
{
DWORD dwBmBitsSize;
dwBmBitsSize=WIDTHBYTES((lpBI->biWidth)*((DWORD)lpBI->biBitCount))*lpBI->biHeight;
dwDIBSize+=dwBmBitsSize;
lpBI->biSizeImage=dwBmBitsSize;
}
bmfHdr.bfSize=dwDIBSize+sizeof(BITMAPFILEHEADER);
bmfHdr.bfReserved1=0;
bmfHdr.bfReserved2=0;
bmfHdr.bfOffBits=(DWORD)sizeof(BITMAPFILEHEADER)+lpBI->biSize+::PaletteSize((LPSTR)lpBI);
TRY
{
file.Write((LPSTR)&bmfHdr,sizeof(BITMAPFILEHEADER));
file.WriteHuge(lpBI,dwDIBSize);
}
CATCH(CFileException,e)
{
::GlobalUnlock((HGLOBAL)hDib);
THROW_LAST();
}
END_CATCH
::GlobalUnlock((HGLOBAL)hDib);
return TRUE;
}
HDIB WINAPI ReadDIBFile(CFile& file)
{
BITMAPFILEHEADER bmfHeader;
DWORD dwBitsSize;
HDIB hDIB;
LPSTR pDIB;
dwBitsSize=file.GetLength();
if(file.Read((LPSTR)&bmfHeader,sizeof(bmfHeader))!=sizeof(bmfHeader))
return NULL;
if(bmfHeader.bfType!=DIB_HEADER_MARKER)
return NULL;
hDIB=(HDIB)::GlobalAlloc(GMEM_MOVEABLE|GMEM_ZEROINIT,dwBitsSize);
if(hDIB==0)
return NULL;
pDIB=(LPSTR)::GlobalLock((HGLOBAL)hDIB);
if(file.ReadHuge(pDIB,dwBitsSize-sizeof(BITMAPFILEHEADER))!=dwBitsSize-sizeof(BITMAPFILEHEADER))
{
::GlobalUnlock((HGLOBAL)hDIB);
::GlobalFree((HGLOBAL)hDIB);
return NULL;
}
::GlobalUnlock((HGLOBAL)hDIB);
return hDIB;
}
//在此处输入自己的函数定义
上面是DIBAPI.h头文件中声明的函数的定义,你将自己的函数定义加到“//在此处输入自己的函数定义”后面。
下面加相应的消息映射函数,点Ctrl+W键,会出现MFC ClassWizard对话框,点Message Maps,在Class name下面的框中选择CMy111Doc,在Messages里面选中OnOpenDocument,然后点击Add Function按钮就加入了相应的消息映射函数,同样的方法以此加入OnSaveDocument、DeleteContents、CanCloseFrame相应的消息函数,点“确定”即可。
点开111Doc.cpp文件,里面有相应的消息映射函数定义位置,在函数CMy111Doc::CMy111Doc()里输入以下代码:
m_refColorBKG=0x00808080;
m_hDIB=NULL;
m_palDIB=NULL;
m_sizeDoc=CSize(1,1);
在函数CMy111Doc::~CMy111Doc()里输入以下代码:
if(m_hDIB!=NULL)
::GlobalFree((HGLOBAL)m_hDIB);
if(m_palDIB!=NULL)
delete m_palDIB;
在函数BOOL CMy111Doc::OnOpenDocument(LPCTSTR lpszPathName)里/ TODO: Add your specialized creation code here下面添加如下代码:
CFile file;
CFileException fe;
if(!file.Open(lpszPathName,CFile::modeRead|CFile::shareDenyWrite,&fe))
{
ReportSaveLoadException(lpszPathName,&fe,FALSE,AFX_IDP_FAILED_TO_OPEN_DOC);
return FALSE;
}
DeleteContents();
BeginWaitCursor();
TRY
{
m_hDIB=::ReadDIBFile(file);
}
CATCH(CFileException,eLoad)
{
file.Abort();
EndWaitCursor();
ReportSaveLoadException(lpszPathName,eLoad,FALSE,AFX_IDP_FAILED_TO_OPEN_DOC);
m_hDIB=NULL;
return FALSE;
}
END_CATCH
InitDIBData();
EndWaitCursor();
if(m_hDIB==NULL)
{
CString strMsg;
strMsg="读取图像时出错!可能是不支持该类型的图像文件!";
MessageBox(NULL,strMsg,NULL,MB_ICONINFORMATION|MB_OK);
return FALSE;
}
SetPathName(lpszPathName);
SetModifiedFlag(FALSE);
在函数BOOL CMy111Doc::OnSaveDocument(LPCTSTR lpszPathName)里// TODO: Add your specialized code here and/or call the base class后面添加如下代码:
CFile file;
CFileException fe;
if(!file.Open(lpszPathName,CFile::modeCreate|CFile::modeReadWrite|CFile::shareExclusive,&fe))
{
ReportSaveLoadException(lpszPathName,&fe,TRUE,AFX_IDP_INVALID_FILENAME);
return FALSE;
}
BOOL bSuccess=FALSE;
TRY
{
BeginWaitCursor();
bSuccess=::SaveDIB(m_hDIB,file);
file.Close();
}
CATCH(CException,eSave)
{
file.Abort();
EndWaitCursor();
ReportSaveLoadException(lpszPathName,eSave,TRUE,AFX_IDP_FAILED_TO_SAVE_DOC);
return FALSE;
}
END_CATCH
EndWaitCursor();
SetModifiedFlag(FALSE);
if(!bSuccess)
{
CString strMsg;
strMsg="无法保存BMP图像";
MessageBox(NULL,strMsg,NULL,MB_ICONINFORMATION|MB_OK);
}
点开ClassView,右键点击CMy111Doc类,点Add Member Variable出现添加成员变量对话框,在Variable Type下的框中输入变量类型名HDIB,在Variable Name中输入变量名m_hDIB,在Access中选中Public就在CMy111Doc中加入了公共成员变量m_hDIB;同样加入int类型的m_nColorIndex、COLORREF类型的m_refColorBKG公共成员变量,再添加保护成员变量(在Access中选中Protected)CSize类的m_sizeDoc、CPalette*类的m_palDIB变量。右键点击CMy111Doc类,点Add Member Function出现添加成员函数对话框,在Function Type下的框中输入函数类型名void,在Function Declaration中输入函数名InitDIBData(),在Access中选中Public就在CMy111Doc中加入了公共成员函数InitDIBData();同样方法加入void类型的公共成员函数ReplaceHDIB(HDIB hDIB)。在111Doc.cpp中加入函数的代码,InitDIBData()里加如下代码:
if(m_palDIB!=NULL)
{
delete m_palDIB;
m_palDIB=NULL;
}
if(m_hDIB==NULL)
return;
LPSTR lpDIB=(LPSTR)::GlobalLock((HGLOBAL)m_hDIB);
if(::DIBWidth(lpDIB)>INT_MAX||::DIBHeight(lpDIB)>INT_MAX)
{
::GlobalUnlock((HGLOBAL)m_hDIB);
::GlobalFree((HGLOBAL)m_hDIB);
m_hDIB=NULL;
CString strMsg;
strMsg="BMP图像太大!";
MessageBox(NULL,strMsg,NULL,MB_ICONINFORMATION|MB_OK);
return;
}
m_sizeDoc=CSize((int)::DIBWidth(lpDIB),(int)::DIBHeight(lpDIB));
::GlobalUnlock((HGLOBAL)m_hDIB);
m_palDIB=new CPalette;
if(m_palDIB==NULL)
{
::GlobalFree((HGLOBAL)m_hDIB);
m_hDIB=NULL;
return;
}
if(::CreateDIBPalette(m_hDIB,m_palDIB)==NULL)
{
delete m_palDIB;
m_palDIB=NULL;
return;
}
在函数ReplaceHDIB(HDIB hDIB)中加入如下代码:
if(m_hDIB!=NULL)
::GlobalFree((HGLOBAL)m_hDIB);
m_hDIB=hDIB;
在CMy111doc.h的
// Attributes
public:
下面加入如下代码:
HDIB GetHDIB() const
{
return m_hDIB;
}
CPalette* GetDocPalette() const
{
return m_palDIB;
}
CSize GetDocSize() const
{
return m_sizeDoc;
}
注意要在111Doc.h前加上#include "DIBAPI.h"语句(在class CMy111Doc:public CDocument语句前)
下面为CMy111View中的函数 void CMy111View::OnDraw(CDC* pDC)中添加如下代码:
BeginWaitCursor();
CMy111* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
HDIB hDIB=pDoc->GetHDIB();
if(hDIB!=NULL)
{
LPSTR lpDIB=(LPSTR)::GlobalLock((HGLOBAL)hDIB);
int cxDIB=(int)::DIBWidth(lpDIB);
int cyDIB=(int)::DIBHeight(lpDIB);
::GlobalUnlock((HGLOBAL)hDIB);
CRect rcDIB;
rcDIB.top=rcDIB.left=0;
rcDIB.right=cxDIB;
rcDIB.bottom=cyDIB;
CRect rcDest;
if(pDC->IsPrinting())
{
int cxPage=pDC->GetDeviceCaps(HORZRES);
int cyPage=pDC->GetDeviceCaps(VERTRES);
int cxInch=pDC->GetDeviceCaps(LOGPIXELSX);
int cyInch=pDC->GetDeviceCaps(LOGPIXELSY);
rcDest.top=rcDest.left=0;
rcDest.bottom=(int)(((double)cyDIB*cxPage*cyInch)/((double)cxDIB*cxInch));
rcDest.right=cxPage;
int temp=cyPage-(rcDest.bottom-rcDest.top);
rcDest.bottom+=temp/2;
rcDest.top+=temp/2;
}
else
{
rcDest=rcDIB;
}
::PaintDIB(pDC->m_hDC,&rcDest,pDoc->GetHDIB(),&rcDIB,pDoc->GetDocPalette());
}
EndWaitCursor();
以上是关于图像打开、显示、保存的有关步骤和代码。
在111.cpp文件的InitInstance()函数的ParseCommandLine(cmdInfo);语句下输入语句
cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing;
则启动时不自动打开一个空文档。
显示图像直方图步骤:
点击ResourceView,右键点击Dialog,选Insert Dialog 在属性对话框中将ID改为ID_HIST,对话框名称改为“直方图”
在工具栏中点“插入”-“新建类”,输入类名HAHA,并选Base Class为CDialog,Dialog ID为ID_HIST。这样就将对话框和类联系起来了,在该对话框中拖入一Edit控件,将其ID设为IDC_HISTSHOW;
快捷键“Ctrl+W”,出现MFC ClassWizard对话框,在Messages栏中分别选WM_INITDIALOG和WM_Paint,再点击“Add Function”,即将对话框初始化和画图函数加入对话框类之中。
在Hist.h文件“public:”下面输入如下变量定义:(HAHA)
LONG m_lCount[256];
char* m_lpDIBBits;
LONG m_lWidth;
LONG m_lHeight;
int m_iIsDraging;
CDlgIntensity(CWnd* pParent = NULL);
打开Hist.cpp程序(HAHA),在CHist::OnInitDialog()函数中“// TODO: Add extra initialization here”前将如下代码拷贝进去:
unsigned char* lpSrc;
LONG i;
LONG j;
在“// TODO: Add extra initialization here”后将如下代码拷贝进去:
CWnd* pWnd=GetDlgItem(IDC_HISTSHOW);
CRect rect;
GetClientRect(rect);
ClientToScreen(&rect);
for(i=0;i<256;i++)
{
m_lCount[i]=0;
}
LONG lLineBytes;
lLineBytes=WIDTHBYTES(m_lWidth*8);
for(i=0;i<m_lHeight;i++)
{
for(j=0;j<m_lWidth;j++)
{
lpSrc=(unsigned char*)m_lpDIBBits+lLineBytes*i+j;
m_lCount[*(lpSrc)]++;
}
}
m_iIsDraging=0;
在CHist::OnPaint()函数“CPaintDC dc(this);”前将如下代码拷如其中:
CString str;
LONG i;
LONG lMaxCount=0;
LONG m_iLowGray=0;
LONG m_iUpGray=255;
在CHist::OnPaint()函数“// TODO: Add your message handler code here”后将如下代码拷如其中:
CWnd* pWnd=GetDlgItem(IDC_ HISTSHOW);
CDC* pDC=pWnd->GetDC();
pWnd->Invalidate();
pWnd->UpdateWindow();
pDC->Rectangle(0,0,330,300);
CPen* pPenRed=new CPen;
pPenRed->CreatePen(PS_SOLID,1,RGB(255,0,0));
CPen* pPenBlue=new CPen;
pPenBlue->CreatePen(PS_SOLID,1,RGB(0,0,255));
CPen* pPenGreen=new CPen;
pPenGreen->CreatePen(PS_SOLID,1,RGB(0,255,0));
CGdiObject* pOldPen=pDC->SelectObject(pPenRed);
pDC->MoveTo(10,10);
pDC->LineTo(10,280);
pDC->LineTo(320,280);
str.Format("0");
pDC->TextOut(10,283,str);
str.Format("50");
pDC->TextOut(60,283,str);
str.Format("100");
pDC->TextOut(110,283,str);
str.Format("150");
pDC->TextOut(160,283,str);
str.Format("200");
pDC->TextOut(210,283,str);
str.Format("255");
pDC->TextOut(265,283,str);
for(i=0;i<256;i+=5)
{
if((i&1)==0)
{
pDC->MoveTo(i+10,280);
pDC->LineTo(i+10,284);
}
else
{
pDC->MoveTo(i+10,280);
pDC->LineTo(i+10,282);
}
}
pDC->MoveTo(315,275);
pDC->LineTo(320,280);
pDC->LineTo(315,285);
pDC->MoveTo(10,10);
pDC->LineTo(5,15);
pDC->MoveTo(10,10);
pDC->LineTo(15,15);
for(i=m_iLowGray;i<=m_iUpGray;i++)
{
if(m_lCount[i]>lMaxCount)
{
lMaxCount=m_lCount[i];
}
}
pDC->MoveTo(10,25);
pDC->LineTo(14,25);
str.Format("%d",lMaxCount);
pDC->TextOut(11,26,str);
pDC->SelectObject(pPenGreen);
pDC->MoveTo(m_iLowGray+10,25);
pDC->LineTo(m_iLowGray+10,280);
pDC->MoveTo(m_iUpGray+10,25);
pDC->LineTo(m_iUpGray+10,280);
pDC->SelectObject(pPenBlue);
if(lMaxCount>0)
{
for(i=m_iLowGray;i<=m_iUpGray;i++)
{
pDC->MoveTo(i+10,280);
pDC->LineTo(i+10,281-(int)(m_lCount[i]*256/lMaxCount));
}
}
pDC->SelectObject(pOldPen);
delete pPenRed;
delete pPenBlue;
delete pPenGreen;
点开ResourceView中的Menu,出现IDR_MAINFRAME和 IDR_MY111TYPE,双击IDR_MY111TYPE就出现了程序界面,有文件、编辑、查看、窗口、帮助按钮。点“查看”下面的虚线框右键——点“属性”,输入标题(直方图),和ID(大写英文,如ID_VIEW_HIST),这样就将按钮和程序通过ID联系起来了,点击该按钮,就会执行相应的程序。下面为该按钮添加函数:点Ctrl+W ,出现MFC ClassWizard对话框,选Class name为CMy111View,在Object IDs对应的选项中选中设置的ID(如为ID_VIEW_HIST),在Messages选项中选中COMMAND,然后点击“Add Function”按钮,选默认设置,就为ID_VIEW_HIST添加了相应的函数OnViewHist()。该函数在111View.cpp文件中定义,在函数void CMy111View:: OnViewHist()添加如下代码:
// TODO: Add your command handler code here
CMy111Doc* pDoc=GetDocument();
if(pDoc->m_hDIB==NULL)
{
MessageBox("请先打开一幅256色的BMP图像!","系统提示",MB_ICONINFORMATION|MB_OK);
return;
}
LPSTR lpDIB;
LPSTR lpDIBBits;
lpDIB=(LPSTR)::GlobalLock((HGLOBAL)pDoc->GetHDIB());
lpDIBBits=::FindDIBBits(lpDIB);
if(::DIBNumColors(lpDIB)!=256)
{
MessageBox("目前只支持查看256色位图灰度直方图!","系统提示",MB_ICONINFORMATION|MB_OK);
::GlobalUnlock((HGLOBAL)pDoc->GetHDIB());
return;
}
BeginWaitCursor();
CHist dlgPara;
dlgPara.m_lpDIBBits=lpDIBBits;
dlgPara.m_lWidth=::DIBWidth(lpDIB);
dlgPara.m_lHeight=::DIBHeight(lpDIB);
// dlgPara.m_iLowGray=0;
// dlgPara.m_iUpGray=255;
if(dlgPara.DoModal()!=IDOK)
{
return;
}
::GlobalUnlock((HGLOBAL)pDoc->GetHDIB());
EndWaitCursor();
在数字图像处理中,灰度直方图简单且最有用的工具。灰度直方图是灰度级的函数,是像中灰度级分布的统计。即:横表示灰度级,纵坐标表示图像中某灰度级所出现的像素个数。
直方图的性质
? 是一个概率分布函数
? 所有的空间信息全部丢失
? 整个图像的直方图是部分之和
? 相同的直方图可能对应不同的图像直方图的应用:评价图像数字化的好坏
? 直方图给出了一个简单可见的指示,用来判断一幅图像是否合理的利用了全部被允许的灰度级范围。一幅图像应该利用全部或几乎全部可能的灰度级。
图像反色、线性变换、灰度拉伸、阈值变换
代码如下:
/**************************************************************************
* 文件名:PointTrans.cpp
*
* 图像点运算API函数库:
*
* LinerTrans() - 图像线性变换
* WindowTrans() - 图像窗口变换
* GrayStretch() - 图像灰度拉伸
* InteEqualize() - 直方图均衡
* ThresholdTrans() - 阈值变换
* Delay() - 延时
*
*************************************************************************/
#include "stdafx.h"
#include "PointTrans.h"
#include "DIBAPI.h"
#include <math.h>
#include <direct.h>
/*************************************************************************
*
* 函数名称:
* LinerTrans()
*
* 参数:
* LPSTR lpDIBBits - 指向源DIB图像指针
* LONG lWidth - 源图像宽度(象素数)
* LONG lHeight - 源图像高度(象素数)
* FLOAT fA - 线性变换的斜率
* FLOAT fB - 线性变换的截距
*
* 返回值:
* BOOL - 成功返回TRUE,否则返回FALSE。
*
* 说明:
* 该函数用来对图像进行灰度的线性变换操作。
*
************************************************************************/
BOOL WINAPI LinerTrans(LPSTR lpDIBBits, LONG lWidth, LONG lHeight, FLOAT fA, FLOAT fB)
{
// 指向源图像的指针
unsigned char* lpSrc;
// 循环变量
LONG i;
LONG j;
// 图像每行的字节数
LONG lLineBytes;
// 中间变量
FLOAT fTemp;
// 计算图像每行的字节数
lLineBytes = WIDTHBYTES(lWidth * 8);
// 每行
for(i = 0; i < lHeight; i++)
{
// 每列
for(j = 0; j < lWidth; j++)
{
// 指向DIB第i行,第j个象素的指针
lpSrc = (unsigned char*)lpDIBBits + lLineBytes * (lHeight - 1 - i) + j;
// 线性变换
fTemp = fA * (*lpSrc) + fB;
// 判断是否超出范围
if (fTemp > 255)
{
// 直接赋值为255
*lpSrc = 255;
}
else if (fTemp < 0)
{
// 直接赋值为0
*lpSrc = 0;
}
else
{
// 四舍五入
*lpSrc = (unsigned char) (fTemp + 0.5);
}
}
}
// 返回
return TRUE;
}
/*************************************************************************
*
* 函数名称:
* ThresholdTrans()
*
* 参数:
* LPSTR lpDIBBits - 指向源DIB图像指针
* LONG lWidth - 源图像宽度(象素数)
* LONG lHeight - 源图像高度(象素数)
* BYTE bThre - 阈值
*
* 返回值:
* BOOL - 成功返回TRUE,否则返回FALSE。
*
* 说明:
* 该函数用来对图像进行阈值变换。对于灰度值小于阈值的象素直接设置
* 灰度值为0;灰度值大于阈值的象素直接设置为255。
*
************************************************************************/
BOOL WINAPI ThresholdTrans(LPSTR lpDIBBits, LONG lWidth, LONG lHeight, BYTE bThre)
{
// 指向源图像的指针
unsigned char* lpSrc;
// 循环变量
LONG i;
LONG j;
// 图像每行的字节数
LONG lLineBytes;
// 计算图像每行的字节数
lLineBytes = WIDTHBYTES(lWidth * 8);
// 每行
for(i = 0; i < lHeight; i++)
{
// 每列
for(j = 0; j < lWidth; j++)
{
// 指向DIB第i行,第j个象素的指针
lpSrc = (unsigned char*)lpDIBBits + lLineBytes * (lHeight - 1 - i) + j;
// 判断是否小于阈值
if ((*lpSrc) < bThre)
{
// 直接赋值为0
*lpSrc = 0;
}
else
{
// 直接赋值为255
*lpSrc = 255;
}
}
}
// 返回
return TRUE;
}
/*************************************************************************
*
* 函数名称:
* WindowTrans()
*
* 参数:
* LPSTR lpDIBBits - 指向源DIB图像指针
* LONG lWidth - 源图像宽度(象素数)
* LONG lHeight - 源图像高度(象素数)
* BYTE bLow - 窗口下限
* BYTE bUp - 窗口上限
*
* 返回值:
* BOOL - 成功返回TRUE,否则返回FALSE。
*
* 说明:
* 该函数用来对图像进行窗口变换。只有在窗口范围内的灰度保持不变,
* 小于下限的象素直接设置灰度值为0;大于上限的象素直接设置灰度值为255。
*
************************************************************************/
BOOL WINAPI WindowTrans(LPSTR lpDIBBits, LONG lWidth, LONG lHeight, BYTE bLow, BYTE bUp)
{
// 指向源图像的指针
unsigned char* lpSrc;
// 循环变量
LONG i;
LONG j;
// 图像每行的字节数
LONG lLineBytes;
// 计算图像每行的字节数
lLineBytes = WIDTHBYTES(lWidth * 8);
// 每行
for(i = 0; i < lHeight; i++)
{
// 每列
for(j = 0; j < lWidth; j++)
{
// 指向DIB第i行,第j个象素的指针
lpSrc = (unsigned char*)lpDIBBits + lLineBytes * (lHeight - 1 - i) + j;
// 判断是否超出范围
if ((*lpSrc) < bLow)
{
// 直接赋值为0
*lpSrc = 0;
}
else if ((*lpSrc) > bUp)
{
// 直接赋值为255
*lpSrc = 255;
}
}
}
// 返回
return TRUE;
}
/*************************************************************************
*
* 函数名称:
* GrayStretch()
*
* 参数:
* LPSTR lpDIBBits - 指向源DIB图像指针
* LONG lWidth - 源图像宽度(象素数)
* LONG lHeight - 源图像高度(象素数)
* BYTE bX1 - 灰度拉伸第一个点的X坐标
* BYTE bY1 - 灰度拉伸第一个点的Y坐标
* BYTE bX2 - 灰度拉伸第二个点的X坐标
* BYTE bY2 - 灰度拉伸第二个点的Y坐标
*
* 返回值:
* BOOL - 成功返回TRUE,否则返回FALSE。
*
* 说明:
* 该函数用来对图像进行灰度拉伸。
*
************************************************************************/
BOOL WINAPI GrayStretch(LPSTR lpDIBBits, LONG lWidth, LONG lHeight, BYTE bX1, BYTE bY1, BYTE bX2, BYTE bY2)
{
// 指向源图像的指针
unsigned char* lpSrc;
// 循环变量
LONG i;
LONG j;
// 灰度映射表
BYTE bMap[256];
// 图像每行的字节数
LONG lLineBytes;
// 计算图像每行的字节数
lLineBytes = WIDTHBYTES(lWidth * 8);
// 计算灰度映射表
for (i = 0; i <= bX1; i++)
{
// 判断bX1是否大于0(防止分母为0)
if (bX1 > 0)
{
// 线性变换
bMap[i] = (BYTE) bY1 * i / bX1;
}
else
{
// 直接赋值为0
bMap[i] = 0;
}
}
for (; i <= bX2; i++)
{
// 判断bX1是否等于bX2(防止分母为0)
if (bX2 != bX1)
{
// 线性变换
bMap[i] = bY1 + (BYTE) ((bY2 - bY1) * (i - bX1) / (bX2 - bX1));
}
else
{
// 直接赋值为bY1
bMap[i] = bY1;
}
}
for (; i < 256; i++)
{
// 判断bX2是否等于255(防止分母为0)
if (bX2 != 255)
{
// 线性变换
bMap[i] = bY2 + (BYTE) ((255 - bY2) * (i - bX2) / (255 - bX2));
}
else
{
// 直接赋值为255
bMap[i] = 255;
}
}
// 每行
for(i = 0; i < lHeight; i++)
{
// 每列
for(j = 0; j < lWidth; j++)
{
// 指向DIB第i行,第j个象素的指针
lpSrc = (unsigned char*)lpDIBBits + lLineBytes * (lHeight - 1 - i) + j;
// 计算新的灰度值
*lpSrc = bMap[*lpSrc];
}
}
// 返回
return TRUE;
}
/*************************************************************************
*
* 函数名称:
* InteEqualize()
*
* 参数:
* LPSTR lpDIBBits - 指向源DIB图像指针
* LONG lWidth - 源图像宽度(象素数)
* LONG lHeight - 源图像高度(象素数)
*
* 返回值:
* BOOL - 成功返回TRUE,否则返回FALSE。
*
* 说明:
* 该函数用来对图像进行直方图均衡。
*
************************************************************************/
BOOL WINAPI InteEqualize(LPSTR lpDIBBits, LONG lWidth, LONG lHeight)
{
// 指向源图像的指针
unsigned char* lpSrc;
// 临时变量
LONG lTemp;
// 循环变量
LONG i;
LONG j;
// 灰度映射表
BYTE bMap[256];
// 灰度映射表
LONG lCount[256];
// 图像每行的字节数
LONG lLineBytes;
// 计算图像每行的字节数
lLineBytes = WIDTHBYTES(lWidth * 8);
// 重置计数为0
for (i = 0; i < 256; i ++)
{
// 清零
lCount[i] = 0;
}
// 计算各个灰度值的计数
for (i = 0; i < lHeight; i ++)
{
for (j = 0; j < lWidth; j ++)
{
lpSrc = (unsigned char *)lpDIBBits + lLineBytes * i + j;
// 计数加1
lCount[*(lpSrc)]++;
}
}
// 计算灰度映射表
for (i = 0; i < 256; i++)
{
// 初始为0
lTemp = 0;
for (j = 0; j <= i ; j++)
{
lTemp += lCount[j];
}
// 计算对应的新灰度值
bMap[i] = (BYTE) (lTemp * 255 / lHeight / lWidth);
}
// 每行
for(i = 0; i < lHeight; i++)
{
// 每列
for(j = 0; j < lWidth; j++)
{
// 指向DIB第i行,第j个象素的指针
lpSrc = (unsigned char*)lpDIBBits + lLineBytes * (lHeight - 1 - i) + j;
// 计算新的灰度值
*lpSrc = bMap[*lpSrc];
}
}
// 返回
return TRUE;
}
/*************************************************************************
*
* 函数名称:
* pointLineTran()
*
* 参数:
*
* BYTE bX - 灰度拉伸第i个点的X坐标
* BYTE bY - 灰度拉伸第i个点的Y坐标
* int n - 灰度拉伸折点数目,由于以数组存储,所以为0,1,2,....n-1
*
* 返回值:
* BYTE - 返回BYTE类型
*
* 说明:
* 该函数用来对图像点进行线性变换。与函数GrayStretch类似
*
************************************************************************/
/*BYTE* WINAPI pointLineTran( BYTE* bX, BYTE* bY,int n)
{
// 循环变量
LONG i;
int m;//第m个折点
// 灰度映射表
BYTE* bMap;
bMap=new BYTE[256];
// 计算灰度映射表, 第0个折点
for (i = 0; i <= bX[0]; i++)
{
// 判断bX1是否大于0(防止分母为0)
if (bX[0] > 0)
{
// 线性变换
bMap[i] = (BYTE) bY[0] * i / bX[0];
}
else
{
// 直接赋值为0
bMap[i] = 0;
}
}
/////////////////////////////////////
///////////////////////////////////////////
m=1;//第1段折线,第1~n-1个折点
for(m=1;m<=n-1;m++)
{
for (; i <= bX[m]; i++)
{
// 判断bX1是否等于bX2(防止分母为0)
if (bX[m] != bX[m-1])
{
// 线性变换
bMap[i] = bY[m-1] + (BYTE) ((bY[m] - bY[m-1]) * (i - bX[m-1]) / (bX[m] - bX[m-1]));
}
else
{
// 直接赋值为bY1
bMap[i] = bY[m-1];
}
}
}
for (; i < 256; i++)
{
// 最后一个折点
if (bX[n-1] != 255)
{
// 线性变换
bMap[i] = bY[n-1] + (BYTE) ((255 - bY[n-1]) * (i - bX[n-1]) / (255 - bX[n-1]));
}
else
{
// 直接赋值为255
bMap[i] = 255;
}
}
return bMap;
}*/
/*void WINAPI Delay(DWORD dwDelayTime)
{
DWORD dwTimeBegin, dwTimeEnd;
dwTimeBegin = timeGetTime();
do
{
dwTimeEnd = timeGetTime();
}while(dwTimeEnd - dwTimeBegin < dwDelayTime);
}*/
打开人机界面时
打开后的原图直方图
灰度均衡化后图及直方图
图像反色后图及直方图
线性变换
线性变换后图及直方图
灰度拉伸
拉伸后图形及直方图
阈值变换
阈值变换后图及直方图
经过这次课程设计,给我很多收获,不论是工程分析、组织、建设上,我发现做软件需要做很多工作,不仅仅是敲代码。
首先,必须要有程序结构分析。就拿这次的题目来说,一个清晰的结构分析能让我省去很多工作,能让我把代码写的更清晰,让我的代码能有更好的结构组织,以此简化程序。
其次,有了清晰的结构分析,还要有注释。注释也很重要,特别是写过之后重用和测试代码时,都必须得看。否则就不得不将已写好的封装函数从头到尾再看一遍,再理解,这样很浪费时间。有了注释,就可以省去这些重新理解函数的时间,可以提高效率。
再次,写注释是为了使函数更简单的被理解。而写注释之前,必须要测试这段代码的可行性。必须要尽可能多的考虑会出现的情况,对不希望出现的情况予以相对的措施或者提示。这样在代码重用的时候也可以放心的重用,而不必因为代码写的不够完善而再来修改,这样也会浪费很多时间。
最后,程序的测试。一个完善的程序应该经得起测试。自己的程序写得好不好,最终得看测试。如果输入了非法的输入或者操作,程序是否能够正常运行?还是会像这次输入错误就会死循环?这是程序的健壮性。做好以上几个方面,程序基本就做好了。但是任何一个程序都不可能没有错误,金无足赤人无完人。如果要追求完美,就不得不锲而不舍,定期得到用户的反馈然后修复相关问题,就像微软一样,总是会在问题出现之后就发布漏洞补丁。总的来说通过这次课程设计自己收获了很多东西,希望这样一个不怎么完美的开始可以给自己一个警示,在今后的学习及工作能更好的总结成长,做到完美。
[1] 郑莉 董渊 张瑞丰, C++语言程序设计(第三版), 清华大学出版社 2003
[2]David J.Kruglinski(美)等, Visual C++ 6.0技术内幕,北京希望电子出版社,2002
[3]谢凤英 赵丹培, Visual C++ 数字图像处理,电子工业出版社,2008
[4]求是科技 张宏林,精通Visual C++ 数字图像处理典型算法及实现,人民邮电出版社,2008
[5]刘锐宁 宋坤等, Visual C++从入门到精通,清华大学出版社,2008
2.不要自己写,要利用word来自动生成。详情请看最后一页
课程设计报告书课程名称题目学生姓名专业班别学号指导老师日期年月日数字图像处理的傅里叶变换1课程设计目的和意义1了解图像变换的意义和…
数字图像处理课程设计报告课设题目学院专业班级姓名学号指导教师彩色图像增强软件信息科学与工程学院电子与信息工程1002501曾小路1…
摘要MATLAB和MathematicaMaple并称为三大数学软件它在数学类科技应用软件在数值计算方面首屈一指MATLAB可以进…
计算机图像处理(课程设计报告)系别:计算机与通信工程系专业:计算机应用学号:姓名:指导教师:车牌识别系统的设计一、摘要:随这图形图…
数字图像处理课程设计报告1课程设计目的1提高分析问题解决问题的能力进一步巩固数字图像处理系统中的基本原理与方法2熟悉掌握一门计算机…
东北大学秦皇岛分校计算机与通信工程学院综合课程设计设计题目形态学图像处理的Matlab设计与实现专业名称班级学号学生姓名指导教师设…
数字图像处理课程设计报告课设题目学院专业班级姓名学号指导教师彩色图像增强软件信息科学与工程学院电子与信息工程1002501曾小路1…
课程设计报告书课程名称题目学生姓名专业班别学号指导老师日期年月日数字图像处理的傅里叶变换1课程设计目的和意义1了解图像变换的意义和…
摘要MATLAB和MathematicaMaple并称为三大数学软件它在数学类科技应用软件在数值计算方面首屈一指MATLAB可以进…
计算机图像处理(课程设计报告)系别:计算机与通信工程系专业:计算机应用学号:姓名:指导教师:车牌识别系统的设计一、摘要:随这图形图…