+LBPH模型實戰(zhàn))
簡介面向計算機視覺開發(fā)者的Python人臉識別系統(tǒng)源碼包內(nèi)置主程序、配置文件、說明文檔以及多組訓練好的識別器模型。項目基于OpenCV庫實現(xiàn)配套官方人臉檢測器可直接運行體驗人臉檢測、訓練與識別流程。壓縮包共14個文件核心為10個yml識別器模型另含Python腳本、xml人臉檢測器、txt配置與md說明整體僅148KB輕量簡潔適合畢設(shè)、課設(shè)或入門學習。已有1083人學習下載具有一定參考熱度。通過對比不同編號的yml模型可以觀察訓練參數(shù)對識別效果的影響結(jié)合主程序與說明文檔能夠快速理解人臉特征提取、比對識別的工程實現(xiàn)便于在此基礎(chǔ)上二次開發(fā)。資源還附帶了清晰的目錄結(jié)構(gòu)和運行說明有助于快速定位主程序入口、模型文件與配置項省去環(huán)境搭建和調(diào)試時間。1. 基于 OpenCV 的人臉識別系統(tǒng)這份源碼包里到底有什么說實話很多人一聽到“人臉識別系統(tǒng)”就以為要上深度學習、要訓練神經(jīng)網(wǎng)絡(luò)、要裝 CUDA其實 OpenCV 這套傳統(tǒng)方案完全夠用在很多實際場景里。這份源碼包很典型main.py 主程序、config.txt 配置文件、haarcascade_frontalface_default.xml 檢測模型再加上 10 個 yml 識別器文件1.yml 到 10.yml。一看這個結(jié)構(gòu)就知道它是基于 Haar 級聯(lián)檢測 LBPH局部二值模式直方圖識別器的經(jīng)典組合對應了 10 個不同人的訓練結(jié)果。它解決的是“從攝像頭讀到人臉 → 識別出是誰”的完整閉環(huán)特別適合課程設(shè)計、畢業(yè)設(shè)計、考勤門禁原型以及想快速理解 OpenCV 人臉識別全流程的從業(yè)者。整體邏輯不復雜跑通一遍你就把人臉檢測、數(shù)據(jù)集訓練、模型持久化、實時識別這幾塊全摸透了。2. 先看懂源碼結(jié)構(gòu)config.txt 與 yml 文件的角色分配2.1 文件清單里每個文件是干什么的解開壓縮包里面文件不多但每個都有明確職責。haarcascade_frontalface_default.xml 是 OpenCV 自帶的 Haar 級聯(lián)分類器負責在畫面里把人臉位置框出來這是“檢測”環(huán)節(jié)。main.py 是主程序負責把檢測和識別串起來。config.txt 是配置文件我一般會在這里放識別閾值、檢測參數(shù)、模型路徑這些運行時參數(shù)這樣換場景不用改代碼。1.yml 到 10.yml 是訓練好的識別器模型文件每個 yml 對應一個 ID 的人臉樣本訓練結(jié)果也就是說源碼包里已經(jīng)預置了 10 個人的模型數(shù)據(jù)。拿到源碼的第一件事我建議先打開 README.md 看作者寫的環(huán)境依賴再打開 config.txt 看參數(shù)。這套組合的典型運行邏輯是main.py 啟動后讀攝像頭幀 → Haar 檢測人臉 → 把檢測到的人臉區(qū)域預處理后送入識別器 → 識別器返回 ID 和置信度 → 程序根據(jù) ID 映射姓名根據(jù)置信度決定是否顯示“陌生人”警告。yml 文件是核心資產(chǎn)沒有它們 main.py 跑不起來或者只能檢測不能識別。2.2 config.txt 典型內(nèi)容與參數(shù)語義很多初學者拿到代碼會忽略 config.txt覺得它不重要。實際上在這種結(jié)構(gòu)里config.txt 承擔了“參數(shù)與代碼分離”的作用。常見的寫法類似下面這樣# config.txt 典型內(nèi)容 camera_index 0 model_path . confidence_threshold 80 face_size (200, 200) scale_factor 1.1 min_neighbors 5 # 人物 ID 與姓名映射 1: ZhangSan 2: LiSi 3: WangWu 4: ZhaoLiu 5: QianQi 6: SunBa 7: ZhouJiu 8: WuShi 9: ZhengYi 10: WangEr我來解釋一下這些參數(shù)的實際意義。camera_index 是攝像頭編號0 一般是筆記本內(nèi)置攝像頭如果外接攝像頭可能變成 1 或 2這個值不對會出現(xiàn)“攝像頭打開失敗”或者黑屏報錯。model_path 是 yml 文件所在目錄程序里通常會把路徑拼接成model_path / str(id) .yml去加載。confidence_threshold 是置信度閾值LBPH 的 predict 返回的距離值越小表示越匹配所以閾值 80 表示“距離小于 80 才認可這個 ID”大于 80 就當陌生人處理。face_size 是送入識別器前的人臉圖像縮放尺寸統(tǒng)一尺寸是訓練和識別一致性的關(guān)鍵太小丟特征太大浪費計算。這段配置最重要的一句話是程序加載模型時不是一次性加載全部 yml而是按需加載或者啟動時遍歷目錄加載。如果某個人臉 ID 缺失比如 4.yml 丟了但 config 里又映射了 ZhaoLiu程序會嘗試讀取不存在的文件。我一般會建議在 main.py 里做一個模型文件存在性檢查缺失時跳過而不是直接崩潰。2.3 主程序 main.py 的執(zhí)行骨架如果作者把 main.py 寫得規(guī)整代碼骨架大致如下import cv2 import os import configparser # 讀取配置文件 config configparser.ConfigParser() config.read(config.txt) camera_index int(config[DEFAULT][camera_index]) threshold int(config[DEFAULT][confidence_threshold]) face_size eval(config[DEFAULT][face_size]) # 加載人臉檢測器 face_cascade cv2.CascadeClassifier( cv2.data.haarcascades haarcascade_frontalface_default.xml ) # 初始化 LBPH 識別器 recognizer cv2.face.LBPHFaceRecognizer_create() # 加載已訓練好的模型文件 model_dir config[DEFAULT][model_path] id_map {} for name in os.listdir(model_dir): if name.endswith(.yml): model_id int(name.split(.)[0]) recognizer.read(os.path.join(model_dir, name)) id_map[model_id] name這段代碼有幾處值得注意。cv2.face.LBPHFaceRecognizer_create()來自 opencv-contrib-python 包如果你裝的是普通的 opencv-python會報module cv2 has no attribute face。這是這個源碼包里最容易踩的坑之一。cv2.data.haarcascades是 OpenCV 自帶的級聯(lián)模型路徑這樣寫的好處是不用自己復制 xml 文件路徑但前提是 OpenCV 版本不要過舊3.x 早期版本沒有這個屬性。遍歷模型目錄加載 yml 而不是硬編碼 10 個 read 調(diào)用這也是更穩(wěn)的寫法別人在你的代碼上增刪模型不用改主程序的邏輯。3. Haar 級聯(lián)檢測環(huán)節(jié)從視頻幀里把人臉框出來3.1 Haar 特征與級聯(lián)分類器的工作方式Haar 級聯(lián)檢測器是老牌的人臉檢測方案核心是用 Haar 特征描述人臉區(qū)域的明暗分布規(guī)律。比如眼睛區(qū)域比臉頰暗鼻梁比兩側(cè)亮這些特征通過積分圖快速計算。級聯(lián)的意思是一串越來越嚴格的分類器串聯(lián)前幾層用少量特征快速過濾掉絕大部分非人臉區(qū)域后幾層用更多特征對候選區(qū)域做精細判斷。這個設(shè)計讓檢測速度非??煸跇漭蛇@類低算力設(shè)備上也能跑實時。在這份源碼里haarcascade_frontalface_default.xml 就是訓練好的級聯(lián)模型文件。detectMultiScale 是調(diào)用入口它有四個關(guān)鍵參數(shù)。scaleFactor 控制每次縮放圖像的比例1.1 表示每次縮小 10%越小檢測越慢但越細致。minNeighbors 控制候選矩形最少被多少個鄰近矩形“投票”認可值越大漏檢越多但誤檢越少。minSize 和 maxSize 限制檢測人臉的最小和最大像素尺寸設(shè)置合理能過濾掉遠處的路人臉和近景誤檢。3.2 detectMultiScale 參數(shù)怎么調(diào)才合理看一段實際調(diào)用代碼# 常見調(diào)用方式 gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces face_cascade.detectMultiScale( gray, scaleFactor1.1, minNeighbors5, minSize(64, 64), maxSize(512, 512) ) for (x, y, w, h) in faces: cv2.rectangle(frame, (x, y), (xw, yh), (0, 255, 0), 2) face_roi gray[y:yh, x:xw] face_roi cv2.resize(face_roi, face_size)每個參數(shù)的調(diào)整方向我都踩過。scaleFactor 設(shè)成 1.1 時對斜側(cè)臉和小尺寸臉的檢測比較友好但如果畫面里人臉很遠很小可以適當降低到 1.05代價是每幀耗時增加。minNeighbors 設(shè)成 3 會明顯增加誤檢背景里的圓形物體可能被當成臉設(shè)成 8 以上又可能漏掉側(cè)面臉。我一般從 5 開始調(diào)出錯時看是誤檢多還是漏檢多再往對應方向調(diào)。minSize 建議和訓練時的 face_size 一致或接近設(shè)太小會導致大量背景小矩形參與計算浪費時間。檢測環(huán)節(jié)有兩個常見翻車場景。一是 Haar 對側(cè)臉和低頭臉效果明顯變差如果你做的是門禁場景攝像頭裝得偏高人臉往往呈仰角這時候 Haar 的漏檢率會上去可以考慮換成 OpenCV 的 YuNet 或 MediaPipe但這超出了當前源碼包的范疇。二是光照變化半張臉在陰影里時 Haar 檢測框會漂移到亮的那半張臉上識別自然跟著錯后面避坑章節(jié)我會專門展開。3.3 檢測框坐標與模型輸入的一致性detectMultiScale 返回的每個元素是 (x, y, w, h)分別是矩形左上角橫縱坐標和寬高。后面的處理一定是gray[y:yh, x:xw]而不是gray[x:xw, y:yh]這個順序?qū)懛词切率肿畛R姷腻e誤會直接導致切出來的人臉區(qū)域是歪的識別器輸出的置信度永遠在閾值之外。裁剪出來的人臉還必須縮放到和訓練時一致的尺寸比如 config 里的 (200, 200)否則 LBPH 直方圖特征的空間對齊就被破壞識別率大跌。這里還有一個容易被忽略的細節(jié)送入識別器之前要不要做直方圖均衡化。光照不均勻時cv2.equalizeHist(face_roi)能顯著提升識別穩(wěn)定性。很多源碼包作者不會寫這一步但在實際場景里我會建議加上尤其是在現(xiàn)場燈光條件不穩(wěn)定的情況下這一步比換識別器算法更立竿見影。代價是每幀多一次直方圖均衡計算對現(xiàn)代 CPU 來說幾乎可以忽略。4. LBPH 識別器與 yml 模型文件10 個人是怎么被區(qū)分開的4.1 LBPH 為什么適合這種小樣本場景源碼包里預置了 10 個 yml對應的就是 10 個訓練人物的模型。LBPH 是 OpenCV 里免訓練深度網(wǎng)絡(luò)也能用的人臉識別方案原理不復雜把圖像劃分成若干小塊每個像素和它周圍 8 個像素比較大小得到一串二進制編碼統(tǒng)計每個小塊的直方圖把所有小塊直方圖拼接起來作為這張臉的“指紋”。識別時計算兩幅圖直方圖的距離距離越小越像同一個人。這個方案的優(yōu)勢是訓練樣本需求量小每個人給 10 到 20 張不同角度的臉照就能得到可用的模型而且訓練速度極快筆記本上幾千張圖也就是秒級訓練。缺點是對光照和姿態(tài)的魯棒性不如深度學習方案同一場景拍出來的效果很好換個燈光位置識別率就可能明顯下降。課件或者畢設(shè)場景完全夠用商業(yè)級考勤門禁就要謹慎。4.2 從數(shù)據(jù)集到 yml 的訓練流程雖然源碼包只給了訓練好的 yml 文件但你要真正用起來、要加新人就必須自己訓練模型。完整流程分四步import cv2 import os import numpy as np # 1. 準備數(shù)據(jù)集目錄 # dataset/1/001.jpg, dataset/1/002.jpg ... # dataset/2/001.jpg, dataset/2/002.jpg ... faces [] labels [] for person_id in os.listdir(dataset): person_dir os.path.join(dataset, person_id) if not os.path.isdir(person_dir): continue for img_name in os.listdir(person_dir): img_path os.path.join(person_dir, img_name) gray cv2.imread(img_path, cv2.IMREAD_GRAYSCALE) gray cv2.resize(gray, (200, 200)) faces.append(gray) labels.append(int(person_id)) # 2. 創(chuàng)建 LBPH 識別器并訓練 recognizer cv2.face.LBPHFaceRecognizer_create() recognizer.train(faces, np.array(labels)) # 3. 保存模型 recognizer.save(1.yml) # 4. 驗證模型 test_img cv2.imread(test.jpg, cv2.IMREAD_GRAYSCALE) test_img cv2.resize(test_img, (200, 200)) label, confidence recognizer.predict(test_img) print(f識別結(jié)果: label{label}, confidence{confidence})數(shù)據(jù)集的組織我建議一個子目錄一個人目錄名就是 ID。這里參數(shù)上有個關(guān)鍵點recognizer.train要求 faces 是列表、labels 是 numpy 數(shù)組而且每個 face 的尺寸必須完全一致。這個一致性的約束比你想的更嚴格不是寬高比一樣就行是所有像素尺寸精確相同。我見過有人用cv2.resize(gray, (200, 200))時把參數(shù)寫反成(200, 200)以為是寬高其實 OpenCV 的 resize 第一個參數(shù)是目標寬度、第二個是高度寫反了就會出現(xiàn)訓練時正常、識別時置信度異常高的聲音。4.3 project 出來的是什么recognizer.predict(test_img)返回的 confidence 是 LBPH 直方圖距離。很多人第一次拿到這個值會很困惑因為它是幾十到一兩百的數(shù)字不像深度學習里輸出一個 0 到 1 的概率。這個距離的含義是兩張人臉圖特征向量的相似程度越小越像。config 里的 confidence_threshold 80 就是拿來做判斷的距離小于 80 認為匹配大于 80 認為陌生人。但這里要特別提醒一個反直覺的現(xiàn)象同一個人的不同照片置信度可能差很大。訓練集中某個人只用了 10 張照片其中 8 張是正臉、2 張是側(cè)臉那你拿一張側(cè)臉去測置信度可能飆到 90 多直接被判定為陌生人。這是 LBPH 的天然邊界不是源碼包的問題。實際應用時我一般會把訓練集覆蓋“正臉、左右側(cè)臉、抬頭、低頭、戴眼鏡、不戴眼鏡”這些變化而且每類至少 3 張這樣模型才能泛化。ID 從 1 到 10 的映射關(guān)系也要和 config.txt 中的姓名映射保持一致否則會出現(xiàn)“識別出 ZhangSan 但框里顯示 WangEr”的錯位。5. 避坑與排錯從 ModuleNotFoundError 到識別不準的五個高頻問題5.1 ModuleNotFoundError: No module named cv2現(xiàn)象剛下載完源碼按照 README 里的命令運行python main.py報錯找不到 cv2 模塊。原因沒安裝 OpenCV或者裝錯了包。這一個源碼包最常見的安裝命令pip install opencv-python裝出來的 OpenCV 不包含cv2.face模塊因為人臉識別模塊在 opencv-contrib-python 里。解決先卸載現(xiàn)有版本再安裝 contrib 包。一定要先卸載直接裝 opencv-contrib-python 會和已有的 opencv-python 沖突可能出現(xiàn)安裝成功但 import 還是失敗的玄學情況。pip uninstall opencv-python opencv-contrib-python -y pip install opencv-contrib-python numpy裝完之后檢查python -c import cv2; print(cv2.__version__); print(hasattr(cv2, face))。如果輸出里 face 是 True說明人臉識別模塊可用了。5.2 程序能跑但框出來的人臉全被識別成陌生人現(xiàn)象攝像頭畫面正常人臉檢測框也有但屏幕上一直顯示 Stranger陌生人誰過來都不認識。原因最常見的兩個。第一個是攝像頭拍攝畫面和你訓練模型的場景差異太大比如訓練時用的 200x200 正臉照現(xiàn)場是斜上方監(jiān)控角度LBPH 特征對角度特別敏感。第二個是confidence_threshold設(shè)得太嚴比如設(shè)成了 50真實匹配距離 60 到 80 的情況被你一刀切了。解決先在 config.txt 里把閾值放寬到 100觀察實時顯示的置信度值大概在哪個范圍再根據(jù)分布反推合理閾值。同時檢查攝像頭角度盡量讓人的臉正對鏡頭傾斜角度超過 30 度時 LBPH 識別率會斷崖式下降。5.3 攝像頭打不開代碼報 VIDIOC_QUERYMENU 錯誤現(xiàn)象Linux 環(huán)境下運行 main.py攝像頭初始化失敗提示VIDIOC_QUERYMENU或其他 V4L2 相關(guān)錯誤。原因camera_index 是 0但你的攝像頭被系統(tǒng)識別成了別的索引或者 OpenCV 是用不支持 V4L2 的方式編譯的。解決先用系統(tǒng)命令確認攝像頭索引ls /dev/video*一般有 /dev/video0 和 /dev/video1筆記本內(nèi)置攝像頭常是 0USB 外接攝像頭在某些驅(qū)動組合下會占用 video0 導致內(nèi)置攝像頭變成 video1。然后改 config.txt 里的 camera_index 試試。如果還是不行可以臨時在代碼里加cv2.CAP_PROP_FRAME_WIDTH和cv2.CAP_PROP_FRAME_HEIGHT的設(shè)置把分辨率降到 640x480有時候高分辨率攝像頭在部分 Linux 驅(qū)動下就是打不開。5.4 10 個 yml 加載到一半報錯現(xiàn)象程序啟動后加載 3.yml 時報錯提示文件不存在或者模型格式錯誤。原因模型文件路徑拼接錯誤或者壓縮包解壓時有個別文件損壞。也有可能是os.listdir(model_dir)遍歷到了隱藏文件或臨時文件比如 .DS_Store 或者 1.yml.tmp按擴展名.yml判斷時不會加載這些文件但如果某個 yml 文件內(nèi)容被截斷了recognizer.read()就會拋異常。解決在讀取之前加一個 try-except跳過損壞的模型并打印日志方便排查。同時檢查 model_path 路徑是否包含中文字符OpenCV 的read函數(shù)對中文路徑支持很差這是老版本 OpenCV 的通病遇到中文路徑必須復制到純英文目錄下運行。model_files [] for f in os.listdir(model_dir): if f.endswith(.yml) and f.split(.)[0].isdigit(): model_files.append(f) for mf in model_files: try: model_id int(mf.split(.)[0]) recognizer.read(os.path.join(model_dir, mf)) id_map[model_id] mf print(floaded model: {mf}) except Exception as e: print(fskip {mf}: {e})5.5 識別結(jié)果穩(wěn)定但速度很卡幀率掉到不到 10現(xiàn)象畫面明顯一卡一卡的識別延遲嚴重FPS 目測不到 10。原因檢測區(qū)域太大、圖像尺寸太大、或者 main.py 的 while 循環(huán)里做了太多高耗時操作比如把每一幀都存盤。Haar 檢測在 1080p 全幀圖上跑本身就慢再加上多人臉場景每個人都走一遍識別CPU 直接拉滿。解決先裁剪檢測區(qū)域。固定一個 ROI感興趣區(qū)域只在畫面中央的矩形區(qū)域做檢測比如只檢測畫面中間 70% 的區(qū)域這個改動對性能提升立竿見影。其次是先把幀 resize 到 640x480 再做檢測再用檢測框放大回原圖坐標去裁剪人臉這能省一半以上計算量。如果還是不夠就把 face_size 從 200 降到 160LBPH 的特征維度會下降識別計算量也隨之下降。6. 往這個源碼包里加新人訓練腳本、ID 對齊與實測驗證技巧這個源碼包不是只能跑那 10 個人你完全可以按照同樣的數(shù)據(jù)格式往里加新人。要做的核心工作是把新人的 yml 文件生成出來并讓配置文件認識他。我來給你一套我自己反復用過的完整操作流程。第一步構(gòu)建數(shù)據(jù)集目錄。我一般會在項目根目錄下新建 dataset 文件夾結(jié)構(gòu)是dataset/11/img1.jpg、dataset/11/img2.jpg這樣其中 11 是新人的 ID要和現(xiàn)有 1 到 10 錯開。從攝像頭采集照片可以直接復用 Haar 檢測人臉區(qū)域再去重采樣每個 ID 采集 30 張左右涵蓋正臉、左右轉(zhuǎn)頭、戴不戴眼鏡等變化。手動從歷史照片里整理也可以關(guān)鍵是保證統(tǒng)一尺寸和灰度模式。第二步寫訓練腳本。剛才第 4 章給的訓練代碼可以直接用把 label 從數(shù)據(jù)集目錄名自動讀取這一步注意 labels 必須是整數(shù)數(shù)組。訓練完成后保存成new_model.yml。第三步做 ID 對齊和模型掃描。把訓練好的 yml 文件命名為和 ID 一致的11.yml和原有的 1.yml 到 10.yml 放在同一目錄下。然后在 config.txt 里加一行11: 新人姓名主程序啟動時會自動掃到 11.yml 并加載。這個做法比改代碼里的 id_map 硬編碼要干凈得多以后加第 12 個人也不用改代碼。第四步實測驗證這里我有一個強烈推薦的做法不要只看屏幕上顯示的姓名要看置信度。為了讓置信度暴露出來可以臨時改代碼在識別結(jié)果旁邊畫上 confidence 數(shù)值# 臨時調(diào)試代碼把置信度顯示在框上方 label, confidence recognizer.predict(face_roi) display_text f{id_map.get(label, Unknown)} ({confidence:.1f}) cv2.putText(frame, display_text, (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2)實測時新人站在攝像頭前如果置信度穩(wěn)定在 30 到 60 之間說明模型訓練質(zhì)量很好如果一直在 80 附近抖動說明訓練集的光線或姿態(tài)太單一需要補數(shù)據(jù)。我通常要求每位新人的“自測置信度”穩(wěn)定低于閾值的一半才認為這個模型合格。還有一個細節(jié)測試時不要只測正臉故意測側(cè)臉和低頭看置信度的惡化程度這決定了這個系統(tǒng)在實際使用中到底可不可靠。從那次調(diào)優(yōu)以后我每次拿到此類人臉識別源碼都會強制走一遍這套流程先跑通原版、看每個 ID 自測置信度分布、再調(diào)閾值、最后加新人驗證擴展流程這已經(jīng)是我的肌肉記憶了。希望這個思路能幫你少走點彎路。本文還有配套的精品資源點擊獲取