
Вот и финал этой серии статей (ссылки на предыдущие части — в конце этого материала), в которой мы создавали в браузере фильтры в стиле Snapchat, обучая модель ИИ понимать выражения лиц и добились ещё большего, используя библиотеку Tensorflow.js и отслеживание лиц.
Было бы здорово закончить, реализовав обнаружение движения на лицах? Позвольте показать, как по ключевым точкам лица определять, когда мы открываем рот и моргаем глазами, чтобы активировать события, происходящие на экране.
Вы можете загрузить демоверсию этого проекта. Для обеспечения необходимой производительности может потребоваться включить в веб-браузере поддержку интерфейса WebGL. Вы также можете загрузить код и файлы для этой серии. Предполагается, что вы знакомы с JavaScript и HTML и имеете хотя бы базовое представление о нейронных сетях.
Обнаружение моргания глаз и открывания рта
Мы собираемся использовать ключевые точки лица, предоставляемые кодом отслеживания лиц, который мы разработали в первой статье этой серии, а также отслеживание лица в реальном времени, чтобы обнаружить моргание глаз и открывание рта.
Аннотированные точки лица дают достаточно информации, чтобы определить, когда глаза закрыты и когда открыт рот. Хитрость заключается в том, чтобы масштабировать положения с учетом относительного размера в анфас.
Для этого мы можем обратиться к удобному расстоянию между глазами, чтобы аппроксимировать относительную шкалу в функции trackFace:
async function trackFace() {
...
faces.forEach( face => {
const eyeDist = Math.sqrt(
( face.annotations.leftEyeUpper1[ 3 ][ 0 ] - face.annotations.rightEyeUpper1[ 3 ][ 0 ] ) ** 2 +
( face.annotations.leftEyeUpper1[ 3 ][ 1 ] - face.annotations.rightEyeUpper1[ 3 ][ 1 ] ) ** 2 +
( face.annotations.leftEyeUpper1[ 3 ][ 2 ] - face.annotations.rightEyeUpper1[ 3 ][ 2 ] ) ** 2
);
const faceScale = eyeDist / 80;
});
requestAnimationFrame( trackFace );
}
Затем мы можем вычислить расстояние между верхней и нижней частью как левого, так и правого глаза и использовать значение faceScale для оценки момента пересечения порога. Мы можем использовать аналогичный расчёт для обнаружения открывания рта.
Взгляните:
async function trackFace() {
...
let areEyesClosed = false, isMouthOpen = false;
faces.forEach( face => {
...
// Check for eyes closed
const leftEyesDist = Math.sqrt(
( face.annotations.leftEyeLower1[ 4 ][ 0 ] - face.annotations.leftEyeUpper1[ 4 ][ 0 ] ) ** 2 +
( face.annotations.leftEyeLower1[ 4 ][ 1 ] - face.annotations.leftEyeUpper1[ 4 ][ 1 ] ) ** 2 +
( face.annotations.leftEyeLower1[ 4 ][ 2 ] - face.annotations.leftEyeUpper1[ 4 ][ 2 ] ) ** 2
);
const rightEyesDist = Math.sqrt(
( face.annotations.rightEyeLower1[ 4 ][ 0 ] - face.annotations.rightEyeUpper1[ 4 ][ 0 ] ) ** 2 +
( face.annotations.rightEyeLower1[ 4 ][ 1 ] - face.annotations.rightEyeUpper1[ 4 ][ 1 ] ) ** 2 +
( face.annotations.rightEyeLower1[ 4 ][ 2 ] - face.annotations.rightEyeUpper1[ 4 ][ 2 ] ) ** 2
);
if( leftEyesDist / faceScale < 23.5 ) {
areEyesClosed = true;
}
if( rightEyesDist / faceScale < 23.5 ) {
areEyesClosed = true;
}
// Check for mouth open
const lipsDist = Math.sqrt(
( face.annotations.lipsLowerInner[ 5 ][ 0 ] - face.annotations.lipsUpperInner[ 5 ][ 0 ] ) ** 2 +
( face.annotations.lipsLowerInner[ 5 ][ 1 ] - face.annotations.lipsUpperInner[ 5 ][ 1 ] ) ** 2 +
( face.annotations.lipsLowerInner[ 5 ][ 2 ] - face.annotations.lipsUpperInner[ 5 ][ 2 ] ) ** 2
);
// Scale to the relative face size
if( lipsDist / faceScale > 20 ) {
isMouthOpen = true;
}
});
setText( `Eyes: ${areEyesClosed} Mouth: ${isMouthOpen}` );
requestAnimationFrame( trackFace );
}
Теперь мы готовы к обнаружению некоторых движений на лицах.
Время вечеринки с конфетти
На каждом празднике требуется конфетти, верно? Мы собираемся соединить виртуальное конфетти с моргающими глазами и открывающимся ртом, чтобы получилась настоящая вечеринка.
Для этого мы будем использовать библиотеку JavaScript с открытым исходным кодом, которая называется Party-JS. Включите её в верхней части своей страницы следующим образом:
<script src="https://cdn.jsdelivr.net/npm/party-js@1.0.0/party.min.js"></script>
Давайте зададим глобальную переменную, по состоянию которой будем отслеживать запуск конфетти.
let didParty = false;
И последнее, но не менее важное: мы можем включать анимацию вечеринки, когда мы моргаем или открываем рот.
async function trackFace() {
...
if( !didParty && ( areEyesClosed || isMouthOpen ) ) {
party.screen();
}
didParty = areEyesClosed || isMouthOpen;
requestAnimationFrame( trackFace );
}
А теперь время для вечеринки! Используя возможности отслеживания лиц и конфетти, вы запускаете вечеринку на экране по движению своих губ.

Этот проект не закончен без полного кода, на который вы могли бы взглянуть. Поэтому вот он:
Простыня с кодом
Что дальше?
Собственно, на этом пока всё. В этой серии статей мы научились применять ИИ к лицам, чтобы отслеживать их в режиме реального времени, а также определять эмоции на лице и движения рта и глаз. Мы даже создали с нуля собственную игру с дополненной реальностью и виртуальными очками, и всё это работает в веб-браузере.
Хотя мы выбрали для применения забавные примеры, для этой технологии также существует множество приложений в бизнесе. Представьте продавца очков, который хочет позволить посетителям своего веб-сайта выбирать очки, примеряя их. Нетрудно представить, как вы будете использовать знания, приобретённые в этой серии статей, для создания нужных функциональных возможностей. Надеюсь, теперь у вас есть инструменты для создания более полезных решений с использованием ИИ и TensorFlow.js.
Попробуйте реализовать конфетти в проекте виртуальных очков. Проверьте, сможете ли вы применить обнаружение эмоций к фотоальбому.
И если эти серии статей вдохновят вас на создание ещё более крутых проектов, поделитесь ими в комментариях! Мы будем рады узнать о ваших проектах.
Удачи и удовольствия от программирования!
Узнайте подробности, как получить Level Up по навыкам и зарплате или востребованную профессию с нуля, пройдя онлайн-курсы SkillFactory со скидкой 40% и промокодом HABR, который даст еще +10% скидки на обучение.
Другие профессии и курсы
ПРОФЕССИИ
КУРСЫ