GDI+数字图像处理初探

来源:互联网 发布:战锤2全面战争 优化 编辑:程序博客网 时间:2024/05/19 00:39

GDI+开发环境的Visual C++6.0的配置

这个我就不多说了,详细内容请看 http://blog.csdn.net/poonjun/archive/2009/01/04/3701724.aspx

特殊说明

这里要对一些问题做点特殊说明:首先就是我建立的是一个基于对话框的MFC应用程序,还有就是要用GDI+在对话框或单文档上面画图就只能在对话框的OnPaint函数或单文档的OnDraw函数中加入GDI+画图代码,至于网络上有些人说只要获得客户区或画图区的句柄就能在按钮下执行GDI+的画图程序的话,绝对是没有经过验证的。我发现,上面的说法纯粹是瞎说,所以没有经过试验的话,就不要轻易的去推测。因此,我下面的程序都是要在菜单下对图片操作完成后,用ReDrawWindow函数来请求程序重画,也就是调用对话框的OnPaint函数的。另外,对于MFC的打开和保存对话框等基础知识,我在这里也不多说,下面就说说一个简单的数字图像处理操作,就是图像的灰度化。图像的灰度化也有很多方法,这里就用两种方法来灰度化,这两种方法分别对应着用GDI+进行数字图形处理的两种方法。

简单的GDI+数字图像处理的对话框程序

新建一个基于对话框的MFC应用程序

至于如何创建,不多说了,不要说你不会创建基于对话框的MFC应用程序,如果你真的不会,请不用来看这篇文章了。

编写对话框的OnPaint函数

void CGDIDlg::OnPaint()
{
 CPaintDC dc(this); // device context for painting
 
 // TODO: Add your message handler code here
 // Do not call CDialog::OnPaint() for painting messages
    Graphics graphics( dc.m_hDC );
 if(m_filename!="")
 {
  graphics.DrawImage(m_pImage,0,0);
  UpdateWindow();
 }

 
}
对上面的程序做点解释:红色字体部分是自己添加的程序。其中m_filename是对话框程序的一个CString类的成员变量,m_pImage是一个Image类的指针。他们在对话框的构造函数中的声明如下:
private:
 Image* m_pImage;
 CString m_filename;

编写图像灰度化的程序

用ImageAttribute类来对图形的RGB分量进行操作

完整的程序如下:
//使用ImageAttributes来控制图像的RGB各分量
 int nWidth=m_pImage->GetWidth();
 int nHeight=m_pImage->GetHeight();
 //新建图像对象
 Bitmap image(nWidth,nHeight);
 Rect rect(0,0,nWidth,nHeight);
 //利用新图像对象绘制
 Graphics graph(&image);
 ColorMatrix colorMatrix={
  0.299f,0.299f,0.299f,0.0f,0.0f,
  0.587f,0.587f,0.587f,0.0f,0.0f,
  0.114f,0.114f,0.114f,0.0f,0.0f,
  0.0f,  0.0f,  0.0f,  1.0f,0.0f,
  0.0f,  0.0f,  0.0f,  0.0f,1.0f
 };
 ImageAttributes imageAttr;
 imageAttr.SetColorMatrix(&colorMatrix);
 graph.DrawImage(m_pImage,rect,0,0,nWidth,nHeight,UnitPixel,&imageAttr);
 CLSID encoderClsid;
 GetEncoderClsid(L"image/bmp",&encoderClsid);
 image.Save(L"C://icetim.bmp",&encoderClsid,NULL);
m_pImage=Image::FromFile(L"C://icetim.bmp");
 m_filename="C://icetim.bmp";
 RedrawWindow();
程序说明:需要先将处理后的图片保存在硬盘上一份,然后再次读入和显示处理后的图片。通过用Graphics类的DrawImage方法的时候,添加一个ImageAttributes类的对象,那么会在绘画的时候用这个ImageAttributes类的对象的ColorMatrix来对原图像的RGB分量来进行模板操作,而ColorMatrix也是一个固定的矩阵,它的第一列代表的是R分量,第二列是G分量,第三列是B分量,第四列是Alpha分量(也就是透明度),第五列是保留的。注意其中的Rect类是GDI+中的类,而不是CRect类。
这里的灰度化采用的公式是:N=0.299*R+0.587*G+0.114*B; N是变化后的图像的灰度的像素值。
其中的GetEncoderClsid是一个全局的函数,在本文章的最后会给出其具体的函数内容。它的作用是获得一个图片格式的ClassID。

通过获取每个像素点的值来图像的RGB分量进行操作

完整的程序如下:
//通过控制单个像素来控制图像的RGB各分量
 //比ImageAttribute要慢的多
 int nWidth=m_pImage->GetWidth();
 int nHeight=m_pImage->GetHeight();
 Bitmap bitmap(m_filename.AllocSysString());
 Color color,colorTemp;
 for(int i=0;i<nHeight;i++)
 {
  for(int j=0;j<nWidth;j++)
  {
   bitmap.GetPixel(j,i,&color);
   int y=(color.GetR()+color.GetB()+color.GetG())/3;
   //colorTemp.SetValue(color.MakeARGB(color.GetA(),color.GetR()/3,color.GetG()/3,color.GetB()/3));
   colorTemp.SetValue(color.MakeARGB(color.GetA(),y,y,y));
   bitmap.SetPixel(j,i,colorTemp);
  }
 }
 
 CLSID encoderClsid;
 GetEncoderClsid(L"image/bmp",&encoderClsid);
 m_pImage=(Image*)&bitmap;
 m_pImage->Save(L"C://icetim.bmp",&encoderClsid,NULL);
