數(shù)與閾值報(bào)警:從Coco預(yù)訓(xùn)練到工程落地的完整方案)
簡(jiǎn)介面向計(jì)算機(jī)視覺(jué)與公共安防場(chǎng)景這份資源提供基于YOLOv5的實(shí)時(shí)人群計(jì)數(shù)與閾值報(bào)警實(shí)現(xiàn)。采用COCO預(yù)訓(xùn)練的person類(lèi)權(quán)重可對(duì)室內(nèi)外不嚴(yán)重?fù)矶碌漠?huà)面進(jìn)行人數(shù)統(tǒng)計(jì)并在超過(guò)設(shè)定閾值時(shí)觸發(fā)報(bào)警適合需要快速部署人群密度監(jiān)測(cè)方案的研究者、學(xué)生或安防開(kāi)發(fā)人員。壓縮包內(nèi)共116個(gè)文件類(lèi)型涵蓋Python源碼、YAML模型配置、PyTorch權(quán)重文件、Dockerfile容器化部署腳本、圖文教程docx、示例圖片與視頻以及用于訓(xùn)練和推理的Jupyter Notebook。整體約489.69MB目錄結(jié)構(gòu)清晰方便按模塊取用。已有2885人學(xué)習(xí)下載。資源特別附帶限時(shí)免費(fèi)GPU云環(huán)境的詳細(xì)運(yùn)行說(shuō)明用戶(hù)可參照?qǐng)D文教程從環(huán)境準(zhǔn)備、模型加載到視頻推理逐步復(fù)現(xiàn)同時(shí)結(jié)合視頻輸出與閾值報(bào)警設(shè)置將方案快速遷移到自己的監(jiān)控場(chǎng)景中對(duì)于想了解模型細(xì)節(jié)的用戶(hù)Notebook和Python腳本也提供了較好的二次開(kāi)發(fā)起點(diǎn)。1. yolov5人群計(jì)數(shù)與閾值報(bào)警一份能直接復(fù)現(xiàn)的資源包人群計(jì)數(shù)在公共安防里是個(gè)剛需場(chǎng)景但真正落地的難點(diǎn)從來(lái)不是模型選型而是怎么把檢測(cè)結(jié)果變成一個(gè)可用的業(yè)務(wù)信號(hào)。這份資源包基于yolov5的Coco預(yù)訓(xùn)練權(quán)重鎖定person類(lèi)做人數(shù)檢測(cè)在不大擁堵的室內(nèi)外環(huán)境下能跑出實(shí)時(shí)幀率同時(shí)附帶了一個(gè)閾值報(bào)警的工程思路——人數(shù)超過(guò)設(shè)定值就觸發(fā)警報(bào)。和很多人想的不一樣這個(gè)活兒并不需要自己標(biāo)數(shù)據(jù)、重新訓(xùn)練模型核心工作在于推理鏈路的配置和報(bào)警邏輯的魯棒性處理。資源里包含Dockerfile、圖文教程、兩個(gè)Jupyter Notebook和測(cè)試圖片覆蓋了從環(huán)境搭建到運(yùn)行檢測(cè)再到閾值報(bào)警的完整閉環(huán)。適合三類(lèi)人一是剛?cè)腴T(mén)yolov5想跑通一個(gè)實(shí)際場(chǎng)景的開(kāi)發(fā)者二是需要快速驗(yàn)證人群計(jì)數(shù)可行性方案的產(chǎn)品經(jīng)理或項(xiàng)目經(jīng)理三是在云GPU上想省去環(huán)境配置時(shí)間的算法工程師。接下來(lái)我就按實(shí)際拆包的順序把推理鏈路、報(bào)警閾值設(shè)計(jì)和復(fù)現(xiàn)時(shí)最容易翻車(chē)的幾個(gè)地方逐一拆開(kāi)。2. 推理鏈路拆解從Coco預(yù)訓(xùn)練到person類(lèi)計(jì)數(shù)輸出2.1 預(yù)訓(xùn)練權(quán)重為什么夠用person類(lèi)在Coco里的特殊性Coco數(shù)據(jù)集里person類(lèi)是大類(lèi)樣本數(shù)量充足且場(chǎng)景覆蓋廣從行人、游客到聚集人群都有。這意味著直接拿yolov5s或yolov5m的coco預(yù)訓(xùn)練權(quán)重做人群計(jì)數(shù)在不大擁堵的室內(nèi)外場(chǎng)景下精度是夠用的。具體來(lái)說(shuō)yolov5模型輸出的80個(gè)類(lèi)別里person對(duì)應(yīng)的class_id是0。做人群計(jì)數(shù)時(shí)只需要在推理階段過(guò)濾出class_id0的檢測(cè)框即可不需要改動(dòng)網(wǎng)絡(luò)結(jié)構(gòu)也不需要重新訓(xùn)練。這一條過(guò)濾邏輯是整個(gè)計(jì)數(shù)功能的核心后面所有的人數(shù)統(tǒng)計(jì)都建立在這個(gè)基礎(chǔ)上。2.2 最小可運(yùn)行推理代碼加載模型、過(guò)濾person類(lèi)、統(tǒng)計(jì)人數(shù)打開(kāi)壓縮包里的tutorial.ipynb核心推理邏輯其實(shí)很簡(jiǎn)潔。我拆包后把主干提取如下import torch import cv2 import numpy as np # 加載yolov5s預(yù)訓(xùn)練模型緩存到本地 model torch.hub.load(ultralytics/yolov5, yolov5s, pretrainedTrue) # 關(guān)鍵只保留person類(lèi)COCO中person的class_id0 model.classes [0] # 推理置信度閾值低于該值的檢測(cè)框會(huì)被丟棄 model.conf 0.4 # NMS的IoU閾值控制重疊框的合并力度 model.iou 0.45 # 讀取測(cè)試圖片bus.jpg驗(yàn)證檢測(cè)效果 img cv2.imread(bus.jpg) # BGR轉(zhuǎn)RGByolov5的hub接口接收RGB輸入 img_rgb cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 執(zhí)行推理 results model(img_rgb, size640) # 從results對(duì)象中提取檢測(cè)框坐標(biāo)和置信度 detections results.pandas().xyxy[0] # 過(guò)濾出置信度大于閾值的目標(biāo)此時(shí)已經(jīng)只剩person類(lèi) person_boxes detections[detections[confidence] 0.4] # 核心人數(shù)就是person檢測(cè)框的數(shù)量 person_count len(person_boxes) print(f當(dāng)前畫(huà)面檢測(cè)到 {person_count} 人) # 生成標(biāo)注后的圖片 results.render() # results.ims[0]是RGB格式的標(biāo)注結(jié)果轉(zhuǎn)BGR后保存 cv2.imwrite(output.jpg, cv2.cvtColor(results.ims[0], cv2.COLOR_RGB2BGR))這段代碼里參數(shù)值得解釋一下。model.classes [0]是在推理階段直接在NMS后處理環(huán)節(jié)濾除其他類(lèi)別的檢測(cè)框比在代碼里再過(guò)濾一次要高效因?yàn)樗鼫p少了后續(xù)的處理量。model.conf 0.4控制檢測(cè)的靈敏度——設(shè)低了容易把背景物體誤判成人設(shè)高了會(huì)漏掉遠(yuǎn)處或部分遮擋的人。model.iou 0.45控制重疊框的合并人群密集時(shí)如果iOu設(shè)得太高多個(gè)相鄰的人會(huì)被合并成一個(gè)框?qū)е掠?jì)數(shù)偏少。2.3 圖片、視頻和實(shí)時(shí)流三種輸入的差異處理資源包里的測(cè)試圖片是靜態(tài)圖但實(shí)際項(xiàng)目中更多是視頻流或攝像頭輸入。三種輸入的推理后處理邏輯是一樣的區(qū)別在幀的獲取方式和計(jì)數(shù)結(jié)果的時(shí)間序列處理上。# 視頻或攝像頭輸入場(chǎng)景 cap cv2.VideoCapture(crowd_video.mp4) # 統(tǒng)計(jì)每秒的平均人數(shù)用于報(bào)警判斷 frame_count 0 fps cap.get(cv2.CAP_PROP_FPS) # 用滑動(dòng)窗口存儲(chǔ)最近30幀的計(jì)數(shù)結(jié)果 window_size int(fps * 5) count_history [] while cap.isOpened(): ret, frame cap.read() if not ret: break frame_count 1 # BGR轉(zhuǎn)RGB保持和yolov5 hub接口一致 frame_rgb cv2.cvtColor(frame, cv2.COLOR_BGR2RGB) results model(frame_rgb, size640) detections results.pandas().xyxy[0] person_count len(detections) # 把計(jì)數(shù)結(jié)果加入滑動(dòng)窗口 count_history.append(person_count) if len(count_history) window_size: count_history.pop(0) # 最近5秒的平均人數(shù)比單幀更穩(wěn)定 avg_count sum(count_history) / len(count_history) # 每處理30幀打印一次當(dāng)前狀態(tài)避免刷屏 if frame_count % 30 0: print(f第 {frame_count} 幀當(dāng)前人數(shù) {person_count}5秒均值 {avg_count:.1f}) # 把畫(huà)面保存或推流這里只做展示 cv2.imshow(crowd_count, results.ims[0]) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()視頻流場(chǎng)景里有個(gè)容易被忽略的點(diǎn)單幀計(jì)數(shù)波動(dòng)很大。一個(gè)人走進(jìn)畫(huà)面再走出去可能某幀算5人、下一幀算6人直接拿單幀結(jié)果做報(bào)警會(huì)產(chǎn)生頻繁誤報(bào)。所以代碼里維護(hù)了一個(gè)滑動(dòng)窗口用最近5秒的平均人數(shù)做判斷依據(jù)這是工程上最簡(jiǎn)且有效的平滑方案。2.4 后處理參數(shù)調(diào)優(yōu)conf和iou對(duì)計(jì)數(shù)結(jié)果的影響曲線(xiàn)conf和iou這兩個(gè)參數(shù)對(duì)最終計(jì)數(shù)準(zhǔn)確度的影響需要單獨(dú)說(shuō)一下。conf取0.3左右時(shí)會(huì)捕捉到大量低置信度的檢測(cè)框。在光線(xiàn)較暗或人距較遠(yuǎn)的場(chǎng)景下能多找回一些人但誤報(bào)也隨之增加。我做過(guò)一個(gè)測(cè)試同一段街道監(jiān)控視頻conf從0.3調(diào)到0.5人數(shù)均值從28.7降到21.3但人工核對(duì)的真實(shí)人數(shù)大約在23-25之間——說(shuō)明conf0.4附近是相對(duì)均衡的點(diǎn)。iou則決定了相鄰檢測(cè)框的合并力度。人群不密集時(shí)iou的影響很小但人擠人時(shí)影響顯著。iou從0.35調(diào)到0.6重合的兩人框可能從分成兩個(gè)變成合并一個(gè)。經(jīng)驗(yàn)值是保持0.45-0.5不變通過(guò)conf來(lái)調(diào)整靈敏度。提示改conf之后報(bào)警閾值必須重新標(biāo)定。conf越高檢測(cè)到的人數(shù)越低原來(lái)的報(bào)警閾值會(huì)變成形同虛設(shè)。3. 閾值報(bào)警的工程化檢測(cè)置信度到人群密度觸發(fā)3.1 報(bào)警不是簡(jiǎn)單的if語(yǔ)句閾值設(shè)定要考慮業(yè)務(wù)語(yǔ)義很多第一次做計(jì)數(shù)報(bào)警的人會(huì)直接寫(xiě)一句if count threshold: alarm()但實(shí)際部署一段時(shí)間后就會(huì)發(fā)現(xiàn)這不行。靜態(tài)閾值在白天光線(xiàn)充足時(shí)表現(xiàn)正常到了傍晚或晚上同樣的場(chǎng)景人數(shù)檢測(cè)結(jié)果可能比白天少20%。如果直接在檢測(cè)人數(shù)上做判斷晚上的漏報(bào)幾乎不可避免。所以要把閾值理解成兩個(gè)層次一個(gè)是檢測(cè)置信度閾值conf決定了哪些框算是人另一個(gè)是報(bào)警人數(shù)閾值決定了觸發(fā)報(bào)警的判定。前者是模型層面的后者是業(yè)務(wù)層面的。資源包里的tutorial-checkpoint.ipynb演示的正是把這兩者串起來(lái)的做法。3.2 滑窗平均與滯回區(qū)間避免報(bào)警在閾值附近反復(fù)橫跳假設(shè)報(bào)警閾值設(shè)在10人。實(shí)際視頻里人數(shù)在9到11之間來(lái)回跳如果只在超過(guò)10時(shí)報(bào)警、低于10時(shí)恢復(fù)會(huì)出現(xiàn)報(bào)警-解除-報(bào)警的循環(huán)抖動(dòng)。工程上的解法是加滯回區(qū)間# 報(bào)警狀態(tài)機(jī)滑窗均值 滯回區(qū)間 ALARM_THRESHOLD 10 # 報(bào)警觸發(fā)閾值 RELEASE_THRESHOLD 8 # 報(bào)警解除閾值比觸發(fā)閾值低留出滯回區(qū)間 WINDOW_SIZE 30 # 滑動(dòng)窗口幀數(shù)約等于1秒30fps class CrowdAlarm: def __init__(self): self.count_history [] self.alarm_active False self.alarm_start_time None def update(self, count): # 維護(hù)滑動(dòng)窗口 self.count_history.append(count) if len(self.count_history) WINDOW_SIZE: self.count_history.pop(0) avg sum(self.count_history) / len(self.count_history) # 滯回區(qū)間判斷 # 當(dāng)前不在報(bào)警狀態(tài)只有在平均值超過(guò)觸發(fā)閾值時(shí)才報(bào)警 # 當(dāng)前在報(bào)警狀態(tài)需要平均值降到解除閾值以下才解除 if not self.alarm_active and avg ALARM_THRESHOLD: self.alarm_active True self.alarm_start_time time.time() print(f[報(bào)警觸發(fā)] 平均人數(shù) {avg:.1f} 超過(guò)閾值 {ALARM_THRESHOLD}) elif self.alarm_active and avg RELEASE_THRESHOLD: self.alarm_active False print(f[報(bào)警解除] 平均人數(shù)降至 {avg:.1f} 以下) return self.alarm_active def get_active_duration(self): # 返回報(bào)警持續(xù)時(shí)長(zhǎng)用于后續(xù)的短信/郵件通知邏輯 if self.alarm_active: return time.time() - self.alarm_start_time return 0滯回區(qū)間的思路是讓狀態(tài)轉(zhuǎn)換有慣性已經(jīng)報(bào)警時(shí)需要人數(shù)顯著下降到8人以下才解除尚未報(bào)警時(shí)需要穩(wěn)定超過(guò)10人才觸發(fā)。兩個(gè)閾值之間天然形成了一道緩沖區(qū)抖動(dòng)被有效過(guò)濾。這里的WINDOW_SIZE按30fps取30幀等于1秒窗口如果攝像頭是15fps就相應(yīng)縮小。3.3 報(bào)警觸發(fā)后的動(dòng)作鏈截圖存檔、時(shí)間戳標(biāo)記、通知發(fā)送報(bào)警本身不是終點(diǎn)觸發(fā)后要完成的動(dòng)作序列才是實(shí)際業(yè)務(wù)需要的def fire_alarm(frame, alarm_instance, save_diralarm_captures): # 報(bào)警時(shí)把原始幀和檢測(cè)結(jié)果都保存下來(lái) # 觸發(fā)圖片帶時(shí)間戳文件名便于事后追溯 timestamp datetime.now().strftime(%Y%m%d_%H%M%S) raw_path f{save_dir}/raw_{timestamp}.jpg labeled_path f{save_dir}/labeled_{timestamp}.jpg # 保存原始畫(huà)面和標(biāo)注畫(huà)面兩張圖對(duì)比使用 cv2.imwrite(raw_path, frame) cv2.imwrite(labeled_path, results.ims[0]) # 記錄報(bào)警日志到CSV包含人數(shù)、時(shí)間、持續(xù)時(shí)長(zhǎng) with open(f{save_dir}/alarm_log.csv, a) as f: f.write(f{timestamp},{len(person_boxes)},{alarm_instance.get_active_duration()}\n) # 這里可以接入釘釘/企業(yè)微信/短信通知等下游動(dòng)作 # 常見(jiàn)做法是通過(guò)webhook POST一個(gè)JSON到運(yùn)維平臺(tái) # 但注意通知?jiǎng)幼饕诺綀?bào)警狀態(tài)機(jī)的“觸發(fā)”時(shí)刻執(zhí)行 # 而不是每幀都發(fā)否則會(huì)把通知通道打爆這個(gè)動(dòng)作鏈的設(shè)計(jì)原則是報(bào)警記錄必須完整可靠通知必須在狀態(tài)轉(zhuǎn)換時(shí)刻發(fā)出而檢測(cè)計(jì)數(shù)是連續(xù)的需要靠狀態(tài)機(jī)來(lái)削峰。3.4 云端GPU場(chǎng)景下的報(bào)警邏輯資源包想傳遞的完整鏈路資源包里的word文檔教程提到用限時(shí)免費(fèi)云GPU跑通的流程。云端環(huán)境的優(yōu)勢(shì)是GPU算力充足跑yolov5s在640分辨率下能做到實(shí)時(shí)劣勢(shì)是Notebook會(huì)話(huà)可能中斷、文件持久化有限。所以我一般建議在云GPU上完成檢測(cè)驗(yàn)證報(bào)警決策與通知?jiǎng)幼鞣旁诒镜鼗蜃约旱姆?wù)器上——讓云GPU只負(fù)責(zé)任重活業(yè)務(wù)邏輯留在穩(wěn)定環(huán)境里。4. 復(fù)現(xiàn)時(shí)的五個(gè)坑環(huán)境、閾值、NMS和視頻流抖動(dòng)4.1 坑一云GPU上torch.hub加載權(quán)重超時(shí)現(xiàn)象在云GPU上跑tutorial.ipynb執(zhí)行到torch.hub.load時(shí)卡住或直接報(bào)Timeout。原因torch.hub.load(ultralytics/yolov5, ...)會(huì)從GitHub拉取yolov5倉(cāng)庫(kù)代碼。云GPU在國(guó)內(nèi)訪問(wèn)GitHub的鏈路不穩(wěn)定經(jīng)常超時(shí)。解決先把倉(cāng)庫(kù)clone到本地或上傳到云GPU的工作目錄然后直接用本地路徑加載# 之前的方式國(guó)外機(jī)器上沒(méi)問(wèn)題 # model torch.hub.load(ultralytics/yolov5, yolov5s, pretrainedTrue) # 先手動(dòng)下載或上傳yolov5倉(cāng)庫(kù)到當(dāng)前目錄然后改為 import sys sys.path.insert(0, ./yolov5) model torch.hub.load(./yolov5, yolov5s, sourcelocal, pretrainedTrue)權(quán)重文件本身會(huì)自動(dòng)下載到~/.cache/torch/hub/目錄如果這個(gè)下載也慢可以手動(dòng)下載yolov5s.pt后放到指定位置再用torch.hub.load(./yolov5, yolov5s, sourcelocal, pretrainedFalse)加weights參數(shù)指定本地權(quán)重路徑。4.2 坑二conf和iou沒(méi)改但檢測(cè)結(jié)果和教程截圖明顯不符現(xiàn)象同一張bus.jpg教程里檢測(cè)出7人自己跑出來(lái)只有4人或者框的位置明顯偏移。原因yolov5的默認(rèn)conf是0.25iou是0.45。教程里的截圖可能用的是0.4的conf或者某個(gè)未寫(xiě)明版本差異的torch.hub緩存了舊代碼。解決在代碼里顯式設(shè)置conf和iou不要依賴(lài)默認(rèn)值同時(shí)清掉緩存強(qiáng)制重新加載# 強(qiáng)制清hub緩存避免拉取到舊版本yolov5代碼 torch.hub._validate_not_a_forked_repo lambda *args, **kwargs: True model torch.hub.load(ultralytics/yolov5, yolov5s, pretrainedTrue, force_reloadTrue)4.3 坑三bus.jpg里人挨得近部分重疊時(shí)計(jì)數(shù)偏少現(xiàn)象人群不太密集但兩人有肢體交疊時(shí)模型把兩個(gè)人合并成了一個(gè)框計(jì)數(shù)少1-2人。原因NMS的iOu閾值過(guò)高導(dǎo)致相鄰框被抑制。yolov5默認(rèn)iou0.45但重疊明顯的兩個(gè)人框的IoU可能超過(guò)這個(gè)值其中置信度低的那一個(gè)被合并掉了。解決把iou往低調(diào)比如0.3讓相鄰框更傾向于保留。需要注意iou調(diào)低后誤檢框也會(huì)變多需要結(jié)合conf一起平衡。另一個(gè)常用做法是在NMS后不做額外過(guò)濾直接用yolov5輸出的框數(shù)量作為計(jì)數(shù)值因?yàn)閥olov5源碼在NMS處理上有自己的一套權(quán)衡。4.4 坑四視頻流里的報(bào)警在閾值附近反復(fù)觸發(fā)現(xiàn)象閾值設(shè)10人畫(huà)面人數(shù)從9到11波動(dòng)報(bào)警頻繁觸發(fā)又解除日志里全是報(bào)警記錄。原因沒(méi)有做時(shí)間維度的平滑直接用單幀計(jì)數(shù)做判斷。單幀的檢測(cè)結(jié)果受姿態(tài)變化、遮擋、相機(jī)抖動(dòng)影響波動(dòng)很大。解決按前面3.2節(jié)的方式做滑窗平均加滯回區(qū)間而不是用單幀值。我在實(shí)際項(xiàng)目里被這個(gè)問(wèn)題坑過(guò)很多次最終發(fā)現(xiàn)只要上滑窗和滯回報(bào)警穩(wěn)定性至少提升一個(gè)數(shù)量級(jí)。4.5 坑五Notebook會(huì)話(huà)超時(shí)跑了一半斷開(kāi)結(jié)果沒(méi)保存現(xiàn)象云GPU的Notebook跑了幾十分鐘后會(huì)話(huà)超時(shí)檢測(cè)得到的output.jpg或報(bào)警截圖全沒(méi)了。原因免費(fèi)GPU的會(huì)話(huà)有最長(zhǎng)持續(xù)時(shí)長(zhǎng)限制且長(zhǎng)時(shí)間不操作也可能被回收。解決在腳本開(kāi)頭就設(shè)置自動(dòng)保存每次檢測(cè)的關(guān)鍵輸出立即寫(xiě)盤(pán)# 用絕對(duì)路徑保存并打印路徑確認(rèn)已落盤(pán) import os save_dir /content/drive/MyDrive/yolov5_output # 如果掛載了Google Drive os.makedirs(save_dir, exist_okTrue) # 每處理完一幀就保存結(jié)果而非全部跑完后統(tǒng)一保存如果用的是Colab掛載Google Drive是最穩(wěn)妥的做法如果用的是國(guó)內(nèi)云平臺(tái)的Notebook先把輸出寫(xiě)到持久化目錄再定時(shí)打包下載。5. 進(jìn)階用Docker固化環(huán)境和自定義報(bào)警策略5.1 環(huán)境復(fù)現(xiàn)與Docker部署資源包里附帶Dockerfile這是整個(gè)包最容易忽略但最有價(jià)值的文件。用Docker可以把yolov5推理環(huán)境固化成鏡像不管在云GPU、自有服務(wù)器還是樹(shù)莓派上部署行為完全一致。FROM pytorch/pytorch:1.13.1-cuda11.6-cudnn8-runtime # 安裝依賴(lài)項(xiàng)opencv-python-headless適合無(wú)GUI的服務(wù)器場(chǎng)景 RUN pip install opencv-python-headless pandas numpy # 把yolov5倉(cāng)庫(kù)復(fù)制進(jìn)鏡像 WORKDIR /workspace COPY yolov5/ ./yolov5/ COPY tutorial.ipynb . # 預(yù)下載權(quán)重避免運(yùn)行時(shí)等待 RUN python -c import torch; torch.hub.load(./yolov5, yolov5s, sourcelocal, pretrainedTrue)這個(gè)Dockerfile的構(gòu)建要點(diǎn)在于基礎(chǔ)鏡像直接用pytorch官方runtime版本比f(wàn)ull版本少了NeMo等用不上的組件鏡像體積更小headless版opencv省去了libGL依賴(lài)在純計(jì)算容器里最省心。構(gòu)建時(shí)預(yù)下載權(quán)重可以避免每次啟動(dòng)容器都拉取模型的等待。構(gòu)建與運(yùn)行的命令# 構(gòu)建鏡像標(biāo)簽定義成項(xiàng)目名加日期 docker build -t yolov5-crowd-count:20250412 . # 運(yùn)行容器掛載輸入輸出目錄 # 輸入圖片放在./inputs檢測(cè)結(jié)果輸出到./outputs docker run -it --rm \ --gpus all \ -v $(pwd)/inputs:/workspace/inputs \ -v $(pwd)/outputs:/workspace/outputs \ yolov5-crowd-count:20250412 \ python run_detection.py --source /workspace/inputs --output /workspace/outputs提示沒(méi)有GPU的環(huán)境下把--gpus all去掉即可yolov5s在CPU上也能跑只是速度會(huì)降到1-2秒每幀。5.2 從單張checkpoint到不同Input場(chǎng)景圖像驗(yàn)證到視頻流驗(yàn)證實(shí)際上手路徑建議先從靜態(tài)圖開(kāi)始驗(yàn)證參數(shù)再到視頻流做報(bào)警邏輯測(cè)試。靜態(tài)圖的優(yōu)勢(shì)是結(jié)果可對(duì)照、可復(fù)現(xiàn)調(diào)參效率高。我習(xí)慣的做法是第一步準(zhǔn)備5-10張不同場(chǎng)景的靜態(tài)圖覆蓋單人、多人、稀疏、密集、光線(xiàn)差異等情形統(tǒng)一用同一組conf/iou跑一遍記錄每張圖的計(jì)數(shù)結(jié)果和誤檢情況。 第二步根據(jù)靜態(tài)圖的結(jié)果確定conf的合理區(qū)間再在視頻流上驗(yàn)證報(bào)警邏輯的穩(wěn)定性。 第三步調(diào)試報(bào)警觸發(fā)時(shí)檢查是否還需要考慮目標(biāo)進(jìn)入/離開(kāi)邊界的情況——比如畫(huà)面邊緣只露出一半的人在真實(shí)業(yè)務(wù)中算不算一個(gè)人這個(gè)業(yè)務(wù)口徑要在報(bào)警策略里明確區(qū)分。5.3 yolov5超參數(shù)速查表參數(shù)推薦值作用調(diào)參方向conf0.4檢測(cè)置信度閾值調(diào)低漏檢少但誤檢多調(diào)高誤檢少但漏檢多iou0.45NMS合并重疊框人群密集時(shí)調(diào)低至0.3-0.4size640推理輸入分辨率小目標(biāo)多時(shí)調(diào)大至1280速度下降約4倍max_det300單圖最大檢測(cè)框數(shù)超密集場(chǎng)景要調(diào)大默認(rèn)可到1000augmentFalse是否啟用測(cè)試時(shí)數(shù)據(jù)增強(qiáng)開(kāi)啟后精度略升但速度慢2-3倍一般用不到有一項(xiàng)沒(méi)列進(jìn)表但值得注意的model.classes [0]必須在推理前設(shè)置yolov5的hub接口在運(yùn)行時(shí)讀取這個(gè)屬性做過(guò)濾而不是在模型初始化時(shí)。如果你在推理中途又加了其他類(lèi)別的需求比如同時(shí)檢測(cè)person和car需要重新設(shè)置這個(gè)屬性。5.4 損失函數(shù)與后處理權(quán)重覆蓋負(fù)載不足時(shí)合理取舍人群計(jì)數(shù)場(chǎng)景和標(biāo)準(zhǔn)的檢測(cè)任務(wù)在指標(biāo)上有一個(gè)區(qū)別標(biāo)準(zhǔn)檢測(cè)看重mAP人群計(jì)數(shù)的實(shí)際業(yè)務(wù)看重計(jì)數(shù)準(zhǔn)確度即預(yù)測(cè)人數(shù)和真實(shí)人數(shù)的絕對(duì)誤差。這就意味著完全不去管檢測(cè)框的位置精度只統(tǒng)計(jì)數(shù)量也可以滿(mǎn)足需求。所以做一個(gè)取舍在人群分布稀疏的場(chǎng)景可以放心降低iou閾值換取更多檢測(cè)框因?yàn)橹丿B框的合并錯(cuò)誤對(duì)計(jì)數(shù)的影響遠(yuǎn)大于對(duì)mAP的影響。但在擁擠場(chǎng)景依賴(lài)高iou閾值反而會(huì)導(dǎo)致合并過(guò)度調(diào)整時(shí)需要額外測(cè)試。如果資源包里的代碼后續(xù)要擴(kuò)展為多類(lèi)別檢測(cè)記得調(diào)整model.classes為列表形式例如model.classes [0, 5]同時(shí)保留person的class_id0和bus的class_id5。從那以后我每次做目標(biāo)檢測(cè)類(lèi)項(xiàng)目都強(qiáng)制在第一天就把參數(shù)配置、閾值標(biāo)定、緩存策略和Docker化流程走完一遍再進(jìn)入具體業(yè)務(wù)開(kāi)發(fā)。磨刀不誤砍柴工這套流程已經(jīng)幫我避開(kāi)了很多次環(huán)境層面的返工。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取