浅谈用VC6.0单文档进行数字图像处理(1)

来源:互联网 发布:黑龙江网页关键词优化 编辑:程序博客网 时间:2024/06/05 15:40

以前写一些VC6.0的数字图像处理程序,大多是用对话框写的。主要是因为对话框就那么两个类:App类和Dlg类,所以理解也比较简单。但是,最近,听到有人这么讲文档视图类才是MFC的核心。所以,也想尝试一下。这两天做了点简单的尝试,特此总结一下。

1、写一个DIB类,因为在单文档或多文档下,如果不写一个DIB类,那么你做处理就比较麻烦了。因为我们经常要将这个DIB类的对象来共享,比如一般的DIB类的对象都是放到Doc类中。那么我们经常要在View类和MainFrame类中引用到Doc类的Dib类的实例。如果是多个Doc类和View类,这种数据的共享就显得更加重要了。

2、在DIB类中要有一个获得图像像素数据的指针的函数和一个能够设置DIB类的像素数据的指针。因为,在MainFrame类中我们需要获得Doc类的一个DIB类的对象之后,可以获得指向该对象的像素数据的指针,因为我们要对其中的像素数据进行操作。另外,我们要将操作之后的像素数据拷贝进DIB类的对象的像素数据中。

3、DIB类中还需要有获得操作像素数据中要用到的函数:获得图像高度、获得图像宽度、获得图像位数、获得图像每行像素所占的字节数。

4、DIB类中当然还需要有读入和写出图像的函数。

5、由于菜单的响应函数都是在MainFrame类中,所以,我们需要在MainFrame类中获得Doc类和View类的指针。获得Doc类的指针主要是利用其中的DIB类的对象;获得View类的指针主要是更新显示处理后的图像效果。所以在MainFrame类的cpp文件的包含文件中要包含Doc类和View类的h文件。获得方法是调用MainFrame类的GetActiveDoc()和GetActiveView()函数。

好了,下面说说主要的程序:

 DIB类的头文件:

// Dib.h: interface for the CDib class.
//
//////////////////////////////////////////////////////////////////////

#if !defined(AFX_DIB_H__1065C5DA_1C47_464F_A225_AAF8D2F15064__INCLUDED_)
#define AFX_DIB_H__1065C5DA_1C47_464F_A225_AAF8D2F15064__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

class CDib 
{
public:
 CDib();//构造函数
 virtual ~CDib();//析构函数

private:
 BITMAPFILEHEADER *m_pBmfh;//保存位图文件头
 BYTE *m_pBmInfo;//保存位图信息头+调色板(用于显示位图)
 BYTE *m_pPixel;//保存位图像素数据
 BITMAPINFOHEADER *m_pBmih;//保存位图信息头
public:
 BOOL m_bRead;//标志是否调用了Read函数

public:
 int Read(CString filename);//读入位图
 void Draw(CDC *pDC);//显示位图
 int Write(CString filename);//写出位图

public:
 DWORD GetWidth() const;//获得位图宽度
 DWORD GetHeight() const;//获得位图高度
 WORD GetBitCount() const;//获得位图位数
 DWORD GetLineBytes() const;//获得位图每行像素所占字节数
 BYTE* GetPixelPointer() const;//获得指向位图像素数据的指针
 void SetPixelMatrix(BYTE *newPixel);//设置位图的像素矩阵
};

#endif // !defined(AFX_DIB_H__1065C5DA_1C47_464F_A225_AAF8D2F15064__INCLUDED_)

DIB类的cpp文件:

// Dib.cpp: implementation of the CDib class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "VampireImage.h"
#include "Dib.h"//包含DIB类的头文件

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CDib::CDib()
{
 m_pBmfh=new BITMAPFILEHEADER;
 m_pBmih=new BITMAPINFOHEADER;
 m_pBmInfo=NULL;
 m_pPixel=NULL;
 m_bRead=FALSE;
}

CDib::~CDib()
{
 if(m_pBmfh)
 {
  delete m_pBmfh;
  m_pBmfh=NULL;
 }

 if(m_pBmih)
 {
  delete m_pBmih;
  m_pBmih=NULL;
 }

 if(m_pBmInfo)
 {
  delete[] m_pBmInfo;
  m_pBmInfo=NULL;
 }

 if(m_pPixel)
 {
  delete[] m_pPixel;
  m_pPixel=NULL;
 }
}


