Привет Хабр! Те из вас, кто следит за нашими публикациями про мобильное распознавание документов, знает, что мы придерживаемся принципа распознавания документов только на самом устройстве. Модуль, который отвечает за распознавание и ввод данных, не должен быть уязвимее того, что он в моменте заменяет (а именно, клавиатуру). Наши технологии легко встраиваются в мобильные приложения, но что делать, когда необходимо реализовать веб-приложение с возможностями ИИ? Уступать принципам не приходится - на помощь приходит WebAssembly (или Wasm). Под катом мы расскажем, как мы портировали наши решения по распознаванию документов, банковских карт, баркодов, и всего остального, для использования в Wasm. Уверены, что вам будет интересно.

WebAssembly - формат бинарного кода для виртуальной машины, в который можно компилировать программы на C, C++, C#, Rust или Go и который браузеры могут транслировать в нативный бинарный код для той машины, на котором запущены. WebAssembly разрабатывается с 2015-го года и в данный момент развивается такими компаниями, как Mozilla, Microsoft, Google, Apple, Intel, Red Hat и пр. В настоящее время Wasm уже поддерживают (с разными ограничениями, но об этом позже) наиболее популярные браузеры: Firefox, Chrome, Microsoft Edge, Safari и другие.

Наш основной интерес к технологии Wasm связан с возможностью обработки чувствительной информации в браузере без участия сервера. Обработка цифровых изображений, извлечение текстовых данных из документов, бланков, банковских карт, чтение баркодов происходят только в памяти браузера локальной машины без необходимости пересылки данных по сети, минимизируя риски утечек персональных, биометрических, и всяких других данных. 

Благодаря Wasm, многие тяжелые или платформозависимые приложения теперь можно моментально разворачивать на любом устройстве с выходом в интернет и современным браузером: вместо скачивания и установки приложения пользователю теперь нужен только один клик по ссылке, чтобы получить готовую систему по оптическому извлечению данных с веб-камеры или сканера. 

Более того, даже если целевой кейс - облачные вычисления, но предоставить такую возможность всем клиентам не представляется возможным (проблемы с горизонтальным масштабированием, поддержкой или арендой инфраструктуры и т.п.) Wasm выступает практически безальтернативной технологией. Ему удалось, по крайней мере в области намерений, найти баланс между удобством интеграции, производительностью и безопасностью, путь к которому прослеживается с java-апплетов, Flash, Silverlight, ActiveX и прочего.

Одна из идей Wasm состояла (и состоит) в том, чтобы выполнять браузером вычисления со скоростью, максимально приближенной к нативному приложению. Хотя Wasm - технология достаточно молодая, современная реализация уже позволяет производить сложные вычисления, даже такие, как обработка изображений и распознавание, с достаточно высокой производительностью. В нашем случае, используя все оптимизации, модуль распознавания уже обрабатывает документы за время, немногим уступающее решению, скомпилированным под нативную платформу, как мы и продемонстрируем в конце статьи.

Всякое ли технологическое решение, реализованное в виде той или иной нативной библиотеки, может быть портировано на Wasm так, чтобы все вычисления выполнялись кроссплатформенно, прямо в браузере? Вынося за скобки ряд деталей можно сказать, что да, если все компоненты решения имеются на уровне исходного кода. Нам это сделать удалось – т.к. наша библиотека не имеет никаких внешних зависимостей, весь код системы, алгоритмы обработки изображений, исполнение нейросетей и т.п. – все написано нами, а значит мы можем легко собрать все из исходников и модифицировать при необходимости.

Обертки интерфейсов и модули для встраивания мы создаем всегда на основе С++ интерфейса нашей главной библиотеки. Это позволяет легко интегрироваться в системы, написанные на разных языках программирования и практически на любых платформах, сохраняя логику, заложенную в интерфейс.

Существует множество инструментов для портирования существующей кодовой базы в Wasm модуль. Для языка С++  существует проект Emscripten, который включает в себя инструмент EmBind. Он позволяет максимально простым способом обернуть классы, их поля и методы в объекты, видимые из js. В принципе, это уже позволяет создать минимальное рабочее приложение или библиотеку, однако Wasm постепенно обрастает поддержкой разного рода оптимизаций, которые хотелось бы использовать по-максимуму. 

Для Wasm существует список фич, которые поддерживаются лишь частью существующих браузеров. В их числе как раз используемые нами многопоточность и SIMD-инструкции.

