CImage类的用法(转帖)

From: http://elevenguy.blog.163.com/blog/static/616122092010053550593/


我们知道,Visual C++的CBitmap类和静态图片控件的功能是比较弱的,它只能显示出在资源中的图标、位图、光标以及图元文件的内容,而不像VB中的Image控件可 以显示出绝大多数的外部图像文件(BMP、GIF、JPEG等)。因此,想要在对话框或其他窗口中显示外部图像文件则只能借助于第三方提供的控件或代码。 现在,MFC和ATL共享的新类CImage为图像处理提供了许多相应的方法,这使得Visual C++在图像方面的缺憾一去不复返了。

CImage类概述

CImage是MFC和ATL共享的新类,它能从外部磁盘中调入一个JPEG、GIF、BMP和PNG格式的图像文件加以显示,而且这些文件格式可以 相互转换。由于CImage在不同的Windows操作系统中其某些性能是不一样的,因此在使用时要特别注意。例如,CImage::PlgBlt和 CImage::MaskBlt只能在 Windows NT 4.0 或更高版本中使用,但不能运行在Windows 95/98 应用程序中。CImage::AlphaBlend和CImage::TransparentBlt也只能在 Windows 2000/98或其更高版本中使用。即使在Windows 2000运行程序还必须将stdafx.h文件中的WINVER和_WIN32_WINNT的预定义修改成0x0500才能正常使用。

CImage封装了DIB(设备无关位图)的功能,因而可以让我们能够处理每个位图像素。它具有下列最酷特性:

1、AlphaBlend支持像素级的颜色混合,从而实现透明和半透明的效果。

2、PlgBlt能使一个矩形区域的位图映射到一个平行四边形区域中,而且还可能使用位屏蔽操作。

3、TransparentBlt在目标区域中产生透明图像,SetTransparentColor用来设置某种颜色是透明色。

4、MaskBlt在目标区域中产生源位图与屏蔽位图合成的效果。

使用CImage的一般方法

  使用CImage的一般方法是这样的过程:

(1) 打开应用程序的stdafx.h文件添加CImage类的包含文件:

#include <atlimage.h>

(2) 定义一个CImage类对象,然后调用CImage::Load方法装载一个外部图像文件。

(3) 调用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分别指定图 像要显示的高度和宽度,xSrc、ySrc、nSrcWidth和nSrcHeight用来指定要显示的源图像的某个部分所在的位置和大小。 rectDest和rectSrc分别用来指定目标设备环境上和源图像所要显示的某个部分的位置和大小。

需要说明的是,Draw方法综合了StretchBlt、TransparentBlt和AlphaBlend函数的功能。默认时,Draw的功能和 StretchBlt相同。但当图像含有透明色或Alpha通道时,它的功能又和TransparentBlt、AlphaBlend相同。因此,在一般 情况下,我们都应该尽量调用CImage::Draw方法来绘制图像。

例如,下面的示例Ex_Image是实现这样的功能:当选择"文件"ò"打开"菜单命令后,弹出一个文件打开对话框。当选定一个图像文件后,就会在窗口客户区中显示该图像文件内容。这个示例的具体步骤如下:

(1) 创建一个默认的单文档程序项目Ex_Image。

(2) 打开stdafx.h文件中添加CImage类的包含文件atlimage.h。

(3) 在view类中添加成员变量CImage m_Image;

CEx_ImageView类添加ID_FILE_OPEN的COMMAND事件映射程序,并添加下列代码:

程序代码:

void CImageProcessView::OnFileOpen()

