Come Usare La WEBCAM per il Riconoscimento Facciale con Python
Pubblicato da Michele Saba
In questo tutorial vedremo assieme come usare la WebCam del nostro computer per effettuare operazioni di riconoscimento facciale in tempo reale con Python utilizzando il modulo face_recognition.
Se non avete visto il tutorial precedente sul riconoscimento facciale con le immagini dovreste decisamente andare a vedere prima quello, in quanto diversamente quanto esposto qui potrebbe risultare molto avanzato.
Installiamo le librerie necessarie
Se non lo abbiamo già fatto, creiamo un nuovo ambiente virtuale per il nostro progetto e con PIP installiamo face recognition insieme alle librerie necessarie a farlo funzionare, seguendo l’ordine seguente:
pip install cmake
pip install dlib
pip install face_recognition
Installiamo anche OpenCV (Open Source Computer Vision Library) per Python che si occuperà di catturare i frame che arrivano dalla webcam in modo da poterli analizzare per verificare se al loro interno sia presente o meno un volto conosciuto:
pip install opencv-python
Importiamo il modulo che abbiamo appena installato e anche face_recognition:
import face_recognition
import cv2
Il codice che vedremo in questo tutorial è un mio personale adattamento di uno degli esempi presenti nel repo di face_recognition, a cui se volete potete andare a dare uno sguardo.
Chiediamo all’utente di inserire un immagine con un volto da riconoscere
Inizializziamo la WebCam che utilizzeremo per l'acquisizione video dichiarando una variabile webcam, assegnandola a VideoCapture di cv2 e passandogli un parametro numerico che rappresenta la videocamera che vogliamo utilizzare: in questo caso 0 rappresenta la videocamera di default del computer.
webcam = cv2.VideoCapture(0)
La procedura che descriveremo in questo tutorial ci permetterà di andare effettuare il riconoscimento facciale per un volto specifico, quindi facciamo in modo che il nostro programma chieda il percorso all’interno del computer dell’immagine contenente il volto della persona da identificare tramite input:
image_file = input("Target Image File > ")
Carichiamo l’immagine con la funzione load_image_file di face recognition e acquisiamo l’encoding come spiegato nella prima lezione sul riconoscimento facciale assegnandolo alla variabile target_encoding:
target_image = face_recognition.load_image_file(image_file)
target_encoding = face_recognition.face_encodings(target_image)[0]
Comunichiamo all’utente che l’encoding è stato generato con la funzione print:
print("Image Loaded. 128-dimension Face Encoding Generated. \n")
Chiediamo all’utente con un altro input quale nome utilizzare per il target:
target_name = input("Target Name > ")
Creiamo un loop che analizzi i fotogrammi ricevuti da WebCam
Oltre ad importare i moduli necessari ad inizializzare la WebCam, finora abbiamo solo richiesto all'utente di fornirci un'immagine contenente la persona che vogliamo identificare e un nome per questa persona. Abbiamo visto come caricare l'immagine e generare l'encoding del volto, e per ora nostro script ci permetterà di riconoscere un unico target per volta.
Adesso bisogna fare in modo che il programma effettui un'analisi sui fotogrammi ottenuti dal video trasmesso dalla nostra webcam per vedere se in questi fotogrammi sono presenti dei volti e se questi corrispondono al target che abbiamo scelto. Questo genere di operazioni effettuate in tempo reale sono piuttosto costose a livello di risorse, quindi è necessario disporre di un computer piuttosto potente per poter effettuare questa analisi. Per ridurre le risorse impiegate faremo in modo di scalare ciascun frame della fotocamera senza analizzarli tutti uno per uno.
Definiamo la variabile process_this_frame e inizializziamola a True:
while True:
ret, frame = webcam.read()
Ridimensioniamo i fotogrammi
Andiamo ora a scalare la dimensione del frame acquisito in modo da incrementare la velocità del nostro script e ridurre la richiesta di risorse per effettuare l'analisi.
Definiamo la variabile small_frame e assegniamola a cv2.size, a cui dobbiamo passare alcuni parametri:
- Il frame, ovvero il fotogramma sorgente che vogliamo andare a ridimensionare.
- La dimensione del ridimensionamento: in questo caso, visto che vogliamo andare a ridurre in proporzione, possiamo semplicemente passare None.
- Lo scale factor per l'asse delle x, che andiamo a ridurre ad un quinto impostandolo su 0.20.
- Lo scale factor per l'asse delle y, impostando anch’esso su 0.20.
while True:
# ...
small_frame = cv2.resize(frame, None, fx=0.20, fy=0.20)
Convertiamo i fotogrammi da bgr a rgb
Dobbiamo ora convertire l'immagine da bgr (blue green red) utilizzata da OpenCV a rgb (red green blue) utilizzata da face_recognition.
Definiamo la variabile rgb_small_frame e assegniamola a cv2.cvtColor, che accetta il frame che vogliamo convertire e un codice numerico che rappresenta la conversione che vogliamo effettuare, in questo caso la conversione da bgr a rgb è associata al numero 4:
while True:
# ...
rgb_small_frame = cv2.cvtColor(small_frame, 4)
Confrontiamo il volto nel frame con quello del target
Possiamo ora procedere con l'analisi vera e propria: se il process_this_frame è uguale a True, assegna alle due liste corrispondenti face_location (per trovare le coordinate del viso all’interno dell’immagine) e face_encodings (per effettuare l’encoding del volto all’interno del frame) e passa il frame ridotto di dimensioni e convertito in rgb come parametro. La posizione del volto all'interno del frame ci servirà per mostrare un quadrato con il nome della persona riconosciuta.
if process_this_frame:
face_locations = face_recognition.face_locations(rgb_small_frame)
frame_encodings = face_recognition.face_encodings(rgb_small_frame)
Verifichiamo se siano presenti o meno dei volti: se la lista frame_encoding non è vuota confrontiamo l'encoding del nostro target con quello che abbiamo acquisito dal frame utiizzando compare_faces di face_recognition e assegniamo il nome del target ad un’etichetta label se il volto corrisponde, altrimenti assegniamo “Unknown” (Sconosciuto). Nello stesso livello di indentazione del primo if cambiamo il valore di process_this_frame:
if process_this_frame:
# ...
if frame_encodings:
frame_face_encoding = frame_encodings[0]
match = face_recognition.compare_faces([target_encoding], frame_face_encoding)[0]
label = target_name if match else "Unknown"
process_this_frame = not process_this_frame
Abbiamo deciso di usare ogni secondo fotogramma per ottimizzare le risorse del PC e ci serve un loop infinito perché stiamo analizzando un flusso continuo di frames proveniente dalla WebCam: ciascun frame viene scalato a 1/5 della sua dimensione originale e il modello di colore viene alterato.
Mostriamo un’etichetta con il nome del target se corrisponde
Analizziamo quindi il volto nel frame (se presente) e compariamolo con quello del target. Per mostrare l’etichetta col nome del target, se la lista face_location non è vuota, reperiamo le sue coordinate. Riportiamo il volto alle dimensioni originali moltiplicando per 5:
if face_locations:
top, right, bottom, left = face_locations[0]
top *= 5
right *= 5
bottom *= 5
left *= 5
Ora che conosciamo le coordinate nel frame possiamo disegnare il rettangolo attorno al volto e l’etichetta con il nome usando OpenCV a cui passiamo il frame e le coordinate stesse in questo modo:
if face_locations:
# ...
cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2)
cv2.rectangle(frame, (left, bottom - 30), (right, bottom), (0, 255, 0), cv2.FILLED)
label_font = cv2.FONT_HERSHEY_DUPLEX
cv2.putText(frame, label, (left + 6, bottom - 6), label_font, 0.8, (255, 255, 255), 1)
Finalmente siamo pronti a mostrare quindi il risultato di tutto questo codice! Utilizziamo cv2.show a cui passiamo un nome per la finestra (nel nostro caso ”Video Feed”) e il nostro frame:
if face_locations:
# ...
cv2.imshow("Video Feed", frame)
Definiamo un tasto per interrompere il loop
Ci sono soltanto altre due cose che dobbiamo fare prima di terminare il nostro programma: definire un tasto per interrompere il while loop e rilasciare le risorse utilizzate, in questo caso la WebCam. Facciamo in modo che il programma venga chiuso quando l’utente preme il tasto “q”:
if cv2.waitKey(1) & 0xFF == ord('q'):
break
L’istruzione condizionale controlla se l'utente preme il tasto "q" sulla tastiera durante l'esecuzione del programma, in tal caso il loop viene interrotto con break.
Nel dettaglio, la funzione cv2.waitKey di OpenCV aspetta per un certo periodo di tempo (1 millisecondo nel nostro caso) che l'utente prema un tasto sulla tastiera: quando ciò accade la funzione restituisce il codice ASCII del tasto premuto.
& è l'operatore Bitwise AND. Gli operatori Bitwise permettono di effettuare operazioni logiche su dati rappresentanti da cifre binarie: in questo caso, viene utilizzato per “mascherare” il codice ASCII restituito da cv2.waitKey(1) con la maschera 0xFF (255 in valori decimali).
Una maschera è un valore binario utilizzato per selezionare specifici bit in un valore più grande. In Python, la maschera 0xFF viene spesso utilizzata per selezionare solo i primi 8 bit (1 byte) di un valore più grande perché il valore 0xFF rappresenta il numero binario 11111111, che ha tutti i bit impostati su 1. Quindi, quando viene utilizzato con l'operatore bitwise AND (&), 0xFF seleziona solo i primi 8 bit del valore a sinistra dell'operatore.
Infine, la funzione ord('q') restituisce il codice ASCII del carattere "q" (113 in valori decimali).
Quindi l’istruzione condizionale controlla se il valore restituito da cv2.waitKey mascherato con 0xFF è uguale al codice ASCII del carattere "q".
Rilasciamo le risorse e chiudiamo il programma
Non ci resta quindi che andare a rilasciare le risorse e chiudere le finestre:
webcam.release()
cv2.destroyAllWindows()
Il nostro script è ora completo! Se lo eseguiamo dovremmo vedere il video ripreso dalla webcam, delle cornici verdi intorno ai volti ed etichette con i nomi di quelli riconosciuti. Ecco il codice che abbiamo scritto:
import face_recognition
import cv2 # pip install opencv-python
webcam = cv2.VideoCapture(0)
image_file = input("Target Image File > ")
target_image = face_recognition.load_image_file(image_file)
target_encoding = face_recognition.face_encodings(target_image)[0]
print("Image Loaded. 128-dimension Face Encoding Generated. \n")
target_name = input("Target Name > ")
process_this_frame = True
while True:
ret, frame = webcam.read()
small_frame = cv2.resize(frame, None, fx=0.20, fy=0.20)
rgb_small_frame = cv2.cvtColor(small_frame, 4)
if process_this_frame:
face_locations = face_recognition.face_locations(rgb_small_frame)
frame_encodings = face_recognition.face_encodings(rgb_small_frame)
if frame_encodings:
frame_face_encoding = frame_encodings[0]
match = face_recognition.compare_faces([target_encoding], frame_face_encoding)[0]
label = target_name if match else "Unknown"
process_this_frame = not process_this_frame
if face_locations:
top, right, bottom, left = face_locations[0]
top *= 5
right *= 5
bottom *= 5
left *= 5
cv2.rectangle(frame, (left, top), (right, bottom), (0, 255, 0), 2)
cv2.rectangle(frame, (left, bottom - 30), (right, bottom), (0, 255, 0), cv2.FILLED)
label_font = cv2.FONT_HERSHEY_DUPLEX
cv2.putText(frame, label, (left + 6, bottom - 6), label_font, 0.8, (255, 255, 255), 1)
cv2.imshow("Video Feed", frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
webcam.release()
cv2.destroyAllWindows()
Puoi trovare il codice nel repo nel mio profilo GitHub.
Happy Coding!