import os import time import json import logging import numpy as np from PIL import Image import face_recognition from deepface import DeepFace from flask import Blueprint, request, jsonify from services.face_service import compare_faces_service from services.storage_service import minio_client, BUCKET logger = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) face_bp = Blueprint('face_bp', __name__) threshold = float(os.getenv("THRESHOLD", 0.6)) # @face_bp.route('/compare_faces', methods=['POST']) # def compare_faces(): # logging.info("🖼️ Recebendo imagens para comparação...") # if 'image1' not in request.files or 'image2' not in request.files: # return jsonify({"error": "Missing images. Keys should be 'image1' and 'image2'."}), 400 # image1 = request.files['image1'] # image2 = request.files['image2'] # try: # result = compare_faces_service(image1, image2) # return jsonify(result), 200 # except ValueError as e: # return jsonify({"error": str(e)}), 400 # except Exception as e: # return jsonify({"error": "Internal Server Error", "details": str(e)}), 500 @face_bp.route('/register_face', methods=['POST']) def register_face(): from datetime import datetime from io import BytesIO person_id = request.form.get("person_id") image_file = request.files.get("image") if not person_id or not image_file: return jsonify({"error": "Missing person_id or image"}), 400 try: # Carrega imagem como array RGB image_file.seek(0) image = face_recognition.load_image_file(image_file) # Detecta a primeira face face_locations = face_recognition.face_locations(image) if not face_locations: return jsonify({"error": "No face detected in image"}), 400 top, right, bottom, left = face_locations[0] face_crop = image[top:bottom, left:right] # Converte para PIL face_pil = Image.fromarray(face_crop) # Salva em buffer buffer = BytesIO() face_pil.save(buffer, format="JPEG") buffer.seek(0) # Gera nome baseado em timestamp timestamp = datetime.utcnow().strftime("%Y%m%d-%H%M%S") image_filename = f"registred_faces/{person_id}/{timestamp}.jpg" # Upload para o MinIO minio_client.put_object( BUCKET, image_filename, buffer, length=buffer.getbuffer().nbytes, content_type="image/jpeg" ) return jsonify({ "person_id": person_id, "image_path": image_filename, "timestamp": timestamp, "status": "Face cropped and saved" }), 200 except Exception as e: logger.exception("❌ Erro ao registrar face") return jsonify({"error": "Failed to register face", "details": str(e)}), 500 @face_bp.route("/checkin", methods=["POST"]) def checkin(): from datetime import datetime from io import BytesIO logger.info("📥 Início do registro de ponto (/checkin)") person_id = request.form.get("person_id") image_file = request.files.get("image") ip_address = request.remote_addr if not person_id or not image_file: return jsonify({"error": "Missing person_id or image"}), 400 try: img_probe = np.array(Image.open(image_file).convert("RGB")) objects = list(minio_client.list_objects(BUCKET, prefix=f"registred_faces/{person_id}/", recursive=True)) image_objects = [obj for obj in objects if obj.object_name.endswith(".jpg")] if not image_objects: return jsonify({"error": f"No registered face found for '{person_id}'"}), 404 image_objects.sort(key=lambda x: x.object_name, reverse=True) target_image_obj = image_objects[0] logger.debug(f"🎯 Usando imagem registrada: {target_image_obj.object_name}") response = minio_client.get_object(BUCKET, target_image_obj.object_name) img_registered = np.array(Image.open(response).convert("RGB")) t0 = time.time() result = DeepFace.verify( img_probe, img_registered, model_name="Dlib", enforce_detection=False ) duration = round(time.time() - t0, 4) distance = result["distance"] similarity = 1 - distance threshold = float(os.getenv("THRESHOLD", 0.85)) confidence_high = float(os.getenv("CONFIDENCE_HIGH", 0.95)) confidence_medium = float(os.getenv("CONFIDENCE_MEDIUM", 0.85)) match = similarity >= threshold if similarity >= confidence_high: confidence = "high" elif similarity >= confidence_medium: confidence = "medium" else: confidence = "low" if not match: return jsonify({ "match": False, "similarity_score": round(similarity, 4), "confidence": confidence, "message": "Face not recognized with sufficient confidence." }), 401 # Recorta a face com face_recognition image_file.seek(0) image_rgb = face_recognition.load_image_file(image_file) locations = face_recognition.face_locations(image_rgb) if not locations: return jsonify({"error": "No face found to crop"}), 400 top, right, bottom, left = locations[0] face_crop = image_rgb[top:bottom, left:right] face_pil = Image.fromarray(face_crop) # Organiza por pessoa/data/hora now = datetime.utcnow() date_str = now.strftime("%Y-%m-%d") time_str = now.strftime("%H-%M-%S") path_prefix = f"checkins/{person_id}/{date_str}/{time_str}/" original_name = f"{path_prefix}original.jpg" face_name = f"{path_prefix}face.jpg" json_name = f"{path_prefix}metadata.json" # Upload original image_file.seek(0) minio_client.put_object( BUCKET, original_name, image_file, length=-1, part_size=10*1024*1024, content_type="image/jpeg" ) # Upload face face_buffer = BytesIO() face_pil.save(face_buffer, format="JPEG") face_buffer.seek(0) minio_client.put_object( BUCKET, face_name, face_buffer, length=face_buffer.getbuffer().nbytes, content_type="image/jpeg" ) # Upload JSON data = { "person_id": person_id, "timestamp": now.strftime("%Y-%m-%d %H:%M:%S"), "ip": ip_address, "confidence": confidence, "similarity_score": round(similarity, 4), "duration_sec": duration, "match": match } json_buffer = BytesIO(json.dumps(data).encode("utf-8")) minio_client.put_object( BUCKET, json_name, json_buffer, length=json_buffer.getbuffer().nbytes, content_type="application/json" ) return jsonify(data), 200 except Exception as e: logger.exception("❌ Erro ao processar check-in") return jsonify({"error": str(e)}), 500 ''' Abaixo é o endpoint que precisa que seja passado duas imagens para comparação. ''' # @face_bp.route("/verify_face_dlib", methods=["POST"]) # def verify_face_dlib(): # logger.info("🔍 Verificação facial usando deepface_dlib com imagem cadastrada") # person_id = request.form.get("person_id") # image_file = request.files.get("image") # if not person_id or not image_file: # return jsonify({"error": "Missing person_id or image"}), 400 # try: # img_probe = np.array(Image.open(image_file).convert("RGB")) # objects = list(minio_client.list_objects(BUCKET, prefix=f"{person_id}/", recursive=True)) # image_objects = [obj for obj in objects if obj.object_name.endswith(".jpg")] # if not image_objects: # return jsonify({"error": f"No registered face found for '{person_id}'"}), 404 # image_objects.sort(key=lambda x: x.object_name, reverse=True) # target_image_obj = image_objects[0] # logger.debug(f"🖼 Imagem cadastrada encontrada: {target_image_obj.object_name}") # response = minio_client.get_object(BUCKET, target_image_obj.object_name) # img_registered = np.array(Image.open(response).convert("RGB")) # t0 = time.time() # result = DeepFace.verify( # img_probe, # img_registered, # model_name="Dlib", # enforce_detection=False # ) # duration = round(time.time() - t0, 4) # distance = result["distance"] # similarity = 1 - distance # # Aplica o THRESHOLD sobre a similaridade # threshold = float(os.getenv("THRESHOLD", 0.93)) # ex: 0.85 = exige mais precisão # confidence_high = float(os.getenv("CONFIDENCE_HIGH", 0.95)) # confidence_medium = float(os.getenv("CONFIDENCE_MEDIUM", 0.85)) # match = similarity >= threshold # if similarity >= confidence_high: # confidence = "high" # elif similarity >= confidence_medium: # confidence = "medium" # else: # confidence = "low" # return jsonify({ # "person_id": person_id, # "match": match, # "similarity_score": round(similarity, 4), # "threshold": threshold, # "confidence": confidence, # "duration_sec": duration # }), 200 # except Exception as e: # logger.exception("Erro na verificação facial") # return jsonify({"error": str(e)}), 500