7.3. Hello BlazeFace¶
BlazeFace – это нейронная сеть для обнаружения лиц из коллекции Google MediaPipe. Один вызов вывода возвращает ограничивающий прямоугольник вокруг каждого обнаруженного лица вместе с шестью лицевыми точками – правый глаз, левый глаз, нос, рот, правое ухо, левое ухо. Каждая OpenMV Cam, поставляемая с поддержкой нейронных сетей, несёт модель blazeface_front_128.tflite во флеш-памяти, поэтому запуск сквозного детектора лиц занимает несколько строк Python.
7.3.1. Полный скрипт¶
import csi
import ml
from ml.postprocessing.mediapipe import BlazeFace
csi0 = csi.CSI()
csi0.reset()
csi0.pixformat(csi.RGB565)
csi0.framesize(csi.VGA)
csi0.window((400, 400))
model = ml.Model("/rom/blazeface_front_128.tflite",
postprocess=BlazeFace(threshold=0.4))
while True:
img = csi0.snapshot()
for (x, y, w, h), score, keypoints in model.predict([img]):
img.draw_rectangle((x, y, w, h), color=(0, 255, 0))
ml.utils.draw_keypoints(img, keypoints, color=(255, 0, 0))
Это весь детектор лиц. Больше в нём ничего нет; скрипт захватывает кадр, передаёт его модели, проходит по возвращённому списку обнаружений и рисует ограничивающий прямоугольник каждого лица плюс шесть его точек обратно в кадр. Предпросмотр IDE показывает рамки и точки в реальном времени.
7.3.2. Что делает каждая строка¶
Первые три строки импортируют модули, нужные скрипту. csi – это интерфейс датчика камеры; ml – модуль машинного обучения, которому посвящена остальная часть этой главы; BlazeFace – это постпроцессор, который преобразует сырые выходные тензоры BlazeFace в список ограничивающих рамок и точек, по которому проходит скрипт.
Следующие пять строк настраивают датчик. Камера сбрасывается в известное состояние, переводится в цвет RGB565, устанавливается в разрешение VGA, а затем кадрируется в квадрат 400 на 400. Окно имеет значение: BlazeFace обучалась на квадратных обрезках, и подача ей квадратного входа согласует ожидаемое соотношение сторон сети с тем, что она видит в захваченном кадре.
Строка загрузки модели открывает файл модели:
model = ml.Model("/rom/blazeface_front_128.tflite",
postprocess=BlazeFace(threshold=0.4))
ml.Model читает файл по указанному пути – /rom/ – это файловая система во флеш-памяти, рассматриваемая позже – и возвращает объект модели, на котором скрипт будет выполнять выводы. Ключевое слово postprocess= регистрирует постпроцессор BlazeFace; без него predict возвращал бы сырые выходные тензоры сети, и приложению пришлось бы декодировать их вручную. С ним predict возвращает декодированный результат напрямую. Аргумент threshold=0.4 постпроцессора задаёт минимальную уверенность, которую сеть должна сообщить, прежде чем обнаружение будет сохранено; меньшие значения улавливают более слабые лица ценой большего числа ложных срабатываний.
Остальные четыре строки – это главный цикл. Каждый его проход захватывает один кадр и спрашивает модель, что она видит:
img = csi0.snapshot()
for (x, y, w, h), score, keypoints in model.predict([img]):
img.draw_rectangle((x, y, w, h), color=(0, 255, 0))
ml.utils.draw_keypoints(img, keypoints, color=(255, 0, 0))
predict() принимает список входов (здесь – одно захваченное изображение) и возвращает список кортежей обнаружений. Каждый кортеж содержит ограничивающий прямоугольник (x, y, w, h), уверенность score между нулём и единицей, и ndarray формы (6, 2) с координатами точек – правый глаз, левый глаз, нос, рот, правое ухо и левое ухо в этом порядке. Вызов рисования использует draw_rectangle() – тот же примитив, которым заканчивался каждый классический детектор в главе об изображениях – чтобы обвести лицо. ml.utils.draw_keypoints() – это небольшой помощник из утилит ml, который отмечает каждую ключевую точку крестиком в её позиции (x, y).
7.3.3. Что скрипт не говорит¶
Скрипт содержит семь исполняемых строк работы вывода после импортов и настройки датчика, но внутри этих семи строк происходит огромное количество арифметики. Захваченный кадр 400 на 400 в формате RGB565 становится квантованным 8-битным тензором 128 на 128, прежде чем достигнуть сети; сеть выполняет сотни операций над десятками тысяч весов; получившиеся тензоры оценок уверенности и смещений рамок становятся ранжированным списком неперекрывающихся ограничивающих рамок с присоединёнными точками, прежде чем predict вернёт результат. Каждое из этих преобразований – это то, чем приложение может управлять, если нужно, и несколько из них приходится настраивать для любой нестандартной модели.
Следующие четыре подраздела раскрывают эти преобразования. По порядку:
Модуль ml – что
ml.Modelпредоставляет после загрузки модели и где файл модели на самом деле находится на камере.Конвейер вывода – четыре этапа каждого вызова
predict().Движки вывода – пути CPU и NPU, выполняющие арифметику сети.
Декодирование вывода – постпроцессоры, которые преобразуют сырые выходные тензоры в обнаружения, по которым проходил этот скрипт.
К концу главы читатель сможет написать эквивалентный скрипт для модели, которая не поставлялась с камерой, декодировать тензор, для которого постпроцессора ещё не существует, и рассуждать о том, почему конкретная модель работает на 30 FPS на одной камере и на 3 FPS на другой.