0
点赞
收藏
分享

微信扫一扫

【matlab算法原理详解】车牌识别算法

独兜曲 2022-04-30 阅读 64

详解车牌识别Matlab算法

首先是项目配置:
第一行:functionvarargout = LicenseRecognition(varargin)
分别表示主函数的输出、函数名称、函数输入
在% Begin initialization code - DO NOT EDIT
% End initialization code - DO NOT EDIT
这两行注释符之前的代码是编辑菜单时自动生成的,不需要修改
functionLicenseRecognition_OpeningFcn(hObject, eventdata, handles, varargin)
%系统自动生成
handles.output = hObject;%系统自动生成
——————————————————————————————————
%以下代码为手工添加,表示使用handles结构体来保存图形界面中各种对象的句柄,或者中间结果,这些句柄或者中间结果在创建图形对象或运算中产生,需要在以后的回调函数中多次用到,所以这里的作用类似于全局变量的作用,用来进行数据的传递。
handles.imgIn = [];
handles.imgOut = [];
handles.flag = 0;%判断标志
handles.pos = [];%图形对象所在位置
handles.lef = 1;
handles.top = 1;
handles.wid = 0;
handles.hig = 0;
——————————————————————————————————
% Update handles structure 自动生成,用来将上面定义的数据
guidata(hObject, handles);
%自动生成,定义输出
% — Outputs from this function are returned to the command line.
functionvarargout = LicenseRecognition_OutputFcn(hObject, eventdata, handles)

varargout{1} = handles.output;

