图像处理实践--车牌识别

1、OpenCV 基本操作

1.1 颜色空间
  • RGB 图像:每个像素点包括三个通道(OpenCV 中分别为 BGR,matplotlib 中为 RGB),用一个三维数组表示。

  • 灰度图:把白色与黑色之间按对数关系分为若干等级,称为灰度。灰度分为256阶。

  • 二值图:只有黑白两色,用 0、1 或 0、255 表示。

1.2 图像运算

图像加减乘除等基本操作。

1.3 几何变换

平移、错切、镜像、旋转、透视等变换,关键在于找到相应的变换矩阵 M 。

1.4 阈值分割
  • 直方图阈值分割:根据图像灰度直方图,人工寻找阈值。适用于双峰图像;

  • 三角法阈值:适用于单峰阈值;

  • 自适应阈值:即局部二值化。例如:文字扫描;

  • 大津法:不断迭代,使类间方差最大,使用较多;

1.5 图像平滑处理
  • 卷积滤波:就是利用卷积运算的原理来构成滤波器,来提取特征。卷积核和对应像素点进行运算;

  • 均值滤波:即采用一个求平均值的卷积核;

  • 中值滤波:它用邻域内所有像素值的中间值来替代当前像素点的像素值;

  • 高斯滤波:离卷积核中心点越近的像素权重越大,越远权重越小(sigma 太小,几乎无滤波效果, 太大和中值滤波效果相近);

  • 双边滤波:可保留边缘高频信息。

1.6 形态学操作
  • 腐蚀:将图像的边界点消除,使图像沿着边界向内收缩,也可以将小于指定结构体元素的部分去除。可实现去除噪声、元素分割等功能。

  • 膨胀:膨胀操作和腐蚀操作的作用是相反的,膨胀操作能对图像的边界进行扩张,可使图像更加紧凑。

  • 开运算:先腐蚀、再膨胀,可以用于去噪、计数等。

  • 闭运算:先膨胀、后腐蚀,它有助于关闭前景物体内部的小孔,或去除物体上的小黑点,还可以将不同的前景图像进行连接。

1.7 边缘检测
  • Sobel 算子:

在这里插入图片描述

可分别计算水平和竖直方向的梯度,调用方式

imgX = cv.Sobel(img, -1, dx=1, dy=0)
imgY = cv.Sobel(img, -1, dx=0, dy=1)
imgXY = np.abs(imgX) + np.abs(imgY)
show(np.hstack([img, imgXY]))
  • Canny 边缘检测

算法原理:

(1)降噪(高斯模糊)

(2)求梯度

(3)细化边缘

(4)双阈值抑制, 大于上限的为强边缘, 小于下限的不是边缘,中间部分为弱边缘,进入下一步判断。

(5)边缘追踪:如果弱边缘与强边缘相连,则其也为边缘,否则为弱边缘

函数调用:

edges = cv.Canny(imag, threshold1, threshold2[, apertureSize[,L2gradient]])

2、图像处理一般步骤

(1)图像导入

(2)图像预处理:滤波、去噪

(3)图像分割:即提取出感兴趣区域,包括灰度化、阈值分割、边缘检测

(4)特征识别

(5)显示结果

3、车牌识别

思路:

(1)车牌提取

(2)字符分割

(3)字符匹配

完整代码:

# @Time : 2021/12/3 19:46
# @Author : xiao cong
# @Function :
"""
(1)提取车牌位置,将车牌从图中分割出来;
(2)车牌字符的分割;
(3)通过模版匹配识别字符;
(4)将结果绘制在图片上显示出来。
"""
import numpy as np
import matplotlib.pyplot as plt
import cv2 as cv
import os
from PIL import ImageFont, ImageDraw, Image


# 显示图片
def cv_show(name, img):
    cv.imshow(name, img)
    cv.waitKey()
    cv.destroyAllWindows()


def show(img):
    if img.ndim == 2:
        plt.imshow(img, cmap="gray", vmin=0, vmax=255)
    else:
        plt.imshow(cv.cvtColor(img, cv.COLOR_BGR2RGB))
    plt.show()


# ******************************************************************************
"""
提取车牌位置
"""
# 加载图片
origin_img = cv.imread("car.jpg")

# 高斯滤波、转为灰度图
img = cv.GaussianBlur(origin_img, ksize=(5, 5), sigmaX=1)
img_gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
# show(img_gray)

