前面的文章中我們主要介紹了車牌定位的相關技術,但是定位出來的相關區域可能並非是真實的車牌區域,EasyPR通過SVM支持向量機,一種機器學習算法來判定截取的圖塊是否是真的“車牌”,本節主要對相關的技術做詳細的介紹。
注:SVM相關內容可以詳細參考周志華老師的《機器學習》和一篇名為《支持向量機通俗導論(理解SVM的三層境界)》的文章。
一、SVM簡介
支持向量機,其英文名為 support vector machine,故一般簡稱SVM,通俗來講,它是一種二類分類模型,其基本模型定義為特征空間上的間隔最大的線性分類器,其學習策略便是間隔最大化,最終可轉化為一個凸二次規划問題的求解。
如上圖所示: 距離超平面最近的幾個訓練樣本點被稱之為支持向量(support vector),兩個異類支持向量到超平面的距離之和被稱之為間隔。對應的SVM的基本型如下:
上述問題本身是一個凸二次規划問題,通過拉格朗日乘子法可得到其對偶問題。
上述兩個公式非常重要,簡直是核心公式。
得到上面的兩個公式,再帶回L中把去w和b消掉,得到如下公式:
從對偶問題解出的拉格朗日乘子對應着訓練樣本(xi,yi)。注意到上式中有不等式約束,因此上述過程需滿足KKT條件,即要求
支持向量機有一個重要的性質,訓練完成后,大部分的訓練樣本都不需要保留,最終模型僅與支持向量有關。
接下來就是如何求解這個二次規划問題,可使用通用的二次規划算法來求解,也可以利用問題本身的特性,采用SMO算法來求解。
上面是對SVM的理論做了極其簡單的介紹,對支持向量機理論感興趣的童鞋可以做進一步的研究。
二、車牌判別
opencv中對SVM進行了集成,調用opencv的SVM接口是十分方便的,對於定位的車牌區域,首先獲取其車牌特征,這邊采用的是LBP算子,具體內容上一節做了詳細的介紹。SVM調用的類是opencv 機器學習模塊中的SVM類,具體的代碼如下所示:

1 typedef void (*svmCallback)(const cv::Mat& image, cv::Mat& features); 2 cv::Ptr<ml::SVM> svm_; 3 svmCallback extractFeature; 4 5 PlateJudge::PlateJudge() { 6 svm_ = ml::SVM::load<ml::SVM>(kDefaultSvmPath); 7 extractFeature = getLBPFeatures; 8 } 9 10 void PlateJudge::LoadModel(std::string path) { 11 if (path != std::string(kDefaultSvmPath)) { 12 if (!svm_->empty()) 13 svm_->clear(); 14 15 svm_ = ml::SVM::load<ml::SVM>(path); 16 } 17 } 18 19 int PlateJudge::plateJudge(const Mat &inMat, int &result) { 20 Mat features; 21 extractFeature(inMat, features); 22 float response = svm_->predict(features); 23 result = (int)response; 24 25 return 0; 26 }
通過 SVM::load() 函數加載訓練好的SVM參數文件,然后調用 predict()函數對當前區域進行判定,是否為車牌。
三、SVM訓練
svm訓練通過類SvmTrain 來進行,具體的訓練函數如下所示:

1 void SvmTrain::train() { 2 svm_ = cv::ml::SVM::create(); 3 svm_->setType(cv::ml::SVM::C_SVC); 4 svm_->setKernel(cv::ml::SVM::RBF); 5 svm_->setDegree(0.1); 6 svm_->setGamma(0.1); 7 svm_->setCoef0(0.1); 8 svm_->setC(1); 9 svm_->setNu(0.1); 10 svm_->setP(0.1); 11 svm_->setTermCriteria(cvTermCriteria(CV_TERMCRIT_ITER, 20000, 0.0001)); 12 13 auto train_data = tdata(); 14 15 fprintf(stdout, ">> Training SVM model, please wait...\n"); 16 long start = utils::getTimestamp(); 17 svm_->train(train_data); 18 19 long end = utils::getTimestamp(); 20 fprintf(stdout, ">> Training done. Time elapse: %ldms\n", end - start); 21 fprintf(stdout, ">> Saving model file...\n"); 22 svm_->save(svm_xml_); 23 24 fprintf(stdout, ">> Your SVM Model was saved to %s\n", svm_xml_); 25 fprintf(stdout, ">> Testing...\n"); 26 27 this->test(); 28 29 }
kernel_type:SVM的內核類型(4種):
CvSVM::POLY : 多項式內核:-- polynomial: (gamma*u'*v + coef0)^degree
CvSVM::RBF : 高斯核-- radial basis function: exp(-gamma*|u-v|^2)
CvSVM::SIGMOID:Sigmoid函數內核-- sigmoid: tanh(gamma*u'*v + coef0)
svm_type:指定SVM的類型(5種):
1、CvSVM::C_SVC : C類支撐向量分類機。 n類分組 (n≥2),容許用異常值處罰因子C進行不完全分類。
2、CvSVM::NU_SVC : 類支撐向量分類機。n類似然不完全分類的分類器。參數為
庖代C(其值在區間【0,1】中,nu越大,決定計划鴻溝越膩滑)。
3、CvSVM::ONE_CLASS : 單分類器,所有的練習數據提取自同一個類里,然后SVM建樹了一個分界線以分別該類在特點空間中所占區域和其它類在特點空間中所占區域。
4、CvSVM::EPS_SVR : 類支撐向量回歸機。練習集中的特點向量和擬合出來的超平面的間隔須要小於p。異常值處罰因子C被采取。
5、CvSVM::NU_SVR : 類支撐向量回歸機。
庖代了 p。
degree:內核函數(POLY)的參數degree。
gamma:內核函數(POLY/ RBF/ SIGMOID)的參數。
coef0:內核函數(POLY/ SIGMOID)的參數coef0。
Cvalue:SVM類型(C_SVC/ EPS_SVR/ NU_SVR)的參數C。
nu:SVM類型(NU_SVC/ ONE_CLASS/ NU_SVR)的參數 。
p:SVM類型(EPS_SVR)的參數。
class_weights:C_SVC中的可選權重,賦給指定的類,乘以C今后變成 class_weights*C 。所以這些權重影響不合類此外錯誤分類處罰項。權重越大,某一類此外誤分類數據的處罰項就越大。
term_crit:SVM的迭代練習過程的中斷前提,解決項目組受束縛二次最優題目。您可以指定的公差和或最大迭代次數。
對於訓練數據 tdata()的獲取,如下所示:

1 cv::Ptr<cv::ml::TrainData> SvmTrain::tdata() { 2 this->prepare(); 3 4 cv::Mat samples; 5 std::vector<int> responses; 6 7 for (auto f : train_file_list_) { 8 auto image = cv::imread(f.file); 9 if (!image.data) { 10 fprintf(stdout, ">> Invalid image: %s ignore.\n", f.file.c_str()); 11 continue; 12 } 13 cv::Mat feature; 14 getLBPFeatures(image, feature); 15 feature = feature.reshape(1, 1); 16 17 samples.push_back(feature); 18 responses.push_back(int(f.label)); 19 } 20 21 cv::Mat samples_, responses_; 22 samples.convertTo(samples_, CV_32FC1); 23 cv::Mat(responses).copyTo(responses_); 24 25 return cv::ml::TrainData::create(samples_, cv::ml::SampleTypes::ROW_SAMPLE, 26 responses_); 27 }
對於訓練得到的結果,將保存在 svm_xml_文件中。