Wat is gezichtsherkenning?
Gezichtsherkenning is de taak van het vergelijken van een onbekend gezicht van een individu naar afbeeldingen in een database met opgeslagen records. De mapping kan één-op-één of één-op-veel zijn, afhankelijk van of we gezichtsverificatie of gezichtsidentificatie uitvoeren.
In deze zelfstudie zijn we geïnteresseerd in het bouwen van een gezichtsidentificatiesysteem dat zal verifiëren of een afbeelding, algemeen bekend als sondebeeld, bestaat binnen een reeds bestaande database met gezichten, algemeen bekend als de evaluatieset.
Intuïtie
Er zijn vier hoofdstappen betrokken bij het bouwen van een dergelijk systeem:
1. Detecteer gezichten in een afbeelding
Beschikbare modellen voor gezichtsdetectie zijn MTCNN, FaceNet, Dlib, enz.
2. Gezichten bijsnijden en uitlijnen voor uniformiteit
OpenCV-bibliotheek biedt alle tools die we nodig hebben voor deze stap.
3. Zoek vectorweergave voor elk gezicht
Omdat programma's niet rechtstreeks met jpg- of png-bestanden kunnen werken, hebben we een manier nodig om afbeeldingen naar getallen te vertalen. In deze zelfstudie gebruiken we de Inzichtgezicht model voor het maken van een multidimensionale (512-d) inbedding voor een gezicht zodat het bruikbare semantisch informatie over het gezicht.
Om alle drie de stappen aan te pakken met behulp van een enkele bibliotheek, zullen we gebruiken insightface
. We gaan met name werken met het ArcFace-model van Insightface.
InzichtGezicht is een open-source deep face-analysemodel voor gezichtsherkenning, gezichtsdetectie en gezichtsuitlijningstaken.
4. Vergelijking van de inbeddingen
Zodra we elk uniek gezicht in een vector hebben vertaald, komt het vergelijken van gezichten in wezen neer op het vergelijken van de overeenkomstige inbeddingen. We zullen deze inbeddingen gebruiken om een sci-kit leermodel te trainen.
PS Als je mee wilt doen, de code is beschikbaar op GitHub.
Als deze diepgaande educatieve inhoud nuttig voor u is, abonneer u op onze AI-research mailinglijst om gewaarschuwd te worden wanneer we nieuw materiaal uitbrengen.
Setup
Creëer een virtuele omgeving (optioneel):
python3 -m venv face_search_env
Activeer deze omgeving:
bron face_search_env/bin/activate
Noodzakelijke installaties binnen deze omgeving:
pip install mxnet==1.8.0.post0 pip install -U insightface==0.2.1 pip install onnx==1.10.1 pip install onnxruntime==1.8.1
Wat nog belangrijker is, als je eenmaal klaar bent met het installeren van pip insightface
:
- Download de antilope modelvrijgave van onedrive. (Het bevat twee vooraf getrainde modellen voor detectie en herkenning).
- Leg het eronder ~/.insightface/models/
, dus er zijn onnx-modellen bij ~/.insightface/models/antelope/*.onnx
.
Dit is hoe het eruit zou moeten zien als de installatie correct is uitgevoerd:
en als je naar binnen kijkt antelope
map, vindt u de twee onnx
modellen voor gezichtsherkenning en -herkenning:
Opmerking: Sinds de laatste release van insightface
0.4.1 vorige week was de installatie niet zo eenvoudig als ik had gehoopt (althans voor mij). Daarom zal ik 0.2.1 gebruiken voor deze zelfstudie. In de toekomst zal ik de code op Github dienovereenkomstig bijwerken. Zie de instructies hier als je vastzit.
dataset
We zullen werken met de Yale Faces-dataset die beschikbaar is op Kaggle, met ongeveer 165 grijswaardenafbeeldingen van 15 individuen (dwz 11 unieke afbeeldingen per identiteit). De afbeeldingen zijn samengesteld uit een grote verscheidenheid aan uitdrukkingen, poses en verlichtingsconfiguraties.
Zodra u de dataset heeft, kunt u deze uitpakken in een nieuw gemaaktedata
directory binnen uw project (zie de projectdirectorystructuur op GitHub).
Laten we beginnen…
Als je mee wilt volgen, de Jupyter Notebook is te vinden op Github.
Invoer
import os import pickle import numpy as np from PIL import Image from typing import List from tqdm import tqdm from insightface.app import FaceAnalysis from sklearn.neighbors import NearestNeighborsInsightface-model laden
Eens
insightface
is geïnstalleerd, moeten we bellenapp=FaceAnalysis(name="model_name")
om de modellen te laden.Sinds we onze hebben opgeslagen
onnx
modellen binnen de antilope directory:app = FaceAnalysis(name="antelope") app.prepare(ctx_id=0, det_size=(640, 640))Genereer Insightface-insluitingen
Het genereren van een inbedding voor een afbeelding is vrij eenvoudig met de
insightface
model. Bijvoorbeeld:# Generating embeddings for an image img_emb_results = app.get(np.asarray(img)) img_emb = img_emb_results[0].embedding img_emb.shape------------OUTPUT--------------- (512,)
dataset
Voordat we deze dataset gaan gebruiken, moeten we de extensies voor de bestanden in de map zodanig corrigeren dat de bestandsnamen eindigen op
.gif
. (of.jpg
,.png
Enz.).Het volgende codefragment verandert bijvoorbeeld de bestandsnaam
subject01.glasses
naarsubject01_glasses.gif
.# Fixing the file extensions YALE_DIR = "../data/yalefaces" files = os.listdir(YALE_DIR)[1:] for i, img in enumerate(files): # print("original name: ", img) new_ext_name = "_".join(img.split(".")) + ".gif" # print("new name: ", new_ext_name) os.rename(os.path.join(YALE_DIR, img), os.path.join(YALE_DIR, new_ext_name))Vervolgens splitsen we de gegevens op in de evaluatie- en sondesets: 90% of 10 afbeeldingen per proefpersoon worden onderdeel van de evaluatieset en de resterende 10% of 1 afbeelding per proefpersoon wordt gebruikt in de sondeset.
Om bemonsteringsbias te voorkomen, wordt het sondebeeld voor elk onderwerp willekeurig gekozen met behulp van een hulpfunctie genaamd
create_probe_eval_set()
. Het neemt als invoer een lijst met de (bestandsnamen voor de) 11 afbeeldingen die bij een bepaald onderwerp horen en retourneert twee lijsten met lengtes 1 en 10. De eerste bevat de bestandsnaam die moet worden gebruikt voor de sondeset, terwijl de laatste bestandsnamen bevat voor de evaluatieset.def create_probe_eval_set(files: List): # pick random index between 0 and len(files)-1 random_idx = np.random.randint(0,len(files)) probe_img_fpaths = [files[random_idx]] eval_img_fpaths = [files[idx] for idx in range(len(files)) if idx != random_idx] return probe_img_fpaths, eval_img_fpathsGenereer inbeddingen
Beide lijsten geretourneerd door de
create_probe_eval_set()
worden achtereenvolgens ingevoerd in een helperfunctie genaamdgenerate_embs()
. Voor elke bestandsnaam in de lijst leest het de afbeelding in grijstinten, converteert het naar RGB, berekent de overeenkomstige inbeddingen en retourneert ten slotte de inbeddingen samen met de afbeeldingslabels (geschrapt uit de bestandsnaam).def generate_embs(img_fpaths: List[str]): embs_set = list() embs_label = list() for img_fpath in img_fpaths: # read grayscale img img = Image.open(os.path.join(YALE_DIR, img_fpath)) img_arr = np.asarray(img) # convert grayscale to rgb im = Image.fromarray((img_arr * 255).astype(np.uint8)) rgb_arr = np.asarray(im.convert('RGB')) # generate Insightface embedding res = app.get(rgb_arr) # append emb to the eval set embs_set.append(res) # append label to eval_label set embs_label.append(img_fpath.split("_")[0]) return embs_set, embs_labelNu we een raamwerk hebben voor het genereren van inbeddingen, gaan we verder met het maken van inbeddingen voor zowel de sonde als de evaluatieset met behulp van
generate_embs()
.# sorting files files = os.listdir(YALE_DIR) files.sort() eval_set = list() eval_labels = list() probe_set = list() probe_labels = list() IMAGES_PER_IDENTITY = 11 for i in tqdm(range(1, len(files), IMAGES_PER_IDENTITY), unit_divisor=True): # ignore the README.txt file at files[0] # print(i) probe, eval = create_probe_eval_set(files[i:i+IMAGES_PER_IDENTITY]) # store eval embs and labels eval_set_t, eval_labels_t = generate_embs(eval) eval_set.extend(eval_set_t) eval_labels.extend(eval_labels_t) # store probe embs and labels probe_set_t, probe_labels_t = generate_embs(probe) probe_set.extend(probe_set_t) probe_labels.extend(probe_labels_t)Enkele dingen om te overwegen:
- De bestanden geretourneerd door
os.listdir()
staan in volledig willekeurige volgorde, daarom is sorteren op regel 3 belangrijk. Waarom hebben we gesorteerde bestandsnamen nodig? Onthoudencreate_probe_eval_set()
op regel 11 vereist alle bestanden die bij een bepaald onderwerp horen in een enkele iteratie.
Uitvoer van os.listdir() zonder sorteren (links) en met sorteren (rechts)
- [optioneel] We hadden de kunnen vervangen
create_probe_eval_set()
functie, ontdoen van defor
loop, en vereenvoudigde een paar regels in het bovenstaande codefragment als we de gestratificeerdtrain_test_split
functionaliteit geleverd doorsklearn
. Voor deze zelfstudie heb ik echter prioriteit gegeven aan duidelijkheid boven de eenvoud van de code.
Vaak, insightface
kan een gezicht niet detecteren en genereert er vervolgens een lege inbedding voor. Dat verklaart waarom sommige vermeldingen in probe_set
or eval_set
lijst is mogelijk leeg. Het is belangrijk dat we ze eruit filteren en alleen niet-lege waarden behouden.
Om dit te doen, creëren we een andere helperfunctie genaamd filter_empty_embs()
:
def filter_empty_embs(img_set: List, img_labels: List[str]): # filtering where insightface could not generate an embedding good_idx = [i for i,x in enumerate(img_set) if x] if len(good_idx) == len(img_set): clean_embs = [e[0].embedding for e in img_set] clean_labels = img_labels else: # filtering eval set and labels based on good idx clean_labels = np.array(img_labels)[good_idx] clean_set = np.array(img_set, dtype=object)[good_idx] # generating embs for good idx clean_embs = [e[0].embedding for e in clean_set] return clean_embs, clean_labelsHet neemt als invoer de afbeeldingsset (ofwel
probe_set
oreval_set
) en verwijdert die elementen waarvoorinsightface
kon geen inbedding genereren (zie regel 6). Hierna werkt het ook de labels bij (ofwelprobe_labels
oreval_labels
) (zie Regel 7) zodat beide sets en labels dezelfde lengte hebben.Ten slotte kunnen we de 512-d inbedding verkrijgen voor alleen de goede indices in beide evaluatieset en sondeset:
evaluation_embs, evaluation_labels = filter_empty_embs(eval_set, eval_labels) probe_embs, probe_labels = filter_empty_embs(probe_set, probe_labels)assert len(evaluation_embs) == len(evaluation_labels) assert len(probe_embs) == len(probe_labels)Met beide sets tot onze beschikking, zijn we nu klaar om ons gezichtsidentificatiesysteem te bouwen met behulp van een populaire leermethode zonder toezicht die is geïmplementeerd in de Sklearn-bibliotheek.
Een gezichtsherkenningssysteem maken
Wij trainen de Naaste buur model met behulp van
.fit()
met evaluatie-inbeddingen alsX
. Dit is een handige techniek om zonder toezicht naaste buren te leren.Met de dichtstbijzijnde buurmethode kunnen we een vooraf bepaald aantal trainingsvoorbeelden vinden die qua afstand het dichtst bij een nieuw punt liggen.
Opmerking: de afstand kan in het algemeen elke metrische maat zijn, zoals Euclidisch, Manhattan, Cosinus, Minktowski, enz.
# Nearest neighbour learning method nn = NearestNeighbors(n_neighbors=3, metric="cosine") nn.fit(X=evaluation_embs) # save the model to disk filename = 'faceID_model.pkl' with open(filename, 'wb') as file: pickle.dump(nn, file) # some time later... # load the model from disk # with open(filename, 'rb') as file: # pickle_model = pickle.load(file)Omdat we een ongecontroleerd leermethode, merk op dat we geen labels doorgeven, dwz
evaluation_label
aan defit
methode. Het enige wat we hier doen is het in kaart brengen van de face-inbeddingen in de evaluatieset in een latente ruimte.Waarom??, je vraagt.
Eenvoudig antwoord: door de trainingsset van tevoren in het geheugen op te slaan, kunnen we het zoeken naar de naaste buren tijdens de inferentietijd versnellen.Hoe werkt dit?
Eenvoudig antwoord: het is best handig om de boom op een geoptimaliseerde manier in het geheugen op te slaan, vooral wanneer de trainingsset groot is en het zoeken naar de buren van een nieuw punt rekenkundig duur wordt.Op buren gebaseerde methoden staan bekend als niet-generaliserende methoden voor machinaal leren, omdat ze eenvoudigweg alle trainingsgegevens "onthouden" (mogelijk getransformeerd in een snelle indexeringsstructuur zoals een Ball Tree of KD Tree). [bron]
Opmerking: Zie dit StackOverflow-discussie als je nog steeds niet overtuigd bent!
Gevolgtrekking
Voor elk nieuw sondebeeld kunnen we vinden of het aanwezig is in de evaluatieset door naar de top ervan te zoeken k burengebruik
nn.neighbours()
methode. Bijvoorbeeld,# Example inference on test image dists, inds = nn.kneighbors(X = probe_img_emb.reshape(1,-1), n_neighbors = 3, return_distances = True )Als de labels bij de geretourneerde indexen (
inds
) in de evaluatieset perfect overeenkomen met het originele/echte label van de sondeafbeelding, dan weten we dat we ons gezicht in het verificatiesysteem hebben gevonden.We hebben de bovengenoemde logica verpakt in de
print_ID_results()
methode. Het neemt als invoer het sondebeeldpad, de evaluatiesetlabels en deverbose
vlag om aan te geven of gedetailleerde resultaten moeten worden weergegeven.def print_ID_results(img_fpath: str, evaluation_labels: np.ndarray, verbose: bool = False): img = Image.open(img_fpath) img_emb = app.get(np.asarray(img))[0].embedding # get pred from KNN dists, inds = nn.kneighbors(X=img_emb.reshape(1,-1), n_neighbors=3, return_distance=True) # get labels of the neighbours pred_labels = [evaluation_labels[i] for i in inds[0]] # check if any dist is greater than 0.5, and if so, print the results no_of_matching_faces = np.sum([1 if d <=0.6 else 0 for d in dists[0]]) if no_of_matching_faces > 0: print("Matching face(s) found in database! ") verbose = True else: print("No matching face(s) not found in database!") # print labels and corresponding distances if verbose: for label, dist in zip(pred_labels, dists[0]): print(f"Nearest neighbours found in the database have labels {label} and is at a distance of {dist}")Enkele belangrijke dingen om hier op te merken:
inds
bevatten de indices van de naaste buren in deevaluation_labels
instellen (lijn 6). Bijvoorbeeld,inds = [[2,0,11]]
betekent label op index = 2 inchevaluation_labels
blijkt het dichtst bij het sondebeeld te liggen, gevolgd door het label bij index = 0.- sinds voor elke beeld,
nn.neighbors
een niet-leeg antwoord zal retourneren, moeten we die resultaten alleen beschouwen als een gezichts-ID-overeenkomst if de geretourneerde afstand is kleiner dan of gelijk aan 0.6 (regel 12). (PS De keuze van 0.6 is volledig willekeurig).
Ga bijvoorbeeld verder met het bovenstaande voorbeeld waarinds = [[2,0, 11]]
en laten we zeggendists = [[0.4, 0.6, 0.9]]
, beschouwen we alleen de labels bij index=2 en index = 0 (inevaluation_labels
) als een echte gezichtsovereenkomst omdat dedist
want de laatste buurman is te groot om een echte match te zijn.
Laten we, als een snelle controle van de gezondheid, de reactie van het systeem bekijken wanneer we het gezicht van een baby invoeren als een sondebeeld. Zoals verwacht, onthult het dat er geen overeenkomende gezichten zijn gevonden! We stellen echter vast verbose
as True, waardoor we de labels en afstanden ervoor te zien krijgen nep naaste buren in de database, die allemaal behoorlijk groot lijken te zijn (>0.8).
Evalueren van het gezichtsherkenningssysteem
Een van de manieren om te testen of dit systeem goed is, is door te kijken hoeveel relevante resultaten zijn aanwezig in de top k buren. Een relevant resultaat is een resultaat waarbij het ware label overeenkomt met het voorspelde label. Deze statistiek wordt over het algemeen aangeduid als precisie bij k, waarbij k vooraf bepaald is.
Kies bijvoorbeeld een afbeelding (of liever een inbedding ) uit de sondeset met een echt label als 'subject01'. Als de bovenste twee pred_labels
geretourneerd door nn.neighbors
voor deze afbeelding zijn ['subject01', 'subject01'], dit betekent de precisie bij k (p@k) met k=2
bedraagt 100%. Evenzo, als slechts één van de waarden in pred_labels
was gelijk aan 'subject05', p@k zou 50% zijn, enzovoort...
dists, inds = nn.kneighbors(X=probe_embs_example.reshape(1, -1), n_neighbors=2, return_distance=True) pred_labels = [evaluation_labels[i] for i in inds[0] ] pred_labels----- OUTPUT ------ ['002', '002']
Laten we doorgaan en het gemiddelde berekenen
p@k
waarde over de gehele sondeset:# inference on probe set dists, inds = nn.kneighbors(X=probe_embs, n_neighbors=2, return_distance=True) # calculate avg p@k p_at_k = np.zeros(len(probe_embs)) for i in range(len(probe_embs)): true_label = probe_labels[i] pred_neighbr_idx = inds[i] pred_labels = [evaluation_labels[id] for id in pred_neighbr_idx] pred_is_labels = [1 if label == true_label else 0 for label in pred_labels] p_at_k[i] = np.mean(pred_is_labels) p_at_k.mean()------ UITGANG --------0.9
Geweldig! 90% Niet al te armoedig maar zeker voor verbetering vatbaar (maar dat is voor een andere keer)…
Een pluim voor je dat je dit doorzet! Hopelijk was deze warme kennismaking met gezichtsherkenning, een actief onderzoeksgebied op het gebied van computervisie, voldoende om u op weg te helpen. Zoals altijd, laat het me weten als er een gemakkelijkere manier is om sommige dingen te doen die ik in dit artikel heb genoemd.
Tot de volgende keer 🙂
Podurama is de beste podcastspeler om meer dan een miljoen shows en 30 miljoen afleveringen te streamen. Het biedt de beste aanbevelingen op basis van uw interesses en luistergeschiedenis. Beschikbaar voor iOS Android MacOS Windows 10 en internet. Vroege gebruikers krijgen gratis levenslange synchronisatie tussen onbeperkte apparaten.
Dit artikel is oorspronkelijk gepubliceerd op Op weg naar data science en opnieuw gepubliceerd naar TOPBOTS met toestemming van de auteur.
Geniet van dit artikel? Meld u aan voor meer AI-updates.
We laten het je weten wanneer we meer technisch onderwijs vrijgeven.
Verwant
Bron: https://www.topbots.com/building-face-recognition-system-in-python/
- "
- &
- 11
- 7
- 9
- actieve
- AI
- ai onderzoek
- Alles
- amp
- analyse
- gebruiken
- Apple
- GEBIED
- dit artikel
- AVG
- BEST
- bouw
- Gebouw
- verandering
- code
- Computer visie
- content
- Wij creëren
- gewas
- gegevens
- Database
- Opsporing
- systemen
- afstand
- Vroeg
- Onderwijs
- onderwijs
- Milieu
- essentials
- etc
- extensies
- Gezicht
- gezichtsherkenning
- gezichten
- SNELLE
- Fed
- Tot slot
- Bepalen
- volgen
- Achtergrond
- Gratis
- functie
- toekomst
- Algemeen
- gif
- GitHub
- goed
- Kopen Google Reviews
- grijstinten
- hier
- geschiedenis
- Hoe
- HTTPS
- Identificatie
- Identiteit
- IDX
- beeld
- index
- informatie
- betrokken zijn
- IT
- labels
- Groot
- laatste
- LEARN
- leren
- Bibliotheek
- Lijn
- Lijst
- Het luisteren
- lijsten
- laden
- machine learning
- maken
- Match
- maatregel
- Microsoft
- miljoen
- model
- namen
- Netjes
- buren
- nummers
- bestellen
- speler
- Podcast
- Populair
- precisie
- presenteren
- sonde
- Programma's
- project
- Python
- archief
- onderzoek
- antwoord
- Resultaten
- Retourneren
- lopend
- Ontdek
- reeks
- So
- Tussenruimte
- snelheid
- spleet
- gestart
- shop
- system
- Technisch
- proef
- De toekomst
- niet de tijd of
- top
- Trainingen
- zelfstudie
- zonder toezicht leren
- bijwerken
- updates
- us
- gebruikers
- waarde
- Verificatie
- Virtueel
- visie
- web
- week
- binnen
- Mijn werk
- X