离线手写数字识别系统设计主要分为3个部分:图像预处理、数字的分割、数字的识别。
1.图像预处理
图像预处理主要是指对输入的图像进行灰度化、滤波、二值化。
鉴于数字的识别与色彩无关,并且考虑到噪声的影响(这里采用中值滤波),将图像进行预处理。
1 clear all;
2 clc;
3 test=input(
'Please input a digits image:',
's'); %输入图像
4 x=imread(test);
5 if ~isgray(x)
6 x=rgb2gray(x); %必须转换为灰度图像
7 end
8 xbw=im2bw(x,
0.9); %再转换为二值图像
9 xbw=medfilt2(xbw); %中值滤波
10 bw=xbw; %滤波后二值图像
11 12 figure;imshow(bw);
2.图像切割(字符分离)
原理:
当图像转换为二值图像后,利用边界点确定单个字符(假设每个数字之间均存在空白、间隔),如下:
当需要提取‘4’这个字符的时候,需要在横向、纵向上确定字符的上下边界点。
这里需要用到函数find()和all()【find()结果为满足条件的所有值,all():所有的点均满足某条件的值】
先确定左边界,再找到全1列(右边红线),以此类推确定上下边界。
1 for n=
1:
100 2 [c,l]=size(bw);
3 [i,j] = find(bw==
0);
4 jmin1=min(j);
5 xbwt=bw(:,jmin1:l); %xbwt为切除左边边缘的图像
6 [c,l]=size(xbwt);
7 [i,j]=find(all(xbwt(
1:c,:)==
1));
8 jmin2=min(j); %找右边界
9 if(isempty(jmin1)||isempty(jmin2))
break;
10 end
11 xbwn{n}=xbwt(:,
1:jmin2-
1); %xbwn{n+
1}为第(n+
1)个数字图像
12 bw=xbwt(:,jmin2:l); %bw迭代循环使用
13 [i,j] = find(xbwn{n}==
0); %找上下边界
14 imin=min(i);
15 imax=max(i); %imin:数字最上点,imax:数字最下点
16 xbwn1{n}=xbwn{n}(imin:imax,:);
17 18 end
如上图所示数字太过饱满,需要骨架化一下才能方便识别,这里需要用到matlab的bwmorph()函数,详见http://www.mathworks.cn/help/toolbox/images/ref/bwmorph.html
1 m=n-
1;
2 for n=
2:m
3 [i,j]=size(xbwn1{n});
4 p=-
1.*xbwn1{n}+ones(i,j); %先将矩阵取反
5 xbwn2{n}=bwmorph(p,
'skel',Inf);
6 %figure,imshow(xbwn2{n});
7 end
3.字符的匹配识别
1)统一标准
将提取出来的字符图片大小全部统一为16X16
2)提取特征向量
特征向量x为由以下数据组成的数组,
x1:数字的端点数(8邻域内黑点数目为3(包括本身)),
x2:数字一般连接点(字轨迹长度),
x3:数字分叉点(8邻域内黑点数目大于3(包括本身)),
x4~x19:16条水平线分别与数字的交点个数,
x20~x35:16条垂直线分别与数字的交点个数 。
3)匹配确定数字
假设标准数字(0~9)的特征向量已知为y,则匹配的准则为计算x和y的相似度r.
r=Σ(xi*yi)/sqrt(Σ(xi^2)*Σ(yi^2));【sqrt为平方根运算】
r数字越接近于1,表明y和x越相似,即未知字符最有可能为y对应的标准数字。
下面是matlab代码:
solve.m
1 function x=solve(ju)
2 x=zeros(
1,
35);
3 [m,n]=size(ju);
4 ju1=zeros(m+
2,n+
2);
5 ju1(
2:m+
1,
2:n+
1)=ju(:,:);
6 for i=
2:m+
1 7 for j=
2:n+
1 8 if ju1(i,j)==
1 9 temp=ju1(i-
1:i+
1,j-
1:j+
1);%邻域矩阵
10 [ii,jj]=find(temp==
1);
11 if max(size(ii))>=
412 x(
3)=x(
3)+
1;
13 else if max(size(ii))==
314 x(
2)=x(
2)+
1;
15 else16 x(
1)=x(
1)+
1;
17 end
18 end
19 x(i+
3)=x(i+
3)+
1;
20 x(j+
19)=x(j+
19)+
1;
21 end
22 end
23 end
值得注意的是,上面第5行用ju1代替ju,是为了方便判断边缘上的点(ju此时寻找8邻域会出错),所以在四周加上了无关‘边’。
pipei.m
1 function result=pipei(a,j)
2 sta=[
0 27 5 0 1 3 2 2 2 2 2 2 2 2 2 2 2 2 3 1 0 0 0 0 0 11 5 2 2 3 9 0 0 0 0 3 2 14 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 15 1 0 0 0 0 0 0 4 2 18 3 0 0 0 3 2 2 1 1 1 1 1 1 1 1 1 5 2 0 0 0 0 0 6 4 3 3 4 3 0 0 0 0 5 3 18 4 0 0 3 2 1 1 1 1 4 1 1 1 2 2 2 3 0 0 0 0 0 0 1 3 3 3 3 9 3 0 0 0 6 3 20 6 0 1 1 1 1 2 2 2 2 2 2 2 2 6 1 1 1 0 0 0 0 0 4 4 2 4 13 1 1 0 0 0 7 3 23 3 0 5 1 1 1 1 4 2 2 1 1 1 2 2 2 3 0 0 0 0 0 0 6 4 3 3 3 9 1 0 0 0 8 1 24 4 0 0 3 2 1 1 1 5 2 2 2 2 2 2 2 2 0 0 0 0 0 0 11 3 3 3 4 5 0 0 0 0 9 2 17 3 0 6 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 1 1 5 6 4 4 1 0 0 010 0 23 13 0 1 4 2 2 2 2 2 4 2 2 2 2 2 2 4 1 0 0 0 0 0 8 8 3 3 6 8 0 0 0 011 1 28 4 0 0 4 2 2 2 2 2 2 2 3 3 1 2 2 4 0 0 0 0 0 0 9 3 3 3 3 12 0 0 0 0];
12 sx2=
0;sy2=
0;sxy=
0;
13 for i=
1:
3514 sx2=sx2+a(i)*a(i);
15 sy2=sy2+sta(j,i)*sta(j,i);
16 sxy=sxy+a(i)*sta(j,i);
17 end
18 result=sxy/sqrt(sx2*sy2);
19
上面的sta数据为测试所得,由于标准有异,读者可以自己收集数据,调试。
完整的主程序如下:
View Code
1 clear all;
2 clc;
3 test=input('Please input a digits image:','s'); %输入图像
4 %z=input('z:','s');
5 x=imread(test);
6 if ~isgray(x)
7 x=rgb2gray(x); %必须转换为灰度图像
8 end
9 xbw=im2bw(x,0.9); %再转换为二值图像
10 xbw=medfilt2(xbw); %中值滤波
11 bw=xbw; %滤波后二值图像
12
13 figure;imshow(bw);
14
15 for n=1:100
16 [c,l]=size(bw);
17 [i,j] = find(bw==0);
18 jmin1=min(j);
19 xbwt=bw(:,jmin1:l); %xbwt为切除左边边缘的图像
20 [c,l]=size(xbwt);
21 [i,j]=find(all(xbwt(1:c,:)==1));
22 jmin2=min(j); %找右边界
23 if(isempty(jmin1)||isempty(jmin2)) break;
24 end
25 xbwn{n}=xbwt(:,1:jmin2-1); %xbwn{n+1}为第(n+1)个数字图像
26 bw=xbwt(:,jmin2:l); %bw迭代循环使用
27 [i,j] = find(xbwn{n}==0); %找上下边界
28 imin=min(i);
29 imax=max(i); %imin:数字最上点,imax:数字最下点
30 xbwn1{n}=xbwn{n}(imin:imax,:);
31
32 end
33 m=n-1;
34 for n=2:m
35 [i,j]=size(xbwn1{n});
36 p=-1.*xbwn1{n}+ones(i,j); %先将矩阵取反
37 x=16;
38 p1=ones(x,x);
39 %xbwn2{n}=bwmorph(p,'skel',Inf);
40 xbwn2{n}=xbwn1{n};
41 %转换到x*x矩阵
42 rate=x/max(size(xbwn2{n}));
43 xbwn3{n}=imresize(xbwn2{n},rate);
44 [i,j]=size(xbwn3{n});
45 i1=round((x-i)/2);
46 j1=round((x-j)/2);
47 p1(i1+1:i1+i,j1+1:j1+j)=xbwn3{n};
48 p1=ones(x,x)-p1;
49 p1=bwmorph(p1,'skel',Inf);
50 p2{n}=p1;
51
52 temp1=solve(p1);
53 temp2=0;
54 temp_index=0;
55 for tt=1:10
56 temp3=pipei(temp1,tt);
57 if temp3>temp2
58 temp2=temp3;
59 temp_index=tt-1;
60 end
61 end
62 temp_index %显示数字
63
64 figure,imshow(p1);
65 end
参考论文:
基于图像识别技术的手写数字识别方法 吴忠1,2,朱国龙1,2,黄葛峰1,2,吴建国1,2
需要完善的地方:
1.评判机制
可以适当调整x1~x3等的权系数,使得相似度测量更精确;
2.标准手写字体的数据收集
本实验只是找了一个印刷体的数据得到了sta数组;
3.字符的分割
中值滤波不一定能很好的滤去一些端状杂质,需要根据高度和宽度大小限制再进一步滤波。
最后,有不当之处,希望园友指正。
转载请注明:http://www.cnblogs.com/blue-lg/archive/2012/03/05/2381078.html