2022-12-01

标题: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()
testimg.jpg

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()
色彩空间转换.png

这里是色彩空间转换,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()
提取绿色区域.png

高斯模糊

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()
高斯模糊.png

转换成灰度图并且二值化,方便后续操作

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()
转为灰度图像.png

开运算闭运算进一步排除干扰

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()
开运算.png
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()
闭运算.png

开运算闭运算并不能完美的排除干扰,我们仍需要对残图进一步筛选车牌,根据车牌的物理特征筛选。

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()#将检测到的车牌显示出来
筛选车牌.png

得到的车牌有可能存在本身的模糊或者图像处理本身的问题导致不够清晰,这时需要膨胀算法修复文字

    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)
车牌识别.png

至此,完成车牌的识别

4.总结:

如前面所言,因思路因人而异,实现的方法是多种多样的。但每个思路都有其不够完善的地方。例如在hsv空间筛选车牌的步骤中,本人是以个人计算去猜测得出的值,不够准确。以及在开运算闭运算中,卷积核大小的选择很重要,要依据实例情况选择不同的核。另外,对于过度倾斜的车牌,可能不能很好地识别(本文用例给的是比较正向的车牌),这时用上角度矫正的话会使代码逻辑更完美

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,372评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,368评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,415评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,157评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,171评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,125评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,028评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,887评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,310评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,533评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,690评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,411评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,004评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,812评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,693评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,577评论 2 353

推荐阅读更多精彩内容