一直在做ocr识别的相关工作。由于兴趣及工作需要,研究优化了下tesseract识别引擎的各个模块。发现关于tesseract引擎国内的资料,对于代码及原理的介绍相对较少,大部分都是应用类的文章,我将看过的代码结合自己的理解介绍识别的各个模块,与大家一起交流学习。
先看下训练时我这边提取到的数字1的MF特征(该特征我是理解为笔画直线中点的坐标,该直线长度及角度等,字提取轮廓后其实就是一条条直线了)
mf 5
0.022858083 0.32230198 0.37753356 0 0 0
-0.16590869 0.11053124 0.42354149 0.25 0 0
-0.25303182 -0.20042329 0.26402926 0.13528861 0 0
-0.064265043 -0.29960707 0.55177981 0.5 0 0
0.21162486 0.011347458 0.62190902 0.75 0 0
X y len dir . . .
下面来看MF特征是如何得到的:
首先定义一些方向的sign 值
Left 0 down 32 right 64 up 96
leftdown 16 downright 48 rightup 80 upleft 112 类似对应45度 刚好8个方向
1:二值化提取出轮廓
轮廓点集 <x,y,dir>由46个点组成
2:去掉同一条直线上的点,以及将直线变平滑(如90度角处理成45度)得到点集
点集连成的直线 图像和1中的差不多,但点少了很多
去掉冗余点的程序过程大概如下:
pos = c_outline->start_pos (); //start of loop c_outline轮廓点集
length = c_outline->pathlength ();//个数
stepindex = 0;
epindex = 0;//提取的个数
prevdir = c_outline->step_dir (0);
do {
dir = c_outline->step_dir (stepindex);
if (stepindex < length - 1 && c_outline->step_dir (stepindex + 1) - dir == -32) {
//方向为 DownLeft 16 Rightdown 48 upright 80 rightup 112一个
dir += 128 - 16; //一个字节 不为零 +128不变0与128意义一样
stepinc = 2; //出现拐点则为2
}
else
stepinc = 1;
if (count == 0) {
prevdir = dir;
}
if (prevdir.get_dir () != dir.get_dir ()) {//两个点角度不一样
edgepts[epindex].pos.x = pos.x ();
edgepts[epindex].pos.y = pos.y ();//提取该点
prevdir = dir;
pos = edgepts[epindex].pos;
}
stepindex += stepinc;
}
while (stepindex < length);
0:index = 13 prevdir 32 dir16 push(<5,28>)
1:index = 15 prevdir 16 dir 32 push(<5,15>)
2:index = 20 prevdir 32 dir 48 push(<4,14>)
3:index = 22 prevdir 48 dir 64 push(<4,9>)
4:index = 23 prevdir 64 dir 16 push(<5,8>)
5:index = 25 prevdir 16 dir 96 push(<6,8>)
6:index = 44 prevdir 96 dir 0 push(<7,9>)
7: 加入最后pos
ps: 这边的dir可以理解为这7个点中,该点后两个点连成直线的走势角度
如 (5,15)(3,14) 刚好为leftdown 16
3:再次去冗余。大概去掉距离比较近的点,这部分没怎么看,
只剩五个点了。
点集连成直线
Ps:最后得到的点类似于笔画拐点,点的个数也是mf特征的个数(两点一条直线一个特征,n个点闭合有n条直线,即有n个特征,最后提取的特征为直线的属性)
4:MF特征计算
先看下一行字中每个位置sign
以我测试的一张图片为例
所有矩形框文字点范围 rect x(4,229) y(0,38) mid_x 116.5
X_height 求得21 调为128则对应比率scale 6.095238
数字 1的矩形框x(1,15)y(8,27)
Origion_y 8.02 猜测类似矩形框中点y的起点base_line((4+7)/2)
以mid_x为坐标中点且mid_x映射到0,x_height高度大小放大到128:
X1,x2 文字点范围 Rect中mid_x设 将x_height高度设
为0,对应坐标 为128,乘以scale
<1,15> <4,7> -116.5=<-113,-110> *6.095238 = <-689,-670>
X_height高度大小放大到128,以64为中心点
Y1,y2 文字点范围 -origion_y 将x_height高度设 +final_y_offset
为128,乘以scale
<8,27> <9,28> -8.02=<1,20> *6.095238<6,122> +64=<70,186>
对于提取到的拐点按上面步骤运算,得到下列5个原始点。再做映射
<1>其中映射到0-255 做了宽高比的归一化
X_mid -677.15 y_mid 126.6 x_mid,y_mid的计算非那五个点,而是点连成的直线所有点
X_scale 7.4345096
Y_scale 1.3724890 个人理解,因为文字有可能被压缩变形,默认类似正方形,因为数字1宽度较小,高度长,而为了归一化为正方形,X_scale便会比较大
计算结果 (xi-x_mid)*scale_x+128
如(-683 + 677.15)* 7.4345096 + 128 = 83.21 结果有误差,因为有些小数点损失
<2>映射到-0.5 - 0.5 除以128范围是(-1,1) 所以要除以256
(Xi-127.5) / 128 / 2
(84.5-127.5)/256 = -0.167
通过拐点提取特征点,取两个拐点的直线的中点坐标,直线长度,直线角度,最终提取到的特征
五个拐点五条直线五个特征