SVM支持向量机(Support Vector Machine)-翻译版

第一个支持向量机(SupportVector Machine) CvSVM

利用SVM解决2维空间向量的3级分类问题

#include <cv.h>
#include <highgui.h>
#include <ml.h>
#include <time.h>


int main(int argc, char **argv)
{
 intsize= 400;                    //
图像的长度和宽度
 int s= 1000;
 inti, j, sv_num;
 IplImage*img;
 CvSVMsvm= CvSVM ();
 CvSVMParamsparam;
 CvTermCriteriacriteria;  //
停止迭代的标准
 CvRNG rng= cvRNG(time(NULL));
 CvPointpts[s];                 //
定义1000个点
 float data[s* 2];
 intres[s];
 CvMatdata_mat, res_mat;
 CvScalarrcolor;
 constfloat *support;
 // (1)
图像区域的确保和初始化
  img= cvCreateImage(cvSize(size, size), IPL_DEPTH_8U, 3);
  cvZero(img);
// (1)图像区域的确保和初始化
确保画像区域,并清0(用黑色作初始化处理)。


 // (2)学习数据的生成
 for (i= 0; i< s; i++) {
    pts[i].x= cvRandInt(&rng) % size;      //
用随机整数赋值
    pts[i].y= cvRandInt(&rng) % size;
   if(pts[i].y> 50 * cos(pts[i].x* CV_PI/ 100) + 200) {
      cvLine(img, cvPoint(pts[i].x- 2, pts[i].y- 2), cvPoint(pts[i].x+ 2, pts[i].y+ 2), CV_RGB(255, 0, 0));
      cvLine(img, cvPoint(pts[i].x+ 2, pts[i].y- 2), cvPoint(pts[i].x- 2, pts[i].y+ 2), CV_RGB(255, 0, 0));
      res[i] = 1;
   }
   else{
     if(pts[i].x> 200) {
        cvLine(img, cvPoint(pts[i].x- 2, pts[i].y- 2), cvPoint(pts[i].x+ 2, pts[i].y+ 2), CV_RGB(0, 255, 0));
        cvLine(img, cvPoint(pts[i].x+ 2, pts[i].y- 2), cvPoint(pts[i].x- 2, pts[i].y+ 2), CV_RGB(0, 255, 0));
        res[i]= 2;
     }
     else{
        cvLine(img, cvPoint(pts[i].x- 2, pts[i].y- 2), cvPoint(pts[i].x+ 2, pts[i].y+ 2), CV_RGB(0, 0, 255));
        cvLine(img, cvPoint(pts[i].x+ 2, pts[i].y- 2), cvPoint(pts[i].x- 2, pts[i].y+ 2), CV_RGB(0, 0, 255));
        res[i]= 3;
     }
   }
 }
// (2)训练数据的生成
生成2维随机训练数据,并将其值放在CvPoint数据类型的数组pts[]中。


 // (3)学习数据的显示
  cvNamedWindow("SVM", CV_WINDOW_AUTOSIZE);
  cvShowImage("SVM", img);
  cvWaitKey(0);

将生成的数据绘制在起初确保的图像区域里,并显示出来。如图1-3,用红,绿,蓝各色标记的那样,直到等到某个键被按下。


 // (4)学习参数的生成
 for (i= 0; i< s; i++) {
    data[i* 2] = float (pts[i].x) / size;
    data[i* 2 + 1] = float (pts[i].y) / size;
 }
  cvInitMatHeader(&data_mat, s, 2, CV_32FC1, data);
  cvInitMatHeader(&res_mat, s, 1, CV_32SC1, res);
  criteria=cvTermCriteria(CV_TERMCRIT_EPS, 1000, FLT_EPSILON);
  param=CvSVMParams (CvSVM::C_SVC, CvSVM::RBF, 10.0, 8.0, 1.0, 10.0, 0.5, 0.1, NULL, criteria);

SVM种类:CvSVM::C_SVC

Kernel的种类:CvSVM::RBF

degree:10.0(此次不使用)

gamma:8.0

coef0:1.0(此次不使用)

C:10.0

nu:0.5(此次不使用)

p:0.1(此次不使用)

然后对训练数据正规化处理,并放在CvMat型的数组里。


 // (5)SVM学习
  svm.train(&data_mat, &res_mat, NULL, NULL, param);
利用训练数据和确定的学习参数,进行SVM学习。
 // (6)学习结果的绘图
 for (i= 0; i< size; i++) {
   for(j= 0; j< size; j++) {
     CvMatm;
     floatret= 0.0;
     floata[] = { float (j) / size, float (i) / size};
      cvInitMatHeader(&m, 1, 2, CV_32FC1, a);
      ret=svm.predict(&m);
     switch((int) ret) {
     case1:
        rcolor= CV_RGB(100, 0, 0);
       break;
     case 2:
        rcolor= CV_RGB(0, 100, 0);
       break;
     case 3:
        rcolor= CV_RGB(0, 0, 100);
       break;
     }
      cvSet2D(img, i, j, rcolor);
   }
 }

为了显示学习结果,通过输入图像区域的所有像素(特征向量)并进行分类。然后对输入像素用所属等级的颜色绘图。

 

 // (7)训练数据的再绘制
 for (i= 0; i< s; i++) {
   CvScalar rcolor;
   switch (res[i]) {
   case 1:
      rcolor= CV_RGB(255, 0, 0);
     break;
   case 2:
      rcolor= CV_RGB(0, 255, 0);
     break;
   case 3:
      rcolor= CV_RGB(0, 0, 255);
     break;
   }
    cvLine(img, cvPoint(pts[i].x- 2, pts[i].y- 2), cvPoint(pts[i].x+ 2, pts[i].y+ 2), rcolor);
    cvLine(img, cvPoint(pts[i].x+ 2, pts[i].y- 2), cvPoint(pts[i].x- 2, pts[i].y+ 2), rcolor);
 }
将训练数据在结果图像上重复的绘制出来。
 // (8)支持向量的绘制
  sv_num= svm.get_support_vector_count();
 for (i= 0; i< sv_num; i++) {
    support= svm.get_support_vector(i);
    cvCircle(img, cvPoint((int) (support[0] * size), (int) (support[1] * size)), 5, CV_RGB(200, 200, 200));
 }
用白色的圆圈对支持向量作标记。
 // (9)图像的显示
  cvNamedWindow("SVM", CV_WINDOW_AUTOSIZE);
  cvShowImage("SVM", img);
  cvWaitKey(0);
  cvDestroyWindow("SVM");
  cvReleaseImage(&img);
 return 0;
}显示实际处理结果的图像,直到某个键被按下为止。

 

