熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> .NET編程 >> 正文

基於VC.NET的GDI+編程之CImage

2022-06-13   來源: .NET編程 

  我們知道Visual C++的CBitmap類和靜態圖片控件的功能是比較弱的它只能顯示出在資源中的圖標位圖光標以及圖元文件的內容而不像VB中的Image控件可以顯示出絕大多數的外部圖像文件(BMPGIFJPEG等) 因此想要在對話框或其他窗口中顯示外部圖像文件則只能借助於第三方提供的控件或代碼現在MFC和ATL共享的新類CImage為圖像處理提供了許多相應的方法這使得Visual C++在圖像方面的缺憾一去不復返了

  CImage類概述

  CImage是MFC和ATL共享的新類它能從外部磁盤中調入一個JPEGGIFBMP和PNG格式的圖像文件加以顯示而且這些文件格式可以相互轉換由於CImage在不同的Windows操作系統中其某些性能是不一樣的因此在使用時要特別注意例如CImage::PlgBlt和CImage::MaskBlt只能在 Windows NT 或更高版本中使用但不能運行在Windows / 應用程序中CImage::AlphaBlend和CImage::TransparentBlt也只能在 Windows /或其更高版本中使用即使在Windows 運行程序還必須將stdafxh文件中的WINVER和_WIN_WINNT的預定義修改成x才能正常使用

  CImage封裝了DIB(設備無關位圖)的功能因而可以讓我們能夠處理每個位圖像素它具有下列最酷特性

  AlphaBlend支持像素級的顏色混合從而實現透明和半透明的效果

  PlgBlt能使一個矩形區域的位圖映射到一個平行四邊形區域中而且還可能使用位屏蔽操作

  TransparentBlt在目標區域中產生透明圖像SetTransparentColor用來設置某種顏色是透明色

  MaskBlt在目標區域中產生源位圖與屏蔽位圖合成的效果

  使用CImage的一般方法

  使用CImage的一般方法是這樣的過程

  () 打開應用程序的stdafxh文件添加CImage類的包含文件#include <atlimageh>

  () 定義一個CImage類對象然後調用CImage::Load方法裝載一個外部圖像文件

  () 調用CImage::Draw方法繪制圖像Draw方法具有如下定義

   BOOL Draw( HDC hDestDC int xDest int yDest
int nDestWidth int nDestHeight int xSrc int ySrc
int nSrcWidth int nSrcHeight );
BOOL Draw( HDC hDestDC const RECT& rectDest const RECT& rectSrc );
BOOL Draw( HDC hDestDC int xDest int yDest );
BOOL Draw( HDC hDestDC const POINT& pointDest );
BOOL Draw( HDC hDestDC int xDest int yDest
int nDestWidth int nDestHeight );
BOOL Draw( HDC hDestDC const RECT& rectDest );

  其中hDestDC用來指定繪制的目標設備環境句柄(xDest yDest)和pointDest用來指定圖像顯示的位置這個位置和源圖像的左上角點相對應nDestWidth和nDestHeight分別指定圖像要顯示的高度和寬度xSrcySrcnSrcWidth和nSrcHeight用來指定要顯示的源圖像的某個部分所在的位置和大小rectDest和rectSrc分別用來指定目標設備環境上和源圖像所要顯示的某個部分的位置和大小

  需要說明的是Draw方法綜合了StretchBltTransparentBlt和AlphaBlend函數的功能默認時Draw的功能和StretchBlt相同但當圖像含有透明色或Alpha通道時它的功能又和TransparentBltAlphaBlend相同因此在一般情況下我們都應該盡量調用CImage::Draw方法來繪制圖像

  例如下面的示例Ex_Image是實現這樣的功能當選擇文件ò打開菜單命令後彈出一個文件打開對話框當選定一個圖像文件後就會在窗口客戶區中顯示該圖像文件內容這個示例的具體步驟如下

  () 創建一個默認的單文檔程序項目Ex_Image

  () 打開stdafxh文件中添加CImage類的包含文件atlimageh

  () 在CEx_ImageView類添加ID_FILE_OPEN的COMMAND事件映射程序並添加下列代碼

   void CEx_ImageView::OnFileOpen()
{
 CString strFilter;
 CSimpleArray<GUID> aguidFileTypes;
 HRESULT hResult;

 // 獲取CImage支持的圖像文件的過濾字符串
 hResult = m_ImageGetExporterFilterString(strFilteraguidFileTypes
_T( All Image Files) );
 if (FAILED(hResult)) {
  MessageBox(GetExporterFilter調用失敗!);
  return;
 }
 CFileDialog dlg(TRUE NULL NULL OFN_FILEMUSTEXIST strFilter);
 if(IDOK != dlgDoModal())
  return;

 m_ImageDestroy();
 // 將外部圖像文件裝載到CImage對象中
 hResult = m_ImageLoad(dlgGetFileName());
 if (FAILED(hResult)) {
  MessageBox(調用圖像文件失敗!);
  return;
 }

 // 設置主窗口標題欄內容
 CString str;
 strLoadString(AFX_IDS_APP_TITLE);
 AfxGetMainWnd()>SetWindowText(str + +dlgGetFileName());

 Invalidate(); // 強制調用OnDraw
}

  () 定位到CEx_ImageView::OnDraw函數處添加下列代碼

   void CEx_ImageView::OnDraw(CDC* pDC)
{
 CEx_ImageDoc* pDoc = GetDocument();
 ASSERT_VALID(pDoc);
 if (!m_ImageIsNull()) {
  m_ImageDraw(pDC>m_hDC);
 }
}

  () 打開Ex_ImageViewh文件添加一個公共的成員數據m_Image

   public:
