一、WHAT


论文下载地址:License Plate Detection and Recognition in Unconstrained Scenarios [pdf]
github 的项目地址:alpr-unconstrained

数据集: http://www.inf.ufrgs.br/~crjung/alpr-datasets.

工程主页:alpr-datasets

视频效果: Demi Lovato Rock in Riio Lisboa 2018

本文选自ECCV2018的论文《License Plate Detection and Recognition in Unconstrained Scenarios  ( 复杂| 无约束 场景下的车牌检测和识别)》。该论文不进给出了一套完整的车配识别系统( Automatic License Plate Recognition system,ALPR system)的解决方案,而且提供了在无约束(Unconstrained Scenarios)场景下的识别算法, 很好的解决了实际生活中的车牌识别问题。


1. 引入新颖的CNN,可以检测和纠正单一图像中的多个失真的车牌,将纠正后的车牌图片送入ocr方法中,得到最后的车牌文字

2. 对来自不同区域的车牌进行了人工标注:使用一个由真是数据和人工生成的数据组成的车牌进行训练, 并使用与目标区域相似的字体类重新训练ocr网络

3.扩展了现有的OCR方法,针对巴西车牌进行开发。

结果: 不仅仅在巴西车牌上的准确率很高,在欧洲和台湾车牌生也获得了很高的召回率和准确率

PS: 台湾车牌和欧洲车牌是由数组和字母构成的,其实和巴西车牌的构成是一样的,唯一所有不同的是车牌图像的比例可能会有所不同。但这种图像预处理的时候统一设置图片就好了



二、WHY


常见的商业软件大多是在场景受限的情况下对车牌进行识别的,例如停车场、收费站的监控,这些场景都很固定,摄像的角度和车牌 但是对于宽松的场景,比如执法人员手中的相机或者是智能机拍下的图片,照片的倾斜度可能会很高,对于这些商业软件而言,识别起来就很困难。

因此,本论文提出一个完整的ALPR系统,可以在多变的场景下表现良好。

总的来说就是: 目前很多ALPR系统对正面拍摄车牌照片识别效果良好,但是在多变的角度和场景(光线)下车牌往往是倾斜的,导致识别的效果并不如意。因此,本论文提出一个完整的ALPR系统,可以在多变的场景下表现良好。


数据集中存在的倾斜车牌


基于此,这篇文章提出了一种完整的ALPR系统,可以在各种场景下具有良好的表现。在这篇文章的贡献主要有两个:

1. 介绍了一种新颖的神经网络,能够检测出不同场景环境下的车牌;并在OCR之前对变形的进行校正

2. 利用真实数据合成多样的车牌,用于训练。手动标注的样本了少于200个,并且是从头开始训练网络


三、HOW(实现方法)


ALPR 的主要任务是 在图像中找到并识别车牌(license plates );通常情况下会分为4个子任务序列:


    1. 车辆检测( vehicle detection)
    2. 车牌检测(  license plate detection)
    3. 字符分割( character segmentation)
    4. 字符识别( character recognition)


而子任务3和4可以看做OCR;  所以一共有3个子任务:vehicle detection, license plate detection, OCR.  这篇文章也是按照这三个子任务的顺序进行展开的,如下图所示

具体的说就是:基于YOLOv3 的车辆识别 --> 车牌的检测和校正--> 车牌的OCR识别:



示例流程

问题1: 为什么要先检测出车辆,再检测出车牌?而不是直接检测车牌呢?

根据经验,较大的输入图像上是可以检测较小的物体,但是这会增加了计算量。车牌属于小目标的检测,通常情况下在有车的图片上,往往会伴随着车牌,(即:有车牌的地方一定能检测出车辆),所以可以先检测车辆。

问题2: 为什么对车牌进行校正?

这是因为在正视图上,车牌的大小和车辆的bounding box 的比例较大;而在斜视图或者侧视图上,车牌大小和车辆的比例较小。因此,需要将倾斜的视图调整到正面的视图上,以保持车牌区域的可识别性。 简而言之就是:倾斜视角的车牌面积较小,将车牌变成正视图的视角后,车牌面积会增大,从而增加车牌的识别率。

image

倾斜视角的车牌面积较小,将车牌变成正视图的视角后,车牌面积会增大,从而增加车牌的识别率。



问题3: 车牌的校正是如何实现的?

在车牌校正方面,虽然3D姿态估计是方法可以确定校正的尺寸和调整的尺度,但是本文提出了一种基于车辆bounding box的纵横比(aspect ratio)的一种方法,简单又高效。首先需要设置resizing factor,即当bounding box的aspect ratio接近于1的时候,影响因子会较小;当aspect ratio 增加的时候,影响因子也会增加。

