- PVSM.RU - https://www.pvsm.ru -

Как мы в Smart Engines учили Sailfish OS распознаванию

Smart Engines и Sailfish OS

Всем привет! Как вы уже знаете по нашим статьям, мы в Smart Engines занимаемся распознаванием, причем распознавать мы стараемся на чем угодно и в любых условиях. Мы поддерживаем все популярные операционные системы: iOS, Android, Windows, Linux, MacOS, Solaris. Поддерживаем мы и отечественного производителя: Эльбрус и AstraLinux. Наши алгоритмы оптимизированы под ARMv7-v8, AArch64, x86, x86_64, SPARC, E2K, MIPS.

Поэтому, когда мы увидели нарастающую популярность российской операционной системы Sailfish Mobile OS RUS, мы не смогли обойти ее стороной. Sailfish Mobile OS RUS — это POSIX-совместимая операционная система для мобильных устройств, развиваемая отечественной компанией «Открытая Мобильная Платформа» для решения задач корпоративных пользователей и государственных учреждений. По состоянию на февраль 2018 года является единственной мобильной операционной системой, включенной в реестр Отечественного ПО и прошедшей сертификацию ФСБ по классу АК1/КС1.

В этой статье мы расскажем о своем опыте портирования нашей библиотеки распознавания Smart IDReader [1] (технология Hieroglyph [2]) на Sailfish OS. В ней будет код, ссылки и видео. Мы хотим, чтобы эта статья была технически информативной и полезной в качестве общей инструкции для тех, кто портирует С++ приложения на Sailfish OS.

Мы выражаем благодарность коллегам из Открытой Мобильной Платформы [3] (ОМП) Кириллу Чувилину и Алексею Андрееву за консультации в разработке приложения и предоставление двух устройств INOI R7 для тестирования и демонстрации.

TL;DR — портировать было быстро и просто. Суммарно потребовалось меньше одной рабочей человеко-недели с момента скачивания Sailfish OS SDK до снятия первого видео с демонстрацией распознавания документов на устройстве INOI R7 (видео будут в конце статьи).

Установка инструментария

Первым шагом была установка Sailfish OS SDK. Скачать установочный пакет можно с официального сайта [4], но мы выбрали метод еще проще и установили SDK из AUR — пользовательского репозитория Arch Linux (самого популярного Linux-дистрибутива среди разработчиков Smart Engines), в котором, как обычно, все есть и устанавливается одной командой:

yaourt -S sailfishos-sdk-bin

Средства для разработки включают в себя три основных компонента:

  1. Окружение и система сборки — Oracle VM VirtualBox образ, содержащий установленные компиляторы и библиотеки для сборки приложений для устройства или эмулятора.
  2. SailfishOS Emulator, аналогичный эмуляторам для других мобильных платформ (iOS, Android, ...)
  3. IDE — всем известный Qt Creator, доделанный для поддержки взаимодействия с системой сборки и запуска программы на устройстве или эмуляторе.

Соединение с виртуальной машиной для сборки происходит через SSH. Единственной проблемой было то, что при установке SDK для виртуальной машины и IDE жестко проставляется порт 2222, который уже использовался для других целей на компьютере разработчика. В VirtualBox этот порт меняется понятным образом, а вот в IDE поменять его из GUI было нельзя. К счастью, его можно было поменять в mersdk.xml файле локальных настроек:

<!-- ~/.config/SailfishOS-SDK/qtcreator/mersdk.xml -->
<value type="QString" key="SshPort">2222</value>

Об этой проблеме после ее решения было сообщено Кириллу, а других проблем с инструментарием в дальнейшем не возникало.

Сборка С++ ядра распознавания

Наше ядро распознавания написано на С++ и в данном случае очевидным выбором для портирования на Sailfish OS была сборка ядра в виде статических библиотек.