CImage m_Image;

  () 編譯並運行單擊打開工具按鈕在彈出的對話框中指定一個圖像文件後單擊打開按鈕其結果如圖所示

  基於VC.NET的GDI+編程之CImage

  將圖片用其它格式保存

  CImage::Save方法能將一個圖像文件按另一種格式來保存它的原型如下

   HRESULT Save( LPCTSTR pszFileName REFGUID guidFileType= GUID_NULL);

  其中pszFileName用來指定一個文件名guidFileType用來指定要保存的圖像文件格式當為GUID_NULL時其文件格式由文件的擴展名來決定這也是該函數的默認值它還可以是GUID_BMPFile(BMP文件格式)GUID_PNGFile(PNG文件格式)GUID_JPEGFile(JPEG文件格式)和GUID_GIFFile(GIF文件格式)

  例如下面的過程是在Ex_Image示例基礎上進行的我們在CEx_ImageView類添加ID_FILE_SAVE_AS的COMMAND事件映射程序並添加下列代碼

   void CEx_ImageView::OnFileSaveAs()
{
 if (m_ImageIsNull()) {
  MessageBox(你還沒有打開一個要保存的圖像文件!);
  return;
 }

 CString strFilter;
 strFilter = 位圖文件|*bmp|JPEG 圖像文件|*jpg| \
GIF 圖像文件|*gif|PNG 圖像文件|*png||;
 CFileDialog dlg(FALSENULLNULLNULLstrFilter);
 if ( IDOK != dlgDoModal())
  return;

 // 如果用戶沒有指定文件擴展名則為其添加一個
 CString strFileName;
 CString strExtension;

 strFileName = dlgm_ofnlpstrFile;
 if (dlgm_ofnnFileExtension == )
 {
  switch (dlgm_ofnnFilterIndex)
  {
   case :
    strExtension = bmp; break;
   case :
    strExtension = jpg; break;
   case :
    strExtension = gif; break;
   case :
    strExtension = png; break;
   default:
    break;
  }
  strFileName = strFileName + + strExtension;
 }

 // 圖像保存
 HRESULT hResult = m_ImageSave(strFileName);
 if (FAILED(hResult))
  MessageBox(保存圖像文件失敗!);
}

  柔化和銳化處理

  在圖像處理中我們通常用一些數學手段對圖像進行除去噪聲強調或抽取輪廓特征等圖像空間的變換所謂圖像空間的變換是借助於一個稱之為模板的局部像素域來完成的不同的模板具有不同的圖像效果

  . 柔化

  圖像的柔化是除去圖像中點狀噪聲的一個有效方法所謂柔化是指使圖像上任何一個像素與其相鄰像素的顏色值的大小不會出現陡突的一種處理方法設在一個 x 的模板中其系數為

基於VC.NET的GDI+編程之CImage

  中間有底紋的表示中心元素即用那個元素作為處理後的元素很明顯上述模板(稱之為Box模板)是將圖像上每個像素用它近旁(包括它本身)的個像素的平均值取代這樣處理的結果在除噪的同時也降低圖像的對比度使圖像的輪廓模糊為了避免這一缺陷我們對各點引入加權系數將原來的模板改為

基於VC.NET的GDI+編程之CImage

  新的模板可一方面除去點狀噪聲同時能較好地保留原圖像的對比度因此該模板得到了廣泛的應用由於這個模板是通過二維高斯(Gauss)函數得到的故稱為高斯模板

  . 銳化

  銳化和柔化恰恰相反它通過增強高頻分量減少圖像中的模糊因此又稱為高通濾波銳化處理在增強圖像邊緣效果的同時增加了圖像的噪聲常用的銳化模板是拉普拉斯模板

