Не смотри в камеру! пишем свою систему распознавания лиц на python.

Tartuga

Бывалый
ПРОВЕРЕННЫЙ ПРОДАВЕЦ
PREMIUM USER

Tartuga

Бывалый
ПРОВЕРЕННЫЙ ПРОДАВЕЦ
PREMIUM USER
Регистрация
7 Фев 2020
Сообщения
525
Реакции
98
Репутация
147
В Гонконге протестующие уничтожают уличные камеры, чтобы избежать распознавания лиц. Неужели эта технология уже настолько эффективна, что с ней нужно бороться? Я купил камеру с датчиком глубины, чтобы попробовать самостоятельно реализовать алгоритм сравнения лиц. Вот что у меня получилось.

Зачем нужна аутентификация по лицу
Существует несколько вариантов аутентификации с помощью биометрии, и каждый из них имеет свои недостатки.

  1. Идентификация по отпечатку пальца. Главный недостаток идентификации по отпечатку пальца — сравнительно небольшая точность. Отпечаток пальца легко подделать, особенно если сканер слабый.
  2. Отпечаток голоса. Наш голос можно разбить на множество уникальных характеристик. Но и этот метод весьма неточен: из-за болезни или курения уникальные характеристики голоса могут измениться, и твой девайс тебя уже не узнает.
  3. Сканеры сетчатки и радужки глаза. Они используют сложные алгоритмы и дорогостоящие громоздкие приборы для сканирования. В среднем надежность таких аппаратов выше, чем у более простых в реализации устройств. Но и стоимость таких сканеров соответствующая: базовые модели продаются по цене от пятнадцати тысяч рублей.
  4. Идентификация по трехмерному изображению лица. Этот метод становится все популярнее из-за выгодного соотношения цены и качества. 3D-камеры работают в разном диапазоне дальности, поэтому системы распознавания и идентификации лица могут быть незаметными и не требовать никаких действий от человека. Точность анализа поверхности лица высока, так что все зависит только от разрешения камеры.
Камера
Я выбрал недорогую, но при этом функциональную камеру Intel RealSense SR305, которая может снимать и цветное изображение, и глубинное изображение в разрешении 640 на 480 пикселей с частотой до 60 кадров в секунду.

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


Так выглядит камера, установленная на треногу

Рабочее расстояние камеры небольшое: излучатель расположен так, что объекты, которые находятся ближе двадцати сантиметров, не будут освещены и, соответственно, не будут просканированы. Слишком далеко расположенные предметы — дальше двух метров — тоже окажутся не видны, поскольку мощность лазера не позволит спроецировать на них инфракрасную сетку.

В комплекте с камерой идет провод USB и инструкция со ссылкой на официальный SDK в

Пожалуйста Авторизуйтесь или Зарегистрируйтесь для просмотра скрытого текста.

.


Установка SDK
В первую очередь нужно установить программное обеспечение, чтобы камера заработала. Пользователи Windows могут просто скачать и установить

Пожалуйста Авторизуйтесь или Зарегистрируйтесь для просмотра скрытого текста.

. Если же у тебя Linux, но не Ubuntu 16 или 18, то придется собирать проект самостоятельно.

Для начала скачаем исходный код и подготовим площадку для сборки.

$ git clone

Пожалуйста Авторизуйтесь или Зарегистрируйтесь для просмотра скрытого текста.


$ cd librealsense
$ mkdir build && cd build
Теперь можно собрать проект, чтобы посмотреть, как работает камера.

$ cmake .. -DBUILD_EXAMPLES=true -DBUILD_WITH_OPENMP=false -DHWM_OVER_XU=false
$ make -j4
$ make install
Запуск в macOS
Если ты пользователь macOS, для запуска графических программ тебе придется использовать Xcode и соответствующий флаг при конфигурации. Так ты сможешь сгенерировать проект Xcode, чтобы запустить каждую утилиту по отдельности.

$ cmake .. -DBUILD_EXAMPLES=true -DBUILD_WITH_OPENMP=false -DHWM_OVER_XU=false -G Xcode
$ open librealsense2.xcodeproj
Откроется окно Xcode. Выбери необходимую программу, собери проект комбинацией клавиш Command + B и нажми кнопку Build and run.


Окно Xcode с файлами сборки для macOS

Для пробного запуска нам понадобится утилита RealSense Viewer, которая показывает на экране, что видит камера.


Таким будет окно RealSense Viewer, если подключить камеру


Примерно так выглядит трансляция глубины

Подключаем Python
Мы попробуем написать свою программу для идентификации по трехмерному изображению лица. И для начала нам нужно подключить библиотеку RealSense, например к Python 3. Пользователям Windows и некоторых дистрибутивов Linux не придется напрягаться — можно взять официальный пакет pyrealsense2 в PyPI.

$ pip install pyrealsense2
Остальных же ждет еще одно приключение: необходимо пересобрать весь проект, добавив во флаги враппер для Python.

$ cmake .. -DBUILD_EXAMPLES=true -DBUILD_WITH_OPENMP=false -DHWM_OVER_XU=false -DPYTHON_EXECUTABLE=/usr/bin/python3 -DBUILD_PYTHON_BINDINGS:bool=true
$ make -j4
В папке wrappers/python/ появятся два файла и четыре символические ссылки. Чтобы использовать эти файлы как библиотеку для Python, их необходимо скопировать в папку, из которой ты будешь запускать скрипты.


Так выглядят файлы модуля для Python 3.7

Давай подключим камеру, запустим сканирование и попробуем сделать трехмерную фотографию.

