基于 opencv+yolov8+easyocr的车牌追踪识别

(本项目所有代码打包至我的资源中,大家可在我的文章底部选择下载)

目录

需求

实现效果

学习视频

大致思路

代码实现

资源下载


需求

        通过车辆识别技术,识别视频中每个车辆及其车牌号,车辆应进行追踪,避免重复计数量。

实现效果

车牌识别

学习视频

使用 Python、Yolov8 和 EasyOCR 自动识别车牌 计算机视觉教程_哔哩哔哩_bilibili

大致思路

        通过 opencv 将视频转换为帧,对帧应用车辆识别模型,并使用 model.track 或者 sort 追踪器进行追踪,给每个车辆一个唯一的 id ,然后进行车牌识别,对每一帧识别到的车牌,通过几何判断是否位于某个车辆中,是则将该车牌分配给该车辆,否则说明车牌识别错误或车辆识别错误,不作考虑。将车牌分配好车辆后,对车牌进行裁剪,将裁剪好的车牌使用 opencv 技术转换为灰度值图片,再设置阈值转换为阈值灰白图像,然后使用 easyocr 或者 Paddleocr 等文字识别技术,对阈值黑白图像进行字符识别,最终得到车牌信息,然后将所有信息,包括车辆、车牌 box 信息、置信度、车辆 id ,车牌号等信息放入 csv 表格中,方面查看测试结果。

代码实现

引入模块:

import numpy as np
from ultralytics import YOLO
import cv2from util import *
# from sort.sort import *
# 跟踪器对象
# mot_tracker = Sort()

这里由于我的环境问题,下载不了使用 sort 的库,即这几个:

filterpy==1.4.5
scikit-image==0.17.2
lap==0.4.0

所以只能使用 yolo 自带的追踪器进行追踪

定义存储字典并解析视频:

# 存储所有信息
results = {}# 加载模型
coco_model = YOLO('yolov8n.pt', task='detect')
license_plate_detector = YOLO('best.pt')# 加载视频
cap = cv2.VideoCapture('./3.mp4')

通过 opencv VideoCapture 函数加载视频,并加载模型,这里的车牌模型我使用的官方模型,车牌模型是自己训练的,大家下载后自取。

识别帧,为了方便测试,只取前十帧:

# 读取视频帧
ret = True
count = -1
vehicles = [2, 3, 5, 7]
while ret:count += 1ret, frame = cap.read()# print(frame)if ret and count < 10:# 创建帧编号空字典,方便后面存储数据results[count] = {}# detections = coco_model(frame)[0]detections = coco_model.track(frame, persist=True)  # persist=True 会记住上一帧的信息# detection = detections.boxes.data.tolist()# print(detection)detections_ = []

如果检测到车辆,则放入列表中

检测车牌,并将车牌分配给对应的车辆:

# 检测车牌licence_plates = license_plate_detector(frame)[0]for licence_plate in licence_plates.boxes.data.tolist():x1, y1, x2, y2, score ,class_id = licence_plate# get_car 分配每一个车牌给车辆xcar_1, ycar_1, x_car2, y_car2, car_id = get_car(licence_plate, track_ids)
import os
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
# 函数集合
import csv
import string
import easyocr
# 指定模型存储目录
model_storage_directory = "E:\python_works\yolo\my_detect\my_detect\models"
# 定义字符拾取器
reader = easyocr.Reader(['en', 'ch_sim'], model_storage_directory=model_storage_directory)def get_car(licence_plate, vehicle_track_ids):"""将识别车牌分配给已经追踪的车辆"""global car_indexx1, y1, x2, y2, score, class_id = licence_platefoundIt = False# 遍历所有车辆for j in range(len(vehicle_track_ids)):xcar1, ycar_1, xcar_2, ycar_2, car_id = vehicle_track_ids[j][:5]# 坐标原点为左上角,横轴为x轴,纵轴为y轴if x1 > xcar1 and y1 > ycar_1 and x2 < xcar_2 and y2 < ycar_2:car_index = jfoundIt = Truebreakif foundIt:return vehicle_track_ids[car_index][:5]return -1, -1, -1, -1, -1

分配算法是通过判断车牌 box 是否位于某个车辆 box 中,左上角为坐标原点,水平为 X 轴,竖直为 Y 轴,若分分配失败,则认为车辆识别或车牌识别出现误差。

如果分配成功,则裁剪并处理车牌图像:

# 如果车牌匹配到了车辆if car_id != -1:# 裁剪车牌licence_plate_crop = frame[int(y1):int(y2), int(x1):int(x2), :]# 转换成灰度图片licence_plate_crop_gray = cv2.cvtColor(licence_plate_crop, cv2.COLOR_BGR2GRAY)# 调整阈值,若低于64,则设置值为 255,否则设置成 0 ,输出阈值图像_,licence_plate_crop_thresh = cv2.threshold(licence_plate_crop_gray, 64, 255, cv2.THRESH_BINARY_INV)# # 显示灰度图片和阈值黑白图像# cv2.imshow('original_crop', licence_plate_crop)# cv2.imshow('threshold', licence_plate_crop_thresh)## cv2.waitKey(0)

然后识别车牌:

# read_licence_plate 识别车牌字符licence_plate_text, licence_plate_core = read_licence_plate(licence_plate_crop_thresh)
def read_licence_plate(licence_plate):"""识别车牌字符串返回格式化字符串和置信度得分"""detections = reader.readtext(licence_plate)for detection in detections:bbox, text, score = detectiontext = text.upper().replace(' ','')print(text)return 0, 0

识别车牌是通过 easyocr 的文字识别进行的,先创建识别器,将阈值黑白图像输入函数,去掉空格后打印车牌字符串。

车牌识别后,将所有信息都存储到 csv 表格中进行查看:

# 存储车牌信息if licence_plate_text is not None:results[count][car_id] = {'car':{'bbox':[xcar_1, ycar_1, x_car2, y_car2]},'licence_plates':{'bbox':[x1, y1, x2, y2],'text':licence_plate_text,'bbox_score':score,'text_score':licence_plate_core}}# 写入csv
write_csv(results, './test.csv')
def write_csv(results, output_path):"""Write the results to a CSV file.Args:results (dict): Dictionary containing the results.output_path (str): Path to the output CSV file."""with open(output_path, 'w', newline='') as f:  # 使用newline=''避免在Windows上出现多余的空行writer = csv.writer(f)# 写入表头writer.writerow(['frame_nmr', 'car_id', 'car_bbox', 'license_plate_bbox','license_plate_bbox_score', 'license_number', 'license_number_score'])for frame_nmr in results.keys():for car_id in results[frame_nmr].keys():print(results[frame_nmr][car_id])if 'car' in results[frame_nmr][car_id] and \'licence_plates' in results[frame_nmr][car_id] and \'text' in results[frame_nmr][car_id]['licence_plates']:car_bbox = results[frame_nmr][car_id]['car']['bbox']license_plate_bbox = results[frame_nmr][car_id]['licence_plates']['bbox']bbox_score = results[frame_nmr][car_id]['licence_plates']['bbox_score']text = results[frame_nmr][car_id]['licence_plates']['text']text_score = results[frame_nmr][car_id]['licence_plates']['text_score']# 写入数据行writer.writerow([frame_nmr,car_id,'[{} {} {} {}]'.format(*car_bbox),'[{} {} {} {}]'.format(*license_plate_bbox),bbox_score,text,text_score])

最后运行,识别到车牌信息及其对应车辆信息:

        可以看到已能基本势必到车牌,并且都是 id 为2的车辆,这说明 id 1 的车辆并没有分配到对应的车牌,或者说根本没识别到车牌,这是可接受的,因为我的车牌识别模型是自己训练的,只用了大概 300 张训练集,识别不准确是正常的。然后 id 2 的车辆在这几个帧中识别到的车牌字符是不一样的,但是仔细观察发现 “肃R18”、“京凡168”、“就凡768”,识别到的车牌可以认为是很相像的,可以认为是字符识别误差或者图片质量误差,后期可设计算法匹配到正确的车牌号或者继续优化模型,以识别到更精确的车牌号,这里不做过多介绍,后期会再发博客探讨。

这里再提供下源码:

main.py:

import os
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
import numpy as np
from ultralytics import YOLO
import cv2
from util import *# 存储所有信息
results = {}# 加载模型
coco_model = YOLO('yolov8n.pt', task='detect')
license_plate_detector = YOLO('best.pt')# 加载视频
cap = cv2.VideoCapture('./3.mp4')# 读取视频帧
ret = True
count = -1
vehicles = [2, 3, 5, 7]
while ret:count += 1ret, frame = cap.read()# print(frame)if ret and count < 8:# 创建帧编号空字典,方便后面存储数据results[count] = {}# detections = coco_model(frame)[0]detections = coco_model.track(frame, persist=True)  # persist=True 会记住上一帧的信息# detection = detections.boxes.data.tolist()# print(detection)detections_ = []if len(detections) > 0:track_ids = detections[0].boxes.data.cpu().numpy().tolist()for detection in track_ids:x1, y1, x2, y2, score, class_id = detection[:6]if int(class_id) in vehicles:detections_.append([x1, y1, x2, y2, score])else:track_ids = []print("Track IDs:", track_ids)# for detection in detections.boxes.data.tolist():#     x1, y1, x2, y2, score ,class_id = detection#     # print#     if int(class_id) in vehicles:#         detections_.append([x1, y1, x2, y2, score])# 汽车跟踪,返回的一个 追踪id 列表,每一个车辆都有 id 及其坐标,即使帧变了,同一个目标的 id 不会变# track_ids = mot_tracker.update(np.asarray(detections_))# 检测车牌licence_plates = license_plate_detector(frame)[0]for licence_plate in licence_plates.boxes.data.tolist():x1, y1, x2, y2, score ,class_id = licence_plate# get_car 分配每一个车牌给车辆xcar_1, ycar_1, x_car2, y_car2, car_id = get_car(licence_plate, track_ids)# 如果车牌匹配到了车辆if car_id != -1:# 裁剪车牌licence_plate_crop = frame[int(y1):int(y2), int(x1):int(x2), :]# 转换成灰度图片licence_plate_crop_gray = cv2.cvtColor(licence_plate_crop, cv2.COLOR_BGR2GRAY)# 调整阈值,若低于64,则设置值为 255,否则设置成 0 ,输出阈值图像_,licence_plate_crop_thresh = cv2.threshold(licence_plate_crop_gray, 64, 255, cv2.THRESH_BINARY_INV)# # 显示灰度图片和阈值黑白图像# cv2.imshow('original_crop', licence_plate_crop)# cv2.imshow('threshold', licence_plate_crop_thresh)## cv2.waitKey(0)# read_licence_plate 识别车牌字符licence_plate_text, licence_plate_core = read_licence_plate(licence_plate_crop_thresh)# 存储车牌信息if licence_plate_text is not None:results[count][car_id] = {'car':{'bbox':[xcar_1, ycar_1, x_car2, y_car2]},'licence_plates':{'bbox':[x1, y1, x2, y2],'text':licence_plate_text,'bbox_score':score,'text_score':licence_plate_core}}# 写入csv
write_csv(results, './test.csv')

util.py:

import os
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
# 函数集合
import csv
import string
import easyocr
# 指定模型存储目录
model_storage_directory = "E:\python_works\yolo\my_detect\my_detect\models"
# 定义字符拾取器
reader = easyocr.Reader(['en', 'ch_sim'], model_storage_directory=model_storage_directory)def get_car(licence_plate, vehicle_track_ids):"""将识别车牌分配给已经追踪的车辆"""global car_indexx1, y1, x2, y2, score, class_id = licence_platefoundIt = False# 遍历所有车辆for j in range(len(vehicle_track_ids)):xcar1, ycar_1, xcar_2, ycar_2, car_id = vehicle_track_ids[j][:5]# 坐标原点为左上角,横轴为x轴,纵轴为y轴if x1 > xcar1 and y1 > ycar_1 and x2 < xcar_2 and y2 < ycar_2:car_index = jfoundIt = Truebreakif foundIt:return vehicle_track_ids[car_index][:5]return -1, -1, -1, -1, -1def read_licence_plate(licence_plate):"""识别车牌字符串返回格式化字符串和置信度得分"""detections = reader.readtext(licence_plate)if detections:# 按照置信度排序,取最高分的结果detections.sort(key=lambda x: x[2], reverse=True)bbox, text, score = detections[0]text = text.upper().replace(' ','')print(text)return text, scoreelse:return None, Nonedef write_csv(results, output_path):"""Write the results to a CSV file.Args:results (dict): Dictionary containing the results.output_path (str): Path to the output CSV file."""with open(output_path, 'w', newline='') as f:  # 使用newline=''避免在Windows上出现多余的空行writer = csv.writer(f)# 写入表头writer.writerow(['frame_nmr', 'car_id', 'car_bbox', 'license_plate_bbox','license_plate_bbox_score', 'license_number', 'license_number_score'])for frame_nmr in results.keys():for car_id in results[frame_nmr].keys():print(results[frame_nmr][car_id])if 'car' in results[frame_nmr][car_id] and \'licence_plates' in results[frame_nmr][car_id] and \'text' in results[frame_nmr][car_id]['licence_plates']:car_bbox = results[frame_nmr][car_id]['car']['bbox']license_plate_bbox = results[frame_nmr][car_id]['licence_plates']['bbox']bbox_score = results[frame_nmr][car_id]['licence_plates']['bbox_score']text = results[frame_nmr][car_id]['licence_plates']['text']text_score = results[frame_nmr][car_id]['licence_plates']['text_score']# 写入数据行writer.writerow([frame_nmr,car_id,'[{} {} {} {}]'.format(*car_bbox),'[{} {} {} {}]'.format(*license_plate_bbox),bbox_score,text,text_score])# 示例调用

资源下载

车牌识别项目代码1.0资源-CSDN下载

感谢您的观看!!!

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

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

相关文章

sqlserver函数与过程(二)

过程 SQLserver 过程是具有特定功能&#xff0c;可多次对数据表操作的独立模块。返回值通常用return 返回整数 0&#xff0c;1…。(可选&#xff09;也可通过output 参数或select 语句返回结果集。 1.过程的定义 本过程定义了一个过程&#xff0c;输入一个动态SQL语句&#…

OpenCV学习3

1、创建图像窗口滑动条 OpenCV 4中通过createTrackbar()函数在显示图像的窗口上创建滑动条。 int cv::createTrackbar(const String &trackbarname,const String &winname, int *value, int count, TrackbarCallback onChange 0, void *us…

SRS流媒体服务器之本地测试rtc推流bug

SRS环境版本 commit 44f0c36b61bc7c3a1d51cb60be0ec184c840f09d Author: winlin <winlinvip.126.com> Date: Wed Aug 2 10:34:41 2023 0800 Release v4.0-r5, 4.0 release5, v4.0.271, 145574 lines. bug1: 无法推流 WebRTC推流必须是HTTPS或者localhost&#xff1a;Ht…

物理服务器是指的什么?作用有哪些?-哈尔滨云前沿

物理服务器是一种基于传统硬件架构构建的服务器&#xff0c;物理服务器是具有处理器、硬盘和网络接口等硬件组件的独立服务器&#xff0c;可以用于托管和存储数据服务&#xff0c;&#xff0c;是计算机网络的核心组件之一&#xff0c;本文就来详细了解一下物理服务器。 物理服务…

Lua现学现卖

一、Lua的变量类型 全局变量&#xff1a;MyVar 局部变量&#xff1a;local MyVar 二、Lua的数据类型 1.nil&#xff1a;一个空值 类似C的nullptr 2.Boolean&#xff1a;true/false 类似C的bool 3.string&#xff1a;字符串 类似C的std::string 4.Number&#xff1a;数字 类似C…

(24)如何在 Qt 里创建 c++ 类,以前已经学习过如何在 Qt 里引入资源图片文件。以及如何为继承于 Qt已有类的自定义类重新实现虚函数

&#xff08;1&#xff09; 如何在Qt里创建 c 类 &#xff1a; 效果图如下 &#xff1a; &#xff08;2&#xff09;开始完善自定义类里面的成员函数 &#xff1a; 接着 &#xff1a; 以及 &#xff1a; 接着重新实现这些继承来的虚函数就可以了。 &#xff08;3&#xff09…

怎样优化HDFS的网络传输

优化HDFS&#xff08;Hadoop Distributed File System&#xff09;的网络传输可以从多个方面入手&#xff0c;以下是一些常见的优化策略&#xff1a; 1. 网络硬件升级 增加带宽&#xff1a;使用更高带宽的网络设备&#xff0c;如10Gbps或更高速度的交换机和网卡。减少延迟&am…

深入探索 Pdfium.Net:在 .NET 中处理和渲染 PDF 文件

在现代软件开发中&#xff0c;PDF 文件的处理变得愈加重要&#xff0c;尤其是在文档管理、报表生成和在线内容展示等领域。为了高效地处理和渲染 PDF 文件&#xff0c;开发者通常会选择一些强大的 PDF 处理库。而 Pdfium.Net&#xff0c;作为 PDFium 库的 .NET 封装&#xff0c…

当无人机遇到AI智能体:多领域自主空中智能和无人机智能体综述

作者&#xff1a;Ranjan Sapkota, Konstantinos I. Roumeliotis, Manoj Karkee 单位&#xff1a;康奈尔大学生物与环境工程系&#xff0c;希腊伯罗奔尼撒大学信息与电信系 论文标题&#xff1a;UAVs Meet Agentic AI: A Multidomain Survey of Autonomous Aerial Intelligenc…

从 0 到 1 玩转 React:打造你的趣味美食相册

想象一下&#xff0c;你想制作一个超酷的 “美食相册” 网页&#xff0c;能展示各种美食图片&#xff0c;还能随时切换查看不同美食。这听起来是不是很有趣&#xff1f;别担心&#xff0c;React 能帮你轻松实现&#xff01;作为前端开发领域最受欢迎的库之一&#xff0c;React …

深入浅出:RocketMQ与Kafka的双剑合璧,实现高可用与高吞吐

本文在创作过程中借助 AI 工具辅助资料整理与内容优化。图片来源网络。 文章目录 引言一、RocketMQ与Kafka的江湖地位1.1 RocketMQ的独门绝技1.2 Kafka的凌厉攻势 二、双剑合璧的策略&#xff1a;双写队列2.1 策略概述2.2 代码实现 三、双剑合璧的实战应用3.1 电商订单处理3.2 …

Apache POI-02.入门案例-通过POI向Excel文件写入文件内容-通过POI读取Excel文件内容

一.入门案例 向excel文件中写入并读出 package com.sky.test;import org.apache.poi.xssf.usermodel.XSSFCell; import org.apache.poi.xssf.usermodel.XSSFRow; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; impor…

MongoDB06 - MongoDB 地理空间

MongoDB06 - MongoDB 地理空间 文章目录 MongoDB06 - MongoDB 地理空间一&#xff1a;地理空间数据基础1&#xff1a;地理数据表示方式1.1&#xff1a;GeoJSON 格式1.2&#xff1a;传统坐标对 2&#xff1a;地理空间索引2.1&#xff1a;2dsphere 索引2.2&#xff1a;2d索引2.3&…

Bugku——WEB篇(持续更新ing)

目录 一、滑稽 二、计算器 方法一 方法二 三、alert 四、你必须让他停下 五、头等舱 六、GET 七、POST 方法一 方法二 八、source 九、矛盾 十、备份是个好习惯 一、滑稽 1.启动环境后&#xff0c;访问URL&#xff0c;页面出现了一堆滑稽表情 2.按f12(或fnf12)打…

Linux 网络命名空间的奥秘:深入解析struct net与内核模块编译陷阱

引言:网络隔离的基石 在Linux容器化技术(如Docker)和云计算网络中,网络命名空间是实现网络隔离的核心机制。每个隔离的网络环境都由一个关键的内核数据结构描述——struct net。这个结构体不仅是网络隔离的技术基础,也是内核开发者常遇到的编译陷阱源头。 一、解剖网络命…

idea的EasyCode插件连接瀚高数据库(APP)

文章目录 环境症状问题原因解决方案 环境 系统平台&#xff1a;Linux x86-64 Red Hat Enterprise Linux 7 版本&#xff1a;5.6.5 症状 客户在idea工具中使用EasyCode插件连接瀚高数据库的企业版时&#xff0c;连接设置的url中提示“jdbc:highgo不存在”的错误 问题原因 E…

VMware设置虚拟机为固定IP

1. 修改虚拟网络编辑器 打开虚拟机网络“编辑” 点击“VMnet8” 选择“NAT”模式 修改网关&#xff1a;前面的不要修改&#xff0c;最后一位设置为“1”&#xff0c;然后确定 记住这里的网关&#xff0c;后面的配置要保持一致 设置子网IP和子网掩码&#xff1a;一般就…

智核引擎融合生成式AI,重塑企业知识图谱与研发创新范式!

目录 系统架构设计核心实现步骤步骤1&#xff1a;知识图谱构建与数据预处理步骤2&#xff1a;生成式AI与知识图谱融合&#xff08;RAG增强&#xff09;步骤3&#xff1a;智能推理工作流 核心流程可视化企业级部署方案性能优化策略应用场景示例结语 本文将手把手实现企业级知识图…

LogisticRegression(solver = ‘lbfgs‘)的ConvergenceWarning问题解决

&#x1f466;&#x1f466;一个帅气的boy&#xff0c;你可以叫我Love And Program &#x1f5b1; ⌨个人主页&#xff1a;Love And Program的个人主页 &#x1f496;&#x1f496;如果对你有帮助的话希望三连&#x1f4a8;&#x1f4a8;支持一下博主 LogisticRegression的Co…

web3 docs

区块链重构信任机制&#xff0c;去中心化&#xff0c;用唯一的hash编号来实现防篡改。以数字货币的形式交易&#xff0c;个人持有唯一的数字秘钥(唯一&#xff0c;不可篡改) 详见 以太坊的白皮书 和 数字货币 (加密货币实现隐私交易) 底层基础的很多特点 1.例如p2p&#xf…