ZBar源码分析——Image Scanner模块(三) | 2021SC@SDUSC

2021SC@SDUSC

一、Image Scanner

二、代码分析

数据结构解析

扫描器特征回收

扫描器图像回收

扫描器新增特征

三、总结


一、Image Scanner

Image Scanner,顾名思义是实现对读入图像进行扫描的功能模块。

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()来完成滤波,阈值,确定边缘,转化成宽度流。

前面的代码分析对img_scanner.c的核心函数zbar_scan_image()函数以及几个函数进行过分析,本次代码分析则继续对该函数对于特征的使用和处理进行分析。


二、代码分析

数据结构解析

typedef struct recycle_bucket_s {
    int nsyms;
    zbar_symbol_t *head;
} recycle_bucket_t;

/* image scanner state */
struct zbar_image_scanner_s {
    zbar_scanner_t *scn;        /* associated linear intensity scanner */
    zbar_decoder_t *dcode;      /* associated symbol decoder */
#ifdef ENABLE_QRCODE
    qr_reader *qr;              /* QR Code 2D reader */
#endif

    const void *userdata;       /* application data */
    /* user result callback */
    zbar_image_data_handler_t *handler;

    unsigned long time;         /* scan start time */
    zbar_image_t *img;          /* currently scanning image *root* */
    int dx, dy, du, umin, v;    /* current scan direction */
    zbar_symbol_set_t *syms;    /* previous decode results */
    /* recycled symbols in 4^n size buckets */
    recycle_bucket_t recycle[RECYCLE_BUCKETS];

    int enable_cache;           /* current result cache state */
    zbar_symbol_t *cache;       /* inter-image result cache entries */

    /* configuration settings */
    unsigned config;            /* config flags */
    unsigned ean_config;
    int configs[NUM_SCN_CFGS];  /* int valued configurations */
    int sym_configs[1][NUM_SYMS]; /* per-symbology configurations */

#ifndef NO_STATS
    int stat_syms_new;
    int stat_iscn_syms_inuse, stat_iscn_syms_recycle;
    int stat_img_syms_inuse, stat_img_syms_recycle;
    int stat_sym_new;
    int stat_sym_recycle[RECYCLE_BUCKETS];
#endif
};

这里定义了两个数据结构用于代码调用。

recycle_bucket_s为特征回收数据结构,在结构体中定义了特征指针和索引数据。

zbar_image_scanner_s为ZBar扫描器数据结构,存放当前扫描器的状态、用户反馈数据、被回收的特征数据以及扫描器配置数据。

zbar_scanner_t *scn与扫描器关联的线性强度扫描仪
zbar_decoder_t *dcode与扫描器关联的关联符号解码器
qr_reader *qr二维码阅读器
const void *userdata应用数据
unsigned long time扫描开始时间
zbar_image_t *img当前扫描图像
int dx, dy, du, umin, v当前扫描位置
zbar_symbol_set_t *syms曾经被解码得到的结果
int enable_cache当前结果缓存状态
unsigned config配置标志
int configs[NUM_SCN_CFGS]有值配置
int sym_configs[1][NUM_SYMS]符号配置

扫描器特征回收

扫描器在对二维图像进行扫描时,将识别到的特征进行记录和保存,当当前扫描部分结束时,需要对特征进行回收。

void _zbar_image_scanner_recycle_syms (zbar_image_scanner_t *iscn,
                                       zbar_symbol_t *sym)
{
    zbar_symbol_t *next = NULL;
    for(; sym; sym = next) {
        next = sym->next;
        if(sym->refcnt && _zbar_refcnt(&sym->refcnt, -1)) {
            /* unlink referenced symbol */
            /* FIXME handle outstanding component refs (currently unsupported)
             */
            assert(sym->data_alloc);
            sym->next = NULL;
        }
        else {
            int i;
            recycle_bucket_t *bucket;
            /* recycle unreferenced symbol */
            if(!sym->data_alloc) {
                sym->data = NULL;
                sym->datalen = 0;
            }
            if(sym->syms) {
                if(_zbar_refcnt(&sym->syms->refcnt, -1))
                    assert(0);
                _zbar_image_scanner_recycle_syms(iscn, sym->syms->head);
                sym->syms->head = NULL;
                _zbar_symbol_set_free(sym->syms);
                sym->syms = NULL;
            }
            for(i = 0; i < RECYCLE_BUCKETS; i++)
                if(sym->data_alloc < 1 << (i * 2))
                    break;
            if(i == RECYCLE_BUCKETS) {
                assert(sym->data);
                free(sym->data);
                sym->data = NULL;
                sym->data_alloc = 0;
                i = 0;
            }
            bucket = &iscn->recycle[i];
            /* FIXME cap bucket fill */
            bucket->nsyms++;
            sym->next = bucket->head;
            bucket->head = sym;
        }
    }
}

回收步骤:

