news 2026/6/25 13:21:31

人脸识别:基于ONNX Runtime的人脸识别比对系统全栈实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
人脸识别:基于ONNX Runtime的人脸识别比对系统全栈实战

基于ONNX Runtime的人脸识别比对系统全栈实战

1. 项目简介

2. 整体架构

3. 模型准备与加密加载

4. 人脸检测与图像预处理

5. 多任务属性推理

6. 人脸比对与相似度映射

7. 前后端交互设计

8. 前端视觉与体验优化

9. 部署与演示

10. 总结与展望

一个完整的在线人脸识别比对Web应用,涵盖人脸检测、属性分析、活体特征提取与相似度计算。本文将带你一步步拆解其技术实现,并附上在线Demo方便体验。

1. 项目简介

这是一个轻量级的人脸识别比对工具,用户上传两张含有人脸的图片,系统自动完成人脸检测、年龄/性别估计、姿态角计算、活体特征提取,并输出两张人脸的相似度分数。整个系统采用 **Python Tornado** 作为后端框架,**ONNX Runtime** 进行模型推理,前端使用原生 JavaScript 配合 jQuery 交互,部署简单,适合个人学习和小规模应用。

在线演示地址:http://faceqianmian.top/static/index.html

2. 整体架构

系统分为三层:

- **前端**:HTML5 + CSS3 双栏布局,通过 AJAX 调用后端 RESTful API。
- **后端**:Tornado Web 服务,提供图片上传、人脸检测、相似度比对三个接口。
- **模型层**:使用 **ONNX Runtime** 加载加密的 ONNX 模型文件,进行高效推理。

核心流程图:
上传图片 → 图片接收与格式校验 → 人脸检测(Detect) → 人脸裁剪 →
多模型推理(性别/年龄/活体/角度) → 返回结果(特征、属性、框) →
前端展示 & 用户触发比对 → 余弦相似度计算 → 得分映射 → 显示相似度

3. 模型准备与加载

为了保护模型资产,本项目将 ONNX 模型文件使用 AES-CBC 加密存储,运行时动态解密并加载到内存中。

加密方式:

```python from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from cryptography.hazmat.primitives.padding import PKCS7 def decrypt_file_to_bytes(input_path, key, iv): key = bytes.fromhex(key) iv = bytes.fromhex(iv) with open(input_path, 'rb') as fin: ct_bytes = fin.read() cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) decryptor = cipher.decryptor() unpadder = PKCS7(algorithms.AES.block_size).unpadder() plain_text = unpadder.update(decryptor.update(ct_bytes)) + unpadder.finalize() return plain_text ```

加载模型时直接传入解密后的字节流,避免模型文件在磁盘上以明文形式存在:

```python import onnxruntime as ort face_live_model = ort.InferenceSession( decrypt_file_to_bytes('model/face_live.onnx.enc', key, iv), providers=['CPUExecutionProvider'] ) ```

4. 人脸检测与图像预处理

4.1 检测模型预处理

采用标准的 `detect.onnx` 模型(例如 YOLO 变体),输入尺寸为 640x640。为了保持宽高比,使用 **letterbox** 填充方式,填充区域使用灰色 (128,128,128):

```python def detect_pre_process(img, output_size=(640, 640)): # 等比例缩放 + 居中填充 width, height = img.size new_img = Image.new('RGB', output_size, color=(128,128,128)) ratio = min(output_size[0]/width, output_size[1]/height) new_size = (int(width*ratio), int(height*ratio)) resized = img.resize(new_size, Image.BILINEAR) left = (output_size[0] - new_size[0]) // 2 top = (output_size[1] - new_size[1]) // 2 new_img.paste(resized, (left, top)) img_array = np.array(new_img) / 255.0 img_array = img_array.transpose((2, 0, 1)) # CHW return img_array, width, height ```

4.2 检测后处理与坐标映射

模型输出的检测框是相对于 640x640 画布的归一化坐标(中心点 x, y,宽 w,高 h,置信度 conf)。需要根据原始图片的宽高和填充情况,反算到原始图像坐标系:

```python if width > height: midx = box[0] * (width/640) midy = box[1] * (width/640) - (width-height)/2 w = box[2] * (width/640) h = box[3] * (width/640) else: midx = box[0] * (height/640) - (height-width)/2 midy = box[1] * (height/640) w = box[2] * (height/640) h = box[3] * (height/640) x1 = midx - w/2 y1 = midy - h/2 x2 = midx + w/2 y2 = midy + h/2 ```

为了后续属性分析更准确,将检测框向外扩展一定比例作为裁剪区域:

```python x1_crop = x1 - w * 0.3 y1_crop = y1 - h * 0.6 x2_crop = x2 + w * 0.3 y2_crop = y2 + h * 0.2 # 边界裁剪 x1_crop = max(0, x1_crop) y1_crop = max(0, y1_crop) x2_crop = min(width, x2_crop) y2_crop = min(height, y2_crop) face_crop = img.crop((x1_crop, y1_crop, x2_crop, y2_crop)) ```

5. 多任务属性推理

裁剪后的人脸区域统一缩放到各自模型要求的尺寸,并进行标准化。

- **性别、活体、年龄**:输入 224x224,使用 ImageNet 均值和标准差归一化。
- **角度**:输入 64x64,同样的归一化处理。
- **性别分类**:输出两个类别的概率,取 argmax 得到男女。
- **年龄回归**:输出值加 1 并四舍五入,得到整数值。
- **活体特征**:直接返回模型输出的浮点数向量,用于后续比对。
- **姿态角**:模型输出三个角度值(yaw, pitch, roll)。

