zbar算法是现在网上开源的条形码,二维码检测算法,算法可识别大部分种类的一维码(条形码),比如I25,CODE39,CODE128,不过大家更关心的应该是现在很火的QR码的解码效率,随着现在生活中QR码的普及,扫码支付等行为越来越多的被人们接受,关于QR码是什么,QR码的解码流程是什么样的。本篇文章就互联网上的一个开源解码算法zbar进行简单剖析。
源码可以在网上搜到,或者去github上clone到本地:Zbar/Zbar
先上一个流程图:
首先是算法的初始化,构造一个扫描器ImageScanner对象,并使用其set_config()方法对扫描器进行初始化:
ImageScanner scanner;
// configure the reader
scanner.set_config(ZBAR_NONE, ZBAR_CFG_ENABLE, 1);
IplImage *img = cvLoadImage("E:\\ 文档 \\ 测试素材 _ 一维码二维码 \\QRCODE\\2-1.jpg");
IplImage *imgGray = cvCreateImage(cvGetSize(img), 8, 1);
cvCvtColor(img, imgGray, CV_RGB2GRAY);
int width = imgGray->widthStep;
int height = imgGray->height;
Image image(width, height, "Y800", imgGray->imageData, width * height);
int n = scanner.scan(image);
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;
}
if(y1_rev)
edge = process_edge(scn, y1_1);
scn->width = scn->cur_edge - scn->last_edge;
scn->last_edge = scn->cur_edge;
#ifdef ENABLE_EAN
if((dcode->ean.enable) &&
(sym = _zbar_decode_ean(dcode)))
dcode->type = sym;
#endif
#ifdef ENABLE_CODE39
if(TEST_CFG(dcode->code39.config, ZBAR_CFG_ENABLE) &&
(sym = _zbar_decode_code39(dcode)) > ZBAR_PARTIAL)
{
dcode->type = sym;
}
#endif
#ifdef ENABLE_CODE128
if(TEST_CFG(dcode->code128.config, ZBAR_CFG_ENABLE) &&
(sym = _zbar_decode_code128(dcode)) > ZBAR_PARTIAL)
dcode->type = sym;
#endif
#ifdef ENABLE_I25
if(TEST_CFG(dcode->i25.config, ZBAR_CFG_ENABLE) &&
(sym = _zbar_decode_i25(dcode)) > ZBAR_PARTIAL)
dcode->type = sym;
#endif
#ifdef ENABLE_PDF417
if(TEST_CFG(dcode->pdf417.config, ZBAR_CFG_ENABLE) &&
(sym = _zbar_decode_pdf417(dcode)) > ZBAR_PARTIAL)
dcode->type = sym;
#endif
#ifdef ENABLE_QRCODE
if(TEST_CFG(dcode->qrf.config, ZBAR_CFG_ENABLE) &&
(sym = _zbar_find_qr(dcode)) > ZBAR_PARTIAL)
dcode->type = sym;
#endif
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码解析,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);
int ncenters = qr_finder_centers_locate(¢ers, &edge_pts, reader, 0, 0);
void *bin = qr_binarize((unsigned char*)img->data, img->width, img->height);
qr_reader_match_centers(reader, &qrlist, centers, ncenters,(unsigned char*)bin, img->width, img->height);
version=qr_reader_try_configuration(_reader,&qrdata,_img,_width,_height,c);
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;
}
fmt_info=qr_finder_fmt_info_decode(&ul,&ur,&dl,&hom,_img,_width,_height);
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_sampling_grid_sample(&grid,data_bits,dim,_fmt_info,_img,_width,_height);