目標(biāo)識(shí)別項(xiàng)目:基于Yolov7-LPRNet的動(dòng)態(tài)車(chē)牌目標(biāo)識(shí)別算法模型(一)
前言
目標(biāo)識(shí)別如今以及迭代了這么多年,普遍受大家認(rèn)可和歡迎的目標(biāo)識(shí)別框架就是YOLO了。按照官方描述,YOLOv8 是一個(gè) SOTA 模型,它建立在以前 YOLO 版本的成功基礎(chǔ)上,并引入了新的功能和改進(jìn),以進(jìn)一步提升性能和靈活性。從基本的YOLOv1版本到如今v8版本,完成了多次蛻變,現(xiàn)在已經(jīng)相當(dāng)成熟并且十分的親民。我見(jiàn)過(guò)很多初學(xué)目標(biāo)識(shí)別的同學(xué)基本上只花一周時(shí)間就可以參照案例實(shí)現(xiàn)一個(gè)目標(biāo)檢測(cè)的項(xiàng)目,這全靠YOLO強(qiáng)大的解耦性和部署簡(jiǎn)易性。初學(xué)者甚至只需要修改部分超參數(shù)接口,調(diào)整數(shù)據(jù)集就可以實(shí)現(xiàn)目標(biāo)檢測(cè)了。但是我想表達(dá)的并不是YOLO的原理有多么難理解,原理有多難推理。一般工作中要求我們能夠運(yùn)行并且能夠完成目標(biāo)檢測(cè)出來(lái)就可以了,更重要的是數(shù)據(jù)集的標(biāo)注。我們不需要完成幾乎難以單人完成的造目標(biāo)檢測(cè)算法輪子的過(guò)程,我們需要理解YOLO算法中每個(gè)超參數(shù)的作用以及影響。就算我們能夠訓(xùn)練出一定準(zhǔn)確度的目標(biāo)檢測(cè)模型,我們還需要根據(jù)實(shí)際情況對(duì)生成結(jié)果進(jìn)行一定的改寫(xiě):例如對(duì)于圖片來(lái)說(shuō)一共出現(xiàn)了幾種目標(biāo);對(duì)于一個(gè)視頻來(lái)說(shuō),定位到具體時(shí)間出現(xiàn)了識(shí)別的目標(biāo)。這都是需要我們反復(fù)學(xué)習(xí)再練習(xí)的本領(lǐng)。
完成目標(biāo)檢測(cè)后,我們應(yīng)該輸出定位出來(lái)的信息,YOLO是提供輸出設(shè)定的超參數(shù)的,我們需要根據(jù)輸出的信息對(duì)目標(biāo)進(jìn)行裁剪得到我們想要的目標(biāo)之后再做上層處理。如果是車(chē)牌目標(biāo)識(shí)別的項(xiàng)目,我們裁剪出來(lái)的車(chē)牌就可以進(jìn)行OCR技術(shù)識(shí)別出車(chē)牌字符了,如果是安全帽識(shí)別項(xiàng)目,那么我們可以統(tǒng)計(jì)一張圖片或者一幀中出現(xiàn)檢測(cè)目標(biāo)的個(gè)數(shù)做出判斷,一切都需要根據(jù)實(shí)際業(yè)務(wù)需求為主。本篇文章主要是OCR模型對(duì)車(chē)牌進(jìn)行字符識(shí)別,結(jié)合YOLO算法直接定位目標(biāo)進(jìn)行裁剪,裁剪后生成OCR訓(xùn)練數(shù)據(jù)集即可。
其中數(shù)據(jù)集的質(zhì)量是尤為重要的,決定了模型的上限,因此想要搭建一個(gè)效果較好的目標(biāo)識(shí)別算法模型,就需要處理流程較為完善的開(kāi)源數(shù)據(jù)集。本篇文章采用的是CCPD數(shù)據(jù)集,那么不再過(guò)多描述,讓我們直接開(kāi)始項(xiàng)目搭建。
數(shù)據(jù)集收集
CCPD:https://github.com/Fanstuck/CCPD
一般來(lái)說(shuō)目前常用來(lái)訓(xùn)練車(chē)牌目標(biāo)識(shí)別項(xiàng)目的數(shù)據(jù)集都采用了CCPD數(shù)據(jù)集,CCPD是一個(gè)大型的、多樣化的、經(jīng)過(guò)仔細(xì)標(biāo)注的中國(guó)城市車(chē)牌開(kāi)源數(shù)據(jù)集。CCPD數(shù)據(jù)集主要分為CCPD2019數(shù)據(jù)集和CCPD2020(CCPD-Green)數(shù)據(jù)集。CCPD2019數(shù)據(jù)集車(chē)牌類(lèi)型僅有普通車(chē)牌(藍(lán)色車(chē)牌),CCPD2020數(shù)據(jù)集車(chē)牌類(lèi)型僅有新能源車(chē)牌(綠色車(chē)牌)。
CCPD2019數(shù)據(jù)集
CCPD2019數(shù)據(jù)集主要采集于合肥市停車(chē)場(chǎng),采集時(shí)間為上午7:30到晚上10:00,停車(chē)場(chǎng)采集人員手持Android POS機(jī)對(duì)停車(chē)場(chǎng)的車(chē)輛拍照進(jìn)行數(shù)據(jù)采集。所拍攝的車(chē)牌照片涉及多種復(fù)雜環(huán)境,包括模糊、傾斜、雨天、雪天等。CCPD2019數(shù)據(jù)集包含了25萬(wàn)多幅中國(guó)城市車(chē)牌圖像和車(chē)牌檢測(cè)與識(shí)別信息的標(biāo)注。主要介紹如下:
類(lèi)別 | 描述 | 圖片數(shù) |
---|---|---|
CCPD-Base | 通用車(chē)牌圖片 | 200k |
CCPD-FN | 車(chē)牌離攝像頭拍攝位置相對(duì)較近或較遠(yuǎn) | 20k |
CCPD-DB | 車(chē)牌區(qū)域亮度較亮、較暗或者不均勻 | 20k |
CCPD-Rotate | 車(chē)牌水平傾斜20到50度,豎直傾斜-10到10度 | 10k |
CCPD-Tilt | 車(chē)牌水平傾斜15到45度,豎直傾斜15到45度 | 10k |
CCPD-Weather | 車(chē)牌在雨雪霧天氣拍攝得到 | 10k |
CCPD-Challenge | 在車(chē)牌檢測(cè)識(shí)別任務(wù)中較有挑戰(zhàn)性的圖片 | 10k |
CCPD-Blur | 由于攝像機(jī)鏡頭抖動(dòng)導(dǎo)致的模糊車(chē)牌圖片 | 5k |
CCPD-NP | 沒(méi)有安裝車(chē)牌的新車(chē)圖片 | 5k |
CCPD2019/CCPD-Base中的圖像被拆分為train/val數(shù)據(jù)集。使用CCPD2019中的子數(shù)據(jù)集(CCPD-DB、CCPD-Blur、CCPD-FN、CCPD-Rotate、CCPD-Tilt、CCPD-Challenge)進(jìn)行測(cè)試。
CCPD2020數(shù)據(jù)集
CCPD2020數(shù)據(jù)集采集方法應(yīng)該和CCPD2019數(shù)據(jù)集類(lèi)似。CCPD2020僅僅有新能源車(chē)牌圖片,包含不同亮度,不同傾斜角度,不同天氣環(huán)境下的車(chē)牌。CCPD2020中的圖像被拆分為train/val/test數(shù)據(jù)集,train/val/test數(shù)據(jù)集中圖片數(shù)分別為5769/1001/5006。
=
車(chē)牌號(hào)碼說(shuō)明
車(chē)牌第一位是漢字:代表該車(chē)戶(hù)口所在的省級(jí)行政區(qū),為各(省、直轄市、自治區(qū))的簡(jiǎn)稱(chēng),比如:北京是京,上海是滬,湖南就是湘…
車(chē)牌第二位是英文字母:代表該車(chē)戶(hù)口所在的地級(jí)行政區(qū),一般為各地級(jí)市、地區(qū)、自治州字母代碼,一般按省級(jí)車(chē)管所以各地級(jí)行政區(qū)狀況分劃排名:(字母“A”為省會(huì)、首府或直轄市中心城區(qū)的代碼,其字母排名不分先后)
另在編排地級(jí)行政區(qū)英文字母代碼時(shí),跳過(guò)I和O,O往往被用作警車(chē)或者機(jī)關(guān)單位。
- 省份:[“皖”, “滬”, “津”, “渝”, “冀”, “晉”, “蒙”, “遼”, “吉”, “黑”, “蘇”, “浙”, “京”, “閩”, “贛”, “魯”, “豫”, “鄂”, “湘”, “粵”, “桂”, “瓊”, “川”, “貴”, “云”, “藏”, “陜”, “甘”, “青”, “寧”, “新”]
- 地市:[‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’, ‘H’, ‘J’, ‘K’, ‘L’, ‘M’, ‘N’, ‘P’, ‘Q’, ‘R’, ‘S’, ‘T’, ‘U’, ‘V’, ‘W’,‘X’, ‘Y’, ‘Z’]
- 車(chē)牌字典:[‘A’, ‘B’, ‘C’, ‘D’, ‘E’, ‘F’, ‘G’, ‘H’, ‘J’, ‘K’, ‘L’, ‘M’, ‘N’, ‘P’, ‘Q’, ‘R’, ‘S’, ‘T’, ‘U’, ‘V’, ‘W’, ‘X’,‘Y’, ‘Z’, ‘0’, ‘1’, ‘2’, ‘3’, ‘4’, ‘5’, ‘6’, ‘7’, ‘8’, ‘9’]
綠牌和藍(lán)牌區(qū)別如下:
-
顏色區(qū)別:小型新能源車(chē)牌采用“漸變綠”的配色,大型新能源車(chē)牌采用“黃綠雙拼色”,綠牌的字體顏色為黑色;而傳統(tǒng)燃油車(chē)藍(lán)牌則采用“純藍(lán)色”設(shè)計(jì),字體顏色為白色;
-
號(hào)碼編排:普通藍(lán)牌共有7位字符;新能源車(chē)牌有8位字符;新能源綠牌的號(hào)碼共有6位數(shù),其中小型新能源汽車(chē)牌照的字母設(shè)計(jì)在第一位,大型新能源汽車(chē)牌照的字母設(shè)計(jì)在最后一位。其中車(chē)牌首字母為“D/A/B/C/E”的,代表“純電動(dòng)車(chē)”;首字母為“F/G/H/J/K”的,代表“非純電動(dòng)汽車(chē)”。而普通燃油車(chē)藍(lán)牌的號(hào)碼只有5位數(shù),首字母或數(shù)字一般不代表任何含義,只有部分地區(qū)會(huì)給營(yíng)運(yùn)類(lèi)車(chē)型劃分特定字母。
目標(biāo)識(shí)別模型具體搭建框架
數(shù)據(jù)預(yù)處理
CCPD數(shù)據(jù)集沒(méi)有專(zhuān)門(mén)的標(biāo)注文件,每張圖像的文件名就是該圖像對(duì)應(yīng)的數(shù)據(jù)標(biāo)注?!?25-95_113-154&383_386&473-386&473_177&454_154&383_363&402-0_0_22_27_27_33_16-37-15.jpg】,其文件名的含義如下:
- 025:車(chē)牌區(qū)域占整個(gè)畫(huà)面的比例;
- 95_113: 車(chē)牌水平和垂直角度, 水平95°, 豎直113°
- 154&383_386&473:標(biāo)注框左上、右下坐標(biāo),左上(154, 383), 右下(386, 473)
- 386&473_177&454_154&383_363&402:標(biāo)注框四個(gè)角點(diǎn)坐標(biāo),順序?yàn)?strong>右下、左下、左上、右上
- 0_0_22_27_27_33_16:車(chē)牌號(hào)碼映射關(guān)系如下: 第一個(gè)0為省份 對(duì)應(yīng)省份字典provinces中的’皖’,;第二個(gè)0是該車(chē)所在地的地市一級(jí)代碼,對(duì)應(yīng)地市一級(jí)代碼字典alphabets的’A’;后5位為字母和文字, 查看車(chē)牌號(hào)ads字典,如22為Y,27為3,33為9,16為S,最終車(chē)牌號(hào)碼為皖A(yù)Y339S
因此我們需要根據(jù)圖片文件的目錄名稱(chēng)來(lái)拆分信息:
def getinfo_annotations(image_file):
'''
細(xì)節(jié)看文檔
:param image_file:
:return:
'''
try:
annotations = image_file.split('-')
rate = annotations[0]# 車(chē)牌區(qū)域占整個(gè)畫(huà)面的比例;
angles = annotations[1].split('-')# 車(chē)牌水平和垂直角度, 水平95°, 豎直113°
box = annotations[2].split('_')# 標(biāo)注框左上、右下坐標(biāo),左上(154, 383), 右下(386, 473)
boxes = [list(map(int, i.split('&'))) for i in box]
point = annotations[3].split('_') # 標(biāo)注框四個(gè)角點(diǎn)坐標(biāo),順序?yàn)橛蚁隆⒆笙?、左上、右?/span>
points = [list(map(int, i.split('&'))) for i in point]
plate = annotations[4].split("_") # licenses 標(biāo)注框四個(gè)角點(diǎn)坐標(biāo),順序?yàn)橛蚁?、左下、左上、右?/span>
plates = plate_analysis_licenses(plate)
except Exception as e:
boxes = []
points = []
plates = []
angles = []
info = {"filename": image_file, "boxes": boxes, "points": points,
"plates": plates, "angles": angles}
return info
此外我們還需要編寫(xiě)出解析代表識(shí)別車(chē)牌字符的數(shù)字編碼:
def plate_analysis_licenses(plate):
'''
細(xì)節(jié)看文檔
:param plate:
:return:車(chē)牌info
'''
provinces = ["皖", "滬", "津", "渝",
"冀", "晉", "蒙", "遼",
"吉", "黑", "蘇", "浙",
"京", "閩", "贛", "魯",
"豫", "鄂", "湘", "粵",
"桂", "瓊", "川", "貴",
"云", "藏", "陜", "甘",
"青", "寧", "新", "警", "學(xué)", "O"]
alphabets=['A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'J', 'K',
'L', 'M', 'N', 'P', 'Q',
'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z', 'O']
ads = ['A', 'B', 'C', 'D', 'E',
'F', 'G', 'H', 'J', 'K',
'L', 'M', 'N', 'P', 'Q',
'R', 'S', 'T', 'U', 'V',
'W', 'X','Y', 'Z', '0',
'1', '2', '3', '4', '5',
'6', '7', '8', '9', 'O']
result = [provinces[int(plate[0])], alphabets[int(plate[1])]]
result += [ads[int(p)] for p in plate[2:]]
result = "".join(result)
#print(plate,result)
return result
這樣以來(lái)我們可以獲取所有文件名稱(chēng)解析出的內(nèi)容,包括檢測(cè)框的坐標(biāo)以及車(chē)牌的信息,具體解析情況可以繪制展示代碼:
def draw_box(image, box):
"""
:param image:
:param box:
:return: 邊界框
"""
draw = ImageDraw.Draw(image)
draw.rectangle([tuple(box[0]), tuple(box[1])], outline="#00FF00", width=3)
def draw_point(image,point):
'''
繪制四個(gè)關(guān)鍵點(diǎn)
:param image:
:param point:
:return:
'''
draw = ImageDraw.Draw(image)
for p in point:
center = (p[0],p[1])
radius = 5
right = (center[0]+radius,center[1]+radius)
left = (center[0] - radius, center[1] - radius)
draw.ellipse((left, right), fill="#FF0000")
def draw_label(image,label):
'''
繪制車(chē)牌
:param image:
:param label:
:return:
'''
draw = ImageDraw.Draw(image)
font = ImageFont.truetype('simsun.ttc',64)
draw.text((30,30),label,font=font,fill="#FF0000")
def image_show(imagepath,box,points,label):
'''
圖片展示
:param imagepath:
:param box:
:param points:
:param label:
:return:
'''
image=Image.open(imagepath)
draw_label(image,label)
draw_point(image,points)
draw_box(image,box)
image.show()
效果如下:
那么能夠獲取圖片的關(guān)鍵信息之后,我們就可以開(kāi)始建立我們模型的訓(xùn)練數(shù)據(jù)集和測(cè)試數(shù)據(jù)集了。首先我們需要明確,該項(xiàng)目我們需要完成對(duì)車(chē)牌的檢測(cè),也就是能夠在圖片或者視頻幀中定位到車(chē)牌的坐標(biāo)。定位之后還要能夠識(shí)別出車(chē)牌上面的字符信息。拆解這兩部分我們需要建立兩種模型,一種是目標(biāo)檢測(cè)的算法模型,例如YOLO、R-CNN、SSD等,另一種是OCR文字識(shí)別模型,例如CRNN、PlateNet、LPRNet等。那么第一步我們需要搭建能夠定位車(chē)牌信息的目標(biāo)檢測(cè)模型。
數(shù)據(jù)集準(zhǔn)備
我們現(xiàn)在需要將CCPD數(shù)據(jù)集轉(zhuǎn)換YOLO格式標(biāo)注數(shù)據(jù)集,YOLO文本標(biāo)注數(shù)據(jù)集樣例如:
txt每個(gè)數(shù)值分別代表:檢測(cè)框中心點(diǎn)坐標(biāo)x,檢查框中心點(diǎn)坐標(biāo)y,檢查框?qū)抴,檢測(cè)框高h(yuǎn)。
通過(guò)解析每個(gè)圖片得到的信息很容易完成:
def create_txt_file(file_path,image_name,image_width, image_height, bbox_coords_list, class_index):
yolo_lines = []
bbox_coords_lists=[]
bbox_coords_lists.append(bbox_coords_list)
for bbox_coords in bbox_coords_lists:
print(bbox_coords)
x_center = (bbox_coords[0][0] + bbox_coords[1][0]) / (2 * image_width)
y_center = (bbox_coords[0][1] + bbox_coords[1][1]) / (2 * image_height)
width = (bbox_coords[1][0] - bbox_coords[0][0]) / image_width
height = (bbox_coords[1][1] - bbox_coords[0][1]) / image_height
yolo_lines.append(f"{class_index} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}")
save_path = os.path.join(file_path, "label", f"{image_name}.txt")
for i, line in enumerate(yolo_lines):
txt_filename = f"{image_name}.txt"
txt_path = save_path
with open(txt_path, "w") as txt_file:
txt_file.write(line)
上述代碼將生成對(duì)應(yīng)的YOLO目標(biāo)標(biāo)注文本文件,一些具體功能參數(shù)不清楚的可以去看本人github開(kāi)源代碼,有詳細(xì)說(shuō)明。
隨后就是劃分訓(xùn)練和測(cè)試數(shù)據(jù)集了,CCPD有9類(lèi)性質(zhì)不同的車(chē)牌圖片,這里統(tǒng)一使用CCPD-Base數(shù)據(jù)集,主要是方便處理和展示,如果大家想要追求更好的效果可以依次提取其他數(shù)據(jù)集的圖片,我們的目的是構(gòu)建輕量級(jí)數(shù)據(jù)不用那么多數(shù)據(jù)量增加本地計(jì)算機(jī)負(fù)擔(dān),故從原本數(shù)據(jù)集抽取3W張圖片進(jìn)行訓(xùn)練,僅抽取CCPD-base數(shù)據(jù)集的圖片。
# -*- coding:utf-8 -*-
# @Author: fanstuck
# @Time: 2023/8/24 17:02
# @File: Sampling_of_CCPD_files.py
import os
import random
import shutil
def Random_sampling_of_documents(source_directory,target_directory,num):
'''
目錄文件下隨機(jī)抽樣
:param :source_directory
:param :target_directory
:return:
'''
# 選擇抽取的圖片數(shù)量
num_images_to_extract = num
# 獲取目錄中所有圖片文件
all_image_files = [file for file in os.listdir(source_directory) if file.lower().endswith(('.png', '.jpg', '.jpeg'))]
# 隨機(jī)抽取指定數(shù)量的圖片文件
selected_image_files = random.sample(all_image_files, num_images_to_extract)
# 將選中的圖片文件復(fù)制到目標(biāo)目錄
for image_file in selected_image_files:
source_path = os.path.join(source_directory, image_file)
target_path = os.path.join(target_directory, image_file)
shutil.copy(source_path, target_path)
準(zhǔn)備工作還差一步,我們需要切割數(shù)據(jù)集分為訓(xùn)練數(shù)據(jù)集,測(cè)試數(shù)據(jù)集和真值數(shù)據(jù)集。這里劃分已經(jīng)隨機(jī)抽樣的3W車(chē)牌數(shù)據(jù)進(jìn),進(jìn)行訓(xùn)練:測(cè)試:真值=7:2:1的劃分:
def split_dataset(source_dir,train_dir,test_dir,val_dir,train_ratio,test_ratio,val_ratio):
# 獲取目錄中的所有圖片文件名
image_files = [f for f in os.listdir(source_dir) if f.endswith(".jpg") or f.endswith(".txt")]
# 計(jì)算劃分?jǐn)?shù)量
total_count = len(image_files)
train_count = int(total_count * train_ratio)
test_count = int(total_count * test_ratio)
val_count = total_count - train_count - test_count
# 劃分圖片并移動(dòng)到對(duì)應(yīng)目錄
for i, image_file in enumerate(image_files):
if i < train_count:
dest_dir = train_dir
elif i < train_count + test_count:
dest_dir = test_dir
else:
dest_dir = val_dir
source_path = os.path.join(source_dir, image_file)
dest_path = os.path.join(dest_dir, image_file)
shutil.copy(source_path, dest_path)
這樣以來(lái)我們就處理好了整個(gè)YOLO模型目標(biāo)檢測(cè)數(shù)據(jù)集,可以開(kāi)始進(jìn)行模型訓(xùn)練了。本項(xiàng)目文章量和代碼量偏多,文章將分好幾部分依次記錄完,有想要入門(mén)目標(biāo)檢測(cè)技術(shù)的朋友十分推薦關(guān)注博主和我一起完成整個(gè)項(xiàng)目的搭建,整個(gè)項(xiàng)目將在Github上面開(kāi)源且完全可部署。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-714537.html
點(diǎn)關(guān)注,防走丟,如有紕漏之處,請(qǐng)留言指教,非常感謝
以上就是本期全部?jī)?nèi)容。我是fanstuck ,有問(wèn)題大家隨時(shí)留言討論 ,我們下期見(jiàn)。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-714537.html
到了這里,關(guān)于目標(biāo)識(shí)別項(xiàng)目實(shí)戰(zhàn):基于Yolov7-LPRNet的動(dòng)態(tài)車(chē)牌目標(biāo)識(shí)別算法模型的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!