使用opencv的SVM實現車牌區域識別
阿新 • • 發佈:2019-01-01
一、前言
本文僅僅演示使用opencv2.4.6中已經定義好的SVM函式實現對車牌區域正負樣本的訓練,然後使用訓練好的SVM模型對測試樣本進行預測。
二、所使用的正負樣本
首先我將一系列圖片進行影象預處理、分割等一系列步驟,這部分內容可以參看《深入理解opencv 使用計算機視覺專案解析》,這樣從中挑選出100個正樣本(車牌區域)和70個負樣本(非車牌區域),大小均為144*33,分別存放於目錄F:\SVM_data\posdata與F:\SVM_data\negdata,如下所示:上圖中圖片檔案的命名是為了方便讀入資料,按ctrl+A全選,按F2即可快捷統一地修改名字,當然用一個txt檔案儲存這些圖片的路徑到時再讀取也是可以的,但這裡就不這麼做了。
取另外6張圖片作為測試樣本,其中包含3個車牌區域與3個非車牌區域,測試樣本與前面訓練樣本無關係,為另外選取的。存放於目錄F:\SVM_data\testdata
三、程式碼簡介
其實思路很簡單,對於100張正樣本圖和70張負樣本圖片,首先每張圖片都會轉換為1維陣列,該陣列長度為4752(也即144乘33),也即將圖片中所有的畫素點值均作為特徵,當然也可以選取影象的其他特徵值,這樣每張圖片可得到1*4752矩陣,則將所有這些矩陣保存於一個170*4752的訓練矩陣中;其次建立相應標籤矩陣,為170*1大小,前100個數為1.0,後70個數為-1.0,也就是標記出訓練矩陣中資料的正負性。設定好SVM訓練引數,使用CvSVM::train()函式進行訓練,然後使用訓練得的模型對測試資料進行預測。程式碼如下:
#include "opencv2/core/core.hpp" #include "opencv2/highgui/highgui.hpp" #include "opencv2/ml/ml.hpp" using namespace cv; #include <iostream> int main() { int num = 1; char path[90]; Mat trainningMat; //行數為樣本數170,每個樣本含144*33=4752個數,也即有4752列 Mat inputImg; Mat p; //先載入100個正樣本 while(num < 101) { sprintf( path ,"F:\\SVM_data\\posdata\\0000 (%d).bmp" , num); inputImg = imread(path,-1); if (inputImg.empty()) { std::cerr<<"無法載入正樣本!"; return -1; } p = inputImg.reshape(1,1); p.convertTo(p, CV_32FC1); trainningMat.push_back(p); num++; } num = 1; //再載入70個負樣本 while(num < 71) { sprintf( path ,"F:\\SVM_data\\negdata\\0000 (%d).bmp" , num); inputImg = imread(path,-1); if (inputImg.empty()) { std::cerr<<"無法載入負樣本!"; return -1; } p = inputImg.reshape(1,1); p.convertTo(p, CV_32FC1); trainningMat.push_back(p); num++; } Mat label(170,1,CV_32FC1); //與訓練資料相應的標籤,當然在txt中寫資料再讀出來也可以 for (int i = 0 ;i<label.rows ;++ i) { if( i < 100) label.at<float>(i,0) = 1.0; else label.at<float>(i,0) = -1.0; } CvSVM classifier; CvSVMParams SVM_params; SVM_params.kernel_type = CvSVM::LINEAR; //使用線性劃分 classifier.train(trainningMat,label ,Mat(),Mat(),SVM_params); //SVM訓練 vector<Mat> testdata; //定義測試資料 num = 1; while(num < 4) { sprintf( path ,"F:\\SVM_data\\testdata\\postest%d.bmp" , num); inputImg = imread(path,-1); if (inputImg.empty()) { std::cerr<<"無法載入正測試樣本!"; return -1; } p = inputImg.reshape(1,1); p.convertTo(p, CV_32FC1); testdata.push_back(p); num++ ; } num = 1; while(num < 4) { sprintf( path ,"F:\\SVM_data\\testdata\\negtest%d.bmp" , num); inputImg = imread(path,-1); if (inputImg.empty()) { std::cerr<<"無法載入負測試樣本!"; return -1; } p = inputImg.reshape(1,1); p.convertTo(p, CV_32FC1); testdata.push_back(p); num++ ; } for (int i = 0;i < testdata.size() ; ++i) { std::cout<<"測試樣本"<<i+1<<"的測試結果為:" <<(int)classifier.predict( testdata[i] )<<"\n"; } return 0; }
四、結果分析
程式執行結果如下:
可從結果看出,前3個測試樣本為正樣本,後3個樣本為負樣本,所訓練的模型可以正確地預測測試樣本是否為車牌區域