米尔电子
直播中

筑梦者与梦同行

9年用户 201经验值
擅长:嵌入式技术
私信 关注
[技术]

【米尔MYD-JX8MMA7开发板-ARM+FPGA架构试用体验】十四、车牌识别

【MYD-JX8MMA7】 十四、OpenCV车牌识别
一、基本理论
通过模板匹配的方法进行字符识别,每个字符依次在模板集中寻找与自己最相似的模板,并将最相似的模板对应的字符识别为当前字符。
必须使用量化指标衡量相似度,简单来说,必须用一个数值来衡量两幅图像之间的相似程度。在OpenCV中可以使用函数matchTemplate来衡量两幅图像之间的相似度。
因此,字符识别转换成立如下两个问题:
使用函数matchTemplate依次衡量待识别字符与模板集中每一个字符的匹配值。
将最匹配的模板对应的字符确定为识别结果。
通过如下三个函数解决上述两个问题。
函数1:读取模板图像。模板图像较多,因此构造一个函数来读取模板图像,以便后续计算其与待识别图像间的匹配值。
函数2:计算匹配值。该函数用来计算两幅图像之间的匹配值,主要借助OPenCV中的函数matchTemplate完成。
函数3:识别字符。该函数用来识别字符。

二、主要函数代码
2.1、函数1:读取模板图像
首先,构造一个字典用于存储包含所有数字、字母、部分省份简称在内的字符集;然后,使用glob库获取所有模板的文件名,具体如下:
#=================模板,部分省份,使用字典表示==============================
templateDict = {0:'0',1:'1',2:'2',3:'3',4:'4',5:'5',6:'6',7:'7',8:'8',9:'9',
            10:'A',11:'B',12:'C',13:'D',14:'E',15:'F',16:'G',17:'H',
            18:'J',19:'K',20:'L',21:'M',22:'N',23:'P',24:'Q',25:'R',
            26:'S',27:'T',28:'U',29:'V',30:'W',31:'X',32:'Y',33:'Z',
            34:'京',35:'津',36:'冀',37:'晋',38:'蒙',39:'辽',40:'吉',41:'黑',
            42:'沪',43:'苏',44:'浙',45:'皖',46:'闽',47:'赣',48:'鲁',49:'豫',
            50:'鄂',51:'湘',52:'粤',53:'桂',54:'琼',55:'渝',56:'川',57:'贵',
            58:'云',59:'藏',60:'陕',61:'甘',62:'青',63:'宁',64:'新',
            65:'港',66:'澳',67:'台'}
# ==================获取所有字符的路径信息===================
def getcharacters():
    c=[]
    for i in range(0,67):
        words=[]
        words.extend(glob.glob('template/'+templateDict.get(i)+'/*.*'))
        c.append(words)
    return c
2.2、计算匹配值
将匹配度进行量化,即使用一个数值来表示两幅图像的相似程度。在OpenCV内可使用函数matchTemplate计算两幅图像的匹配值。
构造函数getMatchValue,以计算匹配值。其中,参数template是文件名,参数image是待检测的图像。
#=============计算匹配值函数=====================
def getMatchValue(template,image):
    #读取模板图像
    # templateImage=cv2.imread(template)   #cv2读取中文文件名不友好
    templateImage=cv2.imdecode(np.fromfile(template,dtype=np.uint8),1)
    #模板图像色彩空间转换,BGR-->灰度
    templateImage = cv2.cvtColor(templateImage, cv2.COLOR_BGR2GRAY)
    #模板图像阈值处理, 灰度-->二值
    ret, templateImage = cv2.threshold(templateImage, 0, 255, cv2.THRESH_OTSU)
    # 获取待识别图像的尺寸
    height, width = image.shape
    # 将模板图像调整为与待识别图像尺寸一致
    templateImage = cv2.resize(templateImage, (width, height))
    #计算模板图像、待识别图像的模板匹配值
    result = cv2.matchTemplate(image, templateImage, cv2.TM_CCOEFF)
    # 将计算结果返回
    return result[0][0]
