OPENCV学习笔记(1)


刚上研一,本身想对嵌入式进行一番学习,结果实习的时候用opencv搞图像识别,有点让人失望。不过毕竟是AI的电脑视觉方面的东西,所以也就报有很大热情学的,业余还得巩固基础C、C++的书(等把C++Primer再看一遍后进军SDK了,得等多久到MFC啊)。
CSDN开了博客,写了些,后来才发现有学生大本营,决定在这安家落户,毕竟还是学生嘛。
经过前面学习(图形结构体数组相关操作,opencv自带GUI控件,图片的色彩变换以及座标变换),终于可以进入主题——图像识别了。
图像识别基础是直方图,在我看来好像一切都是重它开始的,比如说图像匹配其实是源图像直方图和特征图像直方图进行数学比较得到的。
啥是直方图?
在我看来就是一张统计图,统计各个像素在一定范围内的数量。举个例子:大家都知道一般RGB图像有3个通道分别是R,G,B。就拿红色通道R来说,它的范围有0-255,直方图就是把它分为N个,每个块是255/N(平均分),统计色彩在0-255/N的像素点有多少个,255/N-255/N*2的像素点,255/N*2-255/N*3的像素点...
了解直方图的结构和操作可以通过下面我对比两张图片相识度来了解:

#include "stdafx.h"
#include <cv.h>
#include <highgui.h>

int main(int argc, char** argv)
{
 IplImage *src = cvLoadImage("./lena.jpg");
 IplImage *hsv = cvCreateImage(cvGetSize(src),8,3);
 cvCvtColor(src,hsv,CV_BGR2HSV);// change RGB of src to HSV of hsv
 IplImage *h_plane = cvCreateImage(cvGetSize(src),8,1);
 IplImage *s_plane = cvCreateImage(cvGetSize(src),8,1);
 IplImage *v_plane = cvCreateImage(cvGetSize(src),8,1);
 IplImage *planes[]={h_plane,s_plane};
 cvSplit(hsv,h_plane,s_plane,v_plane,0);//split HSV to 3 channal
 
 //求得直方图
 int h_bins=30,s_bins=32;
 CvHistogram *hist1,*hist2;
 int size[]={h_bins,s_bins};
 float h_ranges[]={0,180};
 float s_ranges[]={0,255};
 float *ranges[]={h_ranges,s_ranges};

 hist1=cvCreateHist(2,size,CV_HIST_ARRAY,ranges,1);
 cvCalcHist(planes,hist1,0,0);//只能一个通道一个通道的写入直方图,所以上面分成H、S、V
 cvNormalizeHist(hist1,1.0);//归一化直方图,使所有块的值加起来为1
 hist2=cvCreateHist(2,size,CV_HIST_ARRAY,ranges,1);
 cvCalcHist(planes,hist2,0,0);
 cvNormalizeHist(hist2,1.0);
 
 //求得signature用于比较直方图
 CvMat *sig1,*sig2;
 int numrows=h_bins*s_bins;
 sig1=cvCreateMat(numrows,3,CV_32FC1);//由于是2维图像直方图,所以只需保存每个点的(值,横座标,纵座标)三个数,一共numrows个点
 sig2=cvCreateMat(numrows,3,CV_32FC1);
 for(int h=0;h<h_bins;h++)
 {
  for(int s=0;s<s_bins;s++)
  {
   float bin_val=cvQueryHistValue_2D(hist1,h,s);
   cvSet2D(sig1,h*s_bins+s,0,cvScalar(bin_val));
   cvSet2D(sig1,h*s_bins+s,1,cvScalar(h));
   cvSet2D(sig1,h*s_bins+s,2,cvScalar(s));
   bin_val=cvQueryHistValue_2D(hist2,h,s);
   cvSet2D(sig2,h*s_bins+s,0,cvScalar(bin_val));
   cvSet2D(sig2,h*s_bins+s,1,cvScalar(h));
   cvSet2D(sig2,h*s_bins+s,2,cvScalar(s));
  }
 }
 float emd=cvCalcEMD2(sig1,sig2,CV_DIST_L2);
 printf("%d/n",emd);//可以清楚的看出因为我对比同一个图像直方图,所以得数0是对的,完全一样
 return 0;
}
程序不难,主要对于直方图和rignature结构的理解!

 

当然opencv有封装号的可以比较的函数——反向投影,模板匹配。

用直方图比较来定位图像中的模板
void cvCalcBackProjectPatch( IplImage** image, CvArr* dst,
                             CvSize patch_size, CvHistogram* hist,
                             int method, float factor );

