DSP

图像处理

2019-07-13 20:00发布

利用VC实现图像的特殊显示效果
经常看电视的朋友们不知注意到没有,最近的电视连续剧在每集片头或片尾部分都有显示一些特殊效果的图像,比如前一阵子中央一套放的《长征》、目前中央八套正在播放的《康熙王朝》,这些特效称为"图像的浮雕效果"和"图像的雕刻效果",经过这些特效处理后的图像增强了观众们的视觉效果,它们看上去仿佛是使用3D技术作的,这也是为什么这种技术那么流行的原因吧。其实,我们完全可以用一些简单的数字图像处理算法来实现这些看似复杂高深的显示效果。我们手头上的一些关于利用VC开发数字图像资料大都是讲解如何控制图像的每一行或每一列像素的显示时间或顺序来实现一些简单的图像显示效果,涉及到图像算法的文章很少,本文针对上述的这两种图像特效处理技术并加以引伸,讲解了一些实现图像特效算法,以一个标准的Lena灰度图像为原图,给出了处理后的效果图,同时给出了VC开发平台上的部分实现源代码。

  1."浮雕"图像

  "浮雕"图象效果是指图像的前景前向凸出背景。所谓的"浮雕"概念是指标绘图像上的一个像素和它左上方的那个像素之间差值的一种处理过程,为了使图像保持一定的亮度并呈现灰 {MOD},我在处理过程中为这个差值加了一个数值为128的常量。需要读者注意的是,当设置一个像素值的时候,它和它左上方的像素都要被用到,为了避免用到已经设置过的像素,应该从图像的右下方的像素开始处理,下面是实现的源代码:

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);//显示图像
  }
2."雕刻"图像

  上面讲述了通过求一个像素和它左上方像素之间的差值并加上一个常数的方法生成"浮雕"效果的灰度图像,"雕刻"图像与之相反,它是通过取一个像素和它右下方的像素之间的差值并加上一个常数,这里我也取128,经过这样处理,就可以得到"雕刻"图像,这时候图像的前景凹陷进背景之中。同样需要读者注意的是为了避免重复使用处理过的图像像素,处理图像时要从图像的左上方的像素开始处理。实现代码如下:

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原图 
"雕刻"效果图
 "浮雕"效果图