{

// TODO: 在此添加命令处理程序代码

CString strFilter;

CSimpleArray aguidFileTypes;

HRESULT hResult;

// 获取CImage支持的图像文件的过滤字符串

hResult = m_Image.GetExporterFilterString(strFilter,aguidFileTypes,

_T( "All Image Files") );

if (FAILED(hResult)) {

MessageBox(_T("GetExporterFilter调用失败!"));

return;

}

CFileDialog dlg(TRUE, NULL, NULL, OFN_FILEMUSTEXIST, strFilter);

if(IDOK != dlg.DoModal())

return;

m_Image.Destroy();

// 将外部图像文件装载到CImage对象中

hResult = m_Image.Load(dlg.GetFileName());

if (FAILED(hResult)) {

MessageBox(_T("调用图像文件失败!"));

return;

}

// 设置主窗口标题栏内容

CString str;

str.LoadString(AFX_IDS_APP_TITLE);

AfxGetMainWnd()->SetWindowText(str + _T(" - ") +dlg.GetFileName());

Invalidate(); // 强制调用OnDraw

}

  (4) 定位到CEx_ImageView::OnDraw函数处,添加下列代码:

程序代码:

void CEx_ImageView::OnDraw(CDC* pDC)

{

CImageProcessDoc* pDoc = GetDocument();

ASSERT_VALID(pDoc);

if (!m_Image.IsNull()) {

m_Image.Draw(pDC->m_hDC,0,0);}

}

  (5) 打开Ex_ImageView.h文件,添加一个公共的成员数据m_Image:

程序代码:

public:

CImage m_Image;

  (6) 编译并运行。单击"打开"工具按钮,在弹出的对话框中指定一个图像文件后,单击"打开"按钮,其结果如图7.21所示。

CImage类的用法(转帖) - elevenguy - elevenguy的博客

将图片用其它格式保存

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

{

// TODO: 在此添加命令处理程序代码

if (m_Image.IsNull()) {

MessageBox(_T("你还没有打开一个要保存的图像文件!"));

return;

}

CString strFilter;

strFilter ="位图文件|*.bmp|JPEG 图像文件|*.jpg|

GIF 图像文件|*.gif|PNG 图像文件|*.png||";

CFileDialog dlg(FALSE,NULL,NULL,NULL,strFilter);

if ( IDOK != dlg.DoModal())

return;

// 如果用户没有指定文件扩展名,则为其添加一个

CString strFileName;

CString strExtension;

strFileName = dlg.m_ofn.lpstrFile;

if(dlg.m_ofn.nFileExtension == 0)

{

switch (dlg.m_ofn.nFilterIndex)

{

case 1:

strExtension = "bmp"; break;

case 2:

strExtension = "jpg"; break;

case 3:

strExtension = "gif"; break;

case 4:

strExtension = "png"; break;

default:

break;

}

strFileName = strFileName + _T(".") + strExtension;

}

// 图像保存

HRESULT hResult = m_Image.Save(strFileName);

if (FAILED(hResult))

{

MessageBox(_T("保存图像文件失败!"));

}

}

柔化(平滑)和锐化处理

  在图像处理中,我们通常用一些数学手段,对图像进行除去噪声、强调或抽取轮廓特征等图像空间的变换。所谓"图像空间的变换"是借助于一个称之为模板的局部像素域来完成的,不同的模板具有不同的图像效果。

1. 柔化(平滑)

图像的柔化是除去图像中点状噪声的一个有效方法。所谓柔化,是指使图像上任何一个像素与其相邻像素的颜色值的大小不会出现陡突的一种处理方法。设在一个3 x 3的模板中其系数为:

1 1 1

1 1 1        *1/9

1 1 1

 

中间有底纹的表示中心元素,即用那个元素作为处理后的元素。很明显,上述模板(称之为Box模板)是将图像上每个像素用它近旁(包括它本身)的9个像素的平均值取代。这样处理的结果在除噪的同时,也降低图像的对比度,使图像的轮廓模糊。为了避免这一缺陷,我们对各点引入加权系数(不都乘1了),将原来的模板改为:

 1 2 1

 2 4 2    *1/16

 1 2 1

新的模板可一方面除去点状噪声,同时能较好地保留原图像的对比度,因此该模板得到了广泛的应用。由于这个模板是通过二维高斯(Gauss)函数得到的,故称为高斯模板。

2. 锐化

