深度学习:识别图片中的电话号码

时间:2024-03-20 20:22:59

自动识别图片中电话号码,也可以推广到识别字符串,英文等。

深度学习:识别图片中的电话号码

---识别--->

“18811610168”

当然,背景会有干扰(自然环境),字体也更多变。

要求:全对识别,人工修正的成本过高,如果有一位识别错误,人工修正还不如重新输入方便(人工输入可以支持语音识别的)。保证85%以上的数据是可以全对识别的,最好返回全对识别的概率。当然对于电话号码业务,根据识别的文本结果再加上电话的一些规则,后判断程序也可以有效地判断哪些是高概率识别错误的。

方案

目标检测、ocr识别已经是被学术界玩透的技术了。神经网路是目前的大热,而且效果极佳。识别方案有以下三种:

方案一:作为传统ocr来解

检测图片中的数字位置,然后对数字region过分类器识别。传统方法:Sliding Windows、Selective Search等寻找候选区域;Boosting分类器、SVM等都可以用于识别。深度学习的方案效果更好,比如:Faster RCNN、YOLO等。

它们的实质都是:检测字符的位置,识别字符的类别(内容)。

检测:深度学习:识别图片中的电话号码

识别:深度学习:识别图片中的电话号码 --->数字“1”  深度学习:识别图片中的电话号码----->数字“8” … …

识别结果主要受检测准确度和识别精度的影响。在检测完全正确的情况下,自左向右识别各个字符,然后串联识别结果。如果检测完全正确,识别分类器的loss=0.01,每个字符的正确识别的概率p=0.99。11位电话全对的概率约为0.895,loss值=-ln(0.895)=0.111。

但是,检测可能出错。另外,图片中的电话可能是倾斜的,后期串联结果也会比较繁琐,比如。

方案二:循环神经网络RNN

方案一没有考虑电话号码的序列特性,如果将图片分割为时序信号,送入RNN/LSTM/BLSTM等网络,识别性能会大幅提升。

深度学习:识别图片中的电话号码

问题是图片的分割不准确怎么办?其实这里的分割并不是严格的,解决方案是,将图片按行分割成n个,比如电话号码分割成21个小图片,然后20个图片送入RNN网络得到20个序列化的输出。由于一个字符可能在相邻两个小图上出现(各一半),最终的识别结果很可能是“1_88_8_1_1_66_10_16_8”。最后再经过一个CTC网络融合得到“18811610168”。

方案三:循环卷积网路RCN

方案二是把图片原图分割后送入RNN网络,RNN做特征提取和分类识别。级联RNN的训练是不容易收敛的,而且特征的提取工作是CNN的强项。重新设计网络,可以使用cnn提取图片的特征,然后将feature Map分割成n个,送入RNN做识别,最终通过CTC得到识别结果。

深度学习:识别图片中的电话号码

这样的好处是,输入数据不用做预处理,切割可以通过网络中矩阵的转置实现;网络实现了End-to-End;输出是完整字符串,识别准确率高,loss值约为0.02,全对正确率约为0.98。

背景介绍见:深度学习:识别图片中的电话号码(1)

识别电话号码最终采用的是:循环卷积网络。先看下网络的结构,有几处比较trick的地方,后面会讲到。

深度学习:识别图片中的电话号码

注意的地方

1. 上图中的网络是训练网络的结构,训练的输入有三个:image图片;seq是时序的标识,如果时序长度为5则为[0,1,1,1,1],0表示序列的开始,1表示序列继续(当前时刻有信号输入),上一时刻的细胞状态会传递给当前时刻。label表示序列的真实结果,如“123”应该表示为[1,2,3,-1,-1],长度和seq一致。

在batchsize中,多个样本的seq大小如果不同,短序列的seq=[0,1,1,…1,0,0..0],表示短序列提前结束了。当然电话识别中,把图片特征按列等分了n份,大家的序列长度是一致的。

2. 序列的分割

输入image的大小是:batch_size*3*h*w

输入seq的大小是:batch_size*n ,n表示序列的长度=图片特征要切割的份数

输入label的大小是:batch_size*n

经过ZF特征提取网络,relu5的输出为:batch_size*num_output*height*width,num_output表示featureMap的数据,width/height特征层的宽高。

trans1转置层,参数为:

transpose_param {
    dim: 0 
    dim: 3
    dim: 2 
    dim: 1
}

输出的维度为:batch_size*width*num_output*height。trick的做法是:n的大小可以设置为width,即把特征层的每一列作为一个时刻的输入。

conv5_trans为全连接层,当然它的全连接只针对某一列在num_output个featureMap上的节点进行,参数为:

深度学习:识别图片中的电话号码
inner_product_param {
    num_output: 4096
    weight_filler {
      type: "gaussian"
      std: 0.01
    }
    bias_filler {
      type: "constant"
      value: 0
    }
    axis: 2
  }
深度学习:识别图片中的电话号码

输出为:batch_size*width(n)*4096。

3. 构建序列,主要是构建LSTM的输入格式,注意LSTM的输入是:T*batch_size*C,T就是时序长度n,C是特征的维度即4096。

4. LSTM的输出为:T*batch_size*lstm_c,lstm_c表示LSTM中特征维度的大小,这里取256。

经过fc-final,全连接为字符的种类数目+1(背景)。比如数字,输出为T*batch_size*11。

5. 最终输入CTC网络,CTC网络会对T时刻的数据做后处理计算与真实label的差异,返回loss。正如上节说的,T的大小应该大于label的长度,因为CTC的后处理会把结果中的空格(背景)和连续相同字符去除(重复识别)去除,如果T<len(label),后处理后得到的字符一定和label不同了,这样就没意义了。

比如T=20,序列为:“1_88_8_1_1_66_10_16_8”,处理后得到“18811610168”。

检测网络

检测网络和训练类似,只是CTCLoss层替换为CTCDecoder即可。

由于新加了一些层的依赖,基于的caffe为:https://github.com/houkai/caffe