//基于颜色直方图的方法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实现数字图像处理的文章):