1. Introduction
Qu'est-ce que MediaPipe ?
MediaPipe Solutions vous permet d'appliquer des solutions de machine learning (ML) à vos applications. Vous accédez à un framework avec lequel vous pouvez configurer des pipelines de traitement prédéfinis qui fournissent aux utilisateurs une sortie immédiate, attrayante et utile. Vous pouvez même personnaliser ces solutions avec MediaPipe Model Maker afin de modifier les modèles par défaut.
La classification d'images est l'une des nombreuses tâches de vision ML proposées par MediaPipe Solutions. MediaPipe Tasks est disponible pour Android, iOS, Python (y compris Raspberry Pi) et le Web.
Dans cet atelier de programmation, vous allez commencer par créer une application Android qui vous permet de dessiner des chiffres sur l'écran. Vous ajouterez ensuite une fonctionnalité qui classifie ces chiffres dessinés en une seule valeur comprise entre 0 et 9.
Points abordés
- Intégrer une tâche de classification d'images dans une application Android avec MediaPipe Tasks
Prérequis
- Une version installée d'Android Studio (cet atelier a été écrit et testé avec Android Studio Giraffe).
- Un appareil ou un émulateur Android pour exécuter l'application.
- Connaissances de base sur le développement Android (il ne s'agit pas d'un simple"Bonjour le monde", mais presque !).
2. Ajouter MediaPipe Tasks à l'application Android
Télécharger l'application de démarrage Android
Dans cet atelier de programmation, vous allez commencer avec un exemple prédéfini qui vous permet de dessiner sur l'écran. Vous trouverez cette application de démarrage dans le dépôt d'exemples MediaPipe officiel ici. Clonez le dépôt ou téléchargez le fichier ZIP en cliquant sur Code > Télécharger le fichier ZIP.
Importer l'application dans Android Studio
- Ouvrez Android Studio.
- Sur l'écran Bienvenue dans Android Studio, sélectionnez Ouvrir en haut à droite.

- Accédez à l'emplacement où vous avez cloné ou téléchargé le dépôt, puis ouvrez le répertoire codelabs/digitclassifier/android/start.
- Vérifiez que tout s'est ouvert correctement en cliquant sur la flèche verte Exécuter (
) en haut à droite d'Android Studio. - L'application devrait s'ouvrir sur un écran noir sur lequel vous pouvez dessiner, ainsi qu'un bouton Effacer pour réinitialiser cet écran. Vous pouvez dessiner sur cet écran, mais il ne fait pas grand-chose d'autre. Nous allons donc commencer à y remédier.

Modèle
Lorsque vous exécutez l'application pour la première fois, vous remarquerez peut-être qu'un fichier nommé mnist.tflite est téléchargé et stocké dans le répertoire assets de votre application. Par souci de simplicité, nous avons déjà pris un modèle connu, MNIST, qui classe les chiffres, et l'avons ajouté à l'application à l'aide du script download_models.gradle dans le projet. Si vous décidez d'entraîner votre propre modèle personnalisé, par exemple pour les lettres manuscrites, vous devez supprimer le fichier download_models.gradle, supprimer la référence à celui-ci dans votre fichier build.gradle au niveau de l'application et modifier le nom du modèle plus loin dans le code (plus précisément dans le fichier DigitClassifierHelper.kt).
Mettre à jour le fichier build.gradle
Avant de pouvoir utiliser MediaPipe Tasks, vous devez importer la bibliothèque.
- Ouvrez le fichier build.gradle situé dans votre module app, puis faites défiler la page jusqu'au bloc dependencies.
- Un commentaire doit s'afficher en bas de ce bloc : // STEP 1 Dependency Import.
- Remplacez cette ligne par l'implémentation suivante :
implementation("com.google.mediapipe:tasks-vision:latest.release")
- Cliquez sur le bouton Sync Now (Synchroniser) qui s'affiche dans la bannière en haut d'Android Studio pour télécharger cette dépendance.
3. Créer un assistant de classification de chiffres MediaPipe Tasks
À l'étape suivante, vous allez remplir une classe qui effectuera le gros du travail pour votre classification de machine learning. Ouvrez DigitClassifierHelper.kt et lancez-vous !
- Recherchez le commentaire en haut du cours qui indique // STEP 2 Create listener.
- Remplacez cette ligne par le code suivant. Cela créera un écouteur qui sera utilisé pour transmettre les résultats de la classe DigitClassifierHelper à l'endroit où ces résultats sont écoutés (dans ce cas, il s'agira de votre classe DigitCanvasFragment, mais nous y arriverons bientôt).
// STEP 2 Create listener
interface DigitClassifierListener {
fun onError(error: String)
fun onResults(
results: ImageClassifierResult,
inferenceTime: Long
)
}
- Vous devrez également accepter un DigitClassifierListener comme paramètre facultatif pour la classe :
class DigitClassifierHelper(
val context: Context,
val digitClassifierListener: DigitClassifierListener?
) {
- Descendez jusqu'à la ligne // STEP 3 define classifier, puis ajoutez la ligne suivante pour créer un espace réservé pour l'ImageClassifier qui sera utilisé pour cette application :
// ÉTAPE 3 : définir le classificateur
private var digitClassifier: ImageClassifier? = null
- Ajoutez la fonction suivante à l'endroit où se trouve le commentaire // STEP 4 set up classifier :
// STEP 4 set up classifier
private fun setupDigitClassifier() {
val baseOptionsBuilder = BaseOptions.builder()
.setModelAssetPath("mnist.tflite")
// Describe additional options
val optionsBuilder = ImageClassifierOptions.builder()
.setRunningMode(RunningMode.IMAGE)
.setBaseOptions(baseOptionsBuilder.build())
try {
digitClassifier =
ImageClassifier.createFromOptions(
context,
optionsBuilder.build()
)
} catch (e: IllegalStateException) {
digitClassifierListener?.onError(
"Image classifier failed to initialize. See error logs for " +
"details"
)
Log.e(TAG, "MediaPipe failed to load model with error: " + e.message)
}
}
Plusieurs éléments sont en jeu dans la section ci-dessus. Examinons-les plus en détail pour bien comprendre ce qui se passe.
val baseOptionsBuilder = BaseOptions.builder()
.setModelAssetPath("mnist.tflite")
// Describe additional options
val optionsBuilder = ImageClassifierOptions.builder()
.setRunningMode(RunningMode.IMAGE)
.setBaseOptions(baseOptionsBuilder.build())
Ce bloc définira les paramètres utilisés par ImageClassifier. Cela inclut le modèle stocké dans votre application (mnist.tflite) sous BaseOptions et RunningMode sous ImageClassifierOptions, qui est IMAGE dans ce cas, mais VIDEO et LIVE_STREAM sont d'autres options disponibles. Les autres paramètres disponibles sont "MaxResults", qui limite le modèle à renvoyer un nombre maximal de résultats, et "ScoreThreshold", qui définit le niveau de confiance minimal que le modèle doit avoir dans un résultat avant de le renvoyer.
try {
digitClassifier =
ImageClassifier.createFromOptions(
context,
optionsBuilder.build()
)
} catch (e: IllegalStateException) {
digitClassifierListener?.onError(
"Image classifier failed to initialize. See error logs for " +
"details"
)
Log.e(TAG, "MediaPipe failed to load model with error: " + e.message)
}
Après avoir créé vos options de configuration, vous pouvez créer votre nouveau classificateur d'images en transmettant un contexte et les options. Si un problème survient lors de ce processus d'initialisation, une erreur sera renvoyée via votre DigitClassifierListener.
- Comme nous souhaitons initialiser ImageClassifier avant de l'utiliser, vous pouvez ajouter un bloc init pour appeler setupDigitClassifier().
init {
setupDigitClassifier()
}
- Enfin, faites défiler la page jusqu'au commentaire // STEP 5 create classify function et ajoutez le code suivant. Cette fonction accepte un Bitmap, qui dans ce cas est le chiffre dessiné, le convertit en objet MediaPipe Image (MPImage), puis classe cette image à l'aide d'ImageClassifier. Elle enregistre également la durée de l'inférence avant de renvoyer ces résultats via DigitClassifierListener.
// STEP 5 create classify function
fun classify(image: Bitmap) {
if (digitClassifier == null) {
setupDigitClassifier()
}
// Convert the input Bitmap object to an MPImage object to run inference.
// Rotating shouldn't be necessary because the text is being extracted from
// a view that should always be correctly positioned.
val mpImage = BitmapImageBuilder(image).build()
// Inference time is the difference between the system time at the start and finish of the
// process
val startTime = SystemClock.uptimeMillis()
// Run image classification using MediaPipe Image Classifier API
digitClassifier?.classify(mpImage)?.also { classificationResults ->
val inferenceTimeMs = SystemClock.uptimeMillis() - startTime
digitClassifierListener?.onResults(classificationResults, inferenceTimeMs)
}
}
Et voilà pour le fichier d'assistance ! Dans la section suivante, vous allez effectuer les dernières étapes pour commencer à classer les chiffres tirés.
4. Exécuter une inférence avec MediaPipe Tasks
Pour commencer cette section, ouvrez la classe DigitCanvasFragment dans Android Studio, qui est l'endroit où tout le travail sera effectué.
- Tout en bas de ce fichier, vous devriez voir un commentaire indiquant // STEP 6 Set up listener. Vous ajouterez ici les fonctions onResults() et onError() associées à l'écouteur.
// STEP 6 Set up listener
override fun onError(error: String) {
activity?.runOnUiThread {
Toast.makeText(requireActivity(), error, Toast.LENGTH_SHORT).show()
fragmentDigitCanvasBinding.tvResults.text = ""
}
}
override fun onResults(
results: ImageClassifierResult,
inferenceTime: Long
) {
activity?.runOnUiThread {
fragmentDigitCanvasBinding.tvResults.text = results
.classificationResult()
.classifications().get(0)
.categories().get(0)
.categoryName()
fragmentDigitCanvasBinding.tvInferenceTime.text = requireActivity()
.getString(R.string.inference_time, inferenceTime.toString())
}
}
onResults() est particulièrement important, car il affiche les résultats reçus de ImageClassifier. Comme ce rappel est déclenché à partir d'un thread d'arrière-plan, vous devrez également exécuter vos mises à jour de l'UI sur le thread UI d'Android.
- Comme vous ajoutez de nouvelles fonctions à partir d'une interface à l'étape ci-dessus, vous devrez également ajouter la déclaration d'implémentation en haut de la classe.
class DigitCanvasFragment : Fragment(), DigitClassifierHelper.DigitClassifierListener
- Vers le haut de la classe, vous devriez voir un commentaire indiquant // STEP 7a Initialize classifier (Initialiser le classificateur). C'est là que vous placerez la déclaration pour DigitClassifierHelper.
// STEP 7a Initialize classifier.
private lateinit var digitClassifierHelper: DigitClassifierHelper
- En descendant jusqu'à // STEP 7b Initialize classifier (Initialiser le classificateur), vous pouvez initialiser digitClassifierHelper dans la fonction onViewCreated().
// STEP 7b Initialize classifier
// Initialize the digit classifier helper, which does all of the
// ML work. This uses the default values for the classifier.
digitClassifierHelper = DigitClassifierHelper(
context = requireContext(), digitClassifierListener = this
)
- Pour les dernières étapes, recherchez le commentaire // STEP 8a*: classify* et ajoutez le code suivant pour appeler une nouvelle fonction que vous ajouterez dans un instant. Ce bloc de code déclenchera la classification lorsque vous retirerez votre doigt de la zone de dessin dans l'application.
// STEP 8a: classify
classifyDrawing()
- Enfin, recherchez le commentaire // STEP 8b classify pour ajouter la nouvelle fonction classifyDrawing(). Cela extraira un bitmap du canevas, puis le transmettra à DigitClassifierHelper pour effectuer la classification et recevoir les résultats dans la fonction d'interface onResults().
// STEP 8b classify
private fun classifyDrawing() {
val bitmap = fragmentDigitCanvasBinding.digitCanvas.getBitmap()
digitClassifierHelper.classify(bitmap)
}
5. Déployer et tester l'application
Après tout cela, vous devriez disposer d'une application fonctionnelle capable de classer les chiffres dessinés à l'écran. N'hésitez pas à déployer l'application sur un émulateur Android ou un appareil Android physique pour la tester.
- Cliquez sur Exécuter (
) dans la barre d'outils Android Studio pour exécuter l'application. - Dessinez un chiffre sur le bloc-notes et voyez si l'application peut le reconnaître. Il doit afficher à la fois le chiffre que le modèle pense avoir été dessiné et le temps qu'il a mis pour le prédire.

6. Félicitations !
Bravo ! Dans cet atelier de programmation, vous avez appris à ajouter la classification d'images à une application Android, et plus précisément à classer des chiffres dessinés à la main à l'aide du modèle MNIST.
Étapes suivantes
- Maintenant que vous pouvez classer des chiffres, vous pouvez entraîner votre propre modèle pour classer des lettres dessinées, des animaux ou un nombre infini d'autres éléments. Vous trouverez la documentation pour entraîner un nouveau modèle de classification d'images avec MediaPipe Model Maker sur la page developers.google.com/mediapipe.
- Découvrez les autres tâches MediaPipe disponibles pour Android, y compris la détection des points de repère du visage, la reconnaissance des gestes et la classification audio.
Nous sommes impatients de découvrir vos prochaines créations !