基于matlab实现简单的手写字母/数字/汉字程序(神经网络)
本篇博客主要参考了《模式识别与智能计算:MATLAB技术实现(第2版)》。如果想深入了解,我会在评论区贴出链接。
原理:
原理很简单,主要是对于输入的手写样本图片进行切分,切分成5X5的25个小cell。并且对每一个cell进行黑色像素量的计算,将25个cell的像素量以列向量的形式存入结构体的一个类的一列中,完成对一个样本图片的读取。对每种字母/数字/汉字的类读入5个训练的手写样本。
对于已经读入完毕的结构体,需要构建一个简单的神经网络来对其进行训练。得到训练好的网络,再对待测样本进行计算像素量的操作,与训练好的神经网络进行匹配就可以实现相应的功能了。
神经网络结构:
设计的神经网络主要是有3层:输入层、隐含层、输出层。对于一个样本有5X5=25个特征作为神经网络的输入,故有输入结点25个。根据隐含层个数大约为输入结点的两倍关系,故隐含层有50个结点。输出层取4个结点,这四个输出为四位二进制数,代表神经网络的数字类型(如果需要能够识别更多的样本,就请在结构体存入更多的样本的基础上,修改神经网络的输出结点数,并且修改输出矩阵即可)。
部分代码
主要以字母识别为例,汉字和数字的情况仅仅只是改变训练样本图片和相应的匹配数组
读取训练样本
load templet;
% A 被分成5*5=25个cell(A的长宽要能被5整除)
A=imread('B_5.png');
figure(1),imshow(A)
B=zeros(1,25);
[row, col] = size(A);
cellRow = row/5
cellCol = col/5
计算黑色像素量
%计算黑色像素量存入结构体
count = 0;
currentCell = 1;
for currentRow = 0:4
for currentCol = 0:4
for i = 1:cellRow
for j = 1:cellCol
if(A(currentRow*cellRow+i,currentCol*cellCol+j)==0)
count=count+1;
end
end
end
ratio = count/(cellRow*cellCol);
B(1,currentCell) = ratio;
currentCell = currentCell+1;
count = 0;
end
end
pattern(i).num=5 %结构体的第i个字段包括5个测试样本
pattern(i).feature(:,j)=B' %将B以列向量的方式保存进入结构体的第i个字段的j个样本
save templet pattern
定义神经网络的输入输出矩阵
global bpnet;
clc;
load templet pattern;
c=0;
%P为输入矩阵
p=[];
for i=1:10
for j=1:5
c=c+1;
p(:,c)=pattern(i).feature(:,j);
end
end
%t为输出矩阵
t=zeros(4,45);
t(4,1:5)=1;
t(3,6:10)=1;
t(3:4,11:15)=1;
t(2,16:20)=1;
t(2,21:25)=1;
t(4,21:25)=1;
t(2:3,26:30)=1;
t(2:4,31:35)=1;
t(1,36:40)=1;
t(1,41:45)=1;
t(4,41:45)=1;
t(1,46:50)=1;
t(3,46:50)=1;
初始化神经网络
x=ones(25,2);
x(:,1)=0;
bpnet = newff(x,[50,4],{'logsig','logsig','traingd'}); %这里选用梯度下降的方法
bpnet.trainParam.show=50; %每隔50次训练,显示一次训练进程
bpnet.trainParam.lr=0.2; %学习速率定义为0.2
bpnet.trainParam.epochs=2000; %最大训练次数2000次
bpnet.trainParam.goal=0.2e-1; %训练要求的精度小于0.02
bpnet.trainParam.min_grad=1e-10; %最小梯度要求为1e-10
[bpnet] = train(bpnet,p,t);
定义一个函数用于匹配的待测样本和神经训练后的神经网络
function y= bpnet(sample)
global bpnet;
clc;
a = sim(bpnet,sample)
a = round(a)
b = num2str(a);
c = bin2dec(b');
y = c;
测试部分
name1=char('A','B','C','D','E','F','H','I','L','X');%输出
A=imread('F_test.png');%待测样本
figure(1),subplot(121),imshow(A),title(['待识别的字母:']);
B=zeros(1,25);
[row,col] = size(A);
cellRow = row/5
cellCol = col/5
%计算黑色像素量
count = 0;
currentCell = 1;
for currentRow = 0:4
for currentCol = 0:4
for i = 1:cellRow
for j = 1:cellCol
if(A(currentRow*cellRow+i,currentCol*cellCol+j)==0)
count=count+1;
end
end
end
ratio = count/(cellRow*cellCol);
B(1,currentCell) = ratio;
currentCell = currentCell+1;
count = 0;
end
end
class = bpnet(B')
subplot(122),imshow([(name1(class)),'.png']),title(['该字母被识别为:']);
disp(['该字母被识别为:',(name1(class))]);
这里之所以要定义一个字符数组是因为进行匹配的时候,输出的是二进制的数,对应了结构体的第几个类,比如说读入结构体的第一个字母类是“A”,那么匹配出的结果就是0001,读入结构体的第5个字母类是“E”,那么匹配出的结果就是0101.为了让输出的结果和标准的图片匹配上,就定义了这样的字符数组来进行匹配。
部分截图:
总结:
这仅仅只是一个简单的识别程序,主要的缺点就是不能非常灵活地面对需求,如果说对于要求识别的样本的数量过大,需要手动去修改输出矩阵的相关参数,还是比较麻烦的。但如果比较感兴趣,还是可以去试一试这个简单的程序。