基于 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
英文地址,請注明出處:http://en.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…