缩放因子的计算方式如下:

(1)中,Wv和 Hv是识别车辆后的图像大小, Dmin和Dmax是常量,分别是288和608


其中:Wv和Hv是车辆的bounding box的宽度和高度。

注意: 由于Dmin ≤ fsc min(Wv, Hv) ≤ Dmax,所以Dmin和Dmax为调整车辆图片的维度大小划定了范围,经过尝试之后最佳的值是Dmin=288; Dmax=608


fsc代码实现如下:

  1 Ivehicle = cv2.imread(img_path) # vehicle bounding box
  2 
  3 ratio = float(max(Ivehicle.shape[:2]))/min(Ivehicle.shape[:2]) # 计算缩放比例
  4 side  = int(ratio*288.)
  5 bound_dim = min(side + (side%(2**4)),608) # fsc
  6 print "ttBound dim: %d, ratio: %f" % (bound_dim,ratio)
  7 
  8 factor = float(max_dim)/min_dim_img
  9 print "t Fsc is %f"%(factor)



缩放因子用于计算后期的图像的宽高:


  1 # wpod_net前期对车辆的bounding box 的归一化
  2 # 详情见:def detect_lp(model,I,max_dim,net_step,out_size,threshold):
  3 def detect_lp(model,I,max_dim,net_step,out_size,threshold):
  4 	'''
  5 	:param model: 车牌检测模型,wpod_net
  6 	:param I: 车辆图片,im2single(Ivehicle),W,H,C
  7 	:param max_dim: bound_dim
  8 	:param net_step:2**4
  9 	:param out_size:(240,80)
 10 	:param threshold:lp_threshold==0.5
 11 	:return:
 12 	'''
 13 
 14 	min_dim_img = min(I.shape[:2])# [W,H,C]-->[W,H]
 15 	factor =.....# 计算的缩放因子
 16 
 17 	w,h = (np.array(I.shape[1::-1],dtype=float)*factor).astype(int).tolist()# [w,h,c]
 18 	w += (w%net_step!=0)*(net_step - w%net_step)
 19 	h += (h%net_step!=0)*(net_step - h%net_step)
 20 	Iresized = cv2.resize(I,(w,h))# 车辆图片做缩放
 21 
 22 	T = Iresized.copy()
 23 	T = T.reshape((1,T.shape[0],T.shape[1],T.shape[2]))
 24 
 25 	start 	= time.time() # 获取时间
 26 	Yr = model.predict(T) # 预测的车牌区域
 27 	Yr = np.squeeze(Yr)
 28 	elapsed = time.time() - start
 29 	....(略)#后期是放射变换,校正车牌的
 30 
 31 


3.1车辆的检测


考虑车辆的检测的需要使用的较少的时间,于是借用了YUOLOv3的模型与darknet框架进行检测的;

使用 YOLOv3的主要原因是:

1 . 检测速度很快(大约是70 FPS ); 

2. 识别的准确率较高,在PASCAL VOC数据集中的测试结果是76.8% mPA. 

在这篇文章中,将YOLOv3直接拿来使用,并没有做性能上的改进,同时忽略了除了cars以外的其他类别;

PS :

Yolov3 相比于Faster RCNN 确实很快,但是也有致命的缺陷: 不能识别出特别小的物体。如果在实际应用中需要在一张很大的图像上识别很小的车,建议重新训练

另外,YOLOv2 中具有车牌的物体不仅仅是cars, 还有bus和truck;这两个类别在程序中确被忽略的。


使用YOLOv2 用于车辆检测:本文没有对YOLO进行更改和改进,只是将网络该网络作为车辆检测的工具,将汽车和公交车这两类进行合并忽略了yolo里面的其他类别

将检测出的车辆图片裁剪并且进行调整,作为车牌识别的输入。

测出的正样本(车辆),将会被resized ,送入车牌检测模块。resize 因子已经在上一节给出,通过这种缩放计算以后,包含有车辆的图片会被统一成大小为 288*608大小的图片;


3.2 车牌的检测和校正(License Plate Detection and Unwarping)

车牌本质上是附加在车上的矩形平面物体。为了充分的利用车牌的形状优势,本文提出了一种 扭曲平面检测网络(Warped Planar Object Detection Network., WPOD NET)。该网络可以学习识别不同扭曲程度的车牌,并学习回归仿射变换的系数,将就扭曲的车牌unwarps成正面视图下的矩形形状。WOPD的检测流程如下图所示。

