Введение

Изменение архитектуры React Native на Fabric и, следовательно, использование синхронной связи между нативной частью и JS позволило создавать библиотеки, обеспечивающие высокую производительность, недостижимую ранее. Одна из библиотек, обеспечивающих такую ​​коммуникацию, — это react-native-reanimated, которую я буду использовать вместе с react-native-vision-camera для создания приложения, позволяющего обнаруживать элементы с камеры устройства в режиме реального времени с помощью библиотеки OpenCV.

OpenCV — это кроссплатформенная библиотека с открытым исходным кодом для обработки изображений, разработанная на C++, но с портами для других языков, таких как Java, JavaScript и Python. Однако в настоящее время не хватает значимых библиотек для React Native, которые позволили бы легко использовать функциональность OpenCV непосредственно в JS-коде. Однако с помощью появляется возможность использовать собственный код и создавать связь между собственным потоком и потоком JS. Стандартный подход, описанный в некоторых постах, позволяет создать бридж, с которым общение будет происходить асинхронно. Однако это подход, который часто работает недостаточно эффективно, чтобы выполнять обнаружение или преобразование в режиме реального времени.

Подробности о библиотеке OpenCV можно найти здесь: https://opencv.org. Информацию о react-native-vision-camera и нативных кадровых процессорах можно найти здесь: https://mrousavy.com/react-native-vision-camera/docs/guides/frame-processors

Важное примечание: запись была создана с использованием OpenCV 4.6.0 и React Native 0.68.2. Для других версий особенно шаги по импорту OpenCV в проект могут отличаться.

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

Первым шагом будет создание нового приложения с помощью команды:

Процессор инициализации opencvframe с помощью npx react-native

После установки необходимых pod’ов и создания каталогов приступаем к импорту OpenCV в наш проект, отдельно для iOS и Android.

Импорт OpenCV для iOS

Первым шагом будет перейти на https://opencv.org/releases/ и загрузить версию OpenCV для iOS. В моем случае это версия 4.6.0.

После загрузки библиотеки запускаем наш проект в Xcode (не забудьте сделать его проектом с расширением .xcworkspace). Чтобы импортировать библиотеку, мы перетаскиваем скачанный каталог с именем opencv2.framework в основной проект (левая панель окна).

Затем установите флажок «Копировать элементы, если необходимо» и нажмите «Готово». Библиотека должна появиться на панели в левой части окна.

Следующим шагом будет присоединение необходимых фреймворков к проекту. Мы можем сделать это в настройках проекта -› Фазы сборки -› Связать двоичный файл с библиотеками. В проект необходимо добавить следующий список элементов:

  • QuartzCore.framework
  • CoreVideo.framework
  • CoreImage.framework
  • AssetsLibrary.framework
  • CoreFoundation.framework
  • CoreGraphics.framework
  • CoreMedia.framework
  • Ускорение.framework

Следующим шагом будет создание файлов поддержки OpenCV в проекте. Мы создаем файлы в корневом каталоге проекта (где находятся файлы AppDelegate.h и AppDelegate.m).

Сначала мы создаем новый файл с нашим заголовком — назовем его OpenCV.h.

В нем мы объявляем новый класс и пример метода для получения версии OpenCV. Для этого мы используем следующий код.

Затем мы создаем новый файл Objective-C с именем OpenCV.m.

Поскольку библиотека OpenCV написана с использованием C++, необходимо будет изменить формат создаваемого файла на формат .mm (другими словами, Objective C++). Мы можем сделать это на правой панели (дописав формат к имени файла).

Затем в созданном файле создаем код, реализующий класс из заголовочного файла.

Следующим шагом будет создание файла PCH, в который мы добавим информацию о том, что библиотеке OpenCV потребуется компилятор для Objective C++. Для этого мы добавим новый файл PCH с именем PrefixHeader в расположение других ранее созданных файлов.

И задаем ему следующий контент:

Далее в настройках проекта нам нужно указать его расположение. В разделе Настройки сборки -> Префикс заголовка мы добавляем запись следующего содержания: ${PROJECT_DIR}/PrefixHeader.pch.

