1.一维码简述;

一维条码是一种能用于信息编码和信息自动识别的标准符号,是由一组宽度不同的黑白符号按一定规则交替排列编码组成的图形符号,用于表示一定的信息。

码制指条码符号的类型,不同的类型有不同的编码规则。我们本次实验是基于EAN-13码制。EAN-13码主要由起始符(3)、左侧数据符(42)、中间分割符(5)、右侧数据符(42)、校验符、终止符(3)组成,一共95个模块,表示13个字符。条表示1,空表示0;只能表示0-9这十个数字;每个字符的宽度为7个模块,交替由两个条和两个空组成,每个条或者空的宽度不超过4个模块。起始符101,中间分割符01010,终止符101.

我完成的这个识别程序能解析的条码类型包括标准、受噪声污染以及倾斜的一维码图像。

2.解码方法(分为图像处理和译码两个部分);

2.1 图像处理

2.1.1 用imread()方法载入需要验证的一维码图像;

2.1.2 将载入的RGB三通道图像转化为灰度图像,每个像素点取值范围为0-255,共有256个灰度级别。用rgb2gray()函数可得到灰度图:

2.1.3 用大津法求阀值进而从灰度图像得到二值图,二值图的像素点取值范围不是0就是1,利于我们后续的译码操作.求阀值用graythresh()函数,求二值图用im2bw()函数:

2.1.4 接下来可以对图像进行滤波去噪以及图像校正,这一部分将在后文详细描述。这里先只讨论标准一维码的图像。

2.2 译码

2.2.1 获取条和空的宽度:这里的思路是遍历图像的每一个像素点,在一行中,当遇到像素值与其后一个点像素值不等的时候,记录其位置;后面的位置减去前面的位置,既可以得到条或空的宽度。对于一张标准的一维码图像,边界区域有60个,所以每一行应该有59个条/空的宽度值,当某一行的宽度值不等于59时,忽略该行。同时在这一步做了一个优化操作:由于得到的二值图中的条码的边界可能会出现锯齿和毛刺等现象,这就导致每次计算的宽度可能不一样,减少这个误差的方法是将所有有效行(59个宽度)的宽度相加后取平均值。相关代码如下:

 [m,n]=size(A);

 number=;

 for i=:m

     pos_cnt=;width_id=;

     for j=:n-

         if A(i,j) ~= A(i,j+)

             pos(i,pos_cnt)=j;

             if pos_cnt>

                 width(i,width_id)=pos(i,pos_cnt)-pos(i,pos_cnt-);

                 width_id=width_id+;

             end

             pos_cnt=pos_cnt+;

         end

     end 

     if width_id==

             number=number+;

             for k=:

         %将所有条/空的宽度都存储在total_len这个二维数组里

                   total_len(number,k)=width(i,k);

             end

         end

     end

 end
[mm,nn]=size(total_len);
for i=:nn
tmp=;
for j=:mm
tmp=tmp+total_len(j,i); %该宽度的所有值求和
end
final_width(,i)=tmp/mm; %求均值
end

2.2.2 获取单位模块宽度以及条空比例:前文已经提到,一维码图像包括95个图像,将上一步得到的全部宽度求和,除以95即可得到单位模块长度。然后将每个条/空的宽度除以单位模块宽度,即可得到条/空比例。这一步比较简单就不贴代码了。

2.2.3 对条和空区域进行 0/1标注:将条码区标注成 1,空白区标注成 0;这里需要注意的是,一个单位模块只能标注一种符号,条码和空白区域可能占据三四个单位模块。标注完成后,检查起始符( 101)、中间分割符( 01010)、终止符( 101)是否符合 EAN-13的条件,不符合则输入相应的判断信息,否则进行下一步:

 index=

 for i=:

     if mod(i,)==

         for j=::round(proposition(,i))

             mat95(,index)=;

             index=index+;

         end

     else

         for j=::round(proposition(,i))

             mat95(,index)=;

             index=index+;

         end

     end

 end

 isCheck=;

 if(mat95(,)==&&mat95(,)==&&mat95(,)==&&mat95(,)==&&mat95(,)==&&mat95(,)==&&mat95(,)==&&mat95(,)==&&mat95(,)==&&mat95(,)==&&mat95(,)==)

   isCheck=;

 end

  if isCheck==

     msgbox('不满足EAN-13码的条件!');  %不满足则弹出msg框,同时终止程序

     return

 end