遍历扫描器存放的特征数据结构,若当前特征与当前扫描器链接且被引用,则直接在特征列表中截断,不允许在链表中继续链接,然后断开链接。

if(sym->refcnt && _zbar_refcnt(&sym->refcnt, -1)) {
            /* unlink referenced symbol */
            /* FIXME handle outstanding component refs (currently unsupported)
             */
            assert(sym->data_alloc);
            sym->next = NULL;
        }

若未被引用,则不能在链表中进行操作,这部分数据在扫描器中的存储结构比较尴尬:可能在后续扫描过程中被引用,即被其他扫描器结构引用。

在回收时,需要调用回收桶数据结构,将这部分数据存放到回收桶中,这与Windows系统中的回收站功能类似,只是将这部分数据结构从扫描器中移除,在某个地方存放起来,需要时仍可以调用。

该函数也提供了将回收桶中的数据彻底移除的方式。

for(i = 0; i < RECYCLE_BUCKETS; i++)
                if(sym->data_alloc < 1 << (i * 2))
                    break;
            if(i == RECYCLE_BUCKETS) {
                assert(sym->data);
                free(sym->data);
                sym->data = NULL;
                sym->data_alloc = 0;
                i = 0;
            }

扫描器图像回收

当扫描器要对链接的图像进行回收时,就需要借助特征回收函数。另一方面,该函数被设定为内联函数,说明这个函数在ZBar执行过程中频繁被调用。

inline void zbar_image_scanner_recycle_image (zbar_image_scanner_t *iscn,
                                              zbar_image_t *img)
{
    zbar_symbol_set_t *syms = iscn->syms;
    if(syms && syms->refcnt) {
        if(recycle_syms(iscn, syms)) {
            STAT(iscn_syms_inuse);
            iscn->syms = NULL;
        }
        else
            STAT(iscn_syms_recycle);
    }

    syms = img->syms;
    img->syms = NULL;
    if(syms && recycle_syms(iscn, syms))
        STAT(img_syms_inuse);
    else if(syms) {
        STAT(img_syms_recycle);

        /* select one set to resurrect, destroy the other */
        if(iscn->syms)
            _zbar_symbol_set_free(syms);
        else
            iscn->syms = syms;
    }
}

ZBar在回收(循环)图像时采用的方式是:遍历所有图像特征,根据特征标识符选择复活一组图像特征,然后销毁一组图像特征,实现内存空间有效利用,不占用过多内存空间。

扫描器新增特征

前面分析了扫描器对特征的回收和断开链接的过程,与之相对的,扫描器同样也需要加入新的特征的功能。

void _zbar_image_scanner_add_sym(zbar_image_scanner_t *iscn,
                                 zbar_symbol_t *sym)
{
    zbar_symbol_set_t *syms;
    cache_sym(iscn, sym);

    syms = iscn->syms;
    if(sym->cache_count || !syms->tail) {
        sym->next = syms->head;
        syms->head = sym;
    }
    else {
        sym->next = syms->tail->next;
        syms->tail->next = sym;
    }

    if(!sym->cache_count)
        syms->nsyms++;
    else if(!syms->tail)
        syms->tail = sym;

    _zbar_symbol_refcnt(sym, 1);
}

这里对特征的数据结构zbar_symbol_s进行补充分析:

 zbar_symbol_type_t type

符号类型
unsigned int configs符号布尔配置位掩码
  unsigned int modifiers符号修饰符位掩码
  unsigned int data_alloc数据的分配大小
  unsigned int datalen二进制符号数据的长度
char *data符号数据
unsigned pts_allocpts的分配大小
unsigned npts位置多边形中的点数
point_t *pts位置多边形中的点列表
  zbar_orientation_t orient粗方向
refcnt_t refcnt引用计数
zbar_symbol_t *next结果(或同级)链接列表
zbar_symbol_set_t *syms分量
unsigned long time相对符号捕获时间
int cache_count缓存状态
int quality相对符号可靠性度量

以及特征集zbar_symbol_set_s的数据结构:

refcnt_t refcnt索引标识
 int nsyms已过滤特征的数量
zbar_symbol_t *head第一个解码特征结果
zbar_symbol_t *tail最后一个未过滤的特征结果

在扫描器中添加新特征时,需要对特征的缓存状态和在特征集合中的位置进行判断,有以下几种情况:

(1)特征已被缓存或者当前特征不在集合的尾部,则直接在特征集合中插入新特征。

(2)特征未被缓存且者当前特征在集合的尾部,则改变集合尾部指针指向,在尾部添加新特征。

(3)对于未缓存的特征,加入集合中还需要对索引和对已过滤特征进行变更。


三、总结

本次代码分析对扫描器关于特征的回收和新增,以及特征和图像间的处理方式进行分析,下次将对扫描器配置和缓存部分展开分析。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Mai゛

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

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

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

打赏作者

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

抵扣说明:

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

余额充值