ZBar源码分析——img_scanner.c | 2021SC@SDUSC

2021SC@SDUSC

一、ZBar的工作流程

通过分析ZBar项目的结构,可以看到ZBar的工作流程大致分为4个步骤:

(一)读入图像并配置参数;

(二)扫描读入的图像并根据梯度变化分析其明暗宽度流(根据明暗宽度流可以得出读入图像中的条码类型,如:二维码,code93,code128等);

(三)分析读入图像的像素点及其特征;

(四)找到条码格式信息并解码,最后输出恢复出来的码字。

二、Image Scanner

Image Scanner,顾名思义是实现对读入图像进行扫描的功能模块。本次要分析的scanner.c就是这一功能模块的核心之一。 

ZBar实现Image Scanner的核心主要由img_scanner.c和scanner.c两个文件组成。

其中,img_scanner.c中的核心函数是zbar_scan_image(),而scanner.c中的核心函数是zbar_scan_y()。经过简单分析得到,zbar_scan_image主要负责ZBar对读入图像的扫描工作,函数主要根据设定的扫描密度(density)控制像素点读取(按Z字形读取,这也是ZBar名称的由来),scanner.c文件内的zbar_scan_y()来完成滤波,阈值,确定边缘,转化成宽度流。

zbar_scan_image()的实现需要借助zbar_scan_y()扫描图像形成宽度流的结果。

三、img_scanner.c

本次源码分析主要对img_scanner.c文件进行分析(主要分析zbar_scan_image()函数)。

zbar_image_scanner_create() 创建(安装)图片扫描器Image Scanner并将其应用于解码以及各类条码的识别。

//创建图片扫描器的函数实现
zbar_image_scanner_t *zbar_image_scanner_create ()
{
    zbar_image_scanner_t *iscn = calloc(1, sizeof(zbar_image_scanner_t));
//若创建失败,返回NULL
    if(!iscn)
        return(NULL);
//初始化图片扫描器(初始化图片扫描器中的解码器和扫描器)
    iscn->dcode = zbar_decoder_create();
    iscn->scn = zbar_scanner_create(iscn->dcode);
//若解码器或扫描器初始化失败,返回NULL
    if(!iscn->dcode || !iscn->scn) {
        zbar_image_scanner_destroy(iscn);
        return(NULL);
    }
//将图片扫描器应用于解码器
    zbar_decoder_set_userdata(iscn->dcode, iscn);
    zbar_decoder_set_handler(iscn->dcode, symbol_handler);

#ifdef ENABLE_QRCODE
    iscn->qr = _zbar_qr_create();
#endif
//将图片扫描器应用于各类条码的识别
    CFG(iscn, ZBAR_CFG_X_DENSITY) = 1;
    CFG(iscn, ZBAR_CFG_Y_DENSITY) = 1;
    zbar_image_scanner_set_config(iscn, 0, ZBAR_CFG_POSITION, 1);
    zbar_image_scanner_set_config(iscn, 0, ZBAR_CFG_UNCERTAINTY, 2);
    zbar_image_scanner_set_config(iscn, ZBAR_QRCODE, ZBAR_CFG_UNCERTAINTY, 0);
    zbar_image_scanner_set_config(iscn, ZBAR_CODE128, ZBAR_CFG_UNCERTAINTY, 0);
    zbar_image_scanner_set_config(iscn, ZBAR_CODE93, ZBAR_CFG_UNCERTAINTY, 0);
    zbar_image_scanner_set_config(iscn, ZBAR_CODE39, ZBAR_CFG_UNCERTAINTY, 0);
    zbar_image_scanner_set_config(iscn, ZBAR_CODABAR, ZBAR_CFG_UNCERTAINTY, 1);
    zbar_image_scanner_set_config(iscn, ZBAR_COMPOSITE, ZBAR_CFG_UNCERTAINTY, 0);
    return(iscn);
}

 当需要卸载Image Scanner时,不能直接free(img_scanner)释放内存空间,这会直接导致ZBar扫描器zbar_scanner、ZBar解码器zbar_decoder以及各类条码识别应用发生空指针异常。因此需要先判断Image Scanner在各个部件和应用上的使用情况,若正在使用,则需要先解除Image Scanner的应用,最后才能直接释放内存。

