每日更新,建议关注、收藏、点赞
ocr流程
版面分析 、预处理-> 行列切割 -> 字符识别 -> 后处理识别矫正
- 判断页面上的文本朝向,图像预处理,做角度矫正和去噪。
- 对文档版面进行分析,进每一行进行行分割,把每一行的文字切割下来,最后再对每一行文本进行列分割,切割出每个字符。
- 将该字符送入训练好的OCR识别模型进行字符识别,得到结果。
- 但是模型识别结果往往是不太准确的,我们需要对其进行识别结果的矫正和优化,比如我们可以设计一个语法检测器,去检测字符的组合逻辑是否合理。比如,考虑单词Because,我们设计的识别模型把它识别为8ecause,那么我们就可以用语法检测器去纠正这种拼写错误,并用B代替8并完成识别矫正。
字符识别方法
- 开源OCR引擎Tesseract,这是谷歌维护的一个OCR引擎。Tesseract现在的版本已经支持识别很多种语言但汉字识别的精度上还是需要自己去改善。Tesseract在阿拉伯数字和英文字母上的识别还是可以的,如果你要做的应用是要识别英文或者数字,不妨考虑一下使用Tesseract;不过要做到你想要的识别率,后期微调或者优化肯定要多下功夫的。
- 字符模板匹配法。暴力的字符模板匹配法看起来很蠢,但是在简单应用上可能却很凑效。
比如在对电表数字进行识别时,考虑到电表上的字体较少(可能就只有阿拉伯数字),而且字体很统一,清晰度也很高,所以识别难度不高。
针对这种简单的识别场景,我们首先考虑的识别策略当然是最为简单和暴力的模板匹配法。我们首先定义出数字模板(0~9),然后用该模板滑动匹配电表上的字符,这种策略虽然简单但是相当有效。我们不需要左思右想去建模,训练模型,只需要识别前做好模板库就可以了。 - OCR的一般方法,即特征设计、特征提取、分类得出结果的计算机视觉通用的技巧。
在深度学习之前,OCR的方法基本都是这种方法,其效果不算特别好。
第一步是特征设计和提取,特征设计(做过模式识别相关项目的懂得都懂),我们现在识别的目标是字符,所以我们要为字符设计它独有的的特征,来为后面的特征分类做好准备。字符有结构特征,即字符的端点、交叉点、圈的个数、横线竖线条数等等,都是可以利用的字符特征。比如“品”字,它的特征就是它有3个圈,6条横线,6条竖线。除了结构特征,还有大量人工专门设计的字符特征,据说都能得到不错的效果。最后再将这些特征送入分类器(SVM)做分类,得出识别结果。
这种方式最大的缺点就是,人们需要花费大量时间做特征的设计,这是一件相当费工夫的事情。通过人工设计的特征(例如HOG)来训练字符识别模型,此类单一的特征在字体变化,模糊或背景干扰时泛化能力迅速下降。而且过度依赖字符切分的结果,在字符扭曲、粘连、噪声干扰的情况下,切分的错误传播尤其突出。针对传统OCR解决方案的不足,学界业界纷纷拥抱基于深度学习的OCR。 - 现在OCR基本都用卷积神经网络来做了,而且识别率也是惊人的好,人们也不再需要花大量时间去设计字符特征了。在OCR系统中,人工神经网络主要充当特征提取器和分类器的功能,输入是字符图像,输出是识别结果。运用卷积神经网络。
当然用深度学习做OCR并不是在每个方面都很优秀,因为神经网络的训练需要大量的训练数据,那么如果我们没有办法得到大量训练数据时,这种方法很可能就不奏效了。其次,神经网络的训练需要花费大量的时间,并且需要用到的硬件资源一般都比较多,这几个都是需要考虑的问题。
证件照识别
- 将证件轮廓找到
- 二值化+高斯滤波+膨胀+canny边缘提取
这里膨胀的作用:使某些信息区域轮廓闭合,便于提取信息区域轮廓。 - 轮廓查找并筛选
有很多干扰项轮廓,如果我们不能很好的剔除这些轮廓,我们根本没法找出我们想要的信息区域。我筛选轮廓的方法很简单,就是找出一张图片中面积最大的那个轮廓作为我们的信息区域轮廓
- 二值化+高斯滤波+膨胀+canny边缘提取
- 提取证件矩形轮廓四点进行透视变换
- 由于轮廓不一定是四边形的,所以(比如x坐标最大的那个坐标肯定是四边形右上角坐标或者右下角坐标,x坐标最小的那个坐标肯定是左上角或者下角的那个坐标,如此类推)这种思路不可行。
- 基于直线交点的思路。我们首先使用霍夫变换找出四边形的边,然后求两两直线的交点就是四边形的顶点。
最大的问题就是,我们怎么保证我们使用霍夫变换找到的直线刚好就是形成四边形的四条直线?所以我们就必须不断地去改变霍夫变换的参数,不断迭代,来求出一个可以形成四边形的直线情况。
什么情况的直线我们不能接受?两两直线过于接近的、两两直线没有交点、检测出来的直线数目不是4条
如果找到了满足条件的四条直线,我们就可以去计算他们的交点了。
计算出四个交点后,继续筛选:两两定点的距离过近排除、四个点构成不了四边形排除
通过以上筛选条件的,可以认为就是我们找的那四个顶点,这时我们就可以停止迭代,进行顶点排序,即确定这四个顶点哪个是左上角点,哪个又是右下点。 - 用这四点来进行透视变换, 后文有详细的介绍。
- 字符识别部分
复杂场景下的ocr
OCR传统方法在应对复杂图文场景的文字识别能力不够,如何把文字在复杂场景读出来,并且读得准确,关键在于 场景文本识别(文字检测+文字识别)。
- 图片预处理
- 透视矫正/透视变换
透视变换是将图片投影到一个新的视平面,也称作投影映射。它是二维(x,y)到三维(X,Y,Z),再到另一个二维空间(x’,y’)的映射。我们常说的仿射变换是透视变换的一个特例。
相对于仿射变换,它不仅仅是线性变换。它提供了更大的灵活性,可以将一个四边形区域映射到另一个四边形区域。
透视变换也是通过矩阵乘法实现的,使用的是一个3x3的矩阵,矩阵的前两行与仿射矩阵相同,这意味着仿射变换的所有变换透视变换也可以实现。而第三行则用于实现透视变换。
注意:变换矩阵T和KT得到的结果是一样的,这个可以自己推一下,相当于分子共同因子还有k,还是把k给除掉了
- 透视矫正/透视变换
通过透视变换,我们可以实现将一张正对我们的图片转换成仰视、俯视、侧视看这张图片的效果,反之也可以实现转换到正对的效果。
- 仿射变换是透视变换的一种特例。
仿射变换是一种二维坐标到二维坐标之间的线性变换,也就是只涉及一个平面内二维图形的线性变换。
图形的平移、旋转、错切、放缩都可以用仿射变换的变换矩阵表示。
它保持了二维图形的两种性质:
① “平直性”:直线经过变换之后依然是直线。一条直线经过平移、旋转、错切、放缩都还是一条直线。
②“平行性”:变换后平行线依然是平行线,且直线上点的位置顺序不变。
任意的仿射变换都能表示为一个坐标向量乘以一个矩阵的形式
#平移
x = 100
y = 200
M = np.float32([[1, 0, x], [0, 1, y]])
move1 = cv2.warpAffine(img, M, (width, height))
#旋转
retval = cv2.getRotationMatrix2D(center, angle, scale)
'''
center是旋转的中心点
angle是旋转角度,正数表示逆时针旋转,负数表示顺时针旋转
scale为变换尺寸(缩放大小)
'''
M = cv2.getRotationMatrix2D((width/2, height/2), 45, 1)
rotation = cv2.warpAffine(img, M, (width, height))
#透视变换
dst = cv2.warpPerspective(src, M, dsize, [, flags[, borderMode[, borderValue]]])
'''
dst代表透视处理后的输出图像,dsize决定输出图像的实际大小
src代表要透视的图像
M为一个3X3的变换矩阵
dsize代表输出图像的尺寸大小
flags表示差值方法,默认为INTER_LINEAR。当值为WARP_INVERSE_MAP时,意味着M为逆变换,实现从目标图像dst到src的逆变换。具体值见下表。
borderMode表示边类型。默认为BORDER_CONSTANT。当值为BORDER_TRANSPARENT时,意味着目标图像内的值不做改变,这些值对应着原始图像的异常值。
border表示边界值,默认为0
与仿射变化一样,可以使用函数cv2.getPerspectiveTransform()来生成转换矩阵。
'''
rows, cols, ch = img.shape
p1 = np.float32([[80, 266], [494, 27], [239, 543], [655, 300]])
# 左上角,右上角,左下角,右下角
p2 = np.float32(
[[0, 0], [800, 0], [0, 600], [800, 600]])
M = cv2.getPerspectiveTransform(p1, p2)
dst = cv2.warpPerspective(img, M, (cols, rows))
#复杂的仿射变换
retval = cv2.getAffineTransform(src, dst)
'''
src表示输入图像的三个点坐标
dst表示输出图像的三个点坐标
src和dst三个点坐标分别表示平行四边形的左上角、右上角、右下角的三个点。
'''
rows, cols, ch = img.shape
p1 = np.float32([[81, 265], [240, 540], [496, 26]])
p2 = np.float32(
[[0, 0], [0, 200], [300, 0]])
M = cv2.getAffineTransform(p1, p2)
dst = cv2.warpAffine(img, M, (cols, rows))
- 水平矫正