//基于颜色直方图的方法pDC->TextOut(10,168,"检索结果:");CBmpProc *pDestBmp;CString comp_pic_path;double fsim[15]; file://15张待比较的目标图片与用户输入图片的相似度存放的数组int psim[15]; file://与fsim想对应的图片编号数组,以便显示for(int comp_pic=1;comp_pic<=15;comp_pic++){comp_pic_path.Format("image%d.bmp",comp_pic);bmp.LoadFromFile(comp_pic_path); // 从库中读入位图pDestBmp = (CBmpProc*)new(CBmpProc); // 用new分配类目标pDestBmp->LoadFromObject(bmp, &CRect(0,0,128,128));// 从bmp中的指定区域读入图像,以便图片匹配的进行pDestBmp->CalculateColor(*pDC); file://计算目标图片的颜色直方图int x1,x2,y1,y2,x3,x4,y3,y4;x1=obj_set.m_x1;x2=obj_set.m_x2;x3=obj_set.m_x3;x4=obj_set.m_x4;y1=obj_set.m_y1;y2=obj_set.m_y2;y3=obj_set.m_y3;y4=obj_set.m_y4;file://用户输入的对象所在子块(既用户选定的4个子块)的坐标double sim[4][4]; file://子块之间的相似度数组int ccount[4][4]; file://有过统计的颜色数目记录数组for(int i=0;i<4;i++)for(int j=0;j<4;j++){sim[i][j]=0;ccount[i][j]=0;}file://以下两个for按公式计算两幅图像的各对应子块之间的相似度for(i=0;i<4;i++)for(int j=0;j<4;j++)for(int k=0;k<256;k++){if((pDestBmp->Color[i][j][k]>=pBmp->Color[i][j][k])&&pDestBmp->Color[i][j][k]!=0){sim[i][j]+=(1-((fabs(pDestBmp->Color[i][j][k]-pBmp->Color[i][j][k]))/(pDestBmp->Color[i][j][k])));ccount[i][j]++;}if((pDestBmp->Color[i][j][k]Color[i][j][k])&&pBmp->Color[i][j][k]!=0){sim[i][j]+=(1-((fabs(pDestBmp->Colori][j][k]-pBmp->Color[i][j][k]))/(pBmp->Color[i][j][k]))); ccount[i][j]++; } }for(i=0;i<4;i++)for(int j=0;j<4;j++){sim[i][j]=sim[i][j]/ccount[i][j];} file://计算两图像最终的相似度结果double final_sim=0;for(i=0;i<4;i++)for(int j=0;j<4;j++){file://对用户指定的块设置权重为1if((i==x1&&j==y1)||(i==x2&&j==y2)||(i==x3&&j==y3)||(i==x4&&j==y4))final_sim+=sim[i][j];elsefile://其他块降低权重为0.7,提高对对象匹配的精确度final_sim+=(sim[i][j]*0.7);}file://将15幅被比较图像与用户输入源图像的最后计算出来的相似度结果记录在数组中fsim[comp_pic-1]=final_sim; delete (CBmpProc*)pDestBmp;}int count=15;double tempf;int tempp;for(int l=0;l<15;l++){psim[l]=l+1; file://设定编号数组}file://将15个相似度从大到小排列,并且改变次序的时候编号数组和跟着改变for(int i=count;i>0;i--){for(int j=0;jif(fsim[j]tempf=fsim[j];tempp=psim[j];fsim[j]=fsim[j+1];psim[j]=psim[j+1];fsim[j+1]=tempf;psim[j+1]=tempp;}}int disp=0;int space=-128;file://将相似度最大的的两张图片显示出来for(int disp_pic=1;disp_pic<=2;disp_pic++){comp_pic_path.Format("image%d.bmp",psim[disp_pic]);bmp.LoadFromFile(comp_pic_path); // 从库中读入位图pDestBmp = (CBmpProc*)new(CBmpProc); // 用new分配类目标pDestBmp->LoadFromObject(bmp, &CRect(0,0,128,128)); // 从bmp中的指定区域读入图像disp++;space+=128;pDC->Rectangle(10+space-1,190-1,138+space+1,318+1);pDestBmp->Draw(*pDC, &CRect(10+space,190,138+space,318));// 将pBmp中的图像绘入DC的指定区域space+=6; }delete (CBmpProc*)pBmp; // 删除类目标,delete会自动调用类的析构函数。AfxMessageBox("检索完成"); }
//计算子块(x,y)的颜色对表,采取"八方向邻接技术"int CBmpProc::CalculateColorPair(int x, int y){file://颜色对采取欧氏距离来描述double o_dis[8];for(int k=0;k<8;k++){o_dis[k]=0;}file://计算(x,y)与周围所有子块的颜色直方图的欧氏距离file://---------------------------------------------for(int i=0;i<256;i++){if((x-1)>=0&&(y-1)>=0)o_dis[0]=o_dis[0]+(Color[x-1][y-1][i]-Color[x][y][i])*(Color[x-1][y-1][i]-Color[x][y][i]);elseo_dis[0]=-1;if((y-1)>=0)o_dis[1]=o_dis[1]+(Color[x][y-1][i]-Color[x][y][i])*(Color[x][y-1][i]-Color[x][y][i]);elseo_dis[1]=-1;if((x+1)<=3&&(y-1)>=0)o_dis[2]=o_dis[2]+(Color[x+1][y-1][i]-Color[x][y][i])*(Color[x+1][y-1][i]-Color[x][y][i]);elseo_dis[2]=-1;if((x-1)>=0)o_dis[3]=o_dis[3]+(Color[x-1][y][i]-Color[x][y][i])*(Color[x-1][y][i]-Color[x][y][i]);elseo_dis[3]=-1;if((x+1)<=3)o_dis[4]=o_dis[4]+(Color[x+1][y][i]-Color[x][y][i])*(Color[x+1][y][i]-Color[x][y][i]);elseo_dis[4]=-1;if((x-1)>=0&&(y+1)<=3)o_dis[5]=o_dis[5]+(Color[x-1][y+1][i]-Color[x][y][i])*(Color[x-1][y+1][i]-Color[x][y][i]);elseo_dis[5]=-1;if((y+1)<=3)o_dis[6]=o_dis[6]+(Color[x][y+1][i]-Color[x][y][i])*(Color[x][y+1][i]-Color[x][y][i]);elseo_dis[6]=-1;if((x+1)<=3&&(y+1)<=3)o_dis[7]=o_dis[7]+(Color[x+1][y+1][i]-Color[x][y][i])*(Color[x+1][y+1][i]-Color[x][y][i]);elseo_dis[7]=-1;}for(int j=0;j<8;j++){if(o_dis[j]>=0)o_dis[j]=sqrt(o_dis[j]);}file://------------------------------------------------file://欧氏距离计算结束int flag=0;int num=0;for(int pairnum=0;pairnum<32;pairnum++){if(pair[pairnum].x!=-1){num++;}}//因为在计算子块的颜色对表的时候已经写了特征颜色对数组,因此要先统计一下特征颜色对数组里已经//有多少有数值了,以便下次的写入可以接在后面,而不至于覆盖了前面的数值file://计算颜色对差值小于某个"域值"的这个域值double ave=0;for(int e=0;e<8;e++){ave+=o_dis[e];}ave=ave/8;ave=ave*0.02; file://采取与子块周围颜色对的平均值的2%计为域值file://对该子块的颜色对表进行从大到小的排序,采取冒泡排序int count=8; double temp;for(i=count;i>0;i--){for(int j=0;jif(o_dis[j]temp=o_dis[j];o_dis[j]=o_dis[j+1];o_dis[j+1]=temp;}}file://消除那些颜色对差值小于某个"域值"的颜色对,以消除那些没有意义的小对象for(k=0;kif(fabs(o_dis[k]-o_dis[k+1])for(int l=k+1;lo_dis[l]=o_dis[l+1];}count--;k--;o_dis[count]=-1;}}file://将该字块计算得到的颜色对表填入该图像的特征颜色对表for(int scan=0;scan<8;scan++){if(o_dis[scan]>0){pair[num].x=x;pair[num].y=y;pair[num].o_dis=o_dis[scan]; num++;}}return 1;}
//计算该图像的最终确定的特征颜色对表BOOL CBmpProc::SortColorPair(){file://32个数据项中有count个有实际数值for(int count=0;count<32;count++){if(pair[count].x==-1)break;}struct color_pair temp;file://对颜色对表从大到小排列序(冒泡排序法)for(int i=count;i>0;i--){for(int j=0;jif(pair[j].o_distemp=pair[j];pair[j]=pair[j+1];pair[j+1]=temp;}}file://计算域值以消除差值小于这个值的颜色对double ave=0;for(int e=0;eave+=pair[e].o_dis;}ave=ave/count;ave=ave*0.02;file://消除差值小于域值的颜色对for(int k=0;kif(fabs(pair[k].o_dis-pair[k+1].o_dis)for(int l=k+1;lpair[l]=pair[l+1];}count--;k--;}}file://置特征颜色对数目变量pair_count=count;return true;}将计算颜色直方图的代码表达如下:file://以下函数计算颜色直方图BOOL CBmpProc::CalculateColor(CDC &dc){if (!IsValid())return FALSE;ASSERT(m_pInfo);ASSERT(m_pInfo->bmiHeader.biSize == sizeof(BITMAPINFOHEADER));// 复制源图CDC compDC;// 创建与当前显示设备兼容的内存设备描述表compDC.CreateCompatibleDC(&dc);compDC.SelectObject(this);COLORREF clr; file://定义一个COLORREF结构,因为提取的象素点的颜色是以RGB形式表示的int pix_color;int red,green,blue;int x,y;for(int fd=0;fd<4;fd++)for(int sd=0;sd<4;sd++)for(int td=0;td<256;td++){Color[fd][sd][td]=0;}file://计算颜色直方图for(int i=0;i<4;i++)for(int j=0;j<4;j++)for(int k=0;k<32;k++)for(int l=0;l<32;l++){x=j*32+l;y=i*32+k;clr=compDC.GetPixel(x,y);red=GetRValue(clr);green=GetGValue(clr);blue=GetBValue(clr);file://因为RGB颜色共256^3种,不可能都保存到数组中,因此要先进行一定的提取工作,因为人对亮度的感file://觉是最明显的,所以可以先将RGB颜色值转成亮度值,这个公式即转换公司,刚好亮度数值是256级的,//就可以统计颜色直方图了pix_color=red*0.299+green*0.587+blue*0.114; Color[i][j][pix_color]++; file://对该象素点的颜色直方图数组中的相信位置加一,是直方图的物理实现}return true;}
以上三个函数实现对某一图像内部的具体计算,而对于基于颜色对方法的外部计算如下:
//计算用户确定的4块位置与其周围位置的颜色对(颜色对现采取用相邻两块的直方图的欧氏距离表示)pBmp->CalculateColorPair(obj_set.m_x1,obj_set.m_y1);pBmp->CalculateColorPair(obj_set.m_x2,obj_set.m_y2);pBmp->CalculateColorPair(obj_set.m_x3,obj_set.m_y3);pBmp->CalculateColorPair(obj_set.m_x4,obj_set.m_y4);file://其实在以上的4部计算中,已经形成了初步的颜色对表,在此只不过是将表中的数据从大到小排列出来//并且祛除差值小于某一域值的颜色对file://计算颜色对结束,形成颜色对表pBmp->SortColorPair(); file://颜色对表计算出来,表中的数据既是用户输入的该图像的代表特征pDC->TextOut(10,168,"检索结果:");CBmpProc *pDestBmp;CString comp_pic_path;int disp=0;int space=-128;file://读取带比较的图像(在此初定15幅--现定义这15幅图像即图片数据库)for(int comp_pic=1;comp_pic<=15;comp_pic++){comp_pic_path.Format("image%d.bmp",comp_pic);bmp.LoadFromFile(comp_pic_path); // 从库中读入位图pDestBmp = (CBmpProc*)new(CBmpProc); // 用new分配类目标pDestBmp->LoadFromObject(bmp, &CRect(0,0,128,128)); // 从bmp中的指定区域读入图像file://计算当前被比较的图像的颜色直方图pDestBmp->CalculateColor(*pDC);int match=0; file://颜色对匹配数目double ave=0; file://确定匹配时候不能使用精确匹配,所以需要一个差值小于某一域值时的域值for(int s=0;spair_count;s++){ave+=pBmp->pair[s].o_dis;}ave=ave/pBmp->pair_count; file://这个域值的基数即是用户输入的图片的颜色对表中颜色对的平均值ave=ave*0.02; file://确定误差小于2%的颜色对均属于这个域值
int pairflag[32]; file://颜色对匹配标志数组,即某一颜色对如果在目标图像中找到,下一次就不能再匹配for(int t=0;t<32;t++){pairflag[t]=-1;}for(int i=0;i<4;i++){for(int j=0;j<4;j++){file://按顺序计算目标图像中一子块与其周围子块的颜色对,然后在用户输入的图像的颜色对表中查询计算出//来的颜色对pDestBmp->CalculateColorPair(i,j); for(int scan=0;scan<8;scan++){if(pDestBmp->pair[scan].x==-1)break;}for(int comp=0;compfor(int count=0;countpair_count;count++){if((fabs(pBmp->pair[count].o_dis-pDestBmp->pair[comp].o_dis))file://差值小于某域值,则匹配到pairflag[count]=0; file://置颜色对匹配标志位match++; file://匹配数加一break;}}}file://重新置目标图像的颜色对表为空,因为现在的实现方式是在计算某一子块的颜色对时已经写过了颜色对//表,为保证颜色对表的真确性,必须在查询下一子块的时候重新置颜色对表为空for(int re=0;repDestBmp->pair[re].x=-1;}}file://如果有60%以上的特征颜色对匹配到,就说明该图像已经被检索到if(match>=(pBmp->pair_count*0.60)){file://以下是对检索到的图像的界面上的排版显示disp++;space+=128;file://画图像边框pDC->Rectangle(10+space-1,190-1,138+space+1,318+1);pDestBmp->Draw(*pDC, &CRect(10+space,190,138+space,318)); // 将pBmp中的图像绘入DC的指定区域space+=6;}delete (CBmpProc*)pDestBmp; // 删除类目标,delete会自动调用CBmpProc类的析构函数。}delete (CBmpProc*)pBmp; // 删除类目标,delete会自动调用类的CBmpProc析构函数。AfxMessageBox("检索完成");
通过以上的程序,我们就实现了真正的图像内容检索,简单的程序就实现了现代计算机科学在多媒体研究前沿的任务。
Visual C++实现数字图像增强处理前言 对于一个图像处理系统来说,可以将流程分为三个阶段,在获取原始图像后,首先是图像预处理阶段、第二是特征抽取阶段、第三是识别分析阶段。图像预处理阶段尤为重要,如果这阶段处理不好,后面的工作根本无法展开。 在实际应用中,我们的系统获取的原始图像不是完美的,例如对于系统获取的原始图像,由于噪声、光照等原因,图像的质量不高,所以需要进行预处理,以有利于提取我们感兴趣的信息。图像的预处理包括图像增强、平滑滤波、锐化等内容。图像的预处理既可以在空间域实现,也可以在频域内实现,我们主要介绍在空间域内对图像进行点运算,它是一种既简单又重要的图像处理技术,它能让用户改变图像上像素点的灰度值,这样通过点运算处理将产生一幅新图像。下面我们开始介绍与图像点运算的相关知识。 一、图像的直方图 图像直方图是图像处理中一种十分重要的图像分析工具,它描述了一幅图像的灰度级内容,任何一幅图像的直方图都包含了丰富的信息,它主要用在图象分割,图像灰度变换等处理过程中。从数学上来说图像直方图是图像各灰度值统计特性与图像灰度值的函数,它统计一幅图像中各个灰度级出现的次数或概率;从图形上来说,它是一个二维图,横坐标表示图像中各个像素点的灰度级,纵坐标为各个灰度级上图像各个像素点出现的次数或概率。如果不特别说明,本讲座中的直方图的纵坐标都对应着该灰度级在图像中出现的概率。我们的例子是在一个对话框中显示一个图像的直方图,为实现该目的,我们定义了一个名为"ZFT"的对话框类用来显示图像的直方图,具体实现代码和效果图如下(关于代码实现部分可以参考笔者2001年在天极网上发表的一篇VC实现数字图像处理的文章):
基于图像采集卡的视频图像处理系统
计算机图像处理系统从系统层次上可分为高、中、低档三个层次,目前一般比较普及的是低档次的系统,该系统由CCD(摄像头)、图像采集卡、计算机三个部分组成,其结构简单,应用方便,效果也比较不错,得到的图像较清晰。目前网上基于VC开发经验的文章不少,可是关于如何在VC开发平台上使用图像采集卡的文章确没发现,笔者针对在科研开发中积累的使用图像采集卡经验,介绍如何自己是如何将采集卡集成到图像开发系统中,希望能够给目前正需要利用图像采集卡开发自己的图像处理系统的朋友有所帮助。 笔者使用的摄像机采用台湾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_CACardParam(AD_SETHPFREQ,hpGrabFreq);W32_Set_PAL_Range(1250, 1024);//设置水平采集范围W32_Set_VGA_Mode ( 1 );wGrabWinX1 = 0; // 采集窗口的左上角的坐标wGrabWinY1 = 0;firstTime=TRUE; bGrabMode = FRAME;bZipMode = ZIPPLE;
/lpDib=NULL;//存放获取的图像数据}
CTestView::~CTestView(){W32_Close_MPE1000();//关闭采集卡}////显示采集的图象,双击鼠标采集停止void CTestView::OnGraboneframe() {// TODO: Add your command handler code herewCurrent_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 ;//bmi为全局变量,用于显示图像时用bmi->bmiHeader.biSize = 40;bmi->bmiHeader.biWidth = cxDib;bmi->bmiHeader.biHeight = cyDib;bmi->bmiHeader.biPlanes = 1 ;
bmi->bmiHeader.biBitCount = bitCount ;
bmi->bmiHeader.biCompression = 0 ;bmi->bmiHeader.biSizeImage = 0 ;bmi->bmiHeader.biXPelsPerMeter = 0;bmi->bmiHeader.biYPelsPerMeter = 0;bmi->bmiHeader.biClrUsed = 0;bmi->bmiHeader.biClrImportant = 0;
for (i=0;i bmi->bmiColors[i].rgbBlue = i ;bmi->bmiColors[i].rgbGreen = i ;bmi->bmiColors[i].rgbRed = i ;bmi->bmiColors[i].rgbReserved = 0 ;}
}
"画中画"这个概念类似与彩色电视机"画中画",就是在一幅大的图像内显示另外一幅内容不同的小的图像,小图像的尺寸大小一般地说为大图像尺寸的1/4或1/9,显示位置在大图像的右上角。这种技术不仅在电视技术中,在可视电话系统也可以发现这种技术的身影,它们都是依靠硬件来实现的,但是如何在VC开发平台上用编程语言来将该功能添加到自己开发的视频监控软件,为使用者提供更大的信息量呢?也许读者最容易想到的是首先显示大图像,然后再在一个固定位置画第二幅小图像,这种技术技术如果对于静止图像当然没有问题,但是对于视频流,由于每一秒钟需要画25幀,即25幅图像,这样一来计算机需要不停的画不停的擦除,会给用户以闪烁的感觉,如何解决这个问题呢?有的参考书上将大小图像分快显示,这种方法要将待显示的图像数据与显示位置的关系对应起来,容易出错不说,而且麻烦,且速度慢,为此,我对该方法进行了改进,得到了满意的效果。实现的代码如下:
void pictureinpicture( ){………………………..CBitmap bitmap,*oldmap;pData1=(BYTE*)new char[biWidth*biHeight *3];//biWidth和biHeight为视频采集卡获取//的图像尺寸。 Read(pData1,bih.biWidth*bih.biHeight *3);//该函数从采集卡中获取数据CClientDC dc(this);m_pBMI1= new BITMAPINFO;//自定义的BMP文件信息结构,用于后面的图像显示m_pBMI1->bmiHeader.biBitCount=24;m_pBMI1->bmiHeader.biClrImportant=0;m_pBMI1->bmiHeader.biClrUsed=0;m_pBMI1->bmiHeader.biCompression=0;m_pBMI1->bmiHeader.biHeight=biHeight;m_pBMI1->bmiHeader.biPlanes=1;m_pBMI1->bmiHeader.biSize=40;m_pBMI1->bmiHeader.biSizeImage=WIDTHBYTES(biWidth*8)*biHeight*3;m_pBMI1->bmiHeader.biWidth=biWidth;m_pBMI1->bmiHeader.biXPelsPerMeter=0;m_pBMI1->bmiHeader.biYPelsPerMeter=0;//////////////////////////////////////////////////////////////////////// pData2=(BYTE*)new char[biWidth1*biHeight1 *3];//申请存放小图像的缓冲区Read(pData2,biWidth1*biHeight1 *3);////向该缓冲区读数据m_pBMI2= new BITMAPINFO;m_pBMI2->bmiHeader.biBitCount=24;m_pBMI2->bmiHeader.biClrImportant=0;m_pBMI2->bmiHeader.biClrUsed=0;m_pBMI2->bmiHeader.biCompression=0;m_pBMI2->bmiHeader.biHeight=biHeight1;m_pBMI2->bmiHeader.biPlanes=1;m_pBMI2->bmiHeader.biSize=40;m_pBMI2->bmiHeader.biSizeImage=WIDTHBYTES(biWidth1*8)*biHeight1*3;m_pBMI2->bmiHeader.biWidth=biWidth1;m_pBMI2->bmiHeader.biXPelsPerMeter=0;m_pBMI2->bmiHeader.biYPelsPerMeter=0;//下面实现画中画的显示
CDC MemDc;MemDc.CreateCompatibleDC(&dc);bitmap.CreateCompatibleBitmap(&dc,biWidth,biHeight);oldmap=MemDc.SelectObject(&bitmap);::StretchDIBits(MemDc.m_hDC,0,0,biWidth,biHeight,0,0,—biWidth,biHeight,pData1,m_pBMI1,DIB_RGB_COLORS,SRCCOPY);//首先将大图像画在内寸上下文中::StretchDIBits(MemDc.m_hDC,20,20,biWidth1,biHeight1,_0,0,biWidth1,biHeight1,pData2,m_pBMI2,DIB_RGB_COLORS,SRCCOPY);//再将小图像画在内寸上下文中::StretchBlt(dc.m_hDC,0,0,bih.biWidth,bih.biHeight,_MemDc.m_hDC,0,0,bih.biWidth,bih.biHeight,SRCCOPY);//将结果显示在屏幕上。
MemDc.SelectObject(oldmap);delete pData1;delete m_pBMI1;delete pData2;delete m_pBMI2;
一、 引言
在工程上我们经常要判断某设备产生的实际波形信号是否能同预先设计的相拟合,但由于实际产生的波形不仅仅是简单的正、余弦波形,而往往是含有较丰富频率分布的不规则波形,而设备元器件本身及外界的电磁干扰又不可避免的引入了干扰噪声,就为我们分析其与预先设计波形的拟合程度的判别增加了困难。另外,实际波形和预先设计波形间往往存在着时序上的差别,相位的改变同样也不利于信号的拟合判别。本文利用高等数学以及信号与系统方面的有关知识提出对该问题的解决方法。
二、 信号相似程度判别的理论依据
在信号与系统这门学科中,相关性是一种在时域中对信号特性进行描述的重要方法。由于其通信的功率谱函数是一对傅立叶变换,在信号分析中往往利用它来分析随机信号的功率谱分布,以致不少人一提到相关性马上会联想到信号功率谱的计算,但相关在对确定信号的分析也是有一定应用。由于相关的概念是为研究随机信号的统计特性而引入的,那么从理论上我们也可以将其应用于两个确定信号(一个我们采集到的信号波形和一个理论波形)相似性的研究上。
要比较两波形的相似程度还要从相关的概念上入手,假定两信号分别为x(t)、y(t),可以选择当倍数a使a*y(t)去逼近x(t)。再此我们可以借用误差能量来度量这对波形的相似程度,具体方法同高等数学上用来判断函数间正交性的方法基本类似:
误差能量用x(t)-a*y(t)的平方在时域上的积分来表示;倍数a的选择必须要保证能使能量误差为最小,通过对函数求导求极值可以得知当a为x(t)*y(t)在时域的积分与y(t)*y(t)在时域的积分比值时可以满足条件,在此条件下的误差能量是可能所有条件下最小的。定义x(t)与y(t)的相关数为Pxy,其平方与1的差值为相对误差能量,即误差能量与x(t)*x(t)在时域积分的比值。其中,xy就可以用来表征两波形的相似程度。解出关于Pxy的方程,其分子为x(t)*y(t)在时域的积分;分为两信号各自的平方在时域积分之积的平方根。从数学上可以证明分子的模小于分母,也即相关数Pxy的模不会大于1。由于对于能量有限的信号而言,能量是确定的,相关系数Pxy的大小只由x(t)*y(t)的积分所决定。如果两完全不相似的波形其幅度取值和出现时刻是相互独立、彼此无关的,x(t)*y(t)=0,其积分结果亦为0,所以当相关系数为0时相似度最差,即不相关。当相关系数为1,则误差能量为0,说明这两信号相似度很好,是线形相关的。因此把相关系数作为两个信号波形的相似性(或线形相关性)的一种度量完全是有理论依据的、合理的。三、 算法的设计与实现
我们在对信号进行比较之前,先将理论波形做成一个数据文件,实际设备输出的波形也通过计算机接口采集并将数据存成数据文件。我们编写的程序通过对两个数据文件的相关性比较来得出实际波形同理论波形信号的拟合度。下面就对数据文件的读取、数据相关程度计算等关键代码作简要的绍:
首先,要在计算相关系数前把参加运算的两序列数据从文件读取到内存(堆栈)中去,为了方便读取多种数据格式的文件使程序更加灵活,选用MFC基本类库的CFileDialog类的成员函数来选取数文件,然后再通过CFile类的相关成员函数将其读取到内存中:
上述为读取一个信号文件的相关代码,其中buf1是一个char*类型的指针,该指针指向的内存存储有数据文件的数据,m_nData1Len 保存有第一个数据文件的长度。用同样的方法将第二个数据文件也读取到内存中,指向其首地址的指针为buf2,文件长度为m_nData2Len。参与运算的数据序列准备好后就可以进行这两组信号波形的相关系数的计算了,下面是有关的关键部分代码:
由于两序列长度可能不一样,如以较长序列为准,将短序列不足部分补0,根据相关系数的概念,补0部分的x(t)*y(t)的积分为0,没有实际意义,故以较短序列为准可以避免不必要的运算,运算效较高。
在计算机中将积分近似按离散点取和的方式进行近似的积分:
最后释放掉申请的内存:
四、 实验效果的检验
下面通过一个实际的例子来检验一下上述程序,我们想要获得的理想的波形如下图Data1所示,Data2所示波形是设备经过噪声抑制和相位纠偏等诸多措施后产生的实际波形,Data3所示波形是在没有任何保护措施下得到的粗糙的波形,显然Data2要比Data3能更好的同设计的理想波形Data1相拟合,但只是停留在定性分析上,究竟相似程度如何,定量的分析靠肉眼显然是无法完成的。先对Data1和Data2波形信号进行相关系数计算,得出其相关系数为0.793931,基本上是线形相关的,即实际的Data2信号设计的理想信号Data1的拟合程度还是可以接受的;然后再对Data1和Data3两波形信号进行相关系数计算,组信号的相关系数为 -0.013341,基本上线形不相关;再对Data2和Data3进行分析,计算结果是0.011665,结论也是基本不相关。通过上述程序对波形信号进行的定量分析同直观上的定性分析是相吻的。通过实际实验的检验证明该程序是可靠、实用的。
小结:本文提出的对波形信号相似程度的判断在电子工程上有着较为广泛的应用,能准确的判断出参加比较的两波形信号的相似程度,为设备的改进、元器件的选型等提供可参考的依据。另外,在判断移动的信号是否具有线形相关性的场合,如对雷达站接收到的两个不同距离的目标的反射信号的分析等都可以用本算法。通过对本文介绍的相关算法的改进还可以对信号的功率谱进行绘制、对波形信号进行更全面的分析。本程序在Windows 98下,由Microsoft Visual C++ 6.0编译通过。
昵称: [登录] [注册]
主页:
邮箱:(仅博主可见)
验证码: 看不清,换一个
评论内容:
登录 注册
[使用Ctrl+Enter键快速提交评论]