Распознавание, хранение и поиск лиц в базе данных

В этой статье я максимально коротко и просто объясню принцип распознавания, хранения и поиска лиц в базе данных. В качестве примера будет использована библиотека Insightface и база данных PostgreSQL.

В интернете довольно много статей про распознавание лиц, но, чтобы понять, как внедрить в свой проект поиск лиц по базе данных, придется прочитать не одну статью. Поэтому я решил написать собственный материал и, надеюсь, он поможет людям сэкономить несколько часов, получив цельное представление о схеме устройства поиска лиц по базе данных всего лишь из одной статьи.

Для начала кратко пройдемся по всей цепочке действий, чтобы понять общую схему:

  1. Прогоняем фото с лицами через библиотеку insightface и получаем для каждого лица вектор (embedding)

  2. Полученный вектор записываем в базу данных

  3. Чтобы осуществлять поиск по лицу, сравниваем исходный embedding с теми, что хранятся в базе данных

А теперь пройдемся по каждому пункту более подробно.

Преобразование фото лица в вектор

Для начала устанавливаем библиотеку для распознавания лиц:

pip install insightface

Существует много других библиотек для распознавания лиц (к примеру, DeepFace), можете использовать любую библиотеку, принцип работы не изменится.

Далее прогоняем изображение через нейросеть:

from insightface.app import FaceAnalysis
import cv2
app = FaceAnalysis(name="buffalo_sc",providers=['CPUExecutionProvider'])
app.prepare(ctx_id=0, det_size=(256, 256))  #подготовка нейросети
img = cv2.imread("G:/pic.jpg") #считываем изображение
faces = app.get(img) #ищем лица на изображении и получаем информацию о них
for face in faces:
    print(face)

Hidden text

  • если нужна более высокая точность, распознавание пола, возраста, то используйте модель «buffalo_l»

  • можно использовать GPU вместо CPU (providers=«CUDAExecutionProvider»). Также придется установить библиотеку onnx и onnxruntime-gpu

На выходе в переменной face получаем:

35802613eae92a2ede560f0e1666496d.png

  • face.bbox — это область на картинке, где находится лицо

  • face.det_score — уверенность нейросети в полученных результатах

  • face.embedding — точка, или же вектор в 512-мерном пространстве, по которому далее можно сравнивать схожесть лиц

Для того, чтобы видеть, какие лица были найдены, можно использовать данный код:

x, y, x2, y2 = face.bbox #получаем границы лица
cropped = img[int(y):int(y2), int(x):int(x2)] #вырезаем лицо из изображения
cv2.imshow('image', cropped) #показываем лицо
cv2.waitKey(0)

Исходное изображение

Полученные лица. insihtface находят лица, даже если их часть скрыта маской

Полученные лица. insihtface находят лица, даже если их часть скрыта маской

Хранение векторов в базе данных

Есть множество вариантов хранения векторов, но, если лиц десятки тысяч, или даже миллионы, то без хорошей базы данных не обойтись. В этой статье, в качестве примера, я покажу как устроено хранение векторов в моем проекте (django + postgresql)

Для более простого хранения и поиска по векторам потребуется установить расширение для базы данных «pgvector» https://github.com/pgvector/pgvector, а также, установить python библиотеку pip install pgvector

После установки расширения можно создавать таблицу, в которой будем хранить полученные векторы.

Как это выглядит django models.py:

from pgvector.django import VectorField
class Faces(models.Model):
    id = models.AutoField(primary_key=True)
    embedding = VectorField(dimensions = 512,null=True)

Как это выглядит в pgAdmin:

fd1862d25e7b8cfbee529ff00c9af327.png

Как добавлять в базу данных новые лица в django:

Faces.objects.create(embedding=face.embedding)

После того, как у нас уже есть база данных с хранящимися в ней лицами, можно переходить к поиску.

Поиск по базе данных

Итак, лица у нас хранятся в виде векторов. Что же нам нужно сделать с этими векторами, чтобы найти лицо в базе данных? Самые распространенные варианты поиска — это нахождение расстояний между концами векторов (чем меньше расстояние, тем меньше отличаются лица), а также — нахождение косинуса между векторами.

Расстояние между концами векторов в 2-х мерном пространстве

Расстояние между концами векторов в 2-х мерном пространстве

В этой статье рассмотрим поиск через нахождение расстояний (евклидово расстояние). Выполняется оно так:

from pgvector.django import L2Distance
fbase = Faces.objects.alias(distance=L2Distance('embedding', face.embedding)).filter(distance__lt=22)

В этом куске кода мы ищем в базе данных векторы, расстояние до которых от исходного вектора не более 22. Если нужен более точный поиск, можно поставить число поменьше. Если точность не так важна, можно увеличить число. Также, расстояние зависит от размерности вектора, если у вас 128-мерные векторы, то расстояния там, скорее всего, будут поменьше. В общем, подбирайте точность поиска опытным путем исходя из ваших задач.

Если нам просто нужно посчитать расстояние между двумя векторами, то можно воспользоваться библиотекой numpy:

import numpy as np
distance = np.linalg.norm(embedding1 - embedding2)

В приведенном примере в таблице с лицами хранится только id. Можно хранить и любые другие данные, пол, возраст, можно связать id с данными из других таблиц. Тут все зависит от ваших задач.

Заключение

В данной статье мы разобрали общие принципы распознавания, хранения и поиска лиц в базе данных. Далее подбирайте базы данных, библиотеки, которые будут наиболее удобны и эффективны для работы в ваших фреймворках, для решения ваших задач.

© Habrahabr.ru