锐化和柔化恰恰相反,它通过增强高频分量减少图像中的模糊,因此又称为高通滤波。锐化处理在增强图像边缘效果的同时增加了图像的噪声。常用的锐化模板是拉普拉斯模板:

-1 -1 -1

-1 9  -1 

-1 -1 -1

用此模板处理后的图像,轮廓线条将明显得到增强。轮廓线以外的部分将变得较暗,而轮廓线部分将变得比较明亮。

使用程序对模板进行运算时,要考虑到溢出点的处理。所谓溢出点,指的是大于255或小于0的点。处理时,可令大于255的点取255,而小于0的点取其正值。

3. 实现代码

实现柔化和锐化时,我们先调用CImage::GetPixel来依次读取相应的像素,然后用柔化和锐化模板进行处理,最后调用CImage::SetPixel函数将处理后的像素写回到CImage对象中。具体的代码如下:

程序代码:

void FilterImage(CImage* image, int nType)

{

if (image->IsNull())

return;

int smoothGauss[9] = {1,2,1,2,4,2,1,2,1}; // 高斯模板

int sharpLaplacian[9] = {-1,-1,-1,-1,9,-1,-1,-1,-1}; // 拉普拉斯模板

int opTemp[9];

float aver; // 系数

if ( nType > 1) nType = 0;

switch( nType ){

case 0: // 高斯模板 平滑

aver = (float)(1.0/16.0);

memcpy( opTemp, smoothGauss, 9*sizeof(int));

break;

case 1: // 拉普拉斯模板 锐化

aver = 1.0;

memcpy( opTemp, sharpLaplacian, 9*sizeof(int));

break;

}

int i,j;

int nWidth = image->GetWidth();

int nHeight = image->GetHeight();

for (i = 1; i < nWidth-1; i++){

for (j = 1; j < nHeight-1; j++){

int rr = 0, gg = 0, bb = 0;

int index = 0;

for (int col = -1; col <= 1; col++){

for (int row = -1; row <= 1; 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 > 255 ) rr = 255;

else if ( rr < 0 ) rr = -rr;

if ( gg > 255 ) gg = 255;

else if ( gg < 0 ) gg = -gg;

if ( bb > 255 ) bb = 255;

else if ( bb < 0 ) bb = -bb;

// 错位重写以避免前一个像素被新的像素覆盖

image->SetPixel( i-1, j-1, RGB(rr,gg,bb));

}

}

Invalidate(); // 强制调用OnDraw

}

图7.22是使用上述代码将某个图像处理后的结果。

CImage类的用法(转帖) - elevenguy - elevenguy的博客

变成黑白图片(变为灰度图)

  由于许多图像文件使用颜色表来发挥显示设备的色彩显示能力,因而将一张彩色图片变成黑色图片时需要调用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 r,g,b,avg;

for (int x=0; x<maxX; x++) {

for (int y=0; y<maxY; y++) {

pixel = image->GetPixel(x,y);

r = GetRValue(pixel);

g = GetGValue(pixel);

b = GetBValue(pixel);

avg = (int)((r + g + b)/3);

image->SetPixelRGB(x,y,avg,avg,avg);

}

}

} else {

// 获取并修改颜色表

int MaxColors = image->GetMaxColorTableEntries();

RGBQUAD* ColorTable;

ColorTable = new RGBQUAD[MaxColors];

image->GetColorTable(0,MaxColors,ColorTable);

for (int i=0; i<MaxColors; i++)

{

int avg = (ColorTable[i].rgbBlue + ColorTable[i].rgbGreen + ColorTable[i].rgbRed)/3;

ColorTable[i].rgbBlue = avg;

ColorTable[i].rgbGreen = avg;

ColorTable[i].rgbRed = avg;

}

image->SetColorTable(0,MaxColors,ColorTable);

delete(ColorTable);

}

Invalidate();// 强制调用OnDraw

}


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/406331.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java 头尾 队列_Java数据结构之队列(动力节点Java学院整理)