预处理代码示例:

```python def pre_process(img, size=(224, 224)): mean = np.array([0.485, 0.456, 0.406]).reshape(3,1,1) std = np.array([0.229, 0.224, 0.225]).reshape(3,1,1) img_array = np.array(img.resize(size, Image.BILINEAR)) / 255.0 img_array = img_array.transpose((2,0,1)) return ((img_array - mean) / std).astype('float32') ```

6. 人脸比对与相似度映射

从两张图片中分别提取到活体特征向量后,计算**余弦相似度**:

```python def cosine_similarity(vec1, vec2): vec1, vec2 = np.array(vec1), np.array(vec2) dot = np.dot(vec1, vec2) norm = np.linalg.norm(vec1) * np.linalg.norm(vec2) return dot / norm if norm != 0 else 0 ```

但余弦相似度的范围是 [-1, 1],而我们希望输出 0~100 的分值,更直观。本项目采用了一个分段线性映射函数:

```python def get_score(x): if x <= 0.5: score = 120 * x elif x >= 0.616: score = 26.041666 * x + 73.95833 else: score = 258.62069 * x - 69.31034 score = max(0, min(100, score)) return score ```

该映射将阈值 0.5 映射到 60 分,0.616 映射到 90 分,整个函数是单调递增的,保证分数越高表示越相似。实际应用中,80 分以上通常可认为是同一人。

7. 前后端交互设计

7.1 后端接口

- **上传接口** `/face-api/v1/face/upload`
POST 接收 `image` 文件,校验格式与大小,使用 Pillow 的 `ImageOps.exif_transpose` 自动纠正图片方向,存储到 `static/images/`,返回文件路径。

- **人脸检测与属性接口** `/face-api/v1/face/detect_list`
接收 JSON,内容为多个图片路径数组 `params`,批量处理并返回每个人脸的属性、特征(已加密)以及人脸框。活体特征使用简单的异或加密后经 Base64 编码,确保传输中不被篡改。

- **人脸比对接口** `/face-api/v1/face/match`
接收两个加密特征字符串,解密后计算余弦相似度并映射为 0~100 分数。

7.2 前端逻辑

前端使用 jQuery + layer 弹窗,流程如下:

1. 用户选择图片 → 调用上传接口获取服务器路径。
2. 调用 detect_list 接口,传入图片路径,获取年龄、性别、姿态角、框坐标及加密特征,并显示在原图上(后端将框绘制在图片上)。
3. 用户点击“开始比对” → 取出两张图片的加密特征,调用 match 接口,将返回的分数更新到页面中央的相似度显示区。

前端使用 `$.ajax` 发送请求,同时显示加载动画,并对网络异常、超时等进行了友好提示。

8. 前端视觉与体验优化

界面采用渐变背景、圆角卡片和悬浮动画,让整体风格现代且亲和。双栏布局中间放置 VS 标志和实时相似度,一目了然。

底部提供了详细的使用说明,解释姿态角(Yaw/Pitch/Roll)的含义和推荐范围,帮助用户理解人脸质量对识别的影响。同时,页面绘制了人脸框,并将框坐标、置信度等信息清晰展示。

9. 部署与演示

你可以通过以下地址直接体验完整功能:

http://faceqianmian.top/static/index.html

项目依赖较少,所有模型均加密存储,启动脚本直接运行即可(默认 80 端口):

```bash pip install tornado pillow onnxruntime numpy cryptography python app.py ```

若需修改端口,可通过命令行参数传入:

```bash python app.py 8080 ```

10. 总结与展望

本文展示了一个面向实用场景的轻量级人脸识别比对系统的全栈实现。通过 ONNX Runtime 的跨平台推理能力和 Tornado 的异步高性能,我们可以在普通服务器上流畅运行多个模型。模型加密、特征加密等措施也在一定程度上保护了核心资产。

未来可扩展方向:
- 接入人脸特征数据库,实现 1:N 的人脸检索;
- 增加活体检测动作指令,提升防攻击能力;
- 前端使用 WebSocket 实现更流畅的实时摄像头识别。

希望这篇实战分享能帮助你快速搭建自己的人脸识别应用,如果有任何问题或建议,欢迎在评论区交流!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/23 19:19:44

回顾Java知识点,面试题汇总Day9(持续更新)

1.sleep()和wait()的区别&#xff1f;sleep和wait的功能类似&#xff0c;都是让线程暂停执行任务。sleep是Thread类提供的方法&#xff0c;wait是Object类提供的方法sleep是直接作用于线程对象本身&#xff0c;wait作用于线程正在访问的资源。调用A对象的wait&#xff1a;让当前…

作者头像 李华
网站建设 2026/6/23 19:20:02

Linux内核消息观测生产排障流程

Linux内核消息观测生产排障流程这是一篇面向中级 Linux 使用者的技术文章&#xff0c;主题聚焦在内核消息观测&#xff0c;重点讨论内核日志、驱动事件和硬件异常。在真实生产环境中&#xff0c;内核消息观测相关问题往往不会以单一错误形式出现&#xff0c;而是混杂在日志、权…

作者头像 李华
网站建设 2026/6/23 19:41:57

大模型微调实战:用LoRA技术微调LLaMA 2模型

在人工智能技术飞速发展的当下&#xff0c;大语言模型&#xff08;LLM&#xff09;在自然语言处理领域展现出了强大的能力。LLaMA 2作为Meta推出的开源大模型&#xff0c;凭借其出色的性能和广泛的适用性&#xff0c;成为了众多开发者和研究人员的首选。对于软件测试从业者而言…

作者头像 李华