Друзья!
Наверняка многие из вас уже слышали про когнитивные сервисы, которые позволяют одним вызовом REST API решать сложные задачи — определять эмоции и возраст человека по фотографии, делать машинный перевод текста и т.д. Часто когнитивные сервисы внедряют в приложения или веб-бекенд. Сегодня наш большой друг, сотрудник компании VRTech и Game-разработчик Григорий Дядиченко расскажет нам, как внедрять когнитивные сервисы в игры на Unity, а также пригласит вас на митап Unity-разработчиков, где можно будет обсудить это подробнее.
В этой статье мне бы хотелось рассказать про интеграцию Microsoft Cognitive Services в Unity; про то, как делать HTTP запросы к сервисам через класс WWW (если вдруг кто-то ещё не сталкивался с этим и не знает) и рассказать, с какими неожиданными для меня проблемами я столкнулся, разрабатывая приложение с использованием этих сервисов для Google Play.
Microsoft Cognitive Services — это набор облачных сервисов, которые позволяют решать такие задачи, как распознавание речи, лиц, эмоций и многое другое. Подробнее можно узнать тут
Когда-то я уже писал статью про когнитивные сервисы, и даже конкретно про Emotions API. В ней использовалась библиотека для UWP, которую нельзя использовать в Unity проекте. Поэтому недавно мне пришла в голову идея, что неплохо было бы написать обёртку для этих сервисов для Unity. И я взялся за дело.
Эти сервисы являются интересным и недорогим инструментом для создания “вау эффекта” на выставках, сбора контактов и подобных задач. Работать с ними в принципе в разы проще, нежели с тем же OpenCV. В контексте разработки игр можно сделать прикольную плюшку для игрока, которая позволяет генерировать аватарку игроку по его фотографии.
Перейдём к описанию самой обёртки. На данный момент в ней частично покрыты Emotions API и Face API.
Взаимодействие с решением построено очень просто. Вы создаёте нужный вам сервис, указывая в конструкторе SubscriptionKey (для удобства в демо сценах для их хранения создан ScriptableObject), а дальше создаёте корутину, в которой забираете необходимые вам данные.
private IEnumerator CheckEmotions() { EmotionService emoServ = new EmotionService(SubscriptionKeys.Instance.EmotionsApiKey); while (true) { yield return new WaitForEndOfFrame(); yield return emoServ.GetEmoInfoCoroutine(_WebCam.Screenshot); var emotions = emoServ.LastEmotions; if (emotions != null) { _MaxEmotionValue.text = GetMaxEmotionOnScreenshot(emotions); } yield return new WaitForSeconds(DELAY); } }
Бесплатную пробную версию subscription key можно получить на сайте когнитивных сервисов Microsoft
Итак, зачем тут корутины? Дело в том, что самый удобный способ обращаться к сервисам — это Rest API. Проще всего в Unity это делается с помощью класса WWW, в котором запрос работает асинхронно. Есть множество способов дождаться его выполнения.
Например, можно заблокировать главный поток, но я подозреваю, что в большинстве случаев это нежелательно.
А можно сделать корутинами, что и реализовано в данной версии обёртки.
private IEnumerator CreateRecognizeRequestAndSaveResponseCoroutine( string contentHeader, byte[] data) { Dictionary<string, string> headers = new Dictionary<string, string>(); headers.Add(Constants.SUB_KEY_HEADER, _SubscriptionKey); headers.Add(Constants.CONTENT_TYPE_HEADER, contentHeader); WWW request = new WWW(_RecognizeRequestUrl, data, headers); yield return new WaitUntil(() => request.isDone); ParseEmotionsFromJson(request.text); }
Данный способ работает неплохо, и устраивал меня, когда я писал своё приложение под андроид. Так как анализ фотографий занимает некоторое время, чтобы пользователь не сидел без дела, я решил интегрировать рекламу. Но в ходе интеграции рекламы появились неожиданные проблемы. Пользователь смотрит рекламу, профиль анализируется — профит, но не тут-то было. Тут меня ждала особенность, про которую я не знал относительно Unity Ads на андроиде. Дело в том, что во время показа рекламы блокируется главный поток, поэтому для анализа профиля было решено вынести всё в отдельный поток.
Там меня ждало новое, но вполне логичное открытие. Оказывается, класс WWW может работать только в главном потоке. Поэтому пришлось всё писать на System.Net (версии 2.0, так как в Unity именно она). И я бы выложил это решение в репозиторий, но там потребовалось подписывать SSL-сертификат, что неочевидно, и может приводить к непредвиденным последствиям у пользователя обёртки. Если вдруг кому-то будет интересно, то я могу её выложить отдельным проектом на гитхабе, но с точки зрения реализации там нет ничего сложного.
(Не самый красивый пример сделанный на скорую руку)
private void CreateRecognizeRequest() { Clear(); _Thread = new Thread(Run); _Thread.Start(); } private void Run() { WebHeaderCollection headers = new WebHeaderCollection(); headers.Add(Constants.SUB_KEY_HEADER, _SubscriptionKey); var request = HttpWebRequest.Create(_RecognizeRequestUrl); request.ContentType = _ContentHeader; request.Headers = headers; request.ContentLength = _Data.Length; request.Method = WebRequestMethods.Http.Post; var dataStream = request.GetRequestStream(); dataStream.Write(_Data, 0, _Data.Length); dataStream.Close(); var response = request.GetResponse(); dataStream = response.GetResponseStream(); StreamReader reader = new StreamReader(dataStream); string responseString = reader.ReadToEnd(); reader.Close(); dataStream.Close(); response.Close(); Debug.Log(responseString); if (!TryParseEmotionsFromJson(responseString)) { Run(); } else { _IsDataReady = true; } }
Ещё одно забавное открытие было обнаружено при тестировании Face API. Хотелось сделать такой эффект идеальной улыбки.
Face API может возвращать тот же набор эмоций, что и Emotions API. Но в ходе тестов я обнаружил, что результаты разнятся, при этом Emotions API работает чуть более стабильно и точно. Поэтому для данного эффекта, лендмарки лица (чтобы правильно поставить звёздочку) забирались из Face API, а эмоции — из Emotions API.
В ближайшее время я планирую вернуться к реализации этой обёртки и к поиску новых приколов, связанных с использованием Microsoft Cognitive Services. А пока в проекте есть Demo сцены, в которых показано простейшее взаимодействие EmotionService с веб камерой. Кроме того, некоторые полезные утилиты для скриншотов (скрипт, который делает скриншот определённого RectTransform к примеру)
Возвращаясь к обёртке, скачать и следить за её развитием можно в Github репозитории. (Возможно после митапа дойдут руки написать документацию)
Unity Moscow Meetup #3
7 июня в ВШБИ пройдёт третий митап Unity разработчиков в Москве. Если вы занимаетесь разработкой на Unity или она вам интересна — приходите! Мероприятие бесплатное, регистрация обязательна, зарегистрироваться и узнать более подробную информацию можно тут
А так же, чтобы сделить за последующими мероприятиями и посмотреть материалы с прошлых встреч, можете вступить в группы:
VK: vk.com/unimosmeet
FB: www.facebook.com/groups/unimosmeet
Об авторе
Дядиченко Григорий — ведущий Unity разработчик в VRTech. Организатор Unity Moscow Meetup. Увлекается алгоритмами на графах, разработкой игр и всем, что связано с компьютерной графикой.
ссылка на оригинал статьи https://habrahabr.ru/post/329866/
Добавить комментарий