據(jù)集實戰(zhàn):從YOLOv8訓練到安全帽反光衣檢測)
簡介面向智慧工地監(jiān)控場景的視覺檢測數(shù)據(jù)集適用于反光衣穿戴、安全帽佩戴與行人檢測等任務可直接用于主流目標檢測算法的訓練與驗證。資源包共2000個文件壓縮包約585.74MB主要包含YOLO格式標簽、VOC格式標簽、工地實拍圖像等并附完整圖片下載鏈接所有標簽均由LabelImg工具手工標注背景覆蓋多視角、多場景光照與遮擋情況貼近真實環(huán)境。數(shù)據(jù)集覆蓋3065張真實工地監(jiān)控樣本已經(jīng)實際項目驗證算法擬合效果良好可顯著減少數(shù)據(jù)清洗與標注成本雙格式標簽也便于在不同訓練框架間遷移使用。目前已有1436人學習下載適合需要高質(zhì)量安全帽佩戴與反光衣穿戴檢測數(shù)據(jù)的工程師與研究人員直接使用無論是快速驗證算法還是落地部署均有實用價值。1. 智慧工地數(shù)據(jù)集到手先看這兩樣標簽格式和拍攝場景做智慧工地項目最難受的不是算法選型而是數(shù)據(jù)。你部署到工地現(xiàn)場的攝像頭視角是斜的、逆光的、揚塵的跟網(wǎng)上下載的公開數(shù)據(jù)集完全是兩個世界。這套“智慧工地數(shù)據(jù)集3065張反光衣安全帽行人檢測數(shù)據(jù)集”我拆過一遍先說結(jié)論它是真實工地監(jiān)控抓拍不是擺拍場景包含多視角和多背景對落地項目很有參考性。它同時給出 VOC 和 YOLO 兩種標簽這意味著你不用為格式轉(zhuǎn)換折騰太久YOLO 系列、SSD、CenterNet、PP-YOLO、YOLOX、PP-PicoDet 都能直接拿來訓練。適合誰準備做安全帽佩戴檢測、反光衣穿戴檢測、人員入侵告警的工程師也適合剛?cè)腴T想拿真實數(shù)據(jù)練手的學生。接下來我會把它怎么組織、怎么用于訓練、有哪些坑一條條講清楚。2. 把數(shù)據(jù)集拆開看VOC和YOLO標簽的目錄結(jié)構、格式差異與一致性校驗2.1 數(shù)據(jù)集目錄長什么樣images、xml、txt 怎么對應拿到壓縮包后解壓出來的目錄不是千篇一律的images和labels兩個文件夾而是像safety_vest1754.txt、safety_vest1793.txt這樣的文件直接散落在某個目錄下。這是很多標注工具導出時的習慣圖片在JPEGImages或images目錄與圖片同名的.xml在Annotations.txt在labels。但是這套數(shù)據(jù)集的壓縮包里因為上傳限制只帶了部分圖片你需要先看清目錄結(jié)構再把圖片和標簽文件建立對應的映射關系。常見做法是先把所有.jpg文件放進images目錄把對應的.xml放進Annotations把.txt放進labels。需要注意的是圖片文件名和標簽文件名必須完全一致不含擴展名否則后續(xù)訓練時 YOLO 會直接跳過所有找不齊配對的文件。我一般寫一個 Python 腳本跑一遍把缺失對應關系的文件揪出來。import os from pathlib import Path img_dir Path(images) # 圖片目錄 xml_dir Path(Annotations) # VOC xml 目錄 txt_dir Path(labels) # YOLO txt 目錄 img_files {p.stem: p for p in img_dir.glob(*.jpg)} xml_files {p.stem: p for p in xml_dir.glob(*.xml)} txt_files {p.stem: p for p in txt_dir.glob(*.txt)} # 校驗圖片和標簽是否一一對應 missing_xml [k for k in img_files if k not in xml_files] missing_txt [k for k in img_files if k not in txt_files] print(缺 xml:, missing_xml) print(缺 txt:, missing_txt)先跑一遍這個腳本再開始訓練。缺標簽的文件要么刪除要么單獨挑出來手工補標否則訓練時類別數(shù)看起來齊全實際參與訓練的樣本少了一大截。2.2 VOC 的 xml 到底記了什么從object到bndboxVOC 格式的 xml 是手動標注工具 LabelImg 輸出的標準結(jié)構。每個 xml 文件描述一張圖片的寬度、高度、通道數(shù)以及圖片里出現(xiàn)的每個目標。核心在object節(jié)點annotation folderimages/folder filenamesafety_vest1754.jpg/filename size width1920/width height1080/height depth3/depth /size object namesafety_vest/name bndbox xmin734/xmin ymin286/ymin xmax929/xmax ymax612/ymax /bndbox /object /annotationname是類別名從摘要信息來看這套數(shù)據(jù)集包含反光衣safety_vest、安全帽helmet、行人person這幾個類別但具體類別名需要打開一個 xml 確認。bndbox是邊界框的左上角和右下角像素坐標。這里要注意VOC 的坐標是絕對值、從 0 開始YOLO 的格式是歸一化后的相對值兩者轉(zhuǎn)換時經(jīng)常會有人把xmax當成寬度直接算結(jié)果框全部偏移。2.3 YOLO 的 txt 邊界框歸一化坐標轉(zhuǎn)換與一致性校驗腳本YOLO 格式每一行代表一個目標結(jié)構是class_id x_center y_center width height前四個值都除以圖片寬高歸一化到 01。比如上面那個框轉(zhuǎn)成 YOLO 格式后大概是class_id 0.433 0.416 0.102 0.302。手工寫轉(zhuǎn)換腳本容易出錯我習慣直接對每一張圖片做反向校驗讀 txt 里的坐標乘回圖片寬高再算 IoU 或直接對比是否落在 xml 的框內(nèi)。import cv2 import numpy as np def voc_xml_to_yolo_txt(xml_path, img_path, txt_path, class_map): with open(xml_path, r, encodingutf-8) as f: xml_content f.read() import xml.etree.ElementTree as ET root ET.fromstring(xml_content) img cv2.imread(img_path) h, w img.shape[:2] lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_map: print(f未知類別 {name} in {xml_path}) continue class_id class_map[name] bnd obj.find(bndbox) xmin float(bnd.find(xmin).text) ymin float(bnd.find(ymin).text) xmax float(bnd.find(xmax).text) ymax float(bnd.find(ymax).text) x_center (xmin xmax) / 2 / w y_center (ymin ymax) / 2 / h width (xmax - xmin) / w height (ymax - ymin) / h lines.append(f{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}) with open(txt_path, w) as f: f.write(\n.join(lines))這段腳本會讀取 xml用 OpenCV 拿到真實圖片寬高再算歸一化坐標。注意class_map里類別順序YOLO 訓練時的類別順序必須和這個 map 完全一致否則訓練時類別就亂了。這也是很多人用現(xiàn)成 VOC 轉(zhuǎn) YOLO 工具后訓練 Loss 不下降的原因之一——類別 id 對不上。3. 用這個數(shù)據(jù)集跑通 YOLOv8從數(shù)據(jù)集配置文件到訓練命令3.1 先按 YOLO 標準把數(shù)據(jù)集目錄重新組織一遍雖然壓縮包里已經(jīng)給了 YOLO 格式的 txt但直接用散落的文件訓練還是容易出問題。Ultralytics YOLOv8 默認的目錄結(jié)構是images/train、images/val、labels/train、labels/val。我一般把 3065 張圖按 8:2 劃分先創(chuàng)建目錄再用shutil拷貝文件。注意這里不推薦直接把原圖移動走因為壓縮包里可能還有部分圖片在別處拷貝出來訓練原始文件留著備用。mkdir -p dataset/images/train dataset/images/val dataset/labels/train dataset/labels/val然后用腳本按隨機種子劃分。為了讓每次實驗可復現(xiàn)我固定random.seed(42)。劃分完成后檢查一下每個類別的樣本數(shù)量是否均勻。智慧工地場景里未戴安全帽的樣本通常遠少于戴了安全帽的如果驗證集里全是戴帽子的訓練結(jié)果會虛高。3.2 修改數(shù)據(jù)集 yaml類別、路徑、train/val 劃分YOLOv8 通過 yaml 文件告訴模型數(shù)據(jù)在哪、類別有幾個。這個文件必須放到一個固定的位置我會把絕對路徑寫進去避免訓練時工作目錄切換導致路徑失效。# smart_construction.yaml path: /home/yourname/smart_construction_dataset # 數(shù)據(jù)集根目錄 train: images/train val: images/val nc: 3 names: [helmet, safety_vest, person]注意names的順序決定了訓練輸出的類別 id。如果你拿到的 txt 里類別 id 順序不是這個比如反光衣是 0安全帽是 1一定要先打開幾個 txt 看一下再按實際順序改。不要迷信摘要里寫的“類別齊全”以代碼跑出來的類別分布為準。有一種情況是 txt 里存在class_id超出nc-1這類標簽會直接報錯需要提前過濾。3.3 訓練命令與關鍵參數(shù)imgsz、batch、epochs 的現(xiàn)場調(diào)法數(shù)據(jù)集準備好后直接運行 YOLOv8 訓練命令。這套數(shù)據(jù)集是 1080P 工地監(jiān)控畫面我建議先以 640 輸入尺寸跑通流程后面再試 1280 看小目標收益。yolo detect train \ modelyolov8n.pt \ datasmart_construction.yaml \ imgsz640 \ epochs100 \ batch16 \ device0 \ workers4 \ projectruns/smart_construction \ nameyolov8n_640這里modelyolov8n.pt是預訓練權重不是隨意選的。第一次跑用 n 模型最快目的是確認數(shù)據(jù)鏈路沒問題、Loss 能正常下降。batch16在 1080Ti 或 V100 上跑得動顯存不夠就降到 8。workers4是 CPU 讀圖線程數(shù)工地監(jiān)控圖片分辨率高CPU 瓶頸明顯跑滿后可以再調(diào)高。訓練時盯著終端里的box_loss、cls_loss和mAP50前幾輪如果cls_loss一直橫著不降多半是標簽類別映射錯了停掉改 yaml。4. 踩坑記錄反光衣數(shù)據(jù)集在訓練和部署中的五個典型翻車現(xiàn)場4.1 訓練 Loss 正常但 mAP 上不去檢查標簽類別是否對錯現(xiàn)象訓練完 mAP50 只有 0.2 左右但 Loss 曲線看起來正常損失值一直在降。原因txt 里的 class_id 跟實際類別不一致比如反光衣 id 是 0但 yaml 里配的 0 代表安全帽。模型一直在學錯誤的映射Loss 能降但驗證時類別錯位mAP 崩盤。解決訓練前隨機抽 20 個 txt寫腳本把歸一化坐標換算回像素畫框在原圖上人眼看一遍框和標簽是否匹配。不要信任任何轉(zhuǎn)換工具的輸出必須可視化驗證。4.2 白天漏掉反光衣強光下的白平衡與增廣策略現(xiàn)象反光衣在白天強光下反光嚴重穿在身上變成一片亮斑檢測框要么框小了要么根本檢不出來。原因工地攝像頭逆光或陽光直射時反光衣的高光區(qū)域過曝紋理完全丟失。用默認馬賽克增強時顏色被隨機扭曲反光衣的熒光色特征被削弱。解決訓練時降低hsv_h、hsv_s的增強幅度尤其不要對顏色做大幅隨機。反光衣的熒光黃綠是區(qū)分背景的關鍵顏色抖動過大會讓模型去學錯誤特征。另外可以在數(shù)據(jù)集里補充一些過曝樣本或者對圖片做 gamma 校正后再訓練一輪。4.3 數(shù)據(jù)集只有 3065 張卻要求多視角別省驗證集現(xiàn)象訓練集和驗證集隨機劃分但驗證集 mAP 高一到現(xiàn)場新攝像頭就露餡漏檢大量人員。原因3065 張圖來自多個場景但隨機劃分后同一場景的圖片可能同時出現(xiàn)在訓練集和驗證集模型記住了場景背景而不是目標本身?,F(xiàn)場換了個視角背景不同模型就失效。解決按攝像頭視角或場景粒度劃分。理想做法是確認每張圖片來自哪個攝像頭把視角差異最大的作為驗證集。如果沒辦法確認至少先按時間間隔采樣每隔 10 張取 2 張做驗證保證同場景時序相近的圖片不會同時出現(xiàn)在兩端。4.4 百度云下載的文件包和本地文件對不上先算哈希再解壓現(xiàn)象壓縮包里只帶部分圖片完整圖片要從百度云下。下載回來后訓練時發(fā)現(xiàn)很多 txt 沒有對應圖片報錯一堆。原因壓縮包里的部分圖片和解壓后的散裝文件混在一起文件名重復覆蓋或者云盤下載斷點續(xù)傳導致文件損壞打開圖片時cv2.imread返回空對象。解決解壓后立即用哈希校驗。對每個文件計算 MD5再跟包里的清單文件比對。如果包里沒有清單就把所有 JPG 的尺寸讀一遍凡是imread失敗或尺寸為 0 的圖片一律刪掉對應的 txt 和 xml 也要刪掉否則訓練時會導致 batch 報錯。4.5 安全帽是小目標在 1080P 下直接縮圖訓練會丟精度現(xiàn)象攝像頭 1920×1080人站在 20 米外安全帽在圖像里只有 20×20 像素。640 輸入尺寸訓練完成后小目標基本檢不到。原因YOLOv8 的默認下采樣倍數(shù)是 32640 輸入時特征圖 20×20對應原圖一個格子約 96×96 像素安全帽 20 像素無法激活足夠的特征。解決先嘗試imgsz1280訓練。如果顯存不夠就采用切圖策略把原圖切成 4 個 960×960 的區(qū)域分別檢測最后合并框。還有一種思路是單獨訓練一個專門檢測安全帽的模型輸入尺寸設 640但 anchor-free 的模型對絕對尺寸敏感改輸入尺寸后要重新調(diào)imgsz。5. 模型評估和告警邏輯把 mAP 轉(zhuǎn)成工地現(xiàn)場能用的報警5.1 用混淆矩陣看安全帽和反光衣到底誰在漏檢訓練結(jié)束后不要只看 mAP50 一個數(shù)。YOLOv8 會輸出confusion_matrix.png和results.csv?;煜仃嚴锬芮宄吹矫總€類別之間的混淆情況helmet被預測成person、safety_vest被預測成背景這些都是現(xiàn)場報警誤報、漏報的根源。import pandas as pd results pd.read_csv(runs/smart_construction/yolov8n_640/results.csv) print(results.columns) # 看最后一輪的 mAP50 和 mAP50-95 print(results.iloc[-1][metrics/mAP50(B)])一般工地場景只要精確率和召回率平衡不用追求 mAP50-95 很高因為部署時通常會設一個較高的 confidence 閾值0.50.6來減少誤報。我在實際項目里習慣看 F1 曲線找一個合適的 confidence 閾值讓誤報和漏報達到現(xiàn)場可接受的程度。5.2 從檢測框到區(qū)域入侵設置指定的作業(yè)區(qū)域智慧工地里檢測到人只是第一步更常見的是劃一個臨時作業(yè)區(qū)人員進入該區(qū)域但沒有穿反光衣才報警。這個邏輯不能只靠檢測框坐標需要把圖像坐標映射到真實地面坐標。簡單做法是在監(jiān)控畫面里用多邊形劃定危險區(qū)判斷檢測框底邊中點是否落在多邊形內(nèi)即可。import cv2 import numpy as np def point_in_polygon(x, y, pts): n len(pts) inside False p1x, p1y pts[0] for i in range(1, n 1): p2x, p2y pts[i % n] if (y p1y) ! (y p2y) and (x (p2x - p1x) * (y - p1y) / (p2y - p1y 1e-6) p1x): inside not inside p1x, p1y p2x, p2y return inside # 假設檢測框為 (x1, y1, x2, y2)取底邊中點 foot_x (x1 x2) / 2 foot_y y2 region [(100, 300), (500, 320), (480, 700), (80, 650)] # 多邊形頂點 if point_in_polygon(foot_x, foot_y, region): print(人員進入危險區(qū)域)注意1e-6是為了防止除零。實際工地監(jiān)控畫面有透視變形多邊形標定時不要只在截圖里畫最好用現(xiàn)場測量點標定。另一條重要規(guī)則報警要去抖動連續(xù) N 幀比如 5 幀都滿足條件再觸發(fā)否則一個行人走過去就會產(chǎn)生十幾次誤報。5.3 穿戴合規(guī)判斷先檢測人再匹配安全帽框安全帽檢測比反光衣檢測更難因為安全帽戴在頭頂框非常小?,F(xiàn)場合規(guī)判斷需要結(jié)合人的檢測框安全性匹配先找到person框再找helmet框只有當頭盔框的底邊中心點落在人框的頂部區(qū)域比如人框最上面 20% 高度內(nèi)才認為戴了。直接用全局匹配極易把遠處另一個人的安全帽匹配到這個人身上。def helmet_matched(person, helmet): px1, py1, px2, py2 person hx1, hy1, hx2, hy2 helmet person_top py1 0.2 * (py2 - py1) # 人的頭頂區(qū)域上限 # 頭盔底邊中心 hx_c (hx1 hx2) / 2 hy_b hy2 if (px1 hx_c px2) and (py1 hy_b person_top): return True return False這段代碼的邏輯是安全帽的底邊應位于人體檢測框的最上部 20% 區(qū)域。如果安全帽框在人框之外或太靠下判定為未戴帽。現(xiàn)場攝像頭角度傾斜時這個閾值要適當放寬否則戴著帽子的工人在畫面邊緣會被誤判成未戴。多跟幾個角度做迭代最終固定參數(shù)。6. 進階技巧用這 3065 張圖做預標注再擴成自己的數(shù)據(jù)集6.1 用訓練好的模型給新視頻做批量預標注既然這個數(shù)據(jù)集質(zhì)量可靠在它上面訓練出來的模型可以直接部署到現(xiàn)場同時也可以用來給新攝像頭錄制的視頻做預標注。具體做法是用yolo predict跑視頻幀把輸出結(jié)果轉(zhuǎn)成 VOC 或 YOLO 格式再導入 LabelImg 手工校正。這樣你想擴樣本到 10000 張時只需要檢查修改不需要從頭畫框。yolo detect predict \ modelruns/smart_construction/yolov8n_640/weights/best.pt \ sourcenew_video.mp4 \ save_txtTrue \ save_confTrue \ imgsz640save_txtTrue會生成與視頻幀同名的 txt里面是檢測到的類別和歸一化坐標。把這些 txt 和抽取的原圖扔回第 3 章的目錄結(jié)構里用腳本把帶重疊框的樣本剔除剩下來的就是可用的預標注數(shù)據(jù)。注意預標注的 confidence 閾值要設低一點比如 0.3寧可多標一些假目標也不要漏掉真實目標手工刪除比手工補框省時間。6.2 數(shù)據(jù)增強別盲目反光衣的顏色是特征顏色抖動要克制很多人訓練時開了 YOLO 默認的增強包括hsv_h0.015、hsv_s0.7、hsv_v0.4。對常規(guī)物體這沒問題但反光衣的熒光色是區(qū)分于背景的重要特征。如果色相隨機偏移過大熒光綠變成灰色模型就會開始依賴紋路和形狀魯棒性反而下降。我的做法是保留空間增強平移、縮放、翻轉(zhuǎn)把色相增強降到接近 0飽和度增強可以保留但要壓制幅度。如果你發(fā)現(xiàn)模型對白色反光衣和黃色反光衣判斷不穩(wěn)優(yōu)先調(diào)這里。6.3 導出到 ONNX 部署到 Jetson 這類邊緣設備訓練完成后導出成 ONNX 格式再轉(zhuǎn) TensorRT可以在不損失太多精度的前提下大幅提速。yolo export modelruns/smart_construction/yolov8n_640/weights/best.pt formatonnx imgsz640導出后先用onnxruntime在本地跑一遍確認和 PyTorch 輸出一致再上 Jetson 設備做trtexec轉(zhuǎn) engine。這里有個細節(jié)導出時imgsz必須和訓練時一致否則模型輸出的特征圖尺寸和錨框比例會變檢測框全偏。另外工地現(xiàn)場攝像頭常用 H.264 碼流拉流解碼時偶發(fā)丟幀輸入到模型的圖片如果出現(xiàn)黑邊或撕裂會直接導致漏檢。我遇到過類似問題后來在部署時強制檢查每幀圖像素均值低于某個閾值就丟幀不推理從那以后每次部署邊緣設備都把這個邊界條件寫進檢查清單。希望這些拆解和踩坑記錄能幫你在智慧工地項目里少走彎路。本文還有配套的精品資源點擊獲取