计算机视觉:使用opencv实现车牌识别

1 引言

汽车车牌识别(License Plate Recognition)是一个日常生活中的普遍应用,特别是在智能交通系统中,汽车牌照识别发挥了巨大的作用。汽车牌照的自动识别技术是把处理图像的方法与计算机的软件技术相连接在一起,以准确识别出车牌牌照的字符为目的,将识别出的数据传送至交通实时管理系统,以最终实现交通监管的功能。在车牌自动识别系统中,从汽车图像的获取到车牌字符处理是一个复杂的过程,主要分为四个阶段:图像获取、车牌定位、字符分割以及字符识别。目前关于车牌识别的算法有很多,本文基于opencv构建了车牌识别的整个流程,供大家学习参考。

1 车牌识别概述

1.1 opencv介绍

OpenCV的全称是:Open Source Computer Vision Library。OpenCV是一个基于开源发行的跨平台计算机视觉库,可以运行在Linux、Windows和Mac OS操作系统上。它轻量级而且高效——由一系列 C 函数和少量 C++ 类构成,同时提供了Python、Ruby、MATLAB等语言的接口,实现了图像处理和计算机视觉方面的很多通用算法。

1.2 车牌识别分解

车牌辨认的整个过程,可以拆解为以下三个步骤:

  • 车牌定位: 第一步是从轿车上检测车牌地点方位。本文将运用OpenCV中矩形的边框检测来找到车牌位置。
  • 字符切割:检测到车牌后,使用opencv将其裁剪并保存为新的图片,用于后续识别。
  • 字符辨认: 在新的图片运用光学字符识(OCR)技术,提取图片中的文字、字符、数字。

2 车牌识别的实现

2.1 车牌定位

我国的汽车牌照一般由七个字符和一个点组成,车牌字符的高度和宽度是固定的,分别为90mm和45mm,七个字符之间的距离也是固定的12mm,点分割符的直径是10mm,字符间的差异可能会引起字符间的距离变化。

在民用车牌中,字符的排列位置遵循以下规律:

  • 第一个字符通常是我国各省区的简称,用汉字表示;
  • 第二个字符通常是发证机关的代码号,最后五个字符由英文字母和数字组合而成,字母是二十四个大写字母(除去I和O这两个字母)的组合,数字用"0-9"之间的数字表示。

从图像处理角度看,汽车牌照有以下几个特征:

  • 第一个特征是是车牌的几何特征,即车牌形状统一为长宽高固定的矩形;
  • 第二个特征是车牌的灰度分布呈现出连续的波谷-波峰-波谷分布,这是因为我国车牌颜色单一,字符直线排列;
  • 第三个特征是车牌直方图呈现出双峰状的特点,即车牌直方图中可以看到双个波峰;
  • 第四个特征是车牌具有强边缘信息,这是因为车牌的字符相对集中在车牌的中心,而车牌边缘无字符,因此车牌的边缘信息感较强;
  • 第五个特征是车牌的字符颜色和车牌背景颜色对比鲜明。目前,我国国内的车牌大致可分为蓝底白字和黄底黑字,特殊用车采用白底黑字或黑底白字,有时辅以红色字体等。

为了简化处理,本次学习中只考虑蓝底白字的车牌。

2.1.1 图像加载与灰度化

import cv2

img = cv2.imread('../data/bmw01.jpg')

# 调整图片大小
img = cv2.resize(img, (1024, 800))

# 灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 显示效果
cv2.imshow('gray', gray)
cv2.waitKey(0)
cv2.destroyAllWindows()

显示结果如下:

2.1.2 双边滤波去除噪声

# 双边滤波
blf = cv2.bilateralFilter(gray, 13, 15, 15)
show_image('bilateralFilter', blf)

显示结果如下:

2.1.3 边缘检测

# 边缘检测
edged = cv2.Canny(blf, 30, 200)
show_image('canny', edged)

显示结果如下:

2.1.4 寻找车牌轮廓(四边形)

cv2.findContours说明:

  • opencv3.x
image, contours, hierarchy = cv.findContours(image, mode, method[, contours[, hierarchy[, offset]]])
  • opencv2.x和4.x
