上篇文章写了如何用JavaScript生成EAN-13条形码,这次讲下如何不用库,用原生JavaScript识别EAN-13条形码。

老师给的实验方法是:
面向一维条码的图像识别,是模式识别与机器智能的一种最简单的应用。假设之间你生成的一维码图像(不含人识别的数字),假设你的图像扫描设备完全平行于一维码(即图像没有旋转),则图像可简单理解为在一特定矩形区域内的黑白相间的直方图。对于一个特定的像素点,如果是一个字节表示某个像素点的灰度值,则理论上黑的灰度很高,白的灰度很低。识别是取一个中间值,大于此值的为1(黑),小于此值的为零(白)。
识别是,可以在二维的维度进行,也可以把你的二维图像映射到一维,即把矩形区域的直方图全部投影到X轴,简单判别X轴每个像素点的灰度值,就能识别各个黑白模块的宽度,和宽度相对值,即可识别。

原本小组的另一个同学是已经利用Java的条形码库做好识别了,但是按照这个要求,就必须得自己写识别算法了,所以我查了一下如何用JavaScript做图像识别,参照https://blog.csdn.net/sunny_xsc1994/article/details/78355033
根据这篇博客我获取了上传的图片的像素矩阵,获取矩阵以后我发现,这个矩阵好大啊,输出这个像素矩阵还卡了,因为我的图片宽高还有点大,再加上这个矩阵里包含的值有该像素的red、green、blue、alpha四个值,所以这个像素矩阵的大小为宽 X高 X 4,不过得到这个矩阵后,就有了大致思路了

  1. 取条码的第一行像素点的值
  2. 确定条码的单位模块宽度
  3. 根据单位模块宽度确定剩余条和空的宽度并确定是条或空
  4. 查询条码的数值对应表确定后12位的值以及版本
  5. 根据得到的12位码的前六位判断第一位

知道逻辑以后就开始正式的识别条码了。

取条码的第一行像素点的值
我这里采用了偷懒的方式,已经像素矩阵大小 = 图像宽度 * 图像高度 * 4
所以这个矩阵可以利用for循环遍历,其中i代表了行,j就代表了第j个像素点的red值
为什么说偷懒,因为我这里直接取了red值,并没有将rbga的四个值结合,所以这里也导致了我后面出现了识别率的问题。

这个for循环是得到非空白区的第一行的下标以及这一行的起始码的第一个像素点下标,记为startI,startJ。

for(var i=0;i<array.length/(4*image.width);i++){for(var j=0;j<array.length/(4*image.height);j++){if(data[4*i*image.width+j*4-4]===255&&data[4*i*image.width+j*4]===0){startI=i;startJ=j;break;}}}

这个for循环是取出第一行条码的最后一位终止码的最后一个像素点的下标,记为endJ。

for(var i=image.width*4-4;i>=0;i--){for(var j=0;j<array.length/(4*image.height);j++){if(data[4*i*image.width+j*4+4]===255&&data[4*i*image.width+j*4]===0){endJ=j;break;}}
}

然后把这一行的条码的所有像素点的red值放入数组作为之后处理的数据数组

for(var i=startJ;i<endJ;i++){arrayPX.push(data[startI*4*image.width+i*4]);
}
console.log(arrayPX);

这里输出的arrayPX显示为

这里的输出其实能看出一些问题,就是我的结尾像素取得似乎出问题,但是起始像素点取得没有问题,所以暂时先这样,如果大家有好的算法可以自己再优化一下。

确定条码的单位模块宽度
因为EAN-13码的编码规则是有起始码并且为101,而一个“1”和一个“0”都是占了一个单位模块宽度,所以我就从第一个像素点开始进行遍历,来获取单位宽度。
basicLen代表单位模块宽度。

var temp=arrayPX[0];
var j;
var basicLen;
for(j=1;j<arrayPX.length;j++){if(Math.abs(arrayPX[j]-temp)>150){basicLen=j;oneLindeCode.push(1);break;}
}

然后就是利用这个单位宽度,判断后面的条和空的宽度了。

根据单位模块宽度确定剩余条和空的宽度并确定是条或空

这里用tempOneCode储存除起始位的条空的二进制编码。
因为前面偷懒的原因,导致这里就要纠结精确度的问题了,同时因为将截图得到的条形码上传以后用Canvas加载图片出现了失真问题,得到的像素点的red值有了波动,所以我这里做了大约的处理,在判断某个条和空时,如果遍历碰到某个像素点的red值和此次循环的起始像素点的red值差值的绝对值在150以上,说明已经到了下一个空和条,需要进行下一次遍历了,而它的宽度也由于失真出现了波动,如果这个像素的下标减去起始像素点的下标除去单位模块宽度在一定范围内就将它归于一个宽度,参照代码可以知道怎么处理。

var temponeCode=new Array();
for(var i=basicLen;i<arrayPX.length;i++){var flag=1;var temp = arrayPX[i];var j;for(j=i+1;j<arrayPX.length;j++){if(Math.abs(arrayPX[j]-temp)>150){flag=0;break;}}var length = j-i;var dif = length/basicLen;var thisLen;if(dif<1.5&&dif>0.5){thisLen=1;}else if(dif<2.5&&dif>1.5){thisLen=2;}else if(dif<3.5&&dif>2.5){thisLen=3;}else {thisLen=4;}var bin;if(Math.abs(temp-255)<=100)bin=0;elsebin=1;for(var k=0;k<thisLen;k++){temponeCode.push(bin);}i=j-1;
}

前面提到了结束像素点取得有点问题,所以我们直接取得到的前95个二进制码(一个EAN-13码共有95个模块)。

oneLindeCode.push(1);
for(var i=0;i<94;i++){oneLindeCode.push(temponeCode[i]);
}

查询条码的数值对应表确定后12位的值以及版本

这里就遍历数组就行,我先定义了ABC三个版本不同数值对应的二进制编码的值组成的数组,以及不同起始符对应的前六位数值的版本所组成的数组

var PresentNumber=[   "0001101","0100111","1110010","0011001","0110011","1100110","0010011","0011011","1101100","0111101","0100001","1000010","0100011","0011101","1011100","0110001","0111001","1001110","0101111","0000101","1010000","0111011","0010001","1000100","0110111","0001001","1001000","0001011","0010111","1110100"]var version=[["A","A","A","A","A","A"],["A","A","B","A","B","B"],["A","A","B","B","A","B"],["A","A","B","B","B","A"],["A","B","A","A","B","B"],["A","B","B","A","A","B"],["A","B","B","B","A","A"],["A","B","A","B","A","B"],["A","B","A","B","B","A"],["A","B","B","A","B","A"]
];

因为前六位数值和后六位数值中间隔着中间分隔符,所以我将循环分开了,先判断前六位
还有起始码,所以从点3(编号为0-94)开始遍历。

for(var i=3;i<45;i+=7){str="";for(var j=0;j<7;j++){str+=oneLindeCode[i+j].toString();}console.log(str);var index;for(var k=0;k<30;k++){if(str===PresentNumber[k]){index=k;break;}}codeBar.push(parseInt((index+1)/3));if(index%3===0){tempVersion.push("A");}else if(index%3===1){tempVersion.push("B");}elsetempVersion.push("C");
}

然后确定后六位

for(var i=50;i<92;i+=7){str="";for(var j=0;j<7;j++){str+=oneLindeCode[i+j].toString();}console.log(str);var index;for(var k=0;k<30;k++){if(str===PresentNumber[k]){index=k;break;}}codeBar.push(parseInt((index)/3));
}

最后输出结果如下

和上传的图片的码一致了,说明处理成功了,然后就是判断起始符。

根据得到的12位码的前六位判断第一位

js部分

for(var i=0;i<10;i++){if(version[i].toString()==tempVersion.toString()){startCode=i;break;}
}

然后就将得到的这十三位的条码在网页上显示了。

源码

var PresentNumber=[   "0001101","0100111","1110010","0011001","0110011","1100110","0010011","0011011","1101100","0111101","0100001","1000010","0100011","0011101","1011100","0110001","0111001","1001110","0101111","0000101","1010000","0111011","0010001","1000100","0110111","0001001","1001000","0001011","0010111","1110100"]var version=[["A","A","A","A","A","A"],["A","A","B","A","B","B"],["A","A","B","B","A","B"],["A","A","B","B","B","A"],["A","B","A","A","B","B"],["A","B","B","A","A","B"],["A","B","B","B","A","A"],["A","B","A","B","A","B"],["A","B","A","B","B","A"],["A","B","B","A","B","A"]
];var oneLindeCode = new Array();//识别条形码
function readCodeBar() {//创建画布var canvas = document.getElementById("canvas");var ctx = canvas.getContext('2d');//读取图片var myCodeBar = document.getElementById("input_image").files[0];var reader = new FileReader();reader.readAsDataURL(myCodeBar);reader.onload = function (ev) {var image = new Image();image.src = ev.target.result;image.onload = function () {console.log("宽度"+this.width+"高度"+this.height);ctx.drawImage(image,0,0,image.height,image.width);var data = ctx.getImageData(0,0,image.width,image.height).data;// console.log(data.toString());var array = data;var startI ,startJ,endJ;var arrayPX = new Array();for(var i=0;i<array.length/(4*image.width);i++){for(var j=0;j<array.length/(4*image.height);j++){if(data[4*i*image.width+j*4-4]===255&&data[4*i*image.width+j*4]===0){startI=i;startJ=j;break;}}}for(var i=image.width*4-4;i>=0;i--){for(var j=0;j<array.length/(4*image.height);j++){if(data[4*i*image.width+j*4+4]===255&&data[4*i*image.width+j*4]===0){endJ=j;break;}}}for(var i=startJ;i<endJ;i++){arrayPX.push(data[startI*4*image.width+i*4]);}// console.log(arrayPX.toString());// arrayPX.push(data[4*i*image.width+j*4]);var temp=arrayPX[0];var j;var basicLen;for(j=1;j<arrayPX.length;j++){if(Math.abs(arrayPX[j]-temp)>150){basicLen=j;oneLindeCode.push(1);break;}}var temponeCode=new Array();for(var i=basicLen;i<arrayPX.length;i++){var flag=1;var temp = arrayPX[i];var j;for(j=i+1;j<arrayPX.length;j++){if(Math.abs(arrayPX[j]-temp)>150){flag=0;break;}}var length = j-i;var dif = length/basicLen;var thisLen;if(dif<1.5&&dif>0.5){thisLen=1;}else if(dif<2.5&&dif>1.5){thisLen=2;}else if(dif<3.5&&dif>2.5){thisLen=3;}else {thisLen=4;}var bin;if(Math.abs(temp-255)<=100)bin=0;elsebin=1;for(var k=0;k<thisLen;k++){temponeCode.push(bin);}i=j-1;}//生成正式的二进制代码for(var i=0;i<94;i++){oneLindeCode.push(temponeCode[i]);}console.log(oneLindeCode)var codeBar=new Array();var str = new String();var tempVersion = new Array();for(var i=3;i<45;i+=7){str="";for(var j=0;j<7;j++){str+=oneLindeCode[i+j].toString();}console.log(str);var index;for(var k=0;k<30;k++){if(str===PresentNumber[k]){index=k;break;}}codeBar.push(parseInt((index+1)/3));if(index%3===0){tempVersion.push("A");}else if(index%3===1){tempVersion.push("B");}elsetempVersion.push("C");}for(var i=50;i<92;i+=7){str="";for(var j=0;j<7;j++){str+=oneLindeCode[i+j].toString();}console.log(str);var index;for(var k=0;k<30;k++){if(str===PresentNumber[k]){index=k;break;}}codeBar.push(parseInt((index)/3));}console.log(codeBar);console.log(tempVersion);var startCode;for(var i=0;i<10;i++){if(version[i].toString()==tempVersion.toString()){startCode=i;break;}}var tempStr=new String();tempStr+=startCode;for(var i=0;i<12;i++){tempStr+=codeBar[i];}$("#codeBar_content").val(tempStr)// var data = ctxt.getImageData(0,0,this.width,this.height).data;// console.log(data,data.toString());}}
}

html部分

<div id="body_read"><input id="input_image" type="file" placeholder="请上传条码图片"><button id="read_EAN" "readCodeBar()">识别条形码</button>
</div><canvas id="canvas">对不起,你的浏览器不支持Canvas</canvas>
<textarea id="codeBar_content"></textarea>

总结
虽然这个代码可以识别出一些条形码,但是还有挺大的失误的,比如上传的图片上方空白过多就会出错,以及在同一个页面里暂时只能做一次处理,好像是因为图像缓存的问题,大家都可以再优化。