2.3、识别字符
2.3.1、字符模板
图片13.png 图片14.png 图片15.png

先针对模板内的每个字符计算出一个与待识别字最匹配的模板;然后在逐字符匹配结果中找出最佳匹配模板,从而确定最终识别结果。
使用到了3层循环关系检测:
1.逐个遍历提取的各个字符。
2.遍历所有特征字符(字符集中的每个字符)。
3.遍历每一个特征字符的多有模板。
# ===========对车牌内字符进行识别====================
#plates,要识别的字符集,
# 也就是从车牌图像“GUA211”中分离出来的每一个字符的图像"G","U","A","2","1","1"
#chars,所有字符的模板集合,也就是0-9,A-Z,京-台,每一个字符模板
def matchChars(plates,chars):
    results=[]   #存储所有的识别结果
    #最外层循环:逐个遍历要识别的字符。
    # 例如,逐个遍历从车牌图像“GUA211”中分离出来的每一个字符的图像
    # 如"G","U","A","2","1","1"
    # plateChar分别存储,"G","U","A","2","1","1"
    for plateChar in plates:#逐个遍历要识别的字符
        #bestMatch,存储的是待识别字符与每个特征字符的所有模板中最匹配的模板
        # 例如,待识别图像“G”,与所有的字符0-9,A-Z,京-台,每一个字符最匹配的模板
        bestMatch = []      #最佳匹配
        #中间层循环:针对模板内的字符,进行逐个遍历(每次循环针对一个特定的字符),
        #words 对应的是每一个字符(例如字符A)的所有模板
        for words in chars: #遍历字符。chars:所有模板,words:某个字符的所有模板
            #match,存储的是每个特征字符的所有匹配值
            # 例如:待识别图像“G”,与字符7的所有模板的匹配值
            match = []      #每个字符的匹配值
            #最内层循环:针对的是单个字符的所有模板,找到最佳的模板
            #  word对应的是单个模板
            for word in words:  #遍历模板。words:某个字符所有模板,word单个模板
                result = getMatchValue(word,plateChar)
                match.append(result)
            bestMatch.append(max(match))   #将每个字符模板的最佳匹配加入bestMatch
        i = bestMatch.index(max(bestMatch))  #i是最佳匹配的字符模板的索引值
        r = templateDict    #r是单个待识别字符的识别结果
        results.append(r)   #将每一个分割字符的识别结果加入到results内
    return results   #返回所有的识别结果

三、实现程序
# -*- coding: utf-8 -*-
# ==========================导入库==============================
import cv2
# from matplotlib import pyplot as plt
import numpy as np
import glob
# ==========================提取车牌函数==============================
def getPlate(image):
    rawImage=image.copy()
    # 去噪处理
    image = cv2.GaussianBlur(image, (3, 3), 0)
    # 色彩空间转换(RGB-->GRAY)
    image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    # Sobel算子(X方向边缘梯度)
    Sobel_x = cv2.Sobel(image, cv2.CV_16S, 1, 0)
    absX = cv2.convertScaleAbs(Sobel_x)  # 映射到[0.255]内
    image = absX
    # 阈值处理
    ret, image = cv2.threshold(image, 0, 255, cv2.THRESH_OTSU)
    # 闭运算:先膨胀后腐蚀,车牌各个字符是分散的,让车牌构成一体
    kernelX = cv2.getStructuringElement(cv2.MORPH_RECT, (17, 5))
    image = cv2.morphologyEx(image, cv2.MORPH_CLOSE, kernelX)
    # 开运算:先腐蚀后膨胀,去除噪声
    kernelY = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 19))
    image = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernelY)
    # 中值滤波:去除噪声
    image = cv2.medianBlur(image, 15)
    # 查找轮廓
    contours, w1 = cv2.findContours(image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    #测试语句,查看处理结果
    # image = cv2.drawContours(rawImage.copy(), contours, -1, (0, 0, 255), 3)
    # cv2.imshow('imagecc', image)
    #逐个遍历,将宽度>3倍高度的轮廓确定为车牌
    for item in contours:
        rect = cv2.boundingRect(item)
        x = rect[0]
        y = rect[1]
        weight = rect[2]
        height = rect[3]
        if weight > (height * 3):
            plate = rawImage[y:y + height, x:x + weight]   
    return plate
#======================预处理函数,图像去噪等处理=================
def preprocessor(image):
    # 图像去噪灰度处理
    image = cv2.GaussianBlur(image, (3, 3), 0)
    # 色彩空间转换
    gray_image = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
    # 阈值处理(二值化)   
    ret, image = cv2.threshold(gray_image, 0, 255, cv2.THRESH_OTSU)
    #膨胀处理,让一个字构成一个整体(大多数字不是一体的,是分散的)
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))
    image = cv2.dilate(image, kernel)
    return image