После этого проверяем, собирается ли приложение — если да, то наша библиотека добавлена ​​корректно и можно переходить к следующим шагам.

Импорт OpenCV для Android

Чтобы загрузить библиотеку OpenCV для Android, мы возвращаемся на https://opencv.org/releases/, однако на этот раз мы выбираем пакет Android.

Скачав и распаковав архив, открываем наш проект в Android Studio. Первым шагом для импорта нашего модуля будет выбор Файл -> Импорт модуля и указание местоположения в каталоге /sdk (обратите внимание! это не будет каталогом sdk/java). Мы называем библиотеку, например. openCVLib, а остальные параметры оставьте по умолчанию.

Далее нам нужно добавить поддержку языка Kotlin. В файле build.gradle добавляем следующее:

Мы приступаем к добавлению библиотеки в качестве зависимости для проекта. В меню Файл выберите Структура проекта.

Мы переходим на вкладку Зависимости и нажимаем значок +, выбирая Зависимость модуля. На следующем шаге мы выбираем нашу библиотеку OpenCV и добавляем зависимость.

Следующим шагом будет добавление файлов jniLibs в наше приложение. В каталоге app/src/main создаем каталог jniLibs и копируем туда содержимое каталога sdk/native/libs из скачанного ранее архива.

В файл app/build.gradle мы добавляем следующую строку, чтобы исправить ошибку при сборке приложения.

Далее нам нужно проверить, правильно ли была импортирована библиотека. В файле MainActivity.java мы импортируем пакет библиотеки.

А в класс MainActivity добавляем статическое поле:

После сборки и запуска приложения в журналах должно появиться сообщение о том, что OpenCV загружен.

Установка необходимых библиотек

Как я упоминал ранее, следующим шагом будет добавление в наш проект библиотек Vision Camera и Reanimated. Для этого запускаем команды в корневом каталоге проекта React Native:

добавить пряжу

npx pod-установить

Чтобы правильно добавить библиотеку и добавить необходимые разрешения, пройдите процесс установки, описанный здесь: https://mrousavy.com/react-native-vision-camera/docs/guides.

Кадровый процессор для iOS

Для того, чтобы создать новый процессор кадра для библиотеки Vision Camera, необходимо создать файл, в который мы зашиваем логику. Однако прежде чем мы это сделаем, нам нужно расширить наш файл OpenCV.mm функциями для обнаружения объектов. В нашем случае это будет обнаружение синего квадрата. Обработчик кадров по умолчанию возвращает нам кадр с камеры в виде объекта с типом CMSampleBufferRef, а потому необходимо будет подготовить функцию, которая позволит нам преобразовать его в стандартное используемое изображение в iOS с типом UIImage. Мы можем сделать это с помощью функции (давайте добавим ее в класс OpenCV в файле OpenCV.mm):

Библиотека OpenCV выполняет операции над так называемыми матрицами. Следовательно, нам понадобится функция, которая позволит нам преобразовать UIImage в объект Mat. Мы можем сделать это, например, следующим образом:

Далее добавим функцию, в которой будет реализовано обнаружение синих объектов.

Наше обнаружение будет происходить следующим образом:

  • Мы преобразуем изображение, которое по умолчанию сохраняется в формате RGB, в BGR, а затем в HSV.
  • Исходя из диапазона, мы будем резать только интересующий нас цвет (это будет синий).
  • Мы обнаружим контуры синих элементов.
  • Первым элементом, превышающим указанное значение, будет наш обнаруженный элемент, поэтому мы вернем его позицию и размер.

Итак, для начала давайте укажем наши диапазоны значений. Для синего цвета это будут, например:

Далее давайте выполним необходимые преобразования цвета.

Далее давайте обнаружим наш контур и вернемся в виде объекта NSDictionary (чтобы его можно было подобрать на стороне JS).

При отсутствии таких элементов в кадре вернуть пустой объект.

Нам нужно добавить функции, которые мы добавили в заголовочный файл, то есть OpenCV.h. После изменений это будет выглядеть следующим образом.

