First Commit
This commit is contained in:
commit
e297e484ee
4 changed files with 904 additions and 0 deletions
174
face_matcher.py
Normal file
174
face_matcher.py
Normal file
|
|
@ -0,0 +1,174 @@
|
|||
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()
|
||||
Loading…
Add table
Add a link
Reference in a new issue