#===========拆分车牌函数,将车牌内各个字符分离==================
def splitPlate(image):
    # 查找轮廓,各个字符的轮廓
    contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    words = []
    # 遍历所有轮廓
    for item in contours:
        rect = cv2.boundingRect(item)
        words.append(rect)
    # print(len(contours))  #测试语句:看看找到多少个轮廓
    #-----测试语句:看看轮廓效果-----
    # imageColor=cv2.cvtColor(image,cv2.COLOR_GRAY2BGR)
    # x = cv2.drawContours(imageColor, contours, -1, (0, 0, 255), 1)
    # cv2.imshow("contours",x)   
    #-----测试语句:看看轮廓效果-----
    # 按照x轴坐标值排序(自左向右排序)
    words = sorted(words,key=lambda s:s[0],reverse=False)   
    # 用word存放左上角起始点及长宽值
    plateChars = []
    for word in words:
        # 筛选字符的轮廓(高宽比在1.5-8之间,宽度大于3)
        if (word[3] > (word[2] * 1.5)) and (word[3] < (word[2] * 8)) and (word[2] > 3):
            plateChar = image[word[1]:word[1] + word[3], word[0]:word[0] + word[2]]
            plateChars.append(plateChar)
    # 测试语句:查看各个字符
    # for i,im in enumerate(plateChars):
    #     cv2.imshow("char"+str(i),im)
    return plateChars
#=================模板,部分省份,使用字典表示==============================
templateDict = {0:'0',1:'1',2:'2',3:'3',4:'4',5:'5',6:'6',7:'7',8:'8',9:'9',
            10:'A',11:'B',12:'C',13:'D',14:'E',15:'F',16:'G',17:'H',
            18:'J',19:'K',20:'L',21:'M',22:'N',23:'P',24:'Q',25:'R',
            26:'S',27:'T',28:'U',29:'V',30:'W',31:'X',32:'Y',33:'Z',
            34:'京',35:'津',36:'冀',37:'晋',38:'蒙',39:'辽',40:'吉',41:'黑',
            42:'沪',43:'苏',44:'浙',45:'皖',46:'闽',47:'赣',48:'鲁',49:'豫',
            50:'鄂',51:'湘',52:'粤',53:'桂',54:'琼',55:'渝',56:'川',57:'贵',
            58:'云',59:'藏',60:'陕',61:'甘',62:'青',63:'宁',64:'新',
            65:'港',66:'澳',67:'台'}
# ==================获取所有字符的路径信息===================
def getcharacters():
    c=[]
    for i in range(0,67):
        words=[]
        words.extend(glob.glob('template/'+templateDict.get(i)+'/*.*'))
        c.append(words)
    return c
