import sys import signal signal.signal(signal.SIGINT, signal.SIG_IGN) import face_recognition import os import argparse import pickle import csv import numpy as np from pathlib import Path from datetime import datetime date_time = datetime.now().strftime("%Y%m%d_%H%M%S") default_log_filename = f"matcher_log_{date_time}.txt" default_out_filename = f"result_{date_time}.csv" def resolve_path(path, default): default_dirname = "output" default_filename = default if not path: resolved_path = Path(default_dirname) / default_filename return resolved_path.resolve() resolved_path = Path(path).resolve() if resolved_path.is_dir() or path.endswith(os.sep) or path.endswith('/') or not resolved_path.suffix: resolved_path = resolved_path / default_filename return resolved_path.resolve() return resolved_path def load_encodings(encodings, log): encodings_path = Path(encodings).resolve() log_path = resolve_path(log, default_log_filename) log_path.parent.mkdir(parents=True, exist_ok=True) try: with open(encodings_path, "rb") as f: data = pickle.load(f) print(f"Dati caricati correttamente da '{encodings_path}'") with open(log_path, "w", encoding="utf-8") as log_f: log_f.write(f"--- [INFO] Encodings caricati correttamente da '{encodings_path}' ---\n") return data["encodings"], data["filenames"] except FileNotFoundError: print(f"Errore: Il file '{encodings_path}' non esiste.") with open(log_path, "w", encoding="utf-8") as log_f: log_f.write(f"--- [ERRORE] Il file '{encodings_path}' non esiste ---\n") sys.exit(1) def encode_image(image, log): image_path = Path(image).resolve() log_path = resolve_path(log, default_log_filename) log_path.parent.mkdir(parents=True, exist_ok=True) if not image_path.is_file(): print(f"Errore: Il file {image_path} non esiste.") with open(log_path, "a", encoding="utf-8") as log_f: log_f.write(f"--- [ERRORE] Il file {image_path} non esiste ---\n") return None print(f"Elaborazione di: {image_path}...") with open(log_path, "a", encoding="utf-8") as log_f: log_f.write(f"--- [INFO] Match avviato per {image_path} ---\n") image = face_recognition.load_image_file(image_path) image_encoding = face_recognition.face_encodings(image) if image_encoding: return image_encoding[0] else: print(f"Nessun volto trovato in {image_path}") with open(log_path, "a", encoding="utf-8") as log_f: log_f.write(f"--- [INFO] Nessun volto trovato in {image_path} ---\n") sys.exit(1) def match_faces(image_encoding, encodings, filenames, log): log_path = resolve_path(log, default_log_filename) log_path.parent.mkdir(parents=True, exist_ok=True) tolerance = 0.5 results = [] if not encodings: return results face_distances = face_recognition.face_distance(encodings, image_encoding) with open(log_path, "a", encoding="utf-8") as log_f: log_f.write(f"\n============== [INIZIO ELABORAZIONE] ==============\n") current_file = None face_index = 0 total_matches = 0 try: for i, distance in enumerate(face_distances): filename = filenames[i] if filename != current_file: current_file = filename face_index = 1 print() log_f.write("\n") else: face_index += 1 is_match = distance < tolerance status = '✓' if is_match else '' somiglianza = max(0, 100 - (distance * 100)) msg = f"Volto {face_index:<2} in {filename} - [Somiglianza: {somiglianza:>5.1f}%] {status}" print(msg) log_f.write(f"{msg}\n") if is_match: total_matches += 1 if filename not in results: results.append(filename) print("\nMatch completato con successo.") log_f.write(f"\n============== [ELABORAZIONE COMPLETATA CON SUCCESSO] ==============\n\n") except KeyboardInterrupt: print("\nInterruzione manuale dell'elaborazione, salvataggio dei dati finora elaborati...") log_f.write("\n============== [ELABORAZIONE INTERROTTA MANUALMENTE] ==============\n") finally: print(f"Trovati {total_matches} match su {len(encodings)} confronti") print(f"Volto trovato in {len(results)} foto") log_f.write(f"--- [INFO] Trovati {total_matches} match su {len(encodings)} confronti ---\n") log_f.write(f"--- [INFO] Volto trovato in {len(results)} foto ---\n") return results def save_output(results, output, log): output_path = resolve_path(output, default_out_filename) log_path = resolve_path(log, default_log_filename) log_path.parent.mkdir(parents=True, exist_ok=True) try: output_path.parent.mkdir(parents=True, exist_ok=True) with open(output_path, "w", newline="", encoding="utf-8") as csvfile: writer = csv.writer(csvfile) formatted_data = [[f] for f in results] writer.writerows(formatted_data) print(f"Risultati salvati in {output_path.resolve()}") with open(log_path, "a", encoding="utf-8") as log_f: log_f.write(f"--- [INFO] Risultati salvati in {output_path.resolve()} ---\n") except Exception as e: print(f"Errore durante il salvataggio: {e}") with open(log_path, "a", encoding="utf-8") as log_f: log_f.write(f"--- [ERRORE] Errore durante il salvataggio: {e} ---\n") def main(): signal.signal(signal.SIGINT, signal.default_int_handler) parser = argparse.ArgumentParser(description="Associa ad un volto noto un set di immagini in cui compare (tramite encodings precalcolati)") parser.add_argument("-i", "--image", required=True, help="Percorso dell'immagine del volto da riconoscere") parser.add_argument("-e", "--encodings", required=True, help="Percorso del file contenente gli encodings delle foto 'unknown'.") parser.add_argument("-o", "--out", help="Percorso del file/cartella di output. Default: './output/result_[datetime].csv'") parser.add_argument("-l", "--log", help="Percorso del file di log. Default: './output/matcher_log_[datetime].txt'") args = parser.parse_args() encodings, filenames = load_encodings(args.encodings, args.log) image_encoding = encode_image(args.image, args.log) if image_encoding is not None and encodings and filenames: matches = match_faces(image_encoding, encodings, filenames, args.log) save_output(matches, args.out, args.log) if __name__ == "__main__": main()