Création d’une application de reconnaissance de texte à l’aide de CameraX et ML Kit sous Android

Avec la demande croissante d’applications intelligentes capables de traiter et de comprendre les données visuelles, reconnaissance de texte devient une fonctionnalité clé dans de nombreuses applications. Ce blog vous guidera dans la création d’une puissante application de reconnaissance de texte à l’aide des API MLKit, CameraX et Jetpack Compose de Google. MLKit offre un robuste Solution d’apprentissage automatique pour la reconnaissance de texte sur l’appareil, tandis que CameraX offre un moyen simple d’intégrer les fonctionnalités de l’appareil photo. En les combinant avec les fonctionnalités modernes de Jetpack Compose Interface utilisateur boîte à outils, nous créerons une application transparente et réactive. Avant de commencer à plonger dans l’implémentation, comprenons d’abord les composants clés utilisés pour l’implémentation.
Pourquoi utiliser ML Kit pour la reconnaissance de texte ?
Trousse ML est un framework d’apprentissage automatique fourni par Google, conçu pour apporter de puissantes capacités d’apprentissage automatique aux applications mobiles sans avoir besoin d’une connaissance approfondie des algorithmes de ML. L’une de ses fonctionnalités clés est la reconnaissance de texte, qui permet aux développeurs d’extraire le texte des images avec une grande précision. Il s’agit d’une solution indépendante du cloud, ce qui signifie qu’elle fonctionne même hors ligne, ce qui la rend parfaitement adaptée aux applications mobiles nécessitant une reconnaissance de texte robuste et rapide.
En savoir plus: API vocale Web
Utiliser CameraX pour capturer une image
CaméraX est une bibliothèque Android Jetpack qui simplifie l’implémentation de la caméra pour les développeurs. Il prend en charge divers cas d’utilisation tels que l’aperçu, la capture d’images et l’enregistrement vidéo. Dans notre application, nous utilisons CameraX pour capture d’imagesmais il pourrait aussi être adapté pour reconnaissance continue.
Capture d’image unique
CameraX peut être utilisé pour capturer une seule image à traiter. C’est l’approche utilisée dans notre application, où l’utilisateur capture manuellement une image en appuyant sur un bouton. Cette méthode est mieux adaptée lorsque vous capturez des documents statiques ou des captures d’écran pour la reconnaissance de texte. Alternativement, si vous n’avez pas besoin de capture d’image unique, vous pouvez envisager la reconnaissance continue.
Reconnaissance de texte continue avec CameraX
Pour une reconnaissance continue, le cas d’utilisation ImageAnalysis de CameraX peut être utilisé. Au lieu de capturer une seule image et de la traiter, ImageAnalysis analyse en continu les images de la caméra et les envoie à ML Kit pour la reconnaissance de texte. Cette approche est utile lorsque vous souhaitez numériser du texte en continu, comme dans les applications de numérisation de codes-barres ou de documents. Nous pouvons utiliser ImageAnalysis pour la reconnaissance continue de texte.
Commençons maintenant par le projet. Et comprenez comment nous pouvons réaliser la reconnaissance de texte. Nous commencerons par la configuration du projet.
Configuration du projet
Avant de commencer, assurez-vous d’avoir ajouté les dépendances nécessaires dans votre build. diplôme :
- Added dependency in library libs.versions.toml #camera cameraX = "1.3.4" #MLkit playServicesMlkitTextRecognitionCommon = "19.1.0" textRecognition = "16.0.1" - Implementing dependency in build.gradle(app) //Dependency for camera implementation(libs.camera2) implementation(libs.cameraView) implementation(libs.cameraLifecycle) // Dependency For Google ML Kit implementation(libs.play.services.mlkit.text.recognition.common) implementation (libs.text.recognition)
Gestion des autorisations de caméra
Tout d’abord, nous ajouterons des autorisations au fichier AndroidManifest :
<!-- Permission for using camera --> <uses-feature android:name="android.hardware.camera.any" /> <uses-permission android:name="android.permission.CAMERA" />
Ensuite, nous devrons d’abord demander l’autorisation de la caméra à l’utilisateur. Lorsque nous aurons obtenu la permission, nous continuerons. Voici comment nous gérons les autorisations dans le composable CameraPermissionHandler :
- Requested user to provide permission for camera. @Composable fun CameraPermissionHandler(onPermissionGranted: () -> Unit) { val cameraPermission = Manifest.permission.CAMERA val context = LocalContext.current val permissionLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.RequestPermission(), onResult = { isGranted -> if (isGranted) { onPermissionGranted() } else { Toast.makeText(context, "Camera permission denied", Toast.LENGTH_SHORT).show() } } ) // We will be showing permission popup to the user. LaunchedEffect(key1 = true) { when { ContextCompat.checkSelfPermission( context, cameraPermission ) == PackageManager.PERMISSION_GRANTED -> { onPermissionGranted() } else -> { permissionLauncher.launch(cameraPermission) } } } } - Now when user grant permission then we will start camera @Composable fun CameraPermissionScreen() { var permissionGranted by remember { mutableStateOf(false) } // Handle the permission request CameraPermissionHandler( onPermissionGranted = { permissionGranted = true } ) // Show the TextRecognitionScreen only if permission is granted if (permissionGranted) { TextRecognitionScreen() } }
- Gestionnaire de permissions de caméra : Ce composable est chargé de demander l’autorisation de la caméra à l’utilisateur.
- Gestion de l’état : Les éléments Remember et mutableStateOf de Compose sont utilisés pour gérer l’état de savoir si l’autorisation est accordée ou non.
Capturer l’image
Une fois l’autorisation accordée, nous pouvons procéder à l’affichage de l’aperçu de la caméra et à la capture d’images. Ceci est géré par le composable CameraPreview :
@Composable fun CameraPreview(modifier: Modifier, onCapture: (ImageProxy) -> Unit) { val context = LocalContext.current val lifecycleOwner = LocalLifecycleOwner.current val previewView = remember { PreviewView(context) } var imageCapture: ImageCapture? by remember { mutableStateOf(null) } Box(modifier = Modifier.padding(bottom = 50.dp)) { AndroidView({ previewView }, modifier = modifier) { view -> val cameraProviderFuture = ProcessCameraProvider.getInstance(context) cameraProviderFuture.addListener({ val cameraProvider = cameraProviderFuture.get() val preview = androidx.camera.core.Preview.Builder().build() val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA imageCapture = ImageCapture.Builder().build() preview.setSurfaceProvider(view.surfaceProvider) try { // We are here binding the cameraSelector, preview and image capture with lifecycle. So that camera can behaves properly during activity lifecycle events cameraProvider.unbindAll() cameraProvider.bindToLifecycle( lifecycleOwner, cameraSelector, preview, imageCapture ) } catch (e: Exception) { Log.e("CameraPreview", "Use case binding failed", e) } }, ContextCompat.getMainExecutor(context)) } // This Btn will be used to capture image. FloatingActionButton( onClick = { imageCapture?.takePicture( ContextCompat.getMainExecutor(context), object : ImageCapture.OnImageCapturedCallback() { override fun onCaptureSuccess(imageProxy: ImageProxy) { onCapture(imageProxy) imageProxy.close() } override fun onError(exception: ImageCaptureException) { Log.e("CameraCapture", "Capture failed: ${exception.message}") } } ) }, modifier = Modifier .padding(32.dp) .align(Alignment.BottomCenter) ) { Text("Capture Image") } } }
Nous créons un aperçu de la caméra et un bouton pour capturer des images. Nous préparons la fonctionnalité de la caméra avec l’interface utilisateur que nous souhaitons montrer à l’utilisateur.
- Aperçu de la caméra : Affiche le flux de la caméra à l’aide de PreviewView de CameraX, intégré dans une interface utilisateur Compose via AndroidView.
- Bouton de capture : Un bouton d’action flottant capture une image lorsque vous cliquez dessus.
- Capture d’images : CameraX capture l’image et la transmet au rappel (onCapture), où un traitement ultérieur peut avoir lieu (par exemple, reconnaissance de texte).
- Gestion du cycle de vie : Les cas d’utilisation de la caméra sont liés au cycle de vie du composable, garantissant que la caméra se comporte correctement pendant les événements du cycle de vie de l’activité (par exemple, mise en arrière-plan ou fermeture de l’application).

Aperçu de la caméra avec bouton
Traitement de l’image pour la reconnaissance de texte
Une fois qu’une image est capturée, elle est transmise au module de reconnaissance de texte ML Kit dans TextRecognitionViewModel. C’est là que réside la fonctionnalité principale de l’application.
class TextRecognitionViewModel : ViewModel() { private val _recognizedText = mutableStateOf<String?>(null) val recognizedText = _recognizedText fun recognizeText(bitmap: Bitmap) { val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS) val image = InputImage.fromBitmap(bitmap, 0) recognizer.process(image) .addOnSuccessListener { visionText -> _recognizedText.value = visionText.text } .addOnFailureListener { e -> _recognizedText.value = "Error: ${e.message}" } } }
La fonction de reconnaissance de texte prend un Bitmap en entrée et utilise la méthode TextRecognition.getClient() pour reconnaître le texte de l’image. Le texte reconnu est ensuite stocké dans l’état _recognizedText.
Affichage du texte reconnu
Le texte reconnu est affiché sur une feuille inférieure. L’utilisateur peut copier le texte reconnu en cliquant dessus.
Maintenant, nous allons implémenter ici TextRecognitionScreen qui utilisera l’aperçu de la caméra.
@OptIn(ExperimentalMaterial3Api::class) @Composable fun TextRecognitionScreen(viewModel: TextRecognitionViewModel = viewModel()) { val recognizedText by viewModel.recognizedText val context = LocalContext.current // Bottom Sheet State val sheetState = rememberBottomSheetScaffoldState() val coroutineScope = rememberCoroutineScope() val clipboard: ClipboardManager = LocalClipboardManager.current // We will be using BottomSheetScaffold for getting the text and showing it on bottom sheet. BottomSheetScaffold( sheetContent = { recognizedText?.let { // Content of the bottom sheet LazyColumn( modifier = Modifier .fillMaxWidth() .padding(16.dp) .padding(bottom = 60.dp) .heightIn(max = 500.dp) // Limiting the height for the bottom sheet ) { item { // If the text extracted is not empty then we will be able to copy the text which is returned from viewModel. if (it.isNotEmpty()) { Text( text = it, modifier = Modifier .fillMaxWidth() .padding(16.dp) .clickable { clipboard.setText(AnnotatedString((it))) Toast.makeText(context,"Text Copied!",Toast.LENGTH_SHORT).show() } ) } else { Text( text = "No text recognized yet", modifier = Modifier .fillMaxWidth() .padding(16.dp) .padding(bottom = 100.dp) ) } } } } ?: Text( text = "No text recognized yet", modifier = Modifier .fillMaxWidth() .padding(16.dp) .padding(bottom = 100.dp) ) }, scaffoldState = sheetState, sheetPeekHeight = 0.dp, modifier = Modifier.fillMaxSize() ) { Box( modifier = Modifier.fillMaxSize() ) { CameraPreview(modifier = Modifier.fillMaxSize()) { imageProxy -> val bitmap = imageProxy.toBitmapImage() if (bitmap != null) { viewModel.recognizeText(bitmap) coroutineScope.launch { sheetState.bottomSheetState.expand() } } } } } }
L’aperçu de la caméra est affiché à l’aide de PreviewView et un bouton d’action flottant (FAB) est fourni pour capturer l’image. L’image capturée est transmise en tant que ImageProxy s’opposer à la surCapture rappel.
Conversion d’ImageProxy en Bitmap
L’objet ImageProxy doit être converti en Bitmap avant d’être transmis à MLKit. Voici comment procéder :
private fun ImageProxy.toBitmapImage(): Bitmap? { val buffer: ByteBuffer = planes[0].buffer val bytes = ByteArray(buffer.remaining()) buffer[bytes] return BitmapFactory.decodeByteArray(bytes, 0, bytes.size, null) }
Maintenant, à la fin, il vous suffit d’ajouter la méthode dans votre projet où vous souhaitez implémenter :
// This method is implemented in oncreate in this blog. setContent { TextVisionTheme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { CameraPermissionScreen() } } }

Écran après avoir obtenu le texte – Sortie finale.
Conclusion
Cette application montre comment intégrer CameraX pour capturer des images et ML Kit pour reconnaître le texte des images en temps réel. L’utilisation de Jetpack Compose rend le développement d’interface utilisateur moderne et efficace. Avec ces outils, la création d’une puissante application de reconnaissance de texte est simple et transparente. AUX NOUVEAUX Analyse avancée Cette offre permet à votre entreprise d’atténuer les risques en vous permettant de prendre des décisions instantanément pour aider votre entreprise à se développer.
Source link