汽车车牌识别系统实现(二)-- 车牌定位

之前对这部分内容解释的不够详细,自己都看不下去了,因此重新编辑一下。

一、前言

车牌定位是汽车车牌识别能否取得成功的关键过程,车牌定位是否准确直接影响到后续的车牌字符划分和字符识别算法的准确率。顾名思义,车牌定位就是从采集到的原始图像中准确地定位出车牌区域的过程。在实际车牌识别系统中,由于光线强度不同、原始图像中背景较为复杂等原因,导致在某些特殊情况下准确定位出车牌区域较为困难。本文采用基于车牌彩色信息特征与形态学相结合的车牌定位方法,该方法的优点是定位准确率较高,缺点是要求图像质量好。

二、国内车牌规格和特征

在这里插入图片描述

三、车牌定位相关技术

3.1 车牌图像的预处理

由于车牌识别设备一般都是安装在室外,且汽车车身不可能是完全整洁的、无污垢的,车牌上也可能存在泥点、污渍等杂质,因此采集到的图像中难免会存在一些噪声点。这些看似不起眼的噪声点或多或少的都会影响到定位的准确率。车牌定位不成功,后续的操作就难以进行。因此,在所有实际的汽车车牌识别系统中,在定位开始之前,都会对采集到的原始图像进行图像预处理操作以便去除原始图像中的噪声点。图像预处理是车牌定位算法成功的前提条件,整个预处理的流程如图2-2所示。
在这里插入图片描述
获取采集到的图像之后,第一步需要对图像进行缩放处理。由于车牌采集设备采集到的图像像素点非常密集、数量庞大,这么密集庞大的像素点会严重拖慢车牌定位的速度,所以在车牌定位之前对图像进行缩放处理以减少像素点的数量,这对实时性要求较高的车牌识别系统是非常重要的。接下来要对图像进行高斯平滑和中值滤波以减少采集图像中的噪声点以便提高车牌定位算法的识别率。去除孤立噪声点之后,需要对图像进行灰度化操作,灰度化是指将图片的像素值规定到[0,255]区间上,根据人的眼睛对颜色的敏感程度分配蓝色、绿色和红色的权重,根据式(2-1)对RGB三个分量按照分配的权重进行加权求和可以得到较为准确的灰度图像像素值。
在这里插入图片描述

3.2 数学形态学处理

对输入图像采用彩色信息特征定位完车牌后,定位出的区域中或多或少都会存在疑似车牌的区域,这些区域在定位的过程中也会被保留下来,所以需要用数学形态学操作对图像作进一步的处理以获取正确的车牌区域,目的是将车牌区域各个字符连通和去除图像中的孤立的噪声点。在本文中形态学处理主要用途为消除孤立噪声点和将车牌字符区域填充为一个连通域。因本文只用到了形态学的腐蚀和膨胀处理,所以只对腐蚀和膨胀处理做进一步的说明。
(1)腐蚀操作
该处理是将输入图像的边缘加以腐蚀。具体的操作原理[3]:用一个宽m,高n的矩形作为模板S,对图像X中的每一个像素点x做以下操作:将像素x至于模板S的中心,根据模版的大小,遍历所有被模板覆盖的其它像素,修改像素x的值为所有被模板S覆盖像素点中最小的像素值。用数学公式表示为式(2-2)。
在这里插入图片描述
(2)膨胀操作
膨胀与腐蚀恰恰相反,该处理可疑使图像的轮廓呈现出膨胀的效果。操作方法[3]与腐蚀操作类似,不同的是把像素x的值修改为模板覆盖着的所有像素点中最大的像素值。数学公式表示为式(2-3)。
在这里插入图片描述

四、基于车牌彩色信息特征与形态学相结合的车牌定位方法

考虑到现实生活中黑色车牌和白色车牌比较少见,且绿色新能源车辆目前数量较少,黄底黑字车牌一般是大型卡车或客车悬挂,因此本文只研究小型民用车使用的蓝底白字车牌的定位算法。本文采取的定位方法是基于车牌彩色信息特征和形态学相结合的方法,该算法的大致流程如图2-5所示。
在这里插入图片描述

