Opencv人工神经网络实现字母与数字识别流程

2024-06-17 16:38

本文主要是介绍Opencv人工神经网络实现字母与数字识别流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 1 人工神经网络简介
    • 1.1 人工神经元/神经网络模型
  • 2 字符特征提取
  • 3 Opencv的神经网络
    • 3.1 创建一个网络
    • 3.2 网络参数设置
    • 3.3 识别
  • 4 字体样本下载

人工神经网络简介

人工神经网络(Artificial Neural Network,ANN)简称神经网络(NN),是基于生物学中神经网络的基本原理,在理解和抽象了人脑结构和外界刺激响应机制后,以网络拓扑知识为理论基础,模拟人脑的神经系统对复杂信息的处理机制的一种数学模型。神经网络是一种运算模型,由大量的节点(或称神经元)之间相互联接构成。每个节点代表一种特定的输出函数,称为激活函数(activation function)。每两个节点间的连接都代表一个对于通过该连接信号的加权值,称之为权重(weight),神经网络就是通过这种方式来模拟人类的记忆。网络的输出则取决于网络的结构、网络的连接方式、权重和激活函数。

人工神经元/神经网络模型

神经元1.png神经元2.png


左边几个灰底圆中所标字母w代表浮点数,称为权重(weight,或权值,权数)。进入人工神经细胞的每一个input(输入)都与一个权重w相联系,正是这些权重将决定神经网络的整体活跃性。你现在暂时可以设想所有这些权重都被设置到了-1和1之间的一个随机小数。因为权重可正可负,故能对与它关联的输入施加不同的影响,如果权重为正,就会有激发(excitory)作用,权重为负,则会有抑制(inhibitory)作用。当输入信号进入神经细胞时,它们的值将与它们对应的权重相乘,作为图中大圆的输入。大圆的‘核’是一个函数,叫激励函数(activation function),它把所有这些新的、经过权重调整后的输入全部加起来,形成单个的激励值(activation value)。激励值也是一浮点数,且同样可正可负。然后,再根据激励值来产生函数的输出也即神经细胞的输出:如果激励值超过某个阀值(作为例子我们假设阀值为1.0),就会产生一个值为1的信号输出;如果激励值小于阀值1.0,则输出一个0。这是人工神经细胞激励函数的一种最简单的类型。在这里,从激励值产生输出值是一个阶跃函数.

字符特征提取

在深度学习(将特征提取作为训练的一部分)这个概念引入之前,一般在准备分类器进行识别之前都需要进行特征提取。因为一幅图像包含的内容太多,有些信息能区分差异性,而有些信息却代表了共性。所以我们要进行适当的特征提取把它们之间的差异性特征提取出来。

这里面我们计算二种简单的字符特征:梯度分布特征、灰度统计特征。这两个特征只是配合本篇文章来说明神经网络的普遍用法,实际中进行字符识别需要考虑的字符特征远远要比这复杂,还包括相似字特征的选取等,也由于工作上的原因,这一部分并不深入的介绍。

1,首先是梯度分布特征,该特征计算图像水平方向和竖直方向的梯度图像,然后通过给梯度图像分划不同的区域,进行梯度图像每个区域亮度值的统计,以下是算法步骤:

<1>将字符由RGB转化为灰度,然后将图像归一化到16*8。

<2>定义soble水平检测算子: 和竖直方向梯度检测算子 。

<3>对图像分别用 和 进行图像滤波得到 和 ,下图分别代表原图像、 和 。

<4>对滤波后的图像,计算图像总的像素和,然后划分4*2的网络,计算每个网格内的像素值的总和。

<5>将每个网络内总灰度值占整个图像的百分比统计在一起写入一个向量,将两个方向各自得到的向量并在一起,组成特征向量。

2,第二个特征非常简单,只需要将图像归一化到特定的大小,然后将图像每个点的灰度值作为特征即可。

<1>将图像由RGB图像转换为灰度图像;

<2>将图像归一化大小为 ,并将图像展开为一行,组成特征向量。

Sample Code (以下代码使用的是 Opencv 3.0环境)

float sumMatValue(const Mat & image){float sumValue = 0;int r = image.rows;int c = image.cols;if(image.isContinuous()){c = r*c;r = 1;	}for(int i = 0; i < r; i++){const uchar *linePtr = image.ptr<uchar>(i);for (int j = 0; j < c; j++){sumValue += linePtr[j];}	}return sumValue;
}
void calcGradientFeat(Mat & imgSrc, vector<float> & feat){Mat image;cvtColor(imgSrc, image, CV_BGR2GRAY);resize(image, image, Size(8,16));float mask[3][3] = {{1,2,1},{0,0,0},{-1,-2,-1}};      Mat y_mask = Mat(3,3, CV_32F, mask) / 8;Mat x_mask = y_mask.t(); // 转置Mat sobelX, sobelY;filter2D(image, sobelX, CV_32F, x_mask); filter2D(image, sobelY, CV_32F, y_mask);sobelX = abs(sobelX);sobelY = abs(sobelY);float totleValueX = sumMatValue(sobelX);float totleValueY = sumMatValue(sobelY);for(int i = 0; i < image.rows; i = i +4){for( int j = 0; j < image.cols; j = j + 4){Mat subImageX = sobelX(Rect(j, i, 4, 4));feat.push_back(sumMatValue(subImageX) / totleValueX);Mat subImageY= sobelY(Rect(j, i, 4, 4)); feat.push_back(sumMatValue(subImageY) / totleValueY); }	   	}	Mat img2;resize(image, img2, Size(4,8));int r = img2.rows;int c = img2.cols;if(img2.isContinuous()){c = r*c;r = 1;	}for(int i = 0; i < r; i++){const uchar *linePtr = img2.ptr<uchar>(i);for (int j = 0; j < c; j++){feat.push_back(linePtr[j]);}	}	
}

Opencv的神经网络

CvANN_MLP是OpenCV中提供的一个神经网络的类,正如它的名字一样(multi-layer perceptrons),它是一个多层感知网络,它有一个输入层,一个输出层以及1或多个隐藏层。

创建一个网络

Ptr<StatModel> buildMLPClassifier(Mat & input , Mat & output){Ptr<ANN_MLP> model;//train classifier;int layer_sz[] = {input.cols, 100 , output.cols};int nlayers = (int)(sizeof(layer_sz)/ sizeof(layer_sz[0]));Mat layer_sizes(1,nlayers,CV_32S, layer_sz);int method;double method_param;int max_iter;if(1){method = ANN_MLP::BACKPROP;method_param = 0.0001;max_iter = 1000;}else{method = ANN_MLP::RPROP;method_param = 0.1;max_iter = 1000;}Ptr<TrainData> tData = TrainData::create(input,ROW_SAMPLE,output);model = ANN_MLP::create();model->setLayerSizes(layer_sizes);model->setActivationFunction(ANN_MLP::SIGMOID_SYM, 0, 0);model->setTermCriteria(TermCriteria(TermCriteria::MAX_ITER+TermCriteria::EPS, max_iter, FLT_EPSILON));//setIterCondition(max_iter, 0);model->setTrainMethod(method, method_param);model->train(tData);model->save("mlp1.xml");return model;
}
}
layerSizes:一个整型的数组,这里面用Mat存储。它是一个1*N的Mat,N代表神经网络的层数,第 列的值表示第 层的结点数。这里需要注意的是,在创建这个Mat时,一定要是整型的,uchar和float型都会报错。

比如我们要创建一个3层的神经网络,其中第一层结点数为 ,第二层结点数为 ,第三层结点数为 ,则layerSizes可以采用如下定义:

1 Mat layerSizes=(Mat_<int>(1,3)<<x1,x2,x3);

或者用一个数组来初始化:

1 int ar[]={x1,x2,x3}; 2 Mat layerSizes(1,3,CV_32S,ar);

activateFunc:这个参数用于指定激活函数,不熟悉的可以去看我博客里的这篇文章《神经网络:感知器与梯度下降》,一般情况下我们用SIGMOID函数就可以了,当然你也可以选择正切函数或高斯函数作为激活函数。OpenCV里提供了三种激活函数,线性函数(CvANN_MLP::IDENTITY)、sigmoid函数(CvANN_MLP::SIGMOID_SYM)和高斯激活函数(CvANN_MLP::GAUSSIAN)。

后面两个参数则是SIGMOID激活函数中的两个参数 和 ,默认情况下会都被设置为1。

网络参数设置

神经网络训练参数的类型存放在CvANN_MLP_TrainParams这个类里,它提供了一个默认的构造函数,我们可以直接调用,也可以一项一项去设。

 1 CvANN_MLP_TrainParams::CvANN_MLP_TrainParams() 2 { 3     term_crit = cvTermCriteria( CV_TERMCRIT_ITER + CV_TERMCRIT_EPS, 1000, 0.01 ); 4     train_method = RPROP; 5     bp_dw_scale = bp_moment_scale = 0.1; 6     rp_dw0 = 0.1; rp_dw_plus = 1.2; rp_dw_minus = 0.5; 7     rp_dw_min = FLT_EPSILON; rp_dw_max = 50.; 8 }

它的参数大概包括以下几项。

term_crit:终止条件,它包括了两项,迭代次数(CV_TERMCRIT_ITER)和误差最小值(CV_TERMCRIT_EPS),一旦有一个达到条件就终止训练。

train_method:训练方法,OpenCV里提供了两个方法一个是很经典的反向传播算法BACKPROP,另一个是弹性反馈算法RPROP,对第二种训练方法,没有仔细去研究过,这里我们运用第一种方法。

剩下就是关于每种训练方法的相关参数,针对于反向传播法,主要是两个参数,一个是权值更新率bp_dw_scale和权值更新冲量bp_moment_scale。这两个量一般情况设置为0.1就行了;太小了网络收敛速度会很慢,太大了可能会让网络越过最小值点。

我们一般先运用它的默认构造函数,然后根据需要再修改相应的参数就可以了。如下面代码所示,我们将迭代次数改为了5000次。

1 CvANN_MLP_TRainParams param; 2 param.term_crit=cvTermCriteria(CV_TerMCrIT_ITER+CV_TERMCRIT_EPS,5000,0.01);

inputs:输入矩阵。它存储了所有训练样本的特征。假设所有样本总数为nSamples,而我们提取的特征维数为ndims,则inputs是一个 的矩阵,我们可以这样创建它。

1 Mat inputs(nSamples,ndims,CV_32FC1); //CV_32FC1说明它储存的数据是float型的。

我们需要将我们的训练集,经过特征提取把得到的特征向量存储在inputs中,每个样本的特征占一行。

outputs:输出矩阵。我们实际在训练中,我们知道每个样本所属的种类,假设一共有nClass类。那么我们将outputs设置为一个nSample行nClass列的矩阵,每一行表示一个样本的预期输出结果,该样本所属的那类对应的列设置为1,其他都为0。比如我们需要识别0-9这10个数字,则总的类数为10类,那么样本数字“3”的预期输出为[0,0,1,0,0,0,0,0,0,0];

sampleWeights:一个在使用RPROP方法训练时才需要的数据,所以这里我们不设置,直接设置为Mat()即可。

sampleIdx:相当于一个遮罩,它指定哪些行的数据参与训练。如果设置为Mat(),则所有行都参与。

params:这个在刚才已经说过了,是训练相关的参数。

flag:它提供了3个可选项参数,用来指定数据处理的方式,我们可以用逻辑符号去组合它们。UPDATE_WEIGHTS指定用一定的算法去初始化权值矩阵而不是用随机的方法。NO_INPUT_SCALE和NO_OUTPUT_SCALE分别用于禁止输入与输出矩阵的归一化。

一切都准备好后,直接开始训练吧!

识别

Ptr<StatModel> model = buildMLPClassifier(input, output);//Ptr<StatModel> model = loadMLPClassifiler();float response = model->predict(test, test1);cout<<"response = "<<response<<endl;for(int i = 0; i < test1.size(); i++){cout<<"test1 = "<<test1[i]<<endl;	}
识别返回的response 就是预测的值,test1 里面存放的是每个字母的可能概率

完整代码:

  int main(){Mat image;vector<float>feats;vector<float>test,test1;string path = "code/python_image_learn/identfying_code_recognize/charSamples/";int num = 0;int classfilternum = 34;int modlenum = 30;for(int i = 0 ; i < classfilternum ; i++){for(int j = 0; j < modlenum; j++){ostringstream oss;oss<<path<<i<<"/"<<j<<".png";//cout<<oss.str()<<endl;image=imread(oss.str());calcGradientFeat(image, feats);num++;if(i == 10 && j == 10){ostringstream oss; oss<<path<<i<<"/"<<(j+1)<<".png";//cout<<oss.str()<<endl;image=imread(oss.str());calcGradientFeat(image, test);			}	}}	Mat input, output;	input = Mat(classfilternum*modlenum, 48, CV_32F);output = Mat(classfilternum*modlenum, classfilternum, CV_32F, Scalar(0));int r = input.rows;int c = input.cols;if(input.isContinuous()){c = r*c;r = 1;}for(int i = 0; i < r; i++){float *linePtr = input.ptr<float>(i);for (int j = 0; j < c; j++){linePtr[j] = feats[c*i + j];}	}for(int i = 0; i < output.rows; i++){float *lineoutput = output.ptr<float>(i);lineoutput[i/modlenum] = 1;}	//if(Ptr<StatModel> model = buildMLPClassifier(input, output);//Ptr<StatModel> model = loadMLPClassifiler();float response = model->predict(test, test1);cout<<"response = "<<response<<endl;for(int i = 0; i < test1.size(); i++){cout<<"test1 = "<<test1[i]<<endl;	}//cout<<input<<endl;//cout<<"rows = "<<input.rows<<"col = "<<input.cols<<endl;//cout<<output<<endl;
//	waitKey();                    //等待按键return 0;}

字符样本的下载

链接:http://pan.baidu.com/s/1pLPeZkZ 密码:26eb


这篇关于Opencv人工神经网络实现字母与数字识别流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1069984

相关文章

使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)

《使用Java解析JSON数据并提取特定字段的实现步骤(以提取mailNo为例)》在现代软件开发中,处理JSON数据是一项非常常见的任务,无论是从API接口获取数据,还是将数据存储为JSON格式,解析... 目录1. 背景介绍1.1 jsON简介1.2 实际案例2. 准备工作2.1 环境搭建2.1.1 添加

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五

java如何分布式锁实现和选型

《java如何分布式锁实现和选型》文章介绍了分布式锁的重要性以及在分布式系统中常见的问题和需求,它详细阐述了如何使用分布式锁来确保数据的一致性和系统的高可用性,文章还提供了基于数据库、Redis和Zo... 目录引言:分布式锁的重要性与分布式系统中的常见问题和需求分布式锁的重要性分布式系统中常见的问题和需求

SpringBoot基于MyBatis-Plus实现Lambda Query查询的示例代码

《SpringBoot基于MyBatis-Plus实现LambdaQuery查询的示例代码》MyBatis-Plus是MyBatis的增强工具,简化了数据库操作,并提高了开发效率,它提供了多种查询方... 目录引言基础环境配置依赖配置(Maven)application.yml 配置表结构设计demo_st

python使用watchdog实现文件资源监控

《python使用watchdog实现文件资源监控》watchdog支持跨平台文件资源监控,可以检测指定文件夹下文件及文件夹变动,下面我们来看看Python如何使用watchdog实现文件资源监控吧... python文件监控库watchdogs简介随着Python在各种应用领域中的广泛使用,其生态环境也

el-select下拉选择缓存的实现

《el-select下拉选择缓存的实现》本文主要介绍了在使用el-select实现下拉选择缓存时遇到的问题及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录项目场景:问题描述解决方案:项目场景:从左侧列表中选取字段填入右侧下拉多选框,用户可以对右侧

Python pyinstaller实现图形化打包工具

《Pythonpyinstaller实现图形化打包工具》:本文主要介绍一个使用PythonPYQT5制作的关于pyinstaller打包工具,代替传统的cmd黑窗口模式打包页面,实现更快捷方便的... 目录1.简介2.运行效果3.相关源码1.简介一个使用python PYQT5制作的关于pyinstall

使用Python实现大文件切片上传及断点续传的方法

《使用Python实现大文件切片上传及断点续传的方法》本文介绍了使用Python实现大文件切片上传及断点续传的方法,包括功能模块划分(获取上传文件接口状态、临时文件夹状态信息、切片上传、切片合并)、整... 目录概要整体架构流程技术细节获取上传文件状态接口获取临时文件夹状态信息接口切片上传功能文件合并功能小

python实现自动登录12306自动抢票功能

《python实现自动登录12306自动抢票功能》随着互联网技术的发展,越来越多的人选择通过网络平台购票,特别是在中国,12306作为官方火车票预订平台,承担了巨大的访问量,对于热门线路或者节假日出行... 目录一、遇到的问题?二、改进三、进阶–展望总结一、遇到的问题?1.url-正确的表头:就是首先ur

C#实现文件读写到SQLite数据库

《C#实现文件读写到SQLite数据库》这篇文章主要为大家详细介绍了使用C#将文件读写到SQLite数据库的几种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下... 目录1. 使用 BLOB 存储文件2. 存储文件路径3. 分块存储文件《文件读写到SQLite数据库China编程的方法》博客中,介绍了文