Мы продолжаем наш туториал об использовании кастомных жестов в связке 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/
Добавить комментарий