4.1 车牌初步定位

分离出原始图像的RGB通道之后,查阅资料得知,正常光线强度情况下,车牌蓝色的RGB分量值如表2-1所示。
在这里插入图片描述
但在大多数情况下,采集到的车牌图像是不能保证正常曝光的,因此需要预设一个阈值以保证不在正常曝光下的车牌区域也能被定位到。计算图像的每个像素点的RGB值与正常曝光下的车牌蓝色RGB值之间的差值,若在阈值范围之内,则认定是车牌区域;若超过阈值,则认定不是车牌区域。依据该方法,可以从采集到的输入图像中获取疑似车牌的区域,该区域可能存在多个,许多符合条件的非车牌区域像素点也有可能存在其中,接下来需要用形态学的方法进一步处理定位出的车牌以获取最终的牌照区域。车牌初步定位如图2-6所示。
在这里插入图片描述

4.2 基于数学形态学的车牌图像处理

如果仅仅依靠蓝色的RGB分量值来定位车牌位置的话,由于外界的光线强度孰强孰弱、采集到的图像中也有可能存有蓝色区域等干扰因素,使得定位出的车牌位置可能存在多个,非常难确定正确的车牌区域。所以,需要用数学形态学的相关操作对图像做相应的处理以为后续的筛选过程作准备。首先对图像做膨胀操作,目的是将车牌区域的各个字符连接为一个整体,然后再对输入图像作形态学腐蚀操作,目的是去掉图像中孤立的噪声点。经形态学操作处理输入图像之后,车牌区域会被规划成一个较为规整的倾斜的长方形区域。实验结果前后对比如下图2-7和图2-8所示。
在这里插入图片描述

4.3 车牌的精确定位

经形态学方法处理之后,图中绝大多数孤立的噪声点已被清除,且车牌区域呈现出一个较为规则的矩形。由于数学形态学处理会减少车牌的信息以及车牌区域发生倾斜等原因,所以本文预设车牌的宽高比例会比正常情况下的比例范围要更大,设定宽高比例阈值为2.0-5.0,最小面积为2000。根据车牌的宽高比例以及预设的最小面积等特征,将不符合条件的可疑矩形区域排除掉,剩下的矩形区域就是车牌区域,并在原始图像上用红色边框表示,如图2-9所示。
在这里插入图片描述
Note: 这里只提醒一句,大家一定要分清是白底黑字还是黑底白字,当时我在学的时候就吃了不少亏,这点非常重要!
车牌定位的相关步骤已经介绍完毕,因为自己研究方向是生物医学图像分割方向,之后会陆续更新深度学习的相关知识。车牌识别算法的项目我已上传至资源,有需要的小伙伴可以下载查阅,有不对的地方还请批评指正!!!

五、代码实现

#对图像进行缩放处理
if img_width > MAX_WIDTH:
    resize_rate = MAX_WIDTH / img_width
    img = cv2.resize(img,(MAX_WIDTH,int(img_hight * resize_rate)),interpolation=cv2.INTER_AREA)
# cv_show('img_resize',img)

# 高斯平滑
img_aussian = cv2.GaussianBlur(img,(5,5),1)
# cv_show('img_aussian',img_aussian)

#中值滤波
img_median = cv2.medianBlur(img_aussian,3)
# cv_show('img_median',img_median)
#分离通道
img_B = cv2.split(img_median)[0]
img_G = cv2.split(img_median)[1]
img_R = cv2.split(img_median)[2]
# print(img_B)
# print(img_G)
# print(img_R)
for i in range(img_median.shape[:2][0]):
    for j in range(img_median.shape[:2][1]):
        if abs(img_B[i,j] - Blue) < THRESHOLD and abs(img_G[i,j] - Green) <THRESHOLD and abs(img_R[i,j] - Red) < THRESHOLD:
            img_median[i][j][0] = 255
            img_median[i][j][1] = 255
            img_median[i][j][2] = 255
        else:
            img_median[i][j][0] = 0
            img_median[i][j][1] = 0
            img_median[i][j][2] = 0
cv_show('img_median',img_median)

kernel = np.ones((3,3),np.uint8)
img_dilate = cv2.dilate(img_median,kernel,iterations = 5) #膨胀操作
img_erosion = cv2.erode(img_dilate,kernel,iterations = 5) #腐蚀操作
cv_show('img_erosion',img_erosion)
img1 = cv2.cvtColor(img_erosion,cv2.COLOR_RGB2GRAY)
# cv_show('img1',img1)
image, contours, hierarchy = cv2.findContours(img1,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
# for i in range(len(contours)):
#     print(contours[i])

car_contours = []
for cnt in contours: #TODO:此处需要一个异常处理(因为有可能/0)
    # 框选 生成最小外接矩形 返回值(中心(x,y), (宽,高), 旋转角度)rect[0]:矩形中心点坐标;rect[1]:矩形的高和宽;rect[2]:矩形的旋转角度
    rect = cv2.minAreaRect(cnt)
    # print('宽高:',rect[1])
    area_width, area_height = rect[1]
    #计算最小矩形的面积,初步筛选
    area = rect[1][0] * rect[1][1] #最小矩形的面积
    if area > MIN_AREA:
        # 选择宽大于高的区域
        if area_width < area_height:
            area_width, area_height = area_height, area_width
        wh_ratio = area_width / area_height
        # print('宽高比:',wh_ratio)
        # 要求矩形区域长宽比在2到5.5之间,2到5.5是车牌的长宽比,其余的矩形排除
        if wh_ratio > 2 and wh_ratio < 5.5:
            car_contours.append(rect)  # rect是minAreaRect的返回值,根据minAreaRect的返回值计算矩形的四个点
            box = cv2.boxPoints(rect)  # box里面放的是最小矩形的四个顶点坐标
            box = np.int0(box)  # 取整
            # for i in range(len(box)):
            #     print('最小矩形的四个点坐标:', box[i])
            # 获取四个顶点坐标
            left_point_x = np.min(box[:, 0])
            right_point_x = np.max(box[:, 0])
            top_point_y = np.min(box[:, 1])
            bottom_point_y = np.max(box[:, 1])
            left_point_y = box[:, 1][np.where(box[:, 0] == left_point_x)][0]
            right_point_y = box[:, 1][np.where(box[:, 0] == right_point_x)][0]
            top_point_x = box[:, 0][np.where(box[:, 1] == top_point_y)][0]
            bottom_point_x = box[:, 0][np.where(box[:, 1] == bottom_point_y)][0]
            vertices = np.array([[top_point_x, top_point_y], [bottom_point_x, bottom_point_y], [left_point_x, left_point_y],[right_point_x, right_point_y]])
        oldimg = cv2.drawContours(img, [box], 0, (0, 0, 255), 2)
        # print(car_contours)
        cv_show('oldimg', oldimg)

预处理及车牌定位全部代码

** 环境OK的话,应该复制上就能运行!**

import numpy as np
import cv2
import argparse
from matplotlib import pyplot as plt
Blue = 138
Green = 63
Red = 23
THRESHOLD = 50
ANGLE = -45
MIN_AREA = 2000
LICENSE_WIDTH = 440
LICENSE_HIGH = 140

#绘图展示(后期放入工具类中)
def cv_show(name,img):
    cv2.namedWindow(name,0)
    print(img.shape)
    cv2.imshow(name,img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

MAX_WIDTH = 640
# 设置参数
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--image", required=True,help="path to input image") #image是变量名
args = vars(parser.parse_args())

#读取原图像
img = cv2.imread(args["image"])
cv_show('img',img)
img_hight, img_width = img.shape[:2]

#-------------------------------预处理--------------------------------------
#对图像进行缩放处理
if img_width > MAX_WIDTH:
    resize_rate = MAX_WIDTH / img_width
    img = cv2.resize(img,(MAX_WIDTH,int(img_hight * resize_rate)),interpolation=cv2.INTER_AREA)
# cv_show('img_resize',img)

# 高斯平滑
img_aussian = cv2.GaussianBlur(img,(5,5),1)
# cv_show('img_aussian',img_aussian)

#中值滤波
img_median = cv2.medianBlur(img_aussian,3)
# cv_show('img_median',img_median)
# print('width:',img_median.shape[:2][0])
# print('h',img_median.shape[:2][1])
#------------------------------车牌定位-------------------------------------
#分离通道
img_B = cv2.split(img_median)[0]
img_G = cv2.split(img_median)[1]
img_R = cv2.split(img_median)[2]
# print(img_B)
# print(img_G)
# print(img_R)
for i in range(img_median.shape[:2][0]):
    for j in range(img_median.shape[:2][1]):
        if abs(img_B[i,j] - Blue) < THRESHOLD and abs(img_G[i,j] - Green) <THRESHOLD and abs(img_R[i,j] - Red) < THRESHOLD:
            img_median[i][j][0] = 255
            img_median[i][j][1] = 255
            img_median[i][j][2] = 255
        else:
            img_median[i][j][0] = 0
            img_median[i][j][1] = 0
            img_median[i][j][2] = 0
cv_show('img_median',img_median)

kernel = np.ones((3,3),np.uint8)
img_dilate = cv2.dilate(img_median,kernel,iterations = 5) #膨胀操作
img_erosion = cv2.erode(img_dilate,kernel,iterations = 5) #腐蚀操作
cv_show('img_erosion',img_erosion)
img1 = cv2.cvtColor(img_erosion,cv2.COLOR_RGB2GRAY)
# cv_show('img1',img1)
image, contours, hierarchy = cv2.findContours(img1,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
# for i in range(len(contours)):
#     print(contours[i])

car_contours = []
for cnt in contours: #TODO:此处需要一个异常处理(因为有可能/0)
    # 框选 生成最小外接矩形 返回值(中心(x,y), (宽,高), 旋转角度)rect[0]:矩形中心点坐标;rect[1]:矩形的高和宽;rect[2]:矩形的旋转角度
    rect = cv2.minAreaRect(cnt)
    # print('宽高:',rect[1])
    area_width, area_height = rect[1]
    #计算最小矩形的面积,初步筛选
    area = rect[1][0] * rect[1][1] #最小矩形的面积
    if area > MIN_AREA:
        # 选择宽大于高的区域
        if area_width < area_height:
            area_width, area_height = area_height, area_width
        wh_ratio = area_width / area_height
        # print('宽高比:',wh_ratio)
        # 要求矩形区域长宽比在2到5.5之间,2到5.5是车牌的长宽比,其余的矩形排除
        if wh_ratio > 2 and wh_ratio < 5.5:
            car_contours.append(rect)  # rect是minAreaRect的返回值,根据minAreaRect的返回值计算矩形的四个点
            box = cv2.boxPoints(rect)  # box里面放的是最小矩形的四个顶点坐标
            box = np.int0(box)  # 取整
            # for i in range(len(box)):
            #     print('最小矩形的四个点坐标:', box[i])
            # 获取四个顶点坐标
            left_point_x = np.min(box[:, 0])
            right_point_x = np.max(box[:, 0])
            top_point_y = np.min(box[:, 1])
            bottom_point_y = np.max(box[:, 1])
            left_point_y = box[:, 1][np.where(box[:, 0] == left_point_x)][0]
            right_point_y = box[:, 1][np.where(box[:, 0] == right_point_x)][0]
            top_point_x = box[:, 0][np.where(box[:, 1] == top_point_y)][0]
            bottom_point_x = box[:, 0][np.where(box[:, 1] == bottom_point_y)][0]
            vertices = np.array([[top_point_x, top_point_y], [bottom_point_x, bottom_point_y], [left_point_x, left_point_y],[right_point_x, right_point_y]])
        oldimg = cv2.drawContours(img, [box], 0, (0, 0, 255), 2)
        # print(car_contours)
        cv_show('oldimg', oldimg)

运行结果

实验了几张图像,效果还不错!
1、读取原图像
读取原图像
2、预处理及基于彩色信息定位方法后
预处理及车牌定位后
3、形态学操作后
形态学处理后
4、车牌定位及根据特点筛选后
车牌筛选后
下一篇开始讲图片矫正!

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