//////////////////////////////////////////////////////////////////////
// Methods
//////////////////////////////////////////////////////////////////////

int CDib::Read(CString filename)
{
 CFile dib;

 if(!dib.Open(filename,CFile::modeRead))
 {
  return -1;
 }

 if(dib.Read(m_pBmfh,sizeof(BITMAPFILEHEADER))!=sizeof(BITMAPFILEHEADER))
 {//读取位图文件头
  dib.Close();
  return -1;
 }

 m_pBmInfo=new BYTE[m_pBmfh->bfOffBits-14];//为信息头+调色板分配空间
 if(!m_pBmInfo)
 {
  dib.Close();
  return -1;
 }

 if(dib.Read(m_pBmInfo,m_pBmfh->bfOffBits-14)!=(m_pBmfh->bfOffBits-14))
 {//读取位图信息头+调色板(如果有)
  delete[] m_pBmInfo;
  m_pBmInfo=NULL;
  dib.Close();
  return -1;
 }

 memcpy(m_pBmih,m_pBmInfo,sizeof(BITMAPINFOHEADER));//拷贝40字节到位图信息头中
 WORD bitCount=m_pBmih->biBitCount;
 DWORD width=m_pBmih->biWidth;
 DWORD height=m_pBmih->biHeight;
 DWORD lineBytes=(width*bitCount+31)/32*4;

 m_pPixel=new BYTE[height*lineBytes*sizeof(BYTE)];//为位图像素矩阵分配空间
 if(!m_pPixel)
 {
  dib.Close();
  return -1;
 }

 if(dib.Read(m_pPixel,height*lineBytes*sizeof(BYTE))!=(height*lineBytes*sizeof(BYTE)))
 {//读取位图像素矩阵
  dib.Close();
  delete[] m_pBmInfo;
  m_pBmInfo=NULL;
  delete[] m_pPixel;
  m_pPixel=NULL;
  return -1;
 }

 dib.Close();

 m_bRead=TRUE;//标志已经使用过Read函数

 return 0;
}

int CDib::Write(CString filename)
{
 CFile dib;
 
 if(!dib.Open(filename,CFile::modeWrite | CFile::modeCreate | CFile::typeBinary))
 {
  return -1;
 }

 dib.Write(m_pBmfh,sizeof(BITMAPFILEHEADER));//写入位图文件头

 dib.Write(m_pBmInfo,m_pBmfh->bfOffBits-14);//写入位图信息头+调色板(如果有)

 dib.Write(m_pPixel,GetHeight()*GetLineBytes()*sizeof(BYTE));//写入位图像素矩阵

 dib.Close();

 return 0;
}

void CDib::Draw(CDC *pDC)
{
 DWORD width=GetWidth();
 DWORD height=GetHeight();

 StretchDIBits(pDC->m_hDC,0,0,width,height,0,0,width,height,m_pPixel,(BITMAPINFO*)m_pBmInfo,DIB_RGB_COLORS,SRCCOPY);//用StretchDIBits显示位图
}

//////////////////////////////////////////////////////////////////////
// Get/Set Functions
//////////////////////////////////////////////////////////////////////

WORD CDib::GetBitCount() const
{
 return m_pBmih->biBitCount;
}

DWORD CDib::GetWidth() const
{
 return m_pBmih->biWidth;
}

DWORD CDib::GetHeight() const
{
 return m_pBmih->biHeight;
}

DWORD CDib::GetLineBytes() const
{
 return (GetWidth()*GetBitCount()+31)/32*4;
}

BYTE* CDib::GetPixelPointer() const
{
 return m_pPixel;
}

void CDib::SetPixelMatrix(BYTE *newPixel)
{
 delete[] m_pPixel;//删除原来的像素矩阵
 m_pPixel=NULL;

 m_pPixel=new BYTE[GetHeight()*GetLineBytes()];//为新的像素矩阵分配空间
 if(!m_pPixel)
 {
  return;
 }

 memcpy(m_pPixel,newPixel,GetHeight()*GetLineBytes());//将心的像素矩阵拷贝到DIB类中
}

