void CDibView::OnFDImage() //产生"浮雕"效果图函数 { HANDLE data1handle; LPBITMAPINFOHEADER lpBi; CDibDoc *pDoc=GetDocument(); HDIB hdib; unsigned char *hData; unsigned char *data; hdib=pDoc->GetHDIB(); //我是如何打开图像文件并得到图像数据,请感兴趣的朋友参考 //天极网上我的相关文章,那里有详细的论述,这里不再赘述。 BeginWaitCursor(); lpBi=(LPBITMAPINFOHEADER)GlobalLock((HGLOBAL)hdib); hData=(unsigned char*)FindDIBBits((LPSTR)lpBi); pDoc->SetModifiedFlag(TRUE); data1handle=GlobalAlloc(GMEM_SHARE,WIDTHBYTES(lpBi->biWidth*8)*lpBi->biHeight); //声明一个缓冲区用来暂存处理后的图像数据 data=(unsigned char*)GlobalLock((HGLOBAL)data1handle); AfxGetApp()->BeginWaitCursor(); int i,j,buf; for( i=lpBi->biHeight; i>=2; i--) for( j=lpBi->biWidth; j>=2; j--) { //"浮雕"处理 buf=*(hData+(lpBi->biHeight-i)*WIDTHBYTES(lpBi->biWidth*8)+j)-*(hData+(lpBi->biHeight-i+1)*WIDTHBYTES(lpBi->biWidth*8)+j-1)+128; if(buf>255) buf=255; if(buf<0)buf=0; *(data+(lpBi->biHeight-i)*WIDTHBYTES(lpBi->biWidth*8)+j)=(BYTE)buf; } for( j=0; jbiHeight; j++) for( i=0; ibiWidth; i++) //重新写回原始图像的数据缓冲区 *(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)=*(data+i*WIDTHBYTES(lpBi->biWidth*8)+j); AfxGetApp()->EndWaitCursor(); GlobalUnlock((HGLOBAL)hdib); GlobalUnlock(data1handle); EndWaitCursor(); Invalidate(TRUE);//显示图像 } |
void CDibView::OnDKImage() { // TODO: Add your command handler code here HANDLE data1handle; LPBITMAPINFOHEADER lpBi; CDibDoc *pDoc=GetDocument(); HDIB hdib; unsigned char *hData; unsigned char *data; hdib=pDoc->GetHDIB(); BeginWaitCursor(); lpBi=(LPBITMAPINFOHEADER)GlobalLock((HGLOBAL)hdib); hData=(unsigned char*)FindDIBBits((LPSTR)lpBi); pDoc->SetModifiedFlag(TRUE); data1handle=GlobalAlloc(GMEM_SHARE,WIDTHBYTES(lpBi->biWidth*8)*lpBi->biHeight); data=(unsigned char*)GlobalLock((HGLOBAL)data1handle); AfxGetApp()->BeginWaitCursor(); int i,j,buf; //图像的"雕刻"处理 for( i=0;i<=lpBi->biHeight-2; i++) for( j=0;j<=lpBi->biWidth-2; j++) { buf=*(hData+(lpBi->biHeight-i)*WIDTHBYTES(lpBi->biWidth*8)+j)-*(hData+(lpBi->biHeight-i-1)*WIDTHBYTES(lpBi->biWidth*8)+j+1)+128; if(buf>255) buf=255; if(buf<0)buf=0; *(data+(lpBi->biHeight-i)*WIDTHBYTES(lpBi->biWidth*8)+j)=(BYTE)buf; } for( j=0; jbiHeight; j++) for( i=0; ibiWidth; i++) //重新将处理后的图像数据写入原始的图像缓冲区内 *(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)=*(data+i*WIDTHBYTES(lpBi->biWidth*8)+j); AfxGetApp()->EndWaitCursor(); GlobalUnlock((HGLOBAL)hdib); GlobalUnlock(data1handle); EndWaitCursor(); Invalidate(TRUE); } |
Lena原图 |
"雕刻"效果图 |
"浮雕"效果图 |
图 1 软件界面 |
图 2本软件的图形对象类的设计 |
class CMainFrame : public CMDIFrameWnd { DECLARE_DYNAMIC(CMainFrame)//支持动态建立 public: CMainFrame(); // Attributes public: // Operations public: // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CMainFrame) public: virtual BOOL PreCreateWindow(CREATESTRUCT& cs); //}}AFX_VIRTUAL // Implementation public: int m_Depth;//立体深度 COLORREF m_fillcolor;//填充色 COLORREF m_pencolor;//边框色 LOGFONT m_font;//字体 int m_penstyle;//线型 UINT m_penwidth;//笔宽 void SaveToReg();//记录退出前的窗口状态 void ReadFromReg();//读取退出前的窗口状态 objecttype GetDrawType();//返回当前选中的绘图工具类别 virtual ~CMainFrame(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif protected: // control bar embedded members CStatusBar m_wndStatusBar;//状态栏 CReBar m_wndReBar;//标准栏和属性栏的容器工具条 CDialogBar m_wndDlgBar;//属性栏 CToolBar m_wndToolBar;//标准栏 CToolBar m_wndDrawTool;//绘图工具条 UINT objtype;//选中工具的ID号 // Generated message map functions protected: afx_msg void OnDropDown(NMHDR* pNotifyStruct,LRESULT* result); //{{AFX_MSG(CMainFrame) afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct); afx_msg void OnShowdrawtool();//显隐工具条 afx_msg void OnUpdateShowdrawtool(CCmdUI* pCmdUI); afx_msg void OnFont(); afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized); afx_msg void OnColor(); afx_msg void OnUpdateColor(CCmdUI* pCmdUI); afx_msg void OnFillcolor(); afx_msg void OnUpdateFillcolor(CCmdUI* pCmdUI); //}}AFX_MSG afx_msg void OnSelectTool(UINT ID);//选中工具 afx_msg void OnUpdateButtons(CCmdUI* pCmdUI);//处理按钮按下状态 afx_msg void onchangedpenwidth(); DECLARE_MESSAGE_MAP() }; |
class CDrawGraphDoc : public CDocument { protected: // create from serialization only CDrawGraphDoc(); DECLARE_DYNCREATE(CDrawGraphDoc) // Attributes public: // Operations public: // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CDrawGraphDoc) public: virtual BOOL OnNewDocument(); virtual void Serialize(CArchive& ar); virtual BOOL OnOpenDocument(LPCTSTR lpszPathName); virtual void DeleteContents(); //}}AFX_VIRTUAL // Implementation public: CMainFrame* GetMainFrame();//获得对主框架窗口的指针 BOOLEAN m_fillmode;//两种填充方式 void Cancel();//删除当前正在建立的绘图对象 COLORREF m_color; COLORREF m_filledcolor; UINT m_PenWidth; CGraph* NewDrawing(); CObList m_graphoblist;//绘图对象列表 CObList m_redolist;//为redo功能提供的历史记录对象列表 //以后可以添加下面的功能,把图形存储为流行的图形交互格式。 //SaveAsBitmap(); //SaveAsWMF(); //SaveAsJPEG(); //SaveAsGIF(); virtual ~CDrawGraphDoc(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif protected: // Generated message map functions protected: void Refresh();//用于更新视图 void InitDocument(); //{{AFX_MSG(CDrawGraphDoc) afx_msg void OnFillinborder(); afx_msg void OnUpdateFillinborder(CCmdUI* pCmdUI); afx_msg void OnFilloncolor(); afx_msg void OnUpdateFilloncolor(CCmdUI* pCmdUI); afx_msg void OnPenwidth(); afx_msg void OnEditUndo(); afx_msg void OnUpdateEditUndo(CCmdUI* pCmdUI); afx_msg void OnClear(); afx_msg void OnUpdateClear(CCmdUI* pCmdUI); afx_msg void OnEditRedo(); afx_msg void OnUpdateEditRedo(CCmdUI* pCmdUI); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; |
class CDrawGraphView : public CView { protected: // create from serialization only CDrawGraphView(); DECLARE_DYNCREATE(CDrawGraphView) // Attributes public: CDrawGraphDoc* GetDocument(); // Operations public: // Overrides // ClassWizard generated virtual function overrides //{{AFX_VIRTUAL(CDrawGraphView) public: virtual void OnDraw(CDC* pDC); // overridden to draw this view virtual BOOL PreCreateWindow(CREATESTRUCT& cs); protected: virtual BOOL OnPreparePrinting(CPrintInfo* pInfo); virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo); virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo); //}}AFX_VIRTUAL // Implementation public: virtual ~CDrawGraphView(); #ifdef _DEBUG virtual void AssertValid() const; virtual void Dump(CDumpContext& dc) const; #endif protected: // Generated message map functions protected: CPoint m_ptPrev;//前面一次点击的位置 CGraph* m_curGraph;//当前正在绘制的图形对象 state bdrawbegin;//绘制状态 //{{AFX_MSG(CDrawGraphView) afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnMouseMove(UINT nFlags, CPoint point); afx_msg void OnRButtonDown(UINT nFlags, CPoint point); afx_msg void OnLButtonDblClk(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); //}}AFX_MSG DECLARE_MESSAGE_MAP() }; |
enum state{notstart=0,startstroke,continuedrag,enddraw}; typedef enum {line ,bezier,solid,light,stroke,circle,rectangle,filledrectangle,& polyline,filledcircle,filledpolygon,ellipse,fill,text} objecttype; class CGraph : public CObject {protected: CGraph( ){}; DECLARE_DYNAMIC( CGraph ) // Attributes protected: COLORREF m_color;//所有图形对象都有颜色 public: // Operations public: virtual state SetNext(CPoint pt)=0;//再次点击,由返回值确定是否结束绘制 virtual void SetStart(CPoint pt)=0;//一次点击,产生第一点的位置 virtual void Draw( CDC* pDC )=0;//图形对象绘制自己的方法 inline void SetColor(COLORREF color){m_color=color;};//设置图形对象颜色 virtual void DrawXOR(CDC*pDC,CPoint pt)=0;//在拖动状态,图形对象绘制自己的方法 virtual void Serialize( CArchive& ar );//图形对象序列化的方法 //以后可以扩展以下功能 // virtual void IsHit(CPoint pt);//确定对象是否被点击 // virtual void Highlight();//被点击后突出显示 // virtual CRect GetBoundRect();//获得图形矩阵,可以用线索的方法局部更新视图,免除闪烁和时延等。 // virtual void Move(CPoint shift);//移动 // virtual void Rotate(int Degree);//旋转 // virtual void Scale(int scalecoef);//缩放 // virtual void Copy();//拷贝、粘贴、剪切功能 // virtual void Paste(); // virtual void Cut(); }; |
f(x-1,y-1) | f(x,y-1) | f(x+1,y-1) |
f(x-1,y) | f(x,y) | f(x+1,y) |
f(x-1,y+1) | f(x,y+1) | f(x+1,y+1) |
void CDibView::OnMENUSobel() //灰度图像数据的获得参见天极网9.10日发表的拙作//VC数字图像处理一文 { HANDLE data1handle; LPBITMAPINFOHEADER lpBi; CDibDoc *pDoc=GetDocument(); HDIB hdib; unsigned char *hData; unsigned char *data; hdib=pDoc->m_hDIB; BeginWaitCursor(); lpBi=(LPBITMAPINFOHEADER)GlobalLock((HGLOBAL)hdib); hData= lpbi +* (LPDWORD)lpbi + 256*sizeof(RGBQUAD); //得到指向位图像素值的指针 pDoc->SetModifiedFlag(TRUE);//设修改标志为"TRUE" data1handle=GlobalAlloc(GMEM_SHARE,WIDTHBYTES(lpBi->biWidth*8)*lpBi->biHeight); //申请存放处理后的像素值的缓冲区 data=(unsigned char*)GlobalLock((HGLOBAL)data1handle); AfxGetApp()->BeginWaitCursor(); int i,j,buf,buf1,buf2; for( j=0; jbiHeight; j++)//以下循环求(x,y)位置的灰度值 for( i=0; ibiWidth; i++) { if(((i-1)>=0)&&((i+1)biWidth)&&((j-1)>=0)&&((j+1)biHeight)) {//对于图像四周边界处的向素点不处理 buf1=(int)*(hData+(i+1)*WIDTHBYTES(lpBi->biWidth*8)+(j-1)) +2*(int)*(hData+(i+1)*WIDTHBYTES(lpBi->biWidth*8)+(j)) +(int)(int)*(hData+(i+1)*WIDTHBYTES(lpBi->biWidth*8)+(j+1)); buf1=buf1-(int)(int)*(hData+(i-1)*WIDTHBYTES(lpBi->biWidth*8)+(j-1)) -2*(int)(int)*(hData+(i-1)*WIDTHBYTES(lpBi->biWidth*8)+(j)) -(int)(int)*(hData+(i-1)*WIDTHBYTES(lpBi->biWidth*8)+(j+1)); //x方向加权微分 buf2=(int)(int)*(hData+(i-1)*WIDTHBYTES(lpBi->biWidth*8)+(j+1)) +2*(int)(int)*(hData+(i)*WIDTHBYTES(lpBi->biWidth*8)+(j+1)) +(int)(int)*(hData+(i+1)*WIDTHBYTES(lpBi->biWidth*8)+(j+1)); buf2=buf2-(int)(int)*(hData+(i-1)*WIDTHBYTES(lpBi->biWidth*8)+(j-1)) -2*(int)(int)*(hData+(i)*WIDTHBYTES(lpBi->biWidth*8)+(j-1)) -(int)(int)*(hData+(i+1)*WIDTHBYTES(lpBi->biWidth*8)+(j-1)); //y方向加权微分 buf=abs(buf1)+abs(buf2);//求梯度 if(buf>255) buf=255; if(buf<0){buf=0; *(data+i*WIDTHBYTES(lpBi->biWidth*8)+j)=(BYTE)buf; } else *(data+i*lpBi->biWidth+j)=(BYTE)0; } for( j=0; jbiHeight; j++) for( i=0; ibiWidth; i++) *(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)=*(data+i*WIDTHBYTES(lpBi->biWidth*8)+j); //处理后的数据写回原缓冲区 AfxGetApp()->EndWaitCursor(); GlobalUnlock((HGLOBAL)hdib); GlobalUnlock(data1handle); GlobalFree(date1handle); EndWaitCursor(); Invalidate(TRUE); } |
CPoint point; int nWidth,nHeight; int dx,dy; int dx1,dy1; |
BOOL CFlashDlgDlg::OnInitDialog() { CDialog::OnInitDialog(); CRect dlgRect; GetWindowRect(dlgRect); CRect desktopRect; GetDesktopWindow()->GetWindowRect(desktopRect); MoveWindow( (desktopRect.Width() - dlgRect.Width()) / 2, (desktopRect.Height() - dlgRect.Height()) / 2, 0, 0 ); nWidth=dlgRect.Width(); nHeight=dlgRect.Height(); dx=2; dy=4; dx1=2; dy1=2; SetTimer(1,10 , NULL); return TRUE; } |
void CFlashDlgDlg::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default CRect dlgRect; GetWindowRect(dlgRect); CRect desktopRect; GetDesktopWindow()->GetWindowRect(desktopRect); if(nIDEvent == 1) { MoveWindow( (-dx+desktopRect.Width() - dlgRect.Width()) / 2, (-dy+desktopRect.Height() - dlgRect.Height()) / 2, +dx+dlgRect.Width(), +dy+dlgRect.Height() ); if(dlgRect.Width() >=nWidth) dx=0; // do not over grow if(dlgRect.Height() >=nHeight) dy=0; // do not over grow if((dlgRect.Width() >=nWidth) && (dlgRect.Height() >=nHeight)) KillTimer(1); //Stop the timer } if((dlgRect.Width() >=nWidth) && (dlgRect.Height() >=nHeight)) KillTimer(1); //Stop the timer if(nIDEvent == 2) { MoveWindow((+dx+desktopRect.Width() - dlgRect.Width()) / 2, (+dy+desktopRect.Height() - dlgRect.Height()) / 2, -dx1+dlgRect.Width(), -dy1+dlgRect.Height() ); if(dlgRect.Width() <= 0) dx1=0; // do not over grow if(dlgRect.Height() <= 0 ) dy1=0; // do not over grow if((dlgRect.Width() <= 0 ) && (dlgRect.Height() <=0)) { KillTimer(2); //Stop the timer CDialog::OnOK(); } } CDialog::OnTimer(nIDEvent); } |
void CDibView::OnMenuchange() file://图像转换实现函数 { // TODO: Add your command handler code here HANDLE data1handle; LPBITMAPINFOHEADER lpBi; BITMAPINFO *m_pBMI; CDibDoc *pDoc=GetDocument(); HDIB hdib; unsigned char *hData; unsigned char *data; hdib=pDoc->GetHDIB();//得到位图数据的句柄,其中包含图像信息头 BeginWaitCursor(); lpBi=(LPBITMAPINFOHEADER)GlobalLock((HGLOBAL)hdib); hData=(unsigned char*)FindDIBBits((LPSTR)lpBi); m_pBMI=new BITMAPINFO;//生成彩色图像的信息头 m_pBMI->bmiHeader.biBitCount=24; m_pBMI->bmiHeader.biClrImportant=0; m_pBMI->bmiHeader.biClrUsed=0; m_pBMI->bmiHeader.biCompression=BI_RGB; m_pBMI->bmiHeader.biHeight=lpBi->biHeight; m_pBMI->bmiHeader.biWidth=lpBi->biWidth; m_pBMI->bmiHeader.biPlanes=1; m_pBMI->bmiHeader.biSize=sizeof(BITMAPINFOHEADER); m_pBMI->bmiHeader.biXPelsPerMeter=0; m_pBMI->bmiHeader.biYPelsPerMeter=0; m_pBMI->bmiHeader.biSizeImage=WIDTHBYTES(lpBi->biWidth*8)*lpBi->biHeight*3; file://data=hData; int R,G,B,i,j; data1handle=GlobalAlloc(GMEM_SHARE,WIDTHBYTES(lpBi->biWidth*8)*lpBi->biHeight*3); file://生成存储彩色图象数据的缓冲区 data=(unsigned char*)GlobalLock((HGLOBAL)data1handle); for(i=0;ibiHeight;i++)//实现灰度到彩色变换 for(j=0;jbiWidth*8);j++) { if(*(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)<=64) {R=0; G=(int)4*(*(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)); B=255; } if(*(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)>64 && *(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)<=128) {R=0; G=255; B=(int)4*(128-*(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)); } if(*(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)>128 && *(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)<=192) {R=(int)4*(*(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)-128); G=255; B=0; } if(*(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)>192 && *(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)<=255) {R=255; G=(int)4*(255-*(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)); B=0; } file://将生成的R、G、B分量存入目标缓冲区 *(data+i*WIDTHBYTES(lpBi->biWidth*8)*3+j*3)=B; *(data+i*WIDTHBYTES(lpBi->biWidth*8)*3+j*3+1)=G; *(data+i*WIDTHBYTES(lpBi->biWidth*8)*3+j*3+2)=R; } GlobalUnlock((HGLOBAL)hdib); GlobalUnlock(data1handle); EndWaitCursor(); CClientDC pDC(this); file://显示真彩色图像 StretchDIBits(pDC.GetSafeHdc(),0,0,lpBi->biWidth,lpBi->biHeight,0,0, lpBi->biWidth, lpBi->biHeight,data,m_pBMI,DIB_RGB_COLORS, SRCCOPY); delete m_pBMI; } |
图 一 |
图 二 |
BOOL CDibDoc::OnOpenDocument(LPCTSTR lpszPathName) { CFile file; CFileException fe; if (!file.Open(lpszPathName, CFile::modeRead | CFile::shareDenyWrite, &fe)) { AfxMessageBox("文件打不开"); return FALSE; }//打开文件 DeleteContents();//删除文挡 BeginWaitCursor(); BITMAPFILEHEADER bmfHeader;//定义位图文件头结构 DWORD dwBitsSize; HANDLE hDIB; LPSTR pDIB; BITMAPINFOHEADER *bmhdr;//指向位图信息头结构的指针 dwBitsSize = file.GetLength();//得到文件长度 if (file.Read((LPSTR)&bmfHeader, sizeof(bmfHeader)) != sizeof(bmfHeader)) return FALSE; if (bmfHeader.bfType != 0x4d42) file://检查是否为BMP文件 return FALSE; hDIB=(HANDLE) ::GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, dwBitsSize); file://申请缓冲区 if (hDIB == 0) { return FALSE; } pDIB = (LPSTR) ::GlobalLock((HGLOBAL)hDIB); file://得到申请的缓冲区的指针 if (file.ReadHuge(pDIB, dwBitsSize - sizeof(BITMAPFILEHEADER)) != dwBitsSize - sizeof(BITMAPFILEHEADER) ) { ::GlobalUnlock((HGLOBAL)hDIB); hDIB=NULL; return FALSE; }//读数据,包括位图信息、位图颜色表、图像像素的灰度值 bmhdr=(BITMAPINFOHEADER*)pDIB;//为指向位图信息头结构的指针付值 ::GlobalUnlock((HGLOBAL)hDIB); if ((*bmhdr).biBitCount!=8) file://验证是否为8bit位图 return FALSE; m_hDIB=hDIB; InitDIBData(); file://自定义函数,根据读入的数据得到位图的宽、高、颜色表 file:// 来得到初始化视的尺寸、生成调色板 EndWaitCursor(); SetPathName(lpszPathName);//设置存储路径 SetModifiedFlag(FALSE); // 设置文件修改标志为FALSE return TRUE; } |
BOOL CDibDoc::OnSaveDocument(LPCTSTR lpszPathName) { CFile file; CFileException fe; BITMAPFILEHEADER bmfHdr; // 位图文件头结构 LPBITMAPINFOHEADER lpBI; file://指向位图信息结构的指针 DWORD dwDIBSize; if (!file.Open(lpszPathName, CFile::modeCreate | CFile::modeReadWrite | CFile::shareExclusive, &fe)) { AfxMessageBox("文件打不开"); }//打开文件 BOOL bSuccess = FALSE; BeginWaitCursor(); lpBI = (LPBITMAPINFOHEADER) ::GlobalLock((HGLOBAL) m_hDIB); if (lpBI == NULL) return FALSE; dwDIBSize = *(LPDWORD)lpBI + 256*sizeof(RGBQUAD); // Partial Calculation DWORD dwBmBitsSize;//BMP文件信息结构所占的字节数 dwBmBitsSize=WIDTHBYTES((lpBI->biWidth)*((DWORD)lpBI->biBitCount)) *lpBI->biHeight;// 存储时位图所有像素所占的总字节数 dwDIBSize += dwBmBitsSize; lpBI->biSizeImage = dwBmBitsSize; // 位图所有像素所占的总字节数 file://以下五句为文件头结构填充值 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://位图数据距问件头的偏移量 file.Write((LPSTR)&bmfHdr, sizeof(BITMAPFILEHEADER));//写文件头 file.WriteHuge(lpBI, dwDIBSize); file://将位图信息(信息头结构、颜色表、像素数据)写入文件 ::GlobalUnlock((HGLOBAL) m_hDIB); EndWaitCursor(); SetModifiedFlag(FALSE); // back to unmodified return TRUE; } |
void CMainFrame::OnPaletteChanged(CWnd* pFocusWnd) { file://总实现活动视的调色板 CMDIFrameWnd::OnPaletteChanged(pFocusWnd); CMDIChildWnd* pMDIChildWnd = MDIGetActive(); if (pMDIChildWnd == NULL) return CView* pView = pMDIChildWnd->GetActiveView(); ASSERT(pView != NULL); SendMessageToDescendants(WM_DOREALIZE, (WPARAM)pView->m_hWnd); file://通知所有子窗口系统调色板已改变 } 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); file://通知活动视图实现系统调色板 pView->SendMessage(WM_DOREALIZE, (WPARAM)pView->m_hWnd); return TRUE; } LRESULT CDibView::OnDoRealize(WPARAM wParam, LPARAM)//实现系统调色板 { ASSERT(wParam != NULL); CDibDoc* pDoc = GetDocument(); if (pDoc->m_hDIB == NULL) return 0L; // must be a new document CPalette* pPal = pDoc->m_palDIB; file://调色板的颜色表数据在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); file://只有活动视才可以设为"FALSE", // 即根据活动视的调色板设为"前景"调色板 if (oldPalette != NULL) { UINT nColorsChanged = appDC.RealizePalette();//实现系统调色板 if (nColorsChanged > 0) pDoc->UpdateAllViews(NULL);//更新视图 appDC.SelectPalette(oldPalette, TRUE); file://将原系统调色板置为逻辑调色板 } else { TRACE0("\tSelectPalette failed in CDibView::OnPaletteChanged\n"); } } |
void CDibView::OnMENUHighPass() { HANDLE data1handle; LPBITMAPINFOHEADER lpBi; CDibDoc *pDoc=GetDocument(); HDIB hdib; unsigned char *hData; unsigned char *data; hdib=pDoc->GetHDIB(); BeginWaitCursor(); lpBi=(LPBITMAPINFOHEADER)GlobalLock((HGLOBAL)hdib); hData=(unsigned char*)FindDIBBits((LPSTR)lpBi); pDoc->SetModifiedFlag(TRUE); data1handle=GlobalAlloc(GMEM_SHARE,WIDTHBYTES(lpBi->biWidth*8)*lpBi->biHeight); data=(unsigned char*)GlobalLock((HGLOBAL)data1handle); AfxGetApp()->BeginWaitCursor(); int i,j,s,t,ms=1; int sum=0,sumw=0; int mask[3][3]={{-1,-1,-1},{-1,9,-1},{-1,-1,-1}}; for(i=0; ibiHeight; i++) for(j=0; jbiWidth; j++) { sumw=0; sum=0; for(s=(-ms); s<=ms; s++) for(t=(-ms); t<=ms; t++) if(((i+s)>=0) && ((j+t)>=0) && ((i+s)biHeight) && ((j+t)biWidth)) { sumw += mask[1+s][1+t]; sum+=*(hData+(i+s)*WIDTHBYTES(lpBi->biWidth*8)+(j+t))*mask[1+s][1+t]; } if(sumw==0) sumw=1; sum/=sumw; if(sum>255)sum=255; if(sum<0)sum=0; *(data+i*WIDTHBYTES(lpBi->biWidth*8)+j)=sum; } for( j=0; jbiHeight; j++) for( i=0; ibiWidth; i++) *(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)=*(data+i*WIDTHBYTES(lpBi->biWidth*8)+j); AfxGetApp()->EndWaitCursor(); GlobalUnlock((HGLOBAL)hdib); GlobalUnlock(data1handle); EndWaitCursor(); Invalidate(TRUE); } |
//基于颜色直方图的方法 |
//计算子块(x,y)的颜色对表,采取"八方向邻接技术" //计算该图像的最终确定的特征颜色对表 |
以上三个函数实现对某一图像内部的具体计算,而对于基于颜色对方法的外部计算如下:
//计算用户确定的4块位置与其周围位置的颜色对(颜色对现采取用相邻两块的直方图的欧氏距离表示) int pairflag[32]; file://颜色对匹配标志数组,即某一颜色对如果在目标图像中找到,下一次就不能再匹配 |
通过以上的程序,我们就实现了真正的图像内容检索,简单的程序就实现了现代计算机科学在多媒体研究前沿的任务。
Visual C++实现数字图像增强处理
前言
对于一个图像处理系统来说,可以将流程分为三个阶段,在获取原始图像后,首先是图像预处理阶段、第二是特征抽取阶段、第三是识别分析阶段。图像预处理阶段尤为重要,如果这阶段处理不好,后面的工作根本无法展开。
在实际应用中,我们的系统获取的原始图像不是完美的,例如对于系统获取的原始图像,由于噪声、光照等原因,图像的质量不高,所以需要进行预处理,以有利于提取我们感兴趣的信息。图像的预处理包括图像增强、平滑滤波、锐化等内容。图像的预处理既可以在空间域实现,也可以在频域内实现,我们主要介绍在空间域内对图像进行点运算,它是一种既简单又重要的图像处理技术,它能让用户改变图像上像素点的灰度值,这样通过点运算处理将产生一幅新图像。下面我们开始介绍与图像点运算的相关知识。
一、图像的直方图
图像直方图是图像处理中一种十分重要的图像分析工具,它描述了一幅图像的灰度级内容,任何一幅图像的直方图都包含了丰富的信息,它主要用在图象分割,图像灰度变换等处理过程中。从数学上来说图像直方图是图像各灰度值统计特性与图像灰度值的函数,它统计一幅图像中各个灰度级出现的次数或概率;从图形上来说,它是一个二维图,横坐标表示图像中各个像素点的灰度级,纵坐标为各个灰度级上图像各个像素点出现的次数或概率。如果不特别说明,本讲座中的直方图的纵坐标都对应着该灰度级在图像中出现的概率。我们的例子是在一个对话框中显示一个图像的直方图,为实现该目的,我们定义了一个名为"ZFT"的对话框类用来显示图像的直方图,具体实现代码和效果图如下(关于代码实现部分可以参考笔者2001年在天极网上发表的一篇VC实现数字图像处理的文章):
//直方图对话框构造函数; ZFT::ZFT(CWnd* pParent /*=NULL*/) : CDialog(ZFT::IDD, pParent)//ZFT为定义的用来显示直方图的对话框类; { Width=Height=0;//对话框初始化阶段设置图像的宽和高为"0"; } 对话框重画函数; void ZFT::OnPaint() { CRect rect;//矩形区域对象; CWnd *pWnd;//得到图片框的窗口指针; pWnd=GetDlgItem(IDC_Graphic);//得到ZFT对话框内的"Frame"控件的指针; file://(IDC_Graphic为放置在对话框上的一个"Picture"控件,并讲类型设置为"Frame")。 pWnd->GetClientRect(&rect);//得到"Frame"控件窗口的"视"区域; int i; CPaintDC dc(pWnd);//得到"Frame"控件的设备上下文; file://画直方图的x、y轴; dc.MoveTo(0,rect.Height()); dc.LineTo(rect.Width(),rect.Height()); dc.MoveTo(0,rect.Height()); dc.LineTo(0,0); file://画直方图,num[]是"ZFT"的内部数组变量,存放的是图像各个灰度级出现的概率;该数组的各个分量在 显示具体图像的直方图时设置; for(i=0;i<256;i++)//根据图像上的各个灰度级出现的概率,在坐标上对应的画出一根直线,从而各个表示各灰度级出现概率的直线构成了图像的直方图; { dc.MoveTo(i+1,rect.Height()); dc.LineTo (i+1,(rect.Height()-rect.Height()*num[i]*30)); file://此处num分量乘以"30"是为了放大个灰度级上对应的出现概率直线,增强显示效果; } } void ZFT::OnMouseMove(UINT nFlags, CPoint point) {//OnMouseMove函数处理鼠标消息,显示当前鼠标所在直方图上的灰度值等信息; CWnd *pWnd,*pWndText;//定义两个窗口对象; CPoint point1;//定义个一个点对象; point1=point;//存放当前鼠标的位置信息; CRect rect;//矩形对象; CString string ;//字符串对象; pWnd=GetDlgItem(IDC_Graphic);//得到显示直方图的框架窗口对象指针; pWndText=GetDlgItem(IDC_NUM);//得到指向文本框对象(IDC_NUM)窗口的指针; pWnd->GetWindowRect(&rect);//获取pWnd窗口对象窗口区域位置; file://屏幕坐标转换为客户区坐标; ScreenToClient(&rect); file://判断当前鼠标是否指在直方图内; if(rect.PtInRect (point)) { int x=point1.x-rect.left; file://当前鼠标位置减去区域的起始位置恰好为当前鼠标所指位置所表示的灰度级; string.Format("%d",x); file://显示当前位置对应的图像的灰度级; pWndText->SetWindowText((LPCTSTR)string); } CDialog::OnMouseMove(nFlags, point); } void CDibView::OnImagehorgm() file://在程序的"视"类对象内处理显示图像直方图的函数; { CDibDoc *pDoc=GetDocument(); HDIB hdib; hdib=pDoc->GetHDIB(); BITMAPINFOHEADER *lpDIBHdr;//位图信息头结构指针; BYTE *lpDIBBits;//指向位图像素灰度值的指针; lpDIBHdr=( BITMAPINFOHEADER *)GlobalLock(hdib);//得到图像的位图头信息 lpDIBBits=(BYTE*)lpDIBHdr+sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD); file://获取图像像素值 ZFT dialog;//直方图对话框模板对象; int i,j; int wImgWidth=lpDIBHdr->biWidth; int wImgHeight=lpDIBHdr->biHeight; file://a[]数组用来存放各个灰度级出现的概率; float a[256]; for(i=0;i<256;i++)//初始化数组; { a[i]=0; } file://统计各个灰度级出现的次数; for(i=0;i for(j=0;j a[*(lpDIBBits+WIDTHBYTES(wImgWidth*8)*i+j)]++; } file://统计各个灰度级出现的概率; for(i=0;i<256;i++) { a[i]=a[i]/(wImgHeight*wImgWidth);//得到每个灰度级的出现概率; memcpy(dialog.num,a,256*sizeof(float)); } } dialog.DoModal();//显示直方图对话框; } return; } |
(a)LENA图像 |
(b)直方图 |
S=T (R); | k=0,1…, ; |
void CDibView::OnZftJh() { CClientDC pDC(this); HDC hDC=pDC.GetSafeHdc();//获取当前设备上下文的句柄; SetStretchBltMode(hDC,COLORONCOLOR); CDibDoc *pDoc=GetDocument(); HDIB hdib; hdib=pDoc->GetHDIB(); BITMAPINFOHEADER *lpDIBHdr;//位图信息头结构指针; BYTE *lpDIBBits;//指向位图像素灰度值的指针; lpDIBHdr=( BITMAPINFOHEADER *)GlobalLock(hdib);//得到图像的位图头信息 lpDIBBits=(BYTE*)lpDIBHdr+sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD); file://获取图像像素值 float p[256],p1[256],num[256]; int i,j,k; for(i=0;i<256;i++)//清空三个数组; { num[i]=0.0f; p[i]=0.0f; p1[i]=0.0f; } file://num[]存放图象各个灰度级出现的次数; int Height=lpDIBHdr->biHeight; int Width=lpDIBHdr->biWidth; for(i=0;i num[*(lpDIBBits+WIDTHBYTES(Width*8)*i+j)]++; } file://p[]存放图像各个灰度级的出现概率; for(i=0;i<256;i++) { p[i]=num[i]/(Width*Height); } file://p1[]存放各个灰度级之前的概率和,用于直方图变换; for(i=0;i<256;i++) { for(k=0;k<=i;k++) p1[i]+=p[k]; } file://直方图变换; for(i=0;i } StretchDIBits (hDC,0,0,lpDIBHdr->biWidth,lpDIBHdr->biHeight,0,0, lpDIBHdr->biWidth,lpDIBHdr->biHeight, lpDIBBits,(LPBITMAPINFO)lpDIBHdr, DIB_RGB_COLORS, SRCCOPY);//显示图像; } |
(a)LENA原图 |
(b)直方图均衡化后的效果图 |
(c)原始图象的直方图 |
(d)均衡化后的直方他图 |
void CDibView::OnImagePh() { CClientDC pDC(this); HDC hDC=pDC.GetSafeHdc();//获取当前设备上下文的句柄; SetStretchBltMode(hDC,COLORONCOLOR); HANDLE data1handle; LPBITMAPINFOHEADER lpBi; CDibDoc *pDoc=GetDocument(); HDIB hdib; unsigned char *hData; unsigned char *data; hdib=pDoc->GetHDIB(); BeginWaitCursor(); lpBi=(LPBITMAPINFOHEADER)GlobalLock((HGLOBAL)hdib); hData=(unsigned char*)FindDIBBits((LPSTR)lpBi); pDoc->SetModifiedFlag(TRUE); data1handle=GlobalAlloc(GMEM_SHARE,WIDTHBYTES(lpBi->biWidth*8)*lpBi->biHeight); data=(unsigned char*)GlobalLock((HGLOBAL)data1handle); AfxGetApp()->BeginWaitCursor(); int i,j,s,t,ms=1; int sum=0,sumw=0; int mask[3][3]={{1,1,1},{1,2,1},{1,1,1}};//定义的3x3加权平滑模板; for(i=0; ibiHeight; i++) for(j=0; jbiWidth; j++) { sumw=0; sum=0; for(s=(-ms); s<=ms; s++) for(t=(-ms); t<=ms; t++) if(((i+s)>=0)&&((j+t)>=0)&&((i+s)biHeight)&&((j+t)biWidth)) { sumw += mask[1+s][1+t]; sum+=*(hData+(i+s)*WIDTHBYTES(lpBi->biWidth*8)+(j+t))*mask[1+s][1+t]; } if(sumw==0) sumw=1; sum/=sumw; if(sum>255)sum=255; if(sum<0)sum=0; *(data+i*WIDTHBYTES(lpBi->biWidth*8)+j)=sum; } for( j=0; jbiHeight; j++) for(i=0;ibiWidth;i++) *(hData+i*WIDTHBYTES(lpBi->biWidth*8)+j)=*(data+i*WIDTHBYTES(lpBi->biWidth*8)+j); StretchDIBits (hDC,0,0,lpBi->biWidth,lpBi->biHeight,0,0, lpBi->biWidth,lpBi->biHeight, hData,(LPBITMAPINFO)lpBi, DIB_RGB_COLORS, SRCCOPY);//显示图像; } |
(a)LENA原图 |
(b)平滑后的效果图 |
(a) |
(b) |
(c) |
void CDibView::OnMenuitem32785() { CClientDC pDC(this); HDC hDC=pDC.GetSafeHdc();//获取当前设备上下文的句柄; SetStretchBltMode(hDC,COLORONCOLOR); CDibDoc *pDoc=GetDocument(); HDIB hdib; hdib=pDoc->GetHDIB(); BITMAPINFOHEADER *lpDIBHdr;//位图信息头结构指针; BYTE *lpDIBBits;//指向位图像素灰度值的指针; lpDIBHdr=( BITMAPINFOHEADER *)GlobalLock(hdib);//得到图像的位图头信息 lpDIBBits=(BYTE*)lpDIBHdr+sizeof(BITMAPINFOHEADER)+256*sizeof(RGBQUAD);//获取图像像素值 BYTE* pData1; static int a[3][3]={{1,4,1},{4,-20,4},{1,4,1}};//拉普拉斯算子模板; int m,n,i,j,sum; int Width=lpDIBHdr->biWidth; int Height=lpDIBHdr->biHeight; pData1=(BYTE*)new char[WIDTHBYTES(Width*8)*Height]; file://进行拉普拉斯滤波运算; for(i=1;i sum=0; for(m=-1;m<2;m++) for(n=-1;n<2;n++) sum+=*(lpDIBBits+WIDTHBYTES(Width*8)*(i+m)+j+n)*a[1+m][1+n]; if(sum<0) sum=0; if(sum>255) sum=255; *(pData1+WIDTHBYTES(Width*8)*i+j)=sum; } file://原始图像pData减去拉普拉斯滤波处理后的图像pData1 for(i=0;i if(sum<0) sum=0; if(sum>255) sum=255; *(lpDIBBits+WIDTHBYTES(Width*8)*i+j)=sum; } StretchDIBits (hDC,0,0,lpDIBHdr->biWidth,lpDIBHdr->biHeight,0,0, lpDIBHdr->biWidth,lpDIBHdr->biHeight, lpDIBBits,(LPBITMAPINFO)lpDIBHdr, DIB_RGB_COLORS, SRCCOPY); } |
(a)LENA原图 |
(b)拉普拉斯锐化图 |
F(x,v)=N*[(1/N)* f(x,y)exp[-j2πvy/N]] (v=0,1,……,N-1) F(u,v)=(1/N)* F(x,v)exp[-j2πux/N] (u,v=0,1,……,N-1) |
F(x,v)= F(u,v)exp[j2πux/N] (x,y=0,1,……,N-1) f(x,y)=(1/N)* F(x,v)exp[j2πvy/N]] (y=0,1,……,N-1) |
int N=(int)pow(2,M); file://N:序列长度(2的整数次幂) ReverseOrder(A,N); file://对空间序列进行倒序 for(int i=1;i<=M;i++){ int b=(int)pow(2,(i-1)); for(int j=0;j<=(b-1);j++) { float p=(float)(pow(2,(M-i))*j*2.0*PI/(float)N); for(int k=j;k<=(N-1);){ float tr=(float)(A[k+b].Re*cos(p)+A[k+b].Im*sin(p)); file://计算复数运算A*U float ti=(float)(A[k+b].Im*cos(p)-A[k+b].Re*sin(p)); A[k+b].Re=A[k].Re-tr; file://复数运算A-tr A[k+b].Im=A[k].Im-ti; A[k].Re+=tr; file://复数运算A+tr A[k].Im+=ti; k+=b*2; } } } |
int WM=(int)(log(W)/log(2)+1.0f); file://计算图像宽应为2的多少次幂 int HM=(int)(log(H)/log(2)+1.0f); file://计算图像高应为2的多少次幂 WM=HM=max(WM,HM); file://取二者大值 int WN=(int)pow(2,WM); file://构造网格宽度 int HN=(int)pow(2,HM); file://构造网格高度 for{int i=0;i;for(int j=0;j if(i U[i*WN*3+j].Re=D[i*W*3+j]; file://D为图像序列 U[i*WN*3+j].Im=0.0f; }else file://缺位补0 U[i*WN*3+j].Re=U[i*WN*3+j].Im=0.0f; } } |
for(i=0;i for(int j=0;j UH[j].Re=U[j*WN*3+i].Re; UH[j].Im=U[j*WN*3+i].Im; } DFT_FFT(UH,HM); file://对UH进行快速离散傅立叶变换 for(j=0;j U[j*WN*3+i].Re=HN*UH[j].Re; file://N=HN U[j*WN*3+i].Im=HN*UH[j].Im; } } |
for(i=0;i for(int j=0;j UW[j].Re=U[i*WN*3+j*3+k].Re; UW[j].Im=U[i*WN*3+j*3+k].Im; } DFT_FFT(UW,WM); file://对UW序列进行快速离散傅立叶变换 for(j=0;j U[i*WN*3+j*3+k].Re=UW[j].Re; U[i*WN*3+j*3+k].Im=UW[j].Im; } } } |
…… dsp.DFT_2D_FFT(m_cpBuffer+54,m_nWidth,m_nHeight,U); file://进行二维傅立叶变换 for(int i=0;i D1=(float)sqrt(i*i+k*k); H=1.0f/(1+(D1/D0)*(D1/D0)); file://H(u,v)=1/(1+(u2+v2)/D02)) if(H>0.45f){ file://阀值 d取0.45 U[i*3*WN+j].Re/=H; file://在频域与M(u,v)相乘 U[i*3*WN+j].Im/=H; }else{ U[i*3*WN+j].Re*=0.6f; file://如未超过阀值则M(u,v)取常数k=0.6 U[i*3*WN+j].Im*=0.6f; } } } dsp.DFT_2D_IFFT(m_cpBuffer+54,m_nWidth,m_nHeight,U); file://进行傅立叶逆变换 |
F(u,v)=[(1/H(u,v))*(|H(u,v)|2)/(|H(u,v)|2+s*[Sn(u,v)/Sf(u,v)])]*G(u,v) |
F(u,v)=[(1/H(u,v))* (|H(u,v)|2)/(|H(u,v)|2+K)]*G(u,v) |
…… float K=0.05f; file://预先设定常数K dsp.DFT_2D_FFT(m_cpBuffer+54,m_nWidth,m_nHeight,U); file://转换到频域 for(int i=0;i D1=(float)sqrt(i*i+k*k); float H=1.0f/(1+(D1/D0)*(D1/D0));//H(u,v)= 1/(1+(u2+v2)/D02)) U[i*3*WN+j].Re=(U[i*3*WN+j].Re*H)/(H*H+K); file://维纳滤波 U[i*3*WN+j].Im=(U[i*3*WN+j].Im*H)/(H*H+K); } } dsp.DFT_2D_IFFT(m_cpBuffer+54,m_nWidth,m_nHeight,U);//返回到空域 |
基于图像采集卡的视频图像处理系统
计算机图像处理系统从系统层次上可分为高、中、低档三个层次,目前一般比较普及的是低档次的系统,该系统由CCD(摄像头)、图像采集卡、计算机三个部分组成,其结构简单,应用方便,效果也比较不错,得到的图像较清晰。目前网上基于VC开发经验的文章不少,可是关于如何在VC开发平台上使用图像采集卡的文章确没发现,笔者针对在科研开发中积累的使用图像采集卡经验,介绍如何自己是如何将采集卡集成到图像开发系统中,希望能够给目前正需要利用图像采集卡开发自己的图像处理系统的朋友有所帮助。
笔者使用的摄像机采用台湾BENTECH INDUSTRIAL 有限公司生产的CV-155L黑白摄像机。该摄像机分辨率为752x582。图象采集卡我们采用北京中科院科技嘉公司开发的基于PCI 总线的CA-MPE 1000 黑白图象采集卡。使用图像采集卡分三步,首先安装采集卡的驱动程序,并将虚拟驱动文件VxD.vxd拷贝到Windows的SYSTEM目录下;这时候就可以进入开发状态了,进入VC开发平台,生成新的项目,由于生产厂家为图像采集卡提供了以mpew32.dll、mpew32.lib命名的库文件,库中提供了初始硬件、采集图像等函数,为使用这些函数,在新项目上连接该动态库;最后一步就是采集图像并显示处理了,这一步要设置系统调色板,因为采集卡提供的是裸图形式,既纯图像数据,没有图像的规格和调色板信息,这些需要开发者自己规定实现,下面是实现的部分代码:
CTestView::CTestView() / CTestView::~CTestView() SetStretchBltMode (hdc, COLORONCOLOR) ; bGrabMark = TRUE; while (bGrabMark == TRUE)
if ((pLogPal=(LOGPALETTE *)malloc(sizeof(LOGPALETTE) + (j*sizeof(PALETTEENTRY)))) == NULL) pLogPal->palVersion=0x300; for (i=0;i pLogPal->palPalEntry[i].peRed = i ;
cxDib = width; cyDib = height; if ( (bmi = (BITMAPINFO *)malloc(sizeof(BITMAPINFOHEADER) + j*sizeof(RGBQUAD))) == NULL ) bmi->bmiHeader.biBitCount = bitCount ; bmi->bmiHeader.biCompression = 0 ; for (i=0;i bmi->bmiColors[i].rgbBlue = i ; } |
"画中画"这个概念类似与彩色电视机"画中画",就是在一幅大的图像内显示另外一幅内容不同的小的图像,小图像的尺寸大小一般地说为大图像尺寸的1/4或1/9,显示位置在大图像的右上角。这种技术不仅在电视技术中,在可视电话系统也可以发现这种技术的身影,它们都是依靠硬件来实现的,但是如何在VC开发平台上用编程语言来将该功能添加到自己开发的视频监控软件,为使用者提供更大的信息量呢?也许读者最容易想到的是首先显示大图像,然后再在一个固定位置画第二幅小图像,这种技术技术如果对于静止图像当然没有问题,但是对于视频流,由于每一秒钟需要画25幀,即25幅图像,这样一来计算机需要不停的画不停的擦除,会给用户以闪烁的感觉,如何解决这个问题呢?有的参考书上将大小图像分快显示,这种方法要将待显示的图像数据与显示位置的关系对应起来,容易出错不说,而且麻烦,且速度慢,为此,我对该方法进行了改进,得到了满意的效果。实现的代码如下:
void pictureinpicture( ) CDC MemDc; MemDc.SelectObject(oldmap); } |
一、 引言
在工程上我们经常要判断某设备产生的实际波形信号是否能同预先设计的相拟合,但由于实际产生的波形不仅仅是简单的正、余弦波形,而往往是含有较丰富频率分布的不规则波形,而设备元器件本身及外界的电磁干扰又不可避免的引入了干扰噪声,就为我们分析其与预先设计波形的拟合程度的判别增加了困难。另外,实际波形和预先设计波形间往往存在着时序上的差别,相位的改变同样也不利于信号的拟合判别。本文利用高等数学以及信号与系统方面的有关知识提出对该问题的解决方法。
二、 信号相似程度判别的理论依据
在信号与系统这门学科中,相关性是一种在时域中对信号特性进行描述的重要方法。由于其通信的功率谱函数是一对傅立叶变换,在信号分析中往往利用它来分析随机信号的功率谱分布,以致不少人一提到相关性马上会联想到信号功率谱的计算,但相关在对确定信号的分析也是有一定应用。由于相关的概念是为研究随机信号的统计特性而引入的,那么从理论上我们也可以将其应用于两个确定信号(一个我们采集到的信号波形和一个理论波形)相似性的研究上。
要比较两波形的相似程度还要从相关的概念上入手,假定两信号分别为x(t)、y(t),可以选择当倍数a使a*y(t)去逼近x(t)。再此我们可以借用误差能量来度量这对波形的相似程度,具体方法同高等数学上用来判断函数间正交性的方法基本类似:
误差能量用x(t)-a*y(t)的平方在时域上的积分来表示;倍数a的选择必须要保证能使能量误差为最小,通过对函数求导求极值可以得知当a为x(t)*y(t)在时域的积分与y(t)*y(t)在时域的积分比值时可以满足条件,在此条件下的误差能量是可能所有条件下最小的。定义x(t)与y(t)的相关数为Pxy,其平方与1的差值为相对误差能量,即误差能量与x(t)*x(t)在时域积分的比值。其中,xy就可以用来表征两波形的相似程度。解出关于Pxy的方程,其分子为x(t)*y(t)在时域的积分;分为两信号各自的平方在时域积分之积的平方根。从数学上可以证明分子的模小于分母,也即相关数Pxy的模不会大于1。由于对于能量有限的信号而言,能量是确定的,相关系数Pxy的大小只由x(t)*y(t)的积分所决定。如果两完全不相似的波形其幅度取值和出现时刻是相互独立、彼此无关的,x(t)*y(t)=0,其积分结果亦为0,所以当相关系数为0时相似度最差,即不相关。当相关系数为1,则误差能量为0,说明这两信号相似度很好,是线形相关的。因此把相关系数作为两个信号
波形的相似性(或线形相关性)的一种度量完全是有理论依据的、合理的。
三、 算法的设计与实现
我们在对信号进行比较之前,先将理论波形做成一个数据文件,实际设备输出的波形也通过计算机接口采集并将数据存成数据文件。我们编写的程序通过对两个数据文件的相关性比较来得出实际波形同理论波形信号的拟合度。下面就对数据文件的读取、数据相关程度计算等关键代码作简要的绍:
首先,要在计算相关系数前把参加运算的两序列数据从文件读取到内存(堆栈)中去,为了方便读取多种数据格式的文件使程序更加灵活,选用MFC基本类库的CFileDialog类的成员函数来选取数文件,然后再通过CFile类的相关成员函数将其读取到内存中:
…… CFileDialog dlg(TRUE,"dat","*.dat", //TRUE为"打开文件"窗口 OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT, "信号数据文件(*.dat)|*.dat|所有文件(*.*)|*.*||",NULL); if(dlg.DoModal()==IDOK) { CString FileName=""; FileName=dlg.GetPathName(); //取文件所在的完整路径 CFile file; file.Open(FileName,CFile::modeReadWrite); //以读写方式打开文件 buf1=new char [file.GetLength()]; //为指针动态分配堆栈 file.Read(buf1,file.GetLength()); //将数据读取到内存 m_nData1Len=file.GetLength(); //获取文件长度 file.Close(); //关闭文件 } …… |
上述为读取一个信号文件的相关代码,其中buf1是一个char*类型的指针,该指针指向的内存存储有数据文件的数据,m_nData1Len 保存有第一个数据文件的长度。用同样的方法将第二个数据文件也读取到内存中,指向其首地址的指针为buf2,文件长度为m_nData2Len。参与运算的数据序列准备好后就可以进行这两组信号波形的相关系数的计算了,下面是有关的关键部分代码:
…… int N=m_nData1Len>m_nData2Len?m_nData2Len:m_nData1Len; |
由于两序列长度可能不一样,如以较长序列为准,将短序列不足部分补0,根据相关系数的概念,补0部分的x(t)*y(t)的积分为0,没有实际意义,故以较短序列为准可以避免不必要的运算,运算效较高。
…… float A,B,C,Pxy; A=B=C=Pxy=0.0f; …… |
在计算机中将积分近似按离散点取和的方式进行近似的积分:
for(int i=0;i { A+=buf1[i]*buf2[i];//对x(t)*y(t)的积分 B+=buf1[i]*buf1[i];//对x(t)*x(t)的积分 C+=buf2[i]*buf2[i];//对y(t)*y(t)的积分 } Pxy=A/(sqrt(B*C));//计算出相关系数 …… |
最后释放掉申请的内存:
delete[] buf1; delete[] buf2; |
四、 实验效果的检验
下面通过一个实际的例子来检验一下上述程序,我们想要获得的理想的波形如下图Data1所示,Data2所示波形是设备经过噪声抑制和相位纠偏等诸多措施后产生的实际波形,Data3所示波形是在没有任何保护措施下得到的粗糙的波形,显然Data2要比Data3能更好的同设计的理想波形Data1相拟合,但只是停留在定性分析上,究竟相似程度如何,定量的分析靠肉眼显然是无法完成的。先对Data1和Data2波形信号进行相关系数计算,得出其相关系数为0.793931,基本上是线形相关的,即实际的Data2信号设计的理想信号Data1的拟合程度还是可以接受的;然后再对Data1和Data3两波形信号进行相关系数计算,组信号的相关系数为 -0.013341,基本上线形不相关;再对Data2和Data3进行分析,计算结果是0.011665,结论也是基本不相关。通过上述程序对波形信号进行的定量分析同直观上的定性分析是相吻的。通过实际实验的检验证明该程序是可靠、实用的。
小结:本文提出的对波形信号相似程度的判断在电子工程上有着较为广泛的应用,能准确的判断出参加比较的两波形信号的相似程度,为设备的改进、元器件的选型等提供可参考的依据。另外,在判断移动的信号是否具有线形相关性的场合,如对雷达站接收到的两个不同距离的目标的反射信号的分析等都可以用本算法。通过对本文介绍的相关算法的改进还可以对信号的功率谱进行绘制、对波形信号进行更全面
的分析。本程序在Windows 98下,由Microsoft Visual C++ 6.0编译通过。