上一讲中我们介绍了图像特效显示操作的实现方法,如随机显示效果、马赛克效果、拉幕显示效果等,由于篇幅的限制,还有许多效果没有介绍;本期讲座将接着上一讲的内容,继续介绍一些图像特效显示效果。
另外,我们前面的学习都是针对现成的BMP图像,在实际工作学习中,绝大部分处理图像过程都是在一个系统环境中,也就是说需要和图像数据的获取设备直接打交道,一般情况下,计算机图像处理系统从系统层次上可分为高、中、低档三个层次,目前比较普及的是低档次的系统,该系统由CCD(摄像头)、图像采集卡、计算机三个部分组成,其结构简单,应用方便,效果也比较不错,得到的图像较清晰,所以目前在工程应用中采用的比较多。这就给开发人员带来一个现实的问题,如何使用图像采集卡呢?目前虽然各种编程资源中基于VC开发经验的文章不少,但是关于如何在VC开发平台上使用图像采集卡的文章的确没发现,笔者借这期讲座的宝贵机会,补充介绍一下如何在程序中编写自己的代码来操作图像采集卡,从而搭建一个完整的图像处理系统。希望通过这部分内容的学习,在读者的脑海里就可以建立一个完整的图像操作系统概念;同时也能够给目前正需要利用图像采集卡开发自己的图像处理系统的朋友有所帮助。
1. 抖动图像
在上一节讲座中,我们讲到了如何实现图象的"雕刻"和"浮雕"效果,它们的实现思想是通过求取"没有处理过的相邻两个像素之间的差值"来实现的。如果没有限制"以前没有处理过的两个像素之间的操作",取而代之的是"处理以前已经操作过的像素",那末就可以将一个像素的灰度值传递到与其相邻的若干像素。事实上,有时后我们必须通过上述的约定才能实现一些效果,如图像的抖动效果。例如,为了使图象看起来好象从左上角向右下角扫过,以产生运动的感觉,必须要反复拷贝左上方的那些像素的灰度值,逐步把它们融合在一起,看起来好象图像后边有一些颜色在逐渐的消失,这就是我们要讲的图象的抖动效果。下面给出了该效果的实现代码:
void CDibView::OnDouDongImage() //产生"抖动"效果图函数 { HANDLE data1handle;//用来存放图像数据的句柄; LPBITMAPINFOHEADER lpBi;//图像的信息头结构; CDibDoc *pDoc=GetDocument();//得到文挡指针; HDIB hdib;//用来存放图像数据的句柄; unsigned char *pData;//指向原始图像数据的指针; unsigned char *data;//指向处理后图像数据的指针; hdib=pDoc->m_hDIB;//拷贝存放已经读取的图像文件数据句柄; lpBi=(LPBITMAPINFOHEADER)GlobalLock((HGLOBAL)hdib);//获取图像信息头; pData=(unsigned char*)FindDIBBits((LPSTR)lpBi); //FindDIBBits是我定义的一个函数,根据图像的结构得到位图的灰度值数据; pDoc->SetModifiedFlag(TRUE); //设置文档修改标志为"真",为后续的修改存盘作准备; data1handle=GlobalAlloc(GMEM_SHARE,WIDTHBYTES(lpBi->biWidth*8)*lpBi->biHeight); //声明一个缓冲区用来暂存处理后的图像数据; data=(unsigned char*)GlobalLock((HGLOBAL)data1handle);//得到该缓冲区的指针; AfxGetApp()->BeginWaitCursor(); int i,j,buf; for( i=lpBi->biHeight; i>=2; i--)//从图像右下角开始对图像的各个像素进行"抖动"处理; for( j=lpBi->biWidth; j>=2; j--) { //抖动处理、从图像的右下角开始计算图像斜上方相邻像素的均值; buf=(*(pData+(lpBi->biHeight-i)*WIDTHBYTES(lpBi->biWidth*8)+j)+*(pData+(lpBi->biHeight-i+1)*WIDTHBYTES(lpBi->biWidth*8)+j-1))/2; if(buf>255) buf=255;//限制像素点的灰度范围为0-255; if(buf<0)buf=0; *(data+(lpBi->biHeight-i)*WIDTHBYTES(lpBi->biWidth*8)+j)=(BYTE)buf; } for( j=0; j<biHeight; j++) for( i=0; i<biWidth; i++) //重新写回原始图像的数据缓冲区; *(pData+i*WIDTHBYTES(lpBi->biWidth*8)+j)=*(data+i*WIDTHBYTES(lpBi->biWidth*8)+j); AfxGetApp()->EndWaitCursor(); pDoc->m_hDIB =hdib//将处理过的图像数据写回pDoc中的图像缓冲区; GlobalUnlock((HGLOBAL)hdib);//解锁、释放缓冲区; GlobalUnlock((HGLOBAL)data1handle); GlobalFree((HGLOBAL)hdib); GlobalFree((HGLOBAL)data1handle); Invalidate(TRUE);//显示图像 } |
对于比较复杂的图像,计算当前像素的灰度和斜上方像素的均值产生的抖动效果可能不明显,为了解决这个问题,笔者的解决办法是隔行隔列的计算,比如说计算当前位置(i j)的灰度值,我取(i,j)和(i-2,j-2)两个位置的像素的灰度值的平均。
2.图像合成技术
图像合成技术很重要,其实质是操作将两幅或两幅以上的图像,将它们的信息融合在一起,产生1+1>2的效果。我们在进行图像合成的时候可以采用Alpha值的方法,下面来看一下如何利用Alpha值来合成两张图片。
采用Alpha图象合成的方法,就是最终合成的图象的各点像素值是由用来制作合成图的两张图片的相应点的像素值按一定比例混合而成的,这个比例由Alpha值决定,具体算式如下:
resultPixe= (pixel1*(255-Alpha)+pixel2*Alpha)/255; // Alpha取值范围从0到255 |
上面的算式中,pixel1代表图像1的当前像素点的灰度值,pixel2代表图像2的当前像素点的灰度值,Alpha可以看作两个像素在最终合成的结果中所占的权重。可以看出,只要修改Alpha的值,就可以改变合成后的图象中用来合成的两张图片各自所占的比值,改变合成后的显示效果。我们可以利用这个方法,按一定的时间间隔修改Alpha的值、这样就可以很轻易的制作出生动的淡入淡出效果、实现两幅图片间的平滑过度效果。下面给出一个制作合成图的具体源码:
BOOL CompoundImage(HANDLE HDib1,HANDLE HDib2,int alpha) { BYTE lpData1,lpData2; // 源图象2的信息 //由于待合成的两个图象的格式、大小是一样的,所以我只获取一个图像文件的图像信息就可以了。 LPBITMAPINFO lpBi=(LPBITMAPINFO)HDib2; // 计算图象数据偏移量 lpData2=(LPVOID)((LPBYTE)lpBi->bmiColors+256*sizeof(RGBQUAD)); //获取源图像2的图像数据; lpBi=(LPBITMAPINFO)HDib1; lpData1=(LPVOID)((LPBYTE)lpBi->bmiColors+256*sizeof(RGBQUAD)); //通过alpha值合并两张图象的像素值 for ( int i=0;i< lpBi->biWidth; i++ ) for(int j=0;j< lpBi->biHeight;j++ ) { //套用alpha图像混合公式; *(lpData1+i*WIDTHBYTES(lpBi->biWidth*8)+j)=(*(lpData1+i*WIDTHBYTES(lpBi->biWidth*8)+j)*(255-alpha)+ *(lpData2+i*WIDTHBYTES(lpBi->biWidth*8)+j)*alpha)/255; } return lpData1; } |
以上内容我们主要是讲述了alpha图像混合的实现原理和方法,其实读者大可不必自己写这么多代码,微软给我们提供了一个名为AlphaBlend()的函数,它就可以直接实现图像合成的功能,具体怎么使用,还请读者参考MSDN。
3.采集卡的操作
图像处理所涉及的应用领域有军事应用、医学诊断、工业监控、物体的自动分检识别等等,这些应用系统无不需要计算机提供实时动态,效果逼真的图像。目前获取实时图像一般都需要在计算机内部安装一个图像采集卡,用来实现CCD端获取的模拟图像的数字化转换。笔者结合自己在项目开发中积累的一些经验,谈一下如何操作图像采集卡、然后再此基础基础上再实现一些特殊处理。
笔者的摄像机采用台湾BENTECH INDUSTRIAL 有限公司生产的CV-155L黑白摄像机。该摄像机分辨率为752x582。图象采集卡采用的是北京中科院科技嘉公司开发的基于PCI 总线的CA-MPE 1000 黑白图象采集卡。一般情况下,使用图像采集卡分三步,首先安装采集卡的驱动程序,并将虚拟驱动文件VxD.vxd拷贝到Windows的SYSTEM目录下;这时候就可以进入开发状态了,进入VC开发平台,生成新的项目,由于生产厂家为图像采集卡提供了以mpew32.dll、mpew32.lib命名的库文件,库中提供了初始硬件、采集图像等函数,为使用这些函数,需要在新项目上连接该动态库;最后一步就是采集图像并显示处理了,这一步要设置系统调色板,因为采集卡提供的是裸图形式,既纯图像数据,没有图像的规格和调色板信息,这些需要开发者自己规定实现,下面是实现的部分代码:
CTestView::CTestView() { W32_Init_MPE1000();//初始化采集卡 W32_Modify_Contrast(50);//下面的函数是为了对采集卡进行预设置 W32_Modify_Brightness(45);//设置亮度 W32_Set_HP_Value(945);//设置水平采集点数 wCurrent_Frame = 1;//当前帧为1,获取的图像就是从这帧取得的 // 设置采集信号源,仅对MPE1000有效 W32_Set_Input_Source(1);//该图像采集卡支持三路视频,目前采集的图像来自第二路输入端; W32_Set_PAL_Range(1250, 1024);//设置水平采集范围 W32_Set_VGA_Mode ( 1 ); 采用PAL制式; wGrabWinX1 = 0; // 采集窗口的左上角的坐标 wGrabWinY1 = 0; firstTime=TRUE; //第一次采集; bGrabMode = FRAME; //抓图模式为?格式; bZipMode = ZIPPLE; //压缩模式为ZIPPLE; lpDib=NULL;//存放获取的图像数据缓冲区为空; } CTestView::~CTestView() { W32_Close_MPE1000();//关闭采集卡 } void CTestView::OnGraboneframe()//显示采集的图象,双击鼠标采集停止 { // TODO: Add your command handler code here wCurrent_Frame = 1; // 设置采集目标为内存 W32_CACardParam (AD_SETGRABDEST, CA_GRABMEM); // 启动采集 if (lpDib != NULL)//如果图像缓冲区不为空,释放该缓冲区; { GlobalUnlock( hglbDIB ); GlobalFree( hglbDIB ); } //为采集到的图像数据分配内存; hglbDIB=GlobalAlloc(GHND, (DWORD)wImgWidth*(DWORD)wImgHeight ); lpDib = (BYTE *)GlobalLock( hglbDIB ); //得到图像数据的指针; hdc = GetDC()->GetSafeHdc( ) ; //获取视图的设备上下文句柄; if(lpDib != NULL) { cxDib = wImgWidth; cyDib = wImgHeight; SetLogicPal( hdc, cxDib, cyDib, 8 ); //设置调色板; SetStretchBltMode (hdc, COLORONCOLOR) ; bGrabMark = TRUE; while (bGrabMark == TRUE) { if(msg.message==WM_LBUTTONDBLCLK) //分析是否为鼠标双击消息; bGrabMark = FALSE;//如为鼠标双击消息,停止采集图象; W32_ReadXMS2Buf (wCurrent_Frame,lpDib) ; //将图象数据读入到图像数据缓冲区; SetDIBitsToDevice (hdc, 0, 0, cxDib, cyDib, 0, 0, 0, cyDib, (LPSTR) lpDib, bmi, DIB_RGB_COLORS) ; //显示图像; } // 停止采集 W32_CAStopCapture(); ::ReleaseDC( GetSafeHwnd(), hdc ); return ; } //将下面这个函数添加在视图类的CTestView::OnSize()函数中,就可以对系统的调色板进行设置。 void WINAPI InitLogicPal( HDC hdc , short width, short height, WORD bitCount ) { int j, i; short cxDib, cyDib; LOGPALETTE * pLogPal; j=256 ; if((pLogPal=(LOGPALETTE*)malloc(sizeof(LOGPALETTE)+ (j*sizeof(PALETTEENTRY)))) == NULL) return ; pLogPal->palVersion=0x300; //设置调色版的颜色信息; pLogPal->palNumEntries=j; for (i=0;i pLogPal->palPalEntry[i].peRed = i ; { pLogPal->palPalEntry[i].peGreen = i ; pLogPal->palPalEntry[i].peBlue = i ; pLogPal->palPalEntry[i].peFlags = 0; } hPal = ::CreatePalette(pLogPal); //创建调色板; delete pLogPal; ::SelectPalette(hdc,hPal,0);//系统实现调色板; ::RealizePalette(hdc); cxDib = width; cyDib = height; if ( (bmi = (BITMAPINFO *)malloc(sizeof(BITMAPINFOHEADER) + j*sizeof(RGBQUAD))) == NULL ) return ; //定义图 |