Zbar条码解码器

Zbar条码解码器是一个开源的二维码(包括条形码)解码器,可以识别来至于视频流,图像文件、手持扫码器和视频设备(如摄像头)等二维码识别,支持EAN-13/UPC-A, UPC-E, EAN-8, Code 128, Code 39, QR Code(二维码)等常用编码方式的条形码/二维码。
安装教程:https://mp.csdn.net/mdeditor/84074167#
条形码与二维码介绍:https://mp.csdn.net/mdeditor/84074271#

Zbar算法流程

首先是算法的初始化,构造一个扫描器ImageScanner对象,并使用其set_config()方法对扫描器进行初始化:

ImageScanner scanner;
// configure the reader
scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1);

接下来是载入图像,可以使用 ImageMagick 和 OpenCV 读取图片文件,并将其转换为灰度图像,以下以 OpenCV 为例:

IplImage *img = cvLoadImage("E:\\ 文档 \\ 测试素材 _ 一维码二维码 \\QRCODE\\2-1.jpg");
IplImage *imgGray = cvCreateImage(cvGetSize(img), 8, 1);
cvCvtColor(img, imgGray, CV_RGB2GRAY);

构造一个图像Image对象,并调用其构造函数对其进行初始化:

int width = imgGray->widthStep;
int height = imgGray->height;
Image image(width, height, "Y800", imgGray->imageData, width * height);

图像解析,通过调用图像扫描器对象的scan()方法,对图像对象进行处理:

int n = scanner.scan(image);

图像扫描,扫描器对象公有方法scan()主要为zbar_scan_image()函数,函数首先对传入的图像进行配置校验,然后对传入图像先进行逐行扫描,扫描路径为 Z 字型:

while(y < h) {iscn->dx = iscn->du = 1;iscn->umin = 0;while(x < w) {uint8_t d = *p;movedelta(1, 0);zbar_scan_y(scn, d);}quiet_border(iscn);movedelta(-1, density);iscn->v = y;if(y >= h)break;iscn->dx = iscn->du = -1;iscn->umin = w;while(x >= 0) {uint8_t d = *p;movedelta(-1, 0);zbar_scan_y(scn, d);}ASSERT_POS;quiet_border(iscn);movedelta(1, density);iscn->v = y;
}

扫描的主要函数为zbar_scan_y(),在函数内部,以一个像素点为增量在一行内一点一点扫描过去,并且完成滤波,求取边缘梯度,梯度阈值自适应,确定边缘,转化成明暗宽度流。其中确定边缘之后调用process_edge()函数:

if(y1_rev)edge = process_edge(scn, y1_1);

在process_edge()函数内部,使用当前边缘跟上一次保存下来的边缘相减得到一个宽度,并将其保存到扫描器结构变量scn中并将本次边缘信息保存下来:

scn->width = scn->cur_edge - scn->last_edge;
scn->last_edge = scn->cur_edge;

之后对扫描器结构变量scn中保存下来的明暗宽度流进行处理,处理函数为zbar_decode_width(scn->decoder, scn->width),该函数内部处理对象为当前行目前保存下来的宽度流,通过计算各宽度之间的宽度信息提取扫码特征,依次通过几种一维码二维码的检测标准,寻找到符合标准的扫码种类时更新扫描器结构变量scn中的type成员,并且更新lock成员以增加当前种类判断的置信度(可以通过设置关掉其他种类的条码识别):

#ifdef ENABLE_EANif((dcode->ean.enable) &&(sym = _zbar_decode_ean(dcode)))dcode->type = sym;
#endif
#ifdef ENABLE_CODE39if(TEST_CFG(dcode->code39.config, ZBAR_CFG_ENABLE) &&(sym = _zbar_decode_code39(dcode)) > ZBAR_PARTIAL){dcode->type = sym;}
#endif
#ifdef ENABLE_CODE128if(TEST_CFG(dcode->code128.config, ZBAR_CFG_ENABLE) &&(sym = _zbar_decode_code128(dcode)) > ZBAR_PARTIAL)dcode->type = sym;
#endif
#ifdef ENABLE_I25if(TEST_CFG(dcode->i25.config, ZBAR_CFG_ENABLE) &&(sym = _zbar_decode_i25(dcode)) > ZBAR_PARTIAL)dcode->type = sym;
#endif
#ifdef ENABLE_PDF417if(TEST_CFG(dcode->pdf417.config, ZBAR_CFG_ENABLE) &&(sym = _zbar_decode_pdf417(dcode)) > ZBAR_PARTIAL)dcode->type = sym;
#endif
#ifdef ENABLE_QRCODEif(TEST_CFG(dcode->qrf.config, ZBAR_CFG_ENABLE) &&(sym = _zbar_find_qr(dcode)) > ZBAR_PARTIAL)dcode->type = sym;
#endif

以 QR 码为例子,函数_zbar_find_qr(dcode)内部对当前行的宽度流进行计算,判断是否符合下列特征:

qr_finder_t *qrf = &dcode->qrf;
qrf->s5 -= get_width(dcode, 6);
qrf->s5 += get_width(dcode, 1);
unsigned s = qrf->s5;
if(get_color(dcode) != ZBAR_SPACE || s < 7)
return ZBAR_NONE;
int ei = decode_e(pair_width(dcode, 1), s, 7);
if(ei)
goto invalid;
ei = decode_e(pair_width(dcode, 2), s, 7);
if(ei != 2)
goto invalid;
ei = decode_e(pair_width(dcode, 3), s, 7);
if(ei != 2)
goto invalid;
ei = decode_e(pair_width(dcode, 4), s, 7);
if(ei)
goto invalid;
invalid:
return ZBAR_NONE;

符合当前特征的即判断其不为 QR 码,如果不符合,将当前宽度流描述为一个自定义的线段结构,包含两端端点及长度等信息,并将满足条件的横向线段结构变量存入一个容器lines的横向线段集合中。对整幅图像的逐列扫描同逐行扫描一样,扫描路径为 N 字型,同样通过函数zbar_scan_y()和process_edge()进行处理找边缘最后求取出纵向的明暗高度流,通过zbar_decode_width(scn-> decoder, scn->width)函数进行处理,将符合 QR 码的纵向线段存入lines的纵向线段集合中。

QR码解析,QR 码解析模块的入口为函数_zbar_qr_decode(iscn->qr, iscn, img),函数内部结构如下:

int nqrdata = 0;
qr_finder_edge_pt *edge_pts = NULL;
qr_finder_center *centers = NULL;
if(reader->finder_lines[0].nlines < 9 ||
reader->finder_lines[1].nlines < 9)
return(0);
int ncenters = qr_finder_centers_locate(¢ers, &edge_pts, reader, 0, 0);
if(ncenters >= 3) {
void *bin = qr_binarize((unsigned char*)img->data, img->width, img->height);
qr_code_data_list qrlist;
qr_code_data_list_init(&qrlist);
qr_reader_match_centers(reader, &qrlist, centers, ncenters,
(unsigned char*)bin, img->width, img->height);
if(qrlist.nqrdata > 0)
nqrdata = qr_code_data_list_extract_text(&qrlist, iscn, img);
qr_code_data_list_clear(&qrlist);
free(bin);
}
if(centers)
free(centers);
if(edge_pts)
free(edge_pts);
return(nqrdata);

首先第一步需要求出 QR 码的三个定位图案的中心,需要对之前求出的横向,纵向线段集合进行筛选,聚类和求取交叉点:

int ncenters = qr_finder_centers_locate(¢ers, &edge_pts, reader, 0, 0);

函数返回的是共找到多少个交叉点,如果小于三个则此图像无法进行 QR 码解析。 之后对图像进行自适应二值化处理:

void *bin = qr_binarize((unsigned char*)img->data, img->width, img->height);

之后就是解码的主要组成部分,对 QR 码进行码字读取:

qr_reader_match_centers(reader, &qrlist, centers, ncenters,(unsigned char*)bin, img->width, img->height);

函数首先对找到的交叉点按时针顺序进行排序,三个点进行仿射变化求出 QR 码模块宽度(所占像素个数):

version=qr_reader_try_configuration(_reader,&qrdata,_img,_width,_height,c);

