Работа с ML Kit в Android: как распознавать штрихкоды

от автора

Первую в мире покупку по штрихкоду относят к 26 июня 1974 года – это была упаковка жевательной резинки в одном из супермаркетов США. Считывая информацию со штрихкода, по различным оценкам, можно ускорить операции с товарами в среднем на 30%. Сейчас штрихкоды сканируют и продавцы, и работники склада, и покупатели – например, если они хотят сделать покупку на кассе самообслуживания.

В статье рассмотрим некоторые особенности распознавания штрихкодов с помощью библиотеки ML Kit. Материал может быть полезен как начинающим разработчикам с базовыми навыками, так и опытным специалистам, которые хотят изучить новый инструмент.

ML Kit – это бесплатный мобильный SDK от Google, который позволяет использовать машинное обучение на устройствах с операционными системами Android, iOS и Flutter. В мобильной разработке это, пожалуй, простейший способ для добавления нейронных сетей в приложение. В свою очередь, это позволяет упростить реализацию некоторых функций.

Ключевые возможности ML Kit:

•    Распознавание текста (в том числе и рукописного)

•    Перевод текста между языками (офлайн)

•    Распознавание лиц (и эмоций)

•    Распознавание объектов

А также менее известные:

•    Распознавание поз (определяет местоположение головы)

•    Сканирование штрихкодов

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

Итак, перейдем к практике работы с ML Kit. В одном из проектов у нашего партнера была потребность заменить библиотеку для сканирования штрихкодов. Ранее заказчик использовал платную библиотеку Scandit и столкнулся с некоторыми ограничениями. На тот момент, в частности, требовалось выводить логотип библиотеки на экран сканирования кодов. Также лицензионное соглашение не исключало возможности того, что производитель может отозвать лицензию. В качестве альтернативного решения команда разработки выбрала ML Kit Barcode scanning.

Пример работы barcode scanning (Android)

Прежде всего, перед началом работы с ML Kit необходимо подключить необходимые библиотеки в gradle:

implementation 'com.google.mlkit:barcode-scanning:17.0.0'

Или же:

implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:16.2.1'

А также в manifest:

<meta-data     android:name="com.google.mlkit.vision.DEPENDENCIES" android:value="barcode" />

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

Далее необходимо подготовить Detector. Это основной интерфейс в ML Kit, имеющий важнейшие методы process и close: 

  • process производит всю обработку изображений и возвращает результаты, которые зависят от конкретной реализации интерфейса; 

  • с помощью сlose мы высвобождаем занятые ресурсы.

Рассмотрим процесс подготовки BarcodeScanner – одного из наследников Detector:

val detector = BarcodeScanning.getClient(     BarcodeScannerOptions.Builder()     .setBarcodeFormats(Barcode.FORMAT_QR_CODE)     .build() )

BarcodeScanning – вариация порождающего паттерна. На вход единственного метода getClient принимает параметры нужного объекта, на выходе выдает экземпляр BarcodeScanner. В свою очередь BarcodeScannerOptions создается через стандартный Builder. В данном случае мы указали, что желаем распознавать только QR коды. Этот подход относится и к остальным функциям ML Kit.

После этого можно использовать данный Detector, ниже простой пример:

detector.process(image).addOnSuccessListener { barcodes ->     barcodes.firstOrNull()?.let {     Toast.makeText(context, it.rawValue, Toast.LENGTH_SHORT).show()     } }

Возможные трудности

1) Realtime

Хотя ML Kit достаточно удобен в использовании, мы обнаружили некоторые «подводные камни». Основные вопросы оказались связаны с работой в режиме realtime. Во время реализации проекта у нас не было официальных примеров, поэтому мы изучали неофициальные примеры, и в некоторых из них были ошибки. 

Так, первоначально мы рассматривали следующий пример (особенности получения данных с камеры рассмотрим ниже).

private var imageAnalysis = ImageAnalysis.Analyzer { imageProxy ->     val image = imageProxy.image ?: return@Analyzer val inputImage = InputImage.fromMediaImage(imageProxy.image!!, imageProxy.imageInfo.rotationDegrees)     detector?.process(inputImage)?.addOnSuccessListener { barcodes ->     barcodes.firstOrNull()?.let {         Toast.makeText(context, it.rawValue, Toast.LENGTH_SHORT).show()     } } imageProxy.close() }

Хотя на большинстве устройств этот способ работал, на менее мощных он приводил к переполнению памяти. Так как detector обрабатывает кадры не мгновенно, был риск серьезной утечки памяти. Например, если каждое фото “весит” по 2 мегабайта, а в памяти одновременно находится несколько сотен кадров, это приведет к крашу приложения. 

Изучив документацию ImageAnalysis, мы выяснили, что одна из причин вызова imageProxy.close() – необходимость сообщить системе о том, что приложение готово к обработке следующего кадра.

В результате мы изменили код следующим образом:

private var imageAnalysis = ImageAnalysis.Analyzer { imageProxy ->     val image = imageProxy.image ?: return@Analyzer val inputImage = InputImage.fromMediaImage(imageProxy.image!!, imageProxy.imageInfo.rotationDegrees)     detector?.process(inputImage)?.addOnSuccessListener { barcodes ->     barcodes.firstOrNull()?.let {         Toast.makeText(context, it.rawValue, Toast.LENGTH_SHORT).show()     } }?.addOnCompleteListener {     imageProxy.close() } }

При такой реализации в памяти всегда находился только один кадр, и проблема с крашем на малопроизводительных устройствах была решена.

2) Адаптация

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

Для решения проблемы мы изменили код. Предыдущий вариант:

InputImage.fromMediaImage(imageProxy.image!!, imageProxy.imageInfo.rotationDegrees)

Новый вариант стал более сложным, с предварительной обработкой:

private fun getByteDataFromImage(image: Image): ByteArray? {     var data: ByteArray? = null     val planes: Array<Image.Plane> = image.planes if (planes.isNotEmpty()) {     val buffer: ByteBuffer = planes[0].buffer     data = ByteArray(buffer.remaining())     buffer.get(data)     }     return data } getByteDataFromImage(image)?.let { byteArray ->     processImage(byteArray, imageProxy)     val inverted = ByteArray(byteArray.size)     byteArray.forEachIndexed { index, byte ->     inverted[index] = byte.inv()     } processImage(inverted, imageProxy) }

Здесь мы получали картинку как массив байтов и разделяли ее на позитив и негатив, которые отправляли по отдельности в detector.

3) CameraX

Ещё один баг, с которым мы столкнулись, касался неправильного использования разрешения в CameraX. Мы ставили максимальное разрешение 1920×1080.

val camProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_1080P) analysisBuilder.setTargetResolution(Size(camProfile.videoFrameWidth,                                      camProfile.videoFrameHeight))

Однако, в CameraX на выходе получались дефолтные 320×640. Мы выяснили, что порядок width и height зависит от ориентации, и для “портретного” вывода в нашем случае нужно было следующее:

val camProfile = CamcorderProfile.get(CamcorderProfile.QUALITY_1080P) analysisBuilder.setTargetResolution(Size(camProfile.videoFrameHeight,                                      camProfile.videoFrameWidth))

Заключение

После настройки и внедрения в проект мы убедились, что ML Kit соответствует потребностям приложения и может заменить предыдущую библиотеку. В некоторых случаях платная библиотека была эффективнее, например, на небольшой доле «смазанных» штрихкодов. В свою очередь, библиотека ML Kit полностью бесплатная и не требует добавления своего логотипа на экран сканирования. В результате после тестирования владелец приложения решил полностью перейти на ML Kit.

Спасибо за внимание! Надеемся, что материал был вам полезен. 


ссылка на оригинал статьи https://habr.com/ru/articles/584888/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *