Face_Recognition/face_matcher.py

174 lines
6.8 KiB
Python
Raw Normal View History

2026-04-01 08:12:53 +02:00
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()