基於VC.NET的GDI+編程之CImage

  用此模板處理後的圖像輪廓線條將明顯得到增強輪廓線以外的部分將變得較暗而輪廓線部分將變得比較明亮

  使用程序對模板進行運算時要考慮到溢出點的處理所謂溢出點指的是大於或小於的點處理時可令大於的點取而小於的點取其正值

  . 實現代碼

  實現柔化和銳化時我們先調用CImage::GetPixel來依次讀取相應的像素然後用柔化和銳化模板進行處理最後調用CImage::SetPixel函數將處理後的像素寫回到CImage對象中具體的代碼如下

void FilterImage(CImage* image int nType)
{
 if (image>IsNull())
  return;
 int smoothGauss[] = {}; // 高斯模板
 int sharpLaplacian[] = {}; // 拉普拉斯模板

 int opTemp[];
 float aver; // 系數
 if ( nType > ) nType = ;
 switch( nType ){
  case : // 高斯模板
   aver = (float)(/);
   memcpy( opTemp smoothGauss *sizeof(int));
   break;
  case : // 拉普拉斯模板
   aver = ;
   memcpy( opTemp sharpLaplacian *sizeof(int));
   break;
 }

 int ij;

 int nWidth = image>GetWidth();
 int nHeight = image>GetHeight();

 for (i = ; i < nWidth; i++){
  for (j = ; j < nHeight; j++){

   int rr = gg = bb = ;
   int index = ;

   for (int col = ; col <= ; col++){
    for (int row = ; row <= ; row++){
     COLORREF clr = image>GetPixel( i+row j+col);
     rr += GetRValue(clr) * opTemp[index];
     gg += GetGValue(clr) * opTemp[index];
     bb += GetBValue(clr) * opTemp[index];
     index++;
    }
   }
  
   rr = (int)(rr*aver);
   gg = (int)(gg*aver);
   bb = (int)(bb*aver);

   // 處理溢出點
  if ( rr > ) rr = ;
  else if ( rr < ) rr = rr;
  if ( gg > ) gg = ;
  else if ( gg < ) gg = gg;
  if ( bb > ) bb = ;
  else if ( bb < ) bb = bb;

  // 錯位重寫以避免前一個像素被新的像素覆蓋
  image>SetPixel( i j RGB(rrggbb));
  }
 }
}
是使用上述代碼將某個圖像處理後的結果

基於VC.NET的GDI+編程之CImage 

  變成黑白圖片

  由於許多圖像文件使用顏色表來發揮顯示設備的色彩顯示能力因而將一張彩色圖片變成黑色圖片時需要調用CImage::IsIndexed來判斷是否使用顏色表若是則修改顏色表否則直接將像素進行顏色設置例如下面的代碼

   void CEx_ImageView::MakeBlackAndwhite(CImage* image)
{
 if (image>IsNull()) return;

 if (!image>IsIndexed()) {
  // 直接修改像素顏色
  COLORREF pixel;
  int maxY = image>GetHeight() maxX = image>GetWidth();
  byte rgbavg;
  for (int x=; x<maxX; x++) {
   for (int y=; y<maxY; y++) {
    pixel = image>GetPixel(xy);
    r = GetRValue(pixel);
    g = GetGValue(pixel);
    b = GetBValue(pixel);
    avg = (int)((r + g + b)/);
    image>SetPixelRGB(xyavgavgavg);
   }
  }
 } else {
  // 獲取並修改顏色表
  int MaxColors = image>GetMaxColorTableEntries();
  RGBQUAD* ColorTable;
  ColorTable = new RGBQUAD[MaxColors];
  image>GetColorTable(MaxColorsColorTable);
  for (int i=; i<MaxColors; i++)
  {
   int avg = (ColorTable[i]rgbBlue + ColorTable[i]rgbGreen + ColorTable[i]rgbRed)/;
   ColorTable[i]rgbBlue = avg;
   ColorTable[i]rgbGreen = avg;
   ColorTable[i]rgbRed = avg;
  }
  image>SetColorTable(MaxColorsColorTable);
  delete(ColorTable);
 }
}

  至此我們介紹了GDI+和CImage的一般使用方法和技巧當然它們本身還有許多更深入的方法由於篇幅所限這裡不再一一討論


From:http://tw.wingwit.com/Article/program/net/201311/13155.html
    推薦文章
    Copyright © 2005-2022 電腦知識網 Computer Knowledge   All rights reserved.