.. SimpleUI documentation master file, created by
sphinx-quickstart on Sat May 16 14:23:51 2020.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Компьютерное зрение и дополненная реальность ActiveCV
========================================================
.. image:: _static/2025_cv_1.png
:scale: 75%
:align: center
ActiveCV – это технология автоматизации бизнес-процесса, когда все необходимые данные бизнес-процесса выводятся не на экран, а сразу в видеопоток, при этом с камерой работают различные детекторы: штрихкодов, OCR, лиц и т.д. Также смысл сего действия в беспрерывной работе оператора без необходимости каких то переключений. Например, запустил ActiveCV – отсканировал код помещения, не прерываясь и ничего не нажимая переключился на сканирование инвентарных кодов оборудования, VIN-ов, далее серии фото этого оборудования и все это, не переключаясь на обычные экраны с кнопками. И пошел дальше к другим объектам.
Важным свойством технологии является подсветка объектов разными цветами – цветовая маркировка. Примеры цветовой маркировки:
* объект находится там, где надо — зеленый цвет, там, где не надо — красный.
* объект проинвентаризирован — зеленый цвет, не проинвентаризирован — желтый.
* заказ просрочен по дедлайну — красный, подходит срок — желтый, не просрочен — зеленый
.. image:: _static/2025_cv_3.png
:scale: 75%
:align: center
Примеры (лучше смотреть видео или GIFы) по технологии собраны в этих статьях (на платформе было 2 генерации ActiveCV: первая, в виде самостоятельно процесса - устарела, в виде элмента экрана - актуальная):
* https://habr.com/ru/articles/874560/
* (устар.) https://infostart.ru/1c/articles/1427287/
* (устар.) https://infostart.ru/1c/articles/1486598/
* (устар.) https://infostart.ru/1c/tools/1882131/
Обзор механизмов работы.
-----------------------------
Размещение визуального элемента.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. image:: _static/2025_cv_2.png
:scale: 75%
:align: center
Размещение на экране ничем не отличается от других элементов контейнера, но как и например HTML-поле требует чтобы была **отключена прокрутка корневого контейнера**. Сам визуальный элемент контейнера называется ActiveCV. Можно разместить на часть контейнера, весь экран. И также с помощью команды RunCV2 можно запустить полноэкранный режим без каких либо дополнительных элементов в отдельном окне.
Разрешение
~~~~~~~~~~~~
Можно задавать разрешение для детектора **CameraSetResolutionAnalysis** и для фото **CameraSetResolutionImage**. Разрешение предпросмотра меняться не будет – оно подстраивается автоматически. Также заданное разренеие может не поддерживаться (особенно для детектора) – будет выставлено максимально возможное
Возможные разрешения: ``"4K"(4096*2160))``, ``"2K"(2048*1080)``, ``"1080"(1920*1080)``, ``"720"(1280*720)``, ``"640"(640*480)``, ``"360"(360*240)``, ``"200"(200*200)``, ``"100"(100*100)``
Соответственно, чем меньше разрешение (особенно детектора) тем быстрее и плавнее работает визуальная составляющая.
Цикл работы детекторов. Общее.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Детектор включается/переключается командой **CameraSetDetector**, где параметром указывается тип или типы детекторов. Сейчас доступны ``BARCODE``, ``OCR``, ``FACE`` и ``PHOTO``.
Если нужно совместить несколько – то через нижнее подчеркивание. Например, ``BARCODE_PHOTO``
Когда появляется новый объект в кадре, который еще не был распознан срабатывает событие (listener) **new_text_detected** или **new_barcodes_detected** в зависимости от детектора. В стеке доступна строка с JSON-массивом объектов кадра - **detected_values**. Наполнение распознанных элементов зависит от детектора. В обработчике этого события возможно задать внешний вид распознанных объектов.
Отображение объектов
~~~~~~~~~~~~~~~~~~~~~~~~
Можно переопределять заголовки распознанных объектов и задавать цвет рамки над ними. Это все хранится в одном списке **SetObjectsView** в виде JSON-массива объектов с полями id, color (HEX-формат) и caption. Id – это соответственно штрихкод или текст.
Для медленных устройств отображение упрощенное. Для быстрых доступна HTML-строки в заголовках объектов с помощью команды **CameraSetPrettyView**. Т.е. можно например написать в caption ``"Товар такой то, остаток такой то "``. Также в PrettyView секции заголовка выстраивается по размеру объекта, а не текста, т.е. происходят переносы. Для такого отображения дополнительно с SetObjectsView нужно добавить команду CameraSetPrettyView
Ручное управление списком детектированных объектов.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
По умолчанию на новые объекты вызывается обработчик **new_..._detected** (**new_barcodes_detected***, **new_text_detected**) и после этого они уже перестают считаться «новыми», на них события не вызываются. Но можно управлять этим вручную с помощью флага **CameraSetOCRDetectedListManual**, пустой параметр, затем ручная регистрация с помощью **CameraOCRAddDetected**, параметр – список ID. Также доступно **CameraClearDetected**, пустой параметр для того, чтобы просто сбросить список всех детектированных объектов.
Подключение валидатора
~~~~~~~~~~~~~~~~~~~~~~~~~~
Принцип работы один для все детекторов, но вызывается разными командами, чтобы совместить в одном датасете поля, подлежащие валидации. Например, в примерах в одном датасете артикул и штрихкод. Для штрихкодов команда **CameraSetBarcodeValidator** для OCR **CameraSetOCRValidator**
В качестве параметра – объект типа ``{"dataserver":<имя датасета>,"keys":[массив имен hash-индексов]}`` Записи в датасете имеют поле _Id (если оно не задано, то задается автоматически, но можно записывать сразу с _id) – это первый индекс. Также можно добавить хаш-индексы для любых полей в настроки датасете при создании. Например
``CreateDataSet("goods",json_to_str({"hash_keys":["article","barcode"]}))``
По этим полям и будет производиться поиск и валидация. Когда подключен валидатор, то события возникают только если запись найдена в валидаторе. Также в случае с валидатором при нахождении записи в выдаваемое значение будет добавлена сама запись датасета целиков в поле result.
Смена фронтальной и тыловой камеры
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**CameraSetSelector**,<режим> - если режим="front" то камера-фронтальная, если "back"-обычная
Задание рамки
~~~~~~~~~~~~~~~~~~~~~~
На экране можно вывести рамку, тогда значения будут считываться только из нее, игнорируя пространство вне рамки
**CameraSetFrame**,<строка параметров> - задает рамку в процентах от размера элемента ActiveCV в формате <процент_x1>_<процент_y1>_<процент_x2>_<процент_y2>
Например:
.. code-block:: Python
hashMap.put("CameraSetFrame","20_45_80_55")
Зум
~~~~~
**CameraSetZoom**, <параметр> – число требуемого приближения (стек перменных строковый, поэтому и числа и другие параметры в виде строки).
Остановка видеопотока.
~~~~~~~~~~~~~~~~~~~~~~~~
**CameraStopDetectorOnNewObjects* - включение режима, когда предпросмотр камеры встает на паузу автоматически при обнаружении объекта.
Альтернатива – использование из кода обработчика команды **CameraStop**.
Возобновляется – обновлением экрана.
Фонарик
~~~~~~~~~
**CameraTorchTurnOn** – включает подсветку камеры (если есть аппаратная возможность)
Запуск в отдельном экране с возвратом значения
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**RunCV2, ** - запускает из экрана ActiveCV на весь экран до считывания первого результата, после чего закрывает камеру и генерирует событие с указанным в параметре именем события. Эта возможность для ситуаций, когда что-то нужно быстро считать, а размещать на экране элемент ActiveCV не хочется или нет возможности (экран маленький).
При этом в onStart вызывающего экрана нужно указать все опции как и для объекта camera (CameraSetResolutionAnalysis, CameraSetDetector и так далее). В указанный в команде параметр (listener) при этом возвращаются detected_values и в целом работа с обработкой результата аналогична, с той только разницей, что раскраска и подписи объектов SetObjectsView не имеют смысла.
В примере в этой статье(вариант для ТСД) https://infostart.ru/1c/tools/2364633/ я использую чисто для распознавания OCR на новом движке ActiveCV2 для ТСД-варианта. На ТСД не нужен сканер через камеру (свой есть), а вот OCR нужен, но размещать на экране ActiveCV негде (экран маленький).
Особенности детектора штрихкодов (BARCODE)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**CameraSetSupportedBarcodes** задает список доступных штрихкодов через нижнее подчеркивание. Например: ``hashMap.put("CameraSetSupportedBarcodes","QR_EAN13")``
Если не задано, либо задано ALL то сканируются все.
Список доступных форматов: ``QR``, ``EAN13``, ``AZTEC``, ``CODABAR``, ``CODE_93``, ``CODE_39``, ``CODE_128``, ``DATA_MATRIX``, ``EAN_8``, ``ITF``, ``UPC_A``, ``UPC_E``
**CameraSetCurrentBarcodeDetector** задает список текущих форматов штрихкодов при динамическом переключении. Формат аналогичен CameraSetSupportedBarcodes. При этом
CameraSetSupportedBarcodes задает форматы которые камера вообще способна считывать. Это так сказать – для ускорения работы и отсечки возможных ошибок. А CameraSetCurrentBarcodeDetector для переключения между форматами в процессе работы.
Массив штрихкодов в **detected_values** включает в себя объекты с полями: **value** – штрихкод как есть (со спецсимволами если они есть), **display_value** – отображаемое значение, **format** – формат штрихкода. Ну и **result** ,если используется валидатор, с непосредственно записью датасета.
Особенности детектора лиц (FACE)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
При детектировании лиц, результаты возвращаются в событии new_faces_detected в переменную detected_values в виде массива объектов с ключами id(номер объекта) и value(Base64 упакованное изображение лица)
.. code-block:: Python
values = str_to_json(hashMap.get("detected_values"))
faces_list = []
for value_item in values:
faces_list.append({"_id":value_item["id"],"picture":value_item["value"]})
Особенности OCR
~~~~~~~~~~~~~~~~~
Цикл обработки блоков текста включает в себя несколько этапов. Все они происходят очень быстро так как выполняются платформой. Поэтому крайне рекомендую не отдавать в обработчики сырой текст, пропущенный через слабые фильтры и обрабатывать его как есть там – это будет гораздо более тормозящий вариант чем использование масок, валидаторов и предобработки.
Обработка текста в ActiveCV
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Итак, текст может быть подвергнут предобработке, после чего к нему применяются Regex-маски, после чего могут выполниться еще процедуры предобработки (часть настроек работает до масок- часть после), после чего он либо попадает на валидатор либо отдается в обработчик new_text_detected как есть. Если задача к примеру выделить все даты в кадре то валидатор не нужен, а если сверить инвентарные номера - то подключаем валидатор.
Команда **CameraSetOCRFormatOptions** задает опции предобработки текста. Она может включать в себя несколько действий через нижнее подчеркивание:
* CLEARSPACES – убирает различные пробелы
* LOWER -преобразует к нижнему регистру
* UPPER – преобразует к верхнему регистру
* TOZERO – преобразует букву О в ноль
И часть опций, которая выполняется уже после отбора Regex:
* DATE, INT, FLOAT – нативная проверка текста на соответствующий тип
Команда **CameraSetOCRMask** – задает JSON массив строк-масок. Каждая маска представляет из себя Regex-выражение. Например, "([a-zA-Z0-9-.]{5,10})" - это маска, для поиска подстрок включающих в себя символы латинского алфавита и цифры общей длиной от 5 до 10 символов. Удобно проверять маски через редакторы regex-выражений, например https://regex101.com/ Каждая маска последовательно применяется, приоритет имеет та, которая стоит раньше в массиве.
**CameraOCRListOnly** флаг чтобы выводились не только текст после валидатора, если он есть.
**detected_values** в OCR содержат в себе поля:
* value - текст после всех преобразований
* confidence - точность определения
* result - запись валидатора
Примеры обработчика new__detected + SetObjectsView:
.. code-block:: Python
jvalues = str_to_json(hashMap.get("detected_values")) #получим массив распознанных объектов
#читаем ранее созданнуюю раскраску объектов или создаем новую
if hashMap.containsKey("SetObjectsView"):
objects = json.loads(hashMap.get("SetObjectsView"))
else:
objects = []
for item in jvalues :
res = item["result"]
#Такой цвет(color) и заголовок(caption) будет у новых объектов
cv = {"id":item["value"],"color":"#82e0aa","caption":res.get("name")}
id = item["value"]
#Ищем в уже существующей раскраске такие объекты (у них может быть другой цвет)
itemarr = next((itemo for itemo in objects if itemo["id"] == id), None)
if itemarr == None:
objects.append(cv) #если нет, то добавляем
else: #если есть, то перекрашиваем
itemarr["color"] = cv["color"]
itemarr["caption"] = cv["caption"]
hashMap.put("SetObjectsView",json_to_str(objects)) #применяем раскраску
hashMap.put("noRefresh","") #запрещаем перерисовку экрана
Особая версия с EasyOCR
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
В виде apk (не из Play Market) доступна отдельная версия с адаптированным EasyOCR и кастомной моделью распознавания. С ней распознавание не работает в стиле обычного OCR ввиду того, что такое распознавание гораздо менее производительное.
Работать с ней так:
* в onStart включить флаг **CameraOCR2**
* заранее инициализировать объект ридера: ``import easyocr reader = easyocr.Reader(['ru'],gpu=True) ``
* включить рамку CameraSetFrame
* детектор - "OCR" (прочие настройки детектора не важны)
* в момент, когда нужно запустить сканирование (нужный текст попал в рамку) поместить команду **CameraScanOCR2**
* возникнет событие **new_image_capture** и в переменную **image_file** будет помещен абсолютный путь к файлу изображения (обрезанного по рамке, в случае использования рамки)
* распознать файл ``reader.readtext(filename, detail=0,batch_size=5)``