条件:opencv-4.5,QT
一、准备数据集
下载车牌相关字符样本用于训练和测试,本文使用14个汉字样本和34个数字跟字母样本,每个字符样本数为40,样本尺寸为28*28。
二、计算样本HOG特征
方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子。它通过计算和统计图像局部区域的梯度方向直方图来构成特征。具体内容详见以下链接:
OpenCV中的HOGDescriptor类可用于计算HOG特征。在计算HOG特征前需确定HOGDescriptor实例初始化参数,如下图,为HOGDescriptor类hog实例初始化设置参数。HOG特征描述子参数之间必须满足一定的关系,确保能够遍历整个特征提取窗口。
(1)blockSize的宽度和高度要分别能被cellSize的宽度和高度整除。
( 2 ) winSize 减 去 blockSize 的 宽 度 和 高 度 后 , 要 能 被blockStride对应的宽度和高度整除。
由于本例所使用样本为28*28,为满足以上要求,各项参数设置如下:
HOGDescriptor hog(Size(28,28),Size(4,4),Size(8,8),Size(4,4),9,1,-1);
调用HOGDescriptor类的compute方法计算HOG特征,得到特征描述子descriptors(列向量)
hog.compute(image,descriptors);//image为读取的单个样本,灰度图片
将descriptors写入push_back到temp(Mat) ,再将temp转为行向量,然后temp压入trainData,最后形成(行列数为样本数*特征数的矩阵),这里trainData大小为1920*144。
temp.push_back(descriptors);
trainData.push_back(temp.reshape(0,1));
然后需要生成标签向量,opencv SVM 接收类型为int的向量作为responses,
vector<int> trainLabel;
trainLabel.insert(trainLabel.begin()+ int(40*i),40,int(i));//i为对应的标签数据
三、设置SVM参数
由于是做分类任务,因此类型选择C_SVC,然后选择高斯核(RBF)作为kernel,因而需要设置参数Gamma
cv::Ptr<cv::ml::SVM> SVMmodel = cv::ml::SVM::create();
SVMmodel->setType(SVM::C_SVC);
SVMmodel->setKernel(SVM::RBF);
SVMmodel->setC(48);
SVMmodel->setGamma(0.5);
设好参数后开始训练模型
SVMmodel->train(traindata,cv::ml::ROW_SAMPLE,trainlabel);
SVMmodel->save("SVM_Recognization.xml");//保存模型
在模型训练后,本文通过测试集测试其识别的准确率为92%。在实际车牌识别中也可以准确识别(车牌提取,分割处理后)
效果如下:
QString MainWindow::svmTrain(cv::Mat &src)
{
using namespace std;
using namespace cv;
using namespace cv::ml;
string province[14] = {"云","皖","苏","辽","闽","黑","京","川","沪","浙","湘","粤","陕","鲁"};
string numcha[34] = {"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H",
"J","K","L","M","N","P","Q","R","S","T","U","V","W","X","Y","Z"};
Mat traindata,testData,testSample;
std::vector<int> trainlabel;
src.copyTo(testData);
HogGenerate(traindata,trainlabel,testData,testSample);
cv::Ptr<cv::ml::SVM> SVMmodel = cv::ml::SVM::create();
SVMmodel->setType(SVM::C_SVC);
SVMmodel->setKernel(SVM::RBF);
SVMmodel->setC(48);
SVMmodel->setGamma(0.5);
cout << "svm !!! " << endl;
SVMmodel->train(traindata,cv::ml::ROW_SAMPLE,trainlabel);
SVMmodel->save("SVM_Recognization.xml");//保存模型文件到项目的目录下
Mat result;
cout << "testSample Row:" << testSample.rows << " cols: " << testSample.cols << endl;
SVMmodel->predict(testSample,result);
int preIndex = int(result.at<float>(0));
if(preIndex<=13){
return QString::fromStdString(province[preIndex]);
}
else{
return QString::fromStdString(numcha[preIndex-14]);
}
}
void MainWindow::HogGenerate(cv::Mat &trainData, std::vector<int> &trainLabel,cv::Mat &testData,cv::Mat &testSample)
{
using namespace cv;
using namespace std;
vector<string> PlateChar;
string province[14] = {"云","皖","苏","辽","闽","黑","京","川","沪","浙","湘","粤","陕","鲁"};
string numcha[34] = {"0","1","2","3","4","5","6","7","8","9","A","B","C","D","E","F","G","H",
"J","K","L","M","N","P","Q","R","S","T","U","V","W","X","Y","Z"};
size_t samples = 48;
string Chapath = "/home/ghoson-x/Desktop/qt/CarImage/data/train_28_28/";
string Chipath = "/home/ghoson-x/Desktop/qt/CarImage/data2/train_28_28/";
vector<float> descriptors;
HOGDescriptor hog(Size(28,28),Size(4,4),Size(8,8),Size(4,4),9,1,-1);
string imagePath,imageName;
for(size_t i=0;i<samples;++i){
if(i<=13){
imageName = province[i];
imagePath = Chipath;
}
else{
imageName = numcha[i-14];
imagePath = Chapath;
}
imagePath.append(imageName).append("/").append(imageName).append("_");
for(int j=1;j<=40;++j){
string * ImagePath = new string;
ImagePath->append(imagePath);
ImagePath->append((QString::number(j)).toStdString()).append(".jpg");
Mat tmp = imread(ImagePath->data(),IMREAD_GRAYSCALE);
delete ImagePath;
if(tmp.empty()){
break;
}
Mat temp;
hog.compute(tmp,descriptors);
temp.push_back(descriptors);
trainData.push_back(temp.reshape(0,1));
temp.release();
tmp.release();
}
trainLabel.insert(trainLabel.begin()+ int(40*i),40,int(i));
}
Mat test;
hog.compute(testData,descriptors);
test.push_back(descriptors);
testSample.push_back(test.reshape(0,1));
}