- PVSM.RU - https://www.pvsm.ru -
Всем привет! Как вы уже знаете по нашим статьям, мы в 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
Средства для разработки включают в себя три основных компонента:
Соединение с виртуальной машиной для сборки происходит через 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 к виртуальной машине, а затем вызвать обычные команды сборки с указанием правильной архитектуры. Опишем процесс по шагам.
Подключаемся к ней:
ssh -p 2222 -i /opt/SailfishOS/SDK/vmshare/ssh/private_keys/engine/mersdk mersdk@localhost
Кстати, внешняя пользовательская home-директория по умолчанию доступна в виртуальной машине по адресу /home/src1
.
Получаем список поддерживаемых платформ:
[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....>
Устанавливаем необходимые для сборки пакеты. В нашем случае это были CMake, GCC, G++ и Zip:
sb2 -t SailfishOS-2.1.3.7-armv7hl -m sdk-install -R zypper in cmake gcc gcc-c++ zip
Создаем папку сборки и вызываем 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 ..
Собираем проект:
sb2 -t SailfishOS-2.1.3.7-armv7hl -m sdk-build make install -j8
Вот и все! Таким образом, сборка под Sailfish OS практически не отличается от сборки под Linux — требуется лишь подключиться к виртуальной машине и вызывать команды с правильным окружением.
Процесс создания проекта тривиален и про него можно почитать, например, в хабе [6] Sailfish OS на Хабрахабре. После этого нужно было сделать только одно — добавить пути к заголовочным файлам и статическим библиотекам в .pro файл следующим образом:
INCLUDEPATH += /path/to/install.armv7-linux.release/include
LIBS += -L/path/to/install.armv7-linux.release/lib
-lsomeStaticLibrary
-lanotherStaticLibrary
После этого проект с вызовом нашего С++ ядра распознавания успешно скомпилировался и запустился.
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 приложения, что не составило особого труда.
В этом году наша компания уже второй раз представила [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
Нажмите здесь для печати.