识别EAN-13条形码(JavaScript)相关推荐

  1. 使用quaggaJS识别图片中的条形码

    使用quaggaJS识别图片中的条形码 quaggaJS是一个纯JS的插件,用于识别图片中的条形码,很方便.一般用于移动端拍照识别,也可以在网页端上传图片识别. github下载地址 首先要指定正确格 ...

  2. 如何将EAN 13码批量输出成图片

    EAN是标准条形码,目前商店里大多数商品上都打有此码.EAN广泛用于便利店里的POS系统中.由于EAN和美国.加拿大的UPC,日本的JAN兼容,是世界通用的条形码.EAN13码是由前缀码.厂商识别码. ...

  3. 13 种 JavaScript 代码技巧

    13 种 JavaScript 代码技巧 1. 多表达式多 if 判断 我们可以在数组中存储多个值,并且可以使用数组include方法. // 长 if (x === 'abc' || x === ' ...

  4. 13个JavaScript单行式代码

    13个JavaScript单行式代码 1.随机获取布尔值(true/false) 此函数将使用Math.random()方法返回布尔值(真或假).Math.random将创建一个介于0和1之间的随机数 ...

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

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

  6. 条码软件如何批量制作A级EAN 13条码

    打开中琅条码软件,在文档设置窗口,点击"纸张"选项卡,纸张下拉选项选择"自定义大小",然后设置纸张尺寸. 然后选择"布局"选项卡,手工设置一 ...

  7. 13个JavaScript图表图形绘制插件

    由于绘制矢量图的不同技术愈发成熟以及现代浏览器所具备的更强大的计算能力等原因,目前网上出现了越来越多免费 的JavaScript图表和图形绘制解决方案.在本文中就将分享13个优秀实用的JavaScri ...

  8. ICCV2017 论文解读:基于图像检索的行人重识别 | PaperDaily #13

    在碎片化阅读充斥眼球的时代,越来越少的人会去关注每篇论文背后的探索和思考. 在这个栏目里,你会快速 get 每篇精选论文的亮点和痛点,时刻紧跟 AI 前沿成果. 点击本文底部的「阅读原文」即刻加入社区 ...

  9. 13 款 JavaScript 模板引擎

    JavaScript 在生成各种页面内容时如果能结合一些模板技术,可以让逻辑和数据之间更加清晰,本文介绍 X 款 JavaScript 的模板引擎.(排名不分先后顺序) 1. Mustache 基于j ...

  10. 开发者最容易犯的13个JavaScript错误

    开发者最容易犯的JavaScript错误,总结出13个.这些当中可能少不了你犯的错误.我们描述了这些陋习,并列出来解决办法,希望对开发者有帮助. 1.for...数组迭代的用法 Usage of fo ...

最新文章

  1. 这 4 款 MySQL 调优工具 yyds
  2. Ubuntu12.04安装Mac OSX Yosemite
  3. SESSION 页面刷新 失效
  4. MySQL表碎片化(Table Fragmentation)以及处理
  5. angularjs探秘五 举足轻重的scope
  6. Java预编译和批处理
  7. macOS Monterey新问题:“内存泄漏”,应用后台运行消耗上百 GB 内存
  8. 月薪7万,还想着赚“外快”,吃百万回扣, 这名大公司85后员工结局唏嘘
  9. c++时间函数及转换
  10. 【集群仿真】基于matlab固定翼无人机集群仿真演示平台【含Matlab源码 1497期】
  11. 医药电子 | 温度传感器的类型原理特点和应用
  12. 智头条:萤石拟科创板上市将投18.5亿建厂,Matter智能家居标准延迟至明年,涂鸦、公牛、极米等企业发布
  13. 经典面试题-Appium原理
  14. kafka的分布式爬虫系统
  15. 【SAP消息号F5053】
  16. java socket发送json_Java中使用Socket发送Java对象实例
  17. matlab对频谱傅里叶逆变换,基于功率谱的傅里叶逆变换问题
  18. 拿到腾讯 offer 的第二天,我从字节跳动离职了
  19. 酒店、宾馆、饭店的区别
  20. 数字图像增强的一般方法

热门文章

  1. 霍金质疑超光速粒子发现:需要更多实验确认
  2. vue单页面应用的原理
  3. 运放参数解释以及常用运放选型
  4. ddrelease64 黑苹果_[原]红帽 Red Hat Linux相关产品iso镜像下载【百度云】【更新7.7】...
  5. 51单片机与6264通信探讨
  6. 四川建院计算机应用考试试题,自考计算机基础知识100题及答案
  7. Redis6入门数据类型持久化主从集群
  8. 安全基线(Linux和Windows篇)讲的很详细,内有学习资料
  9. 计算机技术在医学应用中的论文,试论计算机技术在口腔医学中的应用
  10. Modernizr的简单使用