从零到一:用Retinaface+CurricularFace镜像构建人脸识别Web服务

📅 发布时间:2026/7/5 5:02:47 👁️ 浏览次数:
从零到一:用Retinaface+CurricularFace镜像构建人脸识别Web服务
从零到一用RetinafaceCurricularFace镜像构建人脸识别Web服务想给自己开发的应用加上人脸识别能力但一看到复杂的模型部署、环境配置就头疼别担心今天我就带你用最简单的方式把一个专业级的人脸识别系统变成可调用的Web服务。整个过程就像搭积木一样简单依托CSDN星图平台提供的预置镜像你甚至不需要懂太多深度学习知识跟着步骤走就能搞定。这个镜像已经打包好了RetinaFace和CurricularFace这两个业界知名的人脸识别模型还预装了所有依赖环境。我们要做的就是在这个基础上用Flask框架给它套上一个Web API的“外壳”让任何应用都能通过HTTP请求来调用它。学完这篇文章你会掌握如何快速启动并验证RetinafaceCurricularFace镜像怎样用不到100行代码搭建一个完整的人脸比对Web服务如何设计合理的API接口和错误处理机制将服务部署到公网让外部应用也能访问准备好了吗让我们开始吧。1. 环境准备与镜像验证1.1 理解我们的技术栈在动手写代码之前先花两分钟了解一下我们要用的“积木”是什么。整个系统可以分成三层底层RetinafaceCurricularFace镜像这是CSDN星图平台提供的预置环境相当于一个已经装好所有软件和模型的“电脑”。它包含了Python 3.11和PyTorch 2.5.0深度学习框架CUDA 12.1GPU加速驱动预训练好的RetinaFace和CurricularFace模型一个现成的推理脚本inference_face.py中间层Flask Web框架Flask是一个轻量级的Python Web框架特别适合快速构建API服务。它就像是一个“接线员”负责接收HTTP请求、调用底层的人脸识别功能然后把结果包装成JSON格式返回。应用层你的业务逻辑这是最灵活的一层。你可以基于这个Web服务开发考勤系统、相册管理、门禁控制等各种应用。1.2 启动镜像并验证基础功能首先在CSDN星图平台找到“RetinafaceCurricularFace 人脸识别模型镜像”点击“一键部署”。等待1-2分钟系统会为你创建一个包含所有环境的容器。进入容器后执行以下命令激活环境cd /root/Retinaface_CurricularFace conda activate torch25看到命令行前面出现(torch25)的提示说明环境激活成功。现在让我们先验证一下基础的人脸比对功能是否正常python inference_face.py如果一切正常你会看到类似这样的输出检测到人脸数量: 1 检测到人脸数量: 1 相似度得分: 0.856 判定结果: 同一人这个测试使用了镜像自带的示例图片。输出告诉我们两件事每张图片都检测到了1张人脸两张脸的相似度是0.856大于默认阈值0.4所以判定为同一人现在用你自己的照片测试一下python inference_face.py --input1 /path/to/your/photo1.jpg --input2 /path/to/your/photo2.jpg如果也能正常输出相似度分数恭喜你底层的人脸识别功能已经就绪。接下来我们要给它加上Web API的能力。2. 构建基础Web服务2.1 创建Flask应用骨架在/root/Retinaface_CurricularFace目录下创建一个新的Python文件命名为app.py。我们将从这里开始构建我们的Web服务。首先导入必要的库并初始化Flask应用from flask import Flask, request, jsonify import os import sys # 将当前目录添加到Python路径以便导入推理模块 sys.path.append(/root/Retinaface_CurricularFace) # 初始化Flask应用 app Flask(__name__) # 这里稍后会添加人脸识别器的初始化代码 if __name__ __main__: app.run(host0.0.0.0, port5000, debugTrue)这段代码做了几件事导入了Flask的核心组件确保能够导入镜像中的人脸识别模块创建了一个Flask应用实例设置了服务运行在5000端口并开启调试模式2.2 封装人脸识别功能接下来我们需要封装镜像提供的人脸识别能力。查看inference_face.py脚本我们可以提取出核心的识别逻辑。创建一个新的文件face_service.py来专门处理人脸识别import cv2 import torch import numpy as np from modelscope.pipelines import pipeline from modelscope.utils.constant import Tasks class FaceRecognitionService: def __init__(self): 初始化人脸识别服务 print(正在加载人脸识别模型...) # 初始化RetinaFace人脸检测器 self.face_detection pipeline( taskTasks.face_detection, modeldamo/cv_resnet50_face-detection_retinaface ) # 初始化CurricularFace人脸识别器 self.face_recognition pipeline( taskTasks.face_recognition, modelbubbliiiing/cv_retinafce_recognition ) print(模型加载完成) def extract_feature(self, image_path): 从图片中提取人脸特征 Args: image_path: 图片路径或URL Returns: 512维的人脸特征向量如果检测不到人脸则返回None try: # 检测人脸 detection_result self.face_detection(image_path) if not detection_result or boxes not in detection_result: print(f在 {image_path} 中未检测到人脸) return None # 使用检测到的人脸进行特征提取 recognition_result self.face_recognition(image_path) if recognition_result and img_embedding in recognition_result: # 转换为numpy数组并归一化 feature recognition_result[img_embedding] feature feature / np.linalg.norm(feature) return feature return None except Exception as e: print(f处理图片 {image_path} 时出错: {str(e)}) return None def compare_faces(self, image_path1, image_path2, threshold0.4): 比较两张图片中的人脸相似度 Args: image_path1: 第一张图片路径 image_path2: 第二张图片路径 threshold: 判定阈值默认0.4 Returns: 字典包含相似度分数和判定结果 # 提取特征 feature1 self.extract_feature(image_path1) feature2 self.extract_feature(image_path2) if feature1 is None or feature2 is None: return { success: False, error: 一张或两张图片中未检测到人脸, similarity: 0.0, is_same_person: False } # 计算余弦相似度 similarity np.dot(feature1, feature2) # 判定是否为同一人 is_same similarity threshold return { success: True, similarity: float(similarity), is_same_person: bool(is_same), threshold: float(threshold) }这个服务类封装了人脸识别的核心功能__init__: 初始化时加载两个模型人脸检测和人脸识别extract_feature: 从单张图片中提取人脸特征向量compare_faces: 比较两张图片的人脸相似度2.3 实现Web API接口现在回到app.py完善我们的Web服务。我们将创建两个主要的API端点from flask import Flask, request, jsonify import os import tempfile from face_service import FaceRecognitionService app Flask(__name__) # 全局人脸识别服务实例 face_service FaceRecognitionService() app.route(/health, methods[GET]) def health_check(): 健康检查端点 return jsonify({ status: healthy, service: face-recognition-api, version: 1.0.0 }) app.route(/compare, methods[POST]) def compare_faces(): 人脸比对API 支持两种方式上传图片 1. 文件上传multipart/form-data 2. 图片URLapplication/json # 检查请求内容类型 content_type request.headers.get(Content-Type, ) if multipart/form-data in content_type: # 方式1文件上传 return handle_file_upload() elif application/json in content_type: # 方式2URL上传 return handle_url_upload() else: return jsonify({ success: False, error: 不支持的Content-Type请使用multipart/form-data或application/json }), 400 def handle_file_upload(): 处理文件上传 # 检查文件是否存在 if image1 not in request.files or image2 not in request.files: return jsonify({ success: False, error: 需要上传两个图片文件参数名分别为image1和image2 }), 400 file1 request.files[image1] file2 request.files[image2] # 检查文件名 if file1.filename or file2.filename : return jsonify({ success: False, error: 未选择文件 }), 400 # 创建临时文件保存上传的图片 temp_dir tempfile.gettempdir() temp_path1 os.path.join(temp_dir, file1.filename) temp_path2 os.path.join(temp_dir, file2.filename) try: # 保存文件 file1.save(temp_path1) file2.save(temp_path2) # 获取阈值参数可选 threshold float(request.form.get(threshold, 0.4)) # 调用人脸比对服务 result face_service.compare_faces(temp_path1, temp_path2, threshold) # 清理临时文件 os.remove(temp_path1) os.remove(temp_path2) return jsonify(result) except Exception as e: # 发生错误时清理临时文件 if os.path.exists(temp_path1): os.remove(temp_path1) if os.path.exists(temp_path2): os.remove(temp_path2) return jsonify({ success: False, error: f处理过程中发生错误: {str(e)} }), 500 def handle_url_upload(): 处理URL上传 data request.get_json() if not data: return jsonify({ success: False, error: 请求体必须是有效的JSON }), 400 # 检查必要的参数 if image1_url not in data or image2_url not in data: return jsonify({ success: False, error: JSON中必须包含image1_url和image2_url字段 }), 400 image1_url data[image1_url] image2_url data[image2_url] # 获取阈值参数可选 threshold float(data.get(threshold, 0.4)) try: # 直接使用URL进行比对 result face_service.compare_faces(image1_url, image2_url, threshold) return jsonify(result) except Exception as e: return jsonify({ success: False, error: f处理过程中发生错误: {str(e)} }), 500 app.route(/extract, methods[POST]) def extract_feature(): 提取单张图片的人脸特征 返回512维的特征向量 content_type request.headers.get(Content-Type, ) if multipart/form-data in content_type: # 文件上传方式 if image not in request.files: return jsonify({ success: False, error: 需要上传图片文件参数名为image }), 400 file request.files[image] if file.filename : return jsonify({ success: False, error: 未选择文件 }), 400 # 保存临时文件 temp_dir tempfile.gettempdir() temp_path os.path.join(temp_dir, file.filename) file.save(temp_path) try: # 提取特征 feature face_service.extract_feature(temp_path) # 清理临时文件 os.remove(temp_path) if feature is None: return jsonify({ success: False, error: 未检测到人脸 }), 400 return jsonify({ success: True, feature: feature.tolist(), # 转换为列表便于JSON序列化 dimension: len(feature) }) except Exception as e: if os.path.exists(temp_path): os.remove(temp_path) return jsonify({ success: False, error: f特征提取失败: {str(e)} }), 500 elif application/json in content_type: # URL方式 data request.get_json() if not data or image_url not in data: return jsonify({ success: False, error: JSON中必须包含image_url字段 }), 400 try: feature face_service.extract_feature(data[image_url]) if feature is None: return jsonify({ success: False, error: 未检测到人脸 }), 400 return jsonify({ success: True, feature: feature.tolist(), dimension: len(feature) }) except Exception as e: return jsonify({ success: False, error: f特征提取失败: {str(e)} }), 500 else: return jsonify({ success: False, error: 不支持的Content-Type }), 400 if __name__ __main__: app.run(host0.0.0.0, port5000, debugTrue)现在我们有了一个完整的人脸识别Web服务提供三个API端点/health- 健康检查用于监控服务状态/compare- 人脸比对支持文件上传和URL两种方式/extract- 特征提取获取单张图片的人脸特征向量3. 测试与使用Web服务3.1 启动服务并测试API保存所有文件后在终端中启动Web服务cd /root/Retinaface_CurricularFace conda activate torch25 python app.py你会看到类似这样的输出正在加载人脸识别模型... 模型加载完成 * Serving Flask app app * Debug mode: on * Running on all addresses (0.0.0.0) * Running on http://127.0.0.1:5000 * Running on http://192.168.x.x:5000服务已经启动现在打开另一个终端窗口我们来测试API。3.2 使用curl测试API测试健康检查接口curl http://127.0.0.1:5000/health应该返回{ status: healthy, service: face-recognition-api, version: 1.0.0 }测试文件上传方式的人脸比对curl -X POST http://127.0.0.1:5000/compare \ -F image1/path/to/your/photo1.jpg \ -F image2/path/to/your/photo2.jpg \ -F threshold0.5测试URL方式的人脸比对curl -X POST http://127.0.0.1:5000/compare \ -H Content-Type: application/json \ -d { image1_url: https://example.com/photo1.jpg, image2_url: https://example.com/photo2.jpg, threshold: 0.5 }测试特征提取接口# 文件上传方式 curl -X POST http://127.0.0.1:5000/extract \ -F image/path/to/your/photo.jpg # URL方式 curl -X POST http://127.0.0.1:5000/extract \ -H Content-Type: application/json \ -d {image_url: https://example.com/photo.jpg}3.3 使用Python客户端测试如果你更喜欢用Python这里有一个简单的客户端示例import requests import json class FaceRecognitionClient: def __init__(self, base_urlhttp://127.0.0.1:5000): self.base_url base_url def health_check(self): 检查服务状态 response requests.get(f{self.base_url}/health) return response.json() def compare_faces_by_files(self, image1_path, image2_path, threshold0.4): 通过文件上传进行人脸比对 with open(image1_path, rb) as f1, open(image2_path, rb) as f2: files { image1: f1, image2: f2 } data {threshold: str(threshold)} response requests.post(f{self.base_url}/compare, filesfiles, datadata) return response.json() def compare_faces_by_urls(self, image1_url, image2_url, threshold0.4): 通过URL进行人脸比对 data { image1_url: image1_url, image2_url: image2_url, threshold: threshold } headers {Content-Type: application/json} response requests.post(f{self.base_url}/compare, jsondata, headersheaders) return response.json() def extract_feature(self, image_pathNone, image_urlNone): 提取人脸特征 if image_path: # 文件上传方式 with open(image_path, rb) as f: files {image: f} response requests.post(f{self.base_url}/extract, filesfiles) elif image_url: # URL方式 data {image_url: image_url} headers {Content-Type: application/json} response requests.post(f{self.base_url}/extract, jsondata, headersheaders) else: raise ValueError(必须提供image_path或image_url) return response.json() # 使用示例 if __name__ __main__: client FaceRecognitionClient() # 检查服务状态 print(服务状态:, client.health_check()) # 比对两张本地图片 result client.compare_faces_by_files( /path/to/photo1.jpg, /path/to/photo2.jpg, threshold0.5 ) print(比对结果:, json.dumps(result, indent2, ensure_asciiFalse)) # 提取单张图片的特征 feature_result client.extract_feature(image_path/path/to/photo.jpg) if feature_result[success]: print(f特征维度: {feature_result[dimension]}) print(f前5个特征值: {feature_result[feature][:5]})4. 生产环境部署建议4.1 性能优化配置我们目前使用的是Flask的开发服务器适合测试但不适合生产环境。对于生产部署建议进行以下优化使用Gunicorn作为WSGI服务器首先安装Gunicornpip install gunicorn然后使用Gunicorn启动服务gunicorn -w 4 -b 0.0.0.0:5000 app:app参数说明-w 4启动4个工作进程-b 0.0.0.0:5000绑定所有网络接口的5000端口app:appFlask应用实例添加Nginx反向代理创建Nginx配置文件/etc/nginx/sites-available/face-recognitionserver { listen 80; server_name your-domain.com; # 你的域名或IP location / { proxy_pass http://127.0.0.1:5000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } # 限制请求体大小防止上传过大图片 client_max_body_size 10M; # 超时设置 proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; }启用配置并重启Nginxsudo ln -s /etc/nginx/sites-available/face-recognition /etc/nginx/sites-enabled/ sudo nginx -t sudo systemctl restart nginx4.2 添加身份验证生产环境中你可能需要为API添加身份验证。这里提供一个基于API Key的简单方案from functools import wraps from flask import request, jsonify # 简单的API Key验证生产环境应该使用更安全的方案 VALID_API_KEYS { your-secret-key-1: client-1, your-secret-key-2: client-2 } def require_api_key(f): wraps(f) def decorated_function(*args, **kwargs): api_key request.headers.get(X-API-Key) if not api_key or api_key not in VALID_API_KEYS: return jsonify({ success: False, error: 无效或缺失API Key }), 401 return f(*args, **kwargs) return decorated_function # 在需要保护的端点添加装饰器 app.route(/compare, methods[POST]) require_api_key def compare_faces(): # ... 原有代码 ...客户端调用时需要添加API Key头curl -X POST http://your-domain.com/compare \ -H X-API-Key: your-secret-key-1 \ -F image1photo1.jpg \ -F image2photo2.jpg4.3 添加日志和监控为了更好地监控服务运行状态添加日志记录import logging from datetime import datetime # 配置日志 logging.basicConfig( levellogging.INFO, format%(asctime)s - %(name)s - %(levelname)s - %(message)s, handlers[ logging.FileHandler(fface_api_{datetime.now().strftime(%Y%m%d)}.log), logging.StreamHandler() ] ) logger logging.getLogger(__name__) # 在关键位置添加日志 app.route(/compare, methods[POST]) require_api_key def compare_faces(): client_id VALID_API_KEYS.get(request.headers.get(X-API-Key)) logger.info(f收到人脸比对请求客户端: {client_id}) try: # ... 处理逻辑 ... logger.info(f人脸比对完成相似度: {result[similarity]}) return jsonify(result) except Exception as e: logger.error(f人脸比对失败: {str(e)}) return jsonify({ success: False, error: 内部服务器错误 }), 5004.4 错误处理优化为API添加更完善的错误处理from werkzeug.exceptions import HTTPException app.errorhandler(HTTPException) def handle_http_exception(e): 处理HTTP异常 return jsonify({ success: False, error: e.description }), e.code app.errorhandler(Exception) def handle_general_exception(e): 处理其他异常 logger.error(f未处理的异常: {str(e)}) return jsonify({ success: False, error: 内部服务器错误 }), 500 app.errorhandler(413) def handle_file_too_large(e): 处理文件过大异常 return jsonify({ success: False, error: 上传文件过大请确保每张图片小于10MB }), 4135. 实际应用场景示例5.1 考勤系统集成假设你要开发一个员工考勤系统可以通过人脸识别验证员工身份import requests import base64 from datetime import datetime class AttendanceSystem: def __init__(self, api_url, api_key): self.api_url api_url self.api_key api_key self.employee_faces {} # 员工ID - 特征向量 def register_employee(self, employee_id, face_image_path): 注册员工人脸 # 提取特征 with open(face_image_path, rb) as f: files {image: f} headers {X-API-Key: self.api_key} response requests.post( f{self.api_url}/extract, filesfiles, headersheaders ) if response.json()[success]: self.employee_faces[employee_id] response.json()[feature] return True return False def check_in(self, employee_id, checkin_image_path): 员工打卡 if employee_id not in self.employee_faces: return {success: False, error: 员工未注册} # 临时保存打卡图片并提取特征 with open(checkin_image_path, rb) as f: files {image: f} headers {X-API-Key: self.api_key} response requests.post( f{self.api_url}/extract, filesfiles, headersheaders ) if not response.json()[success]: return {success: False, error: 未检测到人脸} checkin_feature response.json()[feature] registered_feature self.employee_faces[employee_id] # 计算相似度 import numpy as np similarity np.dot( np.array(checkin_feature), np.array(registered_feature) ) # 判定阈值设为0.6提高安全性 is_match similarity 0.6 # 记录考勤 if is_match: attendance_record { employee_id: employee_id, checkin_time: datetime.now().isoformat(), similarity: float(similarity), status: success } # 这里可以保存到数据库 return { success: True, match: True, similarity: float(similarity), message: 打卡成功 } else: return { success: True, match: False, similarity: float(similarity), message: 人脸不匹配打卡失败 } # 使用示例 attendance AttendanceSystem( api_urlhttp://your-api-domain.com, api_keyyour-secret-key ) # 注册员工 attendance.register_employee(EMP001, /path/to/employee_photo.jpg) # 员工打卡 result attendance.check_in(EMP001, /path/to/checkin_photo.jpg) print(f打卡结果: {result})5.2 相册人脸聚类如果你有一个包含很多人脸的照片集可以用这个服务自动分类import os from collections import defaultdict class PhotoAlbumOrganizer: def __init__(self, api_url, api_key): self.api_url api_url self.api_key api_key self.face_features {} # 图片路径 - 特征向量 self.clusters defaultdict(list) # 聚类结果 def process_photos(self, photo_dir, threshold0.5): 处理照片目录进行人脸聚类 # 第一步提取所有人脸特征 for filename in os.listdir(photo_dir): if filename.lower().endswith((.jpg, .jpeg, .png)): photo_path os.path.join(photo_dir, filename) # 提取特征 with open(photo_path, rb) as f: files {image: f} headers {X-API-Key: self.api_key} response requests.post( f{self.api_url}/extract, filesfiles, headersheaders ) if response.json()[success]: self.face_features[photo_path] response.json()[feature] print(f已处理: {filename}) else: print(f跳过未检测到人脸: {filename}) # 第二步聚类相似的人脸 processed set() cluster_id 0 for path1, feature1 in self.face_features.items(): if path1 in processed: continue # 创建新的聚类 self.clusters[cluster_id].append(path1) processed.add(path1) # 寻找相似的人脸 for path2, feature2 in self.face_features.items(): if path2 in processed: continue # 计算相似度 import numpy as np similarity np.dot(np.array(feature1), np.array(feature2)) if similarity threshold: self.clusters[cluster_id].append(path2) processed.add(path2) cluster_id 1 return self.clusters def export_clusters(self, output_dir): 将聚类结果导出到不同文件夹 for cluster_id, photo_paths in self.clusters.items(): cluster_dir os.path.join(output_dir, fperson_{cluster_id}) os.makedirs(cluster_dir, exist_okTrue) for photo_path in photo_paths: import shutil shutil.copy2(photo_path, cluster_dir) print(f已导出 {len(self.clusters)} 个人物聚类到 {output_dir}) # 使用示例 organizer PhotoAlbumOrganizer( api_urlhttp://your-api-domain.com, api_keyyour-secret-key ) # 处理照片并聚类 clusters organizer.process_photos(/path/to/photos, threshold0.4) print(f找到 {len(clusters)} 个不同的人物) # 导出结果 organizer.export_clusters(/path/to/organized_photos)6. 总结通过这篇文章我们完成了一个完整的人脸识别Web服务的构建过程。从环境准备到API开发从基础功能到生产部署你现在应该能够快速启动服务在CSDN星图平台一键部署RetinafaceCurricularFace镜像并在几分钟内启动一个完整的人脸识别Web服务理解API设计掌握了如何设计RESTful API接口包括健康检查、人脸比对、特征提取等核心功能处理多种输入学会了同时支持文件上传和URL两种图片输入方式优化生产环境了解了如何使用Gunicorn和Nginx部署生产级服务以及如何添加身份验证和日志监控集成实际应用看到了如何将这个人脸识别服务集成到考勤系统、相册管理等实际场景中这个方案的最大优势是简单高效。你不用关心底层模型的训练、优化和部署只需要关注业务逻辑的实现。无论是个人项目、创业原型还是企业内部工具都能快速上手。几个关键要点再强调一下阈值选择很重要安全场景用高阈值0.6-0.7普通场景用中等阈值0.4-0.5宽松筛选用低阈值0.3-0.4图片质量影响结果尽量使用清晰、正面、光线均匀的照片服务需要监控生产环境一定要添加日志、监控和告警API安全不能忽视至少要实现基本的API Key验证现在你的人脸识别Web服务已经就绪。你可以基于这个服务开发更多有趣的应用比如智能门禁、照片整理、社交应用等等。发挥你的创意吧获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。