import piexif from PIL import Image import face_recognition from datetime import datetime from models import db, User, Photo from werkzeug.utils import secure_filename import os import numpy as np def process_face_encoding(photo_path): """提取人脸特征编码并裁剪出最大的面部区域""" # 加载图片 image = face_recognition.load_image_file(photo_path) # 获取所有人脸的位置 face_locations = face_recognition.face_locations(image) if not face_locations: return None # 没有检测到人脸 # 找到最大的人脸 largest_face = max(face_locations, key=lambda x: (x[2] - x[0]) * (x[3] - x[1])) # 按照面积选取最大的人脸 # 获取该人脸的特征编码 face_encodings = face_recognition.face_encodings(image, [largest_face]) if face_encodings: # 裁剪出最大的人脸部分 top, right, bottom, left = largest_face image_pil = Image.open(photo_path) cropped_face = image_pil.crop((left, top, right, bottom)) # 保存裁剪后的图像(可选) cropped_face_path = photo_path.replace(".jpg", "_cropped.jpg") cropped_face.save(cropped_face_path) return face_encodings[0], cropped_face_path # 返回面部编码和裁剪后的人脸图片路径 return None # 如果没有提取到人脸特征编码 def add_user(username, userphoto): """添加用户并存储人脸特征""" if userphoto: filename = secure_filename(userphoto.filename) user_dir = os.path.join("static/uploads/users",username) user_dir =user_dir.replace("\\","/") os.makedirs(user_dir, exist_ok=True) photo_path = os.path.join(user_dir,"photo.jpg") photo_path = photo_path.replace("\\","/") # 保存头像 userphoto.save(photo_path) # 处理人脸特征 face_encoding = process_face_encoding(photo_path) if face_encoding is None: print("未检测到人脸,无法添加用户") return False # 存储到数据库 new_user = User(username=username, encoding=face_encoding, photo=photo_path) db.session.add(new_user) db.session.commit() return True return False # 删除用户 def delete_user(user_id): try: user = User.query.get(user_id) if user: db.session.delete(user) db.session.commit() return True return False except Exception as e: print(e) return False # 编辑用户 def edit_user(user_id, username, userphoto): try: user = User.query.get(user_id) if user: user.username = username if userphoto: photo_path = os.path.join('static', 'uploads', 'users', str(user.id)) os.makedirs(photo_path, exist_ok=True) photo_file = os.path.join(photo_path, userphoto.filename) userphoto.save(photo_file) # 更新照片路径 photo = Photo.query.filter_by(user_id=user.id).first() photo.photo_path = photo_file db.session.commit() return True return False except Exception as e: print(e) return False # 获取用户 def get_user(user_id): return User.query.get(user_id) def save_temp_photo(photo): """保存临时照片""" temp_dir = 'static/uploads/temp' os.makedirs(temp_dir, exist_ok=True) photo_path = os.path.join(temp_dir, photo.filename).replace("\\", "/") photo.save(photo_path) return photo_path def get_largest_face(image_path): """检测并裁剪最大的人脸""" image = face_recognition.load_image_file(image_path) face_locations = face_recognition.face_locations(image) if not face_locations: return None # 没有检测到人脸 # 找到最大的人脸 largest_face = max(face_locations, key=lambda loc: (loc[2] - loc[0]) * (loc[3] - loc[1])) top, right, bottom, left = largest_face # 裁剪最大的人脸 with Image.open(image_path) as img: face_img = img.crop((left, top, right, bottom)) face_crop_path = image_path.replace(".jpg", "_face.jpg") # 生成裁剪后的人脸图片路径 face_img.save(face_crop_path) return face_crop_path def match_face(face_path): """匹配人脸并返回匹配的用户""" face_image = face_recognition.load_image_file(face_path) face_encodings = face_recognition.face_encodings(face_image) if not face_encodings: return None # 未检测到人脸 face_encoding = face_encodings[0] # 取第一张脸 users = User.query.all() for user in users: if user.encoding: user_encoding = np.array(user.encoding) # 从数据库中获取存储的编码 match = face_recognition.compare_faces([user_encoding], face_encoding, tolerance=0.35) if match[0]: return user # 匹配成功 return None # 未找到匹配 def get_exif_time(photo_path): """提取 EXIF 时间""" try: with Image.open(photo_path) as img: exif_data = img._getexif() if exif_data: timestamp = exif_data.get(36867, None) # 36867 为 EXIF 拍摄时间 return timestamp if timestamp else datetime.now().strftime("%Y-%m-%d %H:%M:%S") except Exception as e: print(f"无法提取 EXIF 时间: {e}") return datetime.now().strftime("%Y-%m-%d %H:%M:%S") def save_classified_photo(photo, user, timestamp): """保存分类后的照片""" user_dir = os.path.join('static/uploads/users', str(user.id), timestamp) os.makedirs(user_dir, exist_ok=True) new_photo_path = os.path.join(user_dir, photo.filename) try: with Image.open(photo) as img: img.save(new_photo_path, quality=95) except Exception as e: print(f"保存图片时出错: {e}") # 保存到数据库 photo_record = Photo(user_id=user.id, created_at=timestamp, classification=True, classification_image_path=new_photo_path) db.session.add(photo_record) db.session.commit() print(f"照片 {photo.filename} 已保存至 {new_photo_path}") def classify_photos(photos): """处理上传的照片""" for photo in photos: temp_photo_path = save_temp_photo(photo) cropped_face_path = get_largest_face(temp_photo_path) if not cropped_face_path: print(f"没有检测到人脸:{photo.filename}") continue matched_user = match_face(cropped_face_path) timestamp = get_exif_time(temp_photo_path) if matched_user: save_classified_photo(photo, matched_user, timestamp) else: print(f"没有找到匹配的人脸:{photo.filename}") # 根据姓名和时间查找照片 def search_photos(name, date): user = User.query.filter_by(username=name).first() if not user: return [] photos = Photo.query.filter_by(user_id=user.id).all() filtered_photos = [] for photo in photos: # 过滤日期 exif_data = piexif.load(photo.photo_path) date_taken = exif_data.get(piexif.ExifIFD.DateTimeOriginal, b"").decode("utf-8") if date_taken and date in date_taken: filtered_photos.append(photo) return filtered_photos def find_match(image_path, known_face_encodings): # 加载图片 unknown_image = face_recognition.load_image_file(image_path) # 获取图片中的人脸特征编码 unknown_face_encoding = face_recognition.face_encodings(unknown_image) if unknown_face_encoding: unknown_face_encoding = unknown_face_encoding[0] # 设置一个匹配的阈值,例如0.6,值越低匹配越宽松 matches = face_recognition.compare_faces(known_face_encodings, unknown_face_encoding, tolerance=0.6) if True in matches: match_index = matches.index(True) return match_index return None