2021SC@SDUSC
目录
一、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的主要代码,这次博客则主要分析scanner.c的主要代码。
二、scanner.c
扫描器结构体组成zbar_scanner_s
struct zbar_scanner_s {
zbar_decoder_t *decoder; /* associated bar width decoder */
unsigned y1_min_thresh; /* minimum threshold */
unsigned x; /* relative scan position of next sample */
int y0[4]; /* short circular buffer of average intensities */
int y1_sign; /* slope at last crossing */
unsigned y1_thresh; /* current slope threshold */
unsigned cur_edge; /* interpolated position of tracking edge */
unsigned last_edge; /* interpolated position of last located edge */
unsigned width; /* last element width */
};
zbar_scanner_s是扫描器结构体变量,由解码器和一系列图像参数变量(边界、宽度流等)组成。
zbar_decoder_t *decoder; /* 对应条宽解码器 */
unsigned y1_min_thresh; /* 最小阈值*/
unsigned x; /* 下一个样本的相对扫描位置 */
int y0[4]; /* 平均强度的短循环缓冲区 */
int y1_sign; /* 最后一个扫描节点的“坡度”(用于判断亮暗) */
unsigned y1_thresh; /* 当前“坡度”阈值 */
unsigned cur_edge; /* 跟踪边的插值位置 */
unsigned last_edge; /* 最后定位边的插值位置 */
unsigned width; /* 最后元素宽度 */
扫描器的创建与释放
zbar_scanner_t *zbar_scanner_create (zbar_decoder_t *dcode)
{
//设定内存分配大小
zbar_scanner_t *scn = malloc(sizeof(zbar_scanner_t));
//初始化,全局变量赋值
scn->decoder = dcode;
scn->y1_min_thresh = ZBAR_SCANNER_THRESH_MIN;
//调用函数,重置扫描器
zbar_scanner_reset(scn);
return(scn);
}
void zbar_scanner_destroy (zbar_scanner_t *scn)
{
free(scn);
//由于扫描器的初始化没有应用于其他组件,可以直接释放
}
zbar_symbol_type_t zbar_scanner_reset (zbar_scanner_t *scn)
{
//初始化扫描器内存,调用memset函数为&scn->x申请内存空间
memset(&scn->x, 0, sizeof(zbar_scanner_t) - offsetof(zbar_scanner_t, x));
scn->y1_thresh = scn->y1_min_thresh;
//若当前扫描器已调用解码器,重置解码器
if(scn->decoder)
zbar_decoder_reset(scn->decoder);
return(ZBAR_NONE);
}
zbar_scanner_reset函数中提及的menset函数是计算机中C/C++语言初始化函数。作用是将某一块内存中的内容全部设置为指定的值, 这个函数通常为新申请的内存做初始化工作。
void *memset(void *s, int ch, size_t n);
函数解释:将s中当前位置后面的n个字节 (typedef unsigned int size_t )用 ch 替换并返回 s 。
实际应用:将扫描器结构中x(下一个样本相对扫描位置)后面的x所需要的内存空间清空,即申请所需内存。
offsetof(type, member-designator);
C 库宏 offsetof(type, member-designator) 会生成一个类型为 size_t 的整型常量,它是一个结构成员相对于结构开头的字节偏移量。成员是由 member-designator 给定的,结构的名称是在 type 中给定的。
此处sizeof(zbar_scanner_t) - offsetof(zbar_scanner_t, x))获得的即为x所需内存空间。
差分运算
差分(difference)又名差分函数或差分运算,差分的结果反映了离散量之间的一种变化,是研究离散数学的一种工具。它将原函数f(x) 映射到f(x+a)-f(x+b) 。差分运算,相应于微分运算,是微积分中重要的一个概念。总而言之,差分对应离散,微分对应连续。
在社会经济活动与自然科学研究中,我们经常遇到与时间t有关的变量,而人们往往又只能观察或记录到这些变量在离散的t时的值。对于这类变量,如何去研究它们的相互关系,就离不开差分与差分方程的工具。微积分中的微分与微分方程的工具,事实上来源于差分与差分方程.因此差分与差分方程更是原始的客观的生动的材料。
读者熟悉等差数列:a1 a2 a3……an……,其中an+1= an + d( n = 1,2,…n )d为常数,称为公差, 即 d = an+1 -an , 这就是一个差分, 通常用D(an) = an+1- an来表示,于是有D(an)= d , 这是一个最简单形式的差分方程。
二阶差分
当自变量从x变到x+1时,函数y=y(x)一阶差分的差分
Δ(Δy(x))=Δ(y(x+1) - y(x))=Δy(x+1) - Δy(x)
=(y(x+2) - y(x+1)) - (y(x+1) - y(x))
=y(x+2) - 2y(x+1) + y(x)
称为二阶差分。
图像的差分方法
差分法是一种用于运动目标检测的可用于克服干扰的行之有效的方法。
两种算法
差分法有两种方式:
(1)当前图像与固定背景图像的差分;
(2)连续两幅图像之间的差分。
第二种算法在很多情况下存在检测物体位置不精确和运算量大的缺点,会影响到定位算法的快速性和准确性。当运动检测开始的时候选择一帧没有运动目标的图像作为差分的背景图像,出现运动目标的时候开始把当前图像和背景图像做差分,当运动目标检测结束时,更新背景图像,当下一个运动目标出现的时候再进行差分。差分的结果可以去除一部分噪声,而且可以去掉与运动目标检测无关的静止背景区域,采用背景图像更新机制,还可以在一定程度上适应背景和光线的变化。在进行差分处理之后,差分图像中只剩下了运动目标和部分噪声,再利用基于投影法的定位算法进行识别和定位。
图像的二阶差分
对于一个一维信号,二阶差分可以看作一个点的左边与右边点的相加减去二倍的这个点,二阶差分更适合锐化图像, 是由于二阶差分产生由零分开的一个像素宽的双边缘, 而一阶微分在类似于斜坡过渡时产生较粗的边缘,因为沿斜坡的一阶微分非零。
边界判断
unsigned zbar_scanner_get_edge (const zbar_scanner_t *scn,
unsigned offset,
int prec)
{
unsigned edge = scn->last_edge - offset - (1 << ZBAR_FIXED) - ROUND;
prec = ZBAR_FIXED - prec;
if(prec > 0)
return(edge >> prec);
else if(!prec)
return(edge);
else
return(edge << -prec);
}
zbar_color_t zbar_scanner_get_color (const zbar_scanner_t *scn)
{
return((scn->y1_sign <= 0) ? ZBAR_SPACE : ZBAR_BAR);
}
图像预处理:对运动均值后图像做一阶差分、二阶差分。一阶差分主要用来判定边界两侧像素大小变化。
ZBar边缘判定规则:二阶导数(y1_sign,即最后一个斜坡的坡度)为零的位置是一阶时的最大值或最小值,因此认为是边缘点。
对二阶导数符号发生变化的地方一定存在边缘点,由于进行的是差分运算,需要进行插值找到导数为零的近似位置。另外还需要满足阈值。
三、总结
本次博客对ZBar扫描器的内存分配以及图像的边界判断(包括差分的运算和应用)进行了分析,对于scanner.c中关于阈值的计算等内容将在下次博客中分析。