计算机视觉:使用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 cv2img = 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_maxhist_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 = approxbreak
# 如果找到了四边形
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 PaddleOCRdef 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_maxhist_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_colorimg = 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 = approxbreak
# 如果找到了四边形
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])

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Flutter 中在单个屏幕上实现多个列表

今天&#xff0c;我将提供一个实际的示例&#xff0c;演示如何在单个页面上实现多个列表&#xff0c;这些列表可以水平排列、网格格式、垂直排列&#xff0c;甚至是这些常用布局的组合。 下面是要做的&#xff1a; 实现 让我们从创建一个包含产品所有属性的产品模型开始。 …

ZJU Beamer学习手册(二)

ZJU Beamer学习手册基于 Overleaf 的 ZJU Beamer模板 进行解读&#xff0c;本文则基于该模版进行进一步修改。 参考文献 首先在frame文件夹中增加reference.tex文件&#xff0c;文件内容如下。这段代码对参考文献的引用进行了预处理。 \usepackage[backendbiber]{biblatex} \…

【机器学习】划分训练集和测试集的方法

在机器学习中&#xff0c;我们的模型建立完成后&#xff0c;通常要根据评估指标来对模型进行评估&#xff0c;以此来判断模型的可用性。而评估指标主要的目的是让模型在未知数据上的预测能力最好。因此&#xff0c;我们在模型训练之前&#xff0c;要对训练集和测试集进行划分。…

【Python数据结构与算法】——(线性结构)精选好题分享,不挂科必看系列

&#x1f308;个人主页: Aileen_0v0&#x1f525;系列专栏:<<Python数据结构与算法专栏>>&#x1f4ab;个人格言:"没有罗马,那就自己创造罗马~" 时间复杂度大小比较 1.time complexity of algorithm A is O(n^3) while algorithm B is O(2^n). Which o…

大数据-之LibrA数据库系统告警处理(ALM-12053 主机文件句柄使用率超过阈值)

告警解释 系统每30秒周期性检测主机文件句柄使用率&#xff0c;并把实际使用率和阈值&#xff08;系统默认阈值80%&#xff09;进行比较&#xff0c;当检测到主机文件句柄使用率连续多次&#xff08;默认值为5&#xff09;超过阈值时产生该告警。 用户可通过“系统设置 > …

创新研报|新业务发展是CEO推动企业增长的必要选择 – Mckinsey研究

&#x1f50d;&#x1f4c8; 创新研究报告 |新业务拓展&#xff1a;CEO推动企业成长的必然选择 – 麦肯锡研究 &#x1f525;&#x1f4bc; 所有执行长和商界领袖请注意&#xff01;您是否正在寻找为您的组织释放成长机会的钥匙&#xff1f; &#x1f310; 麦肯锡最近的一份研究…

股东入股可用的出资形式主要有哪些

股东入股&#xff0c;可用的出资形式主要包括货币以及实物、知识产权、土地使用权等可以用货币估价并可以依法转让的非货币财产。 第一&#xff0c;货币。设立公司必然需要一定数量的流动资金。以支付创建公司时的开支和启动公司运营。因此&#xff0c;股东可以用货币出资。 第…

fully_connected与linear

1、tf.contrib.layers.fully_connected与tf.contrib.layers.linear的区别 tf.contrib.layers.fully_connected与tf.contrib.layers.linear实现一致&#xff0c;使用tf.contrib.layers.fully_connected一般将激活函数选项至None,因为线性变换不需要非线性变换&#xff0c;与此同…

自学嵌入式,已经会用stm32做各种小东西了

自学嵌入式&#xff0c;已经会用stm32做各种小东西了 1、stm32 工程中&#xff0c;定义一个变量&#xff0c;记录复位次数&#xff0c;即复位一次变量加一。要求不许用备份寄存器和 flash 保存信息。本题只讨论不断电热启动情况&#xff0c;至于冷启动&#xff0c;不在此讨论。…

【MATLAB源码-第80期】基于蚯蚓优化算法(EOA)的无人机三维路径规划,输出做短路径图和适应度曲线

