算機(jī)視覺的司機(jī)疲勞檢測:dlib人臉關(guān)鍵點(diǎn)與EAR算法實(shí)戰(zhàn))
簡介面向計(jì)算機(jī)視覺與圖像處理方向的學(xué)習(xí)者及相關(guān)專業(yè)學(xué)生一份PDF技術(shù)文獻(xiàn)系統(tǒng)介紹基于計(jì)算機(jī)視覺的司機(jī)駕駛疲勞檢測方案可作為課程設(shè)計(jì)、畢業(yè)設(shè)計(jì)或相關(guān)研究的參考。內(nèi)容涵蓋人臉識別與人眼定位算法設(shè)計(jì)、基于dlib庫的六十八個(gè)臉部特征點(diǎn)提取、眼部縱橫比EAR值計(jì)算以及基于閉眼閾值與連續(xù)幀計(jì)數(shù)的疲勞判定流程實(shí)驗(yàn)結(jié)果顯示系統(tǒng)成功率高達(dá)百分之九十。資源包內(nèi)為單個(gè)PDF文檔文件壓縮包大小約2.66MB十分便于離線閱讀截至目前已有165人瀏覽學(xué)習(xí)。文中還給出了從視頻幀預(yù)處理、人臉檢測到EAR判斷的完整實(shí)現(xiàn)步驟并討論了不同環(huán)境下檢測可信度及局限性閱讀后可快速把握疲勞駕駛檢測系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)要點(diǎn)為后續(xù)改進(jìn)提供參考。1. 基于計(jì)算機(jī)視覺的司機(jī)駕駛疲勞檢測為什么我要拆這份論文做計(jì)算機(jī)視覺大作業(yè)的同學(xué)十有八九會(huì)搜到吉林大學(xué)黃永平老師這篇《基于計(jì)算機(jī)視覺的司機(jī)駕駛疲勞檢測系統(tǒng)》。論文思路很清晰先用 dlib 檢測人臉 68 個(gè)特征點(diǎn)再用其中 36~47 號眼周特征點(diǎn)計(jì)算眼睛縱橫比 EAR通過 EAR 低于閾值的持續(xù)幀數(shù)判斷司機(jī)是否閉眼疲勞。系統(tǒng)實(shí)驗(yàn)室內(nèi)成功率能到 90% 上下但論文里也承認(rèn)實(shí)驗(yàn)室環(huán)境光線穩(wěn)定、駕駛員正對鏡頭一到實(shí)際道路場景就會(huì)暴露不少問題。這份資源適合兩類人一是做 CV 課程設(shè)計(jì)、需要一套能跑通并寫進(jìn)報(bào)告的技術(shù)路線二是想入門人臉關(guān)鍵點(diǎn)檢測、想搞懂 EAR 閾值怎么標(biāo)定的初學(xué)者。我將從選型對比、特征點(diǎn)坐標(biāo)、EAR 算法推導(dǎo)、代碼落地到參數(shù)坑位逐一拆解。2. 從 haarcascades 到 dlib人臉檢測器的選型理由與特征點(diǎn)坐標(biāo)解讀2.1 為什么 haarcascades 在人眼閉合時(shí)直接翻車論文開頭提到先用了 OpenCV 自帶的 haarcascades 包做人臉檢測和人眼定位效果不理想光線好、眼睛睜大時(shí)勉強(qiáng)能用一旦眼睛閉合程度大、人眼區(qū)域在畫面里占比小就經(jīng)常定位不到眼睛甚至人臉都檢測失敗。這個(gè)現(xiàn)象我復(fù)現(xiàn)過原因在于 haar 特征本質(zhì)是基于像素灰度差異的弱分類器級聯(lián)它對睜開的眼睛這種有明確紋理對比的區(qū)域敏感而閉眼狀態(tài)下眼瞼紋理被皮膚褶皺替代灰度梯度特征消失分類器自然失效。另外 haar 檢測器輸出的是矩形包圍盒不給關(guān)鍵點(diǎn)坐標(biāo)你拿到眼睛框后還得自己從框內(nèi)繼續(xù)找瞳孔或眼瞼位置多做一步就多一個(gè)誤差源。所以論文選 dlib 是合理的dlib 的 68 點(diǎn)人臉關(guān)鍵點(diǎn)檢測器基于 HOG 特征和線性分類器加上形狀回歸的級聯(lián)思想對局部紋理變化更魯棒且直接輸出每個(gè)關(guān)鍵點(diǎn)的 (x, y) 坐標(biāo)省掉了二次定位的麻煩。2.2 68 點(diǎn)模型中 36~47 號點(diǎn)的位置含義與坐標(biāo)提取dlib 的 68 點(diǎn)模型把面部關(guān)鍵區(qū)域劃分為下頜輪廓 0~16左眉 17~21右眉 22~26鼻梁 27~30鼻翼 31~35左眼 36~41右眼 42~47嘴巴 48~67。注意論文里寫36-47 為左右眼的特征點(diǎn)嚴(yán)格講是左眼 36~41、右眼 42~47。每個(gè)點(diǎn)是按固定語義順序輸出的左眼 36 是外眼角、39 是內(nèi)眼角37、38 是上眼瞼、40、41 是下眼瞼。import dlib import cv2 # 加載預(yù)訓(xùn)練模型shape_predictor_68_face_landmarks.dat 需要另行下載 detector dlib.get_frontal_face_detector() predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat) img cv2.imread(driver_face.jpg) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) faces detector(gray, 0) for face in faces: shape predictor(gray, face) # 打印左眼6個(gè)點(diǎn)坐標(biāo)索引36~41 for i in range(36, 42): pt shape.part(i) print(fPoint {i}: ({pt.x}, {pt.y})) # 打印右眼6個(gè)點(diǎn)坐標(biāo)索引42~47 for i in range(42, 48): pt shape.part(i) print(fPoint {i}: ({pt.x}, {pt.y}))detector(gray, 0)的第二個(gè)參數(shù) 0 表示不使用圖像金字塔上采樣檢測速度快但小臉可能漏檢如果攝像頭離人較遠(yuǎn)、人臉像素少改成 1 或 2 會(huì)增加一次上采樣能多檢出小臉但耗時(shí)翻倍。predictor接收灰度圖和檢測到的人臉框輸出 68 個(gè)點(diǎn)的坐標(biāo)集合。這里有個(gè)經(jīng)驗(yàn)輸入圖像分辨率不要太低dlib 官方建議人臉區(qū)域至少 80x80 像素否則關(guān)鍵點(diǎn)回歸精度會(huì)明顯下降。拿到坐標(biāo)后別直接拿去做距離計(jì)算最好先按人臉框大小做歸一化。因?yàn)椴煌穗x攝像頭遠(yuǎn)近不同同一雙眼睛在畫面里的絕對像素距離可以差一倍以上直接比絕對值沒有意義。EAR 算法的妙處就在于它是一個(gè)比值分子分母同時(shí)縮放距離影響被抵消了。2.3 論文沒細(xì)說的特征點(diǎn)檢測失敗的常見輸入原因用 dlib 時(shí)最容易翻車的不是算法本身而是輸入圖像質(zhì)量?;叶绒D(zhuǎn)換是必須的dlib 的 HOG 檢測器在灰度圖上提取梯度特征彩色圖會(huì)被內(nèi)部轉(zhuǎn)灰度提前轉(zhuǎn)換能省一次轉(zhuǎn)換開銷。光照過暗時(shí)梯度信息弱檢測器可能整個(gè)人臉都框不出來強(qiáng)側(cè)光會(huì)在面部形成大塊陰影區(qū)關(guān)鍵點(diǎn)回歸容易偏移。我實(shí)測過當(dāng)人臉偏航角超過 45 度即司機(jī)轉(zhuǎn)頭看右側(cè)后視鏡時(shí)68 點(diǎn)模型會(huì)在可見的半張臉上輸出全部 68 個(gè)點(diǎn)其中被遮擋側(cè)的點(diǎn)坐標(biāo)基本是猜的計(jì)算出的 EAR 值完全不可信。提示實(shí)際做車載場景時(shí)優(yōu)先保證正臉采樣。攝像頭裝在方向盤前儀表盤位置比裝在 A 柱更利于關(guān)鍵點(diǎn)檢測。3. 核心算法 EAR眼睛縱橫比的計(jì)算邏輯與閉眼判定原理3.1 從眼周 6 點(diǎn)到縱橫比公式的推導(dǎo)論文的疲勞判定核心是 EAR全稱 Eye Aspect Ratio眼睛縱橫比。睜眼時(shí)上下眼瞼距離大閉眼時(shí)上下眼瞼幾乎貼合通過計(jì)算上下眼瞼特征點(diǎn)之間的垂直距離與內(nèi)外眼角水平距離的比值就能得到一個(gè)對距離不敏感、對睜閉眼狀態(tài)敏感的量。左眼的 6 個(gè)特征點(diǎn)編號為 36~41定義如下p36 為左外眼角p39 為左內(nèi)眼角p37、p38 為上眼瞼左右兩點(diǎn)p40、p41 為下眼瞼左右兩點(diǎn)。EAR 公式為EAR (||p37 - p41|| ||p38 - p40||) / (2 * ||p36 - p39||)分子是兩條垂直方向上的眼瞼距離之和分母是眼角水平距離的兩倍。睜眼時(shí)分子較大EAR 通常在 0.25~0.35 之間閉眼時(shí)分子趨近于零EAR 會(huì)掉到 0.1 以下。論文提到一般睜眼時(shí)上下特征點(diǎn)距離較大閉眼時(shí)上下距離較小說的就是這個(gè)幾何關(guān)系。兩只眼睛分別計(jì)算后取平均能抵消單眼誤檢帶來的抖動(dòng)。3.2 完整可運(yùn)行的 EAR 檢測代碼與逐行參數(shù)說明import dlib import cv2 import numpy as np detector dlib.get_frontal_face_detector() predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat) def eye_aspect_ratio(eye_points): # eye_points 是由6個(gè) (x, y) 坐標(biāo)組成的數(shù)組順序?yàn)橥庋劢堑絻?nèi)眼角環(huán)繞 p2_p6 np.linalg.norm(eye_points[1] - eye_points[5]) p3_p5 np.linalg.norm(eye_points[2] - eye_points[4]) p1_p4 np.linalg.norm(eye_points[0] - eye_points[3]) ear (p2_p6 p3_p5) / (2.0 * p1_p4) return ear cap cv2.VideoCapture(0) frame_count 0 ear_sum 0.0 while True: ret, frame cap.read() if not ret: break gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces detector(gray, 0) for face in faces: shape predictor(gray, face) left_eye [] right_eye [] for i in range(36, 42): pt shape.part(i) left_eye.append((pt.x, pt.y)) for i in range(42, 48): pt shape.part(i) right_eye.append((pt.x, pt.y)) left_ear eye_aspect_ratio(np.array(left_eye, dtypenp.float64)) right_ear eye_aspect_ratio(np.array(right_eye, dtypenp.float64)) ear (left_ear right_ear) / 2.0 cv2.putText(frame, fEAR: {ear:.2f}, (30, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) cv2.imshow(Fatigue Detection, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()eye_aspect_ratio函數(shù)里eye_points[0]是外眼角、eye_points[3]是內(nèi)眼角這是因?yàn)?dlib 輸出順序固定為順時(shí)針環(huán)繞眼睛索引 0 對應(yīng) 36 外眼角索引 3 對應(yīng) 39 內(nèi)眼角。np.linalg.norm計(jì)算兩點(diǎn)間歐氏距離p2_p6對應(yīng)上眼瞼點(diǎn) 37 到下眼瞼點(diǎn) 41 的距離p3_p5對應(yīng) 38 到 40 的距離這兩個(gè)都是垂直方向。p1_p4是水平方向的內(nèi)外眼角距乘以 2 是為了讓睜眼時(shí)的 EAR 值落在 0.25~0.35 這個(gè)便于比較的區(qū)間。實(shí)時(shí)視頻流里我一般直接取相鄰幀的 EAR 值做均值濾波比如滑動(dòng)窗口取最近 5 幀的平均可以有效抑制單幀抖動(dòng)。但注意窗口別太大否則閉眼瞬間的 EAR 變化會(huì)被平滑掉導(dǎo)致漏檢。3.3 EAR 閾值與連續(xù)幀判定論文流程的兩處關(guān)鍵參數(shù)論文的判定流程是EAR 小于閉眼閾值則計(jì)數(shù)器加一否則計(jì)數(shù)器清零當(dāng)連續(xù)幀數(shù)大于疲勞閾值時(shí)發(fā)出警告。這里有兩個(gè)參數(shù)需要標(biāo)定一個(gè)是閉眼閾值另一個(gè)是疲勞閾值即連續(xù)多少幀判定為疲勞。閉眼閾值我一般取 0.2~0.25 之間。太大會(huì)把正常眨眼誤判為閉眼太小則閉眼不徹底時(shí)檢不出來。論文提到眨眼時(shí)眼睛的寬度會(huì)迅速下降到零但實(shí)際視頻流里受幀率和運(yùn)動(dòng)模糊影響EAR 很難降到零通常閉眼瞬間 EAR 在 0.1 左右睜眼在 0.3 左右所以閾值取 0.2 是比較穩(wěn)妥的分界線。疲勞閾值和幀率直接相關(guān)30fps 下閉眼 0.5 秒就是 15 幀所以連續(xù)幀閾值取 15~20 比較合理。如果攝像頭幀率只有 15fps同等閉眼時(shí)長對應(yīng)幀數(shù)減半疲勞閾值要相應(yīng)下調(diào)。4. 從視頻流到疲勞報(bào)警系統(tǒng)實(shí)現(xiàn)的完整流程與邊界條件4.1 六步處理流程的代碼化落地論文 3.1 節(jié)給出的流程可以概括為灰度轉(zhuǎn)換、加載檢測器、檢測人臉、提取眼部特征點(diǎn)、計(jì)算 EAR、閾值比較與計(jì)數(shù)器累加。用代碼把這幾步串起來加上報(bào)警邏輯就是一個(gè)最小可用的疲勞檢測系統(tǒng)。import dlib import cv2 import numpy as np detector dlib.get_frontal_face_detector() predictor dlib.shape_predictor(shape_predictor_68_face_landmarks.dat) EYE_AR_THRESH 0.20 EYE_AR_CONS_FRAMES 15 frame_counter 0 alarm_on False def eye_aspect_ratio(eye): A np.linalg.norm(eye[1] - eye[5]) B np.linalg.norm(eye[2] - eye[4]) C np.linalg.norm(eye[0] - eye[3]) return (A B) / (2.0 * C) cap cv2.VideoCapture(driver_video.mp4) while True: ret, frame cap.read() if not ret: break gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) faces detector(gray, 0) if len(faces) 0: cv2.putText(frame, No face, (30, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2) for face in faces: shape predictor(gray, face) left_eye np.array([(shape.part(i).x, shape.part(i).y) for i in range(36, 42)], dtypenp.float64) right_eye np.array([(shape.part(i).x, shape.part(i).y) for i in range(42, 48)], dtypenp.float64) ear (eye_aspect_ratio(left_eye) eye_aspect_ratio(right_eye)) / 2.0 if ear EYE_AR_THRESH: frame_counter 1 if frame_counter EYE_AR_CONS_FRAMES: alarm_on True cv2.putText(frame, FATIGUE ALERT!, (100, 100), cv2.FONT_HERSHEY_SIMPLEX, 1.2, (0, 0, 255), 3) else: frame_counter 0 alarm_on False cv2.putText(frame, fEAR: {ear:.2f} Frames: {frame_counter}, (30, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) cv2.imshow(Fatigue Detection, frame) if cv2.waitKey(1) 0xFF ord(q): break cap.release() cv2.destroyAllWindows()EYE_AR_THRESH和EYE_AR_CONS_FRAMES分別對應(yīng)論文中的閉眼閾值和疲勞閾值。計(jì)數(shù)器邏輯是關(guān)鍵每幀 EAR 小于閾值就累加直到達(dá)到疲勞閾值才報(bào)警這樣一次快速眨眼只有三四幀低于閾值不會(huì)觸發(fā)報(bào)警而真正打瞌睡時(shí)閉眼會(huì)持續(xù)半秒以上必然突破連續(xù)幀閾值。這里報(bào)警只做了畫面提示實(shí)際車載系統(tǒng)可以接蜂鳴器或方向盤震動(dòng)模塊。4.2 接口與硬件設(shè)計(jì)論文圖 4 的實(shí)際含義論文 3.3 節(jié)提到接口設(shè)計(jì)雖然沒有給出詳細(xì)電路圖但從系統(tǒng)架構(gòu)能推斷出基本的硬件鏈路USB 攝像頭采集模擬視頻信號通過 USB 接口傳入車載工控機(jī)或嵌入式板卡板卡上的 OpenCV/dlib 處理完幀數(shù)據(jù)后通過 GPIO 或串口輸出報(bào)警信號給蜂鳴器或震動(dòng)座椅。我重新搭過一套類似的用的樹莓派 4B 加 USB 攝像頭處理 640x480 分辨率視頻能達(dá)到 20fps 左右EAR 計(jì)算耗時(shí)約 4ms瓶頸在 dlib 的人臉檢測器上。嵌入式部署有一個(gè)容易被忽視的問題dlib 模型文件約 60MB加載進(jìn)內(nèi)存后樹莓派這類設(shè)備的內(nèi)存占用會(huì)偏高。解決辦法是換用 OpenCV 的 DNN 模塊加載小型人臉檢測模型或者干脆用 MediaPipe 的 Face Mesh它在嵌入式設(shè)備上更輕量。但論文場景下的核心算法邏輯是通用的換檢測器不影響 EAR 計(jì)算與疲勞判定部分。4.3 論文沒提但必須考慮的多人臉、側(cè)臉與遮擋論文實(shí)驗(yàn)環(huán)境是單人正對攝像頭實(shí)際駕駛場景會(huì)出現(xiàn)副駕駛有人、司機(jī)轉(zhuǎn)頭、手部遮擋面部。dlib 檢測器返回的faces是一個(gè)列表代碼里for face in faces會(huì)遍歷所有人臉如果副駕人臉更靠近鏡頭可能先被處理并占用 EAR 計(jì)算資源。需要加一個(gè)人臉排序邏輯取畫面中面積最大的臉作為駕駛員。側(cè)臉場景更麻煩當(dāng)偏航角超過 30 度時(shí)左右眼特征點(diǎn)會(huì)重疊計(jì)算出的 EAR 值變得極小可能持續(xù)低于閾值系統(tǒng)誤報(bào)疲勞。處理方法是用人臉姿態(tài)估計(jì)篩選有效幀我一般用 OpenCV 的solvePnP配合 68 點(diǎn)中的鼻尖、下巴、左右眼角做姿態(tài)解算偏航角超過 30 度的幀直接跳過不參與 EAR 統(tǒng)計(jì)。5. 避坑與常見問題我用這份論文方案時(shí)踩過的五個(gè)坑5.1 幀率不匹配導(dǎo)致連續(xù)幀閾值失效現(xiàn)象按論文參數(shù)設(shè)置EYE_AR_CONS_FRAMES 15在低幀率攝像頭下正常眨眼都被報(bào)警。原因15 幀這個(gè)數(shù)字只有在 30fps 下才對應(yīng)閉眼 0.5 秒。15fps 攝像頭下 15 幀已經(jīng)是閉眼 1 秒了反而容易漏報(bào)如果幀率只有 10fps15 幀對應(yīng) 1.5 秒漏報(bào)更嚴(yán)重。反之幀率 60fps 時(shí) 15 幀僅對應(yīng) 0.25 秒正常眨眼就可能觸發(fā)報(bào)警。解決處理前先讀取cap.get(cv2.CAP_PROP_FPS)把連續(xù)幀閾值換算成時(shí)間按int(fps * 0.5)方式動(dòng)態(tài)設(shè)置。從那以后我每次接入新攝像頭第一件事就是打印幀率再?zèng)Q定閾值取多少。5.2 光照突變導(dǎo)致 EAR 曲線出現(xiàn)斷崖式下跌現(xiàn)象車輛駛出隧道瞬間畫面亮度驟變EAR 值突然從 0.3 掉到 0.1觸發(fā)報(bào)警。原因dlib 關(guān)鍵點(diǎn)檢測對光照變化敏感光線突變時(shí)眼瞼特征點(diǎn)回歸不穩(wěn)定上下眼瞼點(diǎn)可能收斂到同一位置導(dǎo)致分子趨近于零。解決對 EAR 序列做低通濾波我用的是滑動(dòng)窗口均值窗口大小取 5~7 幀。光照突變引起的異常 EAR 通常只持續(xù)一兩幀均值濾波后會(huì)被平抑掉。同時(shí)可以檢測畫面整體亮度變化率變化超過 30% 時(shí)短暫凍結(jié)疲勞判定等光線穩(wěn)定后再恢復(fù)。5.3 眼鏡反光讓上眼瞼點(diǎn)漂移現(xiàn)象佩戴反光較強(qiáng)的鏡片時(shí)左眼 EAR 正常但右眼 EAR 長期偏低系統(tǒng)頻繁報(bào)警。原因眼鏡片反射的燈光或陽光會(huì)在眼周區(qū)域形成高光HOG 特征提取時(shí)高光區(qū)域梯度極大關(guān)鍵點(diǎn)回歸被高光吸引上眼瞼點(diǎn)被拉到鏡片反光位置。解決先用 Haar 檢測眼鏡區(qū)域或者對眼部 ROI 做直方圖均衡化壓低高光影響。更簡單的做法是直接提高閉眼閾值到 0.22并加大連續(xù)幀閾值到 20犧牲一點(diǎn)靈敏度換取穩(wěn)定性。論文沒提眼鏡問題但在中國駕駛員里戴眼鏡的比例很高這一條不做肯定翻車。5.4 人臉檢測器在低分辨率下漏檢現(xiàn)象攝像頭分辨率設(shè)為 320x240 時(shí)人臉稍遠(yuǎn)就檢測不到程序直接輸出No face。原因dlib 的 HOG 人臉檢測器要求人臉區(qū)域至少 80x80 像素320x240 畫面中人的頭部可能只有 60x60 像素。解決把detector(gray, 0)改成detector(gray, 1)開啟一次圖像金字塔上采樣相當(dāng)于把圖像放大一倍再檢測小臉也能框出來。代價(jià)是每幀耗時(shí)從約 15ms 漲到約 30ms。另一個(gè)方案是縮小輸出畫面但不縮小處理畫面即攝像頭采集 640x480檢測時(shí)用完整分辨率顯示時(shí)才縮放。5.5 連續(xù)幀計(jì)數(shù)器沒有設(shè)置上限現(xiàn)象司機(jī)確實(shí)閉眼睡著了系統(tǒng)報(bào)警一次后EAR 一直低于閾值計(jì)數(shù)器持續(xù)累加報(bào)警邏輯反復(fù)觸發(fā)。原因計(jì)數(shù)器無上限時(shí)閉眼 10 秒和閉眼 1 秒的最終結(jié)果一樣都是超過閾值后報(bào)警但無法區(qū)分輕微疲勞和深度睡眠。解決為計(jì)數(shù)器設(shè)置一個(gè)最大值比如frame_counter min(frame_counter, EYE_AR_CONS_FRAMES 30)達(dá)到最大值后保持報(bào)警狀態(tài)但不再累加。更進(jìn)階的做法是分兩級判定連續(xù) 15 幀觸發(fā)一級警告連續(xù) 40 幀觸發(fā)二級強(qiáng)警告對應(yīng)論文里閉眼或瞇眼時(shí)間過長的表述。6. 進(jìn)階把 PERCLOS 指標(biāo)引入系統(tǒng)替換簡單的連續(xù)幀計(jì)數(shù)論文用連續(xù)幀計(jì)數(shù)判斷疲勞這在工程上過于粗暴。真實(shí)駕駛場景中司機(jī)可能不會(huì)一次性閉眼超過 0.5 秒而是頻繁出現(xiàn)微閉眼每次只持續(xù) 0.2~0.3 秒。這類情況用連續(xù)幀閾值完全檢測不到。我在論文方案基礎(chǔ)上引入 PERCLOS 指標(biāo)即單位時(shí)間內(nèi)眼睛閉合時(shí)間占比這是交通心理學(xué)研究里公認(rèn)的疲勞度量。具體做法是計(jì)算最近 60 秒內(nèi)眼睛閉合幀數(shù)占總幀數(shù)的比例。統(tǒng)計(jì)窗口用 60 秒閉眼幀定義為 EAR 小于 0.2 的幀。當(dāng) PERCLOS 超過 0.15即每分鐘閉眼累計(jì) 9 秒以上判定為疲勞狀態(tài)。這個(gè)指標(biāo)對頻繁短閉眼更敏感因?yàn)樗墙y(tǒng)計(jì)量而不是連續(xù)量。實(shí)現(xiàn)上只需要在原有 EAR 循環(huán)里增加一個(gè)環(huán)形緩沖區(qū)from collections import deque ear_history deque(maxlen1800) # 30fps下60秒的幀數(shù) closed_frames 0 window_frames 0 while True: # 原有EAR計(jì)算邏輯... ear_history.append(ear) if len(ear_history) 1800: closed_frames sum(1 for e in ear_history if e 0.20) perclos closed_frames / 1800 if perclos 0.15: cv2.putText(frame, FATIGUE (PERCLOS), (100, 100), cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 0, 255), 3) ear_history.clear()deque(maxlen1800)自動(dòng)丟棄最老的數(shù)據(jù)幀保持窗口長度為 60 秒省去手動(dòng)管理列表頭尾的麻煩。perclos 0.15這個(gè)閾值參考了駕駛疲勞研究的常模但實(shí)際使用時(shí)需要根據(jù)目標(biāo)人群微調(diào)經(jīng)常熬夜的司機(jī)的正常閉眼頻率本身就高閾值要放寬到 0.18 左右。驗(yàn)證這個(gè)進(jìn)階方案是否有效你可以錄制一段 5 分鐘視頻前 3 分鐘正常開車狀態(tài)后 2 分鐘模擬頻繁短閉眼。用原有連續(xù)幀邏輯大概率報(bào)不出警換 PERCLOS 后能穩(wěn)定檢出。從那以后我做人臉疲勞檢測默認(rèn)就是連續(xù)幀閾值加 PERCLOS 雙通道并行判定單一指標(biāo)太容易出邊界 case。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取