В этом посте я покажу, как использовать MTCNN для извлечения лиц и функций из изображений или видео, и, наконец, как увеличить скорость в 100 раз.

Что такое MTCNN

MTCNN - это библиотека Python (pip), написанная пользователем Github ipacz, которая реализует статью Zhang, Kaipeng et al. «Совместное обнаружение и выравнивание лиц с использованием многозадачных каскадных сверточных сетей. Письма об обработке сигналов IEEE 23.10 (2016): 1499–1503. Crossref. Интернет ».

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

MTCNN довольно быстро работает на ЦП, хотя S3FD все еще быстрее работает на ГП, но это тема для другого поста.

В этом посте используется код из следующих двух источников, ознакомьтесь с ними, они тоже интересны:

Базовое использование MTCNN

Не стесняйтесь получить доступ ко всей записной книжке через:

Https://github.com/JustinGintage/mtcnn-face-extraction-eyes-mouth-nose-and-speeding-it-up

git clone https://github.com/JustinGuese/mtcnn-face-extraction-eyes-mouth-nose-and-speeding-it-up

К счастью, MTCNN доступен в виде пакета pip, что означает, что мы можем легко установить его, используя

pip install mtcnn

Теперь, переключившись на Python / Jupyter Notebook, мы можем проверить установку с помощью импорта и быстрой проверки:

import mtcnn
# print version
print(mtcnn.__version__)

После этого мы готовы загрузить тестовое изображение с помощью matplotlib imread function.

import matplotlib.pyplot as plt
# load image from file
filename = "glediston-bastos-ZtmmR9D_2tA-unsplash.jpg"
pixels = plt.imread(filename)
print("Shape of image/array:",pixels.shape)
imgplot = plt.imshow(pixels)
plt.show()

Теперь ваш результат будет выглядеть примерно так:

{'box': [1942, 716, 334, 415], 'confidence': 0.9999997615814209, 'keypoints': {'left_eye': (2053, 901), 'right_eye': (2205, 897), 'nose': (2139, 976), 'mouth_left': (2058, 1029), 'mouth_right': (2206, 1023)}}
{'box': [2084, 396, 37, 46], 'confidence': 0.9999206066131592, 'keypoints': {'left_eye': (2094, 414), 'right_eye': (2112, 414), 'nose': (2102, 426), 'mouth_left': (2095, 432), 'mouth_right': (2112, 431)}}
{'box': [1980, 381, 44, 59], 'confidence': 0.9998701810836792, 'keypoints': {'left_eye': (1997, 404), 'right_eye': (2019, 407), 'nose': (2010, 417), 'mouth_left': (1995, 425), 'mouth_right': (2015, 427)}}
{'box': [2039, 395, 39, 46], 'confidence': 0.9993435740470886, 'keypoints': {'left_eye': (2054, 409), 'right_eye': (2071, 415), 'nose': (2058, 422), 'mouth_left': (2048, 425), 'mouth_right': (2065, 431)}}

Что это говорит нам? Многие из них говорят сами за себя, но в основном он возвращает координаты или значения пикселей прямоугольника, в котором алгоритм MTCNN обнаружил лица. Значение «box» выше возвращает расположение всего лица, за которым следует уровень «достоверности».

Если вы хотите выполнять более сложные операции извлечения или алгоритмы, у вас также будет доступ к другим ориентирам лица, называемым «ключевыми точками». А именно модель MTCNN обнаружила также глаза, рот и нос!

Рисование рамки вокруг лиц

Чтобы продемонстрировать это еще лучше, нарисуем рамку вокруг лица с помощью matplotlib:

# draw an image with detected objects
def draw_facebox(filename, result_list):
# load the image
data = plt.imread(filename)
# plot the image
plt.imshow(data)
# get the context for drawing boxes
ax = plt.gca()
# plot each box
for result in result_list:
# get coordinates
x, y, width, height = result['box']
# create the shape
rect = plt.Rectangle((x, y), width, height, fill=False, color='green')
# draw the box
ax.add_patch(rect)
# show the plot
plt.show()
# filename = 'test1.jpg' # filename is defined above, otherwise uncomment
# load image from file
# pixels = plt.imread(filename) # defined above, otherwise uncomment
# detector is defined above, otherwise uncomment
#detector = mtcnn.MTCNN()
# detect faces in the image
faces = detector.detect_faces(pixels)
# display faces on the original image
draw_facebox(filename, faces)

Отображение глаз, рта и носа вокруг лиц

Теперь давайте посмотрим на вышеупомянутые «ключевые точки», которые вернула модель MTCNN.