2.2.4 查表译码:

 j=;

 for i=::

     left(,j)=bin2dec(num2str(mat95(:,i:i+)));

     j=j+;

 end

 k=;

 for i=::

     right(,k)=bin2dec(num2str(mat95(:,i:i+)));

     k=k+;

 end

查表得到左边和右边各 6个字符对应的 0-9字符,同时根据表格创建一个 Map:根据左边数据用 AB字符集序列得到前置位;部分代码如下 :

 checkLeft=[,,,,,,,,,,,,,,,,,,,];

 num_bar='';

 AB_check='';

 %以下求得左边序列以及AB序列

 for i=:

     for j=:

         if left(i)==checkLeft(j+)

             if j>

                 AB_check=strcat(AB_check,'B');

             else

                 AB_check=strcat(AB_check,'A');

             end

             num_bar=strcat(num_bar,num2str(mod(j,)));

         end

     end

 end

 %以下根据Map得到对应的前置位

 preMap = containers.Map({'AAAAAA','AABABB','AABBAB','AABBBA','ABAABB','ABBAAB','ABBBAA','ABABAB','ABABBA','ABBABA'},...

     {'','','','','','','','','',''});

 pre=preMap(AB_check);

 num_bar=strcat(pre,num_bar);

接下来就是检查校验位是否正确:将前面的12个数字的奇数位相加,得到一个数oddSum:

 oddSum=;evenSum=;

 for i=:

     if mod(i,)==

         oddSum=oddSum+str2num(num_bar(i));

     else

         evenSum=evenSum+str2num(num_bar(i));

     end

 end

  c=oddSum+*evenSum;

 if mod(c,)==

     checkBit=;

 else

     checkBit=-mod(c,);

 end

 %如果checkBit和13位的最后一位相等,则识别正确,否则错误。弹出相应信息

 if num2str(checkBit)==num_bar()

     msgbox('Okay')

 else

     msgbox('Failed');

 end

偶数位相加得到 evenSum,令 c=oddSum+3*evenSum,若 c的个位数为 0,则校验位为 0;否则校验位为 10-c%10.这里判断两个数是否相等时稍微注意一下是否是同一类型的。对应上文中的那张一维码图,检验结果如下:

3.所做的额外工作;

3.1 对于倾斜一维码图像的校正:

对于像上图这样的一维码图像,我们在遍历一行试图求条/空的宽度时,是无论如何也得不到正确结果的,因为图像倾斜后宽度都变长了。所以较好的做法是将这个图像摆正,摆正的关键是找到偏离角度。这里选用的hough直线检测方法。hough变换的主要思想是将该方程的参数和变量交换,对于直线y=kx+b,即用x,y作为参数,k,b作为变量,所以在直角坐标系中的直线y=kx+b在参数坐标上表示为点(k,b),而直角坐标上的点(x1,y1)则在参数坐标下表示为一条直线。此外,为了计算方便,将参数控件的坐标转换成极坐标进行运算。

所以,先将图片进行边缘检测,然后对图像上每一个非零像素点在参数坐标下变换为一条直线,然后根据统计方法找到聚集点即可。边缘检测可以使用edge()方法,这里使用的是canny边缘检测:

以上算法,matlab都帮我们封装好了.这里还有一个小技巧:因为我们需要验证的是一维码图像,一维码图像的特点是所有条/空都是两两平行的,所以我们根本没有必要找出所有的直线,而仅仅需要找出最长的那一条直线(其实无论哪一条都无所谓,对结果没什么影响)即可:

[H,T,R]=hough(BW);

P=houghpeaks(H,4,'threshold',ceil(0.3*max(H(:))));

这一句选取了4个峰值,即聚集点,所以对应到参数坐标上是四条直线;H对应的是theta和ρ的关系矩阵,两个参数分别代表极坐标中的夹角和到原点的距离。

lines=houghlines(BW,T,R,P,'FillGap',50,'MinLength',10);

这里就是利用hough()函数返回的参数值选取线段;参数50是一个正的标量,指定了与相同的hough变换相关的两条线段的距离,小于该距离则将线段合并;参数10是一个正的标量,指定合并的线是丢弃还是保留。lines里的成员是一个结构体,包含了线段端点的坐标等信息。

