據(jù)集:COCO標注轉YOLO訓練與避坑實踐)
簡介面向車牌識別與車牌檢測算法訓練的數(shù)據(jù)集資源聚焦典型黃色車牌檢測場景。整套數(shù)據(jù)包含9324個文件壓縮包約257.79MB其中4627張jpg與35張jpeg圖片對應4662個json標注文件標簽采用polygon多邊形目標框每個車牌由四個頂點貼合定位能較好適配傾斜、透視等復雜拍攝角度為車牌字符識別與目標檢測提供可直接使用的標注信息。圖片經過人工逐張篩選已剔除不清晰樣本并處理有歧義區(qū)域質量較統(tǒng)一無需二次清洗即可用于模型訓練。作者還提供標簽格式轉換支持如需要rectangle矩形目標框的json可私信溝通便于接入不同檢測框架。目前已有885人學習下載適合計算機視覺初學者、算法工程師以及智慧交通、停車場管理等項目開發(fā)者直接復用。1. 車牌檢測數(shù)據(jù)集黃牌樣本為什么比藍牌更容易翻車做車牌檢測的人大概率都經歷過這種場景藍牌車跑得穩(wěn)穩(wěn)當當一到黃牌就漏檢、誤檢輪著來。黃牌多掛在貨車、客車和工程車輛上車牌顏色和車身漆面、車廂反光、泥污混在一起檢測框經常畫偏甚至直接丟掉目標。這份車牌檢測數(shù)據(jù)集以黃牌為主體圖像覆蓋白天、傍晚、陰天等多種光照每張圖都配好 JSON 標簽類別、邊框、圖像尺寸齊全拿過來不用重新標注直接能跑通數(shù)據(jù)加載 → 可視化 → 訓練 → 評估的完整鏈路。適合正在做交通監(jiān)控、停車場出入口識別、車型分類預處理的開發(fā)者也適合想用現(xiàn)成數(shù)據(jù)快速驗證 YOLO 系列模型、跑通圖像識別與車牌檢測完整流程的從業(yè)者。2. 數(shù)據(jù)集結構與 JSON 標注格式先搞懂標簽再動手2.1 目錄組織訓練集、驗證集與標注文件的對應關系拿到壓縮包解壓之后目錄組織比較直白圖像和標注分離訓練集與驗證集各自獨立。常見做法是dataset/ ├── images/ │ ├── train/ │ │ ├── img_0001.jpg │ │ └── ... │ └── val/ │ ├── img_0100.jpg │ └── ... └── annotations/ ├── train.json └── val.jsonimages 目錄下是按 train / val 拆好的原始圖像annotations 目錄里放的是與之一一對應的 JSON 標注文件。train.json 只描述 images/train 下的圖像val.json 同理二者不會交叉引用。這種組織方式和 COCO 數(shù)據(jù)集的慣例一致后續(xù)接 Detectron2、MMDetection 或者自己寫數(shù)據(jù)加載器都比較省事。如果打算用 YOLO 系列訓練只需要把 images 目錄原樣保留再把 JSON 轉成同名 txt 標簽丟進 labels 目錄即可具體轉換腳本在下一章。我第一次拿到時踩過一個坑直接用 glob 去掃 images 目錄結果把 val 的圖像也掃進來了訓練集和驗證集出現(xiàn)重疊訓練時 loss 一震蕩就開始懷疑模型最后查了兩小時才發(fā)現(xiàn)是數(shù)據(jù)加載的問題。后來我改成以 JSON 里的 file_name 為準來構建圖像路徑映射而不是反向掃描目錄問題就消失了。具體做法是給 JSON 里的 file_name 拼接上對應的 train 或 val 前綴再檢查文件是否存在。提示以 JSON 的 file_name 為準構建圖像路徑映射訓練前先檢查 train 和 val 的圖像集合是否有交集。2.2 JSON 字段逐項解析images、annotations、categories 三塊各管什么標注文件用了 COCO 風格的字段頂層分成三塊images 記錄圖像元信息annotations 記錄每個車牌框categories 定義類別。打開一個典型的 train.json結構是這樣的{ images: [ { id: 1, file_name: img_0001.jpg, width: 1920, height: 1080 } ], annotations: [ { id: 1, image_id: 1, category_id: 1, bbox: [862, 415, 210, 68], area: 14280, iscrowd: 0 } ], categories: [ { id: 1, name: yellow_plate } ] }三個塊各管各的事別混。images 里的 id 在全局唯一file_name 是相對于 images 目錄的文件名width 和 height 是標注時記錄的原始分辨率。后續(xù)做歸一化坐標換算必須用這兩個記錄值不能用腳本里讀到的實際尺寸代替因為個別圖像可能被預處理改過尺寸標注框是基于原分辨率畫的混用會造成少量框偏移。annotations 里的 image_id 關聯(lián)到 images.idcategory_id 關聯(lián)到 categories.id本數(shù)據(jù)集目前只有 yellow_plate 一個類別bbox 是 [x, y, width, height] 四個數(shù)值表示車牌外接矩形左上角坐標和寬高單位是像素area 等于 width 乘 height一些框架用它做小目標篩選和評估加權iscrowd 為 0 表示普通目標為 1 表示群體區(qū)域轉換格式時容易忽略這個字段后面避坑章節(jié)專門講。2.3 坐標系換算COCO 絕對坐標與 YOLO 歸一化坐標的互轉COCO 的 bbox 是絕對像素坐標原點在圖像左上角x 向右、y 向下。YOLO 系列訓練時需要的是歸一化坐標目標中心點 (cx, cy) 和寬高 (w, h) 都除以圖像寬高取值在 0 到 1 之間。換算公式不復雜但方向搞反的話訓練出來的模型框中心點全在圖像外面損失函數(shù)根本收斂不了。def coco_to_yolo(bbox, img_w, img_h): x, y, w, h bbox cx x w / 2.0 cy y h / 2.0 return cx / img_w, cy / img_h, w / img_w, h / img_h參數(shù)含義bbox 是 COCO 格式的 [左上角 x, 左上角 y, 寬, 高]img_w 和 img_h 來自 JSON 里 images 塊的 width / height。返回的是歸一化后的中心點 x、中心點 y、歸一化寬、歸一化高。注意 x 和 w / 2 的單位都是像素要先加出中心點再除以圖像寬不能先分別歸一化再相加那樣算出來的中心點坐標是錯的。反過來從 YOLO 轉 COCO 時用中心點減半寬得到左上角def yolo_to_coco(cx_norm, cy_norm, w_norm, h_norm, img_w, img_h): x (cx_norm * img_w) - (w_norm * img_w) / 2.0 y (cy_norm * img_h) - (h_norm * img_h) / 2.0 w w_norm * img_w h h_norm * img_h return [int(v) for v in (x, y, w, h)]yolo_to_coco 最后轉 int 是因為畫框時需要整數(shù)像素坐標而訓練標簽文件里一般保留 6 位小數(shù)不需要取整。這兩段函數(shù)建議直接放進一個 utils.py后面做格式轉換和可視化都要反復用到。如果數(shù)據(jù)集里有旋轉標注或者多邊形標注這套矩形換算就不夠用了需要額外處理角度不過本數(shù)據(jù)集的 JSON 里只有軸對齊矩形框用這兩個函數(shù)足夠。3. 把 JSON 標簽用起來統(tǒng)計、可視化與格式轉換3.1 標注統(tǒng)計先摸清數(shù)據(jù)量和分布再決定訓練策略拿到數(shù)據(jù)集先別急著開訓練花兩分鐘跑個統(tǒng)計腳本確認圖像數(shù)量、標注數(shù)量、每張圖平均車牌數(shù)以及 bbox 寬高比的分布。這些數(shù)字直接決定輸入分辨率、錨框設置和是否要做特殊增強。import json from collections import Counter with open(annotations/train.json, r, encodingutf-8) as f: data json.load(f) print(圖像數(shù)量:, len(data[images])) print(標注數(shù)量:, len(data[annotations])) per_image Counter() wh_ratios [] for a in data[annotations]: per_image[a[image_id]] 1 x, y, w, h a[bbox] wh_ratios.append(round(w / h, 2)) print(單圖最多車牌數(shù):, max(per_image.values())) print(寬高比均值:, sum(wh_ratios) / len(wh_ratios)) print(寬高比范圍:, min(wh_ratios), -, max(wh_ratios))這段腳本從 JSON 里讀出標注列表per_image 用 image_id 做鍵統(tǒng)計每張圖的車牌數(shù)量wh_ratios 收集所有框的寬高比。黃牌的標準寬高比接近 3:1如果統(tǒng)計出來大量框的比值明顯偏離這個范圍比如小于 2 或者大于 5說明標注邊界可能有問題或者圖像里存在非標準尺寸的特殊車牌需要回看原始圖確認。寬高比分布還會影響后面的訓練配置YOLOv5 訓練時有一個自動錨框的步驟k-means 聚類結果直接受 bbox 寬高分布影響換 YOLOv8 這類 anchor-free 模型則不用關心錨框聚類但同樣要從輸入分辨率上解決小目標問題。3.2 可視化檢測框檢查標簽是否真的框住了車牌統(tǒng)計只能發(fā)現(xiàn)數(shù)值層面的異常標簽是否真正貼著車牌邊緣必須把框畫出來看。用 OpenCV 疊加標注框是檢查數(shù)據(jù)質量最快的方式隨機抽一部分圖出來看即可。import cv2 import json data json.load(open(annotations/val.json, encodingutf-8)) for img_info in data[images][:10]: img cv2.imread(fimages/val/{img_info[file_name]}) annos [a for a in data[annotations] if a[image_id] img_info[id]] for a in annos: x, y, w, h [int(v) for v in a[bbox]] cv2.rectangle(img, (x, y), (x w, y h), (0, 255, 255), 2) cv2.putText(img, plate, (x, y - 6), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 255), 2) cv2.imwrite(fcheck_{img_info[file_name]}, img)cv2.rectangle 的四個參數(shù)分別是左上角坐標和右下角坐標注意不是中心點加寬高這里用 bbox 的 x、y 和 x w、y h 拼出右下角。顏色用了 BGR 的 (0, 255, 255)也就是黃色和黃牌本身顏色一致如果覺得看不清可以換成紅色 (0, 0, 255)。我一般會抽 20 到 30 張圖重點看三種情況框是否緊貼車牌邊緣、是否把車燈或車身裝飾條也框了進去、車牌字符區(qū)域是否完整包含在框內。抽樣最好覆蓋不同光線條件因為黃牌在逆光和夜間反光時的視覺特征差別很大白天看著正常的標簽到了強反光場景可能就偏了。3.3 轉 YOLO 格式生成 txt 標簽并整理訓練目錄YOLO 系列訓練需要每個圖像對應一個同名 txt 文件每行格式是類別ID 中心點x 中心點y 寬 高全部歸一化。JSON 轉 txt 的腳本可以一次性處理訓練集和驗證集import json import os def convert_json_to_yolo(json_path, out_dir): data json.load(open(json_path, encodingutf-8)) os.makedirs(out_dir, exist_okTrue) for img_info in data[images]: name os.path.splitext(img_info[file_name])[0] img_w, img_h img_info[width], img_info[height] lines [] for a in data[annotations]: if a[image_id] ! img_info[id]: continue if a.get(iscrowd, 0): continue cx, cy, w, h coco_to_yolo(a[bbox], img_w, img_h) lines.append(f0 {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) txt_path os.path.join(out_dir, name .txt) with open(txt_path, w, encodingutf-8) as f: f.write(\n.join(lines) \n) convert_json_to_yolo(annotations/train.json, labels/train) convert_json_to_yolo(annotations/val.json, labels/val)參數(shù)說明json_path 是輸入的 COCO 風格標注文件out_dir 是輸出的 txt 目錄腳本會自動創(chuàng)建。類別 ID 直接寫死為 0因為當前數(shù)據(jù)集只有 yellow_plate 一個類別如果后續(xù)自己擴展成多類別必須建一個類別名到 ID 的映射字典用 name 查 ID不能繼續(xù)寫死。這里顯式跳過 iscrowd 為 1 的標注避免群體區(qū)域被當成單個車牌來訓練。寫文件時每行末尾加了換行符YOLO 訓練不受影響但用其他數(shù)據(jù)檢查腳本按行讀取時更省事。沒有標注的圖片會生成空 txt 文件YOLO 訓練會把它當作背景樣本跳過可以保留也可以刪除取決于你需不需要負樣本。4. 黃牌檢測訓練與評估四個避坑記錄和排查方法4.1 現(xiàn)象遠距離黃牌漏檢嚴重模型只撿近處車牌模型在驗證集上跑出來的 mAP 看起來不錯但實際監(jiān)控畫面里遠處貨車的小車牌基本檢測不到。原因是兩層一是輸入分辨率不夠YOLO 默認 640 輸入下一個 30 像素寬的小車牌經過幾次下采樣后特征圖里只剩一兩個像素分類頭很難給出置信度二是黃牌在遠處本身對比度低和灰色路面、車廂陰影混在一起特征更弱。解決思路是先把訓練輸入分辨率提到 960 甚至 1280同時開啟多尺度訓練讓模型見過不同尺寸的目標如果框架支持按 bbox 面積做采樣加權可以把小目標樣本的權重調高。注意分辨率提高后顯存占用明顯上漲batch size 要相應調小我試過從 640 換到 960batch 從 64 降到 32 才沒爆顯存。4.2 現(xiàn)象黃色誤檢到車燈、車身貼紙和道路標線訓練完可視化 bad case 發(fā)現(xiàn)模型經常把貨車車燈、車身黃色貼紙、甚至路面黃色標線框成車牌。原因在于黃牌的特征主要是顏色加矩形紋理而 HSV 空間里車燈反光和黃色貼紙與車牌顏色非常接近僅靠顏色分支很難區(qū)分。解決分三步訓練階段做 HSV 顏色抖動增強讓模型不只依賴顏色這一個維度數(shù)據(jù)層面把包含車燈的難例挑出來加入訓練集或者做復制粘貼增強把這些區(qū)域作為正樣本讓模型學會區(qū)分推理階段可以配合后處理規(guī)則過濾掉寬高比不在合理范圍、置信度低于閾值的框。如果用的檢測框架支持設置忽略區(qū)域也可以把車燈位置標成 ignore 區(qū)域讓模型在這些位置不計算損失實測對誤檢率下降很明顯。4.3 現(xiàn)象mAP 虛高但實際框偏標注框邊界不齊有一次訓練出來的模型 mAP0.5 到了 0.9 左右看起來很好但部署到現(xiàn)場發(fā)現(xiàn)檢測框總是往左偏半個字符的位置。后來查了幾張驗證集圖像的標注發(fā)現(xiàn)不少框的左邊沿壓到了字符邊緣甚至切進字符里右邊卻多留了一個字符的空白。原因是標注階段不同的人對車牌邊界理解不一致有的以字符區(qū)域為準有的以底板邊緣為準。解決方法是先統(tǒng)一標注規(guī)范黃牌框必須包含完整底板左右留 2 到 3 像素邊距上下留 1 到 2 像素然后寫腳本統(tǒng)計全部 bbox 的寬高比把偏離均值超過 30% 的框列出來人工復查。從那以后我每次拿到數(shù)據(jù)集都會先做一遍 bbox 寬高比分布統(tǒng)計再決定要不要修標注。4.4 現(xiàn)象訓練正常但驗證時 loss 震蕩JSON 和 txt 標簽不一致訓練日志里 loss curve 在某個 epoch 后開始周期性震蕩排查后發(fā)現(xiàn)是因為數(shù)據(jù)加載順序是隨機的但有的 txt 標簽和圖像文件不是一一對應。具體原因是前面提到的掃描目錄時把 val 圖像也掃進了訓練集導致同一張圖一會兒用訓練標簽、一會兒用驗證標簽標簽內容不一致loss 自然震蕩。解決方法是嚴格以 JSON 的 file_name 為準構建數(shù)據(jù)集禁止單獨掃描目錄同時訓練前寫一個校驗腳本檢查每個 txt 標注文件對應的圖像是否存在于訓練目錄、train 和 val 兩個集合是否完全不相交。這個校驗腳本十幾行就能寫完但能避免后面浪費好幾個小時的排錯時間。另外一個容易忽略的點是如果 JSON 里存在 iscrowd1 的標注部分框架會忽略部分框架不處理轉換時統(tǒng)一過濾掉才能保證結果一致。5. 進階針對黃牌的數(shù)據(jù)增強與難例篩選把召回率再往上拉5.1 針對性增強別用通用增強按黃牌物理特征來設計通用增強里的隨機旋轉 90 度對車牌檢測是災難因為真實場景里車牌不會倒著出現(xiàn)。我一般用 Albumentations 做一組針對性增強核心是模擬光線變化和運動模糊import albumentations as A transform A.Compose([ A.HueSaturationValue(hue_shift_limit10, sat_shift_limit30, val_shift_limit20, p0.5), A.RandomBrightnessContrast(brightness_limit0.2, contrast_limit0.2, p0.5), A.MotionBlur(blur_limit5, p0.3), A.RandomSizedBBoxSafeCrop(width960, height960, p0.5) ], bbox_paramsA.BboxParams(formatcoco, label_fields[labels]))參數(shù)說明HueSaturationValue 的 val_shift_limit 控制亮度偏移模擬不同時間段的光照sat_shift_limit 控制飽和度抖動對應晴天和陰天的顏色差異MotionBlur 模擬車輛行駛和相機抖動產生的模糊RandomSizedBBoxSafeCrop 會在裁剪時保持 bbox 完整width 和 height 設為 960對應訓練分辨率。注意 hue 偏移不要超過 15否則車牌顏色會從黃色漂移到綠色或橙色反而制造錯誤樣本?;貍髟鰪妳?shù)時bbox_params 的 format 必須寫 coco和原始標注格式一致如果用 yolo 需要提前做歸一化。5.2 難例篩選用驗證集錯誤樣本反哺訓練集增強做到位之后下一步是難例挖掘。把訓練好的模型在驗證集上推理收集所有漏檢和誤檢的樣本按置信度排序人工挑出典型的難例回填到訓練集。這一步不需要全部重新標注常見做法是只挑 50 到 100 張配合復制粘貼增強把難例復制到多張圖里模型很快就能學會這些特征。驗證時不僅看整體 mAP還要單獨統(tǒng)計黃牌類別的召回率把驗證集里所有包含黃牌的圖像挑出來按類別算 recall這個數(shù)字比整體 mAP 更能反映實際問題有沒有解決。如果召回率上去了但誤檢也變多再回頭看 PR 曲線的平衡點調整置信度閾值和 NMS 參數(shù)。我從那次踩坑之后養(yǎng)成了一個習慣每次拿到新數(shù)據(jù)集訓練前強制走一遍標注可視化復查 bbox 寬高比統(tǒng)計 訓練驗證集合交集檢查三步訓練后再加一次黃牌類別召回率統(tǒng)計才敢部署。這套流程幫我攔下了不少數(shù)據(jù)集層面而非模型層面的問題。這份數(shù)據(jù)集配合前面的腳本從 JSON 解析、可視化到 YOLO 格式轉換整條鏈路可以直接復現(xiàn)希望幫到你。本文還有配套的精品資源點擊獲取