计算机视觉cv2入门之车牌号码识别

    前边我们已经讲解了使用cv2进行图像预处理与边缘检测等方面的知识,这里我们以车牌号码识别这一案例来实操一下。

大致思路

        车牌号码识别的大致流程可以分为这三步:图像预处理-寻找车牌轮廓-车牌OCR识别

接下来我们按照这三步来进行讲解。

图像预处理

首先,在网上随便找一张车牌照:

读取图像 

#读取原始图像
import cv2
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
src_path=r'LicensePlate.jpg'
src_image=cv2.imread(filename=src_path,flags=cv2.IMREAD_COLOR_RGB)
print(src_image.shape)
plt.title('原始图像')
plt.imshow(src_image)

        这里我使用matplotlib的imshow函数来显示图像,这样在jupyter环境中可以不打开任何弹窗直接显示图像,比较方便。

转为灰度图

#转为灰度图
gray_image=cv2.cvtColor(src=src_image,code=cv2.COLOR_RGB2GRAY)
plt.title('原始图像(灰度图)')
plt.imshow(gray_image,cmap='gray')

        将原始图像转化为灰度图是为了后续的检测等操作,在计算机视觉任务中,基本上所有的操作都是针对灰度图来进行的,灰度图是将原始图像的多个通道按照一定权重求和叠加而来,这样一来多通道变成了单通道(Gray=w_1*B+w_2*G+w_3*R),在计算量上也会比较友好。

 阈值化

#阈值化
thresh,binary_image=cv2.threshold(src=gray_image,thresh=128,maxval=255,type=cv2.THRESH_OTSU+cv2.THRESH_BINARY)
plt.imshow(binary_image,cmap='gray')

        阈值化是为了后续的边缘检测,通常在边缘检测前都需要对图像进行阈值化操作,这样识别出来的边缘相对准确。这里阈值化我们使用cv2.THRESH+cv2.THRESH-OTSU方法来自动对图像进行二值化阈值分割。 

边缘检测

#canny边缘检测
edges=cv2.Canny(image=binary_image,threshold1=0.5*thresh,threshold2=thresh,apertureSize=5,L2gradient=True)
plt.imshow(edges,cmap='gray')

        边缘检测是为了初步提取出车牌的轮廓,便于后续的轮廓查找。常用的边缘检测算法有Canny、Sobel、Prewitt等,其中Canny算法具有较高的准确性和鲁棒性,因此在本系统中采用Canny算法进行边缘检测。不太熟悉边缘检测的小伙伴可以去看看我的往期文章:

https://blog.csdn.net/weixin_73953650/article/details/146284620?sharetype=blogdetail&sharerId=146284620&sharerefer=PC&sharesource=weixin_73953650&spm=1011.2480.3001.8118https://blog.csdn.net/weixin_73953650/article/details/146284620?sharetype=blogdetail&sharerId=146284620&sharerefer=PC&sharesource=weixin_73953650&spm=1011.2480.3001.8118

 车牌轮廓查找

#寻找矩形区域轮廓
contours,hiercahy=cv2.findContours(edges,mode=cv2.RETR_TREE,method=cv2.CHAIN_APPROX_SIMPLE)
contours=sorted(contours,key=cv2.contourArea,reverse=True)[:10]
rectangle=None
for point in contours:peri=cv2.arcLength(point,True)polygons=cv2.approxPolyDP(curve=point,epsilon=0.018*peri,closed=True)if len(polygons)==4:rectangle=polygonsplateArea=pointbreak
gray_image_copy=gray_image.copy()
src_image_copy=src_image.copy()
cv2.drawContours(image=src_image_copy,contours=[rectangle],contourIdx=0,color=(255,0,0),thickness=5)
cv2.drawContours(image=gray_image_copy,contours=[rectangle],contourIdx=0,color=255,thickness=5)
figure=plt.figure(figsize=(10,10),dpi=100)
plt.subplot(1,2,1),plt.imshow(src_image_copy),plt.title('车牌定位结果(原始图像)')
plt.subplot(1,2,2),plt.imshow(gray_image_copy,cmap='gray'),plt.title('车牌定位结果(灰度图)')

 

 

         查找轮廓时我们通常使用findContours函数来进行查找(返回值为所有可能的轮廓点contours以及这些点之间的拓扑结构hierachy),考虑到车牌是矩形区域,因此我们可以在查找到的轮廓点中使用cv2.approxPolyDP函数来对查找到的轮廓进行多边形拟合(返回值为各个顶点的坐标构成的列表)只要拟合出的多边形顶点个数为4,那么必然是车牌位置。

       然后,我们再使用cv2.drawContours函数将其在原始图像中标记出来即可。

车牌分割