View类中的OnDraw函数:

void CVampireImageView::OnDraw(CDC* pDC)
{
 CVampireImageDoc* pDoc = GetDocument();
 ASSERT_VALID(pDoc);
 // TODO: add draw code for native data here
 if(pDoc->GetDibInstance()->m_bRead==TRUE)
 {//如果已经读入图片,则调用DIB类的对象的Draw函数来显示位图
  pDoc->GetDibInstance()->Draw(pDC);
 }
}

Doc类中的获得DIB类的对象的指针的函数:

CDib* CVampireImageDoc::GetDibInstance()
{
 return m_pDib;
}

Doc类中的打开和保存菜单的函数:

BOOL CVampireImageDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
 if (!CDocument::OnOpenDocument(lpszPathName))
  return FALSE;
 
 // TODO: Add your specialized creation code here
 m_pDib->Read(lpszPathName);
 
 return TRUE;
}

CDib* CVampireImageDoc::GetDibInstance()
{
 return m_pDib;
}

BOOL CVampireImageDoc::OnSaveDocument(LPCTSTR lpszPathName)
{
 // TODO: Add your specialized code here and/or call the base class
 CFileDialog sfd(false,"*.bmp",NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,"BMP Files(*.bmp)|*.bmp||");
 if(sfd.DoModal()!=IDOK)
 {
  return FALSE;
 }

 if((m_pDib->Write(sfd.GetPathName()))==-1)
 {
  AfxMessageBox("保存失败");
  return FALSE;
 }

 return TRUE;
 
;
}

MainFrame类中灰度图菜单的响应函数:

void CMainFrame::OnGrayimage()
{
 // TODO: Add your command handler code here
 CVampireImageDoc *pDoc=(CVampireImageDoc*)this->GetActiveDocument();//获得当前的Doc类的指针
 DWORD width=(pDoc->GetDibInstance())->GetWidth();//获得DIB对象的宽度
 DWORD height=(pDoc->GetDibInstance())->GetHeight();//获得DIB对象的高度
 WORD bitCount=(pDoc->GetDibInstance())->GetBitCount();//获得DIB对象的位数
 DWORD lineBytes=(pDoc->GetDibInstance())->GetLineBytes();//获得DIB对象的每行所占字节数
 BYTE *dib=(pDoc->GetDibInstance())->GetPixelPointer();//得到DIB对象的像素数据的指针

 BYTE *newDib=new BYTE[height*lineBytes];//新建一块内存
 if(!newDib)
 {
  return;
 }

 memcpy(newDib,dib,height*lineBytes);//将DIB对象的像素数据拷贝至新建的内存

 if(bitCount==8)//如果位图是8位
 {
  AfxMessageBox("已经是8位的灰度图了,没有必要再转换。");
  delete[] newDib;
  newDib=NULL;
  return;
 }
 else if(bitCount==24)//如果位图是24位
 {
  for(DWORD i=0;i<height;i++)
  {
   for(DWORD j=0,n=0;j<width*3;j++,n++)
   {
    BYTE b=*(newDib+lineBytes*(height-1-i)+j);
    j++;
    BYTE g=*(newDib+lineBytes*(height-1-i)+j);
    j++;
    BYTE r=*(newDib+lineBytes*(height-1-i)+j);

    double avg=(b+g+r)/3.0;
    if(avg<=0) avg=0;
    if(avg>=255) avg=255;
    *(newDib+lineBytes*(height-1-i)+n)=(BYTE)avg;
    n++;
    *(newDib+lineBytes*(height-1-i)+n)=(BYTE)avg;
    n++;
    *(newDib+lineBytes*(height-1-i)+n)=(BYTE)avg;
   }
  }
 }
 else
 {
  AfxMessageBox("暂时只支持8或24位位图.");
  delete[] newDib;
  newDib=NULL;
  return;
 }

 pDoc->GetDibInstance()->SetPixelMatrix(newDib);//设置计算后的像素矩阵代替原来DIB对象的像素矩阵

 delete[] newDib;//删除分配的内存空间
 newDib=NULL;

 CVampireImageView *pView=(CVampireImageView*)this->GetActiveView();//获得当前View类的指针
 pView->OnDraw(pView->GetDC());//使当前View类重画
}