SVM能实现功能即在给出的正负样本中找到一条分割线(面),将正负样本分割出来。而这条分割线(面)即我们所说的分类器,其记录的正样本的特征,以及和负样本的区别。当有新的样本过来时,则通过和分类器比较即可分辨出新的样本是否属于正样本行列。
以车辆识别为例,SVM的一般使用步骤如下:
1、获取正负样本。
前面说了SVM能够找到一条存在与正负样本之间的分割线(面),那么何为正负样本呢?
所谓正样本,即你人为的判断出包含所需信息的样本。如下图,在图片中,看到了车子,然后用标记工具将其标记,获得标记信息的记录文件,如下图中的caltechVehicle106640.jpg.StringMark。当然这种信息记录文件有很多种,根据需要生成不同的标记文件。标记文件中一般含有框框的位置信息,大小信息,文件的名称等等。而我们所说的正样本即框住的这部分图片内容,在SVM学习的时候,会根据标记信息记录文件里的位置、大小等特征来获取框框中的内容。
所谓负样本,即在非正样本。例如上面左图中的图片,除了红色框框住的区域不能作为负样本,图片中的其他区域均可以作为负样本。但是负样本不可随意,要尽量符合分类的场景。在车辆识别中,负样本尽量选取公路中的场景。如果负样本过于随意,则会造成训练成本加大,训练结果不准确。
2、获取训练结果
在获得了正负样本后,机器如何得到样本的特征,并进行分辨?
在获取了正样本后,机器本身并不知道正样本中的东西有什么特征。虽然人为的告诉了机器,这个是车子的图片,但是机器怎样记住车子的特征?而且每张样本中的车子肯定长的是有差别的,那么算法在学习样本的过程中是采用什么特征来分辨车子呢?
在实际的运用过程中,这些特征有很多,许多图片的一般性特征都可以做为学习的依据。比如图像的灰度、梯度、直方图、hog特征、haar特征等。只要告诉机器采用哪种特征来学习,那么算法本身会先计算出样本的这种特征,然后学习每幅图片的此类特征,并记录。
因为前面已经人为的给出了正负样本,那么机器在比较了正负样本的特征后,会得到一个分类结果,为一个.xml文件。而这个分类结果就是我们一个能够进行分类的分类器了。
3、进行分类
获取分类结果后,就可以将结果用于给新的图片进行分类。分类的时候也要采取与训练时相同的特征进行。即假如训练时采用的是haar特征,那么在分类时也要先得到新图片的haar特征,将得到的haar特征与分类结果进行比较,从而判断是否是车辆。
下面给出基于opencv的分类代码:
#include "opencv2/objdetect/objdetect.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include <opencv2/opencv.hpp> #include <iostream> #include <stdio.h> using namespace std; using namespace cv; /** 函数声明 */ void detectAndDisplay(Mat frame); /** 全局变量 */ string car_cascade_name = "put.xml"; //训练的结果文件 CascadeClassifier car_cascade; string window_name = "Capture - car detection"; RNG rng(12345); /** @主函数 */ int main(int argc, const char** argv) { Mat frame; VideoCapture video; VideoWriter markVideo; //用于存储识别车辆后的视频 // 加载级联分类器文件 if (!car_cascade.load(car_cascade_name)){ printf("--(!)Error loading\n"); return -1; }; //获取视频信息 double fourcc, fps, width, height; video.open("25.avi"); fourcc = video.get(CV_CAP_PROP_FOURCC); fps = video.get(CV_CAP_PROP_FPS); width = video.get(CV_CAP_PROP_FRAME_WIDTH); height = video.get(CV_CAP_PROP_FRAME_HEIGHT); markVideo.open("markVideo25.avi", fourcc, fps, Size(width, height), false); //新建一个视频,存储结果 if (video.isOpened() && markVideo.isOpened() ) { while(true) { video >> frame; resize(frame, frame, Size(280, 240)); //将图片缩小,加快检测速度 //对当前帧使用分类器进行检测 if (!frame.empty()){ detectAndDisplay(frame); markVideo << frame; } else{printf(" --(!) No captured frame -- Break!"); break;} if ((char)waitKey(1) == 'c') { video.release(); markVideo.release(); break; } } } return 0; } /** @函数 detectAndDisplay */ void detectAndDisplay(Mat frame) { std::vector<Rect> faces; Mat frame_gray; cvtColor(frame, frame_gray, CV_BGR2GRAY); equalizeHist(frame_gray, frame_gray); //直方图均衡化 //-- 多尺寸检测人脸 car_cascade.detectMultiScale(frame_gray, faces, 1.1, 3, 0 , Size(10,12),Size(100,120)); for (int i = 0; i < faces.size(); i++) { rectangle(frame, Point(faces[i].x, faces[i].y), Point(faces[i].x + faces[i].width, faces[i].y + faces[i].height), 0xff, 3); //画出识别后的框框 } //-- 显示结果图像 imshow(window_name, frame); }