第二个图像各像素值转化成特征向量的SVM学习

读取学习用的图像,并将像素值转化成特征向量进行SVM学习。

#include <cv.h>
#include<highgui.h>
#include<ml.h>
#include<stdio.h>

int main(int argc, char**argv)
{
 int i, j, ii, jj;
 int width= 28, height= 30;      /*
样本图像的尺寸大小*/
 int image_dim= width* height;
 int pimage_num= 500;         /*
正样本数*/
 int nimage_num= 1000;        /*负样本数*/
 int all_image_num= pimage_num+ nimage_num;
 IplImage *img_org;
 IplImage *sample_img;
 int res[all_image_num];
 float data[all_image_num* image_dim];
 CvMat data_mat, res_mat;
 CvTermCriteria criteria;
 CvSVM svm= CvSVM ();
 CvSVMParams param;
 char filename[64];

 // (1)
读取正样本数据
 for (i= 0; i< pimage_num; i++) {
    sprintf(filename, "positive/%03d.png", i);
    img_org=cvLoadImage(filename, CV_LOAD_IMAGE_GRAYSCALE);
    sample_img= cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    cvResize(img_org, sample_img);
    cvSmooth(sample_img, sample_img, CV_GAUSSIAN, 3, 0, 0, 0);
   for (ii= 0; ii< height; ii++) {
     for (jj= 0; jj< width; jj++) {
        data[i* image_dim+ (ii* width) + jj] =
         float ((int) ((uchar) (sample_img->imageData[ii* sample_img->widthStep+ jj])) / 255.0);
     }
   }
    res[i] = 1;
 }

    读取正样本的图像群,将各像素值转化成float数据类型。为了方便,预先在"positive/目录下准备了正样本图像,图像名用3位连续的数字名标识。首先,将读取的各图像转换成同一尺寸大小(28×30),为了减轻噪声的影响,对图像作了平滑化处理。然后,为了利用各像素亮度值(这里的图像作为等级图像被读取)的特征向量,将它变换成了数组。总之,对于一张图像的特征向量(图像宽度X图像长度),准备了和样本图像张数相同的数量。"1"表示利用此特征向量的判别数值。此外还使用500张脸部图像的正样本(基本上是正面脸部图像,没有侧面的)。

    在OpenCV里实装了利用haar-like特征的物体检测算法,由于利用它检测脸部的精度和处理速度都很不错,虽然脸部图像检测没有太大意义,但从获取样本的难易程度和理解程度考虑,此次利用脸部图像进行学习。


 // (2) 读取负样本数据
  j= i;
 for (i= j; i< j+ nimage_num; i++) {
    sprintf(filename, "negative/%03d.jpg", i- j);
    img_org= cvLoadImage(filename, CV_LOAD_IMAGE_GRAYSCALE);
    sample_img= cvCreateImage(cvSize(width, height), IPL_DEPTH_8U, 1);
    cvResize(img_org, sample_img);
    cvSmooth(sample_img, sample_img, CV_GAUSSIAN, 3, 0, 0, 0);
   for (ii= 0; ii< height; ii++) {
     for (jj= 0; jj< width; jj++) {
        data[i* image_dim+ (ii* width) + jj] =
         float ((int) ((uchar) (sample_img->imageData[ii* sample_img->widthStep+ jj])) / 255.0);
     }
   }
    res[i] = 0;
 }

读取负样本的图像群,和正样本一样,将它们转化成数组,并用"0"表示利用该特征向量的判别数值。另外使用1000张任意图像的负样本(全部是脸部以外的图像)。


 // (3)SVM学习数据和参数的初始化
  cvInitMatHeader(&data_mat, all_image_num, image_dim, CV_32FC1, data);
  cvInitMatHeader(&res_mat, all_image_num, 1, CV_32SC1, res);
  criteria=cvTermCriteria(CV_TERMCRIT_EPS, 1000, FLT_EPSILON);
  param=CvSVMParams(CvSVM::C_SVC, CvSVM::RBF, 10.0, 0.09, 1.0, 10.0, 0.5, 1.0, NULL, criteria);
样本图像的像素值数组和判别值数组进行行列变换。为了样本学习对参数作了初始化处理。由于指定了非常适合的参数,也就有必要设定相应合适的参数。


 // (4)SVM学习和数据保存
  svm.train(&data_mat, &res_mat, NULL, NULL, param);
  svm.save("svm_image.xml");

  cvReleaseImage(&img_org);
  cvReleaseImage(&sample_img);
 return 0;
}
利用正负样本的像素值和被指定的参数,根据svm.train()方式进行SVM学习。样本数:正样本500,负样本1000,特征向量: 28×30=840维。学习的SVM参数用根据svm.save()方法的XML形式的文件保存。如此页开始部分讲述的那样,为了使用保存和下载功能,有必要对OpenCV源代码作修改。

第三个  根据图像各像素值转化成特征向量的SVM检测物体(脸部)

读取用于学习的SVM参数,从相关图像中检测物体。

#include <cv.h>
#include<highgui.h>
#include<ml.h>
#include<stdio.h>


int main(int argc, char**argv)
{
 int i, j;
 int width= 28, height= 30;  /*样本图像的尺寸大小*/
 int image_dim= width* height;
 CvMat m;
 float a[image_dim];
 float ret= -1.0;
 float scale;
 IplImage *src,*src_color, *src_tmp;
 int sx, sy, tw, th;
 int stepx= 3, stepy= 3;
 double steps= 1.2;
 int iterate;
 CvSVM svm= CvSVM ();

 // (1)图像的读取
 if (argc< 2 ||
     (src= cvLoadImage(argv[1], CV_LOAD_IMAGE_GRAYSCALE))== 0 ||
     (src_color= cvLoadImage(argv[1], CV_LOAD_IMAGE_COLOR))== 0) {
   return -1;
 }

 // (2)SVM数据的读取
  svm.load("svm_image.xml");
 /* 对读取图像的每部分进行处理 */
  cvInitMatHeader(&m, 1, image_dim, CV_32FC1, NULL);
  tw=src->width;
  th=src->height;

 for (iterate= 0; iterate< 1; iterate++) {
// (3)缩小图像,并对当前图像部分作行列变换
    src_tmp= cvCreateImage(cvSize((int) (tw/ steps), (int) (th/ steps)), IPL_DEPTH_8U, 1);
    cvResize(src, src_tmp);
    tw=src_tmp->width;
    th=src_tmp->height;
   for (sy= 0; sy<= src_tmp->height- height; sy+= stepy) {
     for (sx= 0; sx<= src_tmp->width- width; sx+= stepx) {
       for (i= 0; i< height; i++) {
         for (j= 0; j< width; j++) {
            a[i* width+ j] =
             float ((int) ((uchar) (src_tmp->imageData[(i+ sy) * src_tmp->widthStep+ (j+ sx)])) / 255.0);
         }
       }
        cvSetData(&m, a, sizeof (float) * image_dim);
 // (4)根据SVM的判定和结果绘图
        ret= svm.predict(&m);
       if ((int) ret== 1) {
          scale= (float) src->width/ tw;
          cvRectangle(src_color, cvPoint((int) (sx* scale), (int) (sy* scale)),
                       cvPoint((int) ((sx+ width) * scale), (int) ((sy+ height) * scale)), CV_RGB(255, 0, 0), 2);
       }
     }
   }
    cvReleaseImage(&src_tmp);
 }

 // (5)显示检测出的结果图像
  cvNamedWindow("svm_predict", CV_WINDOW_AUTOSIZE);
  cvShowImage("svm_predict", src_color);
  cvWaitKey(0);
  cvDestroyWindow("svm_predict");
  cvReleaseImage(&src);
  cvReleaseImage(&src_color);
  cvReleaseImage(&src_tmp);

 return 0;
}

// (1)图像的读取
    通过命令参数从指定文件中读取由SVM确定的判别对象的图像。处理时虽然是针对灰色等级的图像,但为了显示最终结果另外也准备了彩色图像。

// (2)SVM数据的读取
预先从svm.load()方式得到的文件(此处为"xvm_image.xml")中读取用于学习的SVM参数。如本页开始部分讲述的那样,为了利用保存和下载功能,有必要修改OpenCV的源代码。

// (3)缩小图像,并对当前图像部分作行列变换
为了处理被读取图像的每部分,每stepx=3, stepy=3像素,width×height=28×30尺寸大小的图像部分的像素值代入到了数组中。另外为了使SVM适用,我们通过函数cvSetData()对数组1×(28×30)行列的数据进行设定。

// (4)根据SVM的判定和结果绘图
通过svm.predict()方式对进行行列变换后的图像作所属等级的判定。在这里,由于利用了前面叙述的学习参数文件,对处理对象的部分图像作是否是被学习物体(脸部)的判定。被判定为脸部的部分图像,用红色矩形显示。因为没有对参数的最优化,特殊处理方面(处理领域的选定和特征像素的提取等)作处理,检测的精度不高。而且,在对整张图像作处理时,由于特征向量可能很大而耗用了处理时间。此外,iterate变量虽只作了一次循环,但如果想检测出图像中存在很大差异的脸部的话,应随时缩小原有图像以进行相同的处理。

// (5)显示检测出的结果图像
红色矩形表示被绘制图像的检测结果,直到某个键被按下为止。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值