识别QR二维码技术分析(zbar)

定位解析QR码算法

1、算法原理:

QR码的定位依赖于 三个定位标记(Finder Patterns),其独特的 1:1:3:1:1模块比例(黑:白:黑:白:黑)提供了几何特征。ZBar的解析流程分为 定位→校正→解码 三大阶段,其核心挑战在于快速排除噪声干扰,精准定位标记中心,在此基础上实现正确采样。

算法流程图如下:

                                                                                      算法流程图

2、处理流程:

1、首先是算法的初始化,读入原图像:

zbar_image_scanner_t* scanner = zbar_image_scanner_create();
zbar_image_scanner_set_config(scanner, ZBAR_QRCODE, ZBAR_CFG_ENABLE, 1);

原始图像

2、逐行(列)扫描图像:

扫描器对象公有方法scan()主要为zbar_scan_image()函数,函数首先对传入的图像进行配置校验,然后对传入图像先进行逐行扫描,扫描路径为 Z 字型,有水平、垂直两个方向,下图展示水平扫描:

”z“字型扫描图像

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

edge = process_edge(scn, y1_1);

3、根据梯度变化得到明暗宽度流:

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

scn->width = scn->cur_edge - scn->last_edge;
scn->last_edge = scn->cur_edge;
宽度流

4、宽度流是否满足QR码定位图特征,明暗流大致满足1:1:3:1:1的特征:

当前行目前保存下来的宽度流,通过计算各宽度之间的宽度信息提取扫码特征,依次通过几种一维码二维码的检测标准,寻找到符合标准的扫码种类 时更新扫描器结构变量scn中的type成员,并且更新lock成员以增加当前种类判断的置信度,将符合 QR 码的纵向线段存入lines的纵向线段集合中。

#ifdef ENABLE_EAN
    if((dcode->ean.enable) &&
    (sym = _zbar_decode_ean(dcode)))
        dcode->type = sym;
#endif

所有备选定位点

5、用比例筛选横纵向线段,聚类后求出交叉点:

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

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

函数返回的是共找到多少个交叉点,如果小于三个则此图像无法进行 QR 码解析。

6、之后对图像进行自适应二值化处理:

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

二值化

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

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

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

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

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

这里对QR二维码版本估计做出简单举例介绍:已知三个定位点坐标,假设左上定位点tl,左下定位点bl,通过局部搜索边缘点,分别定位他们各自的左右方向定位点坐标,计算出当前连通域平均宽度从而得到最小单元宽度Wcell,通过抑制tl、tr定位点坐标计算出两点宽度估计出整体QR码宽度Wqr,结合moudel = Wqr / Wcell就得到QR单元个数,结合moudelSize = 17 * versionNum + 4这个规律,最终估计出当前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;
}

9、求 QR 码的格式信息:

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

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

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

3、解析失败数据分析:

1、二值化、定位效果:

案例一:

定位成功

案例二:

定位成功

问题:有很多码已经定位成功,但是还是会导致解析失败?

通过分析得到,通常由于QR二维码不规则形变或者图像模糊等外部因素,导致QR二维码采样过程中出现太多错误采样,从而导致解析失败。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

午夜星空Zp

你的鼓励是我创作的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值