先来看一个例子,假如上帝拥有一个完美的字符归一化算法,那么他将可以做到如下所示的效果:
图1,完美的归一化:左边为原始字符区域,右边为归一化后的结果。
如果说我们能做到上述结果,那么也就无需再做特征提取,也无需再做训练,而只需简单的模板匹配即可得到100%准确的分类结果。可以看到,上述算法的能力在于:
1. 归一化到标准模板大小
2. 倾斜校正
3. 笔画宽度归一化
4. 字形归一化
可惜的是,今天介绍的几种常见算法仅能保证第1点的实现,而2,3则只能实现部分。至于4,就让后续的特征提取去弥补吧。言归正传,3个算法分别是:线性归一化算法,基于图像矩的归一化以及非线性归一化算法。
按惯例,3个算法的标准c实现可在:
https://github.com/UnilVision/visionbase/tree/master/ocr/baseline/normalization找到。希望对大家有所帮助。
线性归一化:线性归一化算法就是一个标准的线性采样过程,采用线性插值获得最终的图像结果。在我们的实现中,使用反向计算的方式:
对应代码中的函数为:
void backward_linear(unsigned char* src, int src_wid, int src_hei, int src_widstep,
CHARECT_t* region,
unsigned char* dst, int dst_wid, int dst_hei, int dst_widstep,
int ratio_preserve_func);
图像矩归一化: 我们可以通过图像矩来预先校正字符的倾斜度,并通过矩来获得字体的实际大小[w1, h1]及中心位置[xc, yc]。归一化的原始区域被修改为
[xc-w1/2, xc+w1/2, yc-h1/2, yc+h1/2]。其计算方法为:
其中图像矩的计算方法:
在找到新的区域[xc-w1/2, xc+w1/2, yc-h1/2, yc+h1/2]后,后续即调用线性归一化算法即可。对应代码中的实现为:
void backward_moment(unsigned char* src, int src_wid, int src_hei, int src_widstep,
CHARECT_t* region,
unsigned char* dst, int dst_wid, int dst_hei, int dst_widstep,
int ratio_preserve_func);
采样计算方式为:
注意这里我们仅调整x的位置以保证图像的中心仍然处于原始的xc,yc。其实现对于:
// slant correction
// Note>> (dst_wid, dst_hei) must equal to (region.width, region.height)
void backward_moment_slantcorrection(unsigned char* src, int src_wid, int src_hei, int src_widstep,
CHARECT_t* region,
unsigned char* dst, int dst_wid, int dst_hei, int dst_widstep);
通常倾斜校正会放在归一化之前,已获得更好的效果。
非线性归一化:这里实现的是Jun Tsukumo在1988年提出的一个经典算法(原论文名称为Classification of Handprinted Chinese Characters Using Non-linear Normalization and Correlation Methods)。作者的思路是希望每一行,每一列的背景区域都可以平均分布。
为此,他首先为每个像素在x,y方向分别定义了个概率密度函数:以及。这两个函数的计算方法是:
如果(x,y)是一个属于字符区域的像素,那么都取一个极小值(在我们的实现中,这个值是0.001f,调整这个参数可以引起归一化后笔画的粗细变化)。
如果(x,y)是背景区域像素,那么:
其中和分别是当前像素所处x方向背景像素的run-length和y方向的run-length。有了这两个密度函数,定义:
这里px和py就是归一化后的投影直方图了,为了在归一化后的图像中让px和py平均分布,引入两个函数hx,hy:
通过前向映射采样即可实现归一化操作:
注意这里与前两个算法的不同之处,前向映射是将当前图像的某个像素映射到归一化的图像中。而反向映射则是将归一化的图像中的某个像素位置映射到原图像中。
非线性归一化的实现对应:
void forward_nonlinear_1d(unsigned char* src, int src_wid, int src_hei, int src_widstep,
CHARECT_t* region,
unsigned char* dst, int dst_wid, int dst_hei, int dst_widstep,
int ratio_preserve_func);
参考结果:
最后看下各个算法的结果:
图2,参考结果。从左到右依次:1. 原始扣取的图像通过OpenCV的resize函数缩放。2. 线性归一化。3.基于矩的归一化。4.先倾斜校正再基于矩的归一化。5.非线性归一化。
[原创文章,转载请注明出处:http://blog.csdn.net/unilvision/article/details/8624606]