本篇详细说明了OCR的应用,技术实现,当前相关进展等,不定时补充更新。
本篇博客最初想写成一本掘金小册,可是掘金的那边看了我的提纲说太难了,应该没什么受众,额……,那我就自己写一下记录下来,不会太讲究斟词酌句了,希望见谅。
本篇博客算是一篇针对OCR相关学习或者初步接触者的引导和入门,讲的不深,实际上针对每个模块,比如CTPN
,CTC
,Attention
,Densenet
这些我都有专门写过博客来讲述过,感兴趣的可以翻翻我的博客文章。
更新记录:
2020.01.31: 自从19年4月份入职字节跳动之后一直忙于工作,很久没有更新个人博客了,今天看到各位的留言很是感动,为自己长久的拖延感到羞愧。由于武汉肺炎延迟开工,今天努力把以前写的提纲补充齐全,这一年来,相比于之前增加了更多的工业界算法思维,一个好的算法是在能够实现预定目的的同时,尽可能节省算力的,接下来也会把自己的一些感触和经验体现在博客里面。
OCR任务详解及应用场景
什么叫OCR
按照维基百科的定义,(Optical Character Recognition,OCR)光学字符识别是指对文本资料的图像文件进行分析识别处理,获取文字及版面信息的过程。通俗来讲,OCR任务就是我们输入一张包含文本资料的图片,得到该图像中包含的文字和版面信息输出,图形中包含的文字很好理解,版面信息对应的是在输入的图片中文字所在的位置信息。
下面通过一张简单的图像进行OCR任务的说明
通过OCR得到的返回结果
上图为使用阿里云提供的OCR服务得到的返回结果,可以看到,通过OCR识别技术,不仅得到了输入图像中的文字信息,并且包含输入图像的版面信息。
OCR用于何处
目前OCR识别在身份证,驾驶证,营业执照,银行卡,车牌,名片,合同文本等具有规范版面格式的图像场景中已经达到了商用指标,并取得了广泛的应用;在学术界则在探索更广泛的应用,研究更先进高效的算法以适用于街景文字识别,手写体识别等场景中。
人的懒惰推动着技术的进步,之所以开发并使用OCR相关技术,就是为了帮助我们更快的进行信息提取。但是在提取图像信息的同时人们对信息提取的准确率是有着的要求的,任何一个算法都要抱着宁缺毋滥的原则,并且在OCR识别中对于小说等文学文档的信息提取准确率,显然和对身份证、驾驶证等识别结果的要求是不同的,这就代表着要开发不同的算法以适应不同的场景需求。
接下来使用阿里云提供的OCR服务:https://ai.aliyun.com/ocr/general?spm=5176.12188956.1280361.58.4ddb1a9eyHHIYB
进行不同类型照片的测试
对于身份证照片
对于汽车车牌照片识别
如上的识别结果由阿里云OCR服务提供,并且目前已经得到了广泛的应用,可以看到对于具有标准格式的照片OCR识别结果非常准确。
总结
本章节结合实例对OCR的概念和应用场景进行了讲解,目前OCR技术已经在众多领域得到了广泛应用,相信大家一定好奇OCR识别具体是如何实现的,会在接下来的章节进行详细讲解
思考
请大家思考一个问题,对于OCR识别结果,如何确保其正确性的呢?
中文OCR任务解决方案
本章节主要针对OCR识别具体的实现方案进行详细的阐述。
环境需求:
python==3.6
opencv-python==4.0
启动jupyter之后,首先导入依赖
1 | import os |
OCR识别过程
图片预处理
对于输入的图片,一般来源是手机拍摄,摄像头拍摄,打印机扫描等,这就意味着输入的图片具有多种格式和色彩特征和多种数据格式,在OCR识别时,要对输入的图片进行预处理,一般的预处理方式包括如下几种。
图像读取
该步骤要读取图像为统一格式,输入的图片包含’jpg’、’png’、’jpeg’等格式,不同的图片格式意味着不同的数据格式,因此在读取图片的时候要转换成一个固定的格式,一般情况下,将所有转换的图像都转换成灰度图数据格式
以上一章节的身份证照片为例
将读取的图像转化成灰度图
可以看到,通过转换成灰度图,输入的彩色图片转换成了灰度图
代码实现如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14# 图片地址
img_path='id_card.png'
# 读取图片
img=Image.open(img_path)
# 显示图片
plt.imshow(img)
# 数据转换成numpy格式
img_data=np.asarray(img)
# 图片转换成灰度图
gray_img=cv2.cvtColor(img_data,cv2.COLOR_RGB2GRAY)
# 显示灰度图
plt.imshow(gray_img)
# 保存图片
cv2.imwrite('gray.png',gray_img)图像色彩处理
一般情况下,OCR输入的图像输入的是彩色图像,图像中包含着大量的色彩信息,为了提高OCR的识别效率,更快的提取图像中的文字信息,通常对输入的图像进行二值化处理,所谓二值化就是将图像进行处理,使其只包含黑白二色。
代码实现如下
1
2
3
4
5
6
7# 动态阈值进行图像二值化处理
blur_img = cv2.adaptiveThreshold(
gray_img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 25, 20)
# 显示二值化图像
plt.imshow(blur_img)
# 保存图片
cv2.imwrite('blur.png',blur_img)图片去噪
将图像转换成二值图之后,会发现图片中存在很多噪声,对于噪声的分类一般为椒盐噪声,高斯噪声。在图像处理中使用中值滤波或者均值滤波进行图片去噪处理
1
2
3
4
5
6
7
8# 对二值图片进行噪声去除
# fastNlMeansDenoising的参数为
# 待处理图片,滤波器强度,对彩色图片滤波摩人与滤波器强度相等,模板尺寸,搜索窗口尺寸
img_denoised=cv2.fastNlMeansDenoising(blur_img,10,10,7,21)
# 显示图片
plt.imshow(img_denoised)
# 保存图片
cv2.imwrite('denoised.png',img_denoised)图片倾斜矫正
如果输入的图片是手机拍摄,一般情况下照片会不可避免的存在倾斜,这时候就要对输入的图片进行倾斜矫正。倾斜矫正包括两种,一种是图片旋转,一种是图片的仿射变换。
对于图片旋转,首先考虑如何实现图片的旋转呢,实际上对图片进行旋转就是进行矩阵旋转,首先分析旋转在二维平面中围绕一个点进行旋转
图中所示为点V绕远点旋转角度$\theta$,得到旋转后的点$\mathbf{v}^{\prime}$,假设$v$点原来的坐标为$(x,y)$,进行推导
$$
\begin{array}
{l}
{x=r \cos \phi \
y=r \sin \phi} \
{x^{\prime}=r \cos (\theta+\phi)\
y^{\prime}=r \sin (\theta+\phi)}
\end{array}
$$
使用三角函数进行展开
$$
\begin{aligned} x^{\prime} &=r \cos \theta \cos \phi-r \sin \theta \sin \phi \ y^{\prime} &=r \sin \theta \cos \phi+r \cos \theta \sin \phi \end{aligned}
$$
再将x,y带入到上面式子中可以得到表达式
$$
\begin{array}{l}{x^{\prime}=x \cos \theta-y \sin \theta} \ {y^{\prime}=x \sin \theta+y \cos \theta}\end{array}
$$
这是对单个点以原点为中心进行旋转,当扩展到一个图像中的时候,写成矩阵形式
$$
\left[ \begin{array}{l}{x^{\prime}} \ {y^{\prime}}\end{array}\right]=\left[ \begin{array}{cc}{\cos \theta} & {-\sin \theta} \ {\sin \theta} & {\cos \theta}\end{array}\right] * \left[ \begin{array}{l}{x} \ {y}\end{array}\right]
$$
如果旋转中心点不是原点,而是任意一个点,那么这时候该怎么做呢?在计算机图形学中,为了统一将图形平移、旋转、缩放等都用矩阵形式表示,需要引入齐次坐标。将上述问题进行拆分,首先将旋转点移动到原点处,之后按照原点进行旋转,最后将旋转点从原点移动到原来的位置。
首先进行二维平移操作,如上图所示,一个点$P(x,y)$,移动到点$P^{\prime}(x^{\prime},y^{\prime})$,从图中可以很明显的看出在二维空间的坐标位置关系
$$
x^{\prime}=x+t x \
y^{\prime}=y+t y
$$
使用其次坐标进行表示二维平移
$$
\left[ \begin{array}{c}{x^{\prime}} \ {y^{\prime}} \ {1}\end{array}\right]=\left[ \begin{array}{lll}{1} & {0} & {t x} \ {0} & {1} & {t y} \ {0} & {0} & {1}\end{array}\right] * \left[ \begin{array}{l}{x} \ {y} \ {1}\end{array}\right]
$$
同理,结合二维空间的旋转操作,在其次坐标下进行旋转操作,得到变换关系式为
$$
\left[ \begin{array}{c}{x^{\prime}} \ {y^{\prime}} \ {1}\end{array}\right]=\left[ \begin{array}{ccc}{\cos \theta} & {-\sin \theta} & {0} \ {\sin \theta} & {\cos \theta} & {0} \ {0} & {0} & {1}\end{array}\right] * \left[ \begin{array}{l}{x} \ {y} \ {1}\end{array}\right]
$$
按照前面对绕任一点旋转的拆分,先进行平移,之后旋转,最后再反向平移可以得到旋转矩阵为
$$
M
=\left[ \begin{array}{ccc}{1} & {0} & {\operatorname{-tx}} \ {0} & {1} & {-t y} \ {0} & {0} & {1}\end{array}\right] * \left[ \begin{array}{ccc}{\cos \theta} & {-\sin \theta} & {0} \ {\sin \theta} & {\cos \theta} & {0} \ {0} & {0} & {1}\end{array}\right] * \left[ \begin{array}{ccc}{1} & {0} & {tx} \ {0} & {1} & {t y} \ {0} & {0} & {1}\end{array}\right]\
=\left[ \begin{array}{ccc}{\cos \theta} & {-\sin \theta} & {(1-\cos \theta) t x-t y * \sin \theta} \ {\sin \theta} & {\cos \theta} & {(1-\cos \theta) t y+t x * \sin \theta} \ {0} & {0} & {1}\end{array}\right]
$$
如上便是在计算机图形学中对图像进行旋转的数学原理,在代码实现中一般使用opencv实现。实现代码
1
2
3
4
5
6
7
8
9
10
11h,w=img_denoised.shape
# 计算图片中心点
center=(w//2,h//2)
# 创建旋转矩阵,旋转角度为45度
M = cv2.getRotationMatrix2D(center, 45, 1.0)
# 得到旋转之后的图像
img_rotated = cv2.warpAffine(img_denoised, M, (w, h))
# 显示图片
plt.imshow(img_rotated)
# 保存图片
cv2.imwrite('rotated.png', img_rotated)上述是进行图像旋转的代码实现。可以打印矩阵M查看和使用公式计算的结果是一致的。
可以看到,旋转前后的图像的尺寸保持一致,这就会造成旋转之后的图片会丢失部分图片信息,实际上可以对上面的代码进行修正
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19# 计算图片的几何尺寸
h,w=img_denoised.shape
# 计算图片中心点
center=(w//2,h//2)
# 创建旋转矩阵,旋转角度为45度
M = cv2.getRotationMatrix2D(center, 45, 1.0)
# 计算旋转角度的正余弦值
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])
# 计算旋转之后的图片的宽度和高度
nW = int((h * sin) + (w * cos))
nH = int((h * cos) + (w * sin))
# 在旋转矩阵的基础上加入角度旋转造成的图片尺寸的影响
M[0, 2] += (nW / 2) - center[0]
M[1, 2] += (nH / 2) - center[1]
# 旋转图像
rotated2=cv2.warpAffine(img_denoised, M, (nW, nH))
# 显示图像
plt.imshow(rotated2)对图片进行修正之后,可以看到旋转之后的图片保留了原图片的全部信息,
版面分析
将输入的文档图片按照段落或行为单位进行版面分析,在实际操作中,由于文档是丰富多样的,因此目前没有固定的版面分析算法,要根据实际应用场景进行设计,这也是目前OCR识别任务重比较重要的一部,会在之后部分详细说明。
字符识别
在OCR识别任务中,存在多种字符识别的方法,早期使用的是模板匹配的方法,后期有特征提取的方法,直至目前的深度学习的方法,该步骤对OCR识别准确率至关重要,同样会在之后的部分进行详细说明。
版面还原
对于OCR任务中,我们希望识别后的文字仍然按照输入文档图片那样排列,保持段落不变,文字位置不变,顺序不变的输出,该步骤就是版面还原的过程。在该步骤中,版面还原的信息依赖于前期的版面分析的到的版面信息。
输出校对
最后是对输出的识别结果使用自然语言的方法进行识别结果的校正。
上述过程就是OCR识别的一般过程,简单来讲就是,先对图像进行预处理,将输入的图像变成统一的格式,之后进行版面分析和文字识别,最终进行版面还原输出。在整个过程中,图像预处理一般使用的是通用的技术解决方案,就如上文所说,进行图像转成灰度图,二值化处理,提高对比度亮度,旋转仿射变换等常规处理手段,经过如上的图像预处理,输入的图像变成相对统一的图像数据。之后便要对输入的图像进行版面分析,主要是找到图像中的待识别文字的位置区域,这里的文字区域可以是中文中的单个文字,可以使英文中的单个单词,也可以是一行文字;当找到待识别文字的位置区域之后,就下来就是对区域内图像文字进行识别。接下来便对版面分析和文字识别进行着重说明。
版面分析
正如上文所述,版面分析就是要找到待识别文字的位置区域,所谓文字区域,一般是指单个文字或者一整行的文字
传统手工特征方法
如果要提取单文字的位置区域,存在多种技术实现方法,下面进行简略说明。
- 图像投影方法
对预处理之后的图像,将图像对垂直方向的坐标轴进行投影,图像中不同行的文字经过投影会被间隔开,如此就可以提取到不同的文本行,如下所示为对进行噪声去噪之后的二值图进行水平方向的投影。
代码实现
1 | # 图片预处理 |
从图中可以看出,对输入图像进行处理之后,图像的背景色为黑色,文字部分为白色,在图像数据中黑色对应的值为$0$,白色对应的值为$255$。对图像进行垂直方向投影,输入图像中的文字区域部分垂直投影之后得到的值较大,背景区域投影之后得到的值较小,通过对垂直投影之后的数据进行提取,就可以确定图像中存在多少行和每行在图片高度方向上的位置。
确定输入图像中的每行文字区域之后,接下来对每行的图像进行水平方向的投影,由此确定当前行中的每个文字所在的位置,处理方法是和竖直方向投影方法相同。
使用如上方法可以实现对输入图像中每个字符进行提取,也就是找到每个文字在输入图像中的位置,提取到单字符之后便可以对每个文字进行识别。
由于对文字的提取基于竖直方向投影和水平方向投影得到,因此提取到的字符依赖于算法中设定的阈值,该阈值用于区分每一行文字或每行中的每个文字。从图中可以看到,提取到的单字文字存在一定错误,这正是由于算法中设定的阈值造成的,这也是该方法的局限性,尤其是对于中文字体结构,在中文中存在很多左右结构的文字,比如“小”,“林”,“从”等文字,如果算法中设定的阈值不合适,那很有可能将左右结构的一个字提取城两个字。
除了上述的方法之外,还有基于连通区域的方法,其假设字符本身是具有连通性的,之后通过连通区域的检测方法好到文字的候选区域,感兴趣的读者可以参考论文Detecting Texts of Arbitrary Orientations in Natural Images;或者使用最大稳定极值区域的方法,使用该方法得到候选字符区域,并将这些区域看做是连通图的顶点,因为,同一行的文本通常具有相同的文字方向,颜色,字体等,所以可以将文本行的确定转化为连通图聚类的过程,通过该方法确定文本行,感兴趣的读者可以参考文献Text Detection for Multi-Orientation Scene Images using Adaptive Clustering。
对于上述的基于人工特征方法的OCR技术实现方案,都伴随着复杂的特征工程,不论是从文本图形的文字的连通性考虑或者从文字的字体等信息考虑,这些方法的适用性都相对狭小,只能处理相对单一类别的文本图像,此外提取的效果也不够鲁棒,容易受到输入图像中的背景信息影响,并且提取结果很容易受到人为设置的一些参数的影响。随着计算机硬件计算能力的大幅度提升,在2014年左右,出现了深度学习方法的OCR技术方案,OCR技术不论是从识别效果还是从OCR识别场景适用性都取得了巨大进步。
深度学习OCR技术
正如上文所述的使用人工特征的方法在OCR识别过程中存在诸多问题,深度学习OCR技术以其适用性更广,人工特征更少,识别准确率更高的优点,目前已经成为OCR识别的主流应用技术。对于OCR识别过程,上文已经有所描述,过程中的难点,一个是版面分析,也就是找到输入图像的文本区域所在的位置,另一个是文本识别,也就是对找到的文本图像进行识别。这里所说的深度学习OCR技术,也是着重解决这两个问题。目前存在的深度学习OCR方案主要是分两种,一种是端到端实现OCR识别,也就是设计一个网络模型,实现文字区域检测和文本识别,另一种是进行对文本检测和文本识别都设计开发单独的网络模型以实现任务目标,下面进行分别说明。
文本检测
说到文本检测,也就是检测到输入图像的文本区域,得到文字区域的位置信息。目前主要有两种深度学习实现思路,一种是基于目标检测方法,一种是基于图像分割的方法,其中基于目标检测的方法是目前应用最广泛的方法。
目前来说,在文本检测中,以目标检测为baseline仍然是占据主流,因为这两个任务具有天然的相关性,文本检测可以看做是目标检测中的一个分支,因此通用的目标检测技术经过一定定制优化是可以直接移植到文本检测上的,本人不太推荐基于分割的技术方法,首先在数据标注上,分割显然比检测的标注难度提升好几个等级,再者就是分割算法相比于检测算法需要更多的参数量,需要更多的计算资源和更久的运算时间,在工业界不太推荐。
基于目标检测的方法
早期比较有代表性的深度学习OCR识别算法是由大名鼎鼎的VGG实验室开发的Reading Text in the Wild with Convolutional Neural Networks,该模型可以实现文本检测和文字识别的端到端OCR识别过程,模型中最具代表性的是文本检测的实现方法。
- 使用EdgeBoxes:边缘框算法生成大量的文本提议区域,该步骤要尽可能生成足够多的候选文本区域,要尽量能够更好的覆盖真实文本区域;
- 使用随机森林分类器对提议文本区域进行分类,过滤第一步生成的非文本提议区域;
- 使用目标检测模型RCNN中对文本提议框回归的计算方法,对过滤之后的文本提议框进行修正以接近真实文本区域范围;
- 最后使用一个文字识别算法进一步过滤非文本提议区域。
该方法可以看做是在RCNN基础上的应用,在RCNN中使用随机搜索的方法生成了候选文本提议,在该算法中使用的则是边缘框算法来实现,该算法是早期比较有代表性的深度学习在OCR识别任务中的应用,在2014-2015年一直保持领先地位。
2016年中科院深圳先进技术研究院乔宇老师研究组,基于目标检测模型FasterRcnn开发了适用于文本行检测的深度学习模型CTPN。
根据文本检测任务中文本区域的特点,设计了区别于常规目标检测任务的文本区域划分方案;
在VGG模型最后一层的特征图上进行AnchorBox计算,计算方法与FasterRcnn相同;
在计算过程中考虑上下信息,将每行的文本特征输入到双向LSTM中以便更好的判断文字区域的概率
判断文字的高度,起始位置和终止位置
CTPN首次在场景文字检测中使用RNN方法,但是受限于CTPN的文本区域划分方案,该算法模型默认只能用于输入图像的水平文本检测。
同样是在FasterRcnn的基础上改进而来,RRPN对AnhocrBox进行了设计,在原FaterRcnn中RPN网络的AnhorBox的基础上加入了角度旋转信息,设计了RRPN网络用以生成带角度信息的文本区域提议,此外,为了适应RRPN,将原FasterRcnn中ROI进行改进设计了RROI,以实现可以计算处理任意方向的文本提议区域。
基于文本在图像中是具有不规则多边形的轮廓的角度思考,DMPNet将目标检测任务中目标区域为矩形框修改为多边形目标区域,在目标检测模型SSD的基础上进行整个模型的设计。
如图是
DMPNet
中关于Anchor
的的设计方案,在图$a$中,如果使用用于的目标检测的Anchor
生成方法,得到的检测区域是蓝色虚线部分,因为在传统的Anchor
生成过程中,先验的Anchor
都是矩形形状,因此得到的检测框肯定也是矩形的,但是在街景OCR
中,使用矩形的区域进行文本区域检测显然会引入更多的背景信息,对后续的文本识别带来干扰。在DMPNet
中的Anchor
设计使用的是图C
这种方式,加入了旋转角度,这样的Anchor
设计会检测到图$a$中红色虚线的区域,显然相比于传统目标检测得到的结果更优。
文本识别
检测之后肯定就是识别了,由于2019年中旬之后更换工作,没有太多关注OCR相关方面的发展,根据我曾经的浅薄的经验,使用何种文本识别技术基于我们使用了何种文本检测技术,尤其在工业界,我们的目的是输出图片中的信息,而不是检测的检测到文本区域。
前面已经说过了,目前的主流做法是基于目标检测的文本检测实现方案,检测到的一般情况是文本区域而不是单个的字,这个时候我们在进行文本识别的时候显然也要对文本区域进行识别,现在的CNN
技术发展迅速,几乎都快到瓶颈了,这些我们先不管,我们只需要知道对于文本识别目前来讲基于CNN
是一种非常可行的方案,不管是CRNN
(将CNN,RNN
联系起,模拟语音识别的方法建立前后文联系),还是CNN+CTC
的方案,都是将文本区域使用CNN
进行特征提取,然后使用NLP
中解决方案。
至于更传统的单字符识别目前应该没有人使用了,这里也就不过多介绍了。关于该部分的内容,如果想了解更多,我也写过针对OCR
的一篇博客文章: 深度学习-OCR_Overview
深度学习技术实现方案综述
前文已经对深度学习在OCR
上的应用有所涉及,目前的不论学术界还是工业界应该都已经抛弃了传统的直方图或单字符识别的研究和投入,但是,还是那句话,最后的算法是在满足需求的条件下尽可能节省算力,如果我们的场景是具有规定排版样式的场景,比如扫描合同,身份证,银行卡,发票单据等,这些使用传统方法实际上完全可以cover
住,没有low
的算法,只有low
的开发者,这是近一年来在字节我最大的收获。
接下来说一下目前的主流解决方案,OCR
目前来讲分为两个过程文本区域检测和文本识别,目前应该还没有End2End
的解决方案。这两个过程主要就是上面所说的那样,文本检测一般是基于目标检测来实现,文本识别一般是不定长文本识别,因此更多的采用的是NLP
中关于语音识别的相关方法。
连续文本提议网络(CTPN)模型原理
这一部分,我已经写作一篇博客了,可以看一下这里深度学习-TextDetection