函数返回值为 QR 码的版本数,并且求出了 QR 码的版本码字和模块宽度(根据三个交叉点处于同边的两个点来计算,仿射变化有单应性仿射 affine homography 和全矩阵仿射 full homography ),将所求得的所有结果进行计算和比对,最终的出 QR 码的版本结果,还需要判断求出结果数是否大于等于 7 。如果是,求得的版本信息是经过编码后的信息,版本号还需要解码;如果小于 7 ,求出来的结果即是 QR 码的版本号:

if(ur.eversion[1]==dl.eversion[0]&&ur.eversion[1]<7){
ur_version=ur.eversion[1];
}
else{
if(abs(ur.eversion[1]-dl.eversion[0])>QR_LARGE_VERSION_SLACK)
continue;
}
if(ur.eversion[1]>=7-QR_LARGE_VERSION_SLACK){
ur_version=qr_finder_version_decode(&ur,&hom,_img,_width,_height,0);
if(abs(ur_version-ur.eversion[1])>QR_LARGE_VERSION_SLACK)
ur_version=-1;
}
else
ur_version=-1;
if(dl.eversion[0]>=7-QR_LARGE_VERSION_SLACK){
dl_version=qr_finder_version_decode(&dl,&hom,_img,_width,_height,1);
if(abs(dl_version-dl.eversion[0])>QR_LARGE_VERSION_SLACK)
dl_version=-1;
}
else
dl_version=-1;
if(ur_version>=0){
if(dl_version>=0&&dl_version!=ur_version)
continue;
}
else if(dl_version<0)
continue;
else
ur_version=dl_version;
}

之后求 QR 码的格式信息:

fmt_info=qr_finder_fmt_info_decode(&ul,&ur,&dl,&hom,_img,_width,_height);

格式信息求出来之后就是 QR 码的功能区到目前为止已全部识别并解码出结果,之后对 QR 码的数据区进行解析,函数为:

qr_code_decode(_qrdata,&_reader->gf,ul.c->pos,ur.c->pos,dl.c->pos,ur_version,fmt_info,_img,_width,_height)

函数注释为:

/*Attempts to fully decode a QR code.
_qrdata: Returns the parsed code data.
_gf: Used for Reed-Solomon error correction
_ul_pos: The location of the UL finder pattern.
_ur_pos: The location of the UR finder pattern.
_dl_pos: The location of the DL finder pattern.
_version: The (decoded) version number.
_fmt_info: The decoded format info.
_img: The binary input image.
_width: The width of the input image.
_height: The height of the input image.
Return: 0 on success, or a negative value on error.*/
static int qr_code_decode(qr_code_data *_qrdata,const rs_gf256 *_gf,
const qr_point _ul_pos,const qr_point _ur_pos,const qr_point _dl_pos,
int _version,int _fmt_info,
const unsigned char *_img,int _width,int _height)

首先对对图像进行消除掩模处理,并且识别出图像中的定位图案:

qr_sampling_grid_init(&grid,_version,_ul_pos,_ur_pos,_dl_pos,_qrdata->bbox,_img,_width,_height);

然后将 QR 码除去功能区之外的区域转换为 0 和 1 的比特流:

qr_sampling_grid_sample(&grid,data_bits,dim,_fmt_info,_img,_width,_height);

使用 Reed-Solomon 纠错算法对提取出来的比特流进行校验和纠错,最后输出最终的识别比特流。 函数nqrdata = qr_code_data_list_extract_text(&qrlist, iscn, img);对求出的比特流进行分析判断,判断当前 QR 码属于什么编码模式,找到相应的编码模式后对比特流进行解码输出,最终求得QR 码的解码结果。

Zbar识别QRcode解读相关推荐

  1. 使用zbar识别二维码

    目录 1 说明 2 软件获取 3 在Windows下使用zbar 4 在Linux PC(Ubuntu Server 1604 64位)下使用zbar 4.1 无内存泄漏的示例代码 5 在ARM Li ...

  2. 读光OCR-文字识别技术解读与应用案例分析

    读光OCR-文字识别技术解读与应用案例分析 摘要:大数据上云特惠活动系列直播,阿里巴巴高级算法专家永攀对读光OCR-文字识别技术和行业应用进行讲述.OCR的本质是识别图片中的文字,即在复杂的图片背景下 ...

  3. 在C#中使用ZBar识别条形码

    目录: 一.识别库 二.从一张图片中提取多个条形码 三.注意事项 一.识别库 目前主流的识别库主要有ZXing.NET和ZBar,这里我使用的是ZBar,ZXing.NET也试过,同等条件下,识别率不 ...

  4. 转载:使用 OpenCV 识别 QRCode

    原文链接:http://coolshell.cn/articles/10590.html#jtss-tsina 识别二维码的项目数不胜数,每次都是开箱即用,方便得很. 这次想用 OpenCV 从零识别 ...

  5. Opencv+ZBar识别条形码、二维码

    文章目录 Opencv识别条形码.二维码 1.ZBar环境配置 2.一维码(条形码)识别 3. 二维码的识别 4. Opencv识别二维码 结束 Opencv识别条形码.二维码 最近的一次作业,恰好之 ...

  6. 配置zbar识别二维码(转载)

    原文地址: http://blog.csdn.net/dcrmg/article/details/52108258   二维码解码器Zbar+VS2012开发环境配置 Zbar条码解码器是一个开源的二 ...

  7. NetVLAD场景识别模型解读

    c论文: NetVLAD: CNN architecture for weakly supervised place recognition 来源:CVPR 2016 应用:NetVLAD是一种场景识 ...

  8. dlib人脸识别代码解读

    文章目录 一 人脸关键点检测器的训练 1.1 原理 1.1.1 级联回归公式 1.1.2 回归方程求解 1.1.3 分裂点 1.2 源代码 1.3 代码解读 1.3.1 预处理阶段 1.3.2 训练阶 ...

  9. 【口罩人脸检测/识别】中科视拓免费开放口罩人脸检测与识别技术解读

    原文:中科视拓免费开放口罩人脸检测与识别技术 思路 通过掺入了20%以上戴口罩的人脸识别数据集训练专门戴口罩识别模型,然后调整整个识别的Pipeline为先检测是否戴口罩,如果判断为带口罩则使用戴口罩 ...

最新文章

  1. c3p0、dbcp、druid三大连接池对比
  2. Android 接入baceBook
  3. 码农小汪-Hibernate学习8-hibernate关联关系注解表示@OneToMany mappedBy @ManyToMany @JoinTable...
  4. C语言的标识符由什么组成
  5. 基于JAVA+SSH+MYSQL的工资管理系统
  6. jQuery源码学习之五 (jQUery继承方法)
  7. PHP Primary script unknown 终极解决方法
  8. [Perforce]password (P4PASSWD) invalid or unset. 的错误解决
  9. 如何升级linux内核
  10. 怎么利用微博进行营销?
  11. 渗透测试工程师可以写进简历的技能介绍部分
  12. 如何给图片去底色?不用ps即可轻松搞定
  13. IB选课指南及热门专业选课建议
  14. 笔记︱盘点实验科学的三种实验模型(A/B实验、因果推断、强化学习)
  15. 详解PON基础知识:OLT、ONU、ONT和ODN
  16. L1和L2简单易懂的理解
  17. 几种基本的数字正则表达式[转]
  18. Squirrel中的类与实例
  19. NorFlash和NandFlash区别
  20. 我的2012,一个2b青年的表白

热门文章

  1. 删除桌面图标计算机权限删除,win10管理员权限删除桌面图标
  2. 一个转身,也许就已经一辈子错过。
  3. CSS-颜色属性+颜色函数+自定义变量
  4. linux多路径后链路聚合,mpio – 链路聚合控制协议或多路径I / O.
  5. linux expect循环用法,Linux expect用法介绍
  6. linux 忘记root如何解决
  7. matlab用于多元曲面拟合,多元拟合函数,三维拟合曲线(曲面)
  8. 计算机网络名词解释知识点简答题整理
  9. Windows 上安装 PostgreSQL教程
  10. 漫画家Tango: 2023,兔子要来了(上)