标题:Python实现车牌的检测与识别
前言
本次实验基于Python+tessert OCR 实现车牌的检测与识别,需要提前搭建好tessert OCR相关环境。基于思路:色彩空间转换——提取绿色区域——高斯模糊——转为灰度图片——二值化——开闭操作。当然实现方法是多样的。
1.环境
import cv2
import numpy as np
import pytesseract as tess
from PIL import Image
from collections import Counter
from matplotlib import pylot as plt
2.打开图片
#image = cv.imread("OCR_test.png") # 打开要识别的照片,不能有中文路径
image =cv2.imread('testimg.jpg', 1)#读取bgr图像
print(image.shape)#输出粗一下原图的大小
#cv2.imshow('image', image)
#cv2.imshow('imgage',image) #显示图像的代码
#cv2.waitKey() #要加的两行代码
#cv2.destroyAllWindows()
fig = plt.figure(figsize=(10, 5))
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB)), plt.axis('off')
plt.show()
3.色彩空间转换——提取绿色区域——高斯模糊——转为灰度图片——二值化——开闭操作
#image_hsv = cv.cvtColor(image, cv.COLOR_BGR2HSV) #从RGB图像转为hsv色彩空间
image_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
low_hsv = np.array([50, 43, 46]) # 设置颜色
high_hsv = np.array([75, 255, 255])
mask = cv2.inRange(image_hsv, lowerb=low_hsv, upperb=high_hsv) # 选出绿色的区域
fig = plt.figure(figsize=(10, 5))
plt.imshow(mask, 'gray'), plt.axis('off')
plt.show()
这里是色彩空间转换,hsv的最小最大值要视情况而定,定的与车牌颜色越接近越好,否则可能将图片里的其他颜色(在hsv域内)一起被选中,但我们的初衷是只对车牌感兴趣。
接着提取我们感兴趣的绿色区域
image_dst = cv2.bitwise_and(image, image, mask=mask) # 取frame与mask中不为0的相与,在原图中扣出绿色的区域,mask=mask必须有
fig = plt.figure(figsize=(10, 5))
plt.imshow(image_dst, 'gray'), plt.axis('off')
plt.show()
高斯模糊
image_blur = cv2.GaussianBlur(image_dst, (9, 9), 0)#高斯模糊,消除噪声。第二个参数为卷积核大小,越大模糊的越厉害
fig = plt.figure(figsize=(10, 5))
plt.imshow(image_blur, 'gray'), plt.axis('off')
plt.show()
转换成灰度图并且二值化,方便后续操作
image_gray = cv2.cvtColor(image_blur, cv2.COLOR_BGR2GRAY)#转为灰度图像
ret, binary = cv2.threshold(image_gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)#二值化
fig = plt.figure(figsize=(10, 5))
plt.imshow(image_gray, 'gray'), plt.axis('off')
plt.show()
开运算闭运算进一步排除干扰
kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT, (6, 9))#得到一个4*6的卷积核
image_opened = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel1)#开操作,去一些干扰
fig = plt.figure(figsize=(10, 5))
plt.imshow(image_opened, 'gray'), plt.axis('off')
plt.show()
kernel2 = cv2.getStructuringElement(cv2.MORPH_RECT, (49,49 ))#得到一个7*7的卷积核
image_closed = cv2.morphologyEx(image_opened, cv2.MORPH_CLOSE, kernel2)#闭操作,填充一些区域
fig = plt.figure(figsize=(10, 5))
plt.imshow(image_closed, 'gray'), plt.axis('off')
plt.show()
开运算闭运算并不能完美的排除干扰,我们仍需要对残图进一步筛选车牌,根据车牌的物理特征筛选。
license_prepared = image_closed #保存闭运算结果
contours, hierarchy = cv2.findContours(license_prepared, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)#初步筛选
Min_Area=2000
def choose_license_area(contours, Min_Area): #筛选在闭运算结果中符合条件的轮廓点集
temp_contours = []
for contour in contours:
if cv2.contourArea(contour) > Min_Area: # 面积大于MIN_AREA的区域保留
temp_contours.append(contour)
car_plate1 = []
car_plate2 = []
car_plate3 = []
for temp_contour in temp_contours:
rect_tupple = cv2.minAreaRect(temp_contour)
rect_width, rect_height = rect_tupple[1]
if rect_width < rect_height:
rect_width, rect_height = rect_height, rect_width
aspect_ratio = rect_width / rect_height
# 中国:蓝牌和黑牌是440×140,黄牌前牌尺寸同,后牌为440×220;摩托车及轻便摩托车前牌是220×95,后牌是220×140。
# 车牌正常情况下宽高比在2 - 3.15之间 稍微放宽点范围
if aspect_ratio > 1.5 and aspect_ratio < 4.65:
car_plate1.append(temp_contour)
rect_vertices = cv2.boxPoints(rect_tupple)
rect_vertices = np.int0(rect_vertices)
# print(temp_contour)
print('一次筛查后,符合比例的矩形有' + str(len(car_plate1)) + '个')
# 二次筛查 如果符合尺寸的矩形大于1,则缩小宽高比
if len(car_plate1) > 1:
for temp_contour in car_plate1:
rect_tupple = cv2.minAreaRect(temp_contour)
rect_width, rect_height = rect_tupple[1]
if rect_width < rect_height:
rect_width, rect_height = rect_height, rect_width
aspect_ratio = rect_width / rect_height
# 中国:蓝牌和黑牌是440×140,黄牌前牌尺寸同,后牌为440×220;摩托车及轻便摩托车前牌是220×95,后牌是220×140。
# 车牌正常情况下宽高比在2 - 3.15之间 稍微放宽点范围
if aspect_ratio > 1.6 and aspect_ratio < 4.15:
car_plate2.append(temp_contour)
rect_vertices = cv2.boxPoints(rect_tupple)
rect_vertices = np.int0(rect_vertices)
print('二次筛查后,符合比例的矩形还有' + str(len(car_plate2)) + '个')
# 三次筛查 如果符合尺寸的矩形大于1,则缩小宽高比
if len(car_plate2) > 1:
for temp_contour in car_plate2:
rect_tupple = cv2.minAreaRect(temp_contour)
rect_width, rect_height = rect_tupple[1]
if rect_width < rect_height:
rect_width, rect_height = rect_height, rect_width
aspect_ratio = rect_width / rect_height
# 中国:蓝牌和黑牌是440×140,黄牌前牌尺寸同,后牌为440×220;摩托车及轻便摩托车前牌是220×95,后牌是220×140。
# 车牌正常情况下宽高比在2 - 3.15之间 稍微放宽点范围
if aspect_ratio > 1.8 and aspect_ratio < 3.35:
car_plate3.append(temp_contour)
rect_vertices = cv2.boxPoints(rect_tupple)
rect_vertices = np.int0(rect_vertices)
print('三次筛查后,符合比例的矩形还有' + str(len(car_plate3)) + '个')
if len(car_plate3) > 0:
return car_plate3
if len(car_plate2) > 0:
return car_plate2
return car_plate1
我们将筛选得到的车牌坐标点集存放记录
license_area = choose_license_area(contours, Min_Area)
for i in license_area:
row_min, col_min = np.min(i[:, 0, :], axis=0)
row_max, col_max = np.max(i[:, 0, :], axis=0)
card_img = image[col_min-25:col_max, row_min-25:row_max, :]
cv2.imwrite("card_img.jpg", card_img)
fig = plt.figure(figsize=(10, 10))
plt.imshow(card_img, 'gray'), plt.axis('off')
plt.show()#将检测到的车牌显示出来
得到的车牌有可能存在本身的模糊或者图像处理本身的问题导致不够清晰,这时需要膨胀算法修复文字
gray = cv2.cvtColor(card_img, cv2.COLOR_BGR2GRAY)#转为灰度图片
ret, binary = cv2.threshold(gray, 130, 255, cv2.THRESH_BINARY_INV)#二值化
fig = plt.figure(figsize=(10, 10))
plt.imshow(binary, 'gray'), plt.axis('off')
plt.show()#将检测到的车牌显示出来
#bin1 = cv2.resize(binary, (370, 82))#改变一下大小,有助于识别(420,119)
bin1 = cv2.resize(binary, (420,119))
#kernel1 = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 5))#获取一个卷积核
dim = 3
kernel3 = np.ones((dim, dim), np.uint8)
dilated = cv2.dilate(bin1, kernel3,iterations=1) # 白色区域膨胀
fig = plt.figure(figsize=(10, 10))
plt.imshow(dilated, 'gray'), plt.axis('off')
plt.show()#将检测到的车牌显示出来
text = tess.image_to_string(dilated, lang='chi_sim')#识别
print('识别的结果为:%s' %text)
至此,完成车牌的识别
4.总结:
如前面所言,因思路因人而异,实现的方法是多种多样的。但每个思路都有其不够完善的地方。例如在hsv空间筛选车牌的步骤中,本人是以个人计算去猜测得出的值,不够准确。以及在开运算闭运算中,卷积核大小的选择很重要,要依据实例情况选择不同的核。另外,对于过度倾斜的车牌,可能不能很好地识别(本文用例给的是比较正向的车牌),这时用上角度矫正的话会使代码逻辑更完美