Facial recognition
𝗔𝗣𝗜 𝗳𝗮𝗰𝗲 𝗿𝗲𝗰𝗼𝗴𝗻𝗶𝘁𝗶𝗼𝗻 (ageitgey)
████ 𝗚𝗣𝗨
# raspi-config
split under 'Advanced'. Set it up '16'.
████ 𝗗𝗟𝗜𝗕 (25min)
# cd /opt && mkdir -p dlib
# git clone -b 'v19.6' --single-branch https://github.com/davisking/dlib.git dlib/
# cd ./dlib
# python3 setup.py install --compiler-flags "-mfpu=neon"
████ 𝗔𝗚𝗘𝗜𝗚𝗧𝗘𝗬 𝗙𝗔𝗖𝗜𝗔𝗟 𝗥𝗘𝗖𝗢 𝗔𝗣𝗜
# pip3 install face_recognition
████ 𝗥𝗘𝗖𝗢𝗚𝗡𝗜𝗧𝗜𝗢𝗡 𝗖𝗢𝗗𝗘 𝗘𝗫𝗔𝗠𝗣𝗟𝗘
# cd /opt && mkdir -p ageitgey
# git clone --single-branch https://github.com/ageitgey/face_recognition.git ageitgey/
# cd ./face_recognition/examples
# python3 facerec_on_raspberry_pi.py
████ 𝗚𝗘𝗧 𝗕𝗘𝗧𝗧𝗘𝗥 𝗣𝗘𝗥𝗙𝗢𝗥𝗠𝗔𝗡𝗖𝗘 (optio?)
█ 𝐨𝐩𝐞𝐧𝐛𝐥𝐚𝐬
# apt install libopenblas-dev liblapack-dev libatlas-base-dev
On injecte dans l'environnement du shell avant l'execution du script de reco
# export OPENBLAS_NUM_THREADS=1
# export OPENBLAS_MAIN_FREE=1
source : https://gist.github.com/ageitgey/1ac8dbe8572f3f533df6269dab35df65#gistcomment-2071073
████ 𝗧𝗘𝗟𝗘𝗚𝗥𝗔𝗠
# pip3 install python-telegram-bot --upgrade
source : https://github.com/python-telegram-bot/python-telegram-bot
████ 𝗩𝗢𝗜𝗖𝗘 𝗧𝗧𝗦
Puis installation de pico tts, la voix synthétique est de
meilleur qualité que 'espeak' ou 'google'
# apt install libttspico-utils
Augmenter le volume général au max
# amixer sset 'PCM' 100%
source : https://la-programmation.surleweb-france.fr/linux-faire-parler-systeme/
source : https://www.framboise314.fr/donnez-la-parole-a-votre-raspberry-pi/
████ 𝗦𝗬𝗦𝗧𝗘𝗠𝗗
# nano /lib/systemd/system/recofacial.service
╔══════════════════════════════════════╗
[Unit]
Description=Facial Reco
After=network.target
[Service]
Type=forking
ExecStart=/opt/reco/cronfacial.sh
Restart=always
RuntimeMaxSec=43200
[Install]
WantedBy=multi-user.target
╚══════════════════════════════════════╝
RuntimeMaxSec=43200 = reboot du prog toute les 12h
# systemctl daemon-reload
# systemctl enable recofacial
# systemctl start recofacial
# nano /opt/reco/cronfacial.sh
# chmod +x cronfacial.sh
╔══════════════════════════════════════════╗
#!/usr/bin/env bash
cd /opt/reco/
screen -dmS recofacial python3 /opt/reco/reco.py --cpus 4
╚══════════════════════════════════════════╝
█ 𝐜𝐨𝐫𝐫𝐮𝐩𝐭𝐢𝐨𝐧 ?
𝙝𝙮𝙥𝙤𝙩𝙝𝙚𝙨𝙚 1
Après 35heures de fonctionnement, le système se voit apparaître une fuite de mémoire...
On remarque facilement les crontabs screen de 4h.
𝙝𝙮𝙥𝙤𝙩𝙝𝙚𝙨𝙚 2
Visiblement la fautive serait la pi camera !
source : https://github.com/raspberrypi/linux/issues/2680 (faire CTRL+F : camera)
𝙝𝙮𝙥𝙤𝙩𝙝𝙚𝙨𝙚 3 ( ✓ ✓ ) (( 𝐞𝐝𝐢𝐭 𝐬𝐞𝐩𝐭𝐞𝐦𝐛𝐫𝐞 𝟐𝟎𝟏𝟗))
trop de reboot de la carte SD, il y a des dégats sur le FileSystem (FS)
besoin de réparer avec l'utilitaire "fsck"
( screen UAG478 )
( screen YZB416 )
╔═══════════════════════════════════════════╗
# fsck /dev/mmcblk0p1
1) Remove dirty bit <----
2) No action
╚═══════════════════════════════════════════╝
https://raspberrypi.stackexchange.com/questions/251/how-can-i-determine-when-an-sd-card-needs-replacement
edit : idée, mettre un watchdog du process screen, si down alors faire la commande qui repare la carte SD puis reboot
# cat /var/log/kern.log
╔══════════════════════════════════════╗
Jan 21 04:00:20 raspberrypi kernel: [135619.301497] cma: cma_alloc: alloc failed, req-size: 1 pages, ret: -16
Jan 21 08:13:25 raspberrypi kernel: [150804.353280] cma: cma_alloc: alloc failed, req-size: 1 pages, ret: -16
Jan 21 12:00:20 raspberrypi kernel: [164419.222848] cma: cma_alloc: alloc failed, req-size: 1 pages, ret: -16
Jan 21 16:00:19 raspberrypi kernel: [178818.772892] cma: cma_alloc: alloc failed, req-size: 1 pages, ret: -16
Jan 21 20:00:20 raspberrypi kernel: [193219.314390] cma: cma_alloc: alloc failed, req-size: 1 pages, ret: -16
Jan 22 00:00:20 raspberrypi kernel: [207619.939983] cma: cma_alloc: alloc failed, req-size: 1 pages, ret: -16
Jan 22 04:00:20 raspberrypi kernel: [222019.662578] cma: cma_alloc: alloc failed, req-size: 1 pages, ret: -16
Jan 22 08:58:54 raspberrypi kernel: [239933.801494] cma: cma_alloc: alloc failed, req-size: 1 pages, ret: -16
Jan 22 12:00:19 raspberrypi kernel: [250819.417679] cma: cma_alloc: alloc failed, req-size: 1 pages, ret: -16
Jan 22 16:00:20 raspberrypi kernel: [265219.816146] cma: cma_alloc: alloc failed, req-size: 1 pages, ret: -16
Jan 22 20:00:20 raspberrypi kernel: [279620.458547] cma: cma_alloc: alloc failed, req-size: 1 pages, ret: -16
Jan 23 00:00:20 raspberrypi kernel: [294020.066337] cma: cma_alloc: alloc failed, req-size: 1 pages, ret: -16
Jan 23 04:00:20 raspberrypi kernel: [308420.471800] cma: cma_alloc: alloc failed, req-size: 1 pages, ret: -16
Jan 23 08:00:20 raspberrypi kernel: [322820.903216] cma: cma_alloc: alloc failed, req-size: 1 pages, ret: -16
Jan 23 12:00:20 raspberrypi kernel: [337220.452950] cma: cma_alloc: alloc failed, req-size: 1 pages, ret: -16
Jan 23 13:13:24 raspberrypi kernel: [ 0.000000] Booting Linux on physical CPU 0x0
╚═════════════════════════════════════════╝
A l'heure actuelle, je ne trouve aucune solution mise à part un reboot du système
Pas térrible pour la sdcards sur la longueur. Nous allons faire ce reboot via crontab.
# nano /opt/reco/reboot.sh
# chmod +x reboot.sh
╔═════════════════════════════╗
#!/usr/bin/env bash
screen -X -S recofacial kill
sleep 20
/sbin/shutdown -r now
╚═════════════════════════════╝
# crontab -e (reboot miniuit tous les jours)
╔════════════════════════════════════════════╗
0 0 * * * /opt/reco/reboot.sh >/dev/null 2>&1
╚════════════════════════════════════════════╝
𝐞𝐝𝐢𝐭 𝐣𝐮𝐢𝐥𝐥𝐞𝐭 𝟐𝟎𝟏𝟗 : 3 fois sur 5 nous avons cette erreur au reboot de la Pi
╔════════════════════════════════════╗
root@raspberrypi:/opt/reco# python3 reco.py --cpus 4
Traceback (most recent call last):
File "reco.py", line 3, in <module>
import face_recognition
File "/usr/local/lib/python3.5/dist-packages/face_recognition/__init__.py", line 7, in <module>
from .api import load_image_file, face_locations, batch_face_locations, face_landmarks, face_encodings, compare_faces, face_distance
File "/usr/local/lib/python3.5/dist-packages/face_recognition/api.py", line 17, in <module>
pose_predictor_68_point = dlib.shape_predictor(predictor_68_point_model)
RuntimeError: Error deserializing object of type short
while deserializing a floating point number.
while deserializing a dlib::matrix
while deserializing object of type std::vector
while deserializing object of type std::vector
while deserializing object of type std::vector
root@raspberrypi:/opt/reco#
╚═════════════════════════════════════╝
ce problème pourrait etre du à la carte micro SD, corruption?
pour pallier le problème, il faut reboot jusqu'a ce que le systeme (carte sd?) soit ok
et donc le programme pourra être lancé!
la solution: arreter de reboot toute les nuits. mettre un reboot du prog sur systemd
=============
Restart=always
RuntimeMaxSec=604800
=============
𝗲𝗱𝗶𝘁 : Rigolo.. Adrian Rosebrock recommande aussi
d'utiliser un script bash puis d'appeller un script python
████ 𝗢𝗨𝗩𝗘𝗥𝗧𝗨𝗥𝗘 𝗣𝗢𝗥𝗧𝗘
█ 𝐬𝐬𝐡 𝐩𝐚𝐬𝐬𝐰𝐨𝐫𝐝𝐥𝐞𝐬𝐬
(Depuis rpi recofacial)
# ssh-keygen
Enter passphrase (empty for no passphrase): on laisse vide
# ssh-copy-id user@ip_relai
normalement nous pouvons être log passwordless
# ssh user@ip_relai
source : https://askubuntu.com/questions/46930/how-can-i-set-up-password-less-ssh-login
█ 𝐬𝐜𝐫𝐢𝐩𝐭 𝐩𝐲𝐭𝐡𝐨𝐧 𝐫𝐞𝐜𝐨.𝐩𝐲
Pas besoin d'utiliser de librairies comme "paramiko"
juste quelques lignes suffisent avec subprocess
╔════════════════════════════════════════════════════╗
import subprocess
import sys
...
HOST="pi@192.168.1.24"
COMMAND="sudo python relay.py"
...
ssh = subprocess.Popen(["ssh", "%s" % HOST, COMMAND])
╚═════════════════════════════════════════════════════╝
voir : hg commit n° 13 et 15"
source : https://stackoverflow.com/questions/28411960/execute-a-command-on-remote-machine-in-python/28413657#28413657
sinon pour paramiko (mais on l'utilise pas!) :
* https://github.com/paramiko/paramiko
* https://blog.ruanbekker.com/blog/2018/04/23/using-paramiko-module-in-python-to-execute-remote-bash-commands/
* https://stackoverflow.com/questions/3586106/perform-commands-over-ssh-with-python (best!)
# pip3 install paramiko
█ 𝐏𝐢 𝐑𝐞𝐥𝐚𝐲
voir guide install basics rpi puis,
$ cd ~
$ nano relay.py
╔══════════════════════════╗
import time
import RPi.GPIO as GPIO
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.OUT)
GPIO.output(17, GPIO.LOW)
time.sleep(4)
GPIO.output(17, GPIO.HIGH)
GPIO.cleanup()
╚══════════════════════════╝
source : https://github.com/teroknor01/relay_pi/blob/master/relay.py
$ chmod +x relay.py
➤ GPIO.setwarnings(False)
on désactive log warning à cause de
/home/pi/relay.py:7: RuntimeWarning: This channel is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.
car le trigger s'active hyper rapidement avec la boucle for
➤ time.sleep(4)
4 secondes c'est le temps durant lequel on resterait appuyer
sur l'interrupteur de la porte
connection PIN photos
█ 𝐑𝐞𝐛𝐨𝐨𝐭 𝐏𝐢 𝐑𝐞𝐥𝐚𝐲
Etrange après plusieurs jours
le temps de latence augmente sur la commande SSH
un reboot daily est nécessaire
$ sudo nano /home/pi/reboot.sh
╔═════════════════════════════╗
#!/usr/bin/env bash
sudo /sbin/shutdown -r now
╚═════════════════════════════╝
$ sudo chmod +x reboot.sh
$ crontab -e (reboot miniuit tous les jours)
╔════════════════════════════════════════════╗
0 0 * * * /home/pi/reboot.sh >/dev/null 2>&1
╚════════════════════════════════════════════╝
░▄█▀▀███▀▀▀▀▀▀▀▀██▄░░░██░░░░█▀░▄█▀░▄▄▀▀▀▀▀▀█▄░▀█
𝐃𝐔𝐏𝐋𝐈𝐐𝐔𝐄𝐑 𝐋𝐀 𝐒𝐎𝐑𝐓𝐈𝐄 𝐇𝐃𝐌𝐈
By mirroring /dev/fb0 onto /dev/fb1, we can take advantage of the GPU for hardware accelrated video playback.
fbcp takes a snapshot of /dev/fb0, copies it to /dev/fb1 and waits 25ms before repeating.
Snapshotting takes ~10ms and with a 25ms delay it gives roughly 1000/(10+25) = 28fps
CPU usage: ~2% Note: Snapshot and /dev/fb1 driver refresh is not syncronized.
If you have a TFT display , you must use omxplayer and fbcp together
fbcp has to run in the background.
Il est impossible out of the box d'utiliser omxplayer.
omxplayer utilise le fb0 (hdmi) et rien d'autre..
la seule solution c'est de copier le flux du fb0 vers fb1
-----------
𝙀𝙁𝙁𝘼𝘾𝙀𝙍 𝙇𝙀 𝙏𝙏𝙔 (commun aux 2 méthodes ci-dessous)
mettre un delay pour l'execution du script dans la crontab
pour être certain que le shell soit terminer à 100%
puis mettre de couleur noir le shell :p black is black.
# cd ~
# nano clear.sh
# chmod +x clear.sh
╔═════════════════════════════════════════════════╗
#!/usr/bin/env bash
TERM=linux setterm -foreground black -clear all >/dev/tty1
# la commande dd ci-dessous fonctionne, mais me semble un poil aggressif?
# dd if=/dev/zero of=/dev/fb0
╚═════════════════════════════════════════════════╝
# crontab -e
╔════════════════════════════════════════════╗
@reboot sleep 120 && /root/clear.sh
╚════════════════════════════════════════════╝
https://raspberrypi.stackexchange.com/questions/3268/how-to-disable-local-terminal-showing-through-when-playing-video/3269#3269
██████ 𝗙𝗕𝗜 (𝗳𝗿𝗮𝗺𝗲 𝗯𝘂𝗳𝗳𝗲𝗿 𝗶𝗺𝗮𝗴𝗲 𝘃𝗶𝗲𝘄𝗲𝗿)
𝗙𝗕 (𝗳𝗿𝗮𝗺𝗲 𝗯𝘂𝗳𝗳𝗲𝗿)
fb0 = HDMI
fb1 = LCD
On check si fb1 est là
# ls /dev/fb*
𝙢𝙚𝙢𝙤𝙞𝙧𝙚
Je sais pas si c'est l'utilisation du LCD, mais le script crash
pour de la mémoire avec le message suivant :
"picamera.exc.PiCameraMMALError: Failed to enable connection: Out of resources"
On augmente de 128MB à 256MB
# raspi-config
Advanced options -> Memory split -> and set at least 256MB
source : https://raspberrypi.stackexchange.com/questions/26829/picamera-not-working
𝙄𝙣𝙨𝙩𝙖𝙡𝙡𝙖𝙩𝙞𝙤𝙣
# apt install fbi
Creation dossier contenant les visages qui seront afficher sur le LCD
# mkdir /recofacial/reco/lcd
█ 𝘾𝙤𝙢𝙢𝙖𝙣𝙙𝙚 𝙁𝘽𝙄 𝙥𝙧𝙞𝙣𝙘𝙞𝙥𝙖𝙡𝙚
# fbi -T 1 -d /dev/fb1 -noverbose -t 5 -1 -once /opt/reco/lcd/{}.jpg
𝗘𝘅𝗽𝗹𝗶𝗰𝗮𝘁𝗶𝗼𝗻 𝗳𝗹𝗮𝗴𝘀:
-T 1 = console tty n°1 (nous avons 6 tty, switch entre-eux [Ctrl]+[Alt][F1-6])
-d = device sur framebuffer1 , le lcd
-t 5 -1 = durée affichage image 5 secondes et -1 termine le loop de FBI, donc écran noir
██████ 𝗢𝗠𝗫𝗣𝗟𝗔𝗬𝗘𝗥
omxplayer est un excellent sur unix, il est super léger
Creation dossier contenant les visages qui seront afficher sur le LCD
# mkdir /recofacial/reco/lcd
# apt install omxplayer
Puis dans le script python reco
os.system("omxplayer --orientation 270 /opt/reco/lcd/{}.mp4".format(name))
Je n'utilise pas le flag : -b
qui permet un black screen, vue que j'ai déjà black la console dans le boot
sa permet peut-etre d'économiser du CPU sur omxplayer.
░▄█▀▀███▀▀▀▀▀▀▀▀██▄░░░██░░░░░░░░░▄█▀▀███▀▀▀▀▀▀▀▀██▄░░░▄█▀░▄█▀░▄▄▀▀▀▀▀▀█▄░▀█▄▄██▄▄▄▄▄▄▄▄█▀
𝗖𝗥𝗘𝗔𝗧𝗜𝗢𝗡 𝗠𝗜𝗡𝗜 𝗩𝗜𝗗𝗘𝗢
𝗢𝗕𝗦 - 𝗰𝗮𝗽𝘁𝘂𝗿𝗲 𝗲𝗰𝗿𝗮𝗻 𝗽𝗰 (𝘀𝗶𝗺𝗶𝗹𝗮𝗶𝗿𝗲 𝗙𝗿𝗮𝗽𝘀)
Télécharger OBS
https://obsproject.com/fr
𝘃𝗶𝘀𝗮𝗴𝗲𝟯𝗱
Utiliser le site suivant pour faire un visage 3D
http://cvl-demos.cs.nott.ac.uk/vrn/index.php
puis filmer l'écran avec OBS
𝗦𝗢𝗡𝗬 𝗩𝗘𝗚𝗔𝗦
utiliser les templates "lcd"
dans les FX puis dans le render
░▄█▀▀███▀▀▀▀▀▀▀▀██▄░░░██░░░░░░░░░▄█▀▀███▀▀▀▀▀▀▀▀██▄░░░▄█▀░▄█▀░▄▄▀▀▀▀▀▀█▄░▀█▄▄██▄▄▄▄▄▄▄▄█▀
Nous pouvons creer un script (python, bash, etc..) qui porte simplement le nom de la personne :-)
Pleins d'utilisation !
os.system("mon logiciel /opt/reco/lcd/{}.mp4".format(name))
os.system("mon logiciel /opt/reco/lcd/{}.mp3".format(name))
os.system("mon logiciel /opt/reco/lcd/{}.txt".format(name))
os.system("mon logiciel /opt/reco/lcd/{}.sh".format(name))
██████████████████████████████████████████████████████
𝗙𝗔𝗨𝗫 𝗣𝗢𝗦𝗜𝗧𝗜𝗙
𝙉𝙞𝙘𝙤𝙡𝙖𝙨: Comme l'indique Adrian, il s'agit un réseaux neurones
pré entainer sur des millions de visages (majoritairement blanc)
pour calculer les 128D. Il recommande d'entrainer le réseaux neurones
sur nos propres visages pour éviter de faux positif.
𝙌𝙪𝙚𝙨𝙩𝙞𝙤𝙣 𝙩𝙤𝙢𝙢𝙮
Hi, Adrian.
Always thanks for your wonderful article.
I have tested your code for a week.
It was working for small dataset(1~2 people face).
But when I increased number of people(upto 10),
it looked unstable sometims.
𝙍𝙚𝙥𝙤𝙣𝙨𝙚
Once you start getting more and more people in your dataset
this method will start to fail. Keep in mind that we’re
leveraging a pre-trained network here to compute the 128-d
facial embeddings. Try instead fine-tuning the network itself
on the people you want to recognize to increase accuracy.
source : https://www.pyimagesearch.com/2018/09/24/opencv-face-recognition/#comment-485420
ajuster la tolérance pour éviter les faux positives
https://github.com/ageitgey/face_recognition/wiki/Face-Recognition-Accuracy-Problems
██████████████████████████████████████████
𝗧𝗢𝗗𝗢
➤ 𝐃𝐨𝐮𝐛𝐥𝐞 𝐟𝐚𝐜𝐭𝐞𝐮𝐫 (new)
" pour de la double auth,
pourquoi ne pas utiliser le probe request wifi, ou du bluetooth
regarder mes projets probe request python "
rpi zéro bluetooth. smartphone auto connect bluetooth (check nouveau device connected) ouvre la porte
https://towardsdatascience.com/real-time-face-liveness-detection-with-python-keras-and-opencv-c35dc70dafd3
https://www.youtube.com/watch?v=arQN6w0fZw8&feature=youtu.be
https://www.pyimagesearch.com/2019/03/11/liveness-detection-with-opencv
https://github.com/ageitgey/face_recognition/issues/371
➤𝐃𝐚𝐭𝐚𝐬𝐞𝐭𝐬 𝐟𝐨𝐫 𝐦𝐚𝐜𝐡𝐢𝐧𝐞 𝐥𝐞𝐚𝐫𝐧𝐢𝐧𝐠
https://www.datasetlist.com/
➤𝗔𝘂𝘁𝗼𝗺𝗮𝘁𝗶𝗰 𝗠𝗮𝗰𝗵𝗶𝗻𝗲 𝗟𝗲𝗮𝗿𝗻𝗶𝗻𝗴
https://www.makeml.app/?ref=producthunt
➤𝗚𝗔𝗖𝗛𝗘 𝗘𝗟𝗘𝗖𝗧𝗥𝗜𝗤𝗨𝗘 (serrure electrique comme au bureau)
https://www.manomano.fr/serrure-electrique-2266
https://youtu.be/-dMSBzsO2Qw
➤𝗰𝗿𝗼𝗽
https://github.com/theidentity/Face-Tracker/blob/master/detect_faces.py#L11
https://github.com/ageitgey/face_recognition/blob/master/examples/blur_faces_on_webcam.py#L35
➤𝗚𝗲𝘁 𝗻𝗮𝗺𝗲𝘀 𝗳𝗿𝗼𝗺 𝗻𝗮𝗺𝗲 𝗳𝗼𝗹𝗱𝗲𝗿 𝗶𝗻𝘀𝘁𝗲𝗮𝗱 𝗼𝗳 𝗳𝗶𝗹𝗲𝗻𝗮𝗺𝗲
https://github.com/ManishaNatarajan/Facial-Recognition/blob/master/get_encodings.py
https://github.com/ageitgey/face_recognition/issues/599#issuecomment-417850005
https://github.com/jrosebr1/imutils
➤𝗨𝘁𝗶𝗹𝗶𝘀𝗲𝗿 𝗦𝗤𝗟 𝗽𝗼𝘂𝗿 𝘀𝘁𝗼𝗰𝗸𝗲𝗿 𝗹𝗲𝘀 𝟭𝟮𝟴𝗗
https://github.com/Nakroma/facial_recognition_system
https://github.com/ageitgey/face_recognition/issues/403#issuecomment-376136244
https://github.com/vearutop/face-postgre
https://github.com/d-demirci/face-postgre
➤𝗚𝗨𝗜
https://github.com/TheDogeOfTheInternet/ModFaceMatch
██████████████████████████████████████
𝗟𝗘𝗖𝗧𝗨𝗥𝗘𝗦 / 𝗦𝗢𝗨𝗥𝗖𝗘𝗦
➤𝐈𝐧𝐬𝐭𝐚𝐥𝐥𝐚𝐭𝐢𝐨𝐧
https://robotzero.one/face-recognition-party-greeter-raspberry-pi/
https://gist.github.com/ageitgey/1ac8dbe8572f3f533df6269dab35df65
➤𝐃𝐢𝐯𝐞𝐫𝐬
https://twitter.com/techinsider/status/1114222166713741312?s=21
https://blog.csdn.net/qq_30460905/article/details/80864081
https://github.com/ageitgey/face_recognition/issues/501#issuecomment-395716780
https://twitter.com/shirokunet/status/1174322070265556993?s=12
➤𝗖𝗼𝗺𝗺𝗲𝗻𝘁 𝗗𝗟𝗜𝗕 𝗲𝘅𝘁𝗿𝗮𝗶𝘁 𝗹𝗲𝘀 𝟭𝟮𝟴 𝗱𝗶𝗺𝗲𝗻𝘀𝗶𝗼𝗻𝘀
http://dlib.net/dnn_metric_learning_on_images_ex.cpp.html