image
输入图像 (可以传递 CvMat** )
dst
输出图像.
patch_size
扫描输入图像的补丁尺寸
hist
直方图
method
比较方法,传递给 cvCompareHist (见该函数的描述).
factor
直方图的归一化因子,将影响输出图像的归一化缩放。如果为 1,则不定。
函数 cvCalcBackProjectPatch 通过输入图像补丁的直方图和给定直方图的比较,来计算反向投影。提取图像在 ROI 中每一个位置的某种测量结果产生了数组 image. 这些结果可以是色调, x 差分, y 差分, Laplacian 滤波器, 有方向 Gabor 滤波器等中 的一个或多个。每种测量输出都被划归为它自己的单独图像。 image 图像数组是这些测量图像的集合。一个**直方图 hist 从这些图像数组中被采样创建。最后直方图被归一化。直方图 hist 的维数通常很大等于图像数组 image 的元素个数。

在选择的 ROI 中,每一个新的图像被测量并且转换为一个图像数组。在以锚点为“补丁”中心的图像 image 区域中计算直方图 (如下图所示)。用参数 norm_factor 来归一化直方图,使得它可以与 hist 互相比较。计算出的直方图与直方图模型互相比较, (hist 使用函数 cvCompareHist ,比较方法是 method=method). 输出结果被放置到概率图像 dst 补丁锚点的对应位置上。这个过程随着补丁滑过整个 ROI 而重复进行。迭代直方图的更新可以通过在原直方图中减除“补丁”已覆盖的尾象素点或者加上新覆盖的象素点来实现,这种更新方式可以节省大量的操作,尽管目前在函数体中还没有实现。

 

void cvMatchTemplate( const CvArr* image, const CvArr* templ,
                      CvArr* result, int method );

image
欲搜索的图像。它应该是单通道、8-比特或32-比特 浮点数图像
templ
搜索模板,不能大于输入图像,且与输入图像具有一样的数据类型
result
比较结果的映射图像。单通道、32-比特浮点数. 如果图像是 W×H 而 templ 是 w×h ,则 result 一定是 (W-w+1)×(H-h+1).
method
指定匹配方法:
函数 cvMatchTemplate 与函数 cvCalcBackProjectPatch 类似。它滑动过整个图像 image, 用指定方法比较 templ 与图像尺寸为 w×h 的重叠区域,并且将比较结果存到 result 中。 下面是不同的比较方法,可以使用其中的一种

method=CV_TM_SQDIFF:

method=CV_TM_SQDIFF_NORMED:
 
method=CV_TM_CCORR:
 
method=CV_TM_CCORR_NORMED:
 
method=CV_TM_CCOEFF:

给后面的MatchTempate加个例子把:

int main( int argc, char** argv ) {

    IplImage *src, *templ,*ftmp[6]; //ftmp is what to display on
    int i;
    if( argc == 3){
  //Read in the template to be used for matching:
  if((templ=cvLoadImage(argv[1], 1))== 0) {
    printf("Error on reading template %s/n",argv[2]); help();
    return(-1);
  }

  //Read in the source image to be searched:
  if((src=cvLoadImage(argv[2], 1))== 0) {
    printf("Error on reading src image %s/n",argv[i]); help();
    return(-1);
  }

   int patchx = templ->width;
  int patchy = templ->height;
  int iwidth = src->width - patchx + 1;
  int iheight = src->height - patchy + 1;
  for(i=0; i<6; ++i){
   ftmp[i] = cvCreateImage( cvSize(iwidth,iheight),32,1);
  }

  //DO THE MATCHING OF THE TEMPLATE WITH THE IMAGE
  for(i=0; i<6; ++i){
   cvMatchTemplate( src, templ, ftmp[i], i);
//  double min,max;
//  cvMinMaxLoc(ftmp,&min,&max);
   cvNormalize(ftmp[i],ftmp[i],1,0,CV_MINMAX);
  }
        //DISPLAY
  cvNamedWindow( "Template", 0 );
        cvShowImage(   "Template", templ );
        cvNamedWindow( "Image", 0 );
        cvShowImage(   "Image", src );

  cvNamedWindow( "SQDIFF", 0 );
        cvShowImage(   "SQDIFF", ftmp[0] );

  cvNamedWindow( "SQDIFF_NORMED", 0 );
        cvShowImage(   "SQDIFF_NORMED", ftmp[1] );

  cvNamedWindow( "CCORR", 0 );
        cvShowImage(   "CCORR", ftmp[2] );

  cvNamedWindow( "CCORR_NORMED", 0 );
        cvShowImage(   "CCORR_NORMED", ftmp[3] );

  cvNamedWindow( "CCOEFF", 0 );
        cvShowImage(   "CCOEFF", ftmp[4] );

  cvNamedWindow( "CCOEFF_NORMED", 0 );
        cvShowImage(   "CCOEFF_NORMED", ftmp[5] );


  //LET USER VIEW RESULTS:
        cvWaitKey(0);
    }
return 0;
}
 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章