//图片扫描器的卸载
void zbar_image_scanner_destroy (zbar_image_scanner_t *iscn)
{
    int i;
    dump_stats(iscn);
//判断图片扫描器的状态,若处于应用状态,则不可卸载
    if(iscn->syms) {
        if(iscn->syms->refcnt)
            zbar_symbol_set_ref(iscn->syms, -1);
        else
            _zbar_symbol_set_free(iscn->syms);
        iscn->syms = NULL;
    }
//判断图片扫描器是否应用于ZBar扫描器,如果是,则从ZBar扫描器上卸载
    if(iscn->scn)
        zbar_scanner_destroy(iscn->scn);
    iscn->scn = NULL;
判断图片扫描器是否应用于ZBar解码器,如果是,则从ZBar解码器上卸载
    if(iscn->dcode)
        zbar_decoder_destroy(iscn->dcode);
    iscn->dcode = NULL;
//将图片扫描器从各类条码的识别应用上卸载
    for(i = 0; i < RECYCLE_BUCKETS; i++) {
        zbar_symbol_t *sym, *next;
        for(sym = iscn->recycle[i].head; sym; sym = next) {
            next = sym->next;
            _zbar_symbol_free(sym);
        }
    }
#ifdef ENABLE_QRCODE
    if(iscn->qr) {
        _zbar_qr_destroy(iscn->qr);
        iscn->qr = NULL;
    }
#endif
    free(iscn);
}

下面是对zbar_scan_image()部分代码片段的分析:

zbar_scan_image()主要实现了Z字形扫描图像密度,核心思想是通过扫描密度(density)来控制像素点读取。

函数分为从横向和纵向扫描图像两个部分,实现逻辑相差不大,下面给出纵向扫描部分的代码。

density = CFG(iscn, ZBAR_CFG_Y_DENSITY);
    if(density > 0) {
        const uint8_t *p = data;
        int x = 0, y = 0;

        int border = (((img->crop_h - 1) % density) + 1) / 2;
        if(border > img->crop_h / 2)
            border = img->crop_h / 2;
        border += img->crop_y;
        assert(border <= h);
        svg_group_start("scanner", 0, 1, 1, 0, 0);
        iscn->dy = 0;

        movedelta(img->crop_x, border);
        iscn->v = y;

        while(y < cy1) {
            int cx0 = img->crop_x;;
            zprintf(128, "img_x+: %04d,%04d @%p\n", x, y, p);
            svg_path_start("vedge", 1. / 32, 0, y + 0.5);
            iscn->dx = iscn->du = 1;
            iscn->umin = cx0;
            while(x < cx1) {
                uint8_t d = *p;
                movedelta(1, 0);
                zbar_scan_y(scn, d);
            }
            ASSERT_POS;
            quiet_border(iscn);
            svg_path_end();

            movedelta(-1, density);
            iscn->v = y;
            if(y >= cy1)
                break;

            zprintf(128, "img_x-: %04d,%04d @%p\n", x, y, p);
            svg_path_start("vedge", -1. / 32, w, y + 0.5);
            iscn->dx = iscn->du = -1;
            iscn->umin = cx1;
            while(x >= cx0) {
                uint8_t d = *p;
                movedelta(-1, 0);
                zbar_scan_y(scn, d);
            }
            ASSERT_POS;
            quiet_border(iscn);
            svg_path_end();

            movedelta(1, density);
            iscn->v = y;
        }
        svg_group_end();
    }
    iscn->dx = 0;

这部分代码主要实现了纵向扫描图像密度,由于其中调用函数关系较为复杂,若只看坐标部分,可简化为以下代码:

//先判断有没有设定y密度
if(ydensity > 0)
 {
     while(y < h)  
//y从0以ydensity递增到h
 {
   while(x < w) 
//x先从0递增到w,再递减回0
   {
      x += 1;
      zbar_scan_y();
   }
   x = w - 1;
   y = y + ydensity 
//y从0以ydensity递增到h
   if(y >= h)
     break;
   while(x >= 0)
//  x开始递减
   {
      x -= 1;
      zbar_scan_y();
   }
   x = 0 + 1;
   y = y + ydensity;
 }
//接着判断x方向扫描密度,同理Z字形扫描
}
 w = img->width;
 h = img->height;

以上代码中w表示图像宽度,h表示图像高度,函数在循环过程中反复调用zbar_scan_y()来完成滤波,阈值,确定边缘,转化成宽度流。

根据代码进行绘制,大致能得到ZBar扫描图像像素的顺序大致如下:

在这里插入图片描述

 以上便是本次的代码分析报告,如有错误,请指正。

 

 

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Mai゛

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值