定位解析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(¢ers, &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二维码采样过程中出现太多错误采样,从而导致解析失败。