2021SC@SDUSC
根据小组分配到的任务,我将负责HyperLPRLite.py中SimpleRecognizePlateByE2E(self,image)函数的分析。
该函数的代码如下:
def SimpleRecognizePlateByE2E(self,image):
images = self.detectPlateRough(image,image.shape[0],top_bottom_padding_rate=0.1)
res_set = []
for j,plate in enumerate(images):
plate, rect =plate
image_rgb,rect_refine = self.finemappingVertical(plate,rect)
res,confidence = self.recognizeOne(image_rgb)
res_set.append([res,confidence,rect_refine])
return res_set
SimpleRecognizePlateByE2E(self,image)函数是对车牌号进行识别的核心函数,它的算法思路是这样的:
(1)、对导入的车牌文件(也就是图片)进行车牌粗定位,目的是找出图中所有的车牌,这部分是通过delectPlateRough函数实现的。
(2)、找出所有车牌后,对图中所有车牌进行精定位,也就是沿着这些车牌的轮廓,将车牌裁剪下来,方便接下来对车牌内容进行识别,这项功能是通过finemappingVertical函数实现的。
(3)、最后要对车牌进行文字识别,通过recognizeOne函数实现
images = self.detectPlateRough(image,image.shape[0],top_bottom_padding_rate=0.1)
这条语句的作用就是将图片进行车牌粗定位,找到图中所有车牌的大致位置。image.shape[]是Python内置的数组,其定义如下:
image.shape[0] 图片高
image.shape[1] 图片长(水平长度)
image.shape[2] 图片通道数。(灰度图的通道数为1,因为描述灰度图中的每个像素点只需要一个数值,而彩色图通道数为3,因为描述每个像素点都需要RGB三个值。)
top_bottom_padding_rate用来描述要进行粗定位的图片,其上下部分占整体图片的比例,这里直接默认为0.1
下面是detectPlateRough函数的代码:
def detectPlateRough(self,image_gray,resize_h = 720,en_scale =1.08 ,top_bottom_padding_rate = 0.05):
if top_bottom_padding_rate>0.2:
print("error:top_bottom_padding_rate > 0.2:",top_bottom_padding_rate)
exit(1)
height = image_gray.shape[0]
padding = int(height*top_bottom_padding_rate)
scale = image_gray.shape[1]/float(image_gray.shape[0])
image = cv2.resize(image_gray, (int(scale*resize_h), resize_h))
image_color_cropped = image[padding:resize_h-padding,0:image_gray.shape[1]]
image_gray = cv2.cvtColor(image_color_cropped,cv2.COLOR_RGB2GRAY)
watches = self.watch_cascade.detectMultiScale(image_gray, en_scale, 2, minSize=(36, 9),maxSize=(36*40, 9*40))
cropped_images = []
for (x, y, w, h) in watches:
x -= w * 0.14
w += w * 0.28
y -= h * 0.15
h += h * 0.3
cropped = self.cropImage(image_color_cropped, (int(x), int(y), int(w), int(h)))
cropped_images.append([cropped,[x, y+padding, w, h]])
return cropped_images
(1)、首先是进行异常检测,如果top_bottom_padding_rate>0.2,会直接报错并且退出。
if top_bottom_padding_rate>0.2:
print("error:top_bottom_padding_rate > 0.2:",top_bottom_padding_rate)
exit(1)
(2)、接着定义了三个变量。height定义为输入图片的高度,padding定义为图片高度乘top_bottom_padding_rate,显然就是图片上下部分所占实际高度,scale是定义的一个比例系数,即图片的长除以图片的高
height = image_gray.shape[0]
padding = int(height*top_bottom_padding_rate)
scale = image_gray.shape[1]/float(image_gray.shape[0])
(3)、接着对图片进行了缩放,resize函数用于对图片进行缩放。缩放后图片的高统一为720(在函数最开头已经将resize_h设为720)
image = cv2.resize(image_gray, (int(scale*resize_h), resize_h))
(4)、对图片进行裁剪,在高度这一维度上,将之前判定为上下边缘的部分裁剪掉,在长度这一维度上不进行裁剪,全部保留。padding是之前根据上下边缘占比乘以图片高度,算出来的上下边缘的实际高度,只选取图片从上边缘padding到下边缘resize_h-padding的部分,剩下的上下边缘予以舍弃。裁剪后的图片命名为image_color_cropped。
介绍一下裁剪的实现原理。图片是以像素点形式,通过二维数组存储的,第一个维度代表图片的垂直高度,第二个维度代表图片的水平长度。“:”表示对一个维度的数组进行切片,m:n表示对这个数组从m开始到n的部分进行切片,即只保留m到n的部分,舍弃剩下的部分。在该代码段,就是对高度这个维度,只保留从padding到resize_h-padding,也就是从上边缘到下边缘,在长度这个维度,则是从0保留到image_gray.shape[1],在前面已经提到过,这个数值表示图片的长度,也就是长度这个维度是全部保留的。将图片的两个二维数组进行切片后保存在image_color_cropped这个新图片里,这个新图片就是对原图片的裁剪。
image_color_cropped = image[padding:resize_h-padding,0:image_gray.shape[1]]
(5)、对图片进行格式转换,将刚才裁剪后的图片转换成灰度图片。cvtColor是cv2的颜色空间转换函数,用法是cvtColor(p1,p2),p1是要转换的图片,p2是要转换成的格式。cv2.RGB2GRAY表示把RGB图片转换成灰度图。
image_gray = cv2.cvtColor(image_color_cropped,cv2.COLOR_RGB2GRAY)
(6)、接着就用级联分类器进行模式识别。detectMultiScale是cv2中进行模式识别的函数,它能够对输入的图片进行识别,识别出所有待识别模式(在这里是车牌)的位置坐标和大小,将其用矩vector表示并返回,存储在watches
watches = self.watch_cascade.detectMultiScale(image_gray, en_scale, 2, minSize=(36, 9),maxSize=(36*40, 9*40))
(7)、最后一步就是对刚才模式识别出的结果进行处理。watches中存储的x,y,w,h,分别代表识别出的车牌的横坐标、纵坐标、水平宽度、竖直高度。对刚才识别出的每个车牌都进行处理,分别修改它们的横坐标、纵坐标,并扩大它们的宽度和高度,这样就扩大了识别出车牌的边框。最后用cropImage函数对它们进行裁剪,裁剪下来的就是粗识别出的车牌的边框。
for (x, y, w, h) in watches:
x -= w * 0.14
w += w * 0.28
y -= h * 0.15
h += h * 0.3
cropped = self.cropImage(image_color_cropped, (int(x), int(y), int(w), int(h)))
cropped_images.append([cropped,[x, y+padding, w, h]])
return cropped_images
在下周的分析中,我将对finemappingVertical函数进行分析。