Теперь мы будем использовать их для построения графика носа, рта и глаз.
Мы добавим следующий фрагмент кода в наш код выше:

# draw the dots
for key, value in result['keypoints'].items():
# create and draw dot
dot = plt.Circle(value, radius=20, color='orange')
ax.add_patch(dot)

Полный код сверху выглядит так:

# draw an image with detected objects
def draw_facebox(filename, result_list):
# load the image
data = plt.imread(filename)
# plot the image
plt.imshow(data)
# get the context for drawing boxes
ax = plt.gca()
# plot each box
for result in result_list:
# get coordinates
x, y, width, height = result['box']
# create the shape
rect = plt.Rectangle((x, y), width, height,fill=False, color='orange')
# draw the box
ax.add_patch(rect)
# draw the dots
for key, value in result['keypoints'].items():
# create and draw dot
dot = plt.Circle(value, radius=20, color='red')
ax.add_patch(dot)
# show the plot
plt.show()
# filename = 'test1.jpg' # filename is defined above, otherwise uncomment
# load image from file
# pixels = plt.imread(filename) # defined above, otherwise uncomment
# detector is defined above, otherwise uncomment
#detector = mtcnn.MTCNN()
# detect faces in the image
faces = detector.detect_faces(pixels)
# display faces on the original image
draw_facebox(filename, faces)

Расширенный MTCNN: Ускорьте его (\ ~ x100)!

А теперь перейдем к самому интересному. Если вы собираетесь обрабатывать миллионы изображений, вам нужно будет ускорить MTCNN, иначе вы либо заснете, либо ваш процессор сгорит до того, как это будет сделано.

Но о чем именно мы говорим? Если вы запускаете приведенный выше код, это займет около одной секунды, то есть мы будем обрабатывать около одного изображения в секунду. Если вы запускаете MTCNN на графическом процессоре и используете ускоренную версию, она обеспечивает около 60–100 изображений / кадров в секунду. Это увеличение до 100 раз!

Если вы, например, собираетесь извлечь все лица фильма, где вы будете извлекать 10 лиц в секунду (в одной секунде фильма в среднем около 24 кадров, поэтому каждый второй кадр) будет 10 * 60 (секунд) * 120 (минут) = 72000 кадров.

Это означает, что если обработка одного кадра занимает одну секунду, это займет 72000 * 1 (секунды) = 72000 с / 60 с = 1200 м = 20 часов.

В ускоренной версии MTCNN эта задача займет 72 000 (кадров) / 100 (кадров / сек) = 720 секунд = 12 минут!

Чтобы использовать MTCNN на графическом процессоре, вам необходимо настроить CUDA, cudnn, pytorch и так далее. Pytorch написал хорошее руководство по этой части.

После установки мы сделаем необходимый импорт следующим образом:

from facenet_pytorch import MTCNN
from PIL import Image
import torch
from imutils.video import FileVideoStream
import cv2
import time
import glob
from tqdm.notebook import tqdm
device = 'cuda' if torch.cuda.is_available() else 'cpu'
filenames = ["glediston-bastos-ZtmmR9D_2tA-unsplash.jpg","glediston-bastos-ZtmmR9D_2tA-unsplash.jpg"]

Посмотрите, как мы определили устройство в приведенном выше коде? Вы также сможете запускать все на ЦП, если не хотите или можете настроить CUDA.

Далее мы определим экстрактор:

# define our extractor
fast_mtcnn = FastMTCNN(
stride=4,
resize=0.5,
margin=14,
factor=0.6,
keep_all=True,
device=device
)

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

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

def run_detection(fast_mtcnn, filenames):
frames = []
frames_processed = 0
faces_detected = 0
batch_size = 60
start = time.time()
for filename in tqdm(filenames):
v_cap = FileVideoStream(filename).start()
v_len = int(v_cap.stream.get(cv2.CAP_PROP_FRAME_COUNT))
for j in range(v_len):
frame = v_cap.read()
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
frames.append(frame)
if len(frames) >= batch_size or j == v_len - 1:
faces = fast_mtcnn(frames)
frames_processed += len(frames)
faces_detected += len(faces)
frames = []
print(
f'Frames per second: {frames_processed / (time.time() - start):.3f},',
f'faces detected: {faces_detected}\r',
end=''
)
v_cap.stop()
run_detection(fast_mtcnn, filenames)

На изображении выше показан результат выполнения кода на NVIDIA Tesla P100, поэтому в зависимости от исходного материала, графического процессора и процессора производительность может быть лучше или хуже.

Эту историю также можно найти в моем блоге https://www.datafortress.cloud/blog/face-detection-using-mtcnn/.