Indice dei contenuti
Intro
Nell’era digitale, il riconoscimento facciale si è imposto come uno dei pilastri fondamentali dell’innovazione tecnologica. Tuttavia, nonostante i suoi avanzamenti, ciò che una volta sembrava fantascienza continua a evolversi a un ritmo sorprendente. Il focus di questa trattazione è rivolto verso una frontiera che promette di trasformare radicalmente il modo in cui interagiamo con il mondo digitale e oltre.
Nel mio ultimo viaggio nell’ambito del riconoscimento facciale, mi sono imbattuto in un’altra pietra miliare che potrebbe ridefinire i nostri concetti consolidati: un algoritmo in grado di estrarre una ricchezza di informazioni da un volto umano, proprio come leggere pagine di un libro: cercheremo di svelare una serie di caratteristiche che vanno al di là dell’ovvio.
Attraverso l’analisi di un flusso continuo di immagini catturate da una semplice webcam, l’algoritmo che sto per presentarvi non solo identifica i volti umani con sorprendente precisione, ma va oltre, rilevando caratteristiche come l’appartenenza etnica e persino le emozioni scritte sul volto. Sì, avete capito bene. L’AI è ora in grado di leggere le nostre emozioni con un’accuratezza sorprendente, aprendo la porta a una vasta gamma di applicazioni che spaziano dall’analisi comportamentale alla personalizzazione dell’esperienza utente.
In questa esplorazione affascinante ci addentreremo nel cuore pulsante di questa tecnologia innovativa. Scopriremo insieme le sue implicazioni, le sfide etiche e le promesse che porta con sé. Preparatevi a lasciarvi stupire da ciò che l’intelligenza artificiale è in grado di raggiungere nel campo del riconoscimento facciale.
Obiettivo dello progetto
L’obiettivo del progetto è quello di realizzare un riconoscitore facciale, che acquisisca frame fotografici direttamente dalla webcam del pc, riconosca un set predefinito di caratteristiche del volto, le rappresenti a video durante il processo di acquisizione e contemporaneamente le salvi in una lista.
Al completamento del processo di acquisizione la lista sarà memorizzata in un file csv a scopo di backup.
Si creerà un dataframe a partire dal csv di backup dal quale si faranno due tipologie di analisi:
- Analisi delle emozioni del soggetto acquisito;
- Analisi della razza del soggetto acquisito.
Si precisa che il codice proposto per il presente progetto è solo a scopo didattico e non sono state implementate tutte le ottimizzazioni evidentemente necessarie qualora si fosse trattato di una applicazione industriale.
Il codice sorgente sviluppato in python
Nell’ambito del Progetto sono state utilizzate le seguenti librerie:
Il codice che implementa il progetto è stato tutto scritto in un unico file “faceparser.py”. Il file è organizzato nei blocchi di seguito descritti.
Acquisizione dei frame dalla webcam
Per interagire con la webcam del pc è stata utilizzata la libreria OpenCV. Viene dichiarata una variabile “camCapturer” che istanzia l’oggetto restituito dal metodo “cv2.VideoCapturer”:
# Crea l'oggetto per l'acquisizione video dalla webcam camCapturer = cv2.VideoCapture(0)
Tale variabile viene in seguito utilizzata per creare un oggetto denominato frame che memorizza di volta in volta il fotogramma da passare al parser:
# Catturo il frame della webcam ret, frame = camCapturer.read()
Si noti che l’acquisizione di frame è continua ed inserita in un ciclo “while true” fino a che non si preme sulla tastiera il tasto “q”.
Dopo le operazioni di acquisizione la webCam viene rilasciata e la finestra di OpenCV sul quale si visualizzano frame e caratteristiche facciali distrutte:
# Rilascia la webcam e chiudi la finestra camCapturer.release() cv2.destroyAllWindows()
Il riconoscimento delle caratteristiche facciali
Per il riconoscimento delle caratteristiche facciali viene utilizzata la libreria deepface.
I metodi esportati da deepface agiscono su singoli fotogrammi, a tale scopo si passa all’analizzatore del deepface il singolo frame acquisito, di cui al paragrafo precedente:
# Estrapola le caratteristiche facciali del frame acquisito try: features = DeepFace.analyze(frame, actions=['age', 'gender', 'race', 'emotion']) except ValueError: # Non è stato rilevato alcun volto nel frame continue
Viene fatto in un try…except poiché la cam potrebbe non acquisire correttamente, in tal caso l’algoritmo ritorna il controllo al resto del codice nel ciclo while true.
Nel caso in cui, invece, il frame analizzato restituisca valori validi l’oggetto restituito dal metodo analyze è una lista che salviamo nella variabile features, che ha la seguente struttura:
[{
‘age’: int,
‘region’: {‘x’: pixel_x, ‘y’: pixel_y, ‘w’: larghezza del perimetro, ‘h’: altezza del perimetro},
‘gender’: {‘Woman’: 0.00010729074801929528, ‘Man’: 99.9998927116394},
‘dominant_gender’: ‘Man’,
‘race’: {
‘asian’: probabilità in %,,
‘indian’: probabilità in %,,
‘black’: probabilità in %,,
‘white’: probabilità in %,,
‘middle eastern’: probabilità in %,,
‘latino hispanic’: probabilità in %,
},
‘dominant_race’: max prob race,
‘emotion’: {
‘angry’: probabilità in %,
‘disgust’: probabilità in %,,
‘fear’: probabilità in %,,
‘happy’: probabilità in %,,
‘sad’: probabilità in %,,
‘surprise’: probabilità in %,,
‘neutral’: probabilità in %,
},
‘dominant_emotion’: string
}
]
Nella lista sono quindi memorizzati dei dizionari, dai quali si prelevano alcuni valori per ogni iterazione del ciclo while true:
# Individuo alcune caratteristiche del volto rilevato perimetro_facciale = features[0]['region'] #Utile per tracciare il perimetro facciale durante la rilevazione dominant_emotion = features[0]['dominant_emotion'] #Emozione rilevante nel campione age = features[0]['age'] #Età rilevata nel frame dominant_race = features[0]['dominant_race'] #Razza predominante dominant_gender = features[0]['dominant_gender'] #Genere predominante
Si consideri mediante OpenCV visualizzo i frame acquisiti e traccio su layer in primo piano rispetto al frame alcune caratteristiche del riconoscimento:
#Stampo un rettangolo intorno al viso cv2.rectangle(frame, (perimetro_facciale['x'], perimetro_facciale['y']), (perimetro_facciale['x'] + perimetro_facciale['w'], perimetro_facciale['y'] + perimetro_facciale['h']), (255, 0, 0), 2) #Creo una stringa nella quale appendo le predominanti caratteristiche rilevate strPlotOnFrame = 'Emotion: ' + dominant_emotion + ' | Age: ' + str(age) + ' | Gender: ' + dominant_gender + ' | Race: ' + dominant_race #Stampo il rettangolo nero sotto il testo x1 = int(perimetro_facciale['x'] - perimetro_facciale['x'] / 3.5) x2 = int(perimetro_facciale['x'] + perimetro_facciale['w'] + perimetro_facciale['x'] / 3.5) cv2.rectangle(frame, (x1, perimetro_facciale['y']-50), (x2, perimetro_facciale['y']-10), (0, 0, 0), -1) #Stampo il testo della stringa strPlotOnFrame in livello sopra al rettangolo nero per renderla più leggibile cv2.putText(frame, strPlotOnFrame, (perimetro_facciale['x']-120, perimetro_facciale['y']-20), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2) # Mostra il frame con le caratteristiche facciali estratte e inserisce il rettangolo nero e testo sopra il perimetro tracciato cv2.imshow('Caratteristiche facciali', frame)
Memorizzazione delle informazioni su un csv di backup
Prima del ciclo istanzio una lista vuota avente la il primo nodo contenente una lista di stringhe che identificano le caratteristiche delle features restituite dal deepface.analyze():
Dichiaro un lista per contenere i campioni acquisiti dalla webcam row_list = [] row_list.append ( [ 'emotion_angry', 'emotion_disgust', 'emotion_fear', 'emotion_happy', 'emotion_sad', 'emotion_surprise', 'emotion_neutral', 'dominant_emotion', 'age', 'race_asian', 'race_indian', 'race_black', 'race_white', 'race_middle_eastern', 'race_latino_hispanic', 'dominant_race', 'gender_Man', 'gender_Woman', 'dominant_gender' ] )
Nella lista, ad ogni iterazione del while true, aggiungo un nuovo nodo dove memorizzo i dati acquisiti prelevati direttamente dall’oggetto feautures:
row_list.append( [ features[0]['emotion']['angry'], features[0]['emotion']['disgust'], features[0]['emotion']['fear'], features[0]['emotion']['happy'], features[0]['emotion']['sad'], features[0]['emotion']['surprise'], features[0]['emotion']['neutral'], dominant_emotion, age, features[0]['race']['asian'], features[0]['race']['indian'], features[0]['race']['black'], features[0]['race']['white'], features[0]['race']['middle eastern'], features[0]['race']['latino hispanic'], dominant_race, features[0]['gender']['Man'], features[0]['gender']['Woman'], dominant_gender ] )
Alla fine del while true, memorizzo in un file csv la lista dei valori memorizzati temporaneamente utilizzando la libreria csv:
#Salvo i campioni acquisiti all'interno di un csv with open('face_analysis.csv', 'w', newline='') as file: writer = csv.writer(file, quoting=csv.QUOTE_NONNUMERIC, delimiter=',') writer.writerows(row_list)
Se il file face_analysis.csv non esiste lo crea, altrimenti lo sovrascrive.
Analisi dei dati acquisiti
Il blocco di codice che analizza i campioni acquisiti crea un dataframe mediante l’utilizzo della libreria pandas:
#Inserisco il csv in un dataframe dataframe = pd.read_csv('face_analysis.csv')
Si procede all’analisi delle emozioni generare come probabilità e al calcolo delle medie delle colonne:
#Medie delle emozioni rilevate media_angry = dataframe['emotion_angry'].mean() media_disgust = dataframe['emotion_disgust'].mean() media_fear = dataframe['emotion_fear'].mean() media_happy = dataframe['emotion_happy'].mean() media_sad = dataframe['emotion_sad'].mean() media_surprise = dataframe['emotion_surprise'].mean() #Media dell'età e dello scarto media_age = dataframe['age'].mean() sqrt_age = dataframe['age']**(1/2)
che vengono rappresentate mediante la libreria mathplolib, nonostante il plotting venga eseguito solo in seguito alla successiva fase analisi:
#Creo la prima figura sulla quale sarà visualizzato il primo grafico Figura1, ax = plt.subplots() emozioni = ['Rabbia', 'Disgusto', 'Paura', 'Felicità', 'Tristezza', 'Sorpresa'] #Lista delle emozioni conteggi_medi = [media_angry, media_disgust, media_fear, media_happy, media_sad, media_surprise] bar_labels = ['angry', 'disgust', 'fear', 'happy', 'sad', 'surprise'] bar_colors = ['red', 'purple', 'black', 'orange', 'grey', 'green'] ax.bar(emozioni, conteggi_medi, label=bar_labels, color=bar_colors) ax.set_xlabel('Emozione') ax.set_ylabel('Frequenza media') ax.set_title('Lista delle emozioni acquisite dal volto') ax.legend(title = 'Legenda emozioni')
Si procede all’analisi delle caratteristiche razziali individuate dall’algoritmo deepface
razze = ['Asiatico','Indiano','Afroamericano','Caucasico','Medio orientale','Latino ispanico'] #Lista delle razze #Dizionario di dataframe dei campioni acquisiti acquisizioni = { 'Asiatico':dataframe['race_asian'], 'Indiano':dataframe['race_indian'], 'Afroamericano':dataframe['race_black'], 'Caucasico':dataframe['race_white'], 'Medio orientale':dataframe['race_middle_eastern'], 'Latino ispanico':dataframe['race_latino_hispanic'] } Figura2, ax = plt.subplots(layout='constrained') #Talvolta le acquisizioni potrebbero diventare troppe e a livello visivo il grafico è illegibile #se superiori a 10 rappresento solo i primi 10 numero_campioni = len(dataframe.index) if(len(dataframe.index)>10): numero_campioni = 10 #Sull'asse delle X rappresenta i primi numero_campioni x = np.arange(numero_campioni) #Larghezza della barra width = 0.15 #Moltiplicatore dell posizione multiplier = 0 for attributo, acquisizione in acquisizioni.items(): offset = width * multiplier #Calcolo l'offset di ogni razza individuata rettangoli = ax.bar(x+ offset, acquisizione[0], width, label=attributo) #identifico i rettangoli ax.bar_label(rettangoli, padding=3) #inserisco i valori sopr ogni rettangolo multiplier += 1 #incremento il moltiplicatore # Aggiungo etichette ascisse e ordinate, titolo del grafico, legenda e posizione della legenda ax.set_ylabel('Probabilità') ax.set_xlabel('Campione acquisito')</pre> ax.set_title('Probilità della razza') ax.legend(loc='upper left', ncols=2) # Visualizza il grafico delle emozioni in Figura1 e il grafico delle razze in Figura2 plt.show()
Output
In console possiamo apprezzare il log delle singole rilevazioni:
Durante l'acquisizione viene rilevato a schermo il volto individuato e le caratteristiche rilevate in tempo reale:
Infine vengono plottati i grafici su due distinte figure e con tipologie di rappresentazione distinte:
Conclusioni
L’implementazione di un algoritmo per il riconoscimento delle emozioni e della razza mediante l’intelligenza artificiale potrebbe rivelarsi estremamente utile in una vasta gamma di contesti.
Ecco alcuni possibili utilizzi:
- Ricerca e monitoraggio clinico: In ambito medico, un tale algoritmo potrebbe aiutare i professionisti della salute a valutare lo stato emotivo e le esigenze dei pazienti, consentendo una diagnosi più accurata e un trattamento personalizzato. Ad esempio, potrebbe essere utilizzato per monitorare l’umore dei pazienti con disturbi mentali o per valutare il dolore nei pazienti non comunicativi.
- Assistenza virtuale: Nei settori dell’assistenza clienti e dell’assistenza all’utente, un algoritmo di riconoscimento delle emozioni potrebbe consentire a chatbot e assistenti virtuali di adattare le risposte in base allo stato emotivo dell’utente, migliorando così l’esperienza complessiva del cliente.
- Educazione e apprendimento: Nell’ambito dell’istruzione, un algoritmo di riconoscimento delle emozioni potrebbe aiutare gli insegnanti a valutare l’efficacia delle lezioni e a individuare gli studenti che potrebbero necessitare di supporto aggiuntivo. Inoltre, potrebbe essere utilizzato per personalizzare i materiali didattici in base alle esigenze emotive degli studenti.
- Analisi del sentiment: Nel campo del marketing e della ricerca di mercato, un algoritmo di riconoscimento delle emozioni potrebbe essere impiegato per analizzare il sentiment degli utenti sui social media o nelle recensioni online, consentendo alle aziende di adattare le loro strategie di marketing e migliorare la soddisfazione del cliente.
- Sicurezza e sorveglianza: In contesti di sicurezza pubblica, un algoritmo di riconoscimento delle emozioni potrebbe essere utilizzato per individuare comportamenti sospetti o pericolosi, contribuendo così a prevenire crimini o incidenti.
- Riduzione del bias e dell’ingiustizia: Utilizzando tecniche etiche e garantendo la diversità nei dati di addestramento, un algoritmo di riconoscimento delle razze potrebbe essere impiegato per identificare e ridurre il bias nelle decisioni automatizzate, come quelle riguardanti l’assunzione o la concessione di prestiti.
In sintesi, un algoritmo per il riconoscimento delle emozioni e della razza potrebbe avere un impatto significativo su una vasta gamma di settori, migliorando l’efficienza, la personalizzazione e la sensibilità delle applicazioni basate sull’intelligenza artificiale. Tuttavia, è fondamentale adottare misure per garantire l’etica, la privacy e l’equità nell’uso di tali tecnologie.