使用GDI+进行图像处理
2011-10-18 13:48
507 查看
如何进行图像的旋转呢?其实这个问题的一种解决方法是利用二维(x,y坐标中)矩阵转换实现图像旋转。使用高中时所学的三角知识或者大学中的线性代数知识就可以解决。其原理是已知一个点的坐标,那么这个点的旋转坐标可以通过
[/code]
求得,这里A是以弧度为单位的角度(2P弧度=360度)。因此,只要将图像加载到内存中,然后将它选入设备上下文,接着调用GetPixel和SetPixel,象上面所说的那样映射所有的像素,便可以实现图像的旋转效果。对于90、180、-90度的旋转,这是一个不错的方法,因为正弦、余弦的值无外乎+/-1或者0......话还没等我说完,一块砖头就朝我头上飞过来了,啊唷,我当然不会叫你用这种方法做事情啦!有更好的方法呢。下面就是本文的正题:使用GDI+实现图像旋转处理。我想了解GDI+的人不是很多,因为它是Windows中的新东西。要想了解它的细节,请参考MSDN的有关文章。
GDI+是GDI图形库的一个增强版本,C++可以使用这个库。它内建于Windows XP 和Microsoft .NET,而对于Windows 98、Windows NT和Windows 2000,则有一个可重新发布的版本。GDI+是一个C++ API。它用C++类和C++方法。GDI+所包含的内容非常多,远远不止我在此所描述的这些。为了使用GDI+,你必须包含(#include)<gdiplus.h>文件,并将工程链接到gdiplus.lib库,这两个文件包含在最新的Windows SDK中。下面是Myimgapp2运行画面,如图一所示。这个程序示范了如何用GDI+来旋转图像。
图一 图像旋转90度
前面说过,原来程序中的那个C++类CPicture是基于IPicture接口的,它处理图像的COM接口。在本文的例子程序Myimgapp2中,我重写了CPicture的代码,使用了GDI+里的图像类(Image)。对所有的细节都进行了封装。新的CPicture类不再使用 IPicture 这个COM接口,而是用Image取而代之,所有其它的类如CPictureView 和 CPictureCtrl都能和从前一样工作。但有两个障碍要解决:第一个是你必须对GDI库进行初始化以及最后的终止释放操作,与其说它是个障碍,还不如说它是GDI+本身的需要,在哪里进行这两个操作呢?最佳的地方莫过于在程序的
InitInstance 和 ExitInstance函数中:
CMyApp::m_gdiplusToken 是一个很神奇的东东,它来自GdiplusStartup 并被传递到GdiplusShutdown。m_gdiplusStartupInput 是一个结构,它包含某些GDI+的启动参数。缺省构造函数建立一个智能的缺省值,它又一次证明了C++比C更好。一旦你启动GDI+,就可以使用它了。原来的CPicture类有一个指向IPicture的指针,而新版的CPicture类有一个指向Image的指针。同样,它也有可重载的Load函数来从不同的地方加载图象。例如,下面便是新版的CPicture如何从某个路径名中加载图像文件。
[/code]
在此代码段中,重点是GDI+要用宽字符串,所以你要用USES_CONVERSION 和 A2W.。原来的CPicture用Load函数从某个CFile、CArchive、资源ID或流中加载图像。所有Load函数最终都走到从流中加载图像的例程:CPicture::Load(IStream*)。当我开始用Image代替IPicture,并用GDI+函数从流中加载数据时,它不工作。情况真是很糟,令人沮丧。问题出在MFC的CArchiveStream类,这个类在CArchive类之上实现流化。可能是CArchiveStream没有正确实现所有的IStream方法,Image::FromStream无法正确处理基于CArchiveStream的流操作,更糟的是它返回一切OK,但实际上当你试图显示或者删除Image时会失败。
为了解决这个问题,我重写了CPicture::Load(UINT nID),其中用到了CreateStreamOnHGlobal函数。这个API函数很好用,它在一个全局内存块中创建一个流:
[/code]
这里lpRsrc已经指向内存中的图形资源。所以基本思路是加载图像资源,将它拷贝到全局内存中,在内存上创建一个流,然后用Image::FromStream创建一个图象,有关细节请参见例子源代码。当对象被销毁或某人加载另外的图像时,CPicture自动释放全局内存。对于从CFile或者CArchive加载的情况——其实,根本不需要考虑这种情况。 以上是加载的方法,下面看看如何显示图像,你必须使用GDI+类Graphics——与原来GDI中的设备上下文(HDC或CDC)类似,它也有一个方法DrawImage:
[/code]
有关细节请参考源代码。用Image代替IPicture来表现图像要简单一些。Image::GetWidth 和Image::GetHeight分别获得象素的宽度和高度,这正是你所需要代替IPicture中HIMETRIC单位的东东。一般来说,用GDI+很容易编程,例如下面示范了如何旋转:
[/code]
这个代码将图像顺时针旋转90度,下面列出了RotateFlipType所有可能的情况: RotateFlipType 选项
GDI+ 的其它函数用于展开和修剪图像,它甚至还有一个函数Image::GetThumbnailImage用来解决缩略图问题,有兴趣的话不妨自己试一下。 我鼓励大家去研究一下GDI+。有些程序员因为它速度慢而不喜欢它,但是有很多文档中提供了一些技巧来改善它的性能。例如,有一个类叫CachedBitmap,这个类以优化设备的格式保存位图。对于象视频或高端图像编辑等图形敏感的应用程序你可能不会用GDI+来编程(一般都用DirectX),但对于日常的一般图形应用来说,GDI+不失为一种比GDI更好的选择。
(x*cos(A) + y*sin(A),- x*sin(A) + y*cos(A))
[/code]
求得,这里A是以弧度为单位的角度(2P弧度=360度)。因此,只要将图像加载到内存中,然后将它选入设备上下文,接着调用GetPixel和SetPixel,象上面所说的那样映射所有的像素,便可以实现图像的旋转效果。对于90、180、-90度的旋转,这是一个不错的方法,因为正弦、余弦的值无外乎+/-1或者0......话还没等我说完,一块砖头就朝我头上飞过来了,啊唷,我当然不会叫你用这种方法做事情啦!有更好的方法呢。下面就是本文的正题:使用GDI+实现图像旋转处理。我想了解GDI+的人不是很多,因为它是Windows中的新东西。要想了解它的细节,请参考MSDN的有关文章。
GDI+是GDI图形库的一个增强版本,C++可以使用这个库。它内建于Windows XP 和Microsoft .NET,而对于Windows 98、Windows NT和Windows 2000,则有一个可重新发布的版本。GDI+是一个C++ API。它用C++类和C++方法。GDI+所包含的内容非常多,远远不止我在此所描述的这些。为了使用GDI+,你必须包含(#include)<gdiplus.h>文件,并将工程链接到gdiplus.lib库,这两个文件包含在最新的Windows SDK中。下面是Myimgapp2运行画面,如图一所示。这个程序示范了如何用GDI+来旋转图像。
图一 图像旋转90度
前面说过,原来程序中的那个C++类CPicture是基于IPicture接口的,它处理图像的COM接口。在本文的例子程序Myimgapp2中,我重写了CPicture的代码,使用了GDI+里的图像类(Image)。对所有的细节都进行了封装。新的CPicture类不再使用 IPicture 这个COM接口,而是用Image取而代之,所有其它的类如CPictureView 和 CPictureCtrl都能和从前一样工作。但有两个障碍要解决:第一个是你必须对GDI库进行初始化以及最后的终止释放操作,与其说它是个障碍,还不如说它是GDI+本身的需要,在哪里进行这两个操作呢?最佳的地方莫过于在程序的
InitInstance 和 ExitInstance函数中:
//初始化 GDI class CMyApp : public CWinApp { protected: GdiplusStartupInput m_gdiplusStartupInput; ULONG_PTR m_gdiplusToken; ……. }; //释放GDI BOOL CMyApp::InitInstance() { VERIFY(GdiplusStartup(&m_gdiplusToken, &m_gdiplusStartupInput, NULL)==Ok); ……. } int CMyApp::ExitInstance() { GdiplusShutdown(m_gdiplusToken); return CWinApp::ExitInstance(); }
CMyApp::m_gdiplusToken 是一个很神奇的东东,它来自GdiplusStartup 并被传递到GdiplusShutdown。m_gdiplusStartupInput 是一个结构,它包含某些GDI+的启动参数。缺省构造函数建立一个智能的缺省值,它又一次证明了C++比C更好。一旦你启动GDI+,就可以使用它了。原来的CPicture类有一个指向IPicture的指针,而新版的CPicture类有一个指向Image的指针。同样,它也有可重载的Load函数来从不同的地方加载图象。例如,下面便是新版的CPicture如何从某个路径名中加载图像文件。
BOOL CPicture::Load(LPCTSTR pszPathName) { Free(); USES_CONVERSION; m_pImage = Image::FromFile(A2W(pszPathName), m_bUseEmbeddedColorManagement); return m_pImage->GetLastStatus()==Ok; }
[/code]
在此代码段中,重点是GDI+要用宽字符串,所以你要用USES_CONVERSION 和 A2W.。原来的CPicture用Load函数从某个CFile、CArchive、资源ID或流中加载图像。所有Load函数最终都走到从流中加载图像的例程:CPicture::Load(IStream*)。当我开始用Image代替IPicture,并用GDI+函数从流中加载数据时,它不工作。情况真是很糟,令人沮丧。问题出在MFC的CArchiveStream类,这个类在CArchive类之上实现流化。可能是CArchiveStream没有正确实现所有的IStream方法,Image::FromStream无法正确处理基于CArchiveStream的流操作,更糟的是它返回一切OK,但实际上当你试图显示或者删除Image时会失败。
为了解决这个问题,我重写了CPicture::Load(UINT nID),其中用到了CreateStreamOnHGlobal函数。这个API函数很好用,它在一个全局内存块中创建一个流:
// 分配全局内存并在其中创建流 HGLOBAL m_hMem = GlobalAlloc(GMEM_FIXED, len); BYTE* pmem = (BYTE*)GlobalLock(m_hMem); memcpy(pmem,lpRsrc,len); IStream* pstm; CreateStreamOnHGlobal(m_hMem,FALSE,&pstm);
[/code]
这里lpRsrc已经指向内存中的图形资源。所以基本思路是加载图像资源,将它拷贝到全局内存中,在内存上创建一个流,然后用Image::FromStream创建一个图象,有关细节请参见例子源代码。当对象被销毁或某人加载另外的图像时,CPicture自动释放全局内存。对于从CFile或者CArchive加载的情况——其实,根本不需要考虑这种情况。 以上是加载的方法,下面看看如何显示图像,你必须使用GDI+类Graphics——与原来GDI中的设备上下文(HDC或CDC)类似,它也有一个方法DrawImage:
BOOL CPicture::Render(CDC* pDC, CRect rc) const { ……. Graphics graphics(pDC->m_hDC); graphics.DrawImage(m_pImage, rc.left, rc.top, rc.Width(), rc.Height()); }
[/code]
有关细节请参考源代码。用Image代替IPicture来表现图像要简单一些。Image::GetWidth 和Image::GetHeight分别获得象素的宽度和高度,这正是你所需要代替IPicture中HIMETRIC单位的东东。一般来说,用GDI+很容易编程,例如下面示范了如何旋转:
void CPicture::Rotate(RotateFlipType rft) { if (m_pImage) { m_pImage->RotateFlip(rft); } }
[/code]
这个代码将图像顺时针旋转90度,下面列出了RotateFlipType所有可能的情况: RotateFlipType 选项
// 用于Image::RotateFlip的枚举类型 enum RotateFlipType { RotateNoneFlipNone = 0, Rotate90FlipNone = 1, Rotate180FlipNone = 2, Rotate270FlipNone = 3, RotateNoneFlipX = 4, Rotate90FlipX = 5, Rotate180FlipX = 6, Rotate270FlipX = 7, RotateNoneFlipY = Rotate180FlipX, Rotate90FlipY = Rotate270FlipX, Rotate180FlipY = RotateNoneFlipX, Rotate270FlipY = Rotate90FlipX, RotateNoneFlipXY = Rotate180FlipNone, Rotate90FlipXY = Rotate270FlipNone, Rotate180FlipXY = RotateNoneFlipNone, Rotate270FlipXY = Rotate90FlipNone };
GDI+ 的其它函数用于展开和修剪图像,它甚至还有一个函数Image::GetThumbnailImage用来解决缩略图问题,有兴趣的话不妨自己试一下。 我鼓励大家去研究一下GDI+。有些程序员因为它速度慢而不喜欢它,但是有很多文档中提供了一些技巧来改善它的性能。例如,有一个类叫CachedBitmap,这个类以优化设备的格式保存位图。对于象视频或高端图像编辑等图形敏感的应用程序你可能不会用GDI+来编程(一般都用DirectX),但对于日常的一般图形应用来说,GDI+不失为一种比GDI更好的选择。
相关文章推荐
- VC6使用GDI+进行图像的特效处理和MFC学习笔记-1
- 使用C#进行图像处理的几种方法(转)
- 使用GDI+位图数据扫描线处理图像的小技巧
- php使用imagick进行图像处理
- 使用 Java 进行图像处理 - 图像编码输出
- 使用pillow进行图像处理
- ROS学习笔记(2):在ROS中使用OpenCV进行简单的图像处理---代码实现篇
- 使用ABBYY FineReader进行自动图像预处理
- 使用OpenCV读、操作、写图像并与bash合作对某个目录下所有图像进行类似处理
- 使用Matlab进行图像处理
- 在 NASA 使用开源工具进行图像处理
- 使用python进行图像处理
- 使用python进行图像处理的基本方法
- 使用GDI+位图数据扫描线处理图像的小技巧
- 使用C#进行图像处理的几种方法
- 使用OpenCV读、操作、写图像并与bash合作对某个目录下所有图像进行类似处理
- 使用 Java 进行图像处理 - 取得图像上指定位置像素的 rgb 颜色分量
- android开发在jni中使用Opencv进行图像处理,环境搭建篇
- 【数字图像处理】使用kmeans算法对TrueColor图片进行优化
- VC 6.0 使用GDI+类库进行图片处理