操作环境&#xff1a; MATLAB 2022a 1、算法描述 蚯蚓优化算法&#xff08;Earthworm Optimisation Algorithm, EOA&#xff09;是一种启发式算法&#xff0c;灵感来源于蚯蚓在自然界中的行为模式。蚯蚓优化算法主要模仿了蚯蚓在寻找食物和逃避天敌时的行为策略。以下是蚯蚓…

【论文阅读】基于隐蔽带宽的汽车控制网络鲁棒认证(二)

文章目录 第三章 识别CAN中的隐藏带宽信道3.1 隐蔽带宽vs.隐藏带宽3.1.1 隐蔽通道3.1.2 隐藏带宽通道 3.2 通道属性3.3 CAN隐藏带宽信道3.3.1 CAN帧ID字段3.3.2 CAN帧数据字段3.3.3 帧错误检测领域3.3.4 时间通道3.3.5 混合通道 3.4 构建信道带宽公式3.5通道矩阵3.6 结论 第四章…

视频集中存储/云存储平台EasyCVR级联下级平台的详细步骤

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

Linux CentOS 8(MariaDB概述)

Linux CentOS 8&#xff08;MariaDB概述&#xff09; 目录 一、项目描述二、相关知识2.1 数据库的基本介绍2.2 数据库的分类介绍 三、项目分析3.1 安装并启动 MariaDB3.2 登录 MariaDB 数据库3.3 提高 MariaDB 安装安全性 一、项目描述 Jan16 公司为满足部门之间数据共享、减少…

前台页面从数据库中获取下拉框值

后端&#xff1a;查询所有信息 前台&#xff1a;elementUI <el-select v-model"searchData.stationName" clearable> <el-option :label"item.stationName" :value"item.stationName" v-for"item in stationNameList&quo…

GoLong的学习之路,进阶,标准库之并发(context)补充并发三部曲,你真的明白context吗?

其实对于&#xff0c;context来说&#xff0c;如果只是用来做并发处理就有些不太合适。因为对于golang来说&#xff0c;context应用场景不仅在并发有用&#xff0c;并且在网络链接&#xff0c;http处理&#xff0c;gorm中都有体现。但是其实&#xff0c;本质来说。以上这些场景…

【Java 进阶篇】JQuery 事件绑定:`on` 与 `off` 的奇妙舞曲

在前端开发的舞台上&#xff0c;用户与页面的互动是一场精彩的表演。而 JQuery&#xff0c;作为 JavaScript 的一种封装库&#xff0c;为这场表演提供了更为便捷和优雅的事件绑定方式。其中&#xff0c;on 和 off 两位主角&#xff0c;正是这场奇妙舞曲中的核心演员。在这篇博客…

store下的getter.js什么作用

在 Vue.js 应用中&#xff0c;Vuex 是一种用于集中管理应用状态的状态管理工具。Vuex 中的 getters 允许你在获取 state 中的数据时进行一些计算或处理。通常&#xff0c;getters 可以用于从 store 中获取派生的状态&#xff0c;类似于计算属性。 在 Vuex 中&#xff0c;gette…

小米路由器AX1800降级后的SSH登录和关墙等命令

​​​​​​​​​​​​​​​​​​​​​http://192.168.31.1/cgi-bin/luci/;stok429aaa9c7f05ee7d0816a00257b8414a/api/misystem/set_config_iotdev?bssidXiaomi&user_idlongdike&ssid-h%3B%20nvram%20set%20ssh_en%3D1%3B%20nvram%20commit%3B%20sed%20-i%20s%…

云原生是整个信息化行业的未来,一文彻底搞懂云原生

云原生这个词来自英语的Cloud Native的翻译&#xff0c;云原生是已经存多年在术语&#xff0c;真正开始获得关注的是在2015年到2016年。 这归因于这几年逐渐发布的Docker的兴起。 会有越来越多的企业和组织开始关注到它&#xff0c;并把他们的工作负载运行在云端的益处。无论是…

Flask 接口

目录 前言 代码实现 简单接口实现 执行其它程序接口 携带参数访问接口 前言 有时候会想着开个一个接口来访问试试&#xff0c;这里就给出一个基础接口代码示例 代码实现 导入Flask模块&#xff0c;没安装Flask 模块需要进行 安装&#xff1a;pip install flask 使用镜…