Привет Хабр! Те из вас, кто следит за нашими публикациями про мобильное распознавание документов, знает, что мы придерживаемся принципа распознавания документов только на самом устройстве. Модуль, который отвечает за распознавание и ввод данных, не должен быть уязвимее того, что он в моменте заменяет (а именно, клавиатуру). Наши технологии легко встраиваются в мобильные приложения, но что делать, когда необходимо реализовать веб-приложение с возможностями ИИ? Уступать принципам не приходится - на помощь приходит WebAssembly (или Wasm). Под катом мы расскажем, как мы портировали наши решения по распознаванию документов, банковских карт, баркодов, и всего остального, для использования в Wasm. Уверены, что вам будет интересно.
![](https://habrastorage.org/getpro/habr/upload_files/7dc/0e2/d3c/7dc0e2d3cf3384aca15169273fa30fab.png)
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 (ну сами понимаете).
Для определения поддерживаемых браузером фич и, соответственно, загрузки с сервера только нужной версии модуля распознавания, существуют две маленькие опенсорсные библиотеки:
Для загрузки подходящей сборки мы рекомендуем использовать importScripts(), поскольку новые динамические импорты (ESM) не работают в воркерах в Firefox без полифила.
Тестим!
Вот как это выглядит на iPhone 8 и на OnePlus 8T:
Основные выводы, конечно, же не могут обойтись без бенчмарка. Измерим производительность модуля распознавания на различных платформах с разными оптимизациями (в таблицах указаны средние значения).
simd.threads
Устройство | barcode | card | mrz |
Chrome 100 on | 0.30с | 0.30 с | 0.70с |
Chrome 100 on | 0.20с | 0.25с | 0.30с |
Chrome 100 on | 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 | 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 и т.д.).