Далее нам нужно создать новый файл с нашим процессором Frame. Назовем его ObjectDetectFrameProcessor.mm и добавим в него следующий код.

Такой добавленный код мы уже можем использовать в JS-коде. Но сначала давайте добавим аналогичный функционал и для Android.

Фрейм-процессор для Android

Формат по умолчанию, возвращаемый библиотекой Vision Camera в процессоре Frame для Android, — ImageProxy. Чтобы добавить его поддержку в файл app/build.gradle в разделе dependencies, нам нужно добавить:

Давайте добавим файл OpenCV.java, который будет содержать функцию findObject, которая будет отвечать за обнаружение синих объектов. Кроме того, давайте добавим вспомогательный метод для преобразования объекта ImageProxy в объект Mat.

Чтобы добавить процессор кадров, нам нужно создать файл ObjectDetectFrameProcessorPlugin.java со следующим содержимым:

И ObjectDetectFrameProcessorPluginModule.java:

Затем модуль необходимо зарегистрировать. В файле MainApplication.java под строкой:

Мы добавляем новую запись с нашим модулем.

Подготовленный таким образом модуль теперь готов к использованию в JS-коде.

Использование процессоров Frame на стороне JS

Чтобы разрешить использование процессора фреймов на стороне приложения, нам нужно добавить возможность плагина react-native-reanimated обнаруживать его. Для этого нам нужно добавить соответствующую запись в файл babel.config.js (расположенный в корневом каталоге приложения).

Название __objectDetect не случайно, мы дали такое же имя в нативном коде наших процессоров. Мы просто добавляем символы «__» в начале имени.

Перейдем к файлу App.js. Сначала нам нужно объявить нашу функцию ответственной за вызов нативного кода.

Затем в компоненте приложения мы добавляем наш код. Во-первых, начнем с объявления места для хранения параметров обнаруженного квадрата.

Используя хук useSharedValue, мы можем передавать значения положения и размера квадрата непосредственно в стиль, используя хук useAnimatedStyle. Оба взяты из библиотеки react-native-reanimated.

Также важно проверить разрешения камеры, без этого мы не сможем запустить камеру.

Перейдем к объявлению фреймового процессора. Как только объект обнаружен, нам нужно преобразовать значения положения и размера из кадра с камеры в размеры разрешения экрана устройства (по той причине, что они разные). Из-за того, что размер кадра на iOS задается обратно пропорционально, чем на Android, нам необходимо выполнить преобразование размера.

Затем наш компонент должен вернуть компонент ‹Camera /› и анимированный квадрат.

Весь файл выглядит следующим образом:

Полученные результаты

Давайте проверим, как наш код работает в обеих системах. Начнем с iOS.

С другой стороны, на Android наше приложение работает следующим образом:

Краткое содержание

Процесс импорта библиотеки OpenCV и ее использования для обнаружения объектов в реальном времени — непростая задача. Множество версий и способы их использования часто создают множество трудноразрешимых проблем. Тем не менее, результат является достаточной наградой за путешествие. К сожалению, основная проблема с использованием OpenCV в приложениях React Native заключается в необходимости создавать собственный код, будь то на Java (или Kotlin) в случае Android или на Objective C/C++ (или Swift) в случае iOS.

Вы можете найти полный репозиторий с кодом здесь: https://github.com/dogtronic/blog-opencv-frame-processor

Рекомендации

https://brainhub.eu/library/opencv-react-native-image-processing, отличный пост, в котором рассказывается, как импортировать OpenCV в React Native, но, к сожалению, он не актуален для более новых версий библиотеки.

https://opencv.org, домашняя страница библиотеки OpenCV, включая документацию и примеры использования.

https://mrousavy.com/react-native-vision-camera/docs/guides/frame-processors, документация, включающая примеры использования процессоров кадров.

Вы можете найти польскую версию статьи здесь: https://dogtronic.io/tworzenie-natywnych-procesorow-klatek/

Найдите нас на нашем сайте Dogtronic.