import pyrealsense2 as rs # Импортируем библиотеки
import numpy as np
import matplotlib.pyplot as plt

pipeline = rs.pipeline() # Создаем объект, который передает нам новые кадры
config = rs.config() # Получаем объект конфигурации
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
## Настраиваем получение кадров глубины в разрешении 640 на 480 и 30 раз в секунду

pipeline.start(config) # Запускаем поток
try:
frames = pipeline.wait_for_frames() # Ждем первого кадра
depth_frame = frames.get_depth_frame() # Получаем изображение глубины
depth_image = np.asanyarray(depth_frame.get_data()) # Преобразуем изображение в двумерный массив чисел
plt.imshow(depth_image, cmap='magma') # Рисуем изображение на экране
plt.show()
finally:
pipeline.stop() # Останавливаем камеру

Результат выполнения скрипта

Делаем трехмерные модели лица
Чтобы идентифицировать человека по лицу, нам нужно получать форму этого лица. Самый удобный для дальнейшей обработки трехмерной модели лица формат — облако точек.

Точки, которые образуют облако, — это своеобразные трехмерные пиксели: они показывают, где находится граница объекта в трехмерном пространстве.

Чтобы получить трехмерное облако, в библиотеке pyrealsense2 есть специальный класс pointcloud, который из кадра глубины создает массив из координат.

import pyrealsense2 as rs

pc = rs.pointcloud() # Настраиваем облако точек
pipeline = rs.pipeline()

config = rs.config()
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)

pipeline.start(config)

try:
frames = pipeline.wait_for_frames()

depth_frame = frames.get_depth_frame()
depth_intrinsics = depth_frame.profile.as_video_stream_profile().intrinsics
# Получаем внутренние характеристики кадра

w, h = depth_intrinsics.width, depth_intrinsics.height
# высоту и ширину кадра

points = pc.calculate(depth_frame) # Вычисляем положение точек в облаке
points.export_to_ply('cloud.ply', color_frame) # Сохраняем точки в файл
finally:
pipeline.stop()
Этот скрипт создает трехмерную фотографию, а мы сохраняем ее в формате PLY в виде массива всех координат.

Для визуализации нашего облака точек воспользуемся библиотекой open3d из PyPI.

import open3d as o3d
pcd = o3d.io.read_point_cloud("cloud.ply")
o3d.visualization.draw_geometries([pcd])

Так выглядит облако точек


А вот так — с другого ракурса

Можно заметить, что лицо отбрасывает «тень» на стену позади. Поэтому мы модифицируем наш скрипт: используем математику и выбросим все точки, которые лежат дальше 35 см от камеры.

import pyrealsense2 as rs
import numpy as np
import open3d as o3d

pc = rs.pointcloud()
pipeline = rs.pipeline()

config = rs.config()
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)

pipeline.start(config)

try:
frames = pipeline.wait_for_frames()

depth_frame = frames.get_depth_frame()
depth_intrinsics = depth_frame.profile.as_video_stream_profile().intrinsics
w, h = depth_intrinsics.width, depth_intrinsics.height

points = pc.calculate(depth_frame)

vts = np.asanyarray(points.get_vertices(2)) # Получаем массив координат точек
vts = vts[np.where(vts[:, 2] < 0.35)] # Убираем все, что дальше 35 см

pcd = o3d.geometry.PointCloud() # Создаем облако open3d
pcd.points = o3d.utility.Vector3dVector(vts) # Наполняем его точками
o3d.visualization.draw_geometries([pcd]) # Рисуем
o3d.io.write_point_cloud("cloud_filtered.ply", pcd) # Сохраняем в файл
finally:
pipeline.stop()

В итоге получается вот такое трехмерное лицо


С другого ракурса

Сравнение лиц
Для сравнения двух лиц мы выполним несколько шагов.

  1. Найдем «центр масс» для каждого облака, а затем совместим их.
  2. Для каждой точки из первого облака найдем ближайшую точку из второго облака и расстояние между ними просуммируем.
Попробуем написать такой алгоритм.

import open3d as o3d
import numpy as np

## Загрузим облака
pca = o3d.io.read_point_cloud("cloud_1.ply")
pcb = o3d.io.read_point_cloud("cloud_2.ply")

## Уберем все лишние данные (не считанные камерой точки [0, 0, 0])
points_a = np.asarray(pca.points)
points_a = points_a[points_a.nonzero()[0]]

points_b = np.asarray(pcb.points)
points_b = points_b[points_b.nonzero()[0]]

## Посчитаем центр каждого облака
center = center_a = np.mean(points_a.swapaxes(0, 1), axis=-1)
center_b = np.mean(points_b.swapaxes(0, 1), axis=-1)

center_diff = center_b - center_a

## Совместим центры облаков
points_b -= np.array([center_diff]).repeat(len(points_b), axis=0)

diff = 0

## Для каждой точки в первом облаке ищем ближайшую точку и сравниваем со вторым облаком
for point_a in points_a:
diff += (np.abs(points_b - point_a)).min()

print(diff)
Для разных лиц я получил результат в 0,289807649630209541, а для одинаковых — 0,056304771423558416. Разница значительная, а значит, идентификация весьма точная.


Два снимка лица, использовавшихся для сравнения

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

Сканирование лица — один из самых безопасных способов идентификации, поскольку подделать лицо другого человека целиком очень и очень сложно. К тому же большинство 3D-камер имеют встроенный цветовой сенсор, позволяющий анализировать и обычную цветную картинку одновременно с трехмерным изображением.
 
Сверху