下面再讲解下车牌识别算法的每个步骤

  1. 打开读取文件
    functionMenu_File_Open_Callback(hObject, eventdata, handles)%该行为自动生成
    [fname,pname] = uigetfile({‘.bmp;.jpg;.tif’,'All IMAGE Files (.bmp, *.jpg, *.tif)’},‘Open an input image’);%弹出打开文件的对话框
    strPath = [pname fname];%设置文件名和路径名
    if~isempty(strPath)
    [img,cmap] = imread(strPath);%读取文件数据
    [hig,wid,page] = size(img);%获得图像大小
    ifpage == 3 %假设图像是RGB彩色
    imgIn = rgb2gray(img);%转换为灰度图象
    else
    imgIn = img;
    end
    handles.imgIn = imgIn;%将结果赋给全局变量,以便交给下一个函数处理
    handles.flag = 1 %标志为1表示数据已经读取
    % pos = get(handles.mainFrm,‘Position’);
    figure(handles.mainFrm);%创建图形界面,用来显示图像
    set(handles.mainFrm,‘Position’,[400,300,wid,hig]);%设置该图形界面的显示位置
    image(imgIn);%显示图像,其和imshow功能类似,但带有默认的调色板
    colormap(gray(256));%将其默认调色板更改为256级灰度
    handles.pos = [400,300,wid,hig];%将图形界面的位置保存到hdl结构体中
    guidata(hObject,handles);%保存图形界面数据
    set(handles.Menu_EdgeDetect,‘Enable’,‘On’);%设置下一个菜单为可点击的亮色
    end

  2. 完成菜单退出功能
    functionMenu_File_Exit_Callback(hObject, eventdata, handles)
    ifhandles.flag == 1
    str = {‘Are you sure about exiting the program?’,‘If so, the data will be lost!’};
    ret = questdlg(str,‘Warning’);
    switchret
    case’Yes’
    delete(gcf);
    case’No’,‘Cancel’
    return;
    end
    end

  3. 边缘检测功能实现
    functionMenu_EdgeDetect_Callback(hObject, eventdata, handles)
    if~isempty(handles.imgIn) %检查图像数据是否非空
    imgIn = double(handles.imgIn); %在处理之前将图像数据类型转换为double型
    % BW = uint8(255 * edge(handles.imgIn,‘roberts’));
    % handles.imgOut = BW;
    % guidata(hObject,handles);
    % set(handles.mainFrm,‘Position’,handles.pos);
    % image(BW);
    % colormap(gray(256));
    filt = fspecial(‘sobel’); %构建soble算子的模板
    horz = conv2(imgIn,filt,‘same’);%进行卷积运算
    % Cs = conv2(A,B,‘same’) Cs is the same size as A: 3-by-3
    vert = conv2(imgIn,filt’,‘same’);
    imgOut = uint8(max(horz,vert));% 取水平和垂直方向运算结果的最大值
    % imgOut = uint8(sqrt(horz.^2 + vert.^2));
    handles.imgOut = imgOut;
    handles.flag = 1;
    guidata(hObject,handles);
    figure(handles.mainFrm);
    set(handles.mainFrm,‘Position’,handles.pos);
    image(imgOut);
    colormap(gray(256));
    set(handles.Menu_RoughLocate,‘Enable’,‘On’);
    set(handles.Menu_FineLocate,‘Enable’,‘Off’);
    set(handles.Menu_CharacterCutout,‘Enable’,‘Off’);
    set(handles.Menu_LicenseRecognition,‘Enable’,‘Off’);
    end

  4. 车牌粗定位
    % 车牌粗定位:根据车牌区域在水平方向灰度值具有明显频繁的跳变,可求边缘提取后水平方向的差分,然后进行水平方向上的投影,也即沿水平方向进行相邻像素差分值的累加,绘制的投影图横轴为图像高度,原点为左上角,纵轴即为沿水平方向的差分值累加和。
    在这里插入图片描述
    functionMenu_RoughLocate_Callback(hObject, eventdata, handles)
    if~isempty(handles.imgOut)
    imgIn = double(handles.imgOut);
    [hig,wid] = size(imgIn);
    diff_horz = zeros(hig,wid);%构建一个与图像大小一样的全为0的矩阵,存储水平方向相邻像素的差值
    diff_horz = abs(imgIn(:,1:wid-1) - imgIn(:,2:wid));%类似第1列像素减第2列像素,第2列减第3列,依次减下去
    cum_horz = sum(diff_horz’);%对图像矩阵先转置,再投影
    figure;%绘制投影图形,横轴为1到图像高,纵轴为水平方向差分值累计和
    bar(1:hig,cum_horz,‘r’);
    title(‘horizontal projection’);
    % diff_vert = zeros(hig,wid);
    % diff_vert = abs(imgIn(1:hig-1,:) - imgIn(2:hig,:));
    % cum_vert = sum(diff_vert);
    % figure;
    % bar(1:wid,cum_vert,‘b’);
    % title(‘vertical projection’);
    %通过观察投影图,给出车牌的大概位置是,左上角坐标为(85,225),宽度为60,高度为20。
    lef = 85; top = 225; wid = 60; hig = 20;
    handles.lef = lef; %将检测结果传递给hdl结构体,以传递到下一个微定位的函数
    handles.top = top;
    handles.wid = wid;
    handles.hig = hig;
    imgOut = uint8(imgIn(top:top+hig-1,lef:lef+wid-1));%按刚才给的位置大小提取出车牌,转换为无符号整型,保存到imgOut中
    handles.imgOut = imgOut;
    handles.flag = 1;
    guidata(hObject,handles);
    figure(handles.mainFrm);
    set(handles.mainFrm,‘Position’,handles.pos);
    image(imgOut);
    colormap(gray(256));
    axisoff;
    set(handles.Menu_FineLocate,‘Enable’,‘On’);
    set(handles.Menu_RoughLocate,‘Enable’,‘Off’);
    set(handles.Menu_CharacterCutout,‘Enable’,‘Off’);
    set(handles.Menu_LicenseRecognition,‘Enable’,‘Off’);
    end

  5. 车牌微定位
    在粗定位结果的基础上,还需要把红色边框外的图像去掉,以进一步确定字符范围,缩减车牌的左右上下边界,以便后续字符处理。
    functionMenu_FineLocate_Callback(hObject, eventdata, handles)
    if~isempty(handles.imgOut)
    imgIn = double(handles.imgOut);%传入粗定位的结果图像
    [hig,wid] = size(imgIn);% 取图像大小
    lef_tem = wid * ones(hig,1);% 构建一个hig行,1列的值全为1的矩阵,与wid相乘,矩阵值全部为wid
    rig_tem = zeros(hig,1);%构建一个hig*1的0矩阵
    fori = 1:hig
    forj = 1:wid-1%从左到右扫描,遇到相邻像素的灰度值差值大于60时,停止扫描,记下列号,说明此列是车牌左边界
    tem = imgIn(i,j+1) - imgIn(i,j);
    iftem >= 60
    lef_tem(i) = j+1;
    break;
    end
    end
    %从右向左扫描,遇到相邻像素的灰度值差值大于60时,停止扫描,记下列号,说明此列是车牌右边界
    forj = wid : -1 : 2
    tem = imgIn(i,j-1) - imgIn(i,j);
    iftem >= 60
    rig_tem(i) = j-1;
    break;
    end
    end
    end4
    lef = min(lef_tem);%每一行都可以扫描得到一个左边界,取其中最小的
    rig = max(rig_tem);%每一行都可以扫描得到一个右边界,取其中最大的
    %这也是lef_tem、rig_tem在初始化定义的时候分别为ones全为1的矩阵和全为zeros 0的矩阵的原因
    top_tem = hig * ones(wid,1);
    bot_tem = zeros(wid,1);
    forj = lef:rig
    fori = 1:hig-1
    tem = imgIn(i+1,j) - imgIn(i,j);
    iftem >= 60
    top_tem(j) = i+1;
    break;
    end
    end
    fori = hig : -1 : 2
    tem = imgIn(i-1,j) - imgIn(i,j);
    iftem >= 60
    bot_tem(j) = i-1;
    break;
    end
    end
    end
    top = min(top_tem); %按同样的方法找到上下边界,注意图像左上角为原点
    bot = max(bot_tem);
    handles.lef = handles.lef + lef - 1; %在原始图像上定位微定位后车牌的位置
    handles.top = handles.top + top - 1;
    handles.wid = rig - lef + 1;
    handles.hig = bot - top + 1;
    %将微定位后的图像数据取出来
    imgOut = uint8(imgIn(top:bot,lef:rig));
    handles.imgOut = imgOut;
    handles.flag = 1;
    guidata(hObject,handles);
    figure(handles.mainFrm);
    set(handles.mainFrm,‘Position’,handles.pos);
    image(imgOut);
    colormap(gray(256));
    axisoff;
    set(handles.Menu_CharacterCutout,‘Enable’,‘On’);
    set(handles.Menu_RoughLocate,‘Enable’,‘Off’);
    set(handles.Menu_FineLocate,‘Enable’,‘Off’);
    set(handles.Menu_LicenseRecognition,‘Enable’,‘Off’);
    end

  6. 字符分割
    functionMenu_CharacterCutout_Callback(hObject, eventdata, handles)
    ifhandles.wid ~= 0
    lef = handles.lef;%注意:这里是微定位后车牌在原始图像上的位置
    top = handles.top;
    wid = handles.wid;
    hig = handles.hig;
    %此处imgIn是在原始图像上取微定位后车牌位置区域的数据产生的
    imgIn = double(handles.imgIn(top:top+hig-1,lef:lef+wid-1));
    % maxvalue = max(imgIn());
    % minvalue = min(imgIn());
    % imgOut = ~(imgIn >= (maxvalue + minvalue+30)/2);
    imgOut = ~JudgeAnalysis(imgIn); %对图像进行二值化,然后反转
    [label,num] = bwlabel(imgOut); %对二值图像进行连通成分的标记
    %对于以上两个中间变量的结果,大家可以在Matlab的workspace中查看其结果变化,理解
    k = 0;
    fori = 1:num %以下代码为找到像素总数小于10的连通成分,剔除,这里6个字符,但有7个连通成分,剔除其中较少像素的连通成分
    tem = (label == i); %把所有label为i的像素统计出来
    ifsum(tem()) <= 10
    imgOut(find(label == i)) = 0;
    k = k+1;
    end
    end
    num = num - k; %减去剔除的连通成分的个数
    proj_vert = sum(imgOut); % 对图像每一列求和,也即垂直投影
    ifproj_vert(1) ~= 0 %如果第一列的投影不为0,说明第一列的像素中有字符
    coordx(1) = 1;
    k = 1;
    else
    k = 0;
    end
    %以下代码说明:如果第后一列像素为0,而前一列的像素不为0,则说明此处是字符的右边界
    %如果前一列像素为0,后一列不为0,说明此处是字符的左边界
    forj = 2:wid-1
    tem1 = proj_vert(j-1) - proj_vert(j);
    tem2 = proj_vert(j+1) - proj_vert(j);
    if(proj_vert(j) == 0) & (tem1 >= 1)
    k = k+1;
    coordx(k) = j-1;
    elseif(proj_vert(j) == 0) & (tem2 >= 1)
    k = k+1;
    coordx(k) = j+1;
    end
    end
    %如果倒数第二列不为0,倒数第1列为0,则说明倒数第2列为边界
    if(proj_vert(wid-1) ~= 0) & (proj_vert(wid) == 0)
    k = k+1;
    coordx(k) = wid-1;
    end
    %k代表总共有k条边界
    k = length(coordx);
    set(handles.mainFrm,‘Position’,handles.pos);
    image(uint8(255*(~imgOut))); %将其反转回来在0-255之间显示
    colormap(gray(256));
    axisoff;
    holdon;
    forj = 1:k
    x = coordx(j) * ones(1,hig);%将x的数目变成和y一样多,组成(x,y)坐标点对
    y = 1:hig;
    plot(x,y,‘b’);
    holdon;
    end
    holdoff;
    %根据上面所找边界线,对字符进行实际的分割
    forj = 1:num
    tem_wid(j) = coordx(2j) - coordx(2j-1) + 1; %每个字符由一对边界线分割而成,由右边边界线减去左边边界线的位置,得到的是此字符的宽度
    tem = imgOut(:,coordx(2j-1):coordx(2j));%取出每一字符的实际数据
    proj_horz = sum(tem’);%对该字符的行求累加值
    fork = 1:hig %从上往下扫描,找出第j个字符的上边界
    ifproj_horz(k) ~= 0
    tem1 = k;
    coordy(j,1) = tem1;
    break;
    end
    end
    fork = hig-1:1 %从下往上扫描,找出第j个字符的下边界
    ifproj_horz(k) ~= 0
    tem2 = k;
    coordy(j,2) = tem2;
    break;
    end
    end
    tem_hig(j) = tem2 - tem1 + 1; %找出每一字符的高度
    end
    %以最大的宽和高来统一每个字符的大小
    maxwid = max(tem_wid); %总共有6个字符,找出其中最宽的
    maxhig = max(tem_hig); %总共有6个字符,找出其中最高的
    fork = 1:num
    lef = coordx(2k-1); rig = coordx(2k);
    top = coordy(k,1); bot = coordy(k,2);
    wid = rig - lef + 1; hig = bot - top + 1;
    Norm_Char(1:maxhig,1:maxwid,k) = ones(maxhig,maxwid);%初始化为大小统一
    Norm_Char(1:hig,1:wid,k) = ~imgOut(top:bot,lef:rig);%取出每一字符数据
    end
    handles.Norm_Char = Norm_Char;
    handles.flag = 1;
    guidata(hObject,handles);
    % for k = 1:num
    % figure;
    % image(uint8(255*Norm_Char(:,:,k)));
    % colormap(gray(256));
    % axis off;
    % end
    set(handles.Menu_LicenseRecognition,‘Enable’,‘On’);
    set(handles.Menu_RoughLocate,‘Enable’,‘Off’);
    set(handles.Menu_FineLocate,‘Enable’,‘Off’);
    set(handles.Menu_CharacterCutout,‘Enable’,‘Off’);
    end

  7. 字符识别
    %读取事先定义好的模板,将分割后的字符与模板进行匹配,取相关系数最大的。
    functionMenu_LicenseRecognition_Callback(hObject, eventdata, handles)
    if~isempty(handles.Norm_Char)
    Norm_Char = handles.Norm_Char;
    template(:,:,1) = double(imread(‘FY10_0.bmp’));
    template(:,:,2) = double(imread(‘FY10_1.bmp’));
    template(:,:,3) = double(imread(‘FY10_3.bmp’));
    template(:,:,4) = double(imread(‘FY10_F.bmp’));
    template(:,:,5) = double(imread(‘FY10_M.bmp’));
    template(:,:,6) = double(imread(‘FY10_P.bmp’));
    num = size(Norm_Char,3); %返回第3维的个数
    result = [];
    fork = 1:num
    str = recognition(Norm_Char(:,:,k),template);
    result = [result str’ '];
    end
    msgbox({‘The recognition result is:’,result},‘Recognition of the license’);
    end
    %模板比对函数
    functioncout = recognition(cin,template)
    d=zeros(6,1);
    fori = 1:6
    d(i)=corr2(cin,template(:,:,i));%求相关系数
    end
    e = find(d == max(d));
    switche
    case1
    cout =‘0’;
    case2
    cout =‘1’;
    case3
    cout =‘3’;
    case4
    cout =‘F’;
    case5
    cout =‘M’;
    case6
    cout =‘P’;
    end

举报

相关推荐

0 条评论