吴东,谢国波,苏本卉
(1.中山大学附属第一医院继续教育科,广州510080;2.广东工业大学计算机学院,广州510006)
GDI+与OpenCV在编程中混合使用的研究
吴东1,谢国波2,苏本卉2
(1.中山大学附属第一医院继续教育科,广州510080;2.广东工业大学计算机学院,广州510006)
介绍两种常用图像接口GDI+和OpenCV的特点以及功能,主要阐述在Visual Studio开发环境中使用GDI+和OpenCV结合混合使用处理图像的思路并使用代码实现。示例已通过上机调试,并投入实际使用。
GDI+;OpenCV;混合;图像
OpenCV是一个基于(开源)发行的跨平台计算机视觉库,针对2D、3D图像的处理提供了丰富的处理方法,主要应用于物体识别、图象分割、人脸识别、动作识别、运动跟踪、运动分析、机器视觉等领域,开发效率比较高,常常是我们在实际应用中处理图像的首选[1]。而GDI+是微软提供的一种图形设备接口,主要用于2D图像的绘制,其绘制图形的功力非常强大。虽然图像实际应用中我们主要使用OpenCV进行开发,但是一些细节地方难免需要用到GDI+的绘制功能,例如对一些图片处理之后,需要在每张图片上写汉字。前面的图像处理我们可使用OpenCV来完成,但是写汉字就需要GDI+来完成。这就需要用到OpenCV和GDI+来混合使用,而混合使用的关键就是二者格式的转化。
GDI+与OpenCV图像类型的转换是GDI+与OpenCV混合使用的关键,我们可以对GDI+与OpenCV表示图像的类型的结构和方法进行分析。
GDI+共包括三个图像类:Image类、Bitmap类和Metafile类,其Metafile类主要用于处理矢量图(不属于本文讨论范围),Bitmap类主要表示光栅图,Image是二者的父类。OpenCV中主要使用IplImage结构体来创建和处理图像,所以只需讨论Bitmap与IplImage两种格式并实现两种格式的转换即可。
由于GDI+与OpenCV不存在强制互转,所以我们就采用另一种常用的转换图像格式的方法:就是把Bitmap或IplImage类型图像转换为Byte数据[2],然后构造空白的Bitmap或IplImage类型的图片,再把byte数据填充到该空白图像中即可。
1.1Bitmap到Byte的转换
把Bitmap转换为二进制会用到BitmapData这个类,使用BitmapData可以读取图片信息在内存中的地址,BitmapData类中的Scan0表示图片第0个像素的第0个分量在内存的地址,BitmapData中Stride表示图片一行像素的字节宽度,根据图片的原点坐标位置的不同可正可负。Windows系统的窗口坐标原点为左上角,所以图片在内存中存放时顺序是从左到右,从上到下,但当图片在窗口中显示时却有两种坐标。
若图片的原点坐标与Windows相同在左上角,如图1,则Sride为正数,此时Scan0表示图1中像素点a的地址,读取顺序是从左到右,从上到下,即a、b、c、d、e、f、g、h、i、j、k、l、m、n、o、p。
若图片坐标原点在左下角,则像素点的顺序如图2,则此时Stride为负数,此时Scan0表示图1中像素点a的地址,由于memcpy方法拷贝数据是按照内存中存储顺序进行的,而此时图片像素点在内存中保存的顺序为m、n、o、p、i、j、k、l、e、f、g、h、a、b、c、d,所以应调节读取起始点a到m,读取方式是从左到右,最先读取图像的最后一行。读取信息后的图片为原图片关于x轴的反转图片,所以最后用OpenCV中cvFilp方法来再次反转图片,以得到图片的正确显示效果。
图1 原点在左上角时像素位置
图2 原点在左上角时像素位置
1.2Ipllmage到Byte的转换
IplImage转Byte相对于上面来说比较简单,IplImage结构中有imageDataOrigin和imageSize两个参数,其中imageDataOrigin表示图像数据的起始地址,imageSize表示数据的长度,用memcpy方法即可拷贝数据成Byte类型。
1.3Byte到Ipllmage的转换:
IplImage结构中存在imageData变量,表示IplImage类型图像的数据。我们可以先用cvCreateImage方法创建一个和要转换的Bitmap类型的图片长宽一样的空白图像,然后求出Byte中数据的长度,用memcpy方法从Byte拷贝数据到imageData。最后根据Bitmap图像原点坐标的位置,判断是否需要用IplImage的方法从cvFlip反转图像。
1.4Byte到Ipllmage的转换
Byte到Bitmap同样需要借组BitmapData,Bitmap中读写数据时需用LockBits来锁定读写区域,并且LockBits方法中包含BitmapData类型的参数,作用是保存要读写的数据,而BitmapData可强转为Byte类型,所以我们只需先新建一个Bitmap类型指针,锁定Bitmap指针对象,关联BitmapData对象,最后用memcpy方法拷贝Byte数据到BitmapData对象的Scan0变量中。
2.1GDI+与OpenCV类型呼转函数的实现
Bitmap*转IplImage*代码实现:
Ip lImage*BitmapToIplImage(Bitmap*pBitmap)
{
if(pBitmap)
{
BitmapData bmpData;
Rect rect(0,0,pBitmap->GetW idth(),pBitmap->GetH-eight());
pBitmap->LockBits(&rect,ImageLockModeRead,PixelFormat24bppRGB,&bmpData);
BYTE*temp=NULL; If(bmpData.Stride>0)
temp=(BYTE*)bmpData.Scan0;
else
temp=(BYTE*)bmpData.Scan0+bmpData.Stride* (pBitmap->GetHeight()-1);
IplImage*p IplImg=cvCreateImage(cvSize(pBitmap-> GetWidth(),pBitmap->GetHeight()),IPL_DEPTH_8U,3);
if(!p Ip lImg)
{
pBitmap->UnlockBits(&bmpData);
return NULL;
}
memcpy(pIplImg->imageData,temp,abs(bmpData. Stride)*bmpData.Height);
pBitmap->UnlockBits(&bmpData);
if(bmpData.Stride<0)
cvFlip(pIplImg,NULL,0);
return pIplImg;
}
else
return NULL;
}
函数思路过程:首先判断图像是否存在,新建对象bmpData保存Bitmap图像数据信息,LockBits锁定整个Bitmap中数据位置,然后读取到temp中。创建lplImage对象pIplImg,把temp中数据拷贝到pIplImg中并解锁pBitmap,最后返回填充好的lplImage图片指针。
IplImage*转Bitmap*代码实现:
Bitmap*IplImageToBitmap(IplImage*p Ip lImg)
{
if(p IplImg)
{
Bitmap*pBitmap=new Bitmap(pIplImg->width,p I-plImg->height,PixelFormat24bppRGB);
if(!pBitmap)
return NULL;
BitmapData bmpData;
Rect rect(0,0,p IplImg->width,p IplImg->height);
pBitmap->LockBits(&rect,ImageLockModeWrite,PixelFormat24bppRGB,&bmpData);
BYTE*pByte=(BYTE*)bmpData.Scan0;
if(p IplImg->widthStep==bmpData.Stride)//likely
memcpy(bmpData.Scan0,p IplImg->imageDataO-rigin,p IplImg->imageSize);
pBitmap->UnlockBits(&bmpData);
return pBitmap;
}
else
return NULL;
}
函数思路过程:首先判断图像是否存在,然后用new方法新建一个长宽和要转换的IplImage图像相等的Bitmap图像,使用LockBits方法是为了锁定整个Bitmap中数据位置然后等待数据的填充。memcpy用于拷贝填充bmpData,Scan0表示数据矩阵在内存中的地址,imageDataOrigin表示IplImage类型图片中数据的起始位置,imageSize表示数据的长度,把IplImage中数据拷贝到Bitmap中后用UnlockBits解锁区域,最后返回填充好的Bitmap图片指针。
2.2互转函数的测试
本测试主要是验证图片类型互转后可不可以正确使用转换后的图片指针,测试中使用显示图片这个功能进行验证。
首先,用IplImage类型载入C盘下的图片test.jpg,然后用IplImageToBitmap函数把IplImage图片转为Bitmap类型,然后用Draw Image函数显示图片,代码如下:
CDC*pDC=GetDC();
Graphics graph(pDC->GetSafeHdc());
IplImage*pImage=cvLoad Image("c:\test.jpg",1);
Bitmap*img=IplImageToBitmap(p Image);
graph.DrawImage(img,0,0);
程序执行完后可得结果(下图):
图3 转换后结果
然后,用Bitmap类型载入C盘下的图片test.jpg,然后用BitmapToIplImage函数把Bitmap图片转为I-plImage类型,调OpenCV的cvShow Image函数显示图片,代码如下:
CDC*pDC=GetDC();
Bitmap*img=Bitmap::FromFile(L"c:\test.jpg");
IplImage*p Image=BitmapToIplImage(img);
cvShowImage("图片",pImage);
程序执行完后可得结果(图4)。
2.3测试结论
由以上测试可知,GDI+和OpenCV中主要的图片类Bitmap和IplImage类型相互转换后均能正确地显示图片,即在实际的开发应用过程中可以针对不同情况灵活地选择其一进行使用,大大提高了代码的灵活性和编程效率。
图4 转换后结果
GDI+与OpenCV都具有丰富的图像操作及处理方法,实际操作中可能会反复用到两个库的转换,本文提供的算法能够方便地实现它们各自支持的类型的转换,不需要去保存中间图像数据,可以使用户能够方便地在实际应用中根据需要结合二者各自的长处来设计程序,使得图像处理的工作变得事半功倍。
[1]左飞.数字图像处理技术详解与Visual C++实践.北京:电子工业出版社.2014(3):157~242
[2]高守传,刘书志,姚领田等.VC++实践与提高——数字图像处理与工程应用篇.北京:中国铁道出版社.2006(1):5~17
[3]刘瑞桢,于仕琪.OpenCV教程——基础篇.北京:北京航空航天出版社,2007:76~385
[4]Chand M.GDI+图形程序设计.北京:电子工业出版社,2005:1~325
GDI+;OpenCV;Mixed;Images
Research on the Mixed Use of GDI+and OpenCV in Programm ing
WU Dong1,XIE Guo-bo2,SU Ben-hui2
(1.Department of Education,the First Affiliated Hospital of SUN Yat-sen University,Guangzhou 510080;2.College of Computer,Guangdong University of Technology,Guangzhou 510006)
Introduces the two common interface of GDI+and OpenCV image features and functions,mainly elaborates the idea of using GDI+and OpenCV mixing processing image in the Visual Studio development environment and then implementing code.The example has passed the debugging and put in actual application.
1007-1423(2015)16-0082-04
10.3969/j.issn.1007-1423.2015.16.019
吴东(1974-),男,海南人,本科,研究实习员,研究方向为信息化管理
谢国波(1977-),男,广东五华人,博士,教授,研究方向为云计算与大数据、混沌保密通信、低碳制造数据智能化处理
苏本卉(1991-),男,河南安阳人,硕士研究生,研究方向为混沌保密通信
2015-04-07
2015-05-15