# #分割提取车牌
x=[location[0][0] for location in plateArea]
y=[location[0][1] for location in plateArea]
Licenseplate=gray_image[min(y):max(y),min(x):max(x)]#切片图像
plt.imshow(Licenseplate,cmap='gray')
cv2.imwrite('Plate.jpg',Licenseplate)

 

 

        将车牌从原始图像中分割出来的思路也很简单,就是根据我们查找到的轮廓点,来查找其在图像中的位置。PlatArea是矩形车牌轮廓点构成的列表,其内部为各个点的坐标,其中对于任意一点location来说,location[0][0]表示x坐标,location[0][1]表示y坐标。那么,我们只需找到所有x坐标中的最小值与最大值,y坐标中的最小值与最大值,即可确定这个矩形区域在原图像中的范围。 

 最后,我们还需将这个车牌号码保存一下以便后续的字符识别

OCR识别

        考虑到车牌是标准的印刷体,这里我们使用现成的OCR字符识别库,这里我使用的是ddddocr

获取方式

pip install ddddocr

OCR识别

#使用ddddocr进行光学识别
import ddddocr
ocr=ddddocr.DdddOcr(show_ad=False,beta=True)
image=open('Plate.jpg','rb')
answer=ocr.classification(image.read())
image.close()
print(f'车牌号为:{answer.upper()}')
plt.imshow(src_image,cmap='gray')
plt.text(x=src_image.shape[1]//4,y=src_image.shape[0]/2,s=f'车牌号为:{answer.upper()}',size=20,color='red')

 

        使用ddddocr时需要传入的图像数据是Bytes类型,因此我们使用open(‘.jpg’,'rb').read()语句即可实现读取图像的bytes数据,最后我们再将得到的结果其标注在原始图像上。 

 

完整代码

#读取原始图像
import cv2
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
src_path=r'LicensePlate.jpg'
src_image=cv2.imread(filename=src_path,flags=cv2.IMREAD_COLOR_RGB)
print(src_image.shape)
plt.title('原始图像')
plt.imshow(src_image)
#转为灰度图
gray_image=cv2.cvtColor(src=src_image,code=cv2.COLOR_RGB2GRAY)
plt.title('原始图像(灰度图)')
plt.imshow(gray_image,cmap='gray')
#阈值化
thresh,binary_image=cv2.threshold(src=gray_image,thresh=128,maxval=255,type=cv2.THRESH_OTSU+cv2.THRESH_BINARY)
plt.imshow(binary_image,cmap='gray')
#canny边缘检测
edges=cv2.Canny(image=binary_image,threshold1=0.5*thresh,threshold2=thresh,apertureSize=5,L2gradient=True)
plt.imshow(edges,cmap='gray')
#寻找矩形区域轮廓
contours,hiercahy=cv2.findContours(edges,mode=cv2.RETR_TREE,method=cv2.CHAIN_APPROX_SIMPLE)
contours=sorted(contours,key=cv2.contourArea,reverse=True)[:10]
rectangle=None
for point in contours:peri=cv2.arcLength(point,True)polygons=cv2.approxPolyDP(curve=point,epsilon=0.018*peri,closed=True)if len(polygons)==4:rectangle=polygonsplateArea=pointbreak
gray_image_copy=gray_image.copy()
src_image_copy=src_image.copy()
cv2.drawContours(image=src_image_copy,contours=[rectangle],contourIdx=0,color=(255,0,0),thickness=5)
cv2.drawContours(image=gray_image_copy,contours=[rectangle],contourIdx=0,color=255,thickness=5)
figure=plt.figure(figsize=(10,10),dpi=100)
plt.subplot(1,2,1),plt.imshow(src_image_copy),plt.title('车牌定位结果(原始图像)')
plt.subplot(1,2,2),plt.imshow(gray_image_copy,cmap='gray'),plt.title('车牌定位结果(灰度图)')
# #分割提取车牌
x=[location[0][0] for location in plateArea]
y=[location[0][1] for location in plateArea]
Licenseplate=gray_image[min(y):max(y),min(x):max(x)]#切片图像
plt.imshow(Licenseplate,cmap='gray')
cv2.imwrite('Plate.jpg',Licenseplate)
#使用ddddocr进行光学识别
import ddddocr
ocr=ddddocr.DdddOcr(show_ad=False,beta=True)
image=open('Plate.jpg','rb')
answer=ocr.classification(image.read())
image.close()
print(f'车牌号为:{answer.upper()}')
plt.imshow(src_image,cmap='gray')
plt.text(x=src_image.shape[1]//4,y=src_image.shape[0]/2,s=f'车牌号为:{answer.upper()}',size=20,color='red')

总结 

 

        以上便是计算机视觉cv2入门之车牌号码识别的所有内容,如果本文对你有用,还劳驾各位一键三连支持一下博主。

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

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

相关文章

Spring Boot 3 + SpringDoc:打造接口文档

1、背景公司 新项目使用SpringBoot3.0以上构建,其中需要对外输出接口文档。接口文档一方面给到前端调试,另一方面给到测试使用。 2、SpringDoc 是什么? SpringDoc 是一个基于 Spring Boot 项目的库,能够自动根据项目中的配置、…

多路由器通过三层交换机互相通讯(单臂路由+静态路由+默认路由版),通过三层交换机让pc端相互通讯

多路由器通过三层交换机互相通讯(单臂路由静态路由默认路由版) 先实现各个小框框里能够互通 哇咔 交换机1(二层交换机,可看配置单臂路由的文章) Switch>en Switch#conf t Switch(config)#int f0/1 Switch(config-if)#switchport access…

通过gird布局实现div的响应式分布排列

目标&#xff1a;实现对于固定宽度的div盒子在页面中自适应排布&#xff0c;并且最后一行的div盒子可以与前面的盒子对齐。 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" con…

AI Agent系列(九) -Data Agent(数据分析智能体)

AI Agent系列【九】 前言一、Data Agent场景二、Data Agent核心因素2.1 数据源2.2 大模型2.3 应用及可视化 三、Data Agent应用场景 前言 Data Agent就是在大模型基础上构建一个数据分析的智能体&#xff0c;是一种基于人工智能技术&#xff0c;特别是大模型技术的数据分析智…

AUTOSAR图解==>AUTOSAR_SWS_DefaultErrorTracer

AUTOSAR 默认错误追踪器(Default Error Tracer)详细分析 基于AUTOSAR 4.4.0规范的深入解析 目录 概述 DET模块的作用DET模块的定位 架构设计 模块架构接口设计 状态与行为 状态转换错误报告流程 API与数据结构 API概览数据类型定义 配置与扩展 模块配置回调机制 总结 1. 概述 …

Linux,redis群集模式,主从复制,读写分离

redis的群集模式 主从模式 &#xff08;单项复制&#xff0c;主复制到从&#xff09; 一主两从 一台主机上的一主两从 需要修改三个配置文件 主要端口不一样 redis-8001.conf redis-8002.conf redis-8003.conf 哨兵模式 分布式集群模式 redis 安装部署 1&#xff0c;下载…

前端面试题---GET跟POST的区别(Ajax)

GET 和 POST 是两种 HTTP 请求方式&#xff0c;它们在传输数据的方式和所需空间上有一些重要区别&#xff1a; ✅ 一句话概括&#xff1a; GET 数据放在 URL 中&#xff0c;受限较多&#xff1b;POST 数据放在请求体中&#xff0c;空间更大更安全。 &#x1f4e6; 1. 所需空间…

WPF 从Main()方法启动

1.去掉App.xaml StartupUri“MainWindow.xaml” 只会让App.g.cs 不生成这行代码&#xff0c;但是还是会生成的App.g.cs文件中生成Main方法 this.StartupUri new System.Uri("MainWindow.xaml", System.UriKind.Relative);默认的App.xaml的生成操作是 应用程序定义…

ocr-身份证正反面识别

在阿里云官网&#xff0c;申请一个token [阿里官方]身份证OCR文字识别_API专区_云市场-阿里云 (aliyun.com) 观察一下post请求body部分json字符串&#xff0c;我们根据这个创建一个java对象 先默认是人像面 public class IdentityBody {public String image;class configure…

通过GO后端项目实践理解DDD架构

最近在工作过程中重构的项目要求使用DDD架构&#xff0c;在网上查询资料发现教程五花八门&#xff0c;并且大部分内容都是长篇的概念讲解&#xff0c;晦涩难懂&#xff0c;笔者看了一些github上入门的使用DDD的GO项目&#xff0c;并结合自己开发中的经验&#xff0c;谈谈自己对…

LangGraph中预构件,creat_react_agent的实现流程

LangGraph Prebuilt Agent 流程图 本文档展示了LangGraph的prebuilt模块中Agent的实现流程&#xff0c;重点是create_react_agent函数构建的代理系统流程和结构。 ReAct Agent构建流程 #mermaid-svg-ubcEEuBeApApT624 {font-family:"trebuchet ms",verdana,arial,s…

贪心算法学习C++

1&#xff0c;跳跃游戏II 题目连接&#xff1a;45. 跳跃游戏 II - 力扣&#xff08;LeetCode&#xff09; 【题目描述】 在给定的一个nums数组中&#xff0c;nums[i]表示从当前i位置最多可以向后跳跃nums[i]个位置。问跳跃到最后 数组最后一个元素的最少跳跃次数&#xff1f;…