Конечно, не всe заработало сразу - процесс разработки быстро превратился в путь, полный чудес и новых открытий. Первым делом разбирались с дебагом и эксепшнами - exception’ы в васме реализованы через лютый костыль:  программе дают “упасть”, но не удаляют из памяти. Это позволяет взять адрес последнего возвращенного объекта и через cast представить как объект типа exception, после чего из него можно достать exception message и вернуть в консоль браузера. Пришлось заводить специальную функцию в модуле под это дело. В будущем обещают нормальную поддержку exception.

Больше всего времени мы потратили на имплементацию многопоточности. Многопоточность в js эмулируется с помощью воркеров. На этапе инициализации создается пул воркеров, в которых и происходит параллельное выполнение разных частей кода. Методом проб и ошибок выяснилось эмпирическое правило: количество одновременно выполняемых тредов внутри системы не может превышать количество созданных воркеров. Другими словами, создать сотню тредов, открепить их и дождаться результатов выполнения, вы не сможете. Для себя мы решили эту проблему, приравняв количество создаваемых для задачи воркеров и количество одновременно выполняющихся внутри библиотеки тредов, к количеству потоков, доступных системе. Для этого пришлось переделать весь механизм обеспечения многопоточности и внедрить очередь из задач, ожидающих выполнения на доступных системе потоках.

Неполная поддержка SIMD-инструкций, проблемы с i64 пойнтерами на старых архитектурах, слабая документация - но мы в итоге победили.

Для того, чтобы сделать наше распознавание доступным для максимального количества браузеров, в принципе поддерживающих Wasm, мы готовим сразу четыре варианта сборки: 

  • simd.threads (симды и многопоточность);

  • simd.nothreads (симды без многопоточности);

  • nosimd.threads (многопоточность без симдов);

  • nosimd.nothreads (ну сами понимаете).

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

  1. https://github.com/GoogleChromeLabs/wasm-feature-detect (Apache 2).

  2. https://github.com/MaxGraey/wasm-check (MIT).

Для загрузки подходящей сборки мы рекомендуем использовать importScripts(), поскольку новые динамические импорты (ESM) не работают в воркерах в Firefox без полифила.

Тестим!

Вот как это выглядит на iPhone 8 и на OnePlus 8T:

Основные выводы, конечно, же не могут обойтись без бенчмарка. Измерим производительность модуля распознавания на различных платформах с разными оптимизациями (в таблицах указаны средние значения).

simd.threads

Устройство

barcode

card

mrz

Chrome 100 on
OnePlus 8T (Qualcomm 865 Octa-core)

0.30с

0.30 с

0.70с

Chrome 100 on
Ryzen 7 3700X 8-Core

0.20с

0.25с

0.30с

Chrome 100 on
microsoft surface (Pentium 4415Y dual-core)

0.50с

0.90с

2.00с

nosimd.nothreads:

Устройство

barcode

card

mrz

Chrome 100 on OnePlus 8T (Qualcomm 865 Octa-core)

0.55с

0.35с

1.70с

Chrome 100 on
Ryzen 7 3700X 8-Core

0.60с

0.30с

0.90с

Safari 14 on Apple iPhone 8

0.25с

0.45с

1.65с

Chrome 100 on Microsoft Surface (Pentium 4415Y dual-core)

0.40с

1.30с

4.50с

В таблицах выше мы представили результаты замеров двух крайних случаев: работу с оптимизациями (SIMD, многопоточность) и без, на самых разных машинах. 

Планшет и мобильные телефоны работали со штатной камерой, компьютер же был в связке с Microsoft LifeCam Studio. Изображение ограничено 640х480 (размер canvas, на который мы выводили видеопоток). 

iPhone представлен только в колонке без оптимизаций - работу с SIMD-инструкциями на айфоны еще не подвезли, многопоточность присутствует только на старших моделях, да и то работает с ограничениями. 

Surface - машинка с посредственной камерой и довольно средним железом выступает довольно типичным примером кейса работы на тонком клиенте (кассы или рабочие места банковских служащих).

Заключение

В целом, Wasm показал себя прекрасным решением для безопасного кроссплатформенного распознавания. Производительность его уже сегодня вполне достаточна для выполнения даже вычислительно сложных задач, таких, как распознавание документов. Таким образом, у всех наших организаций теперь имеется хороший инструмент сделать удобные веб-приложения, обладающие привычными функционалом на базе ИИ, без привязки к магазинам приложений (App Store, Google Play и т.д.).

Комментарии (1)


  1. caesarisme
    22.04.2022 10:38
    +1

    Спасибо за статью. Радует, что у браузеры все больше и больше развиваются и позволяют делать подобного рода вещи