Zbar源码分析

前言

身在物流行业,扫描条码的场景很多。为了改造 ZBar 使其返回条码方向(条码与水平线的夹角),阅读了 ZBar 的源码。总结一下。(没有弄清楚所有细节,只说说我弄清楚的部分)

主流程

应该大家都知道 ZBar 是 Z 字型扫描的。(为什么要 Z 字型扫描?不清楚。)一次扫描一行或者一列,在扫描过程中做滤波,EWMA,指数加权移动平均,可以消除部分噪声影响,然后计算梯度变化,确定边缘,计算当前边缘与上一边缘的距离作为一个条码元素( element,bar 或者 space)的宽度,这些元素宽度放在一个滑动窗口数组中,以 Code 128 为例,这个数组的长度是 6,因为 Code 128 是 3 个 bar 加 3 个 space 表示一个字元。每个字元宽度是最窄 element 的 11 倍,各个 element 根据类型(bar、space)以及所占份数(宽度是最窄 element 的几倍)被应设成二机制数,(举例来说,x 代表一份宽度的 bar,o 代表一份宽度的 space,xxxoxxooo 被映射成 111011000),根据二进制数查表,得到字符。在对这个宽度数组进行解码的时候使用了一个叫 like-edge measurement 也叫 similar-edge measurement 的机制进行计算,操作上是,计算第 0 和第 1 element 的宽度和 a、第 1 和第 2 element 的宽度和 b、… 第 4 和 第 5 element 的宽度和,可以解决一部分因为边界模糊而导致的宽度误差。识别到终止符的时候组装字符返回结果,再次识别出某结果,该结果quality 加 1(quality 可以理解成 votes,可以考虑在 quality 大于某个阈值时提前结束识别,减少不必要的计算。)

Zbar源码分析 
Img_scanner.c 
int zbar_scan_image (zbar_image_scanner_t *iscn, 
zbar_image_t *img) 
经过上一步的分析后发现图像扫描的工作都是由zbar_scan_image完成的,zbar_scan_image主要根据设定的扫描密度(density)控制像素点读取(Z字形),zbar_scan_y()来完成滤波,阈值,确定边缘,转化成宽度流。

//先判断有没有设定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方向扫描密度(xdensity),同理Z字形扫描
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

2016年3月2日21:00:27 
zbar_symbol_type_t zbar_scan_y (zbar_scanner_t *scn,int y) 
逐点扫描判断边界 
实际的条形码波形不是理论中的方波,而是高斯退化的波,其边界处是凹凸交界的位置。 
求边界的原理是二阶导数为0或者异号的位置可能为边界即Scanner.c中247行的注释/* 2nd zero-crossing is 1st local min/max - could be edge */ 
gauss_20_sigma10 
通过第138行dprintf(1, ” thr=%d t=%ld x=%d last=%d.%d (%d)”, 
thresh, t, scn->x, scn->last_edge >> ZBAR_FIXED, 
可以看出关于ZBAR_FIXED是小数位数,ROUND换算下就是0.5。 
函数一开头使用EWMA对原始数据滤波,抑制突变。再用y0数组存储邻近点的数据,用来求一阶导数和二阶导数, 
scn->y0[(x - 1) & 3]很巧妙的限制住了数组索引不越界,循环使用。 
紧接着开始求一阶导,二阶导 
register int是设置变量常驻cpu,加快执行速度 
2nd differentials 
但第234行的判断不知何意

if((abs(y1_1) < abs(y1_2)) &&
           ((y1_1 >= 0) == (y1_2 >= 0)))
            y1_1 = y1_2;
  //如果y11与y12同向且y11小于y12,则y11=y12
  • 1
  • 2
  • 3
  • 4

static inline unsigned calc_thresh (zbar_scanner_t *scn) 
dx = x - lastedge; 
t = thresh*dx; 
t /= scn->width; 
t /=8; 
calc_thresh 
这一段程序主要是为了使thresh逐渐回归到thresh_min,如果dx在8width范围内,则thresh=thresh(1-dx/8w);即如果dx=width,则thresh=7/8thresh。如果dx超出8w范围,则直接置为thresh_min。thresh在每次确定新的边界时更新为此处的y11的0.44倍。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值