用VC实现小型矢量图形系统的开发
大家学习了VC的MFC的一些基础知识后,如果能用VC开发一个比较实用的软件,对熟悉VC各方面编程和面向对象的软件设计和开发都是很有帮助的。 

  本文旨在通过对一个作者自己开发的小型矢量图形系统全面讲述而达到让读者了解一个小软件从设计到实现的阶段的解决的问题。同时也从界面和功能上对MFC和Windows系统功能的挖掘,同样,对于学习计算机图形学的读者,也可以看到本文有很多对图形学算法和实现的有益探讨。

  一. 功能和界面设计

  首先,让大家对一个本软件功能的大概了解。当你着手开发一个软件时,首先要解决的当然是本软件的功能(软件工程常称作用例,具体概念可以参考有关资料,不妨简单理解为用户使用它能完成哪些工作)。由于写这篇文章时,本软件已经具有比较完整的原型。我们可以结合它的界面(图1)来介绍软件设计的过程。


            图 1 软件界面
  可以看到,本软件是实现了一个绘图功能的子集。最初就确定了开发环境为VC6.0,界面采用IE风格。在使用上为了给用户最大的便利,采用了三种工具条(普通文件、打印操作等标准工具,对图形对象属性设置的工具条式对话框,带文字说明的大按钮式可浮动或任意船坞- Dock定位的绘图工具条)。

  操作上采用左键点击建立图形对象起始点,移动动态调整图形大小和位置(随手画采用按住左键拖动的方式,再次点击左键确定位置,右键取消操作,双击确定(结束)多步图形对象(如多边形)的绘制。在功能设计方面基本符合一般图形软件的惯例,但出于作者的便利和保护鼠标的考虑,整个功能体现了基本无需按住左键拖动的思想。这也是很容易让人接受的,因为即便习惯拖动的用户拖动时也会产生位置调整,只是释放后还是出于拖动状态,再次点击或双击才最终确定。

  功能上选择了画线、框、圆、多边形、立体、文字、曲线、填充以及删除的功能,根据是否填充和光照又增加了几个类别,填充方式根据图形学的概念提供了两种方式(以后介绍)。根据对图形属性取了线宽、线型(很容易实现简单的线型,由于想加入更多的特性,作者先没有具体实现它,以后作者会提到它的实现,读者有兴趣可以试着实现)、边框 {MOD}、填充 {MOD}和字体几个属性。当然,这些功能在面向对象的方法中都是可以很方便扩展的(如画椭圆,选取对象,对象的位移和旋转操作,根据填充算法实现同 {MOD}选取,即Photoshop等软件的魔棒功能等),对于橡皮擦功能可以很简单的实现特定工具或告诉用户如何实现此功能(即用背景 {MOD}利用已有功能绘图)。

  内部实现上,要求单独记录各图形的关键属性(如位置、 {MOD}彩等,这些是矢量图区别于位图的特点)。由于各对象可以形成对象链表,因此,也要求实现多步撤消(Undo)和重做(Redo)的功能,这往往是用户所十分期待的功能(Window自带的画笔附件程序在这点上就很欠缺)。

二. 对象设计

  面向对象的程序设计方法都支持三种基本的活动:识别对象和类,描述对象和类之间的关系,以及通过描述每个类的功能定义对象的行为。

  首先介绍一下对象(Object)和类(Class)的区别,类是同类对象数据凸δ艿拿枋龊褪迪郑–++中用Class关键字定义的是类),对象是类的在内存中的具体形态(用类名声明或用new操作生成的是对象变量),一般称对象为类的实例(Instance)。

  对于图形对象的对象设计由于它们的较强的相关性,往往在很多面向对象编程书都提到过,故相信读者识别对象和类不会很困难。但是,要充分利用继承和多态的特性来描述对象和类之间的关系,以及通过描述每个类的功能定义还是要具体问题具体分析的。

  下面还是以一副图来说明。图2是采用北航软件所的软件分析与测试工具——SafePro生成的本软件的类图局部。

  由图2中可以清晰看到,我们的绘图子系统实现部分主要利用了几个从MFC可序列化的基类CObject继承的四个类:MFC已有类CArray,CObList,CDC以及我们自己需要实现的类CGraph。CDC对象封装了我们可以利用Windows系统绘图功能的设备无关的几乎全部绘图功能。CArray类和CObList 类用于实现基于CObject类的对象的数组和链表存储的辅助类。CGraph是抽象类,所有图形对象都由它继承而来。值得注意的是,由于多边形和框都是直线的组合,本软件采用了从CLine继承的方法,可以充分利用它的功能。

  现在并不想把所有类的功能定义(以后会逐步介绍大部分)。下面介绍一些关系全局的类的设计。


          图 2本软件的图形对象类的设计 1. 基于文档-视图结构的类

  在图1可以看到,本软件是基于多文档界面(MDI)的。由AppWizard选取多文档界面后,它会帮助我们生成基本的基于文档-视图结构的类。本软件使用DrawGraph为应用程序名,故有以下类:CMainFrame,CChildFrame,CDrawGraphApp,CDrawGraphDoc ,CDrawGraphView。


  其中:CDrawGraphApp(以后我用是应用程序类,支持应用程序的建立和基本交互,我们可以不必改它。CChildFrame类是视图文档的容器,除了在显示图标上的定制外,我们也可以不修改它。

  CMainFrame,CDrawGraphDoc ,CDrawGraphView用于分别实现主窗口、文档、视图的功能。

  1). 主窗口(CMainFrame)主要需要定制图标、工具条的建立、显示和交互。下面是类的定义,阴影部分是自己定制的(非AppWizard自动生成)

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;//填充 {MOD}

COLORREF m_pencolor;//边框 {MOD}

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()

};
 2). 文档(CDrawGraphDoc)用于实现矢量图形对象的建立、存储和读取(即序列化)

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()

  };
3). 视图(CDrawGraphView)接收用户的对特定图形对象的操作并绘制图形对象

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()

};
  视图类在界面上改得少,主要是处理鼠标事件和调用各图形对象的绘制方法,实现上也尽量统一,充分利用图形对象的多态性。