[L1,Index1]=max(Len(:));

   x1=[lines(Index1).point1(1) lines(Index1).point2(1)];

   y1=[lines(Index1).point1(2) lines(Index1).point2(2)];

   K1=-(lines(Index1).point1(2)-…

   lines(Index1).point2(2))/(lines(Index1).point1(1)-lines(Index1).point2(1))

angle=atan(K1)*180/pi

A = imrotate(I,90-angle,'bilinear');

先找到最长线段L1以及索引Index1,根据端点求出斜率K1,然后用反正切函数atan()找到偏离角angle。imrotate()默认逆时针旋转,所以最后的结果是将原二值图像逆时针转90-angle。图一是线段标识图,图二是校正后的图:

图一

图二

  这里还有一个很蛋疼的地方,可以发现经过旋转后的图比原图更大,而且四周出现了四个角,关键这四个角还是黑色的,这就会引起一个很严重的问题:在遍历图像某一行时,条/空的数量会比原来多(单单考虑图二的话,确切的说是所有有效行多了两条),画条线看的更清楚。所以在程序中这部分加了一个特判:对于旋转过的图像,计算宽度值的方法要和未旋转的图像区分开来,相关代码如下:

 %以下是针对校正的图像的
if angle~=
if width_id==
number=number+;
for k=:
total_len(number,k)=width(i,k+);
end
end
%以下是针对未校正的图像的
else
if width_id==
number=number+;
for k=:
total_len(number,k)=width(i,k);
end
end
end

当然,上述代码只是针对特定的图而言的,更一般的做法是:如果某一行的条/空总数大于59,如61,则要去掉第一个值和第61个值,保留中间的59个值作为有效值;如果总数等于59则满足要求;小于59则直接忽略改行。代码很容易,在这里不再赘述。

3.3 处理含有椒盐噪声的图像:对于一张含有椒盐噪声的图像,我们做识别处理肯定是会增大误差的。下面是一张例图:

我们滤波的对象是二值图,所以先需要用前文中提及的方法来将这张RGB图转化成二值图,再做滤波处理。相关代码如下:

 A=imread('5.jpg');

 figure(),imshow(A);

 A=rgb2gray(A);

 A=im2bw(A,graythresh(A));

 A=double(A);

 K = medfilt2(A,[,]);

 figure(),imshow(K);

这是直接使用中值滤波函数medfilt2()的例子,滤波后的图像如下:

这里值得注意的一点是medfilt2()函数的第二个参数,是一个[N,M]大小的滑动窗口,对于某一个像素点(x,y),仅处理它邻域的响应。窗口越大,就有越多的像素点对中心像素点有影响。一般而言当图像比较小时,选取的滑动窗口也应该相应的小。对于上面那段代码,如果将滑动窗口改成3×3的话,就会牺牲更多的清晰度,效果很差。图像如下所示:

好了,就说这么多了~~