#=============计算匹配值函数=====================
def getMatchValue(template,image):
    #读取模板图像
    # templateImage=cv2.imread(template)   #cv2读取中文文件名不友好
    templateImage=cv2.imdecode(np.fromfile(template,dtype=np.uint8),1)
    #模板图像色彩空间转换,BGR-->灰度
    templateImage = cv2.cvtColor(templateImage, cv2.COLOR_BGR2GRAY)
    #模板图像阈值处理, 灰度-->二值
    ret, templateImage = cv2.threshold(templateImage, 0, 255, cv2.THRESH_OTSU)
    # 获取待识别图像的尺寸
    height, width = image.shape
    # 将模板图像调整为与待识别图像尺寸一致
    templateImage = cv2.resize(templateImage, (width, height))
    #计算模板图像、待识别图像的模板匹配值
    result = cv2.matchTemplate(image, templateImage, cv2.TM_CCOEFF)
    # 将计算结果返回
    return result[0][0]
# ===========对车牌内字符进行识别====================
#plates,要识别的字符集,
# 也就是从车牌图像“GUA211”中分离出来的每一个字符的图像"G","U","A","2","1","1"
#chars,所有字符的模板集合,也就是0-9,A-Z,京-台,每一个字符模板
def matchChars(plates,chars):
    results=[]   #存储所有的识别结果
    #最外层循环:逐个遍历要识别的字符。
    # 例如,逐个遍历从车牌图像“GUA211”中分离出来的每一个字符的图像
    # 如"G","U","A","2","1","1"
    # plateChar分别存储,"G","U","A","2","1","1"
    for plateChar in plates:#逐个遍历要识别的字符
        #bestMatch,存储的是待识别字符与每个特征字符的所有模板中最匹配的模板
        # 例如,待识别图像“G”,与所有的字符0-9,A-Z,京-台,每一个字符最匹配的模板
        bestMatch = []      #最佳匹配
        #中间层循环:针对模板内的字符,进行逐个遍历(每次循环针对一个特定的字符),
        #words 对应的是每一个字符(例如字符A)的所有模板
        for words in chars: #遍历字符。chars:所有模板,words:某个字符的所有模板
            #match,存储的是每个特征字符的所有匹配值
            # 例如:待识别图像“G”,与字符7的所有模板的匹配值
            match = []      #每个字符的匹配值
            #最内层循环:针对的是单个字符的所有模板,找到最佳的模板
            #  word对应的是单个模板
            for word in words:  #遍历模板。words:某个字符所有模板,word单个模板
                result = getMatchValue(word,plateChar)
                match.append(result)
            bestMatch.append(max(match))   #将每个字符模板的最佳匹配加入bestMatch
        i = bestMatch.index(max(bestMatch))  #i是最佳匹配的字符模板的索引值
        r = templateDict    #r是单个待识别字符的识别结果
        results.append(r)   #将每一个分割字符的识别结果加入到results内
    return results   #返回所有的识别结果
# ================主程序=============
image = cv2.imread("car11.jpeg")           #读取原始图像
cv2.imshow("original",image)            #显示原始图像
image=getPlate(image)                   #获取车牌
cv2.imshow('plate', image)              #测试语句:看看车牌定位情况
image=preprocessor(image)               #预处理
# cv2.imshow("imagePre",image)          #测试语句,看看预处理结果
plateChars=splitPlate(image)            #分割车牌,将每个字符独立出来
for i,im in enumerate(plateChars):      #逐个遍历字符
    cv2.imshow("plateChars"+str(i),im)  #显示分割的字符
chars=getcharacters()                   #获取所有模板文件(文件名)
results=matchChars(plateChars, chars)   #使用模板chars逐个识别字符集plates
results="".join(results)                #将列表转换为字符串
print("识别结果为:",results)            #输出识别结果
cv2.waitKey(0)                          #显示暂停
cv2.destroyAllWindows()                 #释放窗口   
四、运行效果
图片16.png 图片17.png 图片18.png
五、资源程序文件
车牌识别.rar (2.22 MB)
(下载次数: 2, 2023-6-1 20:43 上传)



更多回帖

×
20
完善资料,
赚取积分