2、 灰度位图数据的存储
为了将图像处理后所得到的像素值保存起来,我们重载了文档类的
OnSaveDocument()函数,这样用户在点击Save或SaveAs子菜单后程序自动调用该函数,实现图像数据的存储。该函数的具体实现如下:
/////////////////////////////////////////////////////////////////// BOOL CDibDoc::OnSaveDocument(LPCTSTR lpszPathName) {
CFile file;
CFileException fe;
BITMAPFILEHEADER bmfHdr; // 位图文件头结构;
LPBITMAPINFOHEADER lpBI;//指向位图头信息结构的指针; DWORD dwDIBSize;;
if (!file.Open(lpszPathName, CFile::modeCreate |CFile::modeReadWrite | CFile::shareExclusive, &fe)) {
AfxMessageBox(\文件打不开\; return FALSE;
}//以读写的方式打开文件; BOOL bSuccess = FALSE; BeginWaitCursor();
lpBI = (LPBITMAPINFOHEADER) ::GlobalLock((HGLOBAL) m_hDIB); if (lpBI == NULL) return FALSE;
dwDIBSize = *(LPDWORD)lpBI + 256*sizeof(RGBQUAD); //图像的文件信息所占用的字节数;
DWORD dwBmBitsSize;//BMP文件中位图的像素所占的字节数
dwBmBitsSize=WIDTHBYTES((lpBI->biWidth)*((DWORD)lpBI->biBitCount)) *lpBI->biHeight;// 存储时位图所有像素所占的总字节数
dwDIBSize += dwBmBitsSize; //BMP文件除文件信息结构外的所有数据占用的总字节数;
lpBI->biSizeImage = dwBmBitsSize; // 位图所有像素所占的总字节数 //以下五句为文件头结构填充值
bmfHdr.bfType =0x4d42; // 文件为\BMP\类型
bmfHdr.bfSize = dwDIBSize + sizeof(BITMAPFILEHEADER);//文件总长度 bmfHdr.bfReserved1 = 0; bmfHdr.bfReserved2 = 0;
bmfHdr.bfOffBits = (DWORD)sizeof(BITMAPFILEHEADER) + lpBI->biSize + 256*sizeof(RGBQUAD);
//位图数据距离文件头的偏移量;
file.Write((LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER));//向文件中写文件头信息;
file.WriteHuge(lpBI, dwDIBSize);
//将位图信息(信息头结构、颜色表、像素数据)写入文件; ::GlobalUnlock((HGLOBAL) m_hDIB); EndWaitCursor();
SetModifiedFlag(FALSE); // 将文档设为\干净\标志,表示此后文档不需要存盘提示;
return TRUE; }
二、 调色板的操作
通过上面的操作,我们已经可以获取图像中的数据了,现在的又一个问题是如何在窗口中显示出图像数据。灰度图像要正确显示,必须实现逻辑调色板和系统调色板。首先我们介绍一下逻辑调色板结构LOGPALETTE,该结构定义如下:
typedef struct tagLOGPALETTE {
WORD palVersion;//调色板的板本号,应该指定该值为0x300;
WORD palNumEntries;//调色板中的表项数,对于灰度图像该值为256;
PALETEENTRY palPalEntry[1];//调色板中的颜色表项,由于该表项的数目不一定,所以这里数组长度定义为1,灰度图像对应的该数组的长度为256; }LOGPALETTE;
颜色表项结构PALETTEENTRY定义了调色板中的每一个颜色表项的颜色和使用方式,定义如下:
typedef struct tagPALETTEENTRY {
BYTE peRed; //R分量值; BYTE peGreen; //G分量值; BYTE peBlue; //B分量值;
BYTE peFlags; // 该颜色被使用的方式,一般情况下设为\; }PALETTEENTRY;
Windows系统使用调色板管理器来管理与调色板有关的操作,通常活动窗口的调色板即是当前系统调色板,所有的非活动窗口都必须按照此系统调色板来显示自己的颜色,此时调色板管理器将自动的用系统调色板中的最近似颜色来映射相应的显示颜色。如果窗口或应用程序按自己的调色板显示颜色,就必须将自己的调色板载入到系统调色板中,这种操作叫作实现调色板,实现调色板包括两个步骤,既首先将调色板选择到设备上下文中,然后在设备上下文中实现它。可以通过CDC::SelectPalette()、CDC::RealizePalette()或相应的API函数来实现上述的两个步骤。在实现调色板的过程中,通过在主框架类中处理Windows定义的消息WM_QUERYNEWPALETTE 、WM_PALETTECHANGED及视图类中处理自定义消息WM_DOREALIZE(该消息在主框架窗口定义如下:#define WM_REALIZEPAL (WM_USER+101))来实现调色板的操作。当系统需要处理调色板的变化时,将向
程序的主窗口发送WM_QUERYNEWPALETTE 、WM_PALETTECHANGED,例如当某一窗口即将激活时,主框架窗口将收到WM_QUERYNEWPALETTE消息,通知该窗口将要收到输入焦点,给它一次机会实现其自身的逻辑调色板;当系统调色板改变后,主框架窗口将收到WM_PALETTECHANGED消息,通知其它窗口系统调色板已经改变,此时每一窗口都应该实现其逻辑调色板,重画客户区。
由于上述的调色板变更消息是发往主框架窗口的,所以我们只能在主窗口中响应这两个消息,然后由主框架窗口通知各个视窗,使得程序激活时能自动装载自己的调色板。我们定义的用户消息WM_REALIZEPAL用于主框架窗口通知视窗它已经收到调色板变更消息,视窗应该协调其调色板。下面我们给出了各个消息的响应处理函数的具体实现代码和注释:
////////////////////////////////////////////////////////// void CMainFrame::OnPaletteChanged(CWnd* pFocusWnd) {//总实现活动视的调色板
CMDIFrameWnd::OnPaletteChanged(pFocusWnd);
CMDIChildWnd* pMDIChildWnd = MDIGetActive();//得到活动的子窗口指针; if (pMDIChildWnd == NULL) return
CView* pView = pMDIChildWnd->GetActiveView();//得到视图的指针; ASSERT(pView != NULL);
SendMessageToDescendants(WM_DOREALIZE, (WPARAM)pView->m_hWnd); //通知所有子窗口系统调色板已改变 }
////////////////////////////////////////////////
BOOL CMainFrame::OnQueryNewPalette()//提供实现系统调色板的机会 {
// 实现活动视的调色板
CMDIChildWnd* pMDIChildWnd = MDIGetActive();//得到活动的子窗口指针; if (pMDIChildWnd == NULL)
return FALSE;//no active MDI child frame (no new palette)
CView* pView = pMDIChildWnd->GetActiveView();//得到活动子窗口的视图指针;
ASSERT(pView != NULL);
//通知活动视图实现系统调色板
pView->SendMessage(WM_DOREALIZE, (WPARAM)pView->m_hWnd); return TRUE; }
/////////////////////////////////////////////////
BOOL CDibView::OnDoRealize(WPARAM wParam, LPARAM)//实现系统调色板 {
ASSERT(wParam != NULL);
CDibDoc* pDoc = GetDocument(); if (pDoc->m_hDIB == NULL)
return FALSE; // must be a new document CPalette* pPal = pDoc->m_palDIB;
//调色板的颜色表数据在InitDIBData()函数中实现 if (pPal != NULL) {
CMainFrame* pAppFrame = (CMainFrame*) AfxGetApp()->m_pMainWnd;//得到程序的主框架指针;
ASSERT_KINDOF(CMainFrame, pAppFrame);
CClientDC appDC(pAppFrame);//获取主框架的设备上下文;
CPalette* oldPalette = appDC.SelectPalette(pPal, ((HWND)wParam) != m_hWnd);
//只有活动视才可以设为\,即根据活动视的调色板设为\前景\调色板; if (oldPalette != NULL) {
UINT nColorsChanged = appDC.RealizePalette();//实现系统调色板 if (nColorsChanged > 0)
pDoc->UpdateAllViews(NULL);//更新视图 appDC.SelectPalette(oldPalette, TRUE); //将原系统调色板置为背景调色板 } else {
TRACE0(\}
return TRUE; }
注:在调用API函数显示位图时,不要忘记设置逻辑调色板,即\背景\调色板,否则位图将无法正确显示,读者可以从后面的显示部分的实现看出我们在显示时实现了逻辑调色板。上述的处理相对来说比较繁琐复杂,可能对于初学者来说也比较难于理解,所以如果我们的程序仅仅限于处理灰度图象,可以采用另外一种相对简单的办法,即在文档类的初始化阶段定义一个灰度调色板,然后在设备上下文中实现它,这样作的好处是在度取灰度位图时可以不再考虑文件中的颜色表信息,提高了文件读取速度,笔者在开发一个基于机器视觉的项目时采用的就是这种方法,取的了比较满意的效果。首先定义一个指向逻辑颜色表结构 LOGPALETTE的指针pPal,填充该指针,然后将该指针与调色板指针联系起来,该方法的具体实现如下:
///////////////////////////////////////////////////////// CDibDoc::CDibDoc() {
............................ LOGPALETTE *Pal; Pal=new LOGPALETTE;
m_palDIB=new Cpalette; pPal->palVersion=0x300; pPal->palNumEntries=256; for(int i=0;i<256;i++)
{//每个颜色表项的R、G、B值相等,并且各个值从\到\序列展开; Pal->palPalentry[i].peRed=i; pPal->palPalentry[i].peGreen=i; pPal->palPalentry[i].peBlue=i; pPal->palPalentry[i].peFlags=0; }
m_palDIB->CreatePalette(pPal); ....................... }
三、 图像的显示
显示DIB位图数据可以通过设备上下文CDC对象的成员函数CDC::Bitblt()或CDC::StretchBlt()来实现,也可以通过API函数SetDIBBitsToDevice()或StretchDIBBits()来实现,函数中具体所用到的各个参数的意义可以参考 MSDN。其中StretchDIBBits()和CDC::StretchBlt()可以将图像进行放大和缩小显示。当从文档中装入位图文件时, CDIBView类的OnInitialUpdate函数将被调用,因此可以在该函数中实现对视图尺寸的设置,用于正确的显示位图,然后就可以在视图类的 OnDraw()函数中正确的显示位图了。这两个函数的具体实现代码分别如下所示:
///////////////////////////////////////////////////////////// void CDIBView::OnInitialUpdate() {
CscrollView::OnInitalUpdate(); CDIBDoc *pDoc=GetDocument();
If(pDoc->m_hDIB==NULL)//如果位图数据为空,设置m_sizeDoc的默认尺寸; pDoc->m_sizeDoc.cx=pDoc->m_sizeDoc.cy=100; SetScrollSizes(MM_TEXT,pDoc-> m_sizeDoc); }
///////////////////////////////////////////////////////////// void CDIBView::OnDraw(CDC *pDC) {
BITMAPINFOHEADER *lpDIBHdr;//位图信息头结构指针; BYTE *lpDIBBits;//指向位图像素灰度值的指针; BOOL bSuccess=FALSE;
CPalette*OldPal=NULL;//调色板指针;
HDC hDC=pDC->GetSafeHdc();//获取当前设备上下文的句柄; CDIBDoc *pDoc=GetDocument();//获取活动文档的指针; If(pDoc->m_hDIB ==NULL)
百度搜索“77cn”或“免费范文网”即可找到本站免费阅读全部范文。收藏本站方便下次阅读,免费范文网,提供经典小说综合文库VC数字图像处理编程(3)在线全文阅读。
相关推荐: