第一个支持向量机(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)显示检测出的结果图像
红色矩形表示被绘制图像的检测结果,直到某个键被按下为止。