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. function的name属性

    name属性是函数的一个非标准的属性. 通过这个属性,我们可以访问给定函数的名字.属性name的值永远等于跟在function关键字后的标识符.   eg: function jenny(arg1,a ...

  2. SQL性能优化常见措施(Lock wait timeout exceeded)

    SQL性能优化常见措施 目 录 1.mysql中explain命令使用 2.mysql中mysqldumpslow的使用 3.mysql中修改my.ini配置文件记录日志 4.mysql中如何加索引 ...

  3. AngularJS学习笔记(1) - AngularJS入门

    什么是AngularJS? AngularJS是建立在jQuery的一个轻量级版本之上的MVC框架.MVC将业务逻辑代码和视图.模型相分离.AngularJS提供的所有功能都可以通过使用JavaScr ...

  4. 使用jquery增加网站粘度

    增加网站粘度,可以在页面增加一个“随机访问”链接,当点击链接时,随机打开预先设定好的链接集合中的一个. 使用jquery可以实现这个功能,RandomVisit就是这样的一个jQuery插件. 官方网 ...

  5. 【单页应用】全局控制器app应该干些什么?

    前言 之前,我们形成了页面片相关的mvc结构,但是该结构还仅适用于view(页面)级,那么真正的全局控制器app应该干些什么事情呢?我觉得至少需要干这些: 功能点 ① 提供URL解析机制,以便让控制器 ...

  6. 基于WebGL的三维地形渲染

    1.生成WebMap页面 #!/usr/bin/env python # -*- coding: utf-8 -*- import subprocess from jinja2 import Envi ...

  7. JavaScript - 如果...没有方法

    这篇文章源于我上一周所读的一篇12年的文章.原作者提出了一个问题,如果js没有原生方法Math.round(),我们如何去实现呢? 对此我和我的基友进行了小小探讨,并给出了一些有意思的答案. 本文内容 ...

  8. Android Log Tag含义

    在分析Android问题的时候重要的手段之一就是分析log,在events.log中有很多系统log,其中有些log的含义并不是很了解,下面就是从安卓源码中得到的系统log的tag. 关于Tag的说明 ...

  9. Java内存以及GC

    <深入理解Java虚拟机>第二三章摘要 Java内存区域与内存溢出 Java虚拟机中的内存分配图: 各个区域的特性总结如下表: 补充说明: 当多线程情形下,可能多个线程要在堆上分配内存,那 ...

  10. java设计模式 模板方法模式Template Method

    设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性.毫无疑问,设计模式于己 ...