174 lines
No EOL
6.8 KiB
Python
174 lines
No EOL
6.8 KiB
Python
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() |