ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

uni-app小程序接入百度云人脸识别:从录入到1:N搜索全流程实战

uni-app小程序接入百度云人脸识别:从录入到1:N搜索全流程实战 最近在开发一个基于 uni-app 的微信小程序核心功能是接入百度云的人脸录入和人脸识别。项目里既要支持用户自助录入人脸又要在门禁/签到场景下完成 1:N 识别。这一套做完之后身边好几个朋友都在问人脸能力在小程序里到底怎么接、要注意哪些坑。这篇文章我把整套实现思路从头撸一遍包含百度云平台准备、人脸录入和人脸识别的前后端核心代码、微信小程序环境下的关键配置以及我实测下来遇到的高频问题和处理方式。无论你是正在做考勤门禁、会员识别还是想给自己的小程序加一个人脸登录这篇文章都值得先收藏再慢慢看。1. 方案选型与整体架构思考1.1 为什么用 uni-app 百度云这套组合先说选型。小程序原生开发其实也能做人脸识别但你一旦有 App 端、H5 端的潜在需求原生开发的维护成本就上来了。uni-app 的优势在于一套代码编译到微信小程序、App、H5 等多个平台我这次的项目就是先出微信小程序后续很可能要同步出 App 版本所以直接选了 uni-app。人脸识别服务这块百度云现在叫百度智能云是我的首选。理由很简单开通简单、有免费额度、接口文档比较规范而且人脸检测、人脸注册、人脸搜索这些能力都是标准 RESTful 接口uni-app 里用uni.request就能对接不需要额外引入重量级 SDK。对比了一些离线识别方案离线 SDK 在微信小程序里基本跑不动云识别反而是最务实的路线。顺便提醒一下这里说的百度云不是存资源的百度网盘是百度智能云的 AI 开放平台别搞混了。人脸识别相关能力都在“人脸识别”产品下面控制台搜索就能找到。1.2 整体流程与技术链路先把整体链路理清楚后续写代码才不会乱。人脸录入流程小程序端打开摄像头拍照 - 图片压缩转 base64 - 先调百度人脸检测接口做质量校验 - 校验通过后调人脸注册接口 - 百度把人脸特征存入人脸库返回注册结果。人脸识别流程小程序端拍照 - 同样先压缩转 base64 - 调百度人脸搜索接口 - 后端拿到返回的 user_id 和匹配分数 - 根据业务规则放行或拒绝。注意我的流程里多了一步“人脸检测前置”。百度的人脸注册接口本身就带quality_control参数为什么还要多调一次核心原因是体验。检测接口可以提前拿到模糊度、光照、人脸角度这些信息我可以在前端给出“请正对屏幕”“光线太暗”“请摘掉遮挡物”这类明确提示而不是等注册接口返回一个冷冰冰的错误码。用户感知完全不一样录入成功率也更高。1.3 关键决策密钥必须放后端这是我个人最想强调的一点。百度的 API Key、Secret Key 绝对不能放在小程序前端代码里。小程序包本质上就是一份可以被反编译的静态资源密钥一旦泄露别人就能拿你的账号调接口轻则产生费用重则被人恶意写入大量人脸数据。正确做法是后端封装一层转发接口前端把图片 base64 和用户标识传给后端后端拿着密钥去调百度接口再把结果返回前端。如果你没有独立服务器可以考虑 uniCloud 云函数用云函数转发请求省去服务器运维成本。我这次项目后端是用的 Node.js文章后面给的示例代码也是 Node 风格。2. 百度云接入准备开通服务与凭证管理2.1 创建应用与人脸库规划首先在百度智能云控制台完成账号登录搜索“人脸识别”开通服务。然后在控制台左侧找到“应用列表”创建一个新应用创建完成后能看到应用对应的 API Key 和 Secret Key这两个值就是后面调接口的凭证。接着要规划人脸库的结构。百度人脸识别里的核心概念是group_id用户组和user_id用户 ID。一个组下面可以有多个用户一个用户下面可以有多张人脸。门禁场景通常按部门或场所建组比如building_a_group我用的是staff_group。这个组 ID 后面注册和搜索都要用一定要约定好不要随手写几个字符串就丢一边。免费额度这块百度会给你一定的免费调用量个人开发测试完全够用。但如果识别频率很高提前在控制台看一下计费说明避免产生意外费用。2.2 access_token 获取与缓存技巧调用百度人脸识别 API 时绝大多数接口都要带access_token。它是用 API Key 和 Secret Key 换来的临时凭证有效期大约 30 天。这里有个非常容易踩的坑不能每次请求都去换 token。百度对获取 token 的接口有频率限制频繁调用会报错而且每次都多一次网络请求白白浪费时间。正确做法是后端获取一次然后把 token 缓存起来只在快要过期时重新获取。下面是我项目里用的 Node 端获取和缓存 token 的示例配合一个简单的 JSON 文件做缓存。const axios require(axios); const fs require(fs); const path require(path); const API_KEY 你的API_KEY; const SECRET_KEY 你的SECRET_KEY; const TOKEN_URL https://aip.baidubce.com/oauth/2.0/token; const CACHE_FILE path.join(__dirname, token_cache.json); async function getAccessToken() { // 读缓存如果没过期直接用 if (fs.existsSync(CACHE_FILE)) { const cache JSON.parse(fs.readFileSync(CACHE_FILE, utf8)); if (cache.access_token cache.expire_time Date.now()) { return cache.access_token; } } const res await axios.get(TOKEN_URL, { params: { grant_type: client_credentials, client_id: API_KEY, client_secret: SECRET_KEY } }); const { access_token, expires_in } res.data; // 提前 5 分钟过期避免边缘时间被拒 const expire_time Date.now() (expires_in - 300) * 1000; fs.writeFileSync(CACHE_FILE, JSON.stringify({ access_token, expire_time })); return access_token; }这个缓存逻辑很简单启动后第一次请求真实获取 token后续直接读缓存缓存文件里记录的过期时间到了下次自动换新的。实测下来非常稳定几乎不会碰到 429 限流。2.3 核心接口版本与请求地址百度人脸识别接口我目前用的是 v3 版本请求域名是aip.baidubce.com。三个核心接口的 path 如下人脸检测/rest/2.0/face/v3/detect人脸注册/rest/2.0/face/v3/faceset/user/add人脸搜索/rest/2.0/face/v3/search如果后续平台接口版本升级用法可能略有变化建议以官方文档“人脸识别 API”为准。我下面的示例基于 v3 接口思路依然适用于新版。3. 人脸录入功能的完整实现3.1 小程序端拍照与图片压缩处理录入人脸第一步是拿到清晰的正脸照片。微信小程序里调起相机拍照我推荐用uni.chooseMedia它比老接口uni.chooseImage功能更全支持直接指定sourceType: [camera]。拿到临时文件路径后不要急着转 base64 上传。相机拍出来的原图通常有几 MB直接转 base64 会让请求体变得很大不仅上传慢还可能触发百度接口的大小限制。所以要先压缩。uni-app 提供了uni.compressImage接口可以按质量压缩也可以指定压缩后的宽度。压缩参数我实测下来的经验是目标宽度设置 500px 左右质量压到 80既能保证人脸特征清晰又能把 base64 控制在几百 KB 以内。这个体积对网络传输和百度接口都比较友好。压缩完成后把图片转成 base64。微信小程序端可以通过FileSystemManager读取本地文件并指定编码为 base64function fileToBase64(filePath) { return new Promise((resolve, reject) { const fs uni.getFileSystemManager(); fs.readFile({ filePath: filePath, encoding: base64, success: (res) resolve(res.data), fail: (err) reject(err) }); }); }拿到 base64 字符串后接下来就可以带着它去请求后端或直接调检测接口了。3.2 人脸质量前置检测把废图挡在门外这一步是我强烈建议加的环节。调用百度人脸检测接口能拿到照片里人脸的概率、角度、模糊度、光照、完整度等质量信息。把这些信息拿来做前置判断注册成功率会有非常明显的提升。检测接口的核心参数imagebase64 图片字符串image_type固定BASE64face_field指定要返回的质量字段至少要包含quality,angle,face_probability后端转发逻辑写在 Node 里可以用 axios 把请求打到百度。下面是检测接口的封装示例const axios require(axios); async function faceDetect(base64Image, accessToken) { const url https://aip.baidubce.com/rest/2.0/face/v3/detect?access_token${accessToken}; const res await axios.post(url, { image: base64Image, image_type: BASE64, face_field: face_probability,angle,quality }); return res.data; }拿到返回结果之后重点看这几个字段face_probability是人脸的概率建议大于 0.8angle_list人脸左右偏转、俯仰、平面旋转角建议角度控制在 30 度以内quality.blur模糊程度越小越清晰quality.illumination光照强度不能太暗也不能太亮百度有个合理范围quality.completeness完整度最好接近 1表示五官没有遮挡我项目里的判断逻辑是概率太低提示“请正对屏幕”角度太大提示“请保持正脸”模糊度偏高提示“请保持稳定不要晃动”光照不合适提示“请到光线均匀的地方”。每个阈值具体是多少建议你拿自己手机在真实环境下多测几轮再定因为百度的质量分和实际感受有时候会有偏差自己定出来的阈值最贴合产品体验。3.3 人脸注册接口调用与用户 ID 映射质量检测通过之后就该调注册接口了。人脸注册接口的路径是/faceset/user/add参数比较多逐个说imagebase64 图片image_typeBASE64group_id人脸库组 IDuser_id用户自定义 IDuser_info用户备注信息可以传姓名或手机号quality_control质量控制建议NORMALliveness_control活体控制建议NORMALuser_id这里有个关键点百度对user_id的格式有要求只能数字、字母、下划线而且长度有限制。微信小程序的 openid 虽然大多是字母数字下划线组合但为了安全和长度考虑不建议直接用 openid 当user_id。我后端的做法是根据 openid 生成一个自增 ID或者用 md5 对 openid 做哈希再截断然后把 openid 和user_id的映射关系存到数据库里。注册接口的 Node 转发代码const axios require(axios); async function faceRegister(base64Image, accessToken, groupId, userId, userInfo) { const url https://aip.baidubce.com/rest/2.0/face/v3/faceset/user/add?access_token${accessToken}; const res await axios.post(url, { image: base64Image, image_type: BASE64, group_id: groupId, user_id: userId, user_info: userInfo || , quality_control: NORMAL, liveness_control: NORMAL }); return res.data; }返回结果里如果error_code为 0说明注册成功。如果返回user_id已存在就需要你根据业务决定是提示用户“已录入”还是调用更新接口faceset/user/update覆盖旧人脸。我建议在注册前先查一下用户是否已录入已录入的走更新流程这样重复录入不会报错用户体验更流畅。4. 人脸识别功能的完整实现4.1 1:N 人脸搜索逻辑识别场景最常用的是 1:N 搜索也就是拿一张人脸去库里找他是谁。搜索接口路径是/rest/2.0/face/v3/search。请求参数image待识别的 base64 图片image_typeBASE64group_id_list要搜索的组多个组用逗号分隔比如staff_groupquality_controlNORMALliveness_controlNORMAL搜索接口的返回结果里有一个score代表匹配度。这个值非常重要一定要在后端设定过滤阈值来决定是否放行。比如门禁场景低于 80 分一律拒绝因为低分代表可能不是同一个人甚至可能是陌生人。为什么单独强调这个阈值因为百度搜索接口不管是不是库里的人都会给你返回一个最接近的结果。如果你直接拿 top1 当作识别结果那陌生人也会被当成某个员工放进去门禁形同虚设。阈值定多少看你的场景安全要求高的可以定 85 甚至 90要求宽松、重点是防重复签到的可以定 75 左右。我建议上线前拿真实用户照片做一轮测试画一个分数分布图再定。后端搜索封装const axios require(axios); async function faceSearch(base64Image, accessToken, groupIdList) { const url https://aip.baidubce.com/rest/2.0/face/v3/search?access_token${accessToken}; const res await axios.post(url, { image: base64Image, image_type: BASE64, group_id_list: groupIdList, quality_control: NORMAL, liveness_control: NORMAL }); return res.data; }4.2 与微信登录体系打通code 换 token 换取 openid人脸识别返回的是user_id但业务里往往还要知道这个人的身份比如姓名、工号。所以录入人脸前一定要把微信的 openid 和后端的user_id绑定起来。小程序的登录流程是这样的前端uni.login拿到code把 code 传给后端后端拿 code 去微信接口jscode2session换 openid 和 session_key。这个流程就是大家常说的 code 换 token。const axios require(axios); async function code2Session(code) { const appid 你的小程序APPID; const secret 你的小程序SECRET; const url https://api.weixin.qq.com/sns/jscode2session; const res await axios.get(url, { params: { appid, secret, js_code: code, grant_type: authorization_code } }); return res.data; // { openid, session_key, ... } }拿到 openid 后后端先查库里有没有这个 openid没有就创建一条用户记录生成新的user_id已经有就直接返回绑定的user_id。这样人脸搜索命中后后端就能用user_id反查到用户信息返回姓名、部门、工号之类的前端展示数据。这块设计好的好处是以后用户换手机、换微信环境人脸数据都不会丢因为人脸是和user_id绑定的和微信登录态解耦。4.3 识别结果的业务落地识别接口通了之后真正要做的其实是业务层。门禁和考勤是典型场景识别成功后需要记一条通行记录或打卡记录。记录里至少要包含时间、地点、设备/小程序标识、识别分数。识别失败也要记录方便事后排查。我当时还额外处理了“重复打卡”问题限制同一用户 5 分钟内只能有一次有效打卡记录防止用户频繁刷脸刷出多条数据。这个逻辑很简单就是在写入打卡记录前查一下最近一条记录的时间。如果你的业务需要更严格的身份核验比如“确保操作者是账号本人”可以考虑百度的人脸比对接口/rest/2.0/face/v3/match让用户登录后直接拍一张照片和库里的人脸做 1:1 比对。这个能力适合支付、隐私信息查看等高安全场景接入方式也类似只是把 search 换成 match。5. 微信小程序环境下的关键细节5.1 域名白名单与 network unavailable 报错微信小程序有个让很多人头疼的规矩wx.request只能请求指定域名下的接口。上线环境下所有请求域名都必须在微信公众平台后台“开发管理 - 开发设置 - 服务器域名”里配置而且必须是 HTTPS。开发调试阶段你可以在微信开发者工具右上角“详情 - 本地设置”勾选“不校验合法域名”这样本地接口能正常调通。但一旦要用真机预览或者上传体验版这个开关就不生效了。很多人在真机上遇到request:fail或network unavailable第一反应是网络问题其实大概率是域名校验没过或者证书有问题。我实测的一个排查顺序第一看接口地址是不是 HTTPS域名有没有备案第二看微信公众平台后台的 request 合法域名有没有填对第三看证书链是否完整很多免费证书在手机端会提示证书无效回到开发者工具里反而正常第四看代码里有没有拼错域名或端口。大多数网络层报错都是这四个原因。5.2 HBuilderX 发行微信小程序的几个要点uni-app 项目在 HBuilderX 里打包发布到微信小程序流程其实很固定但新手容易卡在几个地方。先在manifest.json的“微信小程序配置”里填上自己的 AppID注意不是测试号就是正式 AppID。然后开发调试时点击 HBuilderX 菜单栏“运行 - 运行到小程序模拟器 - 微信开发者工具”。如果没反应很大概率是微信开发者工具没开启服务端口。你需要打开微信开发者工具的“设置 - 安全设置”开启“服务端口”选项。正式发布就在 HBuilderX 菜单栏点“发行 - 小程序-微信”构建后会在项目dist/build/mp-weixin目录下生成微信小程序代码然后用微信开发者工具导入这个目录点击“上传”上传版本再去微信公众平台提交审核。这里额外提醒一句如果你的小程序要用摄像头并涉及人脸信息提审时需要在“用户隐私保护指引”里明确声明摄像头、相册等隐私接口的使用目的。不声明的话审核阶段很可能会被打回。5.3 相机授权与兼容性优化相机权限是使用人脸录入前绕不开的一步。uni-app 里可以用uni.authorize来申请权限如果用户拒绝过再次调用授权会直接失败这时要引导用户去设置页手动打开。当前端拿到图片 base64 后建议做一个统一的请求封装设置合理的超时时间。人脸检测、注册、搜索接口因为涉及图片上传和云端计算通常比普通接口慢超时时间我一般设置 10 秒以上避免在网速差的场景下被前端提前判定失败。还有一点要留意部分 Android 机型拍出来的照片自带旋转信息可能在转 base64 后出现人脸角度不对的问题。我在项目中用uni.compressImage压缩后基本能消除大部分旋转问题但如果你的用户群体里有大量老旧安卓机建议在真机上多测几个品牌。iPhone 这边的兼容性整体会好一些。6. 常见问题与排查技巧实录6.1 高频报错与解决方案速查表报错现象出现阶段原因分析解决办法百度返回“人脸未找到”注册/识别拍照时正脸不完整、遮挡较多前置检测接口先判断优化拍摄引导文案百度返回“图像质量差”注册光线过暗或过曝、图片分辨率过低压缩保留人脸区域提示用户到光线均匀处拍摄百度返回“用户已存在”注册同一个 user_id 重复注册注册前查询已存在则调用 update 接口覆盖小程序请求报 url not in domain list所有接口域名白名单没配或开发者工具未关校验后台配置合法域名或开发时勾选不校验真机请求报 network unavailable所有接口证书不合法、域名未备案、本机网络异常按 5.1 节的排查顺序逐项确认搜索接口返回 score 过低识别人脸被遮挡、光线变化大、录入照片太旧调整阈值引导用户补录多张人脸这块内容建议直接复制到你的项目文档里后续同事接手少走很多弯路。遇到百度返回的error_code时一定不要把错误码硬编码在判断逻辑里先对照官方文档确认这个错误码在当前版本下是否仍存在因为云厂商的文档更新频率不低。6.2 避坑清单AK/SK 和 access_token 永远不要出现在前端代码里前端只传图片和业务需要的标识。access_token 一定要做缓存每次请求都换 token 不仅慢还会触发限流。注册前一定要做人脸质量检测宁可多一次请求也别让用户录一张废图进人脸库。搜索接口必须设置 score 阈值否则陌生人会被当作库里的人返回。图片 base64 体积控制在 1M 以下压缩目标 500px 宽度、80 质量是比较稳的组合。活体控制参数记得打开liveness_control设为NORMAL或HIGH能挡掉一部分照片和视频攻击。注意它不是绝对安全安全要求极高的场景需要额外做二次核验。不要在微信开发者工具里把模拟器当成最终测试环境模拟器对摄像头的模拟能力和真机差异很大人脸这种强依赖硬件的功能必须真机测试。隐私合规别偷懒小程序涉及人脸数据用户协议和隐私指引要写清楚用途否则审核会卡。另外再分享一个我在实际项目中的体会人脸能力接入本身不难真正花时间的是调优“通过率”和“误识别率”的平衡。比如同一间办公室上午逆光、下午顺光拍出来的照片质量完全不同阈值定死了一个固定值实际使用中总有人怎么刷都不过。后来我在后台上线了一个动态阈值配置接口先按 80 分默认值跑观察一周真实识别分数的分布再根据场景微调。这种灰度调优的思路比一上来就把阈值定死要稳妥得多。整套功能上线到现在已经稳定跑了几个月录入的人脸数据也在持续增加。后续我打算把识别记录和告警通知串起来比如识别到陌生人时给管理员推一条消息。这次先写到这里希望这套实现思路能帮你少踩几个坑。
返回列表