Короткая, но информативная инструкция по ручной сборке библиотек находится на странице Building Sailfish OS packages manually [5] официальной документации. По ней стало понятно, что требуется просто подключиться по SSH к виртуальной машине, а затем вызвать обычные команды сборки с указанием правильной архитектуры. Опишем процесс по шагам.

  1. Запускаем виртуальную машину Sailfish OS Build Engine (достаточно headless режима)
  2. Подключаемся к ней:

    ssh -p 2222 -i /opt/SailfishOS/SDK/vmshare/ssh/private_keys/engine/mersdk mersdk@localhost 

    Кстати, внешняя пользовательская home-директория по умолчанию доступна в виртуальной машине по адресу /home/src1.

  3. Получаем список поддерживаемых платформ:

    [mersdk@SailfishSDK ~]$ sdk-assistant list
    Toolings:
        SailfishOS-2.1.3.7
    Targets:
        SailfishOS-2.1.3.7-armv7hl    # Устройство
        SailfishOS-2.1.3.7-i486       # Эмулятор

    В отличие от примера на сайте, в именах платформ может присутствовать номер версии, которые тоже нужно будет учитывать. Далее сборка будет проводиться для устройства (SailfishOS-2.1.3.7-armv7hl), но для эмулятора весь процесс аналогичен.

    Для требуемой архитектуры соответствующее окружение обеспечивается командой sb2 (Scratchbox2), поэтому все нужно делать через нее:

    sb2 -t  SailfishOS-2.1.3.7-armv7hl <args....>

  4. Устанавливаем необходимые для сборки пакеты. В нашем случае это были CMake, GCC, G++ и Zip:

    sb2 -t SailfishOS-2.1.3.7-armv7hl -m sdk-install -R zypper in cmake gcc gcc-c++ zip

  5. Создаем папку сборки и вызываем CMake, причем передаем ему правильный CMAKE_SYSROOT, чтобы компиляторы искались в нужном нам месте:

    mkdir build && cd build
    
    sb2 -t SailfishOS-2.1.3.7-armv7hl -m sdk-build cmake -DCMAKE_SYSROOT=/srv/mer/targets/SailfishOS-2.1.3.7-armv7hl ..

  6. Собираем проект:

    sb2 -t SailfishOS-2.1.3.7-armv7hl -m sdk-build make install -j8

Вот и все! Таким образом, сборка под Sailfish OS практически не отличается от сборки под Linux — требуется лишь подключиться к виртуальной машине и вызывать команды с правильным окружением.

Создание и настройка Qt проекта

Процесс создания проекта тривиален и про него можно почитать, например, в хабе [6] Sailfish OS на Хабрахабре. После этого нужно было сделать только одно — добавить пути к заголовочным файлам и статическим библиотекам в .pro файл следующим образом:

INCLUDEPATH += /path/to/install.armv7-linux.release/include

LIBS += -L/path/to/install.armv7-linux.release/lib 
-lsomeStaticLibrary 
-lanotherStaticLibrary

После этого проект с вызовом нашего С++ ядра распознавания успешно скомпилировался и запустился.

Написание GUI с QML и Sailfish Silica

GUI приложений для Sailfish OS пишется с помощью Qt на C++ и QML: как со стандартной библиотекой QtQuick, так и специальной Sailfish SIlica [7].

Практически вся требуемая информация по этим технологиям присутствует в их официальной документации. К дополнительным полезным ссылкам относятся хаб разработки под Sailfish OS [8] на Хабре, шпаргалки по цветам и отступам [9], иконкам [10] и основным компонентам [11], а также бесплатный вводный курс [12] на Stepik.

Для отрисовки графических примитивов мы использовали Canvas [13] и его метод onPaint. Выбор документа горизонтальной прокруткой — SlideshowView (спасибо Алексею за пример реализации). Отображение результатов распознавания — SilicaListView [14] с ListModel.

Помимо написания основных GUI элементов важно научиться вызывать С++ классы из QML кода, что довольно просто и понятно описано на странице Integrating QML and C++ [15] официальной документации Qt: делаем С++ класс, регистрируем его в С++, импортируем его в QML. Для оборачивания С++ структур в QML самым очевидным способом показалось использование Q_GADGET, про что даже есть статья [16] на Хабре.

Взаимодействие с камерой устройства: детективно-научное расследование