m_pImage=Image::FromFile(L"C://icetim.bmp");
 m_filename="C://icetim.bmp";
 RedrawWindow();
程序说明:首先将图片读入到一个Bitmap类的对象bitmap中,然后通过bitmap对象的GetPixel方法来获得图像的颜色值(Color类的color对象),然后通过Color类的GetR、GetG、GetB和GetA方法,可以获得该点的RGB分量和Alpha分量。然后把这些RGB分量变化后,组成一个新的颜色值colorTemp,在此调用bitmap对象的SetPixel方法来设置该点的颜色值就OK了。这个方法,和用C#进行数字图像处理的方法很类似,有兴趣你可以看看 [C#]数字图像处理

两种方法的比较

用到GDI+的书上都说这种用ImageAttributes类的方法是比较快的,因为这是模板操作,比单个的设置像素值的方法要快很多。可能是我处理的图片比较小的缘故吧,我倒没有什么特别的感觉,感觉差不太多,所以选择用何种方法,要看你的需求了。

GetEncoderClsid函数

//用来取得GDI+编码器的ClassID
int GetEncoderClsid(const WCHAR *format, CLSID *pClsid)
{
 int nRet = -1;
 ImageCodecInfo * pCodecInfo = NULL;
 UINT nNum = 0,nSize = 0;
 GetImageEncodersSize(&nNum,&nSize);
 if (nSize<0)
 {
  return nRet;
 }
 pCodecInfo = new ImageCodecInfo[nSize];
 if (pCodecInfo==NULL)
 {
  return nRet;
 }
 GetImageEncoders(nNum,nSize,pCodecInfo);
 for (UINT i=0; i<nNum; i++)
 {
  if (wcscmp(pCodecInfo[i].MimeType,format)==0)
  {
   *pClsid = pCodecInfo[i].Clsid;
   nRet = i;
   delete[] pCodecInfo;
   return nRet;
  }
  else
  {
   continue;
  }
 }
 delete[] pCodecInfo;
 return nRet;
}

程序运行时截图

打开图片之后
图像灰度化之后

打开和保存图片文件

这里GDI+支持的图片文件格式是很多的,比如bmp、gif、png、jpg、jpeg、tiff等,因此这里的图片灰度化的程序都能正常的处理这些图片。当然也能保存成这些格式的图片。

打开图片文件的程序:

void CGDIDlg::OnFileOpen()
{
 // TODO: Add your command handler code here

 //利用系统的打开对话框
 static char szFilter[]="位图文件(*.bmp;*.dib)|*.bmp;*.dib|JPG文件(*.jpg)|*.jpg|JPEG文件(*.jpeg)|*.jpeg|GIF文件(*.gif)|*.gif|PNG文件(*.png)|*.png|All Files(*.*)|*.*||";
  CFileDialog dlg(true,"*.bmp",NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,szFilter);
  if(dlg.DoModal()==IDOK)
  {
   UpdateData(true);
   m_filename=dlg.GetPathName();
  m_pImage=Image::FromFile(m_filename.AllocSysString());
  }
  else
  {
   return;
  }

 //OnPaint();
 
 RedrawWindow(); 
}

保存图片文件的程序:

void CGDIDlg::OnFileSave()
{
 // TODO: Add your command handler code here
 
 if(m_filename=="")
 {
  AfxMessageBox("请先打开图片");
  return;
 }
 //利用系统的保存对话框
 CString filename;
 static char szFilter[]="位图文件(*.bmp;*.dib)|*.bmp;*.dib|JPEG文件(*.jpeg;*.jpg)|*.jpeg;*.jpg|GIF文件(*.gif)|*.gif|PNG文件(*.png)|*.png|All Files(*.*)|*.*||";
  CFileDialog dlg(true,"*.bmp",NULL,OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT,szFilter);
  if(dlg.DoModal()==IDOK)
  {
   UpdateData(true);
   filename=dlg.GetPathName();
  }
  else
  {
   return;
  }


CLSID encoderClsid;
 CString strExt=filename.Right(3);
 strExt.MakeLower();

 if(strExt==L"png")
 {
  GetEncoderClsid(L"image/png",&encoderClsid);
 }
 else if(strExt==L"jpg")
 {
  GetEncoderClsid(L"image/jpg",&encoderClsid);
 }
 else if(strExt==L"gif")
 {
  GetEncoderClsid(L"image/gif",&encoderClsid);
 }
 else
 {
  GetEncoderClsid(L"image/bmp",&encoderClsid);
 }

 Status status=m_pImage->Save(filename.AllocSysString(),&encoderClsid,NULL);
 if(status==Ok)
 {
  AfxMessageBox("保存成功");
 }
 else
 {
  AfxMessageBox("保存失败");
 }
}

原创粉丝点击