Python+OpenCV+paddleocr基于传统图像处理技术实现车牌识别

目录

一、前言

二、预处理-提取车牌

        1. 转灰度图

        2. 顶帽运算

        3. Sobel算子提取y方向边缘

        4. 自适应二值化

        5. 开运算分割(纵向去噪,分隔)

        6. 闭运算合并

        7. 膨胀/腐蚀

        8. 腐蚀、膨胀:去噪

        9. 获取外轮廓

        10. 遍历所有轮廓,找到车牌轮廓

三、OCR-识别文字

四、优点、不足与改进方向

        1.优点

        2.不足 

        3.改进方向

五、完整代码


一、前言

        本文使用了简单的传统图像处理技术,实现了对车牌的提取与字符的识别。

        解决思路:

        (1)首先对图像进行处理,使用人工设计的特征提取出图像中的车牌;

        (2)使用OCR库,对提取出的车牌进行字符识别,达到车牌识别的效果。

二、预处理-提取车牌

        预处理的工作,就是使用传统图像处理技术,从图像中找到车牌的位置并截取出来以供OCR使用。

Python+OpenCV+paddleocr基于传统图像处理技术实现车牌识别_第1张图片 识别流程

        1. 转灰度图

        由于车牌颜色多种多样,无法使用颜色阈值的方法来提取车牌,因此考虑使用形态学操作的方法来提取车牌。首先将图片转为灰度图,排除颜色对识别的干扰。

import cv2


img = cv2.imread("testimg.jpg")
img = cv2.resize(img, (int(img.shape[1] * 0.5), int(img.shape[0] * 0.5)))
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
cv2.imshow('gray', gray)
Python+OpenCV+paddleocr基于传统图像处理技术实现车牌识别_第2张图片 灰度图

        2. 顶帽运算

        图像顶帽(或图像礼帽)运算是原始图像减去图像开运算的结果,得到图像的噪声。

# 创建一个17*17矩阵内核
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (17, 17))
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, kernel)
cv2.imshow('tophat', tophat)
Python+OpenCV+paddleocr基于传统图像处理技术实现车牌识别_第3张图片 图像噪声

        3. Sobel算子提取y方向边缘

        使用Sobel算子对字符进行y方向提取,经测试y方向的提取效果优于x方向。

y = cv2.Sobel(tophat, cv2.CV_16S, 1, 0)
absY = cv2.convertScaleAbs(y)
cv2.imshow('absY', absY)
Python+OpenCV+paddleocr基于传统图像处理技术实现车牌识别_第4张图片 y方向边缘

        4. 自适应二值化

        将灰度图像二值化,把灰度小于75的像素置为0,大于等于75的像素置为255。

ret, binary = cv2.threshold(absY, 75, 255, cv2.THRESH_BINARY)
cv2.imshow('binary', binary)
Python+OpenCV+paddleocr基于传统图像处理技术实现车牌识别_第5张图片 二值图

        5. 开运算分割(纵向去噪,分隔)

        使用形状为(1,15)的矩形kernel对图像进行Y方向开运算,将图像先腐蚀后膨胀消除图像中的小面积白点。

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 15))
Open = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
cv2.imshow('Open', Open)
Python+OpenCV+paddleocr基于传统图像处理技术实现车牌识别_第6张图片 开运算处理后的图像

        6. 闭运算合并

        使用形状为(41,15)的矩形kernel对图像进行偏 X方向的闭运算,将图像进行X方向的融合找出车牌区域

kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (41, 15))
close = cv2.morphologyEx(Open, cv2.MORPH_CLOSE, kernel)
cv2.imshow('close', close)
Python+OpenCV+paddleocr基于传统图像处理技术实现车牌识别_第7张图片 闭运算后的图像

        7. 膨胀/腐蚀

        再次使用特定大小的kernel对图像进行膨胀,腐蚀操作去噪,得到车牌区域。

# 中远距离车牌识别
kernel_x = cv2.getStructuringElement(cv2.MORPH_RECT, (25, 7))
kernel_y = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 11))

# 7-1、腐蚀、膨胀(去噪)
erode_y = cv2.morphologyEx(close, cv2.MORPH_ERODE, kernel_y)
cv2.imshow('erode_y', erode_y)
dilate_y = cv2.morphologyEx(erode_y, cv2.MORPH_DILATE, kernel_y)
cv2.imshow('dilate_y', dilate_y)

# 7-2、膨胀、腐蚀(连接)(二次缝合)
dilate_x = cv2.morphologyEx(dilate_y, cv2.MORPH_DILATE, kernel_x)
cv2.imshow('dilate_x', dilate_x)
erode_x = cv2.morphologyEx(dilate_x, cv2.MORPH_ERODE, kernel_x)
cv2.imshow('erode_x', erode_x)
Python+OpenCV+paddleocr基于传统图像处理技术实现车牌识别_第8张图片 腐蚀膨胀后的图像

        8. 腐蚀、膨胀:去噪

        再次使用特定大小的kernel对图像进行膨胀,腐蚀操作去除噪声保留车牌区域。

kernel_e = cv2.getStructuringElement(cv2.MORPH_RECT, (25, 9))
erode = cv2.morphologyEx(erode_x, cv2.MORPH_ERODE, kernel_e)
cv2.imshow('erode', erode)
kernel_d = cv2.getStructuringElement(cv2.MORPH_RECT, (25, 11))
dilate = cv2.morphologyEx(erode, cv2.MORPH_DILATE, kernel_d)
cv2.imshow('dilate', dilate)
Python+OpenCV+paddleocr基于传统图像处理技术实现车牌识别_第9张图片 去噪后的图像

        9. 获取外轮廓

        对二值图进行轮廓提取。

img_copy = img.copy()

# 9-1、得到轮廓
contours, hierarchy = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

# 9-2、画出轮廓并显示
cv2.drawContours(img_copy, contours, -1, (255, 0, 255), 2)

cv2.imshow('Contours', img_copy)
Python+OpenCV+paddleocr基于传统图像处理技术实现车牌识别_第10张图片 可视化检测到的轮廓

        10. 遍历所有轮廓,找到车牌轮廓

        使用矩形拟合所有轮廓,将满足:高*3<宽<高*7的矩形轮廓表示的ROI作为车牌区域返回。

count = 0
for contour in contours:
    area = cv2.contourArea(contour)  # 计算轮廓内区域的面积
    # 得到矩形区域:左顶点坐标、宽和高
    x, y, w, h = cv2.boundingRect(contour)  # 获取坐标值和宽度、高度

    # 判断宽高比例、面积,截取符合图片
    if h*3 < w < h*7 and area > 1000:
        print(count)
        print(f"CornerNum:{CornerNum},area:{area}")
        # 截取车牌并显示
        print(x, y, w, h)
        ROI = img[(y - 5):(y + h + 5), (x - 5):(x + w + 5)]  # 高,宽
        try:
            # cv2.imshow('license plate%d' % count, ROI)
            count += 1
            #cv2.imwrite('car_licenses/img%d-%d.jpg' % (count, i), img)
            return ROI

        except:
            print("ROI提取出错!")
            return
            pass
提取出的车牌图像

三、OCR-识别文字

        OCR字符识别需要用到OCR库,我首先尝试过使用Tesseract来进行OCR识别,但是效果不尽人意,之后尝试使用百度飞桨的OCR库paddleocr进行识别,效果符合预期。若想尝试使用Tesseract,可以按照这篇文章进行安装使用:

Python+OpenCV+Tesseract实现OCR字符识别_Thomas_221126的博客-CSDN博客本文简要介绍了如何使用Python+OpenCV+Tesseract实现OCR字符识别https://blog.csdn.net/qq_54827663/article/details/128051162        这里我使用paddleocr进行识别,首先,使用pip安装paddleocr库:

pip install paddleocr

        若安装缓慢可以换源安装:

pip install paddleocr -i https://pypi.douban.com/simple

        安装完成后就可以进行OCR识别了,Paddleocr目前支持的多语言语种可以通过修改lang参数进行切换,例如`ch`, `en`, `fr`, `german`, `korean`, `japan`,支持的语种与缩写可在此网页查看:

PaddleOCR/multi_languages.md at release/2.4 · PaddlePaddle/PaddleOCR (github.com)https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.4/doc/doc_ch/multi_languages.md        使用PaddleOCR时默认会打印出debug信息,可输入参数show_log=False关闭打印debug信息,更多的参数说明可查看此网页:

doc/doc_ch/whl.md · PaddlePaddle/PaddleOCR - Gitee.comhttps://gitee.com/paddlepaddle/PaddleOCR/blob/release/2.0/doc/doc_ch/whl.md#%E5%8F%82%E6%95%B0%E8%AF%B4%E6%98%8E

from paddleocr import PaddleOCR


# Paddleocr目前支持的多语言语种可以通过修改lang参数进行切换
# 例如`ch`, `en`, `fr`, `german`, `korean`, `japan`
ocr = PaddleOCR(use_angle_cls=False, use_gpu=False,
                lang="ch", show_log=False)  # need to run only once to download and load model into memory

if img is None:
    print("没有提取到车牌")
    exit()

ocr_text = ocr.ocr(img, cls=False)
for line in ocr_text:
    number_plate = line[-1][-1][0]
    print("车牌:", end="")
    print(number_plate)
识别结果

四、优点、不足与改进方向

        1.优点

        与使用目标识别算法相比,使用传统图像处理技术来提取车牌所需的算力更小、开发成本更低、对硬件设备的要求更低,同时具有可解释性与一定的泛化能力和鲁棒性。

        2.不足 

        使用传统图像处理技术来提取车牌时,其特征提取主要依赖人工设计的提取器,且受到图像的光照、图像中车牌角度、车牌所占面积等因素影响,泛化能力及鲁棒性较差。

        3.改进方向

        本文只使用了简单的传统图像处理技术,导致算法极易受到各种因素的影响,可以尝试使用更专业高级的图像处理技术来提取特征并尽力减小各种因素的影响,以进一步提高算法的泛化能力与鲁棒性,达到实际可用的程度。

五、完整代码

import cv2
from paddleocr import PaddleOCR



