Задача детекции объектов на видео или фото — это распространенная задача, для решения которой уже есть множество решений. Но она становится чуть труднее, если мы хотим сделать это в режиме реального времени на не самом мощном мобильном устройстве. В данной статье мы делимся своим опытом создания приложения для детекции атомобилей на мобильном устройстве в режиме реального времени, разработанного на C++ с использованием TensorFlow-Lite и Qt. TensorFlow-Lite — это библиотека, созданная Google, для разворачивания различных моделей на мобильных устройствах, микроконтроллерах и других устройствах. Библиотека поддерживает несколько языков программирования: C++, Java, Swift, Objective-C, Python. Для TensorFlow-Lite мы можем использовать, как уже готовые модели, например, TensorFlow- Hub, так и собственные модели, которые были конвертированы в TensorFlow-Lite модель. Во втором случае, возможно, понадобится оптимизировать модель. Модели делятся на два типа: Модели с метаданными. Модель с метаданными может использоваться в библиотеке TensorFlow-Lite Support. Там содержатся различные решения многих задач. Например, там есть решение для задачи детекции объектов. Библиотека сама сделает необходимые операции над входным изображением и предоставит ответ в виде специального класса. Модель без метаданных. Для модели без метаданных мы должны будем сами обрабатывать изображение и считывать ответ по указателю. Оптимизация — важный этап для успешного запуска модели на мобильном устройстве и не только. Благодаря ей мы можем уменьшить: Вес самой модели. Это позволит разворачивать модель быстрее; Использование оперативной памяти; Время вывода модели и, следовательно, уменьшить энергопотребление. Существует множество различных методов оптимизации и каждый из них стоит изучить. При использовании уже обученной модели, мы можем использовать только некоторые техники, например: Pruning — обрезание избыточных частей сети для ускорения inference без потери точности. Посмотреть про него можно здесь, а почитать здесь. Кластеризация весов — это метод уменьшения объема хранения вашей модели путем замены многих уникальных значений параметров меньшим количеством уникальных значений. Квантизация — это процесс уменьшения точности чисел, используемых для представления весов модели (обычно представленных в формате float32), путем преобразования их в целочисленный тип. Но из-за неё мы теряем точность. Немного подробнее можно прочесть в этой статье Master the Art of Quantization: A Practical Guide Exploring and Implementing Quantization Methods with TensorFlow and PyTorch Также приведем полезную статью по оптимизации моделей, там также есть объяснение этих способов. TensorFlow предоставляет свой toolkit для оптимизации моделей и в него уже входят эти способы. В данном проекте мы будем рассматривать уже оптимизированную модель для мобильных устройств. Как уже говорилось ранее мы разрабатываем приложение на Qt с использованием Qt Quick и TenosrFlow-Lite. Про сборку TensorFlow-Lite можно почитать в этой статье и в официальной документации. Но у нас будут некоторые отличия. Мы не будем использовать qmake и opencv, как сделано в указанной статье. Вместо qmake мы будем использовать cmake, а вместо opencv мы будем использовать инструменты из Qt. Также мы будем по-другому собирать gpu делегаты. Их можно собрать используя эту команду После того как мы скомпилировали библиотеки, нам необходимо их подключить. Сначала добавим заголовочные файлы. В нашем случае мы добавили TensorFlow, как git submodule, тогда мы можем подключить их с помощью  include_directories(...). Теперь необходимо подключить сами библиотеки. Сначала добавим их используя add_library. После этого нам необходимо указать путь к каждой библиотеки через set_property. И подключить их используя target_link_libraries. Также библиотеки нужно скопировать в ${CMAKE_BINARY_DIR}/android-build/libs/arm64-v8a/. В этой папке содержатся библиотеки, которые затем будут добавлены в apk. Одно из мест, где можно найти модель — TensorFlow Hub. Там уже содержатся модели, подготовленные для мобильных устройств. Мы остановились на EfficientDet-Lite0. У данной модели есть несколько версий: обычная, квантованная int8, с метаданными. Мы использовали, как обычную, так и квантованную. Также мы сравнили эти две модели. Model\Time inference ms. avg min max Default 103.809090909091 58 444 Quant 93.1909090909091 55 275 В итоге наблюдений было замечено, что квантованная модель работает стабильнее, т.е нет скачков времени при inference. В обычной модели это было более выражено. Также не будем исключать, что возможно проблема находится в тестовом устройстве (тестировалось на Realme 8i). На момент написания статьи мы использовали TensorFlow Hub для поиска моделей. Но с 15 ноября модели будут находится в Kaggle Models. Подробнее про это можно почитать в их анонсе. Рассмотрим как использовать данную библиотеку. Сначала объявим необходимые переменные. Дальше мы создадим саму модель. Сделать inference мы можем следующим образом. В данном фрагметне можно увидеть метод transform. Он позволяет уменьшить размер изображения до нужного нам, а также поменять его формат. С помощью метода ниже мы можем получить предсказания модели. В данном фрагменте можно заметить метод getOuput. Данный метод эквивалентен mInterpreter->typed_output_tensor<type>(numOutput) и был введен для улучшения читаемости кода. Одна из проблем, с которой мы столкнулись — камера. Она работала без всяких проблем на настольных системах, но не на мобильных. Во многих релизах она просто не работала и приложение аварийно завершалось. Пример ошибки на версии 6.5.2. По этому багу уже ведется работа и qt уже о нем известно. Сам тикет на баг. bugreports.qt.io Этот баг можно поправить, но потребуется пересобирать qt для этого. Один из вариантов — это сменить версию qt. Мы попробовали 6.2, 6.3, 6.4, 6.5. Из этих версий только в 6.4 камера работала без всяких проблем. Также мы столкнулись с ещё одной проблемой. В вертикальном положении изображение было растянуто. Это удалось исправить самостоятельно указав соотношение сторон. Рассмотрим, как захватить изображение с камеры и на что стоит обратить внимание. Захват изображения мы будем делать с помощью Qt Quick и QtMultimedia. Рассмотрим это фрагмент кода. В нем: MediaDevice позволяет выбрать камеру, CaptureSession делает захват изображения, VideoOutput делает превью нашего видео потока, а VideoHandler — это наш класс, который нужен, чтобы получить картинку с видео потока и отправить её в модель. Получить изображение можно следующим образом mVideoSink->videoFrame().toImage(), где mVideoSink — QPointer<QVideoSink>. Именно его мы установили в VideoHandler. Здесь есть пара замечаний. Работу с моделью будет лучше вынести в отдельный поток, чтобы избежать застывание изображения. Также лучше проводить inference модели не каждое обновление кадра, а с промежутками. Для этого мы создали таймер с интервалом в 1 секунду. В результате каждую секунду мы делали inference модели. Делегаты позволяют ускорить inference модели за счет использования GPU и DSP. В TensorFlow-Lite существуют несколько типов делегатов. GPU делегаты. Поддерживаются android и ios. NNAPI и Hexagon делегаты. Поддерживают только android. Core ML делегаты. Поддерживают только ios. Более подробно про них можно почитать в документации. TensorFlow Lite Delegates В нашем проекте мы использовали GPU делегаты. Но мы столкнулись с небольшой проблемой. В Выбранной нами модели, были кастомные операторы. В результате этого только часть операций выполнялась на GPU и это не принесло значительных улучшений. Это также можно заметить в benchmark-е к нашей модели. 1 CPU (ms) 4 CPU (ms) GPU (ms) EdgeTPU (ms) 49.92 36.77 48.48 29.26 Мы рассмотрели один из способов работы с TensorFlow-Lite на C++ с использованием Qt. Сравнили время inference разных моделей. И также рассмотрели способы оптимизации моделей и использование делегатов. Хотелось бы отметить, что использовать данную связку для разработки приложения возможно, но будьте готовы встретиться лицом к лицу  возникающими сложностями: часть из них мы описали в этой статье, с другой же чатью придется справиться самостотельно. Весь наш проект можно посмотреть на github.