基于python+Opencv的车牌识别
Posted Mr.Hu_is_right_here
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于python+Opencv的车牌识别相关的知识,希望对你有一定的参考价值。
车牌识别包括车牌检测(通过图像分割、特征提取获得车牌位置)+车牌识别(对检测到的车牌进行字符内容识别)。
一、基本流程如下:
1.车牌检测
1)读取需要进行车牌识别的图片;
2)对图像进行灰度化处理(高斯模糊可选择是否进行)和灰度拉伸;
3)进行开运算,消除图像中的噪声;
4)将灰度拉伸后的图像和开运算后的图像求差,并输出其绝对值;
5)将图像二值化,并利用Canny边缘算法提取图像中边缘轮廓;
6)进行闭运算操作,获得小连通域;
7)进行两次开运算操作,获得大连通域;
8)利用车牌长宽比筛选可能属于车牌区域的框,在原图中绘制矩形 。
2.车牌字符识别
1)对车牌ROI图像进行灰度化处理;
2)利用形态学运算中的闭运算消除灰度图像噪声点;
3)利用百度飞桨OCR识别车牌字符与位置;
4)将结果打印并在图片上显示出来。
二、实际代码测试:
1.输入图像:
2.输出图像:
三、注意事项
1.目前代码进行过单个车牌检测,未对多个车牌进行检测;
2.paddleOCR下载和配置,且容易出错,实际使用中速度较慢,可考虑使用EASYOCR作为代替。
附上代码
import cv2
from matplotlib import pyplot as plt
import os
import numpy as np
from paddleocr import PaddleOCR, draw_ocr
from PIL import Image, ImageDraw, ImageFont
#利用paddelOCR进行文字扫描,并输出结果
def text_scan(img_path):
ocr = PaddleOCR(use_angle_cls=True, use_gpu=False)
#img_path = r'test image/license_plate1.jpg'
result = ocr.ocr(img_path, cls=True)
for line in result:
#print(line)
return result
#在图片中写入将车牌信息
def infor_write(img,rect,result):
text=result[1][0]
cv2img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # cv2和PIL中颜色的hex码的储存顺序不同
pilimg = Image.fromarray(cv2img)
#PIL图片上打印汉字
draw = ImageDraw.Draw(pilimg) # 图片上打印
font = ImageFont.truetype("simhei.ttf",20, encoding="utf-8") # 参数1:字体文件路径,参数2:字体大小
draw.text((rect[2], rect[1]), str(text), (0,255,0), font=font) # 参数1:打印坐标,参数2:文本,参数3:字体颜色,参数4:字体
#PIL图片转cv2 图片
cv2charimg = cv2.cvtColor(np.array(pilimg), cv2.COLOR_RGB2BGR)
return cv2charimg
#图像去噪灰度处理
def gray_guss(img):
img=cv2.GaussianBlur(img,(1,1),0)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
return gray
#图像尺寸变换
def img_resize(img):
a=400*img.shape[0]/img.shape[1]
a=int(a)
img=cv2.resize(img,(400,a))
return img
#Sobel检测,x方向上的边缘检测(增强边缘信息)
def Sobel_detec(img):
Sobel_x = cv2.Sobel(img, cv2.CV_16S, 1, 0)
absX = cv2.convertScaleAbs(Sobel_x)
return absX
#寻找某区域最大外接矩形框4点坐标
def find_retangle(contour):
y,x=[],[]
for p in contour:
y.append(p[0][0])
x.append(p[0][1])
return [min(y),min(x),max(y),max(x)]
#寻找并定位车牌轮廓位置
def locate_license(img):
blocks=[]
contours,hierarchy=
cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
x,y,w,h=cv2.boundingRect(c)
r=find_retangle(c)
a=(r[2]-r[0])*(r[3]-r[1])#r=[min(y),min(x),max(y),max(x)]
s=(r[2]-r[0])/(r[3]-r[1])
#根据轮廓形状特点,确定车牌的轮廓位置并截取图像
if (w> (h * 3)) and (w < (h * 5)):
# img=oriimg[y:y+h,x:x+w]
# cv2.rectangle(oriimg, (x, y), (x+w, y+h), (0, 255, 0), 2)
blocks.append([r, a, s])
# 选出面积最大的3个区域
blocks = sorted(blocks, key=lambda b: b[1])[-3:] # 按照blocks第3个元素大小进行排序
# 使用颜色识别判断出最像车牌的区域
maxweight, maxindex = 0, -1
# 划分ROI区域
for i in range(len(blocks)):
b = oriimg[blocks[i][0][1]:blocks[i][0][3], blocks[i][0][0]:blocks[i][0][2]]
# RGB转HSV
hsv = cv2.cvtColor(b, cv2.COLOR_BGR2HSV)
# 蓝色车牌范围
lower = np.array([100, 50, 50])
upper = np.array([140, 255, 255])
# 根据阈值构建掩模
mask = cv2.inRange(hsv, lower, upper)
# 统计权值
w1 = 0
for m in mask:
w1 += m / 255
w2 = 0
for w in w1:
w2 += w
# 选出最大权值的区域
if w2 > maxweight:
maxindex = i
maxweight = w2
# print(blocks[maxindex][0])
return blocks[maxindex][0]#blocks[maxindex][0]即为车牌轮廓位置理想外轮廓
#图像预处理+车牌轮廓位置检测
def fine_lisecenpts(img):
# 图像去噪灰度处理
guss = gray_guss(img)
# Sobel检测,增强边缘信息
sobel = Sobel_detec(guss)
# 图像阈值化操作——获得二值化图
ret, threshold = cv2.threshold(sobel, 0, 255, cv2.THRESH_OTSU)
# # 对二值化图像进行边缘检测(可选,通过边缘检测后,最终进行形态学运算得到的轮廓面积更大)
# threshold=cv2.Canny(threshold,threshold.shape[0],threshold.shape[1])
#形态学运算(从图像中提取对表达和描绘区域形状有意义的图像分量)——闭操作
kernelX = cv2.getStructuringElement(cv2.MORPH_RECT, (30, 10))
closing = cv2.morphologyEx(threshold, cv2.MORPH_CLOSE, kernelX, iterations=1)
# 腐蚀(erode)和膨胀(dilate)
kernelX=cv2.getStructuringElement(cv2.MORPH_RECT,(50,1))
kernelY=cv2.getStructuringElement(cv2.MORPH_RECT,(1,20))
#x方向上进行闭操作(抑制暗细节)
img=cv2.dilate(closing,kernelX)
img=cv2.erode(img,kernelX)
#y方向上进行开操作
img=cv2.erode(img,kernelY)
img=cv2.dilate(img,kernelY)
#进行中值滤波去噪
Blur=cv2.medianBlur(img,15)
#寻找轮廓
rect=locate_license(Blur)
return rect,Blur
#车牌字符识别
def seg_char(rect_list,img):
img=oriimg[rect_list[1]:rect_list[3], rect_list[0]:rect_list[2]]
# 图像去噪灰度处理
gray=gray_guss(img)
# 图像阈值化操作-获得二值化图(可选)
#ret,charimage=cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)
#图像进行闭运算
k1 = np.ones((1, 1), np.uint8)
close = cv2.morphologyEx(gray, cv2.MORPH_CLOSE, k1)
cv2.imshow('close', close)
cv2.imwrite('test image/Char_img.jpg',close)
cv2.waitKey()
res=text_scan(r'test image/Char_img.jpg')
return res
#主函数区
if __name__ == '__main__':
img=cv2.imread('test image/license_plate1.jpg')
# 改变图像尺寸
img=img_resize(img)
oriimg=img.copy()
#寻找到车牌外轮廓矩形坐标
rect, img=fine_lisecenpts(img)
#利用车牌轮廓坐标划分ROI区域用于字符识别,利用OCR识别车牌字符并返回字符串内容
result=seg_char(rect,oriimg)
#循环读取车牌字符串并写入到图片中
for list in result:
oriimg=infor_write(oriimg, rect, list)
cv2.rectangle(oriimg, (rect[0], rect[1]), (rect[2], rect[3]), (0, 255, 0), 2)
cv2.imshow('oriimg',oriimg)
cv2.waitKey()
OpenCV+Python识别车牌和字符分割
本篇文章主要基于python语言和OpenCV库(cv2)进行车牌区域识别和字符分割,开篇之前针对在python中安装opencv的环境这里不做介绍,可以自行安装配置!
车牌号检测需要大致分为四个部分:
- 1.车辆图像获取
- 2.车牌定位、
- 3.车牌字符分割
- 4.车牌字符识别
具体介绍
车牌定位需要用到的是图片二值化为黑白后进canny边缘检测后多次进行开运算与闭运算用于消除小块的区域,保留大块的区域,后用cv2.rectangle选取矩形框,从而定位车牌位置
车牌字符的分割前需要准备的是只保留车牌部分,将其他部分均变为黑色背景。这里我采用cv2.grabCut方法,可将图像分割成前景与背景。分割完成后,再经过二值化为黑白图后即可进行字符分割。由于图像中只有黑色和白色像素,因此我们需要通过图像的白色像素和黑色像素来分割开字符。即分别通过判断每一行每一列的黑色白色像素值的位置,来定位出字符。
具体步骤如下:
- 1.灰度转换:将彩色图片转换为灰度图像,常见的R=G=B=像素平均值。
- 2.高斯平滑和中值滤波:去除噪声。
- 3.Sobel算子:提取图像边缘轮廓,X方向和Y方向平方和开跟。
- 4.二值化处理:图像转换为黑白两色,通常像素大于127设置为255,小于设置为0。
- 5.膨胀和细化:放大图像轮廓,转换为一个个区域,这些区域内包含车牌。
- 6.通过算法选择合适的车牌位置,通常将较小的区域过滤掉或寻找蓝色底的区域。
- 7.标注车牌位置
- 8.图像切割和识别
通过代码实现:
# -*- coding: utf-8 -*-
"""
@email:cuiran2001@163.com
@author: cuiran
"""
import cv2
import numpy as np
from PIL import Image
import os.path
from skimage import io,data
def stretch(img):
'''
图像拉伸函数
'''
maxi=float(img.max())
mini=float(img.min())
for i in range(img.shape[0]):
for j in range(img.shape[1]):
img[i,j]=(255/(maxi-mini)*img[i,j]-(255*mini)/(maxi-mini))
return img
def dobinaryzation(img):
'''
二值化处理函数
'''
maxi=float(img.max())
mini=float(img.min())
x=maxi-((maxi-mini)/2)
#二值化,返回阈值ret 和 二值化操作后的图像thresh
ret,thresh=cv2.threshold(img,x,255,cv2.THRESH_BINARY)
#返回二值化后的黑白图像
return thresh
def find_rectangle(contour):
'''
寻找矩形轮廓
'''
y,x=[],[]
for p in contour:
y.append(p[0][0])
x.append(p[0][1])
return [min(y),min(x),max(y),max(x)]
def locate_license(img,afterimg):
'''
定位车牌号
'''
img,contours,hierarchy=cv2.findContours(img,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
#找出最大的三个区域
block=[]
for c in contours:
#找出轮廓的左上点和右下点,由此计算它的面积和长度比
r=find_rectangle(c)
a=(r[2]-r[0])*(r[3]-r[1]) #面积
s=(r[2]-r[0])*(r[3]-r[1]) #长度比
block.append([r,a,s])
#选出面积最大的3个区域
block=sorted(block,key=lambda b: b[1])[-3:]
#使用颜色识别判断找出最像车牌的区域
maxweight,maxindex=0,-1
for i in range(len(block)):
b=afterimg[block[i][0][1]:block[i][0][3],block[i][0][0]:block[i][0][2]]
#BGR转HSV
hsv=cv2.cvtColor(b,cv2.COLOR_BGR2HSV)
#蓝色车牌的范围
lower=np.array([100,50,50])
upper=np.array([140,255,255])
#根据阈值构建掩膜
mask=cv2.inRange(hsv,lower,upper)
#统计权值
w1=0
for m in mask:
w1+=m/255
w2=0
for n in w1:
w2+=n
#选出最大权值的区域
if w2>maxweight:
maxindex=i
maxweight=w2
return block[maxindex][0]
def find_license(img):
'''
预处理函数
'''
m=400*img.shape[0]/img.shape[1]
#压缩图像
img=cv2.resize(img,(400,int(m)),interpolation=cv2.INTER_CUBIC)
#BGR转换为灰度图像
gray_img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#灰度拉伸
stretchedimg=stretch(gray_img)
'''进行开运算,用来去除噪声'''
r=16
h=w=r*2+1
kernel=np.zeros((h,w),np.uint8)
cv2.circle(kernel,(r,r),r,1,-1)
#开运算
openingimg=cv2.morphologyEx(stretchedimg,cv2.MORPH_OPEN,kernel)
#获取差分图,两幅图像做差 cv2.absdiff('图像1','图像2')
strtimg=cv2.absdiff(stretchedimg,openingimg)
#图像二值化
binaryimg=dobinaryzation(strtimg)
#canny边缘检测
canny=cv2.Canny(binaryimg,binaryimg.shape[0],binaryimg.shape[1])
'''消除小的区域,保留大块的区域,从而定位车牌'''
#进行闭运算
kernel=np.ones((5,19),np.uint8)
closingimg=cv2.morphologyEx(canny,cv2.MORPH_CLOSE,kernel)
#进行开运算
openingimg=cv2.morphologyEx(closingimg,cv2.MORPH_OPEN,kernel)
#再次进行开运算
kernel=np.ones((11,5),np.uint8)
openingimg=cv2.morphologyEx(openingimg,cv2.MORPH_OPEN,kernel)
#消除小区域,定位车牌位置
rect=locate_license(openingimg,img)
return rect,img
def cut_license(afterimg,rect):
'''
图像分割函数
'''
#转换为宽度和高度
rect[2]=rect[2]-rect[0]
rect[3]=rect[3]-rect[1]
rect_copy=tuple(rect.copy())
rect=[0,0,0,0]
#创建掩膜
mask=np.zeros(afterimg.shape[:2],np.uint8)
#创建背景模型 大小只能为13*5,行数只能为1,单通道浮点型
bgdModel=np.zeros((1,65),np.float64)
#创建前景模型
fgdModel=np.zeros((1,65),np.float64)
#分割图像
cv2.grabCut(afterimg,mask,rect_copy,bgdModel,fgdModel,5,cv2.GC_INIT_WITH_RECT)
mask2=np.where((mask==2)|(mask==0),0,1).astype('uint8')
img_show=afterimg*mask2[:,:,np.newaxis]
return img_show
def deal_license(licenseimg):
'''
车牌图片二值化
'''
#车牌变为灰度图像
gray_img=cv2.cvtColor(licenseimg,cv2.COLOR_BGR2GRAY)
#均值滤波 去除噪声
kernel=np.ones((3,3),np.float32)/9
gray_img=cv2.filter2D(gray_img,-1,kernel)
#二值化处理
ret,thresh=cv2.threshold(gray_img,120,255,cv2.THRESH_BINARY)
return thresh
def find_end(start,arg,black,white,width,black_max,white_max):
end=start+1
for m in range(start+1,width-1):
if (black[m] if arg else white[m])>(0.98*black_max if arg else 0.98*white_max):
end=m
break
return end
if __name__=='__main__':
img=cv2.imread('test_images/car001.jpg',cv2.IMREAD_COLOR)
#预处理图像
rect,afterimg=find_license(img)
#框出车牌号
cv2.rectangle(afterimg,(rect[0],rect[1]),(rect[2],rect[3]),(0,255,0),2)
cv2.imshow('afterimg',afterimg)
#分割车牌与背景
cutimg=cut_license(afterimg,rect)
cv2.imshow('cutimg',cutimg)
#二值化生成黑白图
thresh=deal_license(cutimg)
cv2.imshow('thresh',thresh)
cv2.waitKey(0)
#分割字符
'''
判断底色和字色
'''
#记录黑白像素总和
white=[]
black=[]
height=thresh.shape[0] #263
width=thresh.shape[1] #400
#print('height',height)
#print('width',width)
white_max=0
black_max=0
#计算每一列的黑白像素总和
for i in range(width):
line_white=0
line_black=0
for j in range(height):
if thresh[j][i]==255:
line_white+=1
if thresh[j][i]==0:
line_black+=1
white_max=max(white_max,line_white)
black_max=max(black_max,line_black)
white.append(line_white)
black.append(line_black)
print('white',white)
print('black',black)
#arg为true表示黑底白字,False为白底黑字
arg=True
if black_max<white_max:
arg=False
n=1
start=1
end=2
s_width=28
s_height=28
while n<width-2:
n+=1
#判断是白底黑字还是黑底白字 0.05参数对应上面的0.95 可作调整
if(white[n] if arg else black[n])>(0.02*white_max if arg else 0.02*black_max):
start=n
end=find_end(start,arg,black,white,width,black_max,white_max)
n=end
if end-start>5:
cj=thresh[1:height,start:end]
# new_image = cj.resize((s_width,s_height),Image.BILINEAR)
# cj=cj.reshape(28, 28)
print("result/%s.jpg" % (n))
#保存分割的图片 by cayden
# cj.save("result/%s.jpg" % (n))
infile="result/%s.jpg" % (n)
io.imsave(infile,cj)
# im = Image.open(infile)
# out=im.resize((s_width,s_height),Image.BILINEAR)
# out.save(infile)
cv2.imshow('cutlicense',cj)
cv2.waitKey(0)
cv2.waitKey(0)
cv2.destroyAllWindows()
运行效果如图所示
车牌定位并进行处理
车牌分割如图所示
以上是关于基于python+Opencv的车牌识别的主要内容,如果未能解决你的问题,请参考以下文章