# Sobel 算子,X方向边缘检测,使车牌聚拢
imgX = cv.Sobel(img_gray, -1, dx=1, dy=0)
imgX_abs = abs(imgX)
# show(imgX_abs)

# 阈值分割,二值化图像
_, img_bin = cv.threshold(imgX_abs, 0, 255, cv.THRESH_OTSU)
# show(img_bin)

# 闭运算(先膨胀,再腐蚀),使车牌连成整体
kernel = cv.getStructuringElement(cv.MORPH_RECT, (17, 5))  # 17*5 的矩形核
img = cv.morphologyEx(img_bin, op=cv.MORPH_CLOSE, kernel=kernel, iterations=3)
# show(img)

# 去除白点,白线
kernel1 = cv.getStructuringElement(cv.MORPH_RECT, (10, 20))
kernel2 = cv.getStructuringElement(cv.MORPH_RECT, (20, 10))
# 腐蚀、膨胀
img = cv.erode(img, kernel1)
img = cv.dilate(img, kernel1)
# 膨胀、腐蚀
img = cv.dilate(img, kernel2)
img = cv.erode(img, kernel2)
# show(img)

# 中值滤波
img = cv.medianBlur(img, ksize=3)
# show(img)

# 轮廓检测
contours, _ = cv.findContours(img, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
image = origin_img.copy()
cv.drawContours(image, contours, -1, color=(255, 0, 0), thickness=5)  # 画在原图上
# show(image)

# 找出车牌位置,长宽比为 3:1 或 4:1
for item in contours:
    rect = cv.boundingRect(item)
    # cv2.boundingRect(img)函数可以获得一个图像的最小矩形边框一些信息,返回四个参数,左上角坐标,矩形的宽高.
    x, y = rect[0], rect[1]  # 矩形左上角坐标
    w = rect[2]
    h = rect[3]
    if 3 < (w / h) < 4:
        carLicense = origin_img[y:y + h, x:x + w]
        show(carLicense)
        cv.imwrite("carLicense.jpg", carLicense)

# ***************************************************************************************************
""""
车牌字符提取     
"""
originImage = cv.imread("carLicense.jpg")
# 高斯模糊
Img = cv.GaussianBlur(originImage, ksize=(3, 3), sigmaX=1)

# 灰度
Img_gray = cv.cvtColor(Img, cv.COLOR_BGR2GRAY)
# show(Image)

# 二值化
_, Img = cv.threshold(Img_gray, 0, 255, cv.THRESH_OTSU)
# show(Image)


# 如果二值化后是白底黑字,则需要反转过来(绿牌可能会出现这种情况)
area_white, area_black = 0, 0
height, width = Img.shape
for i in range(height):
    for j in range(width):
        if Img[i][j] == 255:
            area_white += 1
        else:
            area_black += 1

if area_white > area_black:  # 此时白底黑字,进行反转
    Img = cv.threshold(Img, 0, 255, cv.THRESH_BINARY_INV)

# 膨胀,使每个字符更紧凑
kernel = cv.getStructuringElement(cv.MORPH_RECT, (2, 2))
Img = cv.dilate(Img, kernel)
show(Img)

# 检测每个字的轮廓
contours, _ = cv.findContours(Img, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
image = originImage.copy()
cv.drawContours(image, contours, -1, color=(0, 0, 255), thickness=1)  # 画在原图上
show(image)

# 字符提取
words = []  # 存储所有字符轮廓
for item in contours:
    word = []  # 存储每个轮廓的信息
    rect = cv.boundingRect(item)
    x, y = rect[0], rect[1]
    w, h = rect[2], rect[3]
    word.append(x)
    word.append(y)
    word.append(w)
    word.append(h)
    words.append(word)

words = sorted(words, key=lambda s: s[0], reverse=False)  # 按照每个字符轮廓 x 坐标从小到大排序,保持车牌字符顺序

# 选择需要的字符
i = 0  # 计数
word_images = []  # 存储分割后的图片
for word in words:
    if 1.8 < (word[3] / word[2]) < 3.5:  # 注意 1 和 I ,长宽比比较大
        i += 1
        word_image = Img[word[1]:word[1] + word[3], word[0]:word[0] + word[2]]
        # show(word_image)
        word_images.append(word_image)
        filename = "word_" + str(i) + ".jpg"
        cv.imwrite(filename=filename, img=word_image)

# **************************************************************************************
"""
模板匹配
"""
template = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W',
            'X', 'Y', 'Z',
            '藏', '川', '鄂', '甘', '赣', '贵', '桂', '黑', '沪', '吉', '冀', '津', '晋', '京', '辽', '鲁', '蒙', '闽', '宁',
            '青', '琼', '陕', '苏', '皖', '湘', '新', '渝', '豫', '粤', '云', '浙']


# 读取一个文件夹下的所有图片,输入参数是文件名,返回模板文件地址列表
def read_directory(directory_name):
    referImg_list = []
    for filename in os.listdir(directory_name):
        referImg_list.append(directory_name + '/' + filename)
    return referImg_list


# 获得中文模板列表(只匹配车牌的第一个字符)
def get_chinese_word_list():
    Chinese_words_list = []
    for i in range(34, 64):
        c_word = read_directory('./refer1/' + template[i])  # 记录每个中文字符文件下每张图片的目录
        Chinese_words_list.append(c_word)
    return Chinese_words_list


Chinese_words_list = get_chinese_word_list()


# 获得英文模板列表(只匹配车牌的第二个字符)
def get_english_word_list():
    English_words_list = []
    for i in range(10, 34):
        e_word = read_directory('./refer1/' + template[i])
        English_words_list.append(e_word)
    return English_words_list


English_words_list = get_english_word_list()


# 获得英文和数字模板列表(匹配车牌后面的字符)
def get_eng_num_words_list():
    eng_num_words_list = []
    for i in range(0, 34):
        word = read_directory('./refer1/' + template[i])
        eng_num_words_list.append(word)
    return eng_num_words_list


eng_num_words_list = get_eng_num_words_list()


# 读取一个模板地址与图片进行匹配,返回得分
def template_score(template, image):  # 传入的是模板图片地址和需要匹配的图像
    template_image = cv.imdecode(np.fromfile(template, dtype=np.uint8), 1)
    # 灰度、二值化
    template_image = cv.cvtColor(template_image, cv.COLOR_BGR2GRAY)
    _, template_image = cv.threshold(template_image, 0, 255, cv.THRESH_OTSU)

    img_ = image.copy()
    height, width = img_.shape
    template_image = cv.resize(template_image, (width, height))  # 缩放至同一尺寸
    result = cv.matchTemplate(img_, template_image, cv.TM_CCOEFF)
    return result[0][0]


# 对分割得到的字符逐一匹配
def template_matching(word_images):
    results = []
    for index, word_image in enumerate(word_images):
        if index == 0:  # 即为第一个:汉字
            best_score = []
            for chinese_words in Chinese_words_list:
                score = []
                for chinese_word in chinese_words:
                    result = template_score(chinese_word, word_image)
                    score.append(result)
                best_score.append(max(score))
            fit_index = best_score.index(max(best_score))  # 记录最佳匹配的下标
            results.append(template[34 + fit_index])
            continue

        if index == 1:  # 匹配第一个英文字母
            best_score = []
            for english_words in English_words_list:
                score = []
                for english_word in english_words:
                    result = template_score(english_word, word_image)
                    score.append(result)
                best_score.append(max(score))
            fit_index = best_score.index(max(best_score))  # 记录最佳匹配的下标
            results.append(template[10 + fit_index])
            continue

        else:
            best_score = []
            for eng_num_words in eng_num_words_list:
                score = []
                for eng_num_word in eng_num_words:
                    result = template_score(eng_num_word, word_image)
                    score.append(result)
                best_score.append(max(score))
            fit_index = best_score.index(max(best_score))
            results.append(template[fit_index])

    return results


word_images_ = word_images.copy()
result = template_matching(word_images_)


# ************************************************************************************************
"""
显示识别结果
"""
height, width = origin_img.shape[0:2]
# print(height, width)
img = origin_img.copy()
cv.rectangle(img, (np.int(0.2 * width), np.int(0.75 * height)), (np.int(width * 0.9), np.int(height * 0.95)), (0, 255, 0),
             5)  # 画出方框

# 设置文字
fontpath = "font/simsun.ttc"
font = ImageFont.truetype(fontpath, 64)
img_pil = Image.fromarray(img)
draw = ImageDraw.Draw(img_pil)

draw.text((np.int(0.2 * width) + 25, np.int(0.75 * height)), "".join(result), font=font, fill=(0, 255, 0))
bk_img = np.array(img_pil)
print("".join(result))
show(bk_img)

识别结果:

在这里插入图片描述

参考:

https://blog.csdn.net/qq_40784418/article/details/105586644

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值