队列的定义&#xff1a;队列(Queue)是只允许在一端进行插入&#xff0c;而在另一端进行删除的运算受限的线性表。(1)允许删除的一端称为队头(Front)。(2)允许插入的一端称为队尾(Rear)。(3)当队列中没有元素时称为空队列。(4)队列亦称作先进先出(First In First Out)的线性表&a…

ListT please check srcIndex

这种错误的原因&#xff0c;是因为List<T>不是线程安全的。 转载于:https://www.cnblogs.com/chucklu/p/4303873.html

vue 实现点击“增加“”按钮,添加一个HTML元素

vue 实现点击“增加“”按钮&#xff0c;添加一个HTML元素

期待flash三剑客

flash cs5 flash catalyst flash builder

pythonlocust使用方法_使用python的locust库进行性能测试

Locust(俗称 蝗虫), 一个轻量级的开源压测工具&#xff0c;用Python编写。安装pip3 install locustPython编写性能测试脚本import jsonfrom locust import HttpLocust, TaskSet, task"""创建后台管理站点压测类&#xff0c;需要继承TaskSet可以添加多个测试任务…

Windows SDK笔记(三):定制控件消息处理函数

From: http://www.vckbase.com/document/viewdoc/?id1009 一、概述 控件的消息处理函数是由系统定义好了的&#xff0c;通常情况下&#xff0c;不需要自己提供。 但当需要对控件进行特殊控制时&#xff0c;可以提供一个消息处理函数&#xff0c;替换原来的消息处理函数。 自…

python tip

Python代码 def run(name,age,sex): print name :,name,age:,age,sex:,sex >>> run(xiaoming,namexiaoming,sexboy) SyntaxError: non-keyword arg after keyword arg def run(name,age,sex): print name :,name,age:,age,sex:,sex >>> run…

js实现使用drag实现元素拖拽排序

js实现使用drag实现元素拖拽排序

.NET Core 和 .NET Framework 之间的关系

引用一段描述&#xff1a;Understanding the relationship between .NET Core and the .NET Framework。 .NET Core and the .NET Framework have (for the most part) a subset-superset relationship. .NET Core is named "Core" since it contains the core featu…

博客园开始对X++语言语法高亮的支持

关注X的博客园博友有福啦&#xff0c;现在发布X代码也同样可以实现语法高亮。目前仅支持SyntaxHighlighter插件。请看如下&#xff1a; public void update(boolean _updateParty true, boolean _updateContactPersonLastEdit true) {Address addressOrig this.orig();ttsbe…

win10网卡高级属性配置图解_简单设置,win10再也不会断网了

你多朋友反应win10会频繁断网&#xff0c;无任何规律。这也是导致很多人反感win10的原因之一。我百度了一下这个问题&#xff0c;解决问题的方法基本上都是关闭省电模式&#xff0c;重装网卡驱动之类的答案。但都解决不了根本问题。虽然不清楚深层次的断网原因&#xff0c;但通…

css 绘制带尖角的矩形

css 绘制带尖角的矩形

Ext GrdPanel多种取值方式

一、单选行取值 1、在Store中设置ReaderID <ext:Store runat"server" ID"SQry" AutoLoad"true" RemoteSort"true"> <Reader> <ext:JsonReader ReaderID"shid"> <Fields> <ext:RecordFiel…

为子控件添加自定义绘图方式

在MFC应用程序中&#xff0c;有时会遇到需要让指定的控件实现自绘。但是看该控件的事件&#xff0c;没有一个像是能承担这种责任的。 我们都知道控件也是窗口&#xff0c;也都有消息循环。所以&#xff1a; 方案一&#xff1a;写个新类&#xff0c;继承自某个窗口类&#xff…

ios7 导航栏 手势 右划 自动返回 相关

http://www.tuicool.com/articles/vMfAVv 纪录一下&#xff0c;转载于:https://www.cnblogs.com/penn-wang/p/4308140.html