Пользовательские жесты, Kinect + Unity. Часть 2

от автора

Мы продолжаем наш туториал об использовании кастомных жестов в связке Kinect+Unity. В первой части мы рассмотрели процесс обучения жестов, в результате чего у нас получилась обученная модель в виде .gdb файла. Сегодня мы будем использовать эту модель в Unity.

Настройка Kinect + Unity

Создадим проект в Unity, добавим скачанные пакеты Kinect’a: Assets -> Import package -> Custom package…, выбирем Kinect.VisualGestureBuilder.2.0.1410.19000.unitypackage и Kinect.2.0.1410.19000.unitypackage (версии могут отличаться). Может возникнуть проблема с тем, что некоторые файлы в этих пакетах одинаковые, Unity добавляет оба файла с именами File.cs и File 1.cs, в этом случае просто удаляем все файлы с индексом 1 (список будет в сообщениях об ошибках).
Структура пустого проекта с добавленными пакетами:

Запустим готовый пример, чтобы убедиться, что все работает. В скачанном пакете для Unity есть два примера: GreenScreen и KinectView. Из примера KinectView добавим две папки «Materials» и «Scripts» и файл «MainScene.unity». Откроем сцену (MainScene.unity) и запустим. Должно получится примерно следующее:

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

Сперва добьемся, чтобы по срабатыванию жеста в лог выводилось сообщение.
Из Kinect SDK нам понадобятся:

  • KinectSensor – класс, представляющий сенсор Kinect;
  • VisualGestureBuilderDatabase – класс, представляющий собой базу жестов;
  • VisualGestureBuilderFrameSource – обработка жестов на кадрах, полученных с Kinect’а;
  • Body – класс, представляющий человека;
  • Gesture – класс, представляющий жест;
  • BodyFrameSource — информация о найденных людях на кадрах, получаемых с Kinect’a.

Кроме того, нам понадобятся два класса для обработки полученных кадров с Kinect’а: VisualGestureBuilderFrameReader и BodyFrameReader.

Загрузка жестов в Unity