def Morph_Distinguish(img):
    # 1、转灰度图
    gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
    # cv2.imshow('gray', gray)

    # 2、顶帽运算
    # 创建一个17*17矩阵内核
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (17, 17))
    # cv2.morphologyEx:形态学操作,将腐蚀膨胀结合使用
    # cv2.MORPH_TOPHAT :顶帽操作(原图像-开运算结果:突出灰度中亮的区域)
    # tophat = cv2.morphologyEx(gray, cv2.MORPH_BLACKHAT, kernel)
    tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, kernel)
    # cv2.imshow('tophat', tophat)

    # 3、Sobel算子提取y方向边缘(揉成一坨)
    y = cv2.Sobel(tophat, cv2.CV_16S, 1, 0)
    absY = cv2.convertScaleAbs(y)
    # cv2.imshow('absY', absY)
    # cv2.waitKey(0)


    # 4、自适应二值化(阈值自己可调)
    ret, binary = cv2.threshold(absY, 75, 255, cv2.THRESH_BINARY)
    # cv2.imshow('binary', binary)
    # cv2.waitKey(0)

    # 5、开运算分割(纵向去噪,分隔)
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 15))
    Open = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
    # cv2.imshow('Open', Open)
    # cv2.waitKey(0)

    # 6、闭运算合并,把图像闭合、揉团,使图像区域化,便于找到车牌区域,进而得到轮廓
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (41, 15))
    close = cv2.morphologyEx(Open, cv2.MORPH_CLOSE, kernel)
    # cv2.imshow('close', close)
    # cv2.waitKey(0)

    # 7、膨胀/腐蚀(去噪得到车牌区域)
    # 中远距离车牌识别
    kernel_x = cv2.getStructuringElement(cv2.MORPH_RECT, (25, 7))
    kernel_y = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 11))
    # 近距离车牌识别
    # kernel_x = cv2.getStructuringElement(cv2.MORPH_RECT, (79, 15))
    # kernel_y = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 31))
    # 7-1、腐蚀、膨胀(去噪)
    erode_y = cv2.morphologyEx(close, cv2.MORPH_ERODE, kernel_y)
    # cv2.imshow('erode_y', erode_y)
    dilate_y = cv2.morphologyEx(erode_y, cv2.MORPH_DILATE, kernel_y)
    # cv2.imshow('dilate_y', dilate_y)
    # 7-2、膨胀、腐蚀(连接)(二次缝合)
    dilate_x = cv2.morphologyEx(dilate_y, cv2.MORPH_DILATE, kernel_x)
    # cv2.imshow('dilate_x', dilate_x)
    erode_x = cv2.morphologyEx(dilate_x, cv2.MORPH_ERODE, kernel_x)
    # cv2.imshow('erode_x', erode_x)
    # cv2.waitKey(0)

    # 8、腐蚀、膨胀:去噪
    kernel_e = cv2.getStructuringElement(cv2.MORPH_RECT, (25, 9))
    erode = cv2.morphologyEx(erode_x, cv2.MORPH_ERODE, kernel_e)
    # cv2.imshow('erode', erode)
    kernel_d = cv2.getStructuringElement(cv2.MORPH_RECT, (25, 11))
    dilate = cv2.morphologyEx(erode, cv2.MORPH_DILATE, kernel_d)
    # cv2.imshow('dilate', dilate)
    # cv2.waitKey(0)

    # 9、获取外轮廓
    img_copy = img.copy()

    # 9-1、得到轮廓
    contours, hierarchy = cv2.findContours(dilate, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    # 9-2、画出轮廓并显示
    cv2.drawContours(img_copy, contours, -1, (255, 0, 255), 2)

    # cv2.imshow('Contours', img_copy)
    # cv2.waitKey(0)


    # 10、遍历所有轮廓,找到车牌轮廓
    count = 0
    for contour in contours:
        area = cv2.contourArea(contour)  # 计算轮廓内区域的面积
        # 得到矩形区域:左顶点坐标、宽和高
        x, y, w, h = cv2.boundingRect(contour)  # 获取坐标值和宽度、高度

        # 判断宽高比例、面积,截取符合图片
        if h*3 < w < h*7 and area > 1000:
            # print(count)
            # print(f"CornerNum:{CornerNum},area:{area}")
            # 截取车牌并显示
            # print(x, y, w, h)
            ROI = img[(y - 5):(y + h + 5), (x - 5):(x + w + 5)]  # 高,宽
            try:
                # cv2.imshow('license plate%d' % count, ROI)
                count += 1
                #cv2.imwrite('car_licenses/img%d-%d.jpg' % (count, i), img)
                return ROI

            except:
                print("ROI提取出错!")
                return
                pass


if __name__ == '__main__':
    # Paddleocr目前支持的多语言语种可以通过修改lang参数进行切换
    # 例如`ch`, `en`, `fr`, `german`, `korean`, `japan`
    ocr = PaddleOCR(use_angle_cls=False, use_gpu=False,
                    lang="ch", show_log=False)  # need to run only once to download and load model into memory

    img = cv2.imread("testimg.jpg")
    img = cv2.resize(img, (int(img.shape[1] * 0.5), int(img.shape[0] * 0.5)))

    img = Morph_Distinguish(img.copy())  # 获取车牌ROI

    if img is None:
        print("没有提取到车牌")
        exit()

    ocr_text = ocr.ocr(img, cls=False)
    for line in ocr_text:
        number_plate = line[-1][-1][0]
        print("车牌:", end="")
        print(number_plate)

    cv2.imshow("img", img)
    cv2.waitKey(0)

你可能感兴趣的:(opencv,图像处理,计算机视觉,python)