OpenCV自学笔记20. 基于SVM和神经网络的车牌识别(四)

来源:互联网 发布:java web毕业论文 编辑:程序博客网 时间:2024/05/16 10:03

基于SVM和神经网络的车牌识别(四)

本系列文章参考自《深入理解OpenCV实用计算机视觉项目解析》仅作学习用途

特征提取与识别

在上一篇中,我们得到了车牌的每一个数字,在本篇将对利用神经网络识别出每一个数字

Step1. 首先提取特征,特征主要包含几个部分:水平方向和竖直方向的累积直方图、以及低分辨率的图像样本

Step2. 提取特征后,我们将创建分类器,并开始训练

Step3. 最后进行预测

我们的样本数据来自一个xml文件:

这个xml 文件包含多组训练数据,其中:
TrainingDataF5 表示 5*5 的低分辨率图像
TrainingDataF10 表示 10*10 的低分辨率图像
TrainingDataF15 表示 15*15 的低分辨率图像
TrainingDataF20 表示 20*20 的低分辨率图像

这里写图片描述

这个过程,完整的程序如下:

#include <iostream>#include "opencv2/imgproc.hpp"#include "opencv2/imgcodecs.hpp"#include "opencv2/highgui.hpp"#include <ml.hpp>using namespace std;using namespace cv;using namespace ml;const int HORIZONTAL = 1; // 水平方向,在创建累积直方图时,需要用到const int VERTICAL = 0; // 竖直方向,在创建累积直方图时,需要用到const char strCharacters[] = {     '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',     'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K', 'L', 'M', };const int numCharacters = 30; // 一共30个字符const int charSize = 20;/* 累积直方图 */Mat ProjectHistogram(Mat img, int t) {    // 如果是竖直方向,t = 0,sz = img.cols    // 总之,选取长宽的最大值,以便创建完全包含数字图像的矩阵    int sz = (t) ? img.rows : img.cols;     Mat mhist = Mat::zeros(1, sz, CV_32F); // mhist是一个1 * sz的矩阵    // 按行/列 统计非零像素值的个数,并保存在mhist中    for (int j = 0; j < sz; j++) {        Mat data = (t) ? img.row(j) : img.col(j);        mhist.at<float>(j) = countNonZero(data);     }    double min, max;    minMaxLoc(mhist, &min, &max); // 找到矩阵中的最大值,以便归一化    if (max > 0) {        // 矩阵的每一个元素都除以最大值,这正是归一化操作        mhist.convertTo(mhist, -1, 1.0f/max, 0);        return mhist;    }}/*   创建特征矩阵   水平方向累积直方图 + 竖直方向累积直方图 + 低分辨率图像*/Mat features(Mat in, int sizeData) {    // 分别在水平方向和垂直方向上 创建累积直方图    Mat vhist = ProjectHistogram(in, VERTICAL);    Mat hhist = ProjectHistogram(in, HORIZONTAL);    // 低分辨率图像    // 低分辨率图像中的每一个像素都将被保存在特征矩阵中    Mat lowData;    resize(in, lowData, Size(sizeData, sizeData));    // 特征矩阵的列数    int numCols = vhist.cols + hhist.cols + lowData.cols * lowData.cols;    Mat out = Mat::zeros(1, numCols, CV_32F); // 创建特征矩阵    // 向特征矩阵赋值    int j = 0;    // 首先把水平方向累积直方图的值,存到特征矩阵中    for (int i = 0; i < vhist.cols; i++) {        out.at<float>(j) = vhist.at<float>(i);        j++;    }    // 然后把竖直方向累积直方图的值,存到特征矩阵中    for (int i = 0; i < hhist.cols; i++) {        out.at<float>(j) = hhist.at<float>(i);        j++;    }    // 最后把低分辨率图像的像素值,存到特征矩阵中    for (int x = 0; x < lowData.cols; x++) {        for (int y = 0; y < lowData.rows; y++){            out.at<float>(j) = (float)lowData.at<unsigned char>(x, y);            j++;        }    }    return out;}/*   训练和识别   注:为了测试方便,我把训练和识别写到一个函数里了     正常情况下,应该单独封装为函数*/int classificationANN(Mat TrainingData, Mat classes, int nlayers, Mat f){    // step1. 生成训练数据    Mat trainClasses;    trainClasses.create(TrainingData.rows, numCharacters, CV_32FC1);    for (int i = 0; i < trainClasses.rows; i++) {        for (int j = 0; j < trainClasses.cols; j++) {            if (j == classes.at<int>(i))                trainClasses.at<float>(i, j) = 1;            else                trainClasses.at<float>(i, j) = 0;        }    }    Ptr<TrainData> trainingData = TrainData::create(TrainingData, ROW_SAMPLE, trainClasses);    // step2. 创建分类器    Mat layers(1, 3, CV_32SC1);    layers.at<int>(0) = TrainingData.cols;    layers.at<int>(1) = nlayers;    layers.at<int>(2) = numCharacters;    Ptr<ANN_MLP> ann = ANN_MLP::create();    ann->setLayerSizes(layers); // 设置层数    ann->setActivationFunction(ANN_MLP::SIGMOID_SYM, 1, 1); // 设置激励函数    // step3. 训练    ann->train(trainingData);    // step4. 预测    // 处理输入的特征Mat f    Mat src;    src.create(45, 77, CV_32FC1);    resize(f, src, src.size(), 0, 0, INTER_CUBIC);    src.convertTo(src, CV_32FC1);    src = src.reshape(1, 1);    Mat output(1, numCharacters, CV_32FC1);    ann->predict(f, output); // 开始预测    Point maxLoc;    double maxVal;    // output为一个向量,向量的每一个元素反映了输入样本属于每个类别的概率    minMaxLoc(output, 0, &maxVal, 0, &maxLoc); // 找到最大概率    // 返回字符在strCharacters[]数组中的索引    return maxLoc.x; }int main() {    // Step0. 预处理数字图像    Mat src = imread("7.jpg", 0); // 读取灰度图    int h = src.rows;    int w = src.cols;    Mat transformMat = Mat::eye(2, 3, CV_32F); // 创建对角阵    int m = max(w, h);    transformMat.at<float>(0, 2) = m / 2 - w / 2;    transformMat.at<float>(1, 2) = m / 2 - h / 2;    // 仿射变换    Mat warpImage(m, m, src.type());    warpAffine(src, warpImage, transformMat, warpImage.size(), INTER_LINEAR, BORDER_CONSTANT, Scalar(0));    Mat out;    resize(warpImage, out, Size(charSize, charSize)); // 重新调整大小    imshow("out", out);    // Step1. 读取训练数据OCR.xml    FileStorage fs;    fs.open("OCR.xml", FileStorage::READ);    Mat TrainingData;    Mat Classes;    fs["TrainingDataF5"] >> TrainingData;    fs["classes"] >> Classes;    // Step2. 创建特征矩阵    Mat f = features(out, 5);    // Step3. 训练 + 测试(写到一个函数里了)    int index = classificationANN(TrainingData, Classes, 10, f);    cout << strCharacters[index] << endl;    waitKey();    return 0;}

实验结果如下:

这里写图片描述

把上一节切割得到的数字,都进行一遍实验,结果如下

发现识别并不准确,
7 被识别为 V
5 被识别为 S
D 被识别为 J

这里写图片描述

参考

  1. 累积直方图:http://blog.csdn.net/combfish/article/details/45056239

系列文章

OpenCV自学笔记17. 基于SVM和神经网络的车牌识别(一)
OpenCV自学笔记18. 基于SVM和神经网络的车牌识别(二)
OpenCV自学笔记19. 基于SVM和神经网络的车牌识别(三)
OpenCV自学笔记20. 基于SVM和神经网络的车牌识别(四)

这里写图片描述

阅读全文
1 0
原创粉丝点击