Подразумевается, что у нас на данном этапе пустой Unity проект с импортированными пакетами Kinect’а, как в примере выше.
Создадим пустой объект (GameObject -> Create Empty), назовем его KinectManager. Добавим к нашему объекту новый скрипт с именем KinectManagerScript (в данном туториале рассматриваются только скрипты, написанные на C#). Открываем скрипт, добавляем using’и:

using Microsoft.Kinect.VisualGestureBuilder; using Microsoft.Kinect; 

Внутри класса объявляем объекты упомянутых выше классов:

VisualGestureBuilderDatabase _dbGestures; Windows.Kinect.KinectSensor _kinect; VisualGestureBuilderFrameSource _gestureFrameSource; Windows.Kinect.BodyFrameSource _bodyFrameSource; VisualGestureBuilderFrameReader _gestureFrameReader; Windows.Kinect.BodyFrameReader _bodyFrameReader; Gesture _swipeUpDown; // наш жест Windows.Kinect.Body[] _bodies; // все пользователи, найденные Kinect'ом Windows.Kinect.Body _currentBody = null; //Текущий пользователь, жесты которого мы отслеживаем public string _getsureBasePath = "upDown.gbd"; //Путь до нашей обученной модели 

Теперь нам нужно проинициализировать значения и добавить обработчики событий. Создадим метод InitKinect() и будем вызывать его в методе Start(). Для начала загрузим жесты из нашей модели:

void InitKinect() {     _dbGestures = VisualGestureBuilderDatabase.Create(_getsureBasePath);     _bodies = new Windows.Kinect.Body[6];     _kinect = Windows.Kinect.KinectSensor.GetDefault();     _kinect.Open();     _gestureFrameSource = VisualGestureBuilderFrameSource.Create(_kinect, 0);      foreach (Gesture gest in _dbGestures.AvailableGestures)     {         if (gest.Name == "UpDownSwipe_Right")         {             _gestureFrameSource.AddGesture(gest);             _swipeUpDown = gest;             Debug.Log("Added:" + gest.Name);         }     } } 

Замечание: мы сталкивались с проблемой, когда Kinect включается не мгновенно, и, следовательно, метод Open() не отрабатывал. Это лечилось периодической проверкой флага IsAvailable в Update и открытием в случае, если Kinect доступен

Запускаем приложение, и если все хорошо, то мы увидим в логе следующее:

Это значит, что мы успешно открыли и загрузили жесты (в нашем случае один) из нашей обученной модели.

Детектирование жестов

Очевидно, что жесты нужно детектировать у человека, поэтому нам нужно получить список всех людей, которых видит Kinect. Инициализируем объект _bodyFrameSource и _bodyFrameReader и добавим обработчик события «кадр пришел» (делаем все это в InitKinect):

_bodyFrameSource = _kinect.BodyFrameSource; _bodyFrameReader = _bodyFrameSource.OpenReader(); _bodyFrameReader.FrameArrived += _bodyFrameReader_FrameArrived; 

Аналогично инициализируем _gestureFrameSource, поставим на паузу и добавим обработчик события:

_gestureFrameReader = _gestureFrameSource.OpenReader(); _gestureFrameReader.IsPaused = true; _gestureFrameReader.FrameArrived += _gestureFrameReader_FrameArrived; 

В обработчике _bodyFrameReader_FrameArrived мы хотим получить информацию о людях и, если в кадре есть кто-нибудь, выбрать первого попавшегося (для простоты), жесты которого будем отслеживать.

Исходный код метода

void _bodyFrameReader_FrameArrived(object sender, Windows.Kinect.BodyFrameArrivedEventArgs args) {     var frame = args.FrameReference;     using (var multiSourceFrame = frame.AcquireFrame())     {         multiSourceFrame.GetAndRefreshBodyData(_bodies); //обновляем данные о найденных людях         _currentBody = null;         foreach (var body in _bodies)         {             if (body != null && body.IsTracked)             {                 _currentBody = body; // для простоты берем первого найденного человека             }         }         if (_currentBody != null)         {             Debug.Log("_currentBody is not null");         }         else          {             Debug.Log("_currentBody is null");         }            } } 

Запустим приложение. Когда Kinect увидит хотя бы одного человека, мы увидим «_currentBody is not null», если никого в кадре нет — «_currentBody is null». Пример лога:

Мы искали активного человека для того, чтобы распознавать его жесты. Если мы нашли человека, сохраняем его id в _gestureFrameSource и убираем с паузы. Наше условие изменится на следующее:

if (_currentBody != null) {     Debug.Log("_currentBody is not null");     _gestureFrameSource.TrackingId = _currentBody.TrackingId;     _gestureFrameReader.IsPaused = false; } else  {     Debug.Log("_currentBody is null");     _gestureFrameSource.TrackingId = 0;     _gestureFrameReader.IsPaused = true; } 

Последнее, что нам нужно от Kinect’a – это обработчик непосредственно жестов. Проверяем валидность нашего id человека, получаем текущий кадр так же, как мы делали это в _bodyFrameReader_FrameArrived:

if (_gestureFrameSource.IsTrackingIdValid) {     Debug.Log("Tracking id is valid, value = " + _gestureFrameSource.TrackingId);     using (var frame = args.FrameReference.AcquireFrame())     {         if (frame != null)         {             /*…*/         }     } } 

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

var results = frame.DiscreteGestureResults; 

если есть какие-нибудь жесты, смотрим, наш жест или нет:

if (results != null && results.Count > 0) {     DiscreteGestureResult swipeUpDownResult;     results.TryGetValue(_swipeUpDown, out swipeUpDownResult);     Debug.Log("Result not null");     if (swipeUpDownResult.Confidence > 0.1)     {         Debug.Log("Up Down Gesture");     } } 

Пороговое значение Confidence зависит от качества вашего обучения, то есть это такое значение, ниже которого шум, а выше которого жест (вспомним «елочку» из первой части), подбирается оно эмпирически 🙂

Запустив приложение, мы увидим, что на один жест происходит множественное детектирование. Это связано с тем, что результаты для каждого кадра независимы, а так как жест выполняется не мгновенно (несколько кадров), то мы получаем положительный результат для каждого из кадров. Устранить это можно логическим флагом:

bool gestureDetected = false; … if (swipeUpDownResult.Confidence > 0.1) {     if (!gestureDetected)     {         gestureDetected = true;         Debug.Log("Up Down Gesture");     } } else {     gestureDetected = false; } 

Использование жестов

Последнее, что нам осталось сделать с KinectManagerScript, это «зажечь» событие, когда мы увидели жест. Объявляем:

public delegate void SimpleEvent(); public static event SimpleEvent OnSwipeUpDown; 

и когда нашли жест:

if (!gestureDetected) {     gestureDetected = true;     Debug.Log("Up Down Gesture");     if (OnSwipeUpDown!= null)             OnSwipeUpDown();    } 

На этом заканчиваем с KinectManagerScript. Вернемся к нашей сцене и создадим сферу. Назовем «MainSphere», добавим новый скрипт «MainSphereScript». В методе Start() создадим обработчик события, проверим с помощью лога, что все работает:

void Start ()  {    KinectManagerScript.OnSwipeUpDown += new KinectManagerScript.SimpleEvent(KinectManagerScript_OnSwipeUpDown); } void KinectManagerScript_OnSwipeUpDown() {    Debug.Log("upDown From listener");    } 

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

Скрипты:
MainSphereScript.cs
KinectManagerScript.cs

ссылка на оригинал статьи https://habrahabr.ru/post/276455/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *