В первом за этот год выпуске (2016 R1, т. е. v8) пакета Intel® RealSense™ SDKреализован целый ряд усовершенствований, но меня больше всего заинтересовал интерфейс JavaScript, в частности насколько быстро удастся использовать данные трехмерной камеры для веб-игр. Боб Даффи (Bob Duffy) написал подробную статьюоб этом, но кое-что изменилось, а мне хотелось бы использовать как можно меньше кода, чтобы опробовать этот подход (это позволит применить тестовый код, предназначенный для проверки концепции, к более крупным системам).
В этой статье я расскажу о функциональности отслеживания лица для получения данных ограничительной рамки и точки носа для пользователя. Это дает возможность управления и анимации аватара в игре (если, к примеру, отслеживать движение носа для прицеливания, то голова пользователя действует в качестве джойстика; можно использовать движение головы, чтобы управлять игровым персонажем, это необычный интерфейс, благодаря которому игра станет интереснее). К счастью, в SDK предусмотрен набор заранее заданных алгоритмов, позволяющих упростить этот процесс.
Начало работы
Сначала необходимо подготовить следующее.
- Камера Intel® RealSense™: установите последнюю версию Intel® RealSense™ Depth Camera Manager (DCM)для вашей камеры.
- Intel RealSense SDK: загрузите последнюю версию SDK.
- Исполняемый модуль веб-приложений: загрузите и установите последнюю версию (в настоящее время это v8), затем перезапустите браузер.
- Расположение файла: для удобства скопируйте файл realsense.js из файловой структуры SDK на рабочий стол. При установке текущей версии в папку по умолчанию это C:\Program Files (x86)\Intel\RSSDK\framework\common\JavaScript\.
- Текстовый редактор: можно использовать практически любой редактор.
- Создание файла: создайте новый HTML-файл в том же месте, где находится файл realsense.js.
- Содержание: скопируйте приведенный ниже пример кода в созданный файл или (желательно) заново вводите его по мере того, как мы будем разбирать его в этой статье.
Пример кода
Для иллюстрации простоты поставленной задачи ниже приводится полный исходный код (исключая комментарий с отказом от гарантий).
<!doctype HTML><html><head><title>RS-JS Face Tracking</title><script type="text/javascript" src="https://autobahn.s3.amazonaws.com/autobahnjs/latest/autobahn.min.jgz"></script><script src="realsense.js"></script></head><body><canvas id="mainCanvas" width=600 height=400 style="border:1px solid black"></canvas><script> var can = document.getElementById("mainCanvas"); var ctx = can.getContext("2d"); ctx.textAlign = "center"; // Set text to be centered at draw point var scale = 500; // Scale nose point movement to significance var rsf = intel.realsense.face; var sm, fm, fc; // Sense Manager, Face Module, Face Config var onFaceData = function(sender,data) { if (data.faces.length>0) { //Ignore empty screens var face = data.faces[0]; // Use first face visible var rect = face.detection.boundingRect; // Get face bounding box var n = face.landmarks.points[29].world; // Get face landmark data var px = rect.x + rect.w/2 + n.x*scale; // Anchor to bounding box var py = rect.y + rect.h/2 - n.y*scale; // Invert y-axis shift ctx.clearRect(0,0,can.width,can.height); // Clear canvas each frame ctx.strokeRect(rect.x,rect.y,rect.w,rect.h); // Show bounding box ctx.fillText(Math.round(n.z*100),px,py); // draw z value at nose point } } intel.realsense.SenseManager.createInstance().then(function (instance){ sm = instance; return rsf.FaceModule.activate(sm); // Activate face module }).then(function (instance) { fm = instance; fm.onFrameProcessed = onFaceData; // Set face data handler return fm.createActiveConfiguration(); // Configure face tracking }).then(function (instance) { fc = instance; fc.detection.isEnabled = true; // Enable face detection return fc.applyChanges(); }).then(function (result) { fc.release() return sm.init(); // Sense manager initialization }).then(function (result) { return sm.streamFrames(); // Start Streaming }); window.onblur=function(){ // Pause face module when window loses focus if(fm != undefined){ fm.pause(true); } } window.onfocus=function(){ // Unpause face module when window regains focus if(fm != undefined){ sm.captureManager.device.restorePropertiesUponFocus(); fm.pause(false); } } window.beforeunload = function(){ // Release sense manager on window close if(sm != undefined){ sm.release().then(function(){ sm=fm=undefined; }); } } </script></body></html>
В зависимости от вашего опыта и уровня подготовки показанный выше код может оказаться простым и удобочитаемым либо, напротив, покажется сложнейшим нагромождением программного кода. Если вы не испытываете затруднений с чтением этого кода, то приступайте к работе и наслаждайтесь; оставшаяся часть этой статьи посвящена разбору приведенного ниже кода.
Разделение по функциям
Раздел HTML
<!doctype HTML><html><head><title>RS-JS Face Tracking</title><script type="text/javascript" src="https://autobahn.s3.amazonaws.com/autobahnjs/latest/autobahn.min.jgz"></script><script src="realsense.js"></script></head><body><canvas id="mainCanvas" width=600 height=400 style="border:1px solid black"></canvas><script>… </script></body></html>
Это в буквальном смысле весь код HTML, находящийся на странице. Добавление сценария autobahn крайне важно для выполнения технической работы, благодаря которой SDK получает возможность работать с камерой из браузера посредством другого добавленного файла (realsense.js), скопированного ранее.
Единственный настоящий элемент, отображаемый на этой странице, — canvas, оно здесь обозначено произвольным образом для отображения данных по мере их получения. Дополнительные сведения об основах этого подхода см. на сайте IDZ.
Оставшаяся часть кода попадает в третий тег script. В идеале следовало бы разместить ее в заголовке, но она находится в конце для упрощения некоторых процедур загрузки. Примечание. Это (как и приведенная выше ссылка на IDZ) — еще один пример JavaScript в стиле хакатона, код по своему устройству пригоден только для демонстрации. Доработав его для выполнения ваших задач, сохраните функциональные фрагменты в более масштабируемой и подходящей структуре.
Переменные и использование
var can = document.getElementById("mainCanvas"); var ctx = can.getContext("2d"); ctx.textAlign = "center"; // Выравнивание текста по центру относительно точки рисования var scale = 500; // Масштабирование движения точки носа var rsf = intel.realsense.face; var sm, fm, fc; // Sense Manager, модуль Face, настройка лица var onFaceData = function(sender,data) { … }
Первые несколько строк — подготовка к нашему конкретному примеру, где мы используем вышеупомянутое полотно. Переменная scale включена произвольно, чтобы сделать перемещение точки носа пользователя более заметным.
Еще одна необязательная, но предпочтительная договоренность: использование кратких имен переменных помогает поддерживать компактность кода и избежать его усложнения из-за длинных строк с пространствами имен и атрибутами.
Весь наш измененный код находится в функции onFaceData. Мы передаем эту функцию в SDK, чтобы она знала, как мы хотим обрабатывать данные, поэтому мы называем ее «обработчиком». Мы доберемся до ее содержания чуть позже, но сначала нужно добиться отправки данных камерой.
Инициализация камеры Intel® RealSense™
intel.realsense.SenseManager.createInstance().then(function (instance){ sm = instance; return rsf.FaceModule.activate(sm); // Включение модуля Face }).then(function (instance) { fm = instance; fm.onFrameProcessed = onFaceData; // Задание обработчика данных лица return fm.createActiveConfiguration(); // Настройка отслеживания лица }).then(function (instance) { fc = instance; fc.detection.isEnabled = true; // Включение обнаружения лица return fc.applyChanges(); }).then(function (result) { fc.release(); return sm.init(); // Инициализация Sense Manager }).then(function (result) { return sm.streamFrames(); // Запуск передачи });
На первый взгляд, тут царит хаос, но на самом деле это просто ряд последовательных вызовов функций.
- Создайте Sense Manager, сохраненный как sm.
- С помощью sm включите модуль Face, сохраненный в fm.
- Задайте наш обработчик данных и используйте fm, чтобы создать активную конфигурацию лица fc.
- Включите обнаружение лиц в fc, затем высвободите его (поскольку больше не требуется вносить изменения в настройку).
- Теперь можно инициализировать sm и приступить к записи данных.
Вот и весь процесс инициализации. Для других модулей используется аналогичный подход.
Обработка камеры
window.onblur=function(){ // Приостановка модуля Face, когда окно теряет фокус ввода if(fm != undefined){ fm.pause(true); } } window.onfocus=function(){ // Возобновление работы модуля Face, когда окно снова получает фокус ввода if(fm != undefined){ sm.captureManager.device.restorePropertiesUponFocus(); fm.pause(false); } } window.beforeunload = function(){ // Высвобождение Sense Manager при закрытии окна if(sm != undefined){ sm.release().then(function(){ sm=fm=undefined; }); } }
Эти дополнительные функции нужны нам, чтобы останавливать камеру, когда она не используется. В частности, нужно приостановить модуль, когда окно теряет фокус ввода, возобновить работу модуля при повторном получении фокуса и высвободить ресурсы после закрытия окна. Оставив за кадром стандартный код, можно перейти к нашему собственному коду.
Обработка данных
Пакет SDK пытается распознать реперные точки на обнаруженном лице, как показано ниже. В этой демонстрации нас интересует реперная точка с индексом 29, это кончик носа.
Рисунок 1:Индекс каждой реперной точки отображается в ее расположении на лице
var onFaceData = function(sender,data) { if (data.faces.length>0) { // Пропуск пустых экранов var face = data.faces[0]; // Использование первого видимого лица var rect = face.detection.boundingRect; // Получение ограничительной рамки лица var n = face.landmarks.points[29].world; // Получение данных реперов лица var px = rect.x + rect.w/2 + n.x*scale; // Привязка к ограничительной рамке var py = rect.y + rect.h/2 - n.y*scale; // Зеркальное отражение сдвига по оси Y ctx.clearRect(0,0,can.width,can.height); // Очистка полотна в каждом кадре ctx.strokeRect(rect.x,rect.y,rect.w,rect.h); // Отображение ограничительной рамки ctx.fillText(Math.round(n.z*100),px,py); // Отображение значения z в точке носа } }
Когда мы получаем кадр данных, то проверяем, обнаружено ли лицо, и используем первое обнаруженное лицо. Эта структура данных лица содержит гораздо больше информации, чем нам нужно, поэтому нам потребуется извлечь лишь ограничительную рамку (rect) и точку носа (n).
Переменные px и py образуют расположение выбранной для представления точки носа в нашем пространстве: перемещение носа вычисляется на основе центральной точки ограничительной рамки, масштабируется и сохраняется с новыми координатами X и Y.
Эти переменные дают нам расположение для изображения значения Z точки носа — глубины, то есть расстояния от экрана (для удобочитаемости оно умножено на 100, поскольку необработанное значение нормализуется до десятичной дроби в диапазоне от 0 до 1). Точка носа и ограничительная рамка отображаются на полотне после его очистки.
Дальнейшие действия
Надеюсь, эта демонстрация помогла вам ознакомиться с работой системы, она послужит неплохой основой, которую можно адаптировать к вашей работе. Добавьте к ней функции определения платформ и обработки ошибок, измените ее структуру, чтобы можно было продолжать выводить изображение без обновленных данных, попробуйте использовать больше реперных точек или сделайте что угодно еще, чтобы свести обработку к минимуму.
Этот SDK можно использовать множеством разных способов для дополнения способов ввода в приложениях и играх. Всегда помните о диапазоне движений пользователя, чтобы он был естественным; помните об усталости пользователей, чтобы работать с программой было удобно, поддерживайте достаточную кадровую скорость, чтобы все работало плавно. Эффективный, продуманный пользовательский интерфейс важен для таких решений, которые будут получать все более широкое распространение.
Справочные материалы
Документация к Intel® RealSense™ SDK 2016 R1: https://software.intel.com/sites/landingpage/realsense/camera-sdk/v1.1/documentation/html/index.html?doc_face_face_tracking_and_recognition.html
Intel® RealSense™ Depth Camera Manager (DCM): https://downloadcenter.intel.com/download/25044/Intel-RealSense-Depth-Camera-Manager-DCM-
Intel® RealSense™ SDK: https://software.intel.com/en-us/intel-realsense-sdk/download
RealSense — ваше лицо в качестве игрового контроллера при использовании Javascript: https://software.intel.com/en-us/blogs/2014/12/08/realsense-your-face-as-a-controller-using-javascript?language=en
Новые возможности Intel RealSense SDK v8 (2016 R1) для Windows: https://software.intel.com/en-us/blogs/2016/01/28/new-realsense-v8
Об авторе
Брэд Хилл (Brad Hill) — инженер по программному обеспечению в подразделении Developer Relations корпорации Intel. Брэд занимается изучением новых технологий на оборудовании Intel® и обменивается лучшими методиками с разработчиками ПО на форумах Intel® Developer Zone и на конференциях разработчиков. Он помогает студентам и разработчикам становиться разработчиками игр и изменять мир к лучшему.