【身份证识别】基于matlab GUI形态学二代身份证号码识别系统【含Matlab源码 948期】
一、身份证号码识别简介
1 引言
作为居民身份的象征,身份证是居民身份的唯一标识,它已成为生活中必不可少的证件。在火车站、酒吧等公共场所,流动人口大人员复杂,警察需要对公民的身份证进行核对,排除可疑人员。为了提高警察的办事效率,提出通过手机终端将居民的身份证号码进行自动识别。
身份证号码的自动识别技术属于图像数字识别领域,目前国内外采用得较多的方法主要有两类:基于模板匹配的 OCR 算法和基于人工神经网络的OCR算法,其中尤以模板匹配法应用最为广泛。模板匹配法主要是通过提取目标的特征(包括灰度特征、形状特征、网格
特征等)与模板库中的模板进行匹配计算它们的相似度或者欧式距离,当满足一定的阈值时就认为目标与该模板一致。模板匹配法通常用于目标特征容易提取且模板库比较小的情形,但模板匹配法容易受噪声干扰而降低识别的准确度,在提取的特征个数较多时匹配所需时间也会延长。
2 基本原理
身份证号码识别技术的基本原理如图 1 所示。首先需要扫描样本图像以获得样本字符的特征以放入特征库中,因为第二代居民身份证号码的字体比较特殊,需要从样本图像中获取数字 0~9及字母 x的特征作为特征库,其次扫描待识别图像提取待识别字符的特征与特
征库中的字符特征进行匹配从而得到识别结果。图像处理中常用到的方法有图像灰度化、二值化、基于投影的字符分割等算法。常常提取的字符特征包括直接将字符图像作为字符特征或将变换后的图像作为特征,将图像在水平及垂直方向上的投影作为特征,还有字
符的笔画密度特征、外围特征、统计特征等;在字符识别这一步中常见的方法有:结构模式识别、统计模式识别、统计识别与结构识别的结合、人工神经网络、基于改进 Adaboost的方法等。
图 1 身份证号码识别技术的基本原理图
本文在获取字符的灰度图像后,在灰度多值化的基础上提取字符的网格特征,再利用模板匹配的方法进行识别。具体的流程包括身份证边缘定位,身份证号码区域定位及灰度归一化,身份证号码单个字符分割,特征提取和身份证号码识别等,如图 2 所示为身份证号码识别的具体流程。
图2 身份证号码识别的处理流程
2.1 居民身份证边缘定位
第二代居民身份证的大小是固定的,宽度为8.6 cm,高度为 5.4 cm,但是在获取身份证图像时,由于焦距的远近,不能利用它的大小信息来获取身份证在图像中所处的位置。如图 3(a)所示为通过摄像头获得的居民身份证图像,在这一步中要求背景比较简单,能和身份证形成明显对比,且身份证在图像中的位置要大体居中不能过小,以保证在图像的横向及纵向 3/8、4/8、5/8处能检测到居民身份证,这将影响到定位的结果。为了确定身份证在图像中的位置,需要对图像进行二值化,但考虑到如果对整张图像进行灰度化及二值化会消耗很多时间,分别在纵向及横向上在图像的 3/8、4/8 及 5/8 处截取宽度为 40 个像素的区域进行灰度化及二值化运算,截取的区域如图 3(b)及 3(c)所示。常用的灰度化处理方法有加权平均法、平均值法和最大值法等。这里采用加权平均法[14],根据重要性及其他指标,将 R、G、B 三个分量以不同的权值进行加权平均。由于人眼对绿色的敏感度最高,对蓝色敏感度最低,因此,可以按公式(1),对 RGB 三个分量进行加权平
均能得到较合理的灰度图像。f (ij) = 0.299R(ij) + 0.587G(ij) + 0.114B(ij)) (1)式中,f (ij) 为像素的灰度值,R(ij) 为彩色图像中 R分量的值,G(ij) 为彩色图像中 G 分量的值,B(ij) 为彩色图像中 B 分量的值。截取的纵向及横向区域做灰度化运算后的结果如图 4(a)所示。对于二值化处理,采用了迭代法进行二值化,其思想是利用迭代的方法最终找到一个最佳的分割阈值。对于身份证图像中所选取的区域,首先遍历区域中所有
点的灰度值,找出该区域内的最大灰度值 Pmax 和最小灰度值 Pmin ,取阈值初值 T0 = (Pmax + Pmin)/2 。根据 T0 将图像分割为两部分并且分别求出各部分的灰度均值 Po 和 Pb ,进而计算新阈值 Ti + 1 = (Po + Pb)/2 。若 Ti + 1 = Ti ,则取阈值为 Ti ,否则返回继续循环迭代。在灰度图像中灰度值 i 在[0,255]这个区间内,Si 表示灰度值为 i 的像素点的个数,Ti 表示阈值,那么则有:
经过二值化运算后的区域,在边缘处与背景区分得很明显,如图 4(b)为纵向区域在二值化后的结果。再通过从上到下的搜索及从下向上的搜索就很容易确定居民身份证上下边缘在图像中 3/8、4/8 及 5/8 处的三个边缘点坐标。
如图 5 所示为根据二值化结果确定的居民身份证上下边缘三个点的位置,分别为上边缘 A1(左)、A2(中)、A3(右)及下边缘 B1(左)、B2(中)、B3(右)。由于光照等其他因素的影响,寻找到的边缘点并不一定是身份证的真实边缘点,这个时候需要排除其中一对点,该方法默认在中间区域即图像 4/8 处所确定的边缘点是准确的,只需要排除另一对点 A1、B1 或 A3、 B3 。以 A2、B2 为基准点,可以求得四条斜率 LA1A2、 LA3A2、LB1B2、LB3B2 ,当 |LA1A2 - LB1B2| < |LA3A2 - LB3B2| 时,则舍掉 A3、B3 这一对点,反之则舍掉 A1、B1这一对点,最后将得到上下边缘的各两个点作为居民身份证的上下边缘点,斜率 L 作为身份证在图像中旋转的斜率。同样,利用同样的方法也可以得到居民身份证左右边缘的各两个点。在知道居民身份证各条边上的两个点及求得斜率,身份证在图像中的位置也就确定了。
2.2 身份证号码区域定位及灰度归一化
身份证号码在居民身份证上的位置是固定的,在拍照获取照片时不管焦距如何变化,身份证号码所在区域相对于身份证本身的位置是不变的,它具有一定的比例关系。根据这一比例关系截取一包含身份证号码的矩形区域,如果身份证发生了旋转,即斜率 L 大于经验值
0.035时,则以该斜率为参考将矩形区域进行矫正。图 6所示为截取的身份证号码区域在经过矫正后的彩色图像。
图 6 身份证号码彩色区域
对得到的彩色图像利用式(1)做灰度化处理,但是因为光照的原因,有时候图像整体偏暗,有时候图像又整体偏亮,这会影响到数字字符的特征提取及最后的识别效果。因此在对图像做灰度化处理后在进行字符分割前需要将号码区域进行灰度拉伸,使得像素的灰度值分布在 0到 255间,也就是灰度归一化。
灰度归一化的方法是:对于输入图像 f (xy) ,灰度级变换 T 将产生一个输出图像 g(xy) ,且 g(xy) 的每一个像素值都是由 f (xy) 的对应输入像素点的值决定的,g(xy) = T( f (xy)) 。首先找出原图像 f (xy) 灰度的最大值max及最小值min,得到系数 k = 1.0 ´ 255/(max - min) ,那 么 对 于 图 像 f (xy) 中 的 每 一 像 素 ,利 用 变 换 T : g(xy) = ( f (xy) - min)´ k ,即 得 到 输 出 图 像 g(xy) ,
图 7 灰度拉伸后的身份证号码区域灰度图像
2.3 单个字符的分割及大小归一化
单个字符的分割是在求得灰度图像的分割阈值后,根据阈值进行行投影及列投影以确定每个字符的左右及上下边界,将字符的左右及上下边界坐标影射到原灰度图像,即可得到每个字符的灰度图像。常用的分割方法有阈值法、区域生长法、分裂合并法、边缘检测法、人工神经网络法、分类器和聚类法等。在这里,对于定位得到的数字区域,首先根据迭代法找出将目标(数字)与背景分割开的阈值 t ,然后根据阈值t 将图像在垂直方向上进行投影,计算每一列上灰度值小于阈值 t 的个数,对于数字间的背景区域在垂直方向上的投影值为 0,即可得到数字的左右边界坐标,再根据数字的左右边界将每个数字进行水平方向上的投影,找到数字的上边缘及下边缘,这样就可以分割出每一个字符。其中阈值 t 的计算方法[15]与前文计算阈值的方法是一样,在这不再描述。
对于分割出来的字符灰度图像,由于输入图像不同分割得到的字符图像尺寸也不相同,即使是同一幅图像中每个字符图像的尺寸也不尽相同,因此还需要将字符图像进行大小的归一化,目的是要使得字符图像与模板图像的尺寸一致。通过双线性插值算法对字符图像作
大小归一化。双线性插值[16]是利用了需要处理的原始图像像素点周围的四个像素点的相关性,通过双线性算法计算得出的。对于一个目的坐标,通过向后映射法得到其在原始图像的对应的浮点坐标 (i + uj + v) ,其中 ij均为非负整数,uv 为[0,1]区间的浮点数,则这个像素的值 f (i + uj + v) 可由原图像中坐标为 (ij)、(i + 1j)、 (ij + 1)、(i + 1j + 1) 所对应的周围四个像素的值决定,即:f (i + uj + v) = (1 - u)´(1 - v)´ f (ij) +
(1 - u)´ v ´ f (ij + 1) + u ´(1 - v)´ f (i + 1j) + u ´ v ´ f (i + 1j + 1) (4)式中 f (ij) 表示源图像 (ij) 处的的像素值,以此类推,这就是双线性内插值法。对于图 7 分割得到的单个字符图像再进行大小归一化之后的结果如图 8,设定字符尺寸为 40×72(宽 40像素,高 72像素)
图 8 对分割得到的字符进行大小归一化后的结果
2.4 特征提取
灰度多值化是指灰度图像 A 按照一定的法则 f 将各个像素的灰度值从 0~255变换到 0~n - 1,这个过程称为灰度多值化或 n 值化,在数学上描述为:
其中式(5)中 G(xy) 为图像 A 中像素的灰度值,g(xy) 为 G(xy) 经过 f 运算后的值;式(6)表示图像 A 在经过f 运算后灰度的维数从 256 维变为 n 维,当 n = 2 时,这
个过程就是人们所熟悉的灰度二值化,当 n = 26 时就是灰度 26值化。
一幅灰度图像再进行二值化处理时,会损失掉很多信息,有时还会因为图像质量问题使得二值化结果不能正确地区分出目标与背景,那么在此基础上提取到的特征也就不能有效地代表字符。如图 9 为从身份证照片中截取到的号码区域,该区域比较模糊,但是肉眼还是
能将号码识别出来;图 10 为将该号码区域进行二值化处理的结果,可以看出二值化之后目标即号码和背景没有被正确地区分开来,像数字 6、8、9 在二值化之后中间的空白区域被填充了,中间的背景像素被理解成了目标像素,使得二值化结果不能正确地反映出图像原本的信息。为此,提出将图像进行多值化处理,而不是二值化,图像中的像素被分为多类而不仅仅是被分为两类,这样可以适当减少信息的损失,在此基础上提取到的特征也
更能有效地区分出字符。
图 9 身份证号码的模糊图像
图 10 模糊图像作二值化处理后结果
在得到身份证号码字符的灰度图像后,将字符图像作 26 值化处理。为什么要将灰度图像作 26 值化处理?在身份证号码的灰度图像中,对属于同一个小区域内的像素,同属于目标或背景的两个像素的灰度值一般是差别不大的,如图 11 为身份证号码所在区域的灰度图像,在图像中间作一条水平线横穿整个图像,通过统计这条水平线上各像素的灰度值,发现在非数字区域也就是背景区域各像素的灰度值都是相差不大,在 10 个灰度级以内,同样在数字区域各像素的灰度值也是相差 10 个灰度级以内,但是数字区域和背景区域的灰度值相差可以达到 60 个灰度级,因此可以认为灰度值变化在 10 个灰度级以内的像素是同一类,在此基础上提出将灰度图像的灰度值从 256个等级作降级处理变为 26个等级,也
就是进行 26 值化,这样既能将像素进行分类又不会使原图像所包含的信息损失太大。
图 11 身份证号码区域的灰度图像为了提取字符的网格特征,首先将字符图像分为大小一致互不重叠的若干个小网格,对于大小为 40×72(宽 40像素,高 72像素)的字符图像,先将字符图像划分M ´ N 个网格,这里 M = 10 ,N = 12 ,再利用公式(7)将数字字符的灰度图像做 26 值化,最后利用式(8)统计每个小网格中像素灰度在 26 值化后的平均值作为该方块的特征,这些特征组合在一起构成特征向量,即得到每个数字字符的二维灰度特征。
其中,式(7)是将像素的灰度进行26值化处理,f (xy) 为像素的原灰度值,g(xy) 为 26值化后得到的值,即灰度值从原来的 0~255 降为 0~25;式(8)是计算第 (ij) 个网
格的特征值 E(ij),H 和 W 分别为该小方块的高和宽,g(xy) 为该小方块中像素灰度 26值化后的值,0 x < W, 0 y < H ,0 i < M ,0 j < N 。那么字符的特征向量则
等于
2.5 字符识别
字符识别就是将待识别字符的特征向量与模板库中保存的每个字符的特征向量进行匹配,找到与待识别字符匹配的字符。本文用到的匹配方法是通过求相似度,相似度最大的即认为是与待识别字符匹配的字符。求相似度的方法如下:
式中 S 定义为相似度,Ts(ab) 为待识别字符的特征属性,To(ab) 为模板的特征属性。
二、部分源代码
function varargout = id_rec_gui(varargin)
% ID_REC_GUI MATLAB code for id_rec_gui.fig
% ID_REC_GUI, by itself, creates a new ID_REC_GUI or raises the existing
% singleton*.
%
% H = ID_REC_GUI returns the handle to a new ID_REC_GUI or the handle to
% the existing singleton*.
%
% ID_REC_GUI('CALLBACK',hObject,eventData,handles,...) calls the local
% function named CALLBACK in ID_REC_GUI.M with the given input arguments.
%
% ID_REC_GUI('Property','Value',...) creates a new ID_REC_GUI or raises the
% existing singleton*. Starting from the left, property value pairs are
% applied to the GUI before id_rec_gui_OpeningFcn gets called. An
% unrecognized property name or invalid value makes property application
% stop. All inputs are passed to id_rec_gui_OpeningFcn via varargin.
%
% *See GUI Options on GUIDE's Tools menu. Choose "GUI allows only one
% instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES
% Edit the above text to modify the response to help id_rec_gui
% Last Modified by GUIDE v2.5 28-May-2021 11:48:47
% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name', mfilename, ...
'gui_Singleton', gui_Singleton, ...
'gui_OpeningFcn', @id_rec_gui_OpeningFcn, ...
'gui_OutputFcn', @id_rec_gui_OutputFcn, ...
'gui_LayoutFcn', [] , ...
'gui_Callback', []);
if nargin && ischar(varargin{1})
gui_State.gui_Callback = str2func(varargin{1});
end
if nargout
[varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT
% --- Executes just before id_rec_gui is made visible.
function id_rec_gui_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% varargin command line arguments to id_rec_gui (see VARARGIN)
% Choose default command line output for id_rec_gui
handles.output = hObject;
% Update handles structure
guidata(hObject, handles);
% UIWAIT makes id_rec_gui wait for user response (see UIRESUME)
% uiwait(handles.figure1);
% --- Outputs from this function are returned to the command line.
function varargout = id_rec_gui_OutputFcn(hObject, eventdata, handles)
% varargout cell array for returning output args (see VARARGOUT);
% hObject handle to figure
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
% Get default command line output from handles structure
varargout{1} = handles.output;
% --- Executes on button press in browse.
function browse_Callback(hObject, eventdata, handles)
% hObject handle to browse (see GCBO)
% eventdata reserved - to be defined in a future version of MATLAB
% handles structure with handles and user data (see GUIDATA)
global pathname filenames
[filenames,pathname]=uigetfile({'*.bmp;*.jpg;*.png;*.gif','All Image Files';'*.*','All Files' },'MultiSelect','on');
if ~isequal(filenames,0)
init_controls(handles)
preprocess(handles)
process(handles)
end
function init_controls(handles)
global filenames current_select_idx
current_select_idx=1;
if ischar(filenames)
filenames={filenames};
end
if ischar(filenames) || length(filenames)==1
set(handles.img_idx,'String','')
set(handles.img_idx_slider,'Visible','off')
else
set(handles.img_idx_slider,'Visible','on')
set(handles.img_idx_slider,'Min',0)
set(handles.img_idx_slider,'Max',length(filenames)-1)
set(handles.img_idx_slider,'SliderStep',ones(1,2)/(length(filenames)-1))
end
set(handles.validate,'Enable','on')
function [org_img,img_gray,thresh_value]=read_id_card(filename)
fileinfo=imfinfo(filename);
if strcmpi(fileinfo.ColorType,'indexed')
[X,map]=imread(filename);
org_img=ind2rgb(X,map);
else
org_img=imread(filename);
end
r=size(org_img,1);
c=size(org_img,2);
size_thresh=2000;
if r>size_thresh
org_img=imresize(org_img,size_thresh/r);
end
if c>size_thresh
org_img=imresize(org_img,size_thresh/c);
end
org_img=im2double(org_img);
if ndims(org_img)==3
img_gray=rgb2gray(org_img);
else
img_gray=org_img;
end
img_gray=imresize(img_gray,[350 500]);
img_gray=img_gray(round(size(img_gray,1)*2/3):end,round(size(img_gray,2)/4):end);
thresh_value=.68*graythresh(img_gray);
function preprocess(handles)
global pathname filenames current_select_idx img_gray
if length(filenames)>1
img_idx_str=sprintf('%d / %d',current_select_idx,length(filenames));
set(handles.img_idx,'String',img_idx_str)
set(handles.img_idx_slider,'Value',current_select_idx-1)
set(handles.img_idx_slider,'TooltipString',img_idx_str)
end
try
filename=fullfile(pathname,filenames{current_select_idx});
[org_img,img_gray,thresh_value]=read_id_card(filename);
axes(handles.img)
imshow(org_img),title(filename,'Interpreter','None')
set(handles.thresh_value,'Visible','on')
set(handles.thresh_value,'value',thresh_value)
catch e
msgbox(sprintf('Cannot read the image: %s.\n\n',filename,e.message),'Error','error')
rethrow(e)
end
function process(handles)
global code_stats img_gray model filenames current_select_idx area_codes training_data_size
if isequal(filenames,0) || isempty(filenames)
return
end
% init all controls
for i=1:18
set(eval(sprintf('handles.result%d',i)),'BackgroundColor',[1 1 1])
set(eval(sprintf('handles.result%d',i)),'Enable','Inactive')
set(eval(sprintf('handles.result%d',i)),'String','')
end
set(handles.birthday,'BackgroundColor',[1 1 1])
set(handles.birthday,'String','')
set(handles.ID_code,'BackgroundColor',[1 1 1])
set(handles.ID_code,'String','')
set(handles.gender,'BackgroundColor',[1 1 1])
set(handles.gender,'String','')
set(handles.address,'BackgroundColor',[1 1 1])
set(handles.address,'String','')
set(handles.save_results,'Enable','off')
% process
thresh=get(handles.thresh_value,'value');
[id_codes,id_bw,code_stats,thresh,iteration]=id_rec_process(img_gray,model,thresh,training_data_size,1);
% if isempty(id_codes)
% msgbox('Cannot recognize ID codes. You can try adjusting the threshold value.','Warning','warn','modal')
% return
% end
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
三、运行结果
四、matlab版本及参考文献
1 matlab版本
2014a
2 参考文献
[1] 蔡利梅.MATLAB图像处理——理论、算法与实例分析[M].清华大学出版社,2020.
[2]杨丹,赵海滨,龙哲.MATLAB图像处理实例详解[M].清华大学出版社,2013.
[3]周品.MATLAB图像处理与图形用户界面设计[M].清华大学出版社,2013.
[4]刘成龙.精通MATLAB图像处理[M].清华大学出版社,2015.
[5]李开,陈礼安,曹计昌.基于灰度多值化的身份证号码识别[J].计算机工程与应用. 2015,51(13)
文章来源: qq912100926.blog.csdn.net,作者:海神之光,版权归原作者所有,如需转载,请联系作者。
原文链接:qq912100926.blog.csdn.net/article/details/117392094
- 点赞
- 收藏
- 关注作者
评论(0)