opencv-python车牌识别
本文尚有很多不足的地方,例如车牌字符的定位,车牌种类的不同,复杂环境下车牌识别的问题等等。。。
不足之处请多指教
主要步骤
- 读入图像
- 颜色识别,设定阈值
- 掩模、按位运算
- 图形灰度化、二值化
- 边缘检测
- 开运算、闭运算
- 轮廓识别,输出矩阵
- 判断长宽比
- 找出车牌四角坐标
- 检测字符位置
- 字符切割
- 模板匹配,找出匹配最大值
- 结果输出
子函数介绍
(1)color_change(picture)函数
对输入的图片,现进行阈值的设定,不同颜色的车牌有不同的阈值,一般为蓝色,黄色和绿色。然后通过掩模运算突出二值化车牌的位置,极大的降低了其他背景对识别的干扰。然后在进行按位运算回复车牌原有的颜色,并返回处理过的图像。
(2)binaryzation(value)函数
把color_change(picture)返回的结果转化为和灰度图并进行二值化,然后进行边缘检测,并对检测后的图像进行开运算和开运算,将车牌区域的轮廓连为一个整体。然后通过cv2.findContours()函数找出图像中单独的轮廓并返回。
cv2.findContours()参数说明:img为寻找轮廓的图像,且为二值图(黑白图)。
mode为轮廓的检索模式,有四种:cv2.RETR_EXTERNAL只检测外轮廓。cv2.RETR_LIST检测的轮廓不建立等级关系。cv2.RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。cv2.RETR_TREE建立一个等级树结构的轮廓。
method为轮廓的近似方法。cv2.CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1)) == 1。cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息。
返回值contours表示图像中的所有轮廓的list,np.array类型。
开闭运算输出的轮廓:
函数画出的轮廓:
(3)cut_out(value1,value2)函数
函数接收 binaryzation(value)返回的结果,利用cv2.minAreaRect(contour)函数计算识别轮廓的长宽比,确定车牌的位置。然后利用v2.boxPoints(rect)函数找出车牌四个角的坐标,最后切割车牌。
切割出的车牌:
(4)car_binaryzation_cut(value)函数
将cut_out(value1,value2)函数返回的图像进行二值化,然后获取图像行方向和列方向上字符的分布图,确定字符的分布位置。然后根据位置进行字符的分割。最后将分割后的字符和保存的字符库进行匹配,找出匹配度最高的图像,最后输出匹配结果。
车牌二值化的图像:
字符行列像素分布图:
字符分割后的图像:
(5)show(value,picture)函数
该函数主要用于在原始图像上添加识别后的结果,因为有汉字,所以运用PIL库进行输出。其参数为原始图像和识别后的字符串结果。
主代码
import cv2
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image,ImageDraw,ImageFont
def color_change(picture):
'''
将图片转化到HSV空间,并按位取反,突出车牌区域
'''
hsv=cv2.cvtColor(picture,cv2.COLOR_BGR2HSV) #转化为HSV颜色空间
lower_blue=np.array([100,43,46]) #蓝色阈值
upper_blue=np.array([124,255,255])
mask1=cv2.inRange(hsv,lower_blue,upper_blue) #构建掩模
res1=cv2.bitwise_and(image,image,mask=mask1) #按位运算
cv2.imwrite('111.jpg',res1)
return res1
def binaryzation(value):
'''
把图象进行二值化,并进行开闭运算,最后找到可能存在的区域,并返回区域信息
'''
gray=cv2.cvtColor(value,cv2.COLOR_BGR2GRAY) #转化为灰度图
ret,thresh=cv2.threshold(gray,127,255,cv2.THRESH_BINARY) #图像二值化
canny = cv2.Canny(thresh, 100, 200) #边缘检测
kernel1 = np.ones((12,40), np.uint8)
img_edge1 = cv2.morphologyEx(canny, cv2.MORPH_CLOSE, kernel1) # 闭运算
img_edge2 = cv2.morphologyEx(img_edge1, cv2.MORPH_OPEN, kernel1) # 开运算
cv2.imwrite('222.jpg',img_edge2)
contours ,hierarchy = cv2.findContours(img_edge2,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
#c=cv2.drawContours(image, contours, -1, (0, 255, 0), 3)
return contours
def cut_out(value1,value2):
'''
通过长宽比判断车牌的位置,并截取
'''
for contour in value1:
rect = cv2.minAreaRect(contour) #找出最小外接矩形 中心点、宽和高、角度
if rect[1][1]>rect[1][0]:
k=rect[1][1]/rect[1][0]
else:
k=rect[1][0]/rect[1][1]
if (k>2.5)&(k<5): #判断车牌的轮廓
a=cv2.boxPoints(rect) #获取外接矩形的四个点
box = np.int0(a)
aa=cv2.drawContours(value2, [box], -1, (0, 255, 0), 3) #找出车牌的位置
cv2.imwrite('aa.jpg',aa)
x=[]
y=[]
for i in range(4):
x.append(box[i][1])
y.append(box[i][0])
min_x=min(x)
max_x=max(x)
min_y=min(y)
max_y=max(y)
cut=image[min_x:max_x,min_y:max_y]
cv2.imwrite('333.jpg',cut)
return cut
def car_binaryzation_cut(value):
'''
截取车牌的二值化并切割
'''
# RGB转GARY
change_size= cv2.resize(value, (440, 140))
gray_img = cv2.cvtColor(change_size, cv2.COLOR_BGR2GRAY)
ret,thresh=cv2.threshold(gray_img,127,255,cv2.THRESH_BINARY) #图像二值化
cv2.imwrite('444.jpg', thresh)
x=list(range(440))
y=[]
for i in range(440): #获取列方向上的字符像素分布图
n=0
for j in range(140):
if thresh[j,i]==255:
n=n+1
y.append(n)
plt.subplot(1,2,1)
plt.plot(x,y)
x1=list(range(140))
y1=[]
for i in range(140): #获取行方向上的字符像素分布图
n=0
for j in range(440):
if thresh[i,j]==255:
n=n+1
y1.append(n)
plt.subplot(1,2,2)
plt.plot(x1,y1)
plt.show()
p1=thresh[15:120,12:63]
p2=thresh[15:120,65:117]
p3=thresh[20:120,142:197]
p4=thresh[20:120,200:260]
p5=thresh[20:120,260:317]
p6=thresh[20:120,320:365]
p7=thresh[20:120,370:425]
cv2.imshow('1',p1)
cv2.imshow('2',p2)
cv2.imshow('3',p3)
cv2.imshow('4',p4)
cv2.imshow('5',p5)
cv2.imshow('6',p6)
cv2.imshow('7',p7)
p=[p1,p2,p3,p4,p5,p6,p7]
tem=['0.jpg','5.jpg','6.jpg','7.jpg','8.jpg','9.jpg','B.jpg','C.jpg','E.jpg','J.jpg','K.jpg','M.jpg','Q.jpg','T.jpg','U.jpg','jin.jpg','chuan.jpg','yu.jpg','lu.jpg','su.jpg','ee.jpg']
a=['0','5','6','7','8','9','B','C','E','J','K','M','Q','T','U','晋','川','豫','鲁','苏','鄂']
ss=[]
for i in range(7): #模板匹配
s=[]
for j in range(len(tem)):
image1=cv2.imread(tem[j],0) #读入图像
__,aaa=cv2.threshold(image1,127,255,cv2.THRESH_BINARY)
bbb= cv2.resize(aaa, (50, 100))
result=cv2.matchTemplate(p[i],bbb,cv2.TM_CCOEFF_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
s.append(max_val)
ss.append(a[s.index(max(s))])
car=ss[0]+ss[1]+'.'+ss[2]+ss[3]+ss[4]+ss[5]+ss[6]
print(car)
return car
def show(value,picture):
cv2img = cv2.cvtColor(picture, cv2.COLOR_BGR2RGB) # cv2和PIL中颜色的hex码的储存顺序不同
pilimg = Image.fromarray(cv2img)
# PIL图片上打印汉字
draw = ImageDraw.Draw(pilimg) # 图片上打印
font = ImageFont.truetype("simhei.ttf", 40, encoding="utf-8") # 参数1:字体文件路径,参数2:字体大小
draw.text((200, 200), value, (255, 0, 0), font=font) # 参数1:打印坐标,参数2:文本,参数3:字体颜色,参数4:字体
# PIL图片转cv2 图片
cv2charimg = cv2.cvtColor(np.array(pilimg), cv2.COLOR_RGB2BGR)
cv2.imshow("result", cv2charimg)
cv2.imwrite('555.jpg',cv2charimg)
if __name__=='__main__':
image=cv2.imread('bbb.jpg') #读入图像
image = cv2.resize(image, (570, 430))
res=color_change(image) #转化到HSV,并大致取出车牌位置
a=binaryzation(res) #对颜色识别过的区域进行二值化,并进行开闭运算识别轮廓
b=cut_out(a,image) #找到符合条件的区域,并进行切割
c=car_binaryzation_cut(b)
show(c,image)
while(1):
k=cv2.waitKey(5)&0xFF
if k==27:
cv2.destroyAllWindows()
break