OpenCV自学笔记20. 基于SVM和神经网络的车牌识别(四)
2017-07-19 19:58
721 查看
基于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
参考
累积直方图:http://blog.csdn.net/combfish/article/details/45056239
系列文章
OpenCV自学笔记17. 基于SVM和神经网络的车牌识别(一)OpenCV自学笔记18. 基于SVM和神经网络的车牌识别(二)
OpenCV自学笔记19. 基于SVM和神经网络的车牌识别(三)
OpenCV自学笔记20. 基于SVM和神经网络的车牌识别(四)
相关文章推荐
- OpenCV自学笔记17. 基于SVM和神经网络的车牌识别(一)
- OpenCV自学笔记18. 基于SVM和神经网络的车牌识别(二)
- OpenCV自学笔记19. 基于SVM和神经网络的车牌识别(三)
- 【OpenCV学习笔记】【教程翻译】一(基于SVM和神经网络的车牌识别概述)
- 【opencv机器学习】基于SVM和神经网络的车牌识别
- 基于SVM和神经网络的车牌识别
- 使用opencv的SVM和神经网络实现车牌识别
- 使用opencv的SVM和神经网络实现车牌识别(高质量的文章******)
- 整理《Mastering OpenCV with Practical Computer Vision Projects》中第5章用SVM和神经网络进行车牌识别操作流程
- 整理《Mastering OpenCV with Practical Computer Vision Projects》中第5章用SVM和神经网络进行车牌识别操作流程
- 使用opencv的SVM和神经网络实现车牌识别
- 使用opencv的SVM和神经网络实现车牌识别
- 基于SVM和神经网络的车牌识别
- 第五章 采用SVM和神经网络的车牌识别(流程图及详细解释)
- OpenCV实现车牌识别,OCR分割,ANN神经网络
- OpenCV进阶之路:神经网络识别车牌字符
- OpenCV进阶之路:神经网络识别车牌字符
- OpenCV进阶之路:神经网络识别车牌字符
- 自动车牌识别(ANPR)练习项目学习笔记2(基于opencv)
- 使用opencv和神经网络车牌识别