# - * - coding:utf - 8 - * - import os from flask import Flask, request, jsonify, render_template, url_for, send_from_directory import numpy as np import face_recognition from PIL import Image # 初始化 Flask 应用 app = Flask(__name__) # 定义路径常量 npy_file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'face_data/face_data.npy')) image_folder_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'face_data')) # 加载 face_data 数据 def load_face_data(): print(f"加载人脸数据: {npy_file_path}") # 打印加载数据的路径 if os.path.exists(npy_file_path): try: face_data = np.load(npy_file_path, allow_pickle=True).item() return face_data except Exception as e: print(f"加载人脸编码时出错: {e}") return {} print("人脸数据文件不存在。") return {} # 保存 face_data 数据 def save_face_data(face_data): print(f"保存人脸数据到: {npy_file_path}") # 打印保存数据的路径 np.save(npy_file_path, face_data) print("人脸数据保存成功。") # 获取图片的 URL def get_image_url(name): # 使用 Flask 的自定义路由返回图片 return url_for('serve_face_image', filename=f'{name}.jpg') # 获取图片的绝对路径 def get_image_path(name): image_path = os.path.join(image_folder_path, f'{name}.jpg') return os.path.abspath(image_path) # 返回绝对路径 # 比较人脸编码之间的相似度 def is_same_person(new_encoding, existing_encoding, threshold=0.5): """ 判断两个人脸编码是否属于同一个人 threshold: 距离小于该阈值认为是同一个人 """ distance = np.linalg.norm(np.array(new_encoding) - np.array(existing_encoding)) return distance < threshold # 检查上传的图片是否有效 def check_image_validity(file_path): try: img = Image.open(file_path) img.verify() # 验证图片是否有效 return True except (IOError, SyntaxError): return False # 保存图片为标准的 .jpg 格式 def save_image_as_jpeg(file, file_path): img = Image.open(file) img.convert("RGB").save(file_path, "JPEG") # 自定义路由来返回图片 @app.route('/face_image/') def serve_face_image(filename): try: return send_from_directory(image_folder_path, filename) except Exception as e: return jsonify({"error": "Image not found"}), 404 # 检查并创建 image_folder_path if not os.path.exists(image_folder_path): print(f"创建人脸图片文件夹: {image_folder_path}") # 打印创建的文件夹路径 os.makedirs(image_folder_path) # 检查上传的图片是否有效 def check_image_validity(file_path): try: img = Image.open(file_path) img.verify() # 验证图片是否有效 print(f"图片 {file_path} 是有效的图像文件。") except (IOError, SyntaxError) as e: print(f"无效的图片文件: {file_path}, 错误信息: {e}") return False return True @app.route('/') def index(): print("访问首页,加载已注册人脸...") face_data = load_face_data() faces_and_images = [ {"name": name, "image": get_image_url(name)} # 使用统一的函数生成路径 for name in face_data.keys() if os.path.exists(os.path.join(image_folder_path, f"{name}.jpg")) ] print(f"加载的已注册人脸: {faces_and_images}") # 打印加载的所有人脸 return render_template('index.html', faces_and_images=faces_and_images, error_message=None) @app.route('/add_face', methods=['POST']) def add_face(): print("接收到添加人脸请求...") face_data = load_face_data() if 'file' not in request.files or 'name' not in request.form: return jsonify({"error": "请求中缺少 'file' 或 'name' 参数。"}), 400 file = request.files['file'] name = request.form['name'] print(f"上传的人脸名称: {name}, 文件: {file.filename}") try: image = face_recognition.load_image_file(file) face_encodings = face_recognition.face_encodings(image) if not face_encodings: return jsonify({"error": "上传的图片中没有检测到人脸。"}), 400 new_face_encoding = face_encodings[0] existing_faces = [face_data[stored_name] for stored_name in face_data.keys()] # 检查是否有已存在的相似编码 for existing_encoding in existing_faces: if is_same_person(new_face_encoding, existing_encoding): return jsonify({"error": "已有相似的人脸编码,无法添加。"}), 400 # 检查是否已有相同名字的图片 file_path = os.path.join(image_folder_path, f"{name}.jpg") if os.path.exists(file_path): return jsonify({"error": f"'{name}' 的图片已经存在。"}), 400 # 保存图片为 JPEG 格式 save_image_as_jpeg(file, file_path) # 检查图片是否有效 if not check_image_validity(file_path): return jsonify({"error": "上传的图片无效或损坏。"}), 400 # 将人脸编码保存 face_data[name] = new_face_encoding.tolist() save_face_data(face_data) return jsonify({"success": "人脸添加成功。"}), 200 except Exception as e: return jsonify({"error": f"发生错误: {str(e)}"}), 500 @app.route('/delete_face', methods=['POST']) def delete_face(): print("接收到删除人脸请求...") face_data = load_face_data() name = request.form.get('name') if not name: print("缺少 'name' 参数。") return jsonify({"error": "缺少 'name' 参数。"}), 400 if name not in face_data: print(f"未找到名为 '{name}' 的人脸数据。") return jsonify({"error": f"未找到名为 '{name}' 的人脸数据。"}), 404 try: # 删除面部数据 del face_data[name] save_face_data(face_data) # 删除图片 image_path = os.path.join(image_folder_path, f"{name}.jpg") if os.path.exists(image_path): os.remove(image_path) print(f"成功删除图片: {image_path}") else: print(f"未找到图片文件: {image_path}") print(f"成功删除人脸: {name}") return jsonify({"success": f"人脸 '{name}' 删除成功。"}), 200 except Exception as e: print(f"发生错误: {str(e)}") return jsonify({"error": f"发生错误: {str(e)}"}), 500 @app.route('/list_faces', methods=['GET']) def list_faces(): print("获取已注册人脸列表...") face_data = load_face_data() faces_with_images = [ {"name": name, "image": get_image_url(name)} # 使用统一的函数生成路径 for name in face_data.keys() if os.path.exists(os.path.join(image_folder_path, f'{name}.jpg')) ] print(f"已注册人脸列表: {faces_with_images}") # 打印已注册人脸 return jsonify({"faces": faces_with_images}), 200 if __name__ == '__main__': print("启动 Flask 应用...") app.run(host='0.0.0.0', port=5000, debug=False)