contours, hierarchy = cv.findContours(image, mode, method[, contours[, hierarchy[, offset]]])

OpenCV中HSV空间颜色对照表

提取图像区域的颜色

def reg_area_color(image):
    """找到原图像最多的颜色,当该颜色为红色或蓝色时返回该颜色的名称"""
    kernel = np.ones((35, 35), np.uint8)
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    # 以上为图像处理
    Open = cv2.morphologyEx(hsv, cv2.MORPH_OPEN, kernel)
    # 对Open图像的H通道进行直方图统计
    hist = cv2.calcHist([Open], [0], None, [180], [0, 180])
    # 找到直方图hist中列方向最大的点hist_max
    hist_max = np.where(hist == np.max(hist))

    # hist_max[0]为hist_max的行方向的值,即H的值,H在0~10为红色
    if 0 < hist_max[0] < 10:
        res_color = 'red'
    elif 100 < hist_max[0] < 124:  # H在100~124为蓝色
        res_color = 'blue'
    else:
        # H不在前两者之间跳出函数
        res_color = 'unknow'
    return res_color

寻找车牌轮廓:

# 寻找轮廓(图像矩阵,输出模式,近似方法)
contours, _ = cv2.findContours(edged.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 根据区域大小排序取前十
contours = sorted(contours, key=cv2.contourArea, reverse=True)[:10]
screenCnt = None
# 遍历轮廓,找到车牌轮廓
for c in contours:
    if cv2.contourArea(c) > 1024 * 768 * 0.05:
        continue

    # 计算轮廓周长(轮廓,是否闭合)
    peri = cv2.arcLength(c, True)
    # 折线化(轮廓,阈值(越小越接近曲线),是否闭合)返回折线顶点坐标
    approx = cv2.approxPolyDP(c, 0.018 * peri, True)
    # 获取四个顶点(即四边形, 左下/右下/右上/左上
    if len(approx) == 4:
        # [参数]左上角纵坐标:左下角纵坐标,左上角横坐标:右上角横坐标
        crop_image = img[approx[3][0][1]:approx[0][0][1], approx[3][0][0]:approx[2][0][0]]
        show_image('crop', crop_image)
        if 'blue' == reg_area_color(crop_image):
            screenCnt = approx
            break
# 如果找到了四边形
if screenCnt is not None:
    # 根据四个顶点坐标对img画线(图像矩阵,轮廓坐标集,轮廓索引,颜色,线条粗细)
    cv2.drawContours(img, [screenCnt], -1, (0, 0, 255), 3)

show_image('contour', img)

运行结果显示:

2.1.5 图像位运算进行遮罩

"""遮罩"""
# 创建一个灰度图一样大小的图像矩阵
mask = np.zeros(gray.shape, np.uint8)
# 将创建的图像矩阵的车牌区域画成白色
cv2.drawContours(mask, [screenCnt], 0, 255, -1, )
# 图像位运算进行遮罩
mask_image = cv2.bitwise_and(img, img, mask=mask)
show_image('mask_image', mask_image)

运行结果显示:

2.1.6 图像剪裁

"""图像剪裁"""
# 获取车牌区域的所有坐标点
(x, y) = np.where(mask == 255)
# 获取底部顶点坐标
(topx, topy) = (np.min(x), np.min(y))
# 获取底部坐标
(bottomx, bottomy,) = (np.max(x), np.max(y))
# 剪裁
Cropped = gray[topx:bottomx, topy:bottomy]

运行结果显示:

2.1.7 OCR字符识别

paddleocr是一款轻量型字符识别工具库,支持多语言识别,支持pip安装与自定义训练。

  • conda下工具类安装
pip install paddleocr -i https://mirror.baidu.com/pypi/simple 
pip install paddlepaddle -i https://mirror.baidu.com/pypi/simple 

代码实现:

"""OCR识别"""

# 使用CPU预加载,不用GPU
ocr = PaddleOCR(use_angle_cls=True, use_gpu=False, ocr_version='PP-OCRv3')
text = ocr.ocr(cropped, cls=True)
for t in text:
    print(t[0][1])

运行结果显示如下:

[2023/11/15 20:57:43] ppocr DEBUG: dt_boxes num : 1, elapsed : 0.016942501068115234
[2023/11/15 20:57:43] ppocr DEBUG: cls num  : 1, elapsed : 0.013955354690551758
[2023/11/15 20:57:43] ppocr DEBUG: rec_res num  : 1, elapsed : 0.12021970748901367
('苏A·0MR20', 0.8559348583221436)

2.2 完整代码实现

import cv2
import numpy as np
from paddleocr import PaddleOCR


def show_image(desc, image):
    cv2.imshow(desc, image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


def reg_area_color(image):
    """找到原图像最多的颜色,当该颜色为红色或蓝色时返回该颜色的名称"""
    kernel = np.ones((35, 35), np.uint8)
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    # 以上为图像处理
    Open = cv2.morphologyEx(hsv, cv2.MORPH_OPEN, kernel)
    # 对Open图像的H通道进行直方图统计
    hist = cv2.calcHist([Open], [0], None, [180], [0, 180])
    # 找到直方图hist中列方向最大的点hist_max
    hist_max = np.where(hist == np.max(hist))

    # hist_max[0]为hist_max的行方向的值,即H的值,H在0~10为红色
    if 0 < hist_max[0] < 10:
        res_color = 'red'
    elif 100 < hist_max[0] < 124:  # H在100~124为蓝色
        res_color = 'blue'
    else:
        # H不在前两者之间跳出函数
        res_color = 'unknow'
    return res_color


img = cv2.imread('../data/bmw01.jpg')

# 调整图片大小
img = cv2.resize(img, (1024, 768))

# 灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
show_image('gray', gray)

# 双边滤波
blf = cv2.bilateralFilter(gray, 13, 15, 15)
show_image('bilateralFilter', blf)

# 边缘检测
edged = cv2.Canny(blf, 30, 200)
show_image('canny', edged)

# 寻找轮廓(图像矩阵,输出模式,近似方法)
contours, _ = cv2.findContours(edged.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# 根据区域大小排序取前十
contours = sorted(contours, key=cv2.contourArea, reverse=True)[:10]
screenCnt = None
# 遍历轮廓,找到车牌轮廓
for c in contours:
    if cv2.contourArea(c) > 1024 * 768 * 0.05:
        continue

    # 计算轮廓周长(轮廓,是否闭合)
    peri = cv2.arcLength(c, True)
    # 折线化(轮廓,阈值(越小越接近曲线),是否闭合)返回折线顶点坐标
    approx = cv2.approxPolyDP(c, 0.018 * peri, True)
    # 获取四个顶点(即四边形, 左下/右下/右上/左上
    if len(approx) == 4:
        # [参数]左上角纵坐标:左下角纵坐标,左上角横坐标:右上角横坐标
        crop_image = img[approx[3][0][1]:approx[0][0][1], approx[3][0][0]:approx[2][0][0]]
        show_image('crop', crop_image)
        if 'blue' == reg_area_color(crop_image):
            screenCnt = approx
            break
# 如果找到了四边形
if screenCnt is not None:
    # 根据四个顶点坐标对img画线(图像矩阵,轮廓坐标集,轮廓索引,颜色,线条粗细)
    cv2.drawContours(img, [screenCnt], -1, (0, 0, 255), 3)
    show_image('contour', img)

"""遮罩"""
# 创建一个灰度图一样大小的图像矩阵
mask = np.zeros(gray.shape, np.uint8)
# 将创建的图像矩阵的车牌区域画成白色
cv2.drawContours(mask, [screenCnt], 0, 255, -1, )
# 图像位运算进行遮罩
mask_image = cv2.bitwise_and(img, img, mask=mask)
show_image('mask_image', mask_image)

"""图像剪裁"""
# 获取车牌区域的所有坐标点
(x, y) = np.where(mask == 255)
# 获取底部顶点坐标
(topx, topy) = (np.min(x), np.min(y))
# 获取底部坐标
(bottomx, bottomy,) = (np.max(x), np.max(y))
# 剪裁
cropped = gray[topx:bottomx, topy:bottomy]
show_image('cropped', cropped)

"""OCR识别"""
# 使用CPU预加载,不用GPU
ocr = PaddleOCR(use_angle_cls=True, use_gpu=False, ocr_version='PP-OCRv3')
text = ocr.ocr(cropped, cls=True)
for t in text:
    print(t[0][1])

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/149481.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

芯向未来|紫光展锐CEO任奇伟博士受邀主持ICCAD 2023高峰论坛

11月10日至11日&#xff0c;中国集成电路设计业2023年会暨广州集成电路产业创新发展高峰论坛&#xff08;ICCAD 2023&#xff09;在广州保利世贸博览馆召开&#xff0c;本届年会以“湾区有你&#xff0c;芯向未来”为主题&#xff0c;分开幕式、高峰论坛、7场专题研讨、产业展览…

全局代码规范配置 ( Eslint )

项目团队开发 为了保证统一的代码格式规范&#xff0c;可以借助两个插件以及 eslint 自由配置进行 首先需要在 vscode 安装 Eslint Prettier - Code formatter 安装所需依赖 pnpm install --save-dev eslint eslint-plugin-react eslint-plugin-react-hooks eslint…

球星马布里申请香港高才通计划落户香港拿身份!谈谈香港身份的好处!

球星马布里申请香港高才通计划落户香港拿身份&#xff01;谈谈香港身份的好处&#xff01; 据香港政府新闻网14日消息&#xff0c;前美国职业篮球联赛球员马布里&#xff0c;日前向香港人才服务办公室递交高端人才通行证计划的申请。香港劳工及福利局局长孙玉菡与他会面&#x…

黄金投资面对K线图有哪些好用的交易策略?

在现货黄金交易中&#xff0c;学会观察K线图能够帮助投资者进行市场分析&#xff0c;根据K线图呈现出来的市场走势制定交易策略&#xff0c;是技术分析的主要作用。在黄金买卖过程中掌握K线交易技巧能够提升理财效率&#xff0c;所以这也就成为了炒金者的必修课。 K线图是以交…

使用手机作为电脑的麦克风和摄像头外设

工具 Iriun Iriun 电脑端安装&#xff1a;Iriun Android: Iriun 4K Webcam for PC and Mac - Apps on Google Play Apple: Iriun Webcam for PC and Mac on the App Store 基础功能免费&#xff0c;普通使用足够了。 付费功能&#xff1a; 使用 这里有介绍&#xff1a…

中国人民大学与加拿大女王大学金融硕士——人生下半场,用实力为自己“撑腰”

人生如同一场漫长的旅程&#xff0c;每个人都在不断地前行&#xff0c;经历着种种的人生阶段。当我们迈入人生的下半场&#xff0c;我们不再是无知少年&#xff0c;而是逐渐成为社会的中坚力量。在这个阶段&#xff0c;我们不仅要面对更多的挑战和压力&#xff0c;还需要用实力…

VulnHub DC-6

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【python】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏…

【多线程面试题二十五】、说说你对AQS的理解

文章底部有个人公众号&#xff1a;热爱技术的小郑。主要分享开发知识、学习资料、毕业设计指导等。有兴趣的可以关注一下。为何分享&#xff1f; 踩过的坑没必要让别人在再踩&#xff0c;自己复盘也能加深记忆。利己利人、所谓双赢。 面试官&#xff1a;说说你对AQS的理解 参…

Facebook游戏出海营销指南

当谈到Facebook游戏出海营销时&#xff0c;有一些关键的策略和指南可以帮助你在国际市场上取得成功。下面是一个详细的指南&#xff0c;帮助你了解如何有效地推广和推出你的游戏。 1、了解目标市场 在开始出海营销之前&#xff0c;你需要对你的目标市场进行深入的研究。了解该…

11月15日星期三今日早报简报微语报早读

1、2023胡润女企业家榜出炉&#xff1a;郭得胜夫人邝肖卿首次成为中国女首富&#xff0c;龙湖吴亚军蝉联中国白手起家女首富&#xff1b; 2、叶剑英元帅夫人吴博逝世&#xff0c;享年106岁&#xff1b; 3、外交部&#xff1a;所谓“联合国军”是冷战产物&#xff0c;于法无据…

前端跨界面之间的通信解决方案

主要是这两个方案&#xff0c;其他的&#xff0c;还有 SharedWorker 、IndexedDB、WebSocket、Service Worker 如果是&#xff0c;父子嵌套 iframe 还可以使用 window.parent.postMessage(“需要传递的参数”, ‘*’) 1、localStorage 核心点 同源&#xff0c;不能跨域(协议、端…

Matter 协议详解

目录 1、Matter 协议发展 1.1、什么是Matter 1.2、Matter能做什么 2、整体介绍 3、架构介绍 3.1、Matter网络拓扑结构 3.2、标识符 3.2.1、Fabric引用和Fabric标识符 3.2.2、供应商标识符&#xff08;Vendor ID&#xff0c;VID&#xff09; 3.2.3、产品标识符&#x…

【vue实战项目】通用管理系统:api封装、404页

前言 本文为博主的vue实战小项目系列中的第三篇&#xff0c;很适合后端或者才入门的小伙伴看&#xff0c;一个前端项目从0到1的保姆级教学。前面的内容&#xff1a; 【vue实战项目】通用管理系统&#xff1a;登录页-CSDN博客 【vue实战项目】通用管理系统&#xff1a;封装to…

【VSCode】Visual Studio Code 配置简体中文环境教程

介绍 Visual Studio Code&#xff08;简称 VS Code&#xff09;是一款轻量级的代码编辑器&#xff0c;它支持多种编程语言&#xff0c;并且具有丰富的功能和插件扩展。如果你更喜欢使用简体中文界面&#xff0c;那么本教程将向你展示如何在 VS Code 中配置简体中文环境。 步骤…

Django之模版层

文章目录 模版语法传值模版语法传值特性模版语法标签语法格式if模板标签for模板标签with起别名 模版语法过滤器常用过滤器 自定义过滤器、标签、inclusion_tag自定义过滤器自定义标签自定义inclusion_tag 模版导入模版继承 模版语法传值 模板层三种语法{{}}:主要与数据值相关{%…

YOLO目标检测——树叶检测数据集下载分享【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;生物多样性研究、林业管理、环境监测和教育科研等方面数据集说明&#xff1a;树叶分类检测数据&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富&#xff0c;总共十个类别。标签说明&#xff1a;使用lableimg标注软件标注&#xff0c;标…

【文件读取/包含】任意文件读取漏洞 afr_2

1.1漏洞描述 漏洞名称任意文件读取漏洞 afr_2漏洞类型文件读取漏洞等级⭐⭐漏洞环境dockers攻击方式 1.2漏洞等级 高危 1.3影响版本 暂无 1.4漏洞复现 1.4.1.基础环境 靶场dockers工具BurpSuite 1.4.2.环境搭建 1.kali创建docker-compose.yml文件 touch docker-compose.ym…

d3dx9_39.dll丢失怎么修复?d3dx9_39.dll丢失的四种修复办法分享

d3dx9_39.dll是DirectX库中的一个重要组件&#xff0c;属于Microsoft Direct3D 9 API。它提供了许多用于创建和渲染3D图形的函数。DirectX是一套开发多媒体应用程序的API&#xff0c;广泛应用于游戏、视频和图形处理等领域。d3dx9_39.dll文件主要负责处理3D图形渲染、动画、光源…

【C++】多态的使用详解

本篇要分享的内容是多态&#xff0c;以下为本篇目录。 目录 1.多态的概念 2. 多态的定义及实现 3.虚函数 4.C11 override和final 4.1final关键字 4.2override关键字 5.抽象类 5.1抽象类的概念 5.2接口继承和实现继承 1.多态的概念 通俗来说&#xff0c;就是多种形态…

【C++】泛型编程 ② ( 函数模板与普通函数区别 )

文章目录 一、函数模板与普通函数区别1、函数模板与普通函数区别2、代码示例 - 函数模板与普通函数区别 一、函数模板与普通函数区别 1、函数模板与普通函数区别 函数模板与普通函数区别 : 主要区别在于它们能够处理的 数据类型数量 和 灵活性 ; 自动类型转换 : 函数模板 不允许…