戰(zhàn):刷臉門禁系統(tǒng)從方案到踩坑全指南)
前陣子有人找我說他們團(tuán)隊(duì)做了一個帶門禁功能的管理系統(tǒng)想讓攝像頭“認(rèn)識”每一個進(jìn)門的員工。需求一句話就能講完刷臉開門。但真正落地的時候從選方案、調(diào)閾值到處理各種照片過不了、活體被繞過的問題遠(yuǎn)比想象中麻煩。這些人臉識別能力現(xiàn)在被云服務(wù)打包成了API騰訊云上就有現(xiàn)成的傳兩張照片給你返回相似度傳一個人臉給你在庫里找人。這篇文章我就基于這個場景把從需求拆解、選型、核心接口、完整接入代碼到踩坑排查的整個流程捋一遍給正準(zhǔn)備接人臉識別的后端同學(xué)一份可以直接抄的實(shí)踐筆記。1. 方案設(shè)計(jì)人臉識別的需求拆解與云端選型思路1.1 先分清楚你要的到底是哪一種人臉能力很多人一上來就搜“人臉識別”結(jié)果看到檢測、比對、搜索、活體、人臉庫一堆名詞直接懵了。其實(shí)這些詞對應(yīng)的是完全不同的接口和能力選錯了整個系統(tǒng)邏輯都要推翻。我先給朋友捋了一下刷臉門禁這件事可以拆成兩層第一步得知道鏡頭前“是不是一張人臉”并且這張臉在不在你的員工名單里這是人臉檢測加人臉?biāo)阉?:N。第二步得確認(rèn)這張臉是“活的”不是拿張照片在鏡頭前晃這是活體檢測。如果場景換成手機(jī)上的實(shí)名核身那就是人臉比對1:1拿現(xiàn)場拍的臉和身份證照片比相似度。用一張表格理解會更直觀能力接口范圍典型場景回答的問題人臉檢測DetectFace抓拍、統(tǒng)計(jì)圖里有沒有臉臉在哪個位置人臉比對CompareFace1:1核身這兩張臉是不是同一個人人臉?biāo)阉鱏earchFaces人臉庫打卡、門禁這張臉在庫里對應(yīng)哪個人人臉庫管理CreatePerson / DeletePerson員工名單增刪庫里的底片怎么維護(hù)活體檢測獨(dú)立能力防止照片視頻攻擊鏡頭前是個活人還是假臉我經(jīng)常看到有人把“人臉比對”當(dāng)“人臉?biāo)阉鳌庇米畹湫偷腻e誤是庫里存了1000個員工進(jìn)了門禁系統(tǒng)就遍歷1000次比對一次一次地調(diào)接口。這不僅是性能災(zāi)難邏輯上也完全不對應(yīng)該直接用搜索接口一次性在人員庫中檢索。選方案之前先把場景目標(biāo)是1:1還是1:N定死后面所有設(shè)計(jì)都圍繞這個來。1.2 為什么我勸你選云端API而不是自己訓(xùn)練模型自訓(xùn)練人臉模型這件事不是不能做而是很多團(tuán)隊(duì)低估了成本。先算賬。人臉識別背后是一個龐大的深度學(xué)習(xí)模型要自己訓(xùn)練你首先得有一批合規(guī)標(biāo)注過的數(shù)據(jù)少說幾十萬張人臉。采集數(shù)據(jù)、清洗、標(biāo)注、訓(xùn)練調(diào)參、模型壓縮、部署上線、后續(xù)數(shù)據(jù)回流迭代每一步都需要專門的人。對于絕大多數(shù)業(yè)務(wù)團(tuán)隊(duì)來說養(yǎng)一個算法工程師的成本夠你調(diào)幾十年的云API。更現(xiàn)實(shí)的問題是就算你把模型訓(xùn)出來了手機(jī)上、攝像頭上算力不同還得做推理優(yōu)化這個工程量不是一個“順便做個人臉識別”的項(xiàng)目能扛住的。那云端方案解決了什么它把最核心的模型能力變成了一個HTTP接口。你傳照片它返回結(jié)構(gòu)化結(jié)果。背后的模型怎么更新、怎么優(yōu)化都不用你操心。騰訊云的人臉識別API就是這么個東西開箱即用按量付費(fèi)或者買資源包前期做功能驗(yàn)證的時候成本可以壓得非常低。當(dāng)然云API也不是萬能。如果你有強(qiáng)數(shù)據(jù)合規(guī)要求圖片不能出內(nèi)網(wǎng)或者你的調(diào)用量大到按量付費(fèi)的成本已經(jīng)超過自建的邊際成本那時候再考慮私有化部署或者白盒模型。但大部分業(yè)務(wù)項(xiàng)目云API是最短路徑。1.3 業(yè)務(wù)鏈路與整體架構(gòu)設(shè)計(jì)以門禁項(xiàng)目為例整體鏈路是這樣的攝像頭或手機(jī)端拍下人臉照片 → 圖片上傳到你的后端服務(wù) → 后端調(diào)用云端人臉識別接口 → 拿到識別結(jié)果 → 執(zhí)行開鎖/打卡/推送通知等業(yè)務(wù)動作。這里面有個設(shè)計(jì)要點(diǎn)不要在端上做完整的人臉識別鏈路。端上只負(fù)責(zé)采集圖像所有識別邏輯都放到服務(wù)端。這樣做的好處有三個算法更新只需要換服務(wù)端配置不用強(qiáng)制升級App人臉庫底圖集中在服務(wù)端好做權(quán)限管理日志統(tǒng)一在服務(wù)端出事能追溯。代價是每次請求都要傳一張圖過去流量成本可以接受圖片壓縮一下通常幾十KB到幾百KB。還有一個容易忽略的點(diǎn)人臉庫與業(yè)務(wù)人員庫的數(shù)據(jù)一致性。你維護(hù)了一套員工表里面存了員工ID、姓名人臉庫里的PersonId最好直接用同一個員工ID不要另搞一套關(guān)聯(lián)關(guān)系。不然離職刪員、換照片的時候兩邊數(shù)據(jù)很難同步很容易出現(xiàn)“人不在庫里但工號還在”這類臟數(shù)據(jù)。我當(dāng)時讓朋友直接以工號當(dāng)PersonId一周后他就體會到這個設(shè)計(jì)有多省事了。2. 核心接口與關(guān)鍵參數(shù)分?jǐn)?shù)、閾值、活體這些坑一次說清2.1 人臉檢測與人臉比對接口到底返回什么先說人臉檢測。大多數(shù)人以為DetectFace只是告訴你“有沒有臉”其實(shí)它能返回的信息比這多得多人臉位置坐標(biāo)框、五官關(guān)鍵點(diǎn)、年齡、性別、是否戴眼鏡、是否眨眼的屬性。但要注意人臉檢測不等于身份識別它只是感知層面的能力識別身份要靠比對和搜索。人臉比對是1:1場景的主力接口傳兩張圖過去返回一個0到100之間的相似度分?jǐn)?shù)。這里要提醒一句不同家、不同版本的人臉識別服務(wù)返回的分?jǐn)?shù)不是同一個語義不能拿A家的80分和B家的80分比較。騰訊云這個接口的分?jǐn)?shù)是“同一個人的置信度”分?jǐn)?shù)越高說明兩張臉越可能是同一個人。實(shí)操的時候圖片參數(shù)有兩種傳法直接傳Base64編碼后的字符串或者傳圖片URL。我建議大部分場景用Base64因?yàn)閁RL還需要保證公網(wǎng)可訪問增加了一層網(wǎng)絡(luò)不可用的風(fēng)險而且URL訪問也要鑒權(quán)。Base64的壞處是參數(shù)會變大但圖片控制在幾百KB以內(nèi)完全沒問題。2.2 相似度閾值怎么定考勤打卡和門禁鎖完全是兩個檔位這是很多人問得最多的一個問題返回分?jǐn)?shù)出來了那多少分算同一個人我直接給一套經(jīng)驗(yàn)值。官方默認(rèn)推薦一般是80分但80分打天下一定出事。為什么因?yàn)殚撝蹈叩椭苯記Q定兩個指標(biāo)閾值調(diào)高安全性好但容易把本人拒之門外用戶體驗(yàn)差這叫“拒識率”升高。閾值調(diào)低通過率上去了但陌生人容易被放進(jìn)來這叫“誤識率”升高。這倆是蹺蹺板沒有哪個分?jǐn)?shù)是萬能正確值必須根據(jù)業(yè)務(wù)對風(fēng)險的容忍度來選。我在門禁項(xiàng)目里的建議是分場景測試業(yè)務(wù)場景推薦閾值設(shè)計(jì)邏輯手機(jī)解鎖60-70追求通過率錯了還能換指紋或密碼兜底考勤打卡75-80容忍偶爾代刷風(fēng)險優(yōu)先保證員工能正常打卡倉庫/實(shí)驗(yàn)室門禁85-90安全優(yōu)先寧肯多刷幾次也不放陌生人進(jìn)金融級1:1核身90以上結(jié)合活體與多因子驗(yàn)證避免身份冒用上面這個表是我自己的經(jīng)驗(yàn)值不是官方的固定配置建議你接完接口后拿一批真實(shí)業(yè)務(wù)數(shù)據(jù)回測一下?;販y方法很簡單拿100個員工每人錄入一張底圖再讓他們分別在早上、中午、晚上用手機(jī)各拍一張人臉跑下來統(tǒng)計(jì)一下本人比對分?jǐn)?shù)的分布區(qū)間。再把幾個陌生人的照片也跑一遍看誤識分?jǐn)?shù)分布。兩個分布中間的凹槽附近通常就是一個適合你這個場景的閾值。競品項(xiàng)目我做過一次采集200人測試數(shù)據(jù)后最終把閾值從默認(rèn)80調(diào)到了78誤識率幾乎沒變化但拒識率直接降了三分之一用戶滿意度提升非常明顯。2.3 活體檢測不是可選項(xiàng)別拿一張照片就開鎖我加這個項(xiàng)目時朋友一開始覺得活體檢測多余“庫里的都是員工誰會拿個假臉來刷”我說你隨便從朋友圈找一張員工正面照打印出來放到攝像頭前試試。他一試秒開。這是人臉識別項(xiàng)目里最典型的漏洞。單純靠“像不像”是攔不住靜態(tài)照片攻擊的。一個成熟的門禁系統(tǒng)活體檢測必須和身份識別配合起來而且順序有講究先做活體檢測確認(rèn)是真人再拿真人的臉去做比對或搜索。順序反了等于把門先開了一半。騰訊云生態(tài)里對應(yīng)的是人臉核身系列能力包含動作活體、數(shù)字活體等模式。動作活體就是讓你眨眨眼、張張嘴、點(diǎn)點(diǎn)頭數(shù)字活體是讓你念一串?dāng)?shù)字。動作活體的體驗(yàn)比數(shù)字活體輕但需要用戶配合適合主動核身場景靜默活體不需要用戶配合體驗(yàn)最好但對設(shè)備和光線要求更高。你要是做一個艙體門禁用戶進(jìn)出頻繁動作活體每趟都讓人轉(zhuǎn)個頭體驗(yàn)會很糟糕這時候可以考慮靜默活體高頻比對的方式。但無論選哪種上線前一定要拿手機(jī)照片、視頻、面具做一輪攻擊性測試不要想當(dāng)然。2.4 人臉庫管理的幾個關(guān)鍵動作人臉?biāo)阉鞯那疤崾悄愕糜幸粡埖讏D放在庫里。庫的管理主要有四個動作創(chuàng)建人員、給人員添加人臉、更新人員人臉、刪除人員。創(chuàng)建人員時有個人臉庫還是空庫都可以直接用PersonId關(guān)聯(lián)業(yè)務(wù)ID。給人員添加人臉時API會返回一個FaceId這個ID不等于人物ID它是這張臉的唯一標(biāo)識。你更新了底圖之后舊的FaceId會失效所以不要在自己業(yè)務(wù)表里長期緩存FaceId做邏輯。刪除人員這個動作很多人會忽略它背后的業(yè)務(wù)流程。離職員工不能只把業(yè)務(wù)狀態(tài)置為“離職”如果人臉庫里的底圖沒刪他還能刷臉進(jìn)來。我當(dāng)時給朋友的方案是人員離職事件觸發(fā)后先調(diào)刪除人臉庫接口再改業(yè)務(wù)狀態(tài)兩個步驟中間如果失敗就靠消息隊(duì)列重試保證最終一致。這個細(xì)節(jié)看著小真出了安全事故就是大事。3. 完整接入實(shí)操從開通、鑒權(quán)到一次真實(shí)調(diào)用3.1 開通服務(wù)與密鑰準(zhǔn)備接入之前先要在云控制臺找到人臉識別服務(wù)并開通。這個過程基本都是界面化操作點(diǎn)開通就行但我強(qiáng)烈建議你做兩件事。第一別用主賬號的密鑰去調(diào)接口。創(chuàng)建子賬號只給人臉識別服務(wù)的調(diào)用權(quán)限把SecretId和SecretKey放到環(huán)境變量或者專門的配置中心里不要寫死到代碼倉庫。主賬號密鑰一旦泄露相當(dāng)于把整個云賬號交出去了而子賬號密鑰泄露最多損失人臉識別這一項(xiàng)的費(fèi)用。第二開通后先看控制臺里的免費(fèi)額度說明。人臉識別通常會有一定量的免費(fèi)調(diào)用額度夠你做功能驗(yàn)證和聯(lián)調(diào)。先跑通流程再談付費(fèi)的事別一上來就買一堆資源包。3.2 前端采集與后端處理攝像頭采集到的圖片不要直接一股腦傳給識別接口。前端可以先做人臉區(qū)域裁剪和壓縮把整幀大圖里那一小塊人臉截出來再上傳。這樣做有兩個好處圖片小了網(wǎng)絡(luò)傳輸快背景信息少了識別準(zhǔn)確率更高。人臉之外的背景越多越容易干擾算法判斷。后端拿到圖片后需要判斷一下格式和大小。騰訊云人臉識別支持常見格式單張圖片建議不要過大處理前最好統(tǒng)一轉(zhuǎn)成JPG、壓縮到幾百KB以內(nèi)。圖片轉(zhuǎn)成Base64時注意不要把整個響應(yīng)體都打出來打日志幾十KB的字符串打到日志里日志系統(tǒng)會直接崩潰這個錯誤我見得太多了。3.3 騰訊云人臉識別Python SDK調(diào)用示例我最常用的是Python下面這個示例直接用騰訊云官方SDK完成人臉比對import os import json import base64 from tencentcloud.common import credential from tencentcloud.common.profile.client_profile import ClientProfile from tencentcloud.common.profile.http_profile import HttpProfile from tencentcloud.face.v20180301 import face_client, models def encode_image(image_path): with open(image_path, rb) as f: return base64.b64encode(f.read()).decode(utf-8) def compare_face(image_a_path, image_b_path): # 從環(huán)境變量讀取密鑰避免硬編碼 cred credential.Credential( os.environ.get(TENCENTCLOUD_SECRET_ID), os.environ.get(TENCENTCLOUD_SECRET_KEY) ) http_profile HttpProfile() http_profile.endpoint face.tencentcloudapi.com client_profile ClientProfile() client_profile.httpProfile http_profile client face_client.FaceClient(cred, , client_profile) req models.CompareFaceRequest() params { ImageA: encode_image(image_a_path), ImageB: encode_image(image_b_path) } req.from_json_string(json.dumps(params)) resp client.CompareFace(req) result json.loads(resp.to_json_string()) return result.get(Score)這段代碼做了幾件事從環(huán)境變量讀密鑰把兩張本地圖片轉(zhuǎn)成Base64用官方SDK封裝請求最終拿到相似度分?jǐn)?shù)。SDK會自動處理鑒權(quán)簽名所以你看不到手寫加密的過程這就是用SDK的好處。換成人臉?biāo)阉饕彩且粯拥奶茁分皇前袰ompareFaceRequest換成SearchFacesRequest參數(shù)里傳入人員庫ID列表。跑通了這段代碼你就已經(jīng)把整個人臉識別主鏈路打通了。后續(xù)要接業(yè)務(wù)系統(tǒng)只需要把這個函數(shù)包裝成一個內(nèi)部服務(wù)暴露給上層業(yè)務(wù)調(diào)用即可。3.4 代碼里容易被忽略的三個細(xì)節(jié)第一個細(xì)節(jié)是超時設(shè)置。SDK默認(rèn)超時可能較短真實(shí)業(yè)務(wù)中圖片上傳加服務(wù)端處理高峰期可能要一兩秒甚至更久。建議把超時時間拉長到5秒以上不然一到高峰期全是超時報(bào)錯。第二個細(xì)節(jié)是異常捕獲。人臉識別不是每次都能成功返回圖片沒人臉、圖片質(zhì)量太差、請求頻率超限、服務(wù)端臨時故障各種異常都要考慮。不要假設(shè)每次調(diào)用都返回一個漂亮分?jǐn)?shù)要把異常當(dāng)成正常分支來寫。第三個細(xì)節(jié)是日志脫敏。Base64圖片本身也是敏感數(shù)據(jù)控制臺和日志里不要打印完整字符串。尤其是做問題排查的時候可以把圖片縮略圖或者取MD5值記錄下來方便定位問題又不會泄露人臉數(shù)據(jù)。3.5 業(yè)務(wù)集成考勤打卡的完整流程以考勤打卡為例接口調(diào)用只是中間一環(huán)完整的業(yè)務(wù)邏輯是員工走到打卡機(jī)前 → 設(shè)備拍一張照片 → 后端先做活體檢測 → 通過后調(diào)人臉?biāo)阉鹘涌?→ 在指定人員庫中檢索 → 返回匹配人員的PersonId和相似度分?jǐn)?shù) → 分?jǐn)?shù)超過閾值 → 記錄打卡時間返回成功 → 否則提示打卡失敗。這一套流程里有兩個優(yōu)化點(diǎn)。一是結(jié)果緩存同一個員工在很短的時間內(nèi)重復(fù)打卡比如在門口連續(xù)刷了兩次第二次可以不必再調(diào)用云端搜索接口直接用第一次的PersonId結(jié)果命中即可。二是并發(fā)控制打卡機(jī)可能瞬間涌入多人后端需要做好限流和隊(duì)列不要一次性懟幾百個請求到云端接口。4. 實(shí)戰(zhàn)中的高頻問題與排查技巧4.1 鑒權(quán)失敗或提示無權(quán)限調(diào)用接口遇到AuthFailure或者權(quán)限不足九成是以下三個原因之一密鑰配錯了SecretId或SecretKey不匹配檢查環(huán)境變量有沒有加載對。子賬號沒有授權(quán)你用的是子賬號密鑰但沒給這個子賬號綁定人臉識別服務(wù)的策略。本地時間和服務(wù)器時間偏差過大簽名用的是UTC時間戳設(shè)備時間不對也能導(dǎo)致簽名校驗(yàn)失敗。排查時先打印一下請求返回的原始錯誤碼不要只看個SadFace。然后逐一對照上面的原因。4.2 照片質(zhì)量沒問題但識別不準(zhǔn)這是人臉識別最讓人頭疼的問題。識別不準(zhǔn)先別急著懷疑API從數(shù)據(jù)源頭排查圖片太暗或太亮面部特征不明顯。人臉識別最理想的是均勻光照側(cè)光和逆光都會大幅影響分?jǐn)?shù)。我建議在門禁機(jī)位置裝補(bǔ)光燈成本低效果立竿見影。角度太大。人臉接口大多期望近似正臉你讓員工低頭看一眼手機(jī)再抬頭走捕捉到的瞬間可能是個大仰角。模糊。攝像頭在人員移動時抓拍很容易糊。可以開啟連拍取清晰度最高的一張送識別。底圖太久沒更新。胖了瘦了、留了胡子、換了發(fā)型都會讓人臉特征漂移。建議員工每半年或一年更新一次底圖或者可以在識別失敗連續(xù)幾次后引導(dǎo)用戶重新錄入。遇到識別不準(zhǔn)我建議用“單人調(diào)試模式”拿到請求里的圖片人工跟底圖對比一下再用調(diào)試工具單獨(dú)調(diào)一次接口看分?jǐn)?shù)到底是多少。這樣能快速定位是前端抓拍的問題還是后端閾值設(shè)置的問題。4.3 免費(fèi)額度消耗很快費(fèi)用怎么控功能上線前調(diào)用量小感受不到成本。上線后用戶多了費(fèi)用會漲得很快。我的成本控制三板斧圖片壓縮把上傳的圖片壓到幾百KB以內(nèi)識別準(zhǔn)確率基本不降但刷新了流量成本和Base64拼接耗時。緩存高頻結(jié)果同一人員在短時間內(nèi)重復(fù)識別直接命中緩存不要每次都打云端接口。分級調(diào)用白天光線好、員工多的時候可以先把照片質(zhì)量和人臉框過濾一遍模糊的根本不送識別。還有就是把云端調(diào)用的日志和費(fèi)用賬單做關(guān)聯(lián)。每個請求都打點(diǎn)記錄業(yè)務(wù)來源和圖片標(biāo)識月底用賬單核對是哪條業(yè)務(wù)線在燒錢。我見過一個團(tuán)隊(duì)跑測試腳本忘了關(guān)一夜燒掉幾個月的免費(fèi)額度那個賬單看得人心疼。4.4 活體檢測被繞過的風(fēng)險防范照片攻擊是活體檢測存在的意義但它不是萬無一失的。視頻攻擊、3D面具、甚至高精度的照片翻拍在某些條件下都可能騙過人眼和算法。如果你的場景安全級別高不要只依賴單層活體可以考慮加上多因子驗(yàn)證刷臉的同時要求刷卡、輸入密碼、手機(jī)確認(rèn)等。上線后的持續(xù)監(jiān)控也很重要。在門禁系統(tǒng)里記錄每次刷臉的結(jié)果如果同一張底圖在短時間內(nèi)被反復(fù)嘗試而且多為失敗就要警惕有人在惡意測試?yán)@過手段這時候可以觸發(fā)人工復(fù)核或者自動加入觀察名單。4.5 關(guān)于數(shù)據(jù)合規(guī)的提醒最后聊一個繞不開的話題人臉數(shù)據(jù)屬于敏感個人信息。做這個項(xiàng)目的時候我?guī)团笥咽崂砹藥讞l底線采集前明確告知用戶并取得同意人臉圖片和底圖加密存儲不用于門禁之外的任何用途提供刪除入口員工離職后能主動清除個人數(shù)據(jù)。不同地區(qū)對生物識別信息的要求可能不一樣具體上線前建議咨詢專業(yè)法務(wù)意見。不要覺得這是小題大做。人臉一旦泄露不像密碼能改你換不了臉。技術(shù)方案做得再漂亮合規(guī)出了問題整個項(xiàng)目都可能被叫停。5. 寫在最后的幾句實(shí)在話人臉識別項(xiàng)目做下來我個人最深的體會是難的不是把接口調(diào)通而是把閾值、活體、異常處理這些“邊界”想清楚。我見過太多項(xiàng)目Demo跑起來的時候人人興奮一上真實(shí)環(huán)境就被用戶罵“刷不開門”“是不是瞎了”問題基本都出在設(shè)計(jì)階段對生產(chǎn)場景的預(yù)估不足。剛開始接手這類項(xiàng)目別急著寫代碼先拿真實(shí)照片在自己業(yè)務(wù)場景里做一輪分?jǐn)?shù)分布測試很多時候你設(shè)的那個閾值根本站不住腳。再一個給未來的自己留條后路所有人臉相關(guān)的圖片、日志、調(diào)用記錄從第一天就設(shè)計(jì)好清理策略。這些小決定項(xiàng)目上線三個月后會變成你最感謝自己的地方。