4). 各图形对象的基类CGraph的考虑是关键,所以是需要关注的

  它定义了绘图类别和绘制状态两个枚举类型。当你把它定义好后,可以在stdafx.h加上#include “graph.h”来使得所有文件都能自由引用它,并且获得预编译。

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;//所有图形对象都有颜 {MOD}

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;};//设置图形对象颜 {MOD}

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();

};
  虽然是个小软件,“开发过程”还是可以和“软件工程”的步骤基本对应的。

  本软件的“需求分析”是人们需要一个比Windows画笔功能强大,但十分小巧易用的小而精的基于矢量的(易于编辑)的图形工具。而且,另一方面,这个小软件的开发是一个典型的基于VC的面向对象软件开发的尝试,很有教学意义。

  至此,基本完成了“概要设计”。以后将把“详细设计”和“编码”结合起来讲。至于“测试”和“维护”(改错、升级)有兴趣的读者可以自己完成。

VC编程实现数字图像的边缘检测
数字图像的边缘检测是图像分割、目标区域的识别、区域形状提取等图像分析领域十分重要的基础,图像理解和分析的第一步往往就是边缘检测,目前它以成为机器视觉研究领域最活跃的课题之一,在工程应用中占有十分重要的地位。本文向读者简单介绍一下这个技术,并给出了在Visual C++环境下实现的代码。

  所谓边缘就是指图像局部亮度变化最显著的部分,它是检测图像局部变化显著变化的最基本的运算。对于数字图像,图像灰度灰度值的显著变化可以用梯度来表示,以边缘检测Sobel算子为例来讲述数字图像处理中边缘检测的实现: 

  对于数字图像,可以用一阶差分代替一阶微分;

  △xf(x,y)=f(x,y)-f(x-1,y);

  △yf(x,y)=f(x,y)-f(x,y-1)

  求梯度时对于平方和运算及开方运算,可以用两个分量的绝对值之和表示,即:

  G[f(x,y)]={[△xf(x,y)] +[△yf(x,y)] } |△xf(x,y)|+|△yf(x,y)|;

  Sobel梯度算子是先做成加权平均,再微分,然后求梯度,即:

  △xf(x,y)= f(x-1,y+1) + 2f(x,y+1) + f(x+1,y+1)- f(x-1,y-1) - 2f(x,y-1) - f(x+1,y-1);

  △yf(x,y)= f(x-1,y-1) + 2f(x-1,y) + f(x-1,y+1)- f(x+1,y-1) - 2f(x+1,y) - f(x+1,y+1);

  G[f(x,y)]=|△xf(x,y)|+|△yf(x,y)|;

  上述各式中的像素之间的关系见图

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)
我在视图类中定义了响应菜单命令的边缘检测Sobel算子实现灰度图像边缘检测的函数:

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);
 }
  上述的数学分析读者可能看起来有些吃力,不过不要紧,对与边缘检测,大家只要知道有若干个检测模板(既边缘检测矩阵)可以直接实现检测功能就行了,现在将常用的检测实现公式列出如下:

  Roberts算子:G[i,i]=|f[i,j]-f[i+1,j+1]|+|f[i+1,j]-f[i,j+1]|;

  Sobe算子:G[i,i]=|f[i-1,j+1]+2f[i,j+1]+f[i+1,j+1]-f[i-1,j-1]-2f[i,j-1]-f[i+1,j-1]|
           +|f[i-1,j-1]+2f[i-1,j]+f[i-1,j+1]-f[i+1,j-1]-2f[i+1,j]-f[i+1,j+1]|;

  拉普拉斯算子:G[I,j]=|f[i+1,j]+f[i-1,j]+f(i,j+1)+f[i,j-1]-4f[i,j]|;

  其中G[i,j]表示处理后(i,j)点的灰度值,f[i,j]表示处理前该点的灰度值。

  笔者开发的该图像处理程序在Windows2000环境下编译通过,下面图2给出了依据图像处理算法得到的图像二值化、高通滤波、Sobel边缘算子的处理结果,读者需要注意的是我在进行Sobel算子进行处理后,又对它进行了二值化处理,这才得到C图。关于如何实现二值化图像,我会后续撰文对相关知识进行介绍。



谈对话框的动画弹出和动画消隐

在Windows应用程序中,对话框是应用最广泛也是比较难控制其风格(外表)的一类窗口。相信用过Windows 的朋友在享受其强大功能的同时,一定也为它所提供的具有立体感的界面而感叹吧。通常情况下,对话框的弹出和消隐都是瞬时的,下面将介绍如何实现对话框的动画弹出和消隐,增强程序的美观性。

  请按以下步骤实现:

  第一步:生成我们的工程(基于对话框)FlashDlg,所有的选项都取默认值,在对话框上随意添加几个控件。

  第二步:在对话框的类头文件中定义如下变量,如下:

CPoint point;
int nWidth,nHeight;
int dx,dy;
int dx1,dy1;
  第三步:在OnInitDialog()中添加如下代码:

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; 
}
  第四步:添加OnTimer函数,添加如下代码:

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);
}
  好了,对话框的动画出现和消隐实现了,运行程序我们会发现对话框平滑的划出,关闭程序我们会发现对话框平滑的消失。

VC编程实现灰度图像与彩 {MOD}图像的相互转换

PhotoShop的图像处理功能很强,其中有一个功能是将灰度图像转换为彩 {MOD}图像,数字图像处理中,也经常要遇到灰度图像与彩 {MOD}图像相互转换的问题,如何自己解决这个问题,值得大家探讨,现将我解决这类问题的方法陈述如下:

  工程应用中经常要遇到需要把彩 {MOD}图像到灰度图像的变换的问题,采集卡过来的图像为彩 {MOD}图像,为加快处理速度,要把彩 {MOD}图像转换为黑白图象,这个问题比较好解决,一般情况下彩 {MOD}图像每个像素用三个字节表示,每个字节对应着R、G、B分量的亮度(红、绿、蓝),转换后的黑白图像的一个像素用一个字节表示该点的灰度值,它的值在0~255之间,数值越大,该点越白,既越亮,越小则越黑。转换公式为Gray(i,j)=0.11*R(i,j)+0.59*G(i,j)+0.3*B(i,j),其中Gray(i,j)为转换后的黑白图像在(i,j)点处的灰度值,我们可以观察该式,其中绿 {MOD}所占的比重最大,所以转换时可以直接使用G值作为转换后的灰度。

  至于灰度图像转换为彩 {MOD}图像,技术上称为灰度图像的伪彩 {MOD}处理,这是一种视觉效果明显而技术又不是很复杂的图像增强技术。灰度图像中,如果相邻像素点的灰度相差不大,但包含了丰富的信息的话,人眼则无法从图像中提取相应的信息,因为人眼分辨灰度的能力很差,一般只有几十个数量级,但是人眼对彩 {MOD}信号的分辨率却很强,这样将黑白图像转换为彩 {MOD}图像人眼可以提取更多的信息量。在转换过程中,经常采用的技术是灰度级-彩 {MOD}变换,意思就是对黑白图像上的每一个像素点,取得该点的灰度值并送入三个通道经过实施不同的变换,产生相应的R、G、B的亮度值,即所求彩 {MOD}图像对应像素点的彩 {MOD}值,具体变换公式很多,我采用的是最常用的一种,变换曲线图如下:

 
  上图中,三个图分别代表了三个变换通道,R、G、B指的是变换后对应点的R、G、B分量值,L指的是各个分量的最大值为255,G(x,y)为相应点的灰度值。理论上就这些,下面是我用VC实现的源代码,图一为我的灰度位图,图二为伪彩 {MOD}处理后的结果图。我这个实现函数中是如何得到灰度位图的数据的就不多讲了,有兴趣的朋友可参考我在天极网上九月十号发表的《VC灰度位图处理》一文,那里应该讲的很清楚了。需要读者注意的是彩 {MOD}图像中每个象素中的三个字节分别代表的分量,第一个字节为B,第二个为G值、最后一个为R值,这个顺序不要搞错了。代码实现如下:

void CDibView::OnMenuchange() file://图像转换实现函数
{
 // TO

热门文章