Vous pouvez utiliser ML Kit pour reconnaître et décoder les codes-barres.
Sélection | Sans catégorie | Groupée |
---|---|---|
Implémentation | Le modèle est téléchargé de manière dynamique via les services Google Play. | Le modèle est associé de manière statique à votre application au moment de la compilation. |
Taille d'application | Augmentation de la taille d'environ 200 Ko. | Augmentation de la taille d'environ 2,4 Mo. |
Délai d'initialisation | Vous devrez peut-être attendre que le modèle soit téléchargé avant de l'utiliser pour la première fois. | Le modèle est disponible immédiatement. |
Essayer
- Jouez avec l'application exemple pour voir un exemple d'utilisation de cette API.
- Consultez l'application Showcase Material Design pour découvrir une implémentation de bout en bout de cette API.
Avant de commencer
Dans le fichier
build.gradle
au niveau du projet, veillez à inclure le dépôt Maven de Google dans vos sectionsbuildscript
etallprojects
.Ajoutez les dépendances des bibliothèques Android ML Kit au fichier Gradle au niveau de l'application de votre module, qui est généralement
app/build.gradle
. Choisissez l'une des dépendances suivantes en fonction de vos besoins:Pour regrouper le modèle avec votre application:
dependencies { // ... // Use this dependency to bundle the model with your app implementation 'com.google.mlkit:barcode-scanning:17.2.0' }
Pour utiliser le modèle dans les services Google Play:
dependencies { // ... // Use this dependency to use the dynamically downloaded model in Google Play Services implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.0' }
Si vous choisissez d'utiliser le modèle dans les services Google Play, vous pouvez configurer votre application pour qu'elle télécharge automatiquement le modèle sur l'appareil une fois qu'elle a été installée depuis le Play Store. Pour ce faire, ajoutez la déclaration suivante au fichier
AndroidManifest.xml
de votre application:<application ...> ... <meta-data android:name="com.google.mlkit.vision.DEPENDENCIES" android:value="barcode" > <!-- To use multiple models: android:value="barcode,model2,model3" --> </application>
Vous pouvez également vérifier explicitement la disponibilité du modèle et demander un téléchargement via l'API ModuleInstallClient des services Google Play.
Si vous n'activez pas le téléchargement du modèle au moment de l'installation ou si vous ne demandez pas de téléchargement explicite, le modèle est téléchargé la première fois que vous exécutez l'outil d'analyse. Les requêtes que vous effectuez avant la fin du téléchargement ne produisent aucun résultat.
Consignes pour les images d'entrée
-
Pour que ML Kit puisse lire avec précision les codes-barres, les images d'entrée doivent contenir des codes-barres représentés par suffisamment de données de pixels.
Les exigences spécifiques concernant les données de pixels dépendent du type de code-barres et de la quantité de données qui y sont encodées, car de nombreux codes-barres sont compatibles avec une charge utile de taille variable. En général, la plus petite unité significative du code-barres doit être d'au moins 2 pixels de large et de 2 pixels de haut pour les codes à deux dimensions.
Par exemple, les codes-barres EAN-13 sont composés de barres et d'espaces d'une largeur de 1, 2, 3 ou 4 unités. Par conséquent, une image de code-barres EAN-13 doit idéalement comporter des barres et des espaces d'une largeur minimale de 2, 4, 6 et 8 pixels. Étant donné qu'un code-barres EAN-13 fait 95 unités de largeur au total, il doit avoir une largeur d'au moins 190 pixels.
Les formats de densité (tels que PDF417) nécessitent des dimensions en pixels supérieures pour que ML Kit puisse les lire de manière fiable. Par exemple, un code PDF417 peut contenir jusqu'à 34 "mots" de 17 unités de large sur une seule ligne, ce qui devrait idéalement faire au moins 1 156 pixels de largeur.
-
Une mauvaise mise au point de l'image peut nuire à la précision de la numérisation. Si votre application n'obtient pas de résultats satisfaisants, demandez à l'utilisateur de capturer à nouveau l'image.
-
Pour les applications courantes, il est recommandé de fournir une image de plus haute résolution, par exemple 1 280 x 720 ou 1 920 x 1 080, qui permet de scanner les codes-barres à plus grande distance de l'appareil photo.
Toutefois, dans les applications où la latence est essentielle, vous pouvez améliorer les performances en capturant des images à une résolution inférieure, mais en exigeant que le code-barres constitue la majorité de l'image d'entrée. Consultez également Conseils pour améliorer les performances en temps réel.
1. Configurer le lecteur de code-barres
Si vous connaissez les formats de code-barres que vous souhaitez lire, vous pouvez améliorer la vitesse du détecteur de codes-barres en le configurant pour ne détecter que ces formats.Par exemple, pour ne détecter que le code aztec et les codes QR, créez un objet BarcodeScannerOptions
comme dans l'exemple suivant:
Kotlin
val options = BarcodeScannerOptions.Builder() .setBarcodeFormats( Barcode.FORMAT_QR_CODE, Barcode.FORMAT_AZTEC) .build()
Java
BarcodeScannerOptions options = new BarcodeScannerOptions.Builder() .setBarcodeFormats( Barcode.FORMAT_QR_CODE, Barcode.FORMAT_AZTEC) .build();
Les formats suivants sont acceptés :
- Code 128 (
FORMAT_CODE_128
) - Code 39 (
FORMAT_CODE_39
) - Code 93 (
FORMAT_CODE_93
) - Codabar (
FORMAT_CODABAR
) - EAN-13 (
FORMAT_EAN_13
) - EAN-8 (
FORMAT_EAN_8
) - ITF (
FORMAT_ITF
) - UPC-A (
FORMAT_UPC_A
) - UPC-E (
FORMAT_UPC_E
) - Code QR (
FORMAT_QR_CODE
) - PDF417 (
FORMAT_PDF417
) - Aztèque (
FORMAT_AZTEC
) - Matrice de données (
FORMAT_DATA_MATRIX
)
À partir du modèle groupé 17.1.0 et du modèle non groupé 18.2.0, vous pouvez également appeler enableAllPotentialBarcodes()
pour renvoyer tous les codes-barres potentiels, même s'ils ne peuvent pas être décodés. Cela peut être utilisé pour faciliter la détection, par exemple en zoomant dans la caméra pour obtenir une image plus nette de n'importe quel code-barres dans le cadre de délimitation renvoyé.
Kotlin
val options = BarcodeScannerOptions.Builder() .setBarcodeFormats(...) .enableAllPotentialBarcodes() // Optional .build()
Java
BarcodeScannerOptions options = new BarcodeScannerOptions.Builder() .setBarcodeFormats(...) .enableAllPotentialBarcodes() // Optional .build();
Further on, starting from bundled library 17.2.0 and unbundled library 18.3.0, a new feature called auto-zoom has been introduced to further enhance the barcode scanning experience. With this feature enabled, the app is notified when all barcodes within the view are too distant for decoding. As a result, the app can effortlessly adjust the camera's zoom ratio to the recommended setting provided by the library, ensuring optimal focus and readability. This feature will significantly enhance the accuracy and success rate of barcode scanning, making it easier for apps to capture information precisely.
To enable auto-zooming and customize the experience, you can utilize the
setZoomSuggestionOptions()
method along with your
own ZoomCallback
handler and desired maximum zoom
ratio, as demonstrated in the code below.
Kotlin
val options = BarcodeScannerOptions.Builder() .setBarcodeFormats(...) .setZoomSuggestionOptions( new ZoomSuggestionOptions.Builder(zoomCallback) .setMaxSupportedZoomRatio(maxSupportedZoomRatio) .build()) // Optional .build()
Java
BarcodeScannerOptions options = new BarcodeScannerOptions.Builder() .setBarcodeFormats(...) .setZoomSuggestionOptions( new ZoomSuggestionOptions.Builder(zoomCallback) .setMaxSupportedZoomRatio(maxSupportedZoomRatio) .build()) // Optional .build();
zoomCallback
is required to be provided to handle whenever the library
suggests a zoom should be performed and this callback will always be called on
the main thread.
The following code snippet shows an example of defining a simple callback.
Kotlin
fun setZoom(ZoomRatio: Float): Boolean { if (camera.isClosed()) return false camera.getCameraControl().setZoomRatio(zoomRatio) return true }
Java
boolean setZoom(float zoomRatio) { if (camera.isClosed()) { return false; } camera.getCameraControl().setZoomRatio(zoomRatio); return true; }
maxSupportedZoomRatio
is related to the camera hardware, and different camera
libraries have different ways to fetch it (see the javadoc of the setter
method). In case this is not provided, an
unbounded zoom ratio might be produced by the library which might not be
supported. Refer to the
setMaxSupportedZoomRatio()
method
introduction to see how to get the max supported zoom ratio with different
Camera libraries.
When auto-zooming is enabled and no barcodes are successfully decoded within
the view, BarcodeScanner
triggers your zoomCallback
with the requested
zoomRatio
. If the callback correctly adjusts the camera to this zoomRatio
,
it is highly probable that the most centered potential barcode will be decoded
and returned.
A barcode may remain undecodable even after a successful zoom-in. In such cases,
BarcodeScanner
may either invoke the callback for another round of zoom-in
until the maxSupportedZoomRatio
is reached, or provide an empty list (or a
list containing potential barcodes that were not decoded, if
enableAllPotentialBarcodes()
was called) to the OnSuccessListener
(which
will be defined in step 4. Process the image).
2. Prepare the input image
To recognize barcodes in an image, create anInputImage
object
from either a Bitmap
, media.Image
, ByteBuffer
, byte array, or a file on
the device. Then, pass the InputImage
object to the
BarcodeScanner
's process
method.
You can create an InputImage
object from different sources, each is explained below.
Using a media.Image
To create an InputImage
object from a media.Image
object, such as when you capture an image from a
device's camera, pass the media.Image
object and the image's
rotation to InputImage.fromMediaImage()
.
If you use the
CameraX library, the OnImageCapturedListener
and
ImageAnalysis.Analyzer
classes calculate the rotation value
for you.
Kotlin
private class YourImageAnalyzer : ImageAnalysis.Analyzer { override fun analyze(imageProxy: ImageProxy) { val mediaImage = imageProxy.image if (mediaImage != null) { val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees) // Pass image to an ML Kit Vision API // ... } } }
Java
private class YourAnalyzer implements ImageAnalysis.Analyzer { @Override public void analyze(ImageProxy imageProxy) { Image mediaImage = imageProxy.getImage(); if (mediaImage != null) { InputImage image = InputImage.fromMediaImage(mediaImage, imageProxy.getImageInfo().getRotationDegrees()); // Pass image to an ML Kit Vision API // ... } } }
Si vous n'utilisez pas de bibliothèque d'appareil photo qui indique le degré de rotation de l'image, vous pouvez le calculer à partir du degré de rotation de l'appareil et de l'orientation du capteur de l'appareil photo de l'appareil:
Kotlin
private val ORIENTATIONS = SparseIntArray() init { ORIENTATIONS.append(Surface.ROTATION_0, 0) ORIENTATIONS.append(Surface.ROTATION_90, 90) ORIENTATIONS.append(Surface.ROTATION_180, 180) ORIENTATIONS.append(Surface.ROTATION_270, 270) } /** * 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, isFrontFacing: Boolean): 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) // Get the device's sensor orientation. val cameraManager = activity.getSystemService(CAMERA_SERVICE) as CameraManager val sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION)!! if (isFrontFacing) { rotationCompensation = (sensorOrientation + rotationCompensation) % 360 } else { // back-facing rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360 } return rotationCompensation }
Java
private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); static { ORIENTATIONS.append(Surface.ROTATION_0, 0); ORIENTATIONS.append(Surface.ROTATION_90, 90); ORIENTATIONS.append(Surface.ROTATION_180, 180); ORIENTATIONS.append(Surface.ROTATION_270, 270); } /** * 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, boolean isFrontFacing) 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); // Get the device's sensor orientation. CameraManager cameraManager = (CameraManager) activity.getSystemService(CAMERA_SERVICE); int sensorOrientation = cameraManager .getCameraCharacteristics(cameraId) .get(CameraCharacteristics.SENSOR_ORIENTATION); if (isFrontFacing) { rotationCompensation = (sensorOrientation + rotationCompensation) % 360; } else { // back-facing rotationCompensation = (sensorOrientation - rotationCompensation + 360) % 360; } return rotationCompensation; }
Transmettez ensuite l'objet media.Image
et la valeur du degré de rotation à InputImage.fromMediaImage()
:
Kotlin
val image = InputImage.fromMediaImage(mediaImage, rotation)
Java
InputImage image = InputImage.fromMediaImage(mediaImage, rotation);
Utiliser un URI de fichier
Pour créer un objet InputImage
à partir d'un URI de fichier, transmettez le contexte de l'application et l'URI du fichier à InputImage.fromFilePath()
. Cela est utile lorsque vous utilisez un intent ACTION_GET_CONTENT
pour inviter l'utilisateur à sélectionner une image dans son application de galerie.
Kotlin
val image: InputImage try { image = InputImage.fromFilePath(context, uri) } catch (e: IOException) { e.printStackTrace() }
Java
InputImage image; try { image = InputImage.fromFilePath(context, uri); } catch (IOException e) { e.printStackTrace(); }
Utiliser un ByteBuffer
ou un ByteArray
Pour créer un objet InputImage
à partir d'un élément ByteBuffer
ou ByteArray
, calculez d'abord le degré de rotation de l'image, comme décrit précédemment pour l'entrée media.Image
.
Créez ensuite l'objet InputImage
avec le tampon ou le tableau, ainsi que la hauteur, la largeur, le format d'encodage des couleurs et le degré de rotation de l'image:
Kotlin
val image = InputImage.fromByteBuffer( byteBuffer, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 ) // Or: val image = InputImage.fromByteArray( byteArray, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 )
Java
InputImage image = InputImage.fromByteBuffer(byteBuffer, /* image width */ 480, /* image height */ 360, rotationDegrees, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 ); // Or: InputImage image = InputImage.fromByteArray( byteArray, /* image width */480, /* image height */360, rotation, InputImage.IMAGE_FORMAT_NV21 // or IMAGE_FORMAT_YV12 );
Utiliser un Bitmap
Pour créer un objet InputImage
à partir d'un objet Bitmap
, effectuez la déclaration suivante:
Kotlin
val image = InputImage.fromBitmap(bitmap, 0)
Java
InputImage image = InputImage.fromBitmap(bitmap, rotationDegree);
L'image est représentée par un objet Bitmap
associé à des degrés de rotation.
3. Obtenir une instance de BarcodeScanner
Kotlin
val scanner = BarcodeScanning.getClient() // Or, to specify the formats to recognize: // val scanner = BarcodeScanning.getClient(options)
Java
BarcodeScanner scanner = BarcodeScanning.getClient(); // Or, to specify the formats to recognize: // BarcodeScanner scanner = BarcodeScanning.getClient(options);
4. Traiter l'image
Transmettez l'image à la méthodeprocess
:
Kotlin
val result = scanner.process(image) .addOnSuccessListener { barcodes -> // Task completed successfully // ... } .addOnFailureListener { // Task failed with an exception // ... }
Java
Task<List<Barcode>> result = scanner.process(image) .addOnSuccessListener(new OnSuccessListener<List<Barcode>>() { @Override public void onSuccess(List<Barcode> barcodes) { // Task completed successfully // ... } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { // Task failed with an exception // ... } });
5. Obtenir des informations à partir des codes-barres
Si l'opération de reconnaissance de code-barres aboutit, une liste d'objetsBarcode
est transmise à l'écouteur de réussite. Chaque objet Barcode
représente un code-barres détecté dans l'image. Pour chaque code-barres, vous pouvez obtenir ses coordonnées de délimitation dans l'image d'entrée, ainsi que les données brutes encodées par le code-barres. En outre, si le lecteur de codes-barres a pu déterminer le type de données encodées par le code-barres, vous pouvez obtenir un objet contenant des données analysées.
Exemple :
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) { Barcode.TYPE_WIFI -> { val ssid = barcode.wifi!!.ssid val password = barcode.wifi!!.password val type = barcode.wifi!!.encryptionType } Barcode.TYPE_URL -> { val title = barcode.url!!.title val url = barcode.url!!.url } } }
Java
for (Barcode 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 Barcode.TYPE_WIFI: String ssid = barcode.getWifi().getSsid(); String password = barcode.getWifi().getPassword(); int type = barcode.getWifi().getEncryptionType(); break; case Barcode.TYPE_URL: String title = barcode.getUrl().getTitle(); String url = barcode.getUrl().getUrl(); break; } }
Conseils pour améliorer les performances en temps réel
Si vous souhaitez scanner des codes-barres dans une application en temps réel, suivez ces consignes pour obtenir une fréquence d'images optimale:
-
Ne enregistrez pas d'entrée à la résolution native de l'appareil photo. Sur certains appareils, la capture d'entrée à la résolution native produit des images extrêmement volumineuses (plus de 10 mégapixels), ce qui se traduit par une latence très faible et sans aucun avantage en termes de précision. Demandez plutôt uniquement à l'appareil photo la taille requise pour la détection des codes-barres, qui ne dépasse généralement pas 2 mégapixels.
Si la vitesse d'analyse est importante, vous pouvez réduire davantage la résolution de capture d'image. Toutefois, tenez compte des exigences minimales de taille de code-barres décrites ci-dessus.
Si vous essayez de reconnaître des codes-barres à partir d'une séquence d'images vidéo en streaming, l'outil de reconnaissance peut produire des résultats différents d'une image à l'autre. Vous devez attendre d'obtenir une série consécutive de la même valeur pour être sûr de renvoyer un bon résultat.
Le chiffre de la somme de contrôle n'est pas accepté pour ITF et CODE-39.
- Si vous utilisez l'API
Camera
oucamera2
, limitez les appels au détecteur. Si une nouvelle image vidéo devient disponible pendant l'exécution du détecteur, supprimez-la. Consultez la classeVisionProcessorBase
de l'exemple d'application de démarrage rapide pour obtenir un exemple. - Si vous utilisez l'API
CameraX
, assurez-vous que la stratégie de contre-pression est définie sur sa valeur par défautImageAnalysis.STRATEGY_KEEP_ONLY_LATEST
. Cela garantit qu'une seule image à la fois sera envoyée pour analyse. Si davantage d'images sont produites lorsque l'analyseur est occupé, elles sont automatiquement supprimées et ne sont pas mises en file d'attente pour la diffusion. Une fois l'image en cours d'analyse fermée en appelant ImageProxy.close(), la dernière image suivante est envoyée. - Si vous utilisez la sortie du détecteur pour superposer des graphiques sur l'image d'entrée, obtenez d'abord le résultat de ML Kit, puis effectuez le rendu de l'image et de la superposition en une seule étape. Le rendu sur la surface d'affichage n'est effectué qu'une seule fois pour chaque image d'entrée. Consultez les classes
CameraSourcePreview
etGraphicOverlay
dans l'exemple d'application de démarrage rapide pour obtenir un exemple. - Si vous utilisez l'API Camera2, capturez des images au format
ImageFormat.YUV_420_888
. Si vous utilisez l'ancienne API Camera, capturez les images au formatImageFormat.NV21
.
Sauf indication contraire, le contenu de cette page est régi par une licence Creative Commons Attribution 4.0, et les échantillons de code sont régis par une licence Apache 2.0. Pour en savoir plus, consultez les Règles du site Google Developers. Java est une marque déposée d'Oracle et/ou de ses sociétés affiliées.
Dernière mise à jour le 2024/07/12 (UTC).