[PYTHON] Mettre en œuvre une recherche de visage similaire en une demi-journée

En guise d'expérimentation pour notre propre service social, nous avons réalisé un prototype simple de la fonction de reconnaissance faciale. C'est très facile à faire, et c'est presque fait en appelant la bibliothèque python, donc cet article n'est pas techniquement intéressant du tout (rires). Je ne pensais pas que ce serait si facile, ou je ne pouvais pas trouver de telles informations en cherchant, alors j'aimerais les partager.

Connaissances préalables

Je n'ai besoin de rien. Je suis un amateur de python et d'apprentissage automatique.

Bibliothèque

https://github.com/ageitgey/face_recognition

Utilisez cette bibliothèque. C'est comme un wrapper pour dlib, une bibliothèque réputée forte en reconnaissance faciale.

Comment ça fonctionne

La bibliothèque ci-dessus a une méthode appelée face_encodings. Si vous transmettez le fichier image à cette méthode, le visage [repère](https://www.google.co.jp/search?biw=2339&bih=1240&tbm=isch&sa=1&q=face+landmarks&oq=face+landmarks&gs_l=psy- ab.3..0i19k1j0i7i30i19k1l7.10726.11076.0.11164.4.4.0.0.0.0.142.354.1j2.3.0 .... 0 ... 1.1.64.psy-ab..2.2.256 ... 0i7i30k1.L9q0iFem60Q #imgrc = JXb98Axur796VM :) Renvoie un vecteur de coordonnées.

Exemple:

[-0.14351621270179749, 0.057226795703172684, 0.07066182047128677, -0.13657408952713013, -0.0695628970861435, -0.09160482883453369, -0.011631922796368599, -0.14444005489349365, 0.1372034251689911, -0.12074219435453415, 0.2784915566444397, -0.1435723453760147, -0.301411509513855, -0.006434443406760693, -0.06611043959856033, 0.21539726853370667, -0.1636665165424347, -0.13245481252670288, -0.08063990622758865, 0.04853415489196777, 0.09177280962467194, -0.01833999715745449, 0.00841446127742529, 0.12095664441585541, -0.08568297326564789, -0.37953001260757446, -0.13193728029727936, -0.03719043731689453, -0.0870690569281578, -0.04294124245643616, -0.038571640849113464, 0.05095953121781349, -0.2148473709821701, -0.041665997356176376, 0.014024296775460243, 0.07775825262069702, -0.034873172640800476, -0.15043900907039642, 0.17863482236862183, -0.030670292675495148, -0.2826652228832245, 0.02874363772571087, 0.09433827549219131, 0.20609621703624725, 0.1781337857246399, 0.005972636863589287, -0.021562352776527405, -0.16687169671058655, 0.07589639723300934, -0.20823828876018524, 0.027126934379339218, 0.10467753559350967, 0.06701159477233887, 0.07915465533733368, -0.024046622216701508, -0.1669970601797104, 0.07604529708623886, 0.1269170194864273, -0.21936824917793274, -0.06592322885990143, 0.06071619316935539, -0.14255106449127197, -0.047067590057849884, -0.08292384445667267, 0.2115967869758606, 0.18284666538238525, -0.15493471920490265, -0.14141127467155457, 0.15566584467887878, -0.1567707657814026, -0.005966860800981522, 0.02694620192050934, -0.14431986212730408, -0.19422967731952667, -0.27188384532928467, 0.003987520933151245, 0.2886632978916168, 0.051324617117643356, -0.24798262119293213, 0.028046492487192154, -0.03672055900096893, 0.048082903027534485, 0.10906309634447098, 0.16191940009593964, -0.008259378373622894, 0.005847998894751072, -0.11125662177801132, 0.006064308807253838, 0.1905171126127243, -0.07413583993911743, 0.02043292298913002, 0.290202260017395, 0.00569811649620533, -0.00016449671238660812, 0.11121324449777603, 0.10905371606349945, -0.09846137464046478, 0.005683856084942818, -0.15451037883758545, 0.05895839259028435, 0.04510065168142319, -0.03569173067808151, -0.06883768737316132, 0.08693848550319672, -0.16857297718524933, 0.1154068261384964, 0.007511516101658344, -0.01983277127146721, 0.02589372731745243, -0.09050130844116211, -0.020625963807106018, -0.11194785684347153, 0.08375582844018936, -0.22212722897529602, 0.17791825532913208, 0.13751709461212158, 0.0053409687243402, 0.1599988043308258, 0.060513101518154144, 0.10321187227964401, -0.030407054349780083, -0.03938911110162735, -0.22134312987327576, 0.0003300569951534271, 0.15529313683509827, 0.004012138117104769, 0.06936727464199066, -0.019267655909061432]

Effectuez cette conversion à l'avance pour toutes les images. Le fait qu'une photo soit similaire à une photo est simplement déterminé par la proximité de ce vecteur. C'est facile!

La distance vectorielle utilise uniquement la distance euclidienne. J'ai fait la partie recherche dans Ruby, donc j'ai implémenté le calcul de distance de manière appropriée, mais si vous recherchez également en python, il existe une méthode appelée face_distance, vous pouvez donc l'utiliser également. Jetez un coup d'œil au code dans exemple.

Installation

Je vais vous expliquer comment le faire sur Ubuntu.

Vous avez d'abord besoin de python et de pip.

apt install python3-pip

Vous aurez besoin de boost et cmake pour construire dlib.

apt install cmake libboost-dev libboost-python-dev

À la fin, installez la bibliothèque python.

pip3 install face_recognition

code

codage

Tout d'abord, encodons l'image dans un vecteur. Écrivez le script de 3 lignes suivant

import face_recognition
import json
import sys
 
image = face_recognition.load_image_file(sys.argv[1])
face_encoding = face_recognition.face_encodings(image)[0]
print(json.dumps(face_encoding.tolist()))

Je vais essayer.

$ python3 recognize.py face.jpeg
[-0.14351621270179749, 0.057226795703172684, 0.07066182047128677, -0.13657408952713013, -0.0695628970861435, -0.09160482883453369, -0.011631922796368599, -0.14444005489349365, 0.1372034251689911, -0.12074219435453415, 0.2784915566444397, -0.1435723453760147, -0.301411509513855, -0.006434443406760693, -0.06611043959856033, 0.21539726853370667, -0.1636665165424347, -0.13245481252670288, -0.08063990622758865, 0.04853415489196777, 0.09177280962467194, -0.01833999715745449, 0.00841446127742529, 0.12095664441585541, -0.08568297326564789, -0.37953001260757446, -0.13193728029727936, -0.03719043731689453, -0.0870690569281578, -0.04294124245643616, -0.038571640849113464, 0.05095953121781349, -0.2148473709821701, -0.041665997356176376, 0.014024296775460243, 0.07775825262069702, -0.034873172640800476, -0.15043900907039642, 0.17863482236862183, -0.030670292675495148, -0.2826652228832245, 0.02874363772571087, 0.09433827549219131, 0.20609621703624725, 0.1781337857246399, 0.005972636863589287, -0.021562352776527405, -0.16687169671058655, 0.07589639723300934, -0.20823828876018524, 0.027126934379339218, 0.10467753559350967, 0.06701159477233887, 0.07915465533733368, -0.024046622216701508, -0.1669970601797104, 0.07604529708623886, 0.1269170194864273, -0.21936824917793274, -0.06592322885990143, 0.06071619316935539, -0.14255106449127197, -0.047067590057849884, -0.08292384445667267, 0.2115967869758606, 0.18284666538238525, -0.15493471920490265, -0.14141127467155457, 0.15566584467887878, -0.1567707657814026, -0.005966860800981522, 0.02694620192050934, -0.14431986212730408, -0.19422967731952667, -0.27188384532928467, 0.003987520933151245, 0.2886632978916168, 0.051324617117643356, -0.24798262119293213, 0.028046492487192154, -0.03672055900096893, 0.048082903027534485, 0.10906309634447098, 0.16191940009593964, -0.008259378373622894, 0.005847998894751072, -0.11125662177801132, 0.006064308807253838, 0.1905171126127243, -0.07413583993911743, 0.02043292298913002, 0.290202260017395, 0.00569811649620533, -0.00016449671238660812, 0.11121324449777603, 0.10905371606349945, -0.09846137464046478, 0.005683856084942818, -0.15451037883758545, 0.05895839259028435, 0.04510065168142319, -0.03569173067808151, -0.06883768737316132, 0.08693848550319672, -0.16857297718524933, 0.1154068261384964, 0.007511516101658344, -0.01983277127146721, 0.02589372731745243, -0.09050130844116211, -0.020625963807106018, -0.11194785684347153, 0.08375582844018936, -0.22212722897529602, 0.17791825532913208, 0.13751709461212158, 0.0053409687243402, 0.1599988043308258, 0.060513101518154144, 0.10321187227964401, -0.030407054349780083, -0.03938911110162735, -0.22134312987327576, 0.0003300569951534271, 0.15529313683509827, 0.004012138117104769, 0.06936727464199066, -0.019267655909061432]

Oh facile! Faites cela pour toutes les images et enregistrez-les dans une base de données.

Chercher

Je ne suis pas habitué à python, ou le code produit est basé sur Ruby, j'ai donc fait la recherche dans Ruby. Cela dit, en substance, il vous suffit de faire ce qui suit:

Compte tenu de l'image que vous souhaitez rechercher, convertissez-la d'abord en vecteur avec le programme python ci-dessus et passez-la à la méthode suivante. Dans la version simplifiée, all_vectors a préchargé tous les vecteurs en mémoire.

def search(v)
  @all_vectors.min_by |u|
    euclidean_distance(u,v)
  end
end

Je pense qu'il est facile de personnaliser, comme retourner les 10 premières places.

résultat

Ce serait bien si je pouvais coller la photo résultante, mais je ne peux pas la montrer car il n'y a qu'une photo du visage de l'utilisateur du service.

Mon impression personnelle est que c'est bien fait pour une fonction créée en une demi-journée. Est-ce la même personne? Parfois, il suggère des visages similaires au niveau que vous pensez qu'il est, et parfois il suggère des visages qui ne sont pas très similaires. J'ai utilisé environ 30 000 images, mais environ 70% du temps, j'ai suggéré des images qui me ressemblaient. Ce type de fonction est encore rare, et je pense que cela correspond à la qualité attractive du modèle Kano, il n'est donc pas nécessaire que tous les résultats soient similaires, et en ce sens, j'ai senti que c'était un niveau qui pouvait être mis en pratique avec notre propre service.

Cela peut être naturel, mais des images de sexes différents ont rarement été suggérées.

Il semble que la précision augmentera en augmentant le nombre d'images.

Quand ça marche

Je pense que cela fonctionne bien pour les visages caractéristiques tels qu'un visage très bien organisé, des contours faciaux arrondis et de grands yeux.

Quand ça ne marche pas

Tout d'abord, dans les cas suivants, le visage n'a pas été reconnu en premier lieu.

Les photos avec de petits visages semblent être encodées de manière assez appropriée et provoqueront du bruit, il est donc préférable de les supprimer. Vous pouvez facilement identifier ces images. (Utilisez une méthode appelée face_locations)

Limites

Comme mentionné au début, cette bibliothèque est une fonction qui extrait uniquement les informations des repères faciaux en tant que caractéristique et les compare. Cela signifie que vous ne regardez que des informations telles que:

En revanche, les informations suivantes ne sont pas utilisées.

Si vous souhaitez effectuer une recherche similaire en utilisant des informations aussi détaillées, je pense que vous devez utiliser l'apprentissage en profondeur.

Accélérant

Dans l'exemple ci-dessus, nous avons effectué une recherche linéaire qui compare simplement toutes les images, mais cela devient inutile lorsque le nombre d'images atteint environ 10 000.

https://ekzhu.github.io/datasketch/lshforest.html

En utilisant une telle bibliothèque d'approximation probabiliste, vous pouvez effectuer une recherche à grande vitesse.

Recommended Posts

Mettre en œuvre une recherche de visage similaire en une demi-journée
Ecrire une dichotomie en Python
Implémenter un paramètre de date dans Tkinter
Écrire une recherche de priorité en profondeur en Python
Implémenter un décorateur de vue personnalisé avec Pyramid
Implémenter un modèle utilisateur personnalisé dans Django
Lancez un simple service de recherche protégé par mot de passe en 5 minutes
Comment implémenter un sélecteur de dégradé dans Houdini
Heppoko développe un service Web en une semaine # 2 Domain Search
Je veux facilement implémenter le délai d'expiration en python
J'ai essayé d'implémenter un pseudo pachislot en Python
Implémenter la recherche de priorité en profondeur (DFS) et la recherche de priorité de largeur (BFS) en python