Понимание того, как правильно взаимодействовать с API камеры, было единственной трудностью во всем процессе разработки демо-приложения и заслуживает отдельного пункта — потребовалось небольшое исследование с мозговым штурмом, гипотезами и экспериментами.

Сценарий использования наших библиотек распознавания видеопотока в реальном времени обычно такой: пока пользователь видит превью камеры и держит перед ней объект (например, паспорт РФ или банковскую карту), в библиотеку распознавания один за одним поступают приходящие с камеры кадры и распознаются. При этом, во время распознавания каждого кадра пользователю показывается промежуточная информация о расположении документа, его полей и т.д. Тем самым, требуется одновременно захватывать кадры с камеры и показывать превью пользователю.

При создании камеры и превью в QML вопросов не возникло: создаем Camera [17] и ссылаемся на нее в VideoOutput [18]:

Camera {
    id: camera
    position: Camera.BackFace
    // ...
}

VideoOutput {
    anchors.fill: parent
    source: camera
}

Осталось найти способ перехватывать приходящие с камеры кадры и передавать их в С++ на распознавание.

В качестве первого способа мы попробовали стандартный QVideoProbe [19], специально существующий для получения приходящих с камеры кадров. Однако, вызов probe.setSource(camera) возвращал false, после чего в callback для перехвата кадров ничего не приходило. Судя по информации в интернете, такая проблема актуальна и для Android устройств, что может свидетельствовать о простом отсутствии реализации QVideoProbe для Sailfish OS. Надеемся, что в скором времени это исправится.

Второй способ — наследование от QAbstractVideoSurface [20] и реализация метода present(const QVideoFrame &frame), который передает пришедший кадр на распознавание. После вызова camera.setViewFinder(&videoSurface) кадры действительно начали приходить (в формате NV21) и даже распознаваться, если держать документ перед камерой… вот только превью в VideoOutput пропало и пользователь потерял возможность видеть, что же снимает его камера. Победить эту проблему быстро не удалось и мы вместе с Кириллом и Алексеем из ОМП начали искать третью альтернативу.

К счастью, третий способ коллективными усилиями был найден довольно быстро. Им оказалась связка из QAbstractVideoFilter [21], который методом createFilterRunnable() создает QVideoFilterRunnable [22], в свою очередь реализующий функционал обработки кадра. После этого реализованный фильтр нужно добавить в QML и сослаться на него в имеющемся VideoOutput:

RecognitionVideoFilter {
    id: recognitionVideoFilter
    // ...
}

VideoOutput {
    anchors.fill: parent
    source: camera
    filters: [ recognitionVideoFilter ]
}

Скомпилировалось, запустилось. Превью видео есть, но кадры по прежнему не приходят. И вдруг, Кирилл поделился инсайдерской информацией о том, что для работы фильтров на устройстве необходимо обновить QtMultimedia плагин, который оказался доступен по ссылке [23]. После этого все заработало — кадры в формате BGRA начали приходить в ядро. Небольшая деталь — функция QAbstractVideoSurface::run блокирует превью камеры (что логично, потому что фильтр подразумевает изменение кадра, а нам нужно его только прочитать и распознать), поэтому для плавного превью потребовалось запускать распознавание в отдельном потоке — например, через QtConcurrent::run(...) [24].

Вот и все! Оставалось только дописать GUI приложения, что не составило особого труда.

Демонстрация на Mobile World Congress 2018 в Барселоне и другие видео

В этом году наша компания уже второй раз представила [25] свои технологии распознавания на ежегодной выставке Mobile World Congress 2018 в Барселоне, снова поразившей нас своим масштабом: более 107 000 посетителей из 205 стран, 2400 стендов компаний, расположенных на 120 000 квадратных метрах павильонов. Как и в прошлом году [26], она произвела на нас крайне положительное впечатление.

Конечно, мы не упустили возможности как пойти похвастаться нашей демкой на стенде финской компании Jolla, являющейся оригинальным разработчиком Sailfish OS, так и показать работу программы "вживую" коллегам из Открытой Мобильной Платформы, которые даже сняли несколько видео и выложили их в группе ВКонтакте [27]:

А вот видео с первой рабочей версией программы:

Много других видео с демонстрациями технологий Smart Engines можно посмотреть на нашем YouTube канале [31].

Заключение

В этой статье мы рассказали о своем опыте портирования библиотеки распознавания Smart IDReader на Sailfish OS, включая написание демо-приложения. Мы были приятно удивлены количеством релевантной официальной документации и качеством инструментария. Конечно, пара технических вопросов возникла, но их удалось оперативно победить. Надеемся, что наш опыт будет полезен и другим.

Автор: SmartEngines

Источник [32]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/programmirovanie/276712

Ссылки в тексте:

[1] Smart IDReader: http://smartengines.ru/smart-idreader/

[2] Hieroglyph: http://smartengines.biz/ocr-products/

[3] Открытой Мобильной Платформы: http://omprussia.ru/

[4] официального сайта: https://sailfishos.org/wiki/Application_SDK#Latest_SDK_Release

[5] Building Sailfish OS packages manually: https://sailfishos.org/develop/tutorials/building-sailfish-os-packages-manually/

[6] хабе: https://habrahabr.ru/post/305510/

[7] Sailfish SIlica: https://sailfishos.org/develop/docs/silica/

[8] хаб разработки под Sailfish OS: https://habrahabr.ru/hub/Sailfish_dev/

[9] цветам и отступам: https://sailfishos.org/wp-content/uploads/2016/06/theme_cheatsheet.png

[10] иконкам: https://sailfishos.org/wp-content/uploads/2016/06/icon_reference.png

[11] основным компонентам: https://sailfishos.org/wp-content/uploads/2016/06/component_cheatsheet.png

[12] вводный курс: http://stepik.org/course2341

[13] Canvas: http://doc.qt.io/qt-5/qml-qtquick-canvas.html#details

[14] SilicaListView: https://sailfishos.org/develop/docs/silica/qml-sailfishsilica-sailfish-silica-silicalistview.html/

[15] Integrating QML and C++: http://doc.qt.io/qt-5/qtqml-cppintegration-topic.html

[16] статья: https://habrahabr.ru/post/307816/

[17] Camera: http://doc.qt.io/qt-5/qml-qtmultimedia-camera.html#details

[18] VideoOutput: http://doc.qt.io/qt-5/qml-qtmultimedia-videooutput.html#details

[19] QVideoProbe: http://doc.qt.io/qt-5/qvideoprobe.html#details

[20] QAbstractVideoSurface: http://doc.qt.io/qt-5/qabstractvideosurface.html#details

[21] QAbstractVideoFilter: https://doc.qt.io/qt-5.10/qabstractvideofilter.html#details

[22] QVideoFilterRunnable: https://doc.qt.io/qt-5.10/qvideofilterrunnable.html#details

[23] ссылке: http://repo.merproject.org/obs/home:/minlexx/sailfish_latest_armv7hl/armv7hl/

[24] QtConcurrent::run(...): http://doc.qt.io/qt-5/qtconcurrentrun.html

[25] представила: http://smartengines.ru/news59/

[26] прошлом году: https://habrahabr.ru/company/smartengines/blog/322400/

[27] выложили их в группе ВКонтакте: https://vk.com/wall-40681615_13027

[28] Первое видео: https://vk.com/wall-40681615_13027?z=video-40681615_456239058%2Fc8289b04eeba5f5b74%2Fpl_post_-40681615_13027

[29] Второе видео: https://vk.com/wall-40681615_13027?z=video-40681615_456239059%2F7aaabf72d4e37f7cb3%2Fpl_post_-40681615_13027

[30] Третье видео: https://vk.com/wall-40681615_13027?z=video-40681615_456239060%2Fbcfdd0d4cfaf70aee8%2Fpl_post_-40681615_13027

[31] нашем YouTube канале: https://www.youtube.com/channel/UCAhRdZxFYK0kmjnqdmNGePA/videos

[32] Источник: https://habrahabr.ru/post/352512/?utm_source=habrahabr&utm_medium=rss&utm_campaign=352512