統(tǒng)計實踐與避坑指南)
簡介基于YOLOv8的智能會議室參會人數(shù)統(tǒng)計項目是一份面向計算機相關(guān)專業(yè)學(xué)生與初學(xué)者的完整實戰(zhàn)資源適合畢業(yè)設(shè)計、課程設(shè)計或大作業(yè)場景解決會議場景下多人目標檢測與人數(shù)統(tǒng)計的需求。壓縮包共8個文件以Python腳本、PyTorch模型權(quán)重和說明文檔為主整體約15.91MB覆蓋模型訓(xùn)練、視頻檢測、可視化界面等核心模塊并內(nèi)置訓(xùn)練好的模型權(quán)重下載后按README指引即可快速運行。目前已有34人學(xué)習(xí)下載可作為同類目標檢測任務(wù)的參考實現(xiàn)。項目內(nèi)代碼均經(jīng)過測試并成功運行可產(chǎn)出核心指標曲線圖、混淆矩陣、F1分數(shù)曲線、精確率-召回率曲線以及驗證集預(yù)測結(jié)果和標簽分布圖方便在答辯或課程展示中直觀驗證效果完整數(shù)據(jù)集與部署說明進一步降低了上手門檻既可直接使用也可在此基礎(chǔ)之上二次開發(fā)。1. 為什么會議室人數(shù)統(tǒng)計聽著簡單做起來全是坑會議室參會人數(shù)統(tǒng)計聽起來就是「打開攝像頭數(shù)一下有幾個人頭」但真做起來會發(fā)現(xiàn)事情沒這么順攝像頭通常是俯拍角度后排人頭小到模型認不出來投影儀一亮背景亮斑把人影邊緣整個吞掉坐著的人只露出半個頭肩檢測框在連續(xù)幀里抖來抖去人數(shù)從 6 跳到 11 再跳回 6。「基于YOLOv8的智能會議室參會人數(shù)統(tǒng)計」這個項目解決的問題不是「能不能檢測到人」而是「在會議室這種半靜態(tài)、強遮擋、固定視角的場景下怎么可靠地告訴你現(xiàn)在屋里到底有幾個人」。它把目標檢測、視頻流處理、人數(shù)去重、可視化界面和部署串成一條完整鏈路適合課程設(shè)計和畢業(yè)設(shè)計也適合想快速驗證「檢測 計數(shù) 界面」這套打法的從業(yè)者。下面按做這個方案的真實順序從選型拆到踩坑。2. 方案選型YOLOv8 為什么是會議室人數(shù)統(tǒng)計的可靠選擇2.1 先想清楚人數(shù)統(tǒng)計到底在統(tǒng)計什么做方案之前先分清楚需求。靜態(tài)圖場景只需要在某一時刻數(shù)一次人頭比如門禁抓拍會議室通常不是這個用法攝像頭常開觀眾席上的人一坐就是半小時系統(tǒng)要持續(xù)給出「當(dāng)前有多少人」的數(shù)字這就需要視頻流處理。視頻流計數(shù)有兩條技術(shù)路線。第一條是樸素的「每幀檢測、每幀計數(shù)」把當(dāng)前幀里 YOLOv8 輸出的 person 框數(shù)量直接當(dāng)成人數(shù)。這條路線實現(xiàn)成本低但檢測框在幀間會有輕微抖動漏檢一兩幀人數(shù)馬上跳變演示時看起來很業(yè)余。第二條是「檢測 跟蹤」先檢測出人再用跟蹤器給每個人分配一個穩(wěn)定的 ID按 ID 去重計數(shù)。會議室里的人長時間保持坐姿ID 一旦建立就很穩(wěn)定即便中間有幾幀漏檢跟蹤器也能靠運動預(yù)測補回來。我做這類項目時一般建議走第二條路線但不用上 DeepSort 這種重方案。會議室場景下人的運動幅度小用輕量跟蹤就能解決問題后面第 5 章會展開講。下表的結(jié)論是我多年下來比較認可的選型依據(jù)方案實現(xiàn)成本人數(shù)輸出穩(wěn)定性適用場景純檢測 幀內(nèi)計數(shù)低差檢測抖動直接傳導(dǎo)靜態(tài)抓拍、快速驗證檢測 輕量跟蹤IOU/ByteTrack中好能扛住幀間漏檢會議室、教室、門店密度估計CSRNet 類高依賴訓(xùn)練數(shù)據(jù)質(zhì)量密集人群不需要坐標框2.2 為什么不是 OpenCV 背景差分、不是人頭密度圖有人會問會議室背景固定能不能用 OpenCV 背景差分檢測人背景差分在會議室場景屬于典型的「實驗室里成立、落地就翻車」人長時間坐著不動會逐漸被模型吸收成背景窗簾被風(fēng)吹動、投影儀內(nèi)容切換、有人開關(guān)燈都會觸發(fā)大范圍誤報。會議室恰恰是「背景在變、人不動」的反直覺組合背景差分幾乎必跪。密度估計方案如 CSRNet、DM-Count的問題在于標注成本和輸出形態(tài)。這類模型訓(xùn)練需要逐點標注人群密度圖數(shù)據(jù)準備工作量遠超畫框而且輸出是一張熱力值圖只能告訴你「這片區(qū)域大概有多少人」給不出每個人在哪、更做不了 ID 跟蹤會議室門禁聯(lián)動這種需求它接不住。YOLOv8 這類 anchor-free 檢測器在會議室這種中等密度場景下是更實際的選項COCO 預(yù)訓(xùn)練權(quán)重里 person 類已經(jīng)很強不需要從零訓(xùn)練輸出天然帶框和置信度往下接跟蹤、接界面都很順。標題里提到「完整數(shù)據(jù)集」實際意義就是給你一個在通用 person 模型基礎(chǔ)上做會議室微調(diào)的起點不是讓你從隨機權(quán)重開始煉。2.3 模型選型n / s / m 三檔怎么選YOLOv8 有 n / s / m / l / x 五檔會議室人數(shù)統(tǒng)計用到 l 和 x 屬于浪費算力。n 最快但精度相對低s 在速度和精度之間最均衡m 適合對精度有執(zhí)念且機器不太差的場景。選擇依據(jù)主要是部署硬件純 CPU 機器用 n入門獨顯用 s嵌入式板子用 n 再導(dǎo)出量化模型。做畢設(shè)演示這件事我的建議是別盲目追高。如果現(xiàn)場機器是普通筆記本的核顯跑 s 會卡到影響演示節(jié)奏老老實實用 n 加 imgsz640四平八穩(wěn)。如果機房給的是 GTX 1660Ti 這類 6G 顯存的入門卡那上 s 沒有任何壓力訓(xùn)練時間和顯存占用都可控。至于 m適合你打算把模型做成產(chǎn)品原型、后面還要換更大數(shù)據(jù)集的場景。從網(wǎng)絡(luò)結(jié)構(gòu)角度看YOLOv8 的 backbone 用了 C2f 模塊和 SPPF 金字塔池化neck 是 PAN-FPN 雙向融合這些設(shè)計讓它對中小尺寸目標比較友好。會議室俯拍畫面里的人頭占比偏小尤其后排可能只有三四十個像素結(jié)構(gòu)上選對模型檔次比調(diào)參更重要。2.4 整體流程拆解從視頻幀到界面數(shù)字整個項目的落地路徑可以拆成一條鏈視頻流或視頻文件 → 丟幀 → letterbox 預(yù)處理 → YOLOv8 推理 → NMS 去重 → 跟蹤 ID 關(guān)聯(lián) → 人數(shù)統(tǒng)計與平滑 → 可視化界面顯示與記錄。訓(xùn)練環(huán)節(jié)在這條鏈之前數(shù)據(jù)準備和模型調(diào)優(yōu)的產(chǎn)出是 best.pt 權(quán)重文件推理環(huán)節(jié)加載它。部署教程里做的事情本質(zhì)上就是把這條鏈固定成兩個入口一條命令行入口負責(zé)訓(xùn)練和驗證一條圖形界面入口負責(zé)加載模型、讀視頻、顯示結(jié)果。界面是給答辯和演示看的訓(xùn)練和推理是給功能兜底的兩條腿缺一不可。接下來按這條鏈的順序先講數(shù)據(jù)準備再講訓(xùn)練調(diào)參然后講最容易翻車的幾個細節(jié)最后說界面和部署。3. 數(shù)據(jù)集準備與標注把俯拍會議室畫面變成 YOLO 訓(xùn)練集3.1 采集規(guī)則一個會議室該拍哪些畫面數(shù)據(jù)集質(zhì)量直接決定后面所有步驟的成敗。會議室場景的采集要覆蓋真實使用中會出現(xiàn)的組合維度至少包含不同人數(shù)檔位1 人、3 人、5 人、8 人、12 人以上、不同座位區(qū)域分布、開燈和關(guān)燈兩種狀態(tài)、投影儀開啟時的強光干擾、窗戶側(cè)光、有人從門口進出、個別單人背對鏡頭坐著。數(shù)量上按做畢設(shè)和課設(shè)的慣例300 到 800 張的標注量已經(jīng)能微調(diào)出可演示的模型。單張圖內(nèi)人數(shù)不用刻意追求很多2 到 15 人的區(qū)間比較合理覆蓋程度比數(shù)量更重要。采集時盡量保留原始分辨率不要為了省硬盤壓得太狠訓(xùn)練時會統(tǒng)一縮放到 imgsz原始大圖是留給自己的一條后悔藥。3.2 標注規(guī)范與工具用哪款工具、按什么標準框標注工具用 LabelImg 或 X-AnyLabeling 都行。LabelImg 老牌穩(wěn)定導(dǎo)出 VOC XML 格式方便X-AnyLabeling 界面更好用支持自動標注輔助。對只畫矩形框的任務(wù)兩者沒有本質(zhì)差別挑個安裝順手的就行。環(huán)境上用 Python 3.8 以上的虛擬環(huán)境安裝避免系統(tǒng) Python 環(huán)境被搞亂。標注規(guī)范是新手最容易忽略的坑。會議室的特殊性在于坐姿正面前方的人可以看到上半身俯拍鏡頭下的人可能只露頭肩后排被前排擋住的人可能只露半個頭。我的做法是統(tǒng)一用 person 這一類坐姿只露頭肩的人框頭肩站姿完整的人框全身遮擋超過一半的目標不標或只標可見部分。這里的關(guān)鍵不是「框得準」而是「框得一致」——如果一半標注是頭肩、一半是全身模型會不知道該學(xué)什么。會議室俯拍畫面還有一個增強上的特殊性訓(xùn)練時不要做上下翻轉(zhuǎn)flipud0.0人不會倒著開會水平翻轉(zhuǎn)fliplr可以保留實測對視角泛化有幫助。3.3 把 VOC 轉(zhuǎn)成 YOLO 格式轉(zhuǎn)換腳本與四個邊界坑LabelImg 導(dǎo)出的是 VOC 格式 XMLYOLOv8 需要的是每張圖對應(yīng)一個 txt每一行是「類ID cx cy w h」的歸一化坐標。這個轉(zhuǎn)換腳本幾乎是每個做檢測項目的必修課下面給一個可以直接照著改的版本import xml.etree.ElementTree as ET import os from glob import glob # 類別映射本項目只有 person 一個類別 CLASSES {person: 0} def convert_voc_to_yolo(xml_path, out_txt_path): tree ET.parse(xml_path) root tree.getroot() # 尺寸從 XML 里讀不要自己去讀圖片避免尺寸不一致 img_w int(root.find(size/width).text) img_h int(root.find(size/height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in CLASSES: continue box obj.find(bndbox) x1 float(box.find(xmin).text) y1 float(box.find(ymin).text) x2 float(box.find(xmax).text) y2 float(box.find(ymax).text) # 邊界處理越界坐標裁剪到圖像范圍內(nèi) x1, y1 max(x1, 0), max(y1, 0) x2, y2 min(x2, img_w), min(y2, img_h) # 過濾掉寬或高為 0 的目標 if x2 x1 or y2 y1: continue cx (x1 x2) / 2 / img_w cy (y1 y2) / 2 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h lines.append(f{CLASSES[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) with open(out_txt_path, w) as f: f.write(\n.join(lines)) xml_files glob(xmls/*.xml) os.makedirs(labels, exist_okTrue) for xml_path in xml_files: name os.path.basename(xml_path).replace(.xml, .txt) convert_voc_to_yolo(xml_path, os.path.join(labels, name))腳本邏輯不復(fù)雜但有幾個邊界處必須說明。第一圖像尺寸一定從 XML 的 size 節(jié)點讀不要為了省事直接cv2.imread取尺寸LabelImg 偶爾會記錄到原圖尺寸之外的框兩者對不上會產(chǎn)出大于 1 或小于 0 的異常坐標。第二越界框要裁剪而不是丟棄很多標注工具的框會自動外擴幾個像素直接min/max裁回圖像范圍內(nèi)即可。第三類 ID 從 0 開始計數(shù)YOLOv8 的 names 列表按索引對應(yīng)這點和 VOC 里字符串類名不同。第四txt 文件名必須和圖片文件名完全一致連后綴都對齊規(guī)則YOLOv8 訓(xùn)練時按前綴匹配圖像和標簽文件。3.4 數(shù)據(jù)集劃分與 data.yaml 配置訓(xùn)練集、驗證集、測試集的劃分要寫腳本固定隨機種子不要手動挑。會議室視頻里連續(xù)幀高度相似如果只按順序切分很容易出現(xiàn)訓(xùn)練集和驗證集里出現(xiàn)同一鏡頭的情況指標虛高。做法是先對文件列表做 shuffle再按比例切分import os import random from glob import glob random.seed(42) # 固定種子保證每次劃分結(jié)果一致 xmls glob(xmls/*.xml) random.shuffle(xmls) n len(xmls) train xmls[: int(n * 0.8)] val xmls[int(n * 0.8) : int(n * 0.9)] test xmls[int(n * 0.9) :]劃分完的目錄結(jié)構(gòu)要固定成 YOLOv8 約定的樣子images/train、images/val、labels/train、labels/val四個目錄圖片和 txt 同名。然后寫 data.yamlpath: ./dataset train: images/train val: images/val names: 0: person注意names從 0 開始數(shù)量必須比標注中的最大類 ID 大 1否則訓(xùn)練報錯。數(shù)據(jù)準備完成后我不急著訓(xùn)練而是先隨機挑幾張圖把 YOLO 格式的框畫回原圖看一眼確認沒有坐標錯位、目標遺漏。這一步 10 分鐘能省掉后面定位數(shù)據(jù)問題的半天。4. 訓(xùn)練與參數(shù)調(diào)優(yōu)從環(huán)境配置到看懂損失曲線4.1 環(huán)境配置CPU 和 GPU 兩條路都要能走通環(huán)境配置是「ubuntu20.04 搭建 YOLOv8 環(huán)境 CPU 版本」這類熱搜最常見的提問來源。其實步驟很短創(chuàng)建虛擬環(huán)境、裝 PyTorch、裝 ultralytics。沒有 N 卡的機器裝 CPU 版 PyTorch有 N 卡就裝 CUDA 版兩套命令差別只在 torch 的安裝源# 創(chuàng)建虛擬環(huán)境Python 3.8 以上 python -m venv yolo_env source yolo_env/bin/activate # CPU 機器裝 CPU 版 torch 和 ultralytics pip install torch torchvision pip install ultralytics # GPU 機器先確認驅(qū)動和 CUDA再裝 torch nvidia-smi pip install torch torchvision pip install ultralyticstorch 和 torchvision 的版本要匹配這是老生常談的坑。直接用 pip 默認源會拉互相兼容的版本別手動指定一堆小版本號。裝完跑一個驗證命令確認 torch 能看到 GPUpython -c import torch; print(torch.__version__); print(torch.cuda.is_available())GPU 機器返回True就說明環(huán)境通了。如果nvidia-smi能看到顯卡但torch.cuda.is_available()返回 False一般是 PyTorch 版本和顯卡驅(qū)動不匹配換新版本 torch 或升級驅(qū)動二選一。環(huán)境配置這個環(huán)節(jié)沒什么玄學(xué)絕大多數(shù)問題都是 Python 版本和 torch 版本配對造成的。4.2 最小訓(xùn)練命令從預(yù)訓(xùn)練權(quán)重到自己的 best.pt環(huán)境就緒后訓(xùn)練命令比大多數(shù)人想象中短。核心就是一條yolo detect train下面這個是我用過很多次的配置yolo detect train \ datadata.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch8 \ device0 \ patience30 \ projectruns/detect \ namemeeting_room \ seed42 \ optimizerauto這條命令里每個參數(shù)都有實際意義。modelyolov8n.pt指定預(yù)訓(xùn)練權(quán)重ultralytics 會在首次運行時自動下載這就是很多人問的「yolov8 哪里下載」的入口——不需要去別的地方找權(quán)重這條命令自己會拉。datadata.yaml指向上一章寫好的數(shù)據(jù)集配置。device0表示用第一塊 GPUCPU 機器改成devicecpu。project和name控制訓(xùn)練輸出目錄訓(xùn)練日志、權(quán)重、曲線圖都會落在runs/detect/meeting_room下。訓(xùn)練中斷了怎么辦不用從頭再來resumeTrue參數(shù)就是后悔藥。接上一條訓(xùn)練yolo detect train resumeTrue它會自動從上次的斷點恢復(fù)。注意 resume 依賴訓(xùn)練目錄里的last.pt所以訓(xùn)練沒跑完別刪目錄。內(nèi)存小的機器可以把 workers 從默認值調(diào)低比如加workers2否則數(shù)據(jù)加載進程會把內(nèi)存吃滿。4.3 imgsz、batch、lr0三個必調(diào)參數(shù)與它們的邊界訓(xùn)練參數(shù)里最值得花時間理解的是 imgsz、batch、lr0這三個直接決定能不能訓(xùn)練出能用的模型對應(yīng)熱搜詞里「yolov8 模型訓(xùn)練參數(shù)含義」的訴求。下面給一份按會議室場景整理的參數(shù)表參數(shù)推薦值說明imgsz640 或 736會議室俯拍人頭小640 起步后排密集建議 736batch4~16由顯存決定6G 顯存配 640 分辨率建議 8~16lr00.005數(shù)據(jù)量小于 500 張時默認 0.01 容易震蕩flipud0.0會議室場景關(guān)閉上下翻轉(zhuǎn)fliplr0.5水平翻轉(zhuǎn)對視角泛化有幫助mosaic1.0小數(shù)據(jù)集上可關(guān)掉避免目標被切碎imgsz 是第一個要決策的參數(shù)。檢測器的訓(xùn)練尺寸和推理尺寸必須一致否則訓(xùn)練時模型學(xué)的是 736 下的目標尺度推理時喂 640 的圖目標相對變小漏檢率直接上升。會議室俯拍畫面里后排人頭可能只有 30 像素我一般建議用 736代價是訓(xùn)練時間和顯存占用上漲。batch 主要受顯存約束。6G 顯存的 GTX 1660Ti 跑 640 分辨率 batch 8 沒問題上 736 就降到 batch 4。CPU 訓(xùn)練內(nèi)存 16G 以上才建議跑 batch 4再小不如換云 GPU。lr0 默認 0.01 是 COCO 大規(guī)模數(shù)據(jù)的經(jīng)驗值自己只有幾百張圖時學(xué)習(xí)率大會導(dǎo)致 loss 震蕩降到 0.005 更穩(wěn)妥優(yōu)化器選auto或AdamW都行小數(shù)據(jù)集上 AdamW 收斂更平緩。4.4 訓(xùn)練完看什么從損失曲線到驗證指標訓(xùn)練結(jié)束后runs/detect/meeting_room目錄下會自動生成results.png這就是「yolov8 畫損失函數(shù)曲線圖」的直接產(chǎn)物圖形包含 train 和 val 的 box loss、cls loss、dfl loss 以及 mAP 曲線。不要只盯 train 曲線重點看 val 側(cè)有沒有和 train 明顯背離train loss 一路下降、val loss 在某個 epoch 后開始回升這是過擬合的信號best.pt會停在回升之前的位置。曲線圖是給人看的做數(shù)據(jù)分析時我更推薦直接讀results.csvimport pandas as pd df pd.read_csv(runs/detect/meeting_room/results.csv) print(df.columns.tolist()) # 不同版本列名可能有前導(dǎo)空格先打印確認 mAP_col metrics/mAP50-95(B) best_epoch df[mAP_col].idxmax() print(fbest epoch: {best_epoch 1}, mAP50-95: {df[mAP_col].max():.4f})不同版本 ultralytics 的列名格式不完全一致打印列名再取索引比盲寫安全。訓(xùn)練完成后單獨跑一次驗證命令輸出 precision、recall、mAP50、mAP50-95 四個指標yolo detect val datadata.yaml modelruns/detect/meeting_room/weights/best.pt會議室任務(wù)的目標是「該漏的少漏、該錯的不錯」mAP50-95 到 0.7 以上、且實測場景不漏后排就可以進入部署環(huán)節(jié)了。指標只是門檻真正的好壞要在真實場景視頻里跑過才算數(shù)。5. 避坑與排查會議室人數(shù)統(tǒng)計最容易翻車的五個細節(jié)5.1 人數(shù)反復(fù)橫跳檢測框不穩(wěn)是根源現(xiàn)象視頻里人數(shù)從 5 跳到 9 再跳回 5看起來像系統(tǒng)在亂報。原因有兩層第一檢測模型在某些幀置信度不足同一幀里人漏了又補回來框數(shù)自然抖第二沒有任何幀間關(guān)聯(lián)機制每一幀都是獨立數(shù)數(shù)抖動被原樣放大。會議室的人長時間不動框閃的原因多半不是人動了而是模型在困難的幀上沒穩(wěn)住。解決分兩步。第一步給檢測結(jié)果加置信度過濾conf0.35左右起步把邊緣誤檢壓掉一部分第二步做幀間平滑最簡單的是 EMA# 幀間指數(shù)平滑平滑系數(shù)越大越遲鈍 ema 0.7 * ema 0.3 * current_count show_count round(ema)更穩(wěn)妥的做法是加跟蹤給每個人分配 ID連續(xù) N 幀確認后才計入總數(shù)離開畫面后延遲若干幀才扣除。會議室場景不需要復(fù)雜的運動模型按檢測框中心做最近鄰匹配的輕量跟蹤就夠用ByteTrack 是效果和實現(xiàn)成本最平衡的選擇。5.2 后排漏檢俯拍視角的人頭就是小現(xiàn)象前排人數(shù)統(tǒng)計沒問題后排漏掉兩三成畫面越遠越嚴重。原因是目標尺寸和訓(xùn)練尺度不匹配640 訓(xùn)練尺寸下后排人頭可能只有二三十像素特征在小尺度分支里沒有被充分表達。這不是模型玄學(xué)問題是分辨率分配問題。解決手段按優(yōu)先級排一是訓(xùn)練和推理都上 736 或 832二是訓(xùn)練增強里開 scale 增強讓模型見過更多尺度變化三是如果攝像頭分辨率很高且 GPU 撐不住高 imgsz就把大幅畫面切塊tile推理每塊單獨檢測再合并結(jié)果。另外攝像頭安裝角度也有影響接近正垂直的俯拍角度下目標形態(tài)最差稍微傾斜一點反而更容易檢測。5.3 CPU 部署慢界面卡成 PPT現(xiàn)象檢測功能一切正常但界面打開攝像頭后幀率極低拖動窗口都卡頓。原因是推理在界面線程里同步執(zhí)行YOLOv8 的 PyTorch 推理在純 CPU 上每幀要幾百毫秒到一秒多界面刷新自然被堵死。解決分兩個層面。第一絕對不能把推理放在 UI 線程里用 QThread 把攝像頭讀取、推理、畫框放在工作線程UI 線程只負責(zé)接收 QImage 刷新畫面這是治本。第二把推理后端從 PyTorch 換成 ONNX Runtimeyolo export modelruns/detect/meeting_room/weights/best.pt formatonnximport onnxruntime as ort sess ort.InferenceSession(best.onnx, providers[CPUExecutionProvider])CPU 上 ONNX Runtime 比 PyTorch 推理快一截因為去掉了動態(tài)圖的調(diào)度開銷。注意 ONNX 版的輸入輸出約定和訓(xùn)練時一致輸入要做 letterbox 歸一化輸出是類似(1, 84, 8400)的格式8400 是各個尺度特征圖相加的錨點數(shù)具體數(shù)值隨 imgsz 變化解析時寫通用邏輯不要硬編碼。5.4 標注不統(tǒng)一模型學(xué)歪了現(xiàn)象訓(xùn)練指標不難看但換一段真實會議視頻就漏檢。原因通常不在模型參數(shù)而在標注本身每個人框的粗細不同有人框頭肩有人框全身后排小目標干脆沒標模型學(xué)到的目標形態(tài)是撕裂的。解決方法是建立標注規(guī)范和自查習(xí)慣。統(tǒng)一頭肩框規(guī)則遮擋目標按規(guī)則取舍然后每條數(shù)據(jù)從三個維度自查有沒有漏標、有沒有框錯、有沒有越界框。做這個項目時我有一個固定習(xí)慣——每標注 100 張就隨機挑 10 張把框畫回去看一遍肉眼掃完再繼續(xù)后面省下的返工時間遠比這 20 分鐘多。5.5 換一間會議室就失效過擬合的典型癥狀現(xiàn)象訓(xùn)練用的會議室跑得很好換隔壁一間燈管位置不同、桌椅顏色不同漏檢率明顯上升。原因很簡單數(shù)據(jù)集里大量樣本來自同一個機位、同一個光照環(huán)境模型記住了這個會議室的特征而不是通用的人的特征。解決要分兩步想。如果是畢設(shè)演示承認邊界用訓(xùn)練場景做主要展示再錄一段目標場景的視頻做二次驗證。如果是想做成通用產(chǎn)品就必須在數(shù)據(jù)側(cè)下功夫多個會議室采集、不同時間點補拍、加入了光照擾動增強。會議室場景的泛化難度不高人這個類別在 COCO 里已經(jīng)被學(xué)得很扎實只要數(shù)據(jù)不是太偏微調(diào)出來的模型換場地后通常只是精度下降不會完全失效。6. 可視化界面與部署落地從權(quán)重文件到雙擊運行的統(tǒng)計工具項目最后一個環(huán)節(jié)是把模型包成能演示的界面。技術(shù)選型上PyQt5 和 Tkinter 之間我建議 PyQt5控件、布局、信號機制都更成熟答辯演示也好看。核心思路是工作線程負責(zé)推理界面線程只顯示結(jié)果兩者用信號連接。界面里最小可跑的邏輯框架是這樣from PyQt5.QtCore import QThread, pyqtSignal from PyQt5.QtGui import QImage, QPixmap from PyQt5.QtWidgets import QLabel, QApplication import cv2 from ultralytics import YOLO class Worker(QThread): frame_ready pyqtSignal(QImage, int) # 信號一幀圖像 當(dāng)前人數(shù) def __init__(self, source): super().__init__() self.model YOLO(best.pt) # 加載訓(xùn)練好的權(quán)重 self.cap cv2.VideoCapture(source) def run(self): while True: ok, frame self.cap.read() if not ok: break results self.model(frame, verboseFalse) count len(results[0].boxes) # 當(dāng)前幀檢測到的人數(shù) frame results[0].plot() # 畫好框的畫面 rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) h, w, ch rgb.shape qimg QImage(rgb.data, w, h, ch * w, QImage.Format_RGB888) self.frame_ready.emit(qimg, count)代碼里幾個關(guān)鍵點說一下。模型加載放在線程初始化里不要放在 run 里每次循環(huán)加載。results[0].boxes取檢測框列表len()就是這一幀的檢測數(shù)配合第 5 章說的平滑邏輯用在界面上。plot()畫完的圖要轉(zhuǎn) RGB 再轉(zhuǎn) QImage否則界面上顏色會偏藍偏亂。UI 側(cè)只需要在槽函數(shù)里更新 QLabel 的 pixmap 和人數(shù)文本控件所有耗時推理都在 Worker 線程完成。部署時的驗證方法也很簡單錄一段 15 個人進進出出的視頻跑完后對比每一幀的統(tǒng)計人數(shù)和真實人數(shù)誤差控制在 ±1 以內(nèi)算合格再測一段單人連續(xù)坐 20 分鐘的錄像看人數(shù)是否保持穩(wěn)定不跳變。驗收這一關(guān)在我這里比訓(xùn)練指標更重要指標是開發(fā)者視角實際視頻里的穩(wěn)定性才是用戶視角。這個方向做完之后再往上走的路子也清晰導(dǎo)出 ONNX 后可以接嵌入式平臺跑實時推理界面端加歷史曲線和 Excel 導(dǎo)出或者把單攝像頭擴展成多會議室并發(fā)。我做過很多個檢測類小項目一個習(xí)慣是每次收尾都把「最小可復(fù)現(xiàn)命令」完整寫進 README——訓(xùn)練命令、驗證命令、導(dǎo)出命令、啟動界面命令四行跑不通時先查環(huán)境而不是查模型。希望這個方案的拆解對你做畢設(shè)或課程設(shè)計有實際幫助。本文還有配套的精品資源點擊獲取