Вы можете использовать ML Kit для распознавания и декодирования штрих-кодов.
Прежде чем начать
- Если вы еще этого не сделали, добавьте Firebase в свой проект Android .
-  Добавьте зависимости для библиотек Android ML Kit в файл Gradle вашего модуля (на уровне приложения) (обычно app/build.gradle):apply plugin: 'com.android.application' apply plugin: 'com.google.gms.google-services' dependencies { // ... implementation 'com.google.firebase:firebase-ml-vision:24.0.3' implementation 'com.google.firebase:firebase-ml-vision-barcode-model:16.0.1' } 
Рекомендации по входному изображению
- Чтобы ML Kit мог точно считывать штрих-коды, входные изображения должны содержать штрих-коды, представленные достаточным количеством пиксельных данных. - Требования к конкретным пиксельным данным зависят как от типа штрих-кода, так и от объема данных, которые в нем закодированы (поскольку большинство штрих-кодов поддерживают полезную нагрузку переменной длины). Как правило, наименьшая значимая единица штрих-кода должна иметь ширину не менее 2 пикселей (а для двумерных кодов — высоту 2 пикселя). - Например, штрих-коды EAN-13 состоят из полос и пробелов шириной 1, 2, 3 или 4 единицы, поэтому изображение штрих-кода EAN-13 в идеале содержит полосы и пробелы длиной не менее 2, 4, 6 и более. Ширина 8 пикселей. Поскольку общая ширина штрих-кода EAN-13 составляет 95 единиц, ширина штрих-кода должна быть не менее 190 пикселей. - Более плотные форматы, такие как PDF417, требуют большего размера в пикселях, чтобы ML Kit мог их надежно читать. Например, код PDF417 может содержать до 34 «слов» шириной 17 единиц в одной строке, которая в идеале должна иметь ширину не менее 1156 пикселей. 
- Плохая фокусировка изображения может снизить точность сканирования. Если вы не получили приемлемых результатов, попробуйте попросить пользователя повторно сделать снимок. 
- Для типичных приложений рекомендуется предоставлять изображение с более высоким разрешением (например, 1280x720 или 1920x1080), что позволяет обнаруживать штрих-коды на большем расстоянии от камеры. - Однако в приложениях, где задержка имеет решающее значение, вы можете повысить производительность, захватывая изображения с более низким разрешением, но требуя, чтобы штрих-код составлял большую часть входного изображения. Также см. Советы по повышению производительности в реальном времени . 
1. Настройте детектор штрих-кода
Если вы знаете, какие форматы штрих-кодов вы собираетесь считывать, вы можете повысить скорость детектора штрих-кодов, настроив его на обнаружение только этих форматов. Например, чтобы обнаружить только ацтекский код и QR-коды, создайте объект FirebaseVisionBarcodeDetectorOptions как показано в следующем примере: 
Java
FirebaseVisionBarcodeDetectorOptions options = new FirebaseVisionBarcodeDetectorOptions.Builder() .setBarcodeFormats( FirebaseVisionBarcode.FORMAT_QR_CODE, FirebaseVisionBarcode.FORMAT_AZTEC) .build();
Kotlin
val options = FirebaseVisionBarcodeDetectorOptions.Builder() .setBarcodeFormats( FirebaseVisionBarcode.FORMAT_QR_CODE, FirebaseVisionBarcode.FORMAT_AZTEC) .build()
Поддерживаются следующие форматы:
-  Код 128 ( FORMAT_CODE_128)
-  Код 39 ( FORMAT_CODE_39)
-  Код 93 ( FORMAT_CODE_93)
-  Кодабар ( FORMAT_CODABAR)
-  EAN-13 ( FORMAT_EAN_13)
-  EAN-8 ( FORMAT_EAN_8)
-  ITF ( FORMAT_ITF)
-  СКП-А ( FORMAT_UPC_A)
-  UPC-E ( FORMAT_UPC_E)
-  QR-код ( FORMAT_QR_CODE)
-  PDF417 ( FORMAT_PDF417)
-  Ацтекский ( FORMAT_AZTEC)
-  Матрица данных ( FORMAT_DATA_MATRIX)
2. Запустите детектор штрих-кода
Чтобы распознавать штрих-коды в изображении, создайте объектFirebaseVisionImage из Bitmap , media.Image , ByteBuffer , массива байтов или файла на устройстве. Затем передайте объект FirebaseVisionImage методу detectInImage FirebaseVisionBarcodeDetector .- Создайте объект - FirebaseVisionImageиз вашего изображения.- Чтобы создать объект - FirebaseVisionImageиз объекта- media.Image, например, при захвате изображения с камеры устройства, передайте объект- media.Imageи поворот изображения в- FirebaseVisionImage.fromMediaImage().- Если вы используете библиотеку CameraX , классы - OnImageCapturedListenerи- ImageAnalysis.Analyzerвычисляют значение поворота за вас, поэтому вам просто нужно преобразовать поворот в одну из констант- ROTATION_ML Kit перед вызовом- FirebaseVisionImage.fromMediaImage():- Java- private class YourAnalyzer implements ImageAnalysis.Analyzer { private int degreesToFirebaseRotation(int degrees) { switch (degrees) { case 0: return FirebaseVisionImageMetadata.ROTATION_0; case 90: return FirebaseVisionImageMetadata.ROTATION_90; case 180: return FirebaseVisionImageMetadata.ROTATION_180; case 270: return FirebaseVisionImageMetadata.ROTATION_270; default: throw new IllegalArgumentException( "Rotation must be 0, 90, 180, or 270."); } } @Override public void analyze(ImageProxy imageProxy, int degrees) { if (imageProxy == null || imageProxy.getImage() == null) { return; } Image mediaImage = imageProxy.getImage(); int rotation = degreesToFirebaseRotation(degrees); FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation); // Pass image to an ML Kit Vision API // ... } } - Kotlin- private class YourImageAnalyzer : ImageAnalysis.Analyzer { private fun degreesToFirebaseRotation(degrees: Int): Int = when(degrees) { 0 -> FirebaseVisionImageMetadata.ROTATION_0 90 -> FirebaseVisionImageMetadata.ROTATION_90 180 -> FirebaseVisionImageMetadata.ROTATION_180 270 -> FirebaseVisionImageMetadata.ROTATION_270 else -> throw Exception("Rotation must be 0, 90, 180, or 270.") } override fun analyze(imageProxy: ImageProxy?, degrees: Int) { val mediaImage = imageProxy?.image val imageRotation = degreesToFirebaseRotation(degrees) if (mediaImage != null) { val image = FirebaseVisionImage.fromMediaImage(mediaImage, imageRotation) // Pass image to an ML Kit Vision API // ... } } } - Если вы не используете библиотеку камер, которая дает вам поворот изображения, вы можете рассчитать его на основе поворота устройства и ориентации датчика камеры на устройстве: - Java- private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 90); ORIENTATIONS.append(Surface.ROTATION_90, 0); ORIENTATIONS.append(Surface.ROTATION_180, 270); ORIENTATIONS.append(Surface.ROTATION_270, 180); } /** * Get the angle by which an image must be rotated given the device's current * orientation. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private int getRotationCompensation(String cameraId, Activity activity, Context context) throws CameraAccessException { // Get the device's current rotation relative to its "native" orientation. // Then, from the ORIENTATIONS table, look up the angle the image must be // rotated to compensate for the device's rotation. int deviceRotation = activity.getWindowManager().getDefaultDisplay().getRotation(); int rotationCompensation = ORIENTATIONS.get(deviceRotation); // On most devices, the sensor orientation is 90 degrees, but for some // devices it is 270 degrees. For devices with a sensor orientation of // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees. CameraManager cameraManager = (CameraManager) context.getSystemService(CAMERA_SERVICE); int sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION); rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360; // Return the corresponding FirebaseVisionImageMetadata rotation value. int result; switch (rotationCompensation) { case 0: result = FirebaseVisionImageMetadata.ROTATION_0; break; case 90: result = FirebaseVisionImageMetadata.ROTATION_90; break; case 180: result = FirebaseVisionImageMetadata.ROTATION_180; break; case 270: result = FirebaseVisionImageMetadata.ROTATION_270; break; default: result = FirebaseVisionImageMetadata.ROTATION_0; Log.e(TAG, "Bad rotation value: " + rotationCompensation); } return result; } - Kotlin- private val ORIENTATIONS = SparseIntArray() init { ORIENTATIONS.append(Surface.ROTATION_0, 90) ORIENTATIONS.append(Surface.ROTATION_90, 0) ORIENTATIONS.append(Surface.ROTATION_180, 270) ORIENTATIONS.append(Surface.ROTATION_270, 180) } /** * Get the angle by which an image must be rotated given the device's current * orientation. */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Throws(CameraAccessException::class) private fun getRotationCompensation(cameraId: String, activity: Activity, context: Context): Int { // Get the device's current rotation relative to its "native" orientation. // Then, from the ORIENTATIONS table, look up the angle the image must be // rotated to compensate for the device's rotation. val deviceRotation = activity.windowManager.defaultDisplay.rotation var rotationCompensation = ORIENTATIONS.get(deviceRotation) // On most devices, the sensor orientation is 90 degrees, but for some // devices it is 270 degrees. For devices with a sensor orientation of // 270, rotate the image an additional 180 ((270 + 270) % 360) degrees. val cameraManager = context.getSystemService(CAMERA_SERVICE) as CameraManager val sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION)!! rotationCompensation = (rotationCompensation + sensorOrientation + 270) % 360 // Return the corresponding FirebaseVisionImageMetadata rotation value. val result: Int when (rotationCompensation) { 0 -> result = FirebaseVisionImageMetadata.ROTATION_0 90 -> result = FirebaseVisionImageMetadata.ROTATION_90 180 -> result = FirebaseVisionImageMetadata.ROTATION_180 270 -> result = FirebaseVisionImageMetadata.ROTATION_270 else -> { result = FirebaseVisionImageMetadata.ROTATION_0 Log.e(TAG, "Bad rotation value: $rotationCompensation") } } return result } - Затем передайте объект - media.Imageи значение поворота в- FirebaseVisionImage.fromMediaImage():- Java- FirebaseVisionImage image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation); - Kotlin- val image = FirebaseVisionImage.fromMediaImage(mediaImage, rotation) 
-  Чтобы создать объект FirebaseVisionImageиз URI файла, передайте контекст приложения и URI файла вFirebaseVisionImage.fromFilePath(). Это полезно, когда вы используете намерениеACTION_GET_CONTENT, чтобы предложить пользователю выбрать изображение из приложения галереи.JavaFirebaseVisionImage image; try { image = FirebaseVisionImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); } Kotlinval image: FirebaseVisionImage try { image = FirebaseVisionImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() } 
-  Чтобы создать объект FirebaseVisionImageизByteBufferили массива байтов, сначала рассчитайте поворот изображения, как описано выше для вводаmedia.Image.Затем создайте объект FirebaseVisionImageMetadata, который содержит высоту, ширину изображения, формат цветовой кодировки и поворот:JavaFirebaseVisionImageMetadata metadata = new FirebaseVisionImageMetadata.Builder() .setWidth(480) // 480x360 is typically sufficient for .setHeight(360) // image recognition .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) .setRotation(rotation) .build(); Kotlinval metadata = FirebaseVisionImageMetadata.Builder() .setWidth(480) // 480x360 is typically sufficient for .setHeight(360) // image recognition .setFormat(FirebaseVisionImageMetadata.IMAGE_FORMAT_NV21) .setRotation(rotation) .build() Используйте буфер или массив и объект метаданных для создания объекта FirebaseVisionImage:JavaFirebaseVisionImage image = FirebaseVisionImage.fromByteBuffer(buffer, metadata); // Or: FirebaseVisionImage image = FirebaseVisionImage.fromByteArray(byteArray, metadata); Kotlinval image = FirebaseVisionImage.fromByteBuffer(buffer, metadata) // Or: val image = FirebaseVisionImage.fromByteArray(byteArray, metadata) 
-  Чтобы создать объект FirebaseVisionImageиз объектаBitmap:Изображение, представленное объектомJavaFirebaseVisionImage image = FirebaseVisionImage.fromBitmap(bitmap); Kotlinval image = FirebaseVisionImage.fromBitmap(bitmap) Bitmap, должно быть вертикальным, без необходимости дополнительного поворота.
 
- Получите экземпляр - FirebaseVisionBarcodeDetector:- Java- FirebaseVisionBarcodeDetector detector = FirebaseVision.getInstance() .getVisionBarcodeDetector(); // Or, to specify the formats to recognize: // FirebaseVisionBarcodeDetector detector = FirebaseVision.getInstance() // .getVisionBarcodeDetector(options); - Kotlin- val detector = FirebaseVision.getInstance() .visionBarcodeDetector // Or, to specify the formats to recognize: // val detector = FirebaseVision.getInstance() // .getVisionBarcodeDetector(options) 
- Наконец, передайте изображение методу - detectInImage:- Java- Task<List<FirebaseVisionBarcode>> result = detector.detectInImage(image) .addOnSuccessListener(new OnSuccessListener<List<FirebaseVisionBarcode>>() { @Override public void onSuccess(List<FirebaseVisionBarcode> barcodes) { // Task completed successfully // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } }); - Kotlin- val result = detector.detectInImage(image) .addOnSuccessListener { barcodes -> // Task completed successfully // ... } .addOnFailureListener { // Task failed with an exception // ... } 
3. Получите информацию из штрих-кодов
Если операция распознавания штрих-кода завершится успешно, список объектовFirebaseVisionBarcode будет передан прослушивателю успеха. Каждый объект FirebaseVisionBarcode представляет штрих-код, обнаруженный на изображении. Для каждого штрих-кода вы можете получить его ограничивающие координаты во входном изображении, а также необработанные данные, закодированные штрих-кодом. Также, если детектор штрих-кода смог определить тип данных, закодированных штрих-кодом, можно получить объект, содержащий разобранные данные.Например:
Java
for (FirebaseVisionBarcode barcode: barcodes) { Rect bounds = barcode.getBoundingBox(); Point[] corners = barcode.getCornerPoints(); String rawValue = barcode.getRawValue(); int valueType = barcode.getValueType(); // See API reference for complete list of supported types switch (valueType) { case FirebaseVisionBarcode.TYPE_WIFI: String ssid = barcode.getWifi().getSsid(); String password = barcode.getWifi().getPassword(); int type = barcode.getWifi().getEncryptionType(); break; case FirebaseVisionBarcode.TYPE_URL: String title = barcode.getUrl().getTitle(); String url = barcode.getUrl().getUrl(); break; } }
Kotlin
for (barcode in barcodes) { val bounds = barcode.boundingBox val corners = barcode.cornerPoints val rawValue = barcode.rawValue val valueType = barcode.valueType // See API reference for complete list of supported types when (valueType) { FirebaseVisionBarcode.TYPE_WIFI -> { val ssid = barcode.wifi!!.ssid val password = barcode.wifi!!.password val type = barcode.wifi!!.encryptionType } FirebaseVisionBarcode.TYPE_URL -> { val title = barcode.url!!.title val url = barcode.url!!.url } } }
Советы по повышению производительности в реальном времени
Если вы хотите сканировать штрих-коды в приложении реального времени, следуйте этим рекомендациям для достижения наилучшей частоты кадров:
- Не записывайте входные данные с собственным разрешением камеры. На некоторых устройствах захват входных данных с собственным разрешением приводит к созданию чрезвычайно больших (10+ мегапикселей) изображений, что приводит к очень низкой задержке без какого-либо улучшения точности. Вместо этого запрашивайте у камеры только тот размер, который необходим для обнаружения штрих-кода: обычно не более 2 мегапикселей. - Если скорость сканирования важна, вы можете еще больше снизить разрешение захвата изображения. Однако помните о требованиях к минимальному размеру штрих-кода, изложенных выше. 
- Дроссель вызывает детектор. Если новый видеокадр становится доступным во время работы детектора, удалите этот кадр.
- Если вы используете выходные данные детектора для наложения графики на входное изображение, сначала получите результат из ML Kit, затем визуализируйте изображение и наложите его за один шаг. При этом вы выполняете рендеринг на поверхность дисплея только один раз для каждого входного кадра.
- Если вы используете API Camera2, захватывайте изображения в формате - ImageFormat.YUV_420_888.- Если вы используете более старый API камеры, захватывайте изображения в формате - ImageFormat.NV21.