经过WPOD网络之后是一个8通道的特征图(图片中显示的是6通道,应该是8)

在特征图上提取扭曲的车牌,首先会设置固定大小的单元(m, n)

如果该单元的目标概率大于给定的阈值(threshold),那么部分回归参数用于构建放射变换矩阵,将虚拟正方形区域转换成车牌的区域

该网络的结构总共21层卷积,其中14层是内部残差块(ResBlock);所有的卷积核大小均为固定大小的3*3;除了识别块(Detection)以外,其他的激励函数全部是ReLu;包含有4个2*2大小的max pooling,stride=2,这样可以使得输入的维度减少了16倍(2*2*4);最后在识别模块有两个并行的卷积层

具体结构如下图(fig4)所示,左侧是网络的整体结构,右侧是ResBlock和Detection块的结构。


可以看出来,在检测出车牌以后,还会对倾斜的车牌进行校正;现在需要关注的部分有两点:
1. WPOD是如何设计的?
2. 如何进行车牌校正的?

3. 为什么会在最后一层使用两个并行的卷积层?


检测车牌的整个流程

问题1. WPOD是如何设计的?

好,回答第一个问题: WPOD的设计:

网络结构如下,可以看出:整个网络是 Conv( 包含res-block ) +Maxpooling +DETECTION构成的; 一共有21个卷积层,其中14个是residual blocks,所有的卷积层的filter/kernal大小都是3*3;并且每个Conv操作都会使用到ReLu; 另外有4个Maxpooling的,kernel=2*2, stride=2,


DETECTION上是两个并行的网络(粉红色的区域); 第一个是使用softmax函数去推断概率; 第二个是回归仿射参数,而不需要激活,使用恒等式F(X)=x作为激活函数

WPOD net 结构详情

  1 # 为了书写方便,将conv+bn+relue封装在一个函数里面
  2 def conv_batch(_input,fsz,csz,activation='relu',padding='same',strides=(1,1)):
  3 	output = Conv2D(fsz, csz, activation='linear', padding=padding, strides=strides)(_input)
  4 	output = BatchNormalization()(output)
  5 	output = Activation(activation)(output)
  6 ## 最后两层的书写方式:
  7 def end_block(x):
  8 	xprobs    = Conv2D(2, 3, activation='softmax', padding='same')(x)
  9 	xbbox     = Conv2D(6, 3, activation='linear' , padding='same')(x)
 10 	return Concatenate(3)([xprobs,xbbox])
 11 
 12 ## model的构建
 13 def create_model_eccv():
 14 
 15 	input_layer = Input(shape=(None,None,3),name='input')
 16 
 17 	x = conv_batch(input_layer, 16, 3)
 18 	x = conv_batch(x, 16, 3)
 19 	x = MaxPooling2D(pool_size=(2,2))(x)
 20 	x = conv_batch(x, 32, 3)
 21 	x = res_block(x, 32)
 22 	x = MaxPooling2D(pool_size=(2,2))(x)
 23 	x = conv_batch(x, 64, 3)
 24 	x = res_block(x,64)
 25 	x = res_block(x,64)
 26 	x = MaxPooling2D(pool_size=(2,2))(x)
 27 	x = conv_batch(x, 64, 3)
 28 	x = res_block(x,64)
 29 	x = res_block(x,64)
 30 	x = MaxPooling2D(pool_size=(2,2))(x)
 31 	x = conv_batch(x, 128, 3)
 32 	x = res_block(x,128)
 33 	x = res_block(x,128)
 34 	x = res_block(x,128)
 35 	x = res_block(x,128)
 36 
 37 	x = end_block(x)
 38 
 39 	return Model(inputs=input_layer,outputs=x)
 40 ## 


res_block的实现:

  1 def res_block(x,sz,filter_sz=3,in_conv_size=1):
  2 	xi  = x
  3 	for i in range(in_conv_size):
  4 		xi  = Conv2D(sz, filter_sz, activation='linear', padding='same')(xi)
  5 		xi  = BatchNormalization()(xi)
  6 		xi  = Activation('relu')(xi)
  7 	xi  = Conv2D(sz, filter_sz, activation='linear', padding='same')(xi)
  8 	xi  = BatchNormalization()(xi)# xi 的通道和x的通道是一样的
  9 	xi  = Add()([xi,x]) ## 这里是矩阵的加法
 10 	xi  = Activation('relu')(xi)
 11 	return xi





问题2. 车牌区域是如何校正的?


如下图所示,WPOD Net本身是没有纠正车牌的能力;

作者是构建仿射矩阵,将虚拟方框(白色的方框)转换为lp区域。 WPOD net会输出一个8-channel的feature map,以及编码对象的概率和放射变换的参数。为了能够获取扭曲的车牌,首先在box的中心设置一个固定大小的虚框。如果以(m,n)单元为中心的虚框的目标概率大于检测的阈值,说明白色方框覆盖的区域里 有车牌,同时为扭曲的车牌进行仿射变换。


image


  1 # inference 
  2 Ivehicle = cv2.imread(img_path) # vehicle bounding box; 读取读取车辆图片(根据bounding box裁剪)
  3 factor= ...# 计算缩放因子
  4 w,h = ... # 根据缩放因子和车辆图像计算w和h
  5 Iresized = cv2.resize(I,(w,h))# 车辆图片做缩放
  6 
  7 T = T.reshape((1,T.shape[0],T.shape[1],T.shape[2]))
  8 Yr= model.predict(T) # 预测的车牌区域
  9 # reconstruct
 10 Yr=Y
 11 Probs = Y[...,0] # 模型的预测结果的概率
 12 Affines = Y[...,2:] # 仿射变换的
 13 xx,yy = np.where(Probs>threshold)#threshold=0.5
 14 
 15 base = lambda vx,vy: np.matrix([[-vx,-vy,1.],[vx,-vy,1.],[vx,vy,1.],[-vx,vy,1.]]).T # 构建放射矩阵
 16 
 17 for i in range(len(xx)):
 18 	y,x = xx[i],yy[i]
 19 	affine = Affines[y,x]
 20 	prob = Probs[y,x]
 21 
 22 	mn = np.array([float(x) + .5,float(y) + .5])
 23 
 24 	A = np.reshape(affine,(2,3))
 25 	A[0,0] = max(A[0,0],0.)
 26 	A[1,1] = max(A[1,1],0.)
 27 
 28 	pts = np.array(A*base(vxx,vyy)) #*alpha
 29 	pts_MN_center_mn = pts*side
 30 	pts_MN = pts_MN_center_mn + mn.reshape((2,1))
 31 
 32 	pts_prop = pts_MN/MN.reshape((2,1))
 33 
 34 	labels.append(DLabel(0,pts_prop,prob))
 35 
 36 ... #仿射变换end
 37 

对扭曲车牌检测的效果图:


问题3. 为什么会在最后一层使用两个并行的卷积层?

到现在为止,我们还没有回答这个网络的最关键的问题:文中提到的warp net放射变换参数是从哪里得到的?

网络最后的并行卷积层有着不同的使命

第一个卷积模块是:用于计算物体的概率的,使用了softmax作为激励函数。

第二个卷积模块是:用于获取仿射参数的,没有使用激励函数

两个卷积学习的目标是根据Loss function 而定的,因此还需要知道loss function 是如何实现的。

image

第一个卷积模块是:用于计算物体的概率的,使用了softmax作为激励函数。

第二个卷积模块是:获取仿射参数的,没有使用激励函数


image

Loss Function 的设计:


假设boldsymbol{P_{i}=left [ x_{i} ,y_{y}right ]}




3.3 车牌的OCR识别


这部分字符分割和识别是使用改进的YOLO模型。

所使用的改进方法和网络架构和这篇文章一样:Silva, S.M., Jung, C.R.: Real-time brazilian license plate detection and recognition using deep convolutional neural networks. In: 2017 30th SIBGRAPI Conference on Graphics, Patterns and Images (SIBGRAPI). pp. 55–62 (Oct 2017). https://doi.org/10.1109/SIBGRAPI.2017.14

数据集是通过使用合成扩增数据来处理世界各地不同区域的LP特征,在该工作中大大地扩大了训练数据集。 人工创建的数据包括将7个字符的字符串粘贴到纹理背景上,然后执行随机转换,如旋转、平移、噪声和模糊。

车牌数据集的制作流程如下图所示:

image

制作好的数据如下入所示:

image


这种综合数据的使用极大地提高了网络的泛化能力,使得完全相同的网络对世界不同地区的LPs都有很好的应用效果。


这部分涉及OCR  , 后期会专门设置一个专题阐述OCR的识别算法


四、总结

这篇论文的实用性应该较高;但就创新性而言,并没有太多改进。值得一提的是对扭曲的车牌进行了仿射变换,使得车牌可以恢复正常矩形的形状 。


敬请期待, by lizhen 2019.05

内容来源于网络如有侵权请私信删除
你还没有登录,请先登录注册
  • 还没有人评论,欢迎说说您的想法!