一维码:EAN-13码的识别的更多相关文章

  1. 第二节 EAN 8 码 / EAN 13 码

    EAN码的全名为欧洲商品条码(European Article Number),源於西元1977年,由欧洲十二个工业国家所共同发展出来的一种条码.目前已成为一种国际性的条码系统.EAN条码系统的管理是 ...

  2. 一维码EAN 13简介及其解码实现(zxing-cpp)

    一维码EAN 13:属于国际标准条码, 由13个数字组成,为EAN的标准编码型式(EAN标准码). 依结构的不同,EAN条码可区分为: 1.  EAN 13码: 由13个数字组成,为EAN的标准编码型 ...

  3. (zxing.net)一维码EAN 13的简介、实现与解码

    一维码EAN 13:属于国际标准条码, 由13个数字组成,为EAN的标准编码型式(EAN标准码). 依结构的不同,EAN条码可区分为: EAN 13码: 由13个数字组成,为EAN的标准编码型式(EA ...

  4. 一维码EAN 8简介及其解码实现(zxing-cpp)

    一维码EAN 8:属于国际标准条码,由8个数字组成,属EAN的简易编码形式(EAN缩短码).当包装面积小于120平方公分以下无法使用标准码时,可以申请使用缩短码. 依结构的不同,EAN条码可区分为: ...

  5. (zxing.net)一维码EAN 8的简介、实现与解码

    一.简介 一维码EAN 8:属于国际标准条码,由8个数字组成,属EAN的简易编码形式(EAN缩短码).当包装面积小于120平方公分以下无法使用标准码时,可以申请使用缩短码. 依结构的不同,EAN条码可 ...

  6. Android平台二维码之生成,扫描 & 识别

    1.二维码的前世今生 “二维条码/二维码(2-dimensional bar code)是用某种特定的几何图形按一定规律在平面(二维方向上)分布的黑白相间的图形记录数据符号信息的:在代码编制上巧妙地利 ...

  7. Solr4.8.0源码分析(13)之LuceneCore的索引修复

    Solr4.8.0源码分析(13)之LuceneCore的索引修复 题记:今天在公司研究elasticsearch,突然看到一篇博客说elasticsearch具有索引修复功能,顿感好奇,于是点进去看 ...

  8. 曹工说Spring Boot源码(13)-- AspectJ的运行时织入(Load-Time-Weaving),基本内容是讲清楚了(附源码)

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  9. 三维码 & 二维码 & 一维码

    三维码 & 二维码 & 一维码 3D, 2D, 1D 防伪国家标准 -<结构三维码防伪技术条件> http://www.xinhuanet.com/tech/2019-12 ...

  10. node.js Websocket实现扫码二维码登录---GoEasy

    最近在做一个扫码登录功能,为此我还在网上搜了一下关于微信的扫描登录的实现方式.当这个功能完成了后,我决定将整个实现思路整理出来,方便自己以后查看也方便其他有类似需求的程序猿些. 要实现扫码登录我们需要 ...

随机推荐

  1. csharp: MySQL Stored Procedure using DAL

    # 建表 塗聚文 20160907 drop table attendrecord; create table attendrecord ( seq INT NOT NULL PRIMARY KEY ...

  2. 《Pro ASP.NET MVC 4》异常整理

    最近在和同学一起研究毕业设计,准备模仿<Pro ASP.NET MVC 4>里面的SportsStore设计模式和测试驱动开发. 由于和书中VS版本不同,发现不少问题,在此总结. 用户代码 ...

  3. How do I see all foreign keys to a table or column?

    down voteaccepted For a Table: SELECT TABLE_NAME,COLUMN_NAME,CONSTRAINT_NAME, REFERENCED_TABLE_NAME, ...

  4. 5、python第一天作业

    作业一:编写登陆接口 1.输入用户名密码 2.认证成功后显示欢迎信息 3.输错三次后锁定 分析: 1.流程控制图 2.编写思路 以r+(读写模式)打开文件,读取文件内容字符串,再写入文件,以字符串的长 ...

  5. PowerDesigner15在win7-64位系统下对MySQL 进行反向工程以及建立物理模型产生SQL语句步骤图文傻瓜式详解

    1.安装PowerDesigner15.MySQL5.不详细讲解了.网上一大把.请各位亲参考去. 2.安MyODBC-standard-3.51.0.7-win.msi.mysql-connector ...

  6. 理解 OpenStack 高可用(HA)(5):RabbitMQ HA

    本系列会分析OpenStack 的高可用性(HA)概念和解决方案: (1)OpenStack 高可用方案概述 (2)Neutron L3 Agent HA - VRRP (虚拟路由冗余协议) (3)N ...

  7. Ajax传递路径问题及解决

    在使用Ajax的过程中,如果要通过JSON传递路径值到处理页面,可能会出现传值不正确.(李昌辉) 解决方法就是在传值之前将路径进行编码: JS中: encodeURIComponent(url); 在 ...

  8. 《C#微信开发系列(4)-接收 / 返回文本消息》

    4.0接收 / 返回文本消息 ①接收/返回文本消息原理说明 当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上,着手开发之前先行阅读微信公众平台接收普通消息 ...

  9. JavsScript+dom

    JavsScript+dom DOM(兼容性) 用于操作文档树 1.帮助我们找到标签 直接查找 间接查找 getElementById getElementsByTageName 2.标签操作 内容: ...

  10. python操作SQL

    pymysql pymsql是Python中操作MySQL的模块,其使用方法和MySQLdb几乎相同 一.下载安装 pip3 install pymysql 二.操作使用 1.执行SQL #!/usr ...