Заархивировано - Отслеживание лица с помощью JavaScript* и Intel® RealSense™ SDK

Выпуск комплекта Intel® RealSense™ SDK прекращен. Его поддержка и обновления более недоступны.

Пример кода

В первом за этот год выпуске (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(); // Запуск передачи
});

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

  1. Создайте Sense Manager, сохраненный как sm.
  2. С помощью sm включите модуль Face, сохраненный в fm.
  3. Задайте наш обработчик данных и используйте fm, чтобы создать активную конфигурацию лица fc.
  4. Включите обнаружение лиц в fc, затем высвободите его (поскольку больше не требуется вносить изменения в настройку).
  5. Теперь можно инициализировать 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 и на конференциях разработчиков. Он помогает студентам и разработчикам становиться разработчиками игр и изменять мир к лучшему.

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