基于SVM的车牌判别(二)

本文介绍了一个基于OpenCV的C++项目,使用SVM进行车牌识别。文章详细阐述了特征提取(自定义特征与HOG特征)、数据加载、模型训练和测试的过程。在测试中,HOG特征提取的SVM模型达到98%以上的准确率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

3 代码实现

本项目主要借助于OpenCV库,在Visual Studio 2017平台开发,用C++语言编写代码。代码主要分为四个模块,分别是特征提取,数据加载,模型训练以及模型测试。下面对这四个模块的代码进行一一解析。

3.1 特征提取

本项目在开发过程中,一共设计了两种特征提取方式,分别是自拟的特征提取和HOG特征提取。下面结合代码对这两种特征提取方式一一介绍。

自拟特征提取的思路是提取每行每列中二值化结果为255的个数。首先,将输入图像经过灰度化转化为灰度图,再经过自适应二值化转化为二值图,最后创建一个一维的矩阵用于存放统计的数据作为特征向量,如下所示。

Mat getFeatures(Mat src,int rows,int cols) {
 Mat grayImage;
 cvtColor(src, grayImage, COLOR_RGB2GRAY);
 Mat img_threshold;
 threshold(grayImage, img_threshold, 0, 255, THRESH_OTSU + THRESH_BINARY);
 Mat out = Mat::zeros(1, rows+cols, CV_8UC1);
 for (int h = 0; h < rows; h++) {
  for (int l = 0; l < cols; l++) {
   if (img_threshold.at<uchar>(h, l) == 255) {
    out.at<uchar>(h)++;
   }
  }
 }
 for (int l = 0; l < cols; l++) {
  for (int h = 0; h < rows; h++) {
   if (img_threshold.at<uchar>(h, l) == 255) {
    out.at<uchar>(rows + l)++;
   }
  }
 }
 return out;
}

HOG特征提取先要对图像大小进行裁剪,有几种效果较好的图像尺寸,本文选用的是128×64。接着,选取8×8的滑动窗口对图像计算特征,这里直接调用OpenCV中的内置函数。最后将提取的特征保存到矩阵out中,过程如下所示。

Mat HOGgetFeatures(Mat src){
 //these parameters work well
 HOGDescriptor hog(Size(128, 64), Size(16, 16), Size(8, 8), Size(8, 8), 3); 
 std::vector<float> descriptor;
 // resize input image to (128,64) for compute
 Size dsize = Size(128, 64);
 Mat trainImg = Mat(dsize, CV_32S);
 resize(src, trainImg, dsize);
 // compute descripter
 hog.compute(trainImg, descriptor, Size(8, 8));
 // copy the result
 Mat features(descriptor);
 Mat out;
 transpose(features, out);
 return out;
}

经过测试,发现用HOG特征提取的方式,提取到的特征输入SVM中训练,得到的模型准确率更高。因此,本项目最终采用的是HOG特征提取。

3.2 数据加载

数据加载包括从车牌数据加载和非车牌数据加载。本项目将车牌数据和非车牌数据分开保存在特定文件夹中,便于给数据打上标签。通过文件路径访问,从相应文件夹中依次读取数据,通过特征提取生成一维特征向量,同时根据数据所属类别生成一个数据标签,最后将这个一维特征和数据标签分别append()到两个大矩阵里。

void getPlate(Mat &trainingImages, vector<int>& trainingLabels, int getfeatureway) {
 string pattern_jpg = "C:\\Users\\nicor\\Desktop\\test_has\\*.jpg";    //从一个文件夹中读取所有图片
 vector<cv::String> image_files;
 cv::glob(pattern_jpg, image_files);
 if (image_files.size() == 0) {          //文件夹中没有车牌图片
  cout << "No image files[jpg]" << endl;
 }
 else {
  switch (getfeatureway)
  {
  case Binary:
   for (unsigned int frame = 0; frame < image_files.size(); ++frame) {//image_file.size()代表文件中总共的图片个数
    Mat src = imread(image_files[frame]);
    Mat out = getFeatures(src, src.rows, src.cols);
    out = out.reshape(1, 1);
    trainingImages.push_back(out);
    trainingLabels.push_back(1);
   }
   break;
  case HOG:
   for (unsigned int frame = 0; frame < image_files.size(); ++frame) {//image_file.size()代表文件中总共的图片个数
    Mat src = imread(image_files[frame]);
    Mat out = HOGgetFeatures(src);
    out = out.reshape(1, 1);
    trainingImages.push_back(out);
    trainingLabels.push_back(1);
   }
   break;
  case SIFT:
   break;
  case LBP:
   break;
  default:
   break;
  }
 }
}

这段代码主要利用先前定义的HOG特征提取函数,提取每张图像的特征,并reshape成一维向量,最后压栈到特征矩阵中。

这样,我们就完成了训练数据的加载,并生成了输入到模型中训练的特征矩阵和标签矩阵。

3.3 模型训练

训练时首先通过调用OpenCV中的一个机器学习库生成一个SVM对象,接着设置参数初始化这个SVM模型,参数的设置参照一些网上其他人的经验值。具体过程如下所示。

//训练模型
 cv::Ptr<cv::ml::SVM> svm_ = cv::ml::SVM::create();
 //SVM模型参数设置
 svm_->setType(ml::SVM::C_SVC);
 svm_->setKernel(cv::ml::SVM::RBF);
 svm_->setDegree(0.1);
 svm_->setGamma(0.1);
 svm_->setCoef0(0.1);
 svm_->setC(1);
 svm_->setNu(0.1);
 svm_->setP(0.1);
 svm_->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER, 20000, 0.0001));

接着,加载数据,并将特征矩阵和标签矩阵转换格式后,生成一个训练数据集,最终输入到SVM中进行训练,过程如下所示。

Mat classes;    //标签
Mat trainingData;    //数据

Mat trainingImages;    //训练数据
vector<int> trainingLabels;     //数据标签
//特征提取方式
getPlate(trainingImages, trainingLabels, HOG);
getNoPlate(trainingImages, trainingLabels, HOG);

Mat(trainingImages).copyTo(trainingData);
trainingData.convertTo(trainingData, CV_32FC1);   //格式转化后的数据
Mat(trainingLabels).copyTo(classes);
classes.convertTo(classes, CV_32SC1);    //格式转换后的标签
Ptr<ml::TrainData> trainData=ml::TrainData::create(trainingData, ml::SampleTypes::ROW_SAMPLE, classes);

最终训练模型时,调用的是CvSVM中的trainAuto函数,这个函数会根据输入的数据自适应训练,自动调整参数,效果好,但耗费时间较长。

svm_->trainAuto(trainData, 10,                 //自适应训练,自动调整参数,效果好,耗费时间大
  ml::SVM::getDefaultGrid(ml::SVM::C),ml::SVM::getDefaultGrid(ml::SVM::GAMMA), 
  ml::SVM::getDefaultGrid(ml::SVM::P),ml::SVM::getDefaultGrid(ml::SVM::NU), 
  ml::SVM::getDefaultGrid(ml::SVM::COEF),ml::SVM::getDefaultGrid(ml::SVM::DEGREE), true);

3.4 模型测试

   Ptr<ml::SVM> svm_ = cv::ml::StatModel::load<cv::ml::SVM>("C:\\Users\\nicor\\Desktop\\svm_HOG.xml");  //加载训练好的模型
   int response = 0;
   for (unsigned int frame = 0; frame < image_files.size(); ++frame) {//image_file.size()代表文件中总共的图片个数
    Mat src = imread(image_files[frame]);
    Mat test = HOGgetFeatures(src);
    Mat test_;
    test.convertTo(test_, CV_32FC1);
    response = 0;
    response = (int)(svm_->predict(test_));
    if (isplate) {
     if (response) {
      correct_cnt++;
     }
    }
    else {
     if (!response) {
      correct_cnt++;
     }
    }

模型训练结束后,会生成一个.xml文件。测试时,我们首先要将该权重模型加载到程序中,之后从测试集里依次读取图像,通过HOG特征提取生成一维特征向量,最后将该一维特征向量输入到模型中,做一次前向传播得到输出。输出会有两个结果,0或1,分别代表预测的类别为车牌和非车牌。经过统计,我们就可以大概得出模型预测的准确率。

4 测试结果

4.1 SVM模型准确率测试

训练结束后,我们调用代码中的模型测试模块,对训练得到的SVM权重模型的准确率进行测试。跟训练集一样,测试集同样分为车牌测试集和非车牌测试集。在代码中加载该权重模型,分别对两个测试集进行判别,得出如图4-1所示结果。可以看到,训练得到的SVM权重模型对车牌数据和非车牌数据的误判都很少,其中利用HOG特征提取训练所得的模型准确率达到了98%以上。

在这里插入图片描述
在这里插入图片描述
从测试集中另取一张原始车辆图像,按照车牌识别流程对图像中的车牌进行提取。经过高斯模糊,灰度化,边缘检测,二值化,闭操作,取轮廓,轮廓外接矩形框,按车牌尺寸筛选车牌区域等一系列操作后,得到如图4-2所示结果。
在这里插入图片描述

此时,提取的区域包含车牌区域和非车牌区域,利用常规方法已很难将它们分离开来,此时就用到了我们上面训练所得的SVM模型进行判别。将这些疑似区域旋转仿射至水平后,尺寸统一调整为136x36,经过HOG特征提取后生成相应一维特征向量,输入SVM模型中。根据模型输出结果,若为1,则表示为车牌;若为0,则是非车牌。最终从图像中提取的车牌如图4-3所示。
在这里插入图片描述

此外,本车牌定位算法配合SVM分类模型有较好的适用性,在多车辆复杂图像中也能有效地提取到车牌区域,结果如图4-4所示。
在这里插入图片描述

### 基于MATLAB的车牌识别项目 #### 一、图像预处理 为了提高后续步骤中的准确性,在开始之前需要对输入图片进行必要的预处理工作。这一步骤主要包括转换成灰度图以及去除噪声等内容。 ```matlab % 读入彩色图像并显示原图 img = imread('car_plate.jpg'); imshow(img); title('Original Image'); % 将RGB颜色空间转为灰度级表示形式 grayImg = rgb2gray(img); figure; imshow(grayImg); title('Grayscale Image'); % 应用高斯滤波器来减少随机干扰因素的影响 filteredGrayImg = imgaussfilt(grayImg, 2); % 参数2代表标准差大小 figure; imshow(filteredGrayImg); title('Filtered Grayscale Image'); ``` #### 车牌区域检测 通过边缘检测算法找到可能存在的矩形框位置作为候选区;接着利用形态学运算进一步优化这些边界线,最后筛选出最有可能属于汽车牌照的那个部分。 ```matlab % 进行Canny Edge Detection得到初步轮廓信息 edgeImage = edge(filteredGrayImg,'canny',[0.15 0.3]); se = strel('square',3); dilatedEdgeImage = imdilate(edgeImage, se); figure; imshow(dilatedEdgeImage); title('Dilated Edges'); % 查找连通域,并计算其面积和周长比例特征向量用于判断是否为车牌形状 stats = regionprops('table', dilatedEdgeImage, 'Area','Perimeter','BoundingBox'); minRatio = inf; plateBoxIdx = -1; for i=1:height(stats) area = stats.Area(i); perimeter = stats.Perimeter(i); ratio = (perimeter * perimeter)/area; if(ratio<minRatio && ratio>4 && ratio<8) % 设定合理的阈值范围 minRatio=ratio; plateBoxIdx=i; end end if(plateBoxIdx~=-1) bbox = round([stats.BoundingBox(plateBoxIdx,:)]); else error('No valid license plate detected!'); end croppedPlateRegion = imcrop(filteredGrayImg,bbox); figure; imshow(croppedPlateRegion); title('Detected License Plate Region'); ``` #### 三、字符分割与识别 当获取到清晰可见的目标对象之后,则可以尝试对其进行逐个文字切分再分别送入训练好的分类器完成最终判别过程。 ```matlab % 对裁剪出来的子图再次做一次腐蚀膨胀操作以便更好地分离各个字母/数字单元格 binaryPlateRegion=~imbinarize(croppedPlateRegion,.7); erodedBinaryPlateRegion=bwareaopen(binaryPlateRegion,50); seSquare = strel('square',2); finalCharSegResult = ~imdilate(erodedBinaryPlateRegion,seSquare); figure; imshow(finalCharSegResult); title('Character Segmentation Result'); % 下面这部分假设已经准备好了一个预先训练好支持多类别的SVM模型文件svmModel.mat load svmModel.mat;% 加载保存下来的SVM模型参数数据包 charLabels={'A','B','C','D','E','F','G','H','I',... 'J','K','L','M','N','O','P','Q','R',... 'S','T','U','V','W','X','Y','Z'}; [charPositions,charWidths]=bwlabel(~finalCharSegResult); predictedChars=''; for idx=1:max(charPositions(:)) charPatch=extractRectangularPatchFromBWLabelledImage(idx,... finalCharSegResult,... croppedPlateRegion); featureVector=getFeatureVectorOfSingleChar(charPatch); [~,predIdx]=max(svmclassify(svmModel,featureVector)); predictedChars=[predictedChars charLabels{predIdx}]; end disp(['Predicted Characters on the Detected LP:',upper(predictedChars)]); ``` 上述代码片段展示了如何使用MATLAB构建一个简单的车牌识别系统框架[^1][^2][^3]。需要注意的是实际应用中还需要考虑更多细节问题比如光照变化影响下的鲁棒性改进措施等。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值