先挖个坑,快期末考试了,有空填上w

好了,今晚刚好有点闲,就把坑填上吧。

//-------------------------------开篇-------------------------------------------

首先讲一下,这篇随笔不是讲HOG特征是什么,怎么提取(这种图像特征网上一搜一大把),也不是讲BP神经网络工作原理,发展史啥的(机器学习小白,ANN深究我也不懂)。在这里我要讲的是,车标识别怎么code,怎么使用OpenCV自带的BP神经网络训练,以及识别。好了废话不多说,咱们开始吧。

//------------------------要准备的东西---------------------------------

正式讲代码前讲一下正式完成本工程需要什么准备工作。

1、配置Opencv 2.4.6及以上版本的VS 2010+,OpenCV 3可能改动的比较大,对本工程来说不建议。

2、一个车标库,尽量多一点,本工程共使用900来张车标样本,分为训练集(560张)和测试集(371张)

(工程迁移到我的Github仓库了)

//--------------------------正篇-----------------------------------------

准备好了以上的东西,我们就可以开始了。

我将结合代码分几步来讲怎么识别图像特征、喂给BP神经网络来识别车标,一步一步扩充代码

////第一步  建立工程

简单讲一下工程预处理吧,本工程就一个main.cpp文件,因此在建好工程后,建一个main.cpp就可以了。然后将我们的车标库test测试集和train训练集放在和整个工程同一目录下。

为了读取图片方便,我们使用两个txt文件trainpath、和testpath来保存每张图片的路径(路径文件在车标库里有,但是如果要使用这两个文件,就必须把他们放在main.cpp同一个目录下,同时,车标库也必须放在和工程同一目录下)

这是车标库和工程的存放位置关系

这是图片路径文件和main.cpp的关系

为main.cpp添加opencv的头文件,因为对OpenCV的结构不是特别熟,因此大家只要把万用的头文件一股脑写出来就行

 #include <iostream>
#include <fstream>
#include <opencv.hpp>
#include <highgui.h>
#include <cxcore.h>
#include <cstring>
#include <ml.h>
#include <vector>
#include <iomanip>

本工程用了以上头文件。

哦对了,还有两个名字空间,写一下吧

 using namespace std;
using namespace cv;

有些头文件就用了一两个函数啦,比如cstring就用到了个memset()23333,还有些OpenCV 1.x版本的头文件也挺多余的,不过还是都写上吧,免得报错了

////第二步    初始化工作

然后我们就开始带代码的编写工作了,我们按照main.cpp的代码顺序来讲。

我们先定义代码要用到的全局变量:

 //----------------------------全局变量定义---------------------------------
vector<float> descriptors; //HOG特征存放向量
float data[m_NUM][F_NUM]; //样本特征存放数组
float f[][F_NUM]; //测试样本特征存放数组
float dataCls[m_NUM][CLASSNUM]; //样本所属类别
int mClass ; //训练样本所属类别
int dNum; //训练样本个数

其中有几个宏要在之前定义一下

 #define  F_NUM    1764     //7*7*9*4  车标特征维数
#define m_NUM 560 //训练样本个数
#define CLASSNUM 4 //车标种类

解释一下两段数据的设置

首先讲一下特征数目吧,HOG特征其实是一个1×N维的特征矩阵,N的确定由检测窗口大小、块大小、胞元大小决定。每个胞元9个bin。

本实验检测窗口定为64×64,就是整张图片的大小,块大小16×16,胞元8×8,步进8×8,这样一张图片就有(64/8-1)*(64/8-1)*9*(16*16)/(8*8)=1764维特征

那么560个样本就有560*1764个特征,就构成了特征矩阵data[560][1764]。

来看看OpenCV的神经网络训练函数

 int CvANN_MLP::train(const Mat& inputs, const Mat& outputs, const Mat& sampleWeights, const Mat& sampleIdx=Mat(), CvANN_MLP_TrainParams params=CvANN_MLP_TrainParams(), int flags= );

这是我们之后要用到的关键函数,OpenCV自带的神经网络训练函数,我们依次来看下参数

第一个input是一个num×F_NUM的训练数据输入矩阵,num是样本个数,F_NUM是每个样本的特征数目,是不是刚好对应了我们的data矩阵。但是我们看到,data是浮点型数组,inputs是Mat阵,怎么统一呢?其实OpenCV在初始化Mat的时候,就可以使用一般的二维数组进行赋值,只要数据类型匹配,并且维度相等就行了,就像下面一样

 Mat trainDate(m_NUM,F_NUM,CV_32FC1,data);

这里使用一个data的首地址初始化了trainData这个输入阵。

再来解释下dataCls为什么是560×4的。

继续看trian()函数的第二个参数,outputs,是一个num×CLASSNUM的数据阵,num是样本个数,CLASSNUM是样本的总类别数。

当然对于560个数据,每个数据都要有一个类别。CLASSNUM是4,那么这个阵具体是怎么样初始化的呢?

举个例子,0号样本属于第1类,那么dataCls[0]={1,0,0,0}   也就是说,对应的那一类初始化为1,其余的都是0。

我们同样使用上述的初始化数据阵的方法将dataCls的内容复制到trainLable中(注意,dataCls和data数组要严格对齐,就是说,x号样本的特征放在data[x]里,所属类别放在dataCls[x]里)

 Mat trainLable(m_NUM,CLASSNUM,CV_32FC1,dataCls);

对于train的其他参数,除了params需要注意下,其他都是默认的。

////第三步   读取训练样本,填充数据矩阵data和类别矩阵dataCls

首先,我们定义了全局变量,要进行初始化工作,那么写完void main()后的第一件事就是调用init()函数,进行初始化工作,init()代码如下

/**************************************************
*名称:init()
*参数:void
*返回值:void
*作用:初始化各类参数
****************************************************/
void init()
{
memset(data,,sizeof(data));
memset(dataCls,,sizeof(dataCls));
mClass = -; //初始类别为-1
dNum = ; //统计样本个数,其实没软用,对于本工程样本个数固定为560
}

之后是读入图像和提取HOG特征,并记录样本所属类别和填充数据矩阵,代码如下

    init();
ifstream in("trainpath.txt");string s,ss;
while( in >> s){
if(ss != s.substr(,)){
mClass++; //类别是0,1,2,3
cout<<mClass<<endl;
}
ss = s.substr(,);
cout<<s<<endl;
//------------------------读入图像,缩放图像----------------------------
Mat imge = imread(s),img;
if(imge.empty())
{
cout<<"image load error!"<<endl;
system("pause");
return ;
}
resize(imge,img,Size(,)); //------------------------提取HOG特征,放入特征数组---------------------
getHOG(img); packData(sta); //填充特征数组和类别数组 }

稍微解释一下流程。

先定义一个文件流用于读取训练集路径文件trainpath.txt,这个txt文件保存了所有训练集的文件路径,一行一个,像这样

..\..\data\train\Citroen\X-雪铁龙_1350198-01_201502010833146800.jpg
..\..\data\train\Citroen\X-雪铁龙_1350198-01_201502010841008800.jpg
..\..\data\train\Citroen\X-雪铁龙_1350198-01_201502010845367300.jpg

  而且,不同类别的车标放在一起,举个例子,共400行,前100行是雪铁龙,再100行本田,再100行一汽,再100行福田(每个字符串的前17行是一样的,19行肯定不一样)

这样有个好处,可以方便地统计这个图片路径对应的图片属于哪个种类的车。我们从代码来看这个过程。

先定义两个字符串ss和s,文件流一行行读入并保存一行到s,取前19行,如果不等于ss,就让mClass+1。

可以看到,初始mClass=-1.并且第一个字符串肯定不等于ss(因为此时ss为空),那么第一个图片数据就属于类别0,之后保存ss为s的前19位。

这样,读完整个图片路径,4种车标就可以很清楚地被区分了。

然后后面这个是读图保护,不管他,

然后读入图片,使用resize函数将其压缩到64×64,看到没,这就是我们提取HOG时候的检测窗口大小。

调用getHog(img)获取图像的HOG特征,这个getHog是自定义函数,写在main函数前面就行,代码如下:

 /**************************************************
*名称:getHOG()
*参数:Mat& img
*返回值:void
*作用:获取图像的HOG特征
****************************************************/
void getHOG(Mat& img)
{
HOGDescriptor *hog = new HOGDescriptor(
Size(,), //win_size 检测窗口大小,这里即完整的图
Size(,), //block_size 块大小
Size(,), //blackStride 步进
Size(,), //cell_size 细胞块大小
//9个bin
);
hog -> compute( //提取HOG特征向量
img,
descriptors, //存放特征向量
Size(,), //滑动步进
Size(,)
);
delete hog;
hog = NULL;
}

这里其实就调用了几个openCV自带的函数,对传进来的图片进行特征提取而已。有一点要注意,compute函数的第二个参数

descriptors是全局变量,记不起来的可以去前面的全局变量定义的地方找找,它就是用来保存提取到的HOG特征。

刚才我们也计算过了,一张图1764个特征,也就是一次提取,descriptors就放一次1×1764的数据。

那么提取到一张图的特征后,我们要把他放到data里,那么就是packData了,同样,packData是一个全局函数

void packData()
{
int p = ;
for (vector<float>::iterator it = descriptors.begin(); it != descriptors.end(); it++)
{
data[dNum][p++] = *it;
}
dataCls[dNum++][mClass] = 1.0;
}

前一半的for循环用来从descriptors中的向量填到data矩阵中,后一个语句就是更新它对应的类别矩阵。

循环执行完,我们的数据也填充完毕了,接下来就是建立网络训练了。

////第四步   建立神经网络  训练参数矩阵

先上这部分代码

 //------------------------建BP神经网络,开始训练------------------------
CvANN_MLP bp; CvANN_MLP_TrainParams params;
params.term_crit=cvTermCriteria(CV_TERMCRIT_ITER+CV_TERMCRIT_EPS,,0.001); //迭代次数7000,最小误差0.001
params.train_method=CvANN_MLP_TrainParams::BACKPROP; //训练方法反向传播
params.bp_moment_scale=0.1;
params.bp_dw_scale=0.1; Mat layerSizes = (Mat_<int>(,) << F_NUM,, ); //3层神经网络
Mat trainDate(m_NUM,F_NUM,CV_32FC1,data);
Mat trainLable(m_NUM,CLASSNUM,CV_32FC1,dataCls);
bp.create(layerSizes, CvANN_MLP::SIGMOID_SYM); //激活函数sigmoid
system("cls");
cout<<"训练中...训练时间大致需要6分钟,请耐心等待";
bp.train(trainDate,trainLable, Mat(),Mat(), params); //开始训练 system("cls");
cout << "训练完成!!" <<endl;

CvANN_MLP是openCV自带的人工神经网络类,可以直接用,很方便吧。

我们先定义了一个CvANN_MLP类,然后看第二块,第二块就是神经网络的一些参数的设定,具体注释都有,就不讲了

第三块:第11行,定义神经网络层数为3层,第一层:F_NUM个神经元,还记得F_NUM吗?全局变量,就是特征数1764!

总之神经网络就是1764,48,4共3层,每次节点数就是这么几个。

12-13行,看见没,这就是把我们填充完的数据数组和类别数组赋值给Mat阵,之后就能调用create函数啦,创建一个网络,使用SIGMOID函数什么的。

然后是训练,train()之前也说过。SO EASY!

等待6分钟左右,训练就结束了,之后就是测试了

////第五步   测试神经网络

老规矩,上代码再说

 //---------------------------------读入图像,开始测试--------------------------
system("cls");
cout<<"开始测试..."<<endl;
system("cls");
Mat imge,img; ifstream ins("testpath.txt"); int cls = -;
int num=,c_num=;
while( ins >> s){
memset(f,,sizeof(f));
if(ss != s.substr(,)){
cls++;
cout<<cls<<endl;
}
cout<<s<<endl;
ss = s.substr(,);
imge = imread(s);
resize(imge,img,Size(,)); //使用线性插值
num++;
if (classifier(img,bp) == cls)
{
c_num++;
} }
system("cls");
cout<<"测试完成"<<endl;
cout<<"***************************************"<<endl;
cout<<"*样本个数:"<<num<<endl;
cout<<"*正确个数:"<<c_num<<endl;
cout<<"*正确率:"<<setprecision()<<(float)c_num/num*<<"%"<<endl;
cout<<"***************************************"<<endl;
system("pause");

测试就不说太多了了,无非读一下测试路径集,匹配一下,唯一要讲的就是那个第22行的classiffier函数,这个也是个全局函数,上代码吧2333

/**************************************************
*名称:classifier()
*参数:Mat& CvANN_MLP&
*返回值:int
*作用:求解测试结果最相邻类别
****************************************************/
int classifier(Mat& image,CvANN_MLP& bp)
{ getHOG(image);
int p = ;
for (vector<float>::iterator it = descriptors.begin(); it != descriptors.end(); it++)
{
f[0][p++] = *it;
}
Mat nearest(, CLASSNUM, CV_32FC1, Scalar()); Mat charFeature(, F_NUM, CV_32FC1,f); bp.predict(charFeature, nearest); Point maxLoc; minMaxLoc(nearest, NULL, NULL, NULL, &maxLoc); int result = maxLoc.x; return result; }

这个函数返回神经网络预测测试图片最可能的所属类别。之后就是统计正确个数了。

//--------------------------结语-----------------------------------------

代码全写在一个cpp里了2333,为了方便讲解,也方便自己学习嘛,不知道你有没有看明白我讲的呢ww。

可能以上讲解也有疏漏,如果建完工程还是有问题的话,就直接下载下面的工程对照着这个讲解再看一遍吧Orz(注意,运行前保证环境搭好,而且文件路径不要更改)

(附完整工程下载地址:https://github.com/Holy-Shine/carLogoRec

有兴趣的小伙伴star一下仓库吧嘻嘻。

 

使用HOG特征+BP神经网络进行车标识别的更多相关文章

  1. 模式识别之ocr项目---(模板匹配&BP神经网络训练)

    摘 要 在MATLAB环境下利用USB摄像头采集字符图像,读取一帧保存为图像,然后对读取保存的字符图像,灰度化,二值化,在此基础上做倾斜矫正,对矫正的图像进行滤波平滑处理,然后对字符区域进行提取分割出 ...

  2. 字符识别OCR研究一(模板匹配&amp;BP神经网络训练)

    摘 要 在MATLAB环境下利用USB摄像头採集字符图像.读取一帧保存为图像.然后对读取保存的字符图像,灰度化.二值化,在此基础上做倾斜矫正.对矫正的图像进行滤波平滑处理,然后对字符区域进行提取切割出 ...

  3. 基于Opencv自带BP网络的车标简易识别

    代码地址如下:http://www.demodashi.com/demo/12966.html 记得把这几点描述好咯:代码实现过程 + 项目文件结构截图 + 演示效果 1.准备工作 1.1 训练集和测 ...

  4. Python实现bp神经网络识别MNIST数据集

    title: "Python实现bp神经网络识别MNIST数据集" date: 2018-06-18T14:01:49+08:00 tags: [""] cat ...

  5. BP神经网络的手写数字识别

    BP神经网络的手写数字识别 ANN 人工神经网络算法在实践中往往给人难以琢磨的印象,有句老话叫“出来混总是要还的”,大概是由于具有很强的非线性模拟和处理能力,因此作为代价上帝让它“黑盒”化了.作为一种 ...

  6. MATLAB神经网络(1) BP神经网络的数据分类——语音特征信号分类

    1.1 案例背景 1.1.1 BP神经网络概述 BP神经网络是一种多层前馈神经网络,该网络的主要特点是信号前向传递,误差反向传播.在前向传递中,输入信号从输入层经隐含层逐层处理,直至输出层.每一层的神 ...

  7. Numpy实现简单BP神经网络识别手写数字

    本文将用Numpy实现简单BP神经网络完成对手写数字图片的识别,数据集为42000张带标签的28x28像素手写数字图像.在计算机完成对手写数字图片的识别过程中,代表图片的28x28=764个像素的特征 ...

  8. 利用c++编写bp神经网络实现手写数字识别详解

    利用c++编写bp神经网络实现手写数字识别 写在前面 从大一入学开始,本菜菜就一直想学习一下神经网络算法,但由于时间和资源所限,一直未展开比较透彻的学习.大二下人工智能课的修习,给了我一个学习的契机. ...

  9. 【机器学习】BP神经网络实现手写数字识别

    最近用python写了一个实现手写数字识别的BP神经网络,BP的推导到处都是,但是一动手才知道,会理论推导跟实现它是两回事.关于BP神经网络的实现网上有一些代码,可惜或多或少都有各种问题,在下手写了一 ...

随机推荐

  1. AutoMapper 忽略某个字段

    以前要在定义中忽略 Mapper.CreateMap<Source, Destination>() .ForMember(dest => dest.SomeValuefff, opt ...

  2. 十七、Java中数组常见的几种排序方法!

    转载自:https://www.cnblogs.com/bekeyuan123/p/6891875.html 数组的定义: // 3种定义方式 int[] arr = new int[5]; int[ ...

  3. c#gridcontrol 的一些设置

    1:单元格变色,当鼠标点击到单元格的时候, 可能我们是想选择整个行,而此时gridview 默认设置是点击单元格整个行变色,这个点击的单元格变白色, 整行选择感觉有断层 于是和可以这么设置 设置Gri ...

  4. NC 刷新后,停留上一次选择的行(多行)

    如图,我点击批量输入后,会调用列表刷新按钮.但数据之前选择的光标会跳到第一行,原先选择的行就不知道是哪行可.图为最终效果 // 批量输入日期后实时刷新 apply update(batch input ...

  5. 20170529计划---统计业务量并生成EXCEL通过邮件发送

    每个月都要统计这些业务量的东东,烦死了,赶紧通过python写一个来搞定吧,三天搞定吧,未完待续哈. 2017-5-29 19:50粗略地做了一个思维导图哈 终于第三天完成啦 #encoding=ut ...

  6. poj 3126 Prime Path bfs

    题目链接:http://poj.org/problem?id=3126 Prime Path Time Limit: 1000MS   Memory Limit: 65536K Total Submi ...

  7. gitlab简介与配置

    版本控制介绍 版本控制是指对软件开发过程中各种程序代码.配置文件及说明文档等文件变更的管理,是软件配置管理的核心思想之一. 版本控制最主要的功能就是追踪文件的变更.它将什么时候.什么人更改了文件的什么 ...

  8. dubbo入门学习 二 RPC框架

    rpc框架解释 谁能用通俗的语言解释一下什么是 RPC 框架? - 远程过程调用协议RPC(Remote Procedure Call Protocol) 首先了解什么叫RPC,为什么要RPC,RPC ...

  9. Python从入门到精通之Seventh!

    函数浅析:可以减少代码重用,保持一致性,可扩展性,易维护性. 定义方法:def 函数名(形参):     '''功能注释'''      代码块 打印函数名时,会出现函数的内存地址.两个函数名相同时, ...

  10. Python的条件判断语句------if/else语句

    计算机之所以能做很多自动化的任务,因为它可以自己做条件判断. 比如,输入用户的年龄,根据年龄打印不同的内容... Python程序中,能让计算机自己作出判断的语句就是if语句: 例: age = 25 ...