1. Introduzione
Che cos'è MediaPipe?
MediaPipe Solutions ti consente di applicare soluzioni di machine learning (ML) alle tue app. Fornisce un framework per la configurazione di pipeline di elaborazione predefinite che forniscono output immediati, coinvolgenti e utili agli utenti. Puoi persino personalizzare queste soluzioni con MediaPipe Model Maker per aggiornare i modelli predefiniti.
La classificazione delle immagini è una delle diverse attività di visione ML offerte da MediaPipe Solutions. MediaPipe Tasks è disponibile per Android, iOS, Python (incluso Raspberry Pi) e il web.
In questo codelab, inizierai con un'app per Android che ti consente di disegnare cifre numeriche sullo schermo, poi aggiungerai una funzionalità che classifica queste cifre disegnate come un singolo valore da 0 a 9.
Obiettivi didattici
- Come incorporare un'attività di classificazione delle immagini in un'app per Android con MediaPipe Tasks.
Che cosa ti serve
- Una versione installata di Android Studio (questo codelab è stato scritto e testato con Android Studio Giraffe).
- Un dispositivo Android o un emulatore per eseguire l'app.
- Conoscenze di base dello sviluppo per Android (non si tratta di "Hello World", ma non ci siamo lontani).
2. Aggiungere MediaPipe Tasks all'app per Android
Scaricare l'app di base per Android
Questo codelab inizierà con un campione predefinito che ti consente di disegnare sullo schermo. Puoi trovare l'app iniziale nel repository ufficiale di esempi di MediaPipe qui. Clona il repository o scarica il file ZIP facendo clic su Codice > Scarica ZIP.
Importare l'app in Android Studio
- Apri Android Studio.
- Nella schermata Benvenuto in Android Studio, seleziona Apri nell'angolo in alto a destra.

- Vai alla posizione in cui hai clonato o scaricato il repository e apri la directory codelabs/digitclassifier/android/start.
- Verifica che tutto sia stato aperto correttamente facendo clic sulla freccia verde Esegui (
) in alto a destra in Android Studio. - Dovresti vedere l'app aprirsi con una schermata nera su cui puoi disegnare, nonché un pulsante Cancella per reimpostare la schermata. Anche se puoi disegnare su questa schermata, non fa molto altro, quindi inizieremo subito a risolvere il problema.

Modello
Quando esegui l'app per la prima volta, potresti notare che un file denominato mnist.tflite viene scaricato e archiviato nella directory assets dell'app. Per semplicità, abbiamo già preso un modello noto, MNIST, che classifica le cifre e lo abbiamo aggiunto all'app utilizzando lo script download_models.gradle nel progetto. Se decidi di addestrare un modello personalizzato, ad esempio uno per le lettere scritte a mano, devi rimuovere il file download_models.gradle, eliminare il riferimento nel file build.gradle a livello di app e modificare il nome del modello più avanti nel codice (in particolare nel file DigitClassifierHelper.kt).
Aggiorna build.gradle
Prima di poter iniziare a utilizzare MediaPipe Tasks, devi importare la libreria.
- Apri il file build.gradle che si trova nel modulo app, quindi scorri verso il basso fino al blocco dependencies.
- Dovresti visualizzare un commento in fondo al blocco che dice // STEP 1 Dependency Import.
- Sostituisci questa riga con la seguente implementazione
implementation("com.google.mediapipe:tasks-vision:latest.release")
- Fai clic sul pulsante Sincronizza ora visualizzato nel banner nella parte superiore di Android Studio per scaricare questa dipendenza.
3. Crea un helper per il classificatore di cifre di MediaPipe Tasks
Nel passaggio successivo compilerai una classe che si occuperà della parte più complessa della classificazione di machine learning. Apri DigitClassifierHelper.kt e iniziamo.
- Trova il commento in alto nel corso che dice // STEP 2 Create listener
- Sostituisci la riga con il seguente codice. In questo modo verrà creato un listener che verrà utilizzato per passare i risultati dalla classe DigitClassifierHelper a chiunque stia rimanendo in ascolto di questi risultati (in questo caso sarà la classe DigitCanvasFragment, ma ci arriveremo presto).
// STEP 2 Create listener
interface DigitClassifierListener {
fun onError(error: String)
fun onResults(
results: ImageClassifierResult,
inferenceTime: Long
)
}
- Dovrai anche accettare un DigitClassifierListener come parametro facoltativo per la classe:
class DigitClassifierHelper(
val context: Context,
val digitClassifierListener: DigitClassifierListener?
) {
- Scendi fino alla riga // STEP 3 define classifier e aggiungi la seguente riga per creare un segnaposto per ImageClassifier che verrà utilizzato per questa app:
// STEP 3 define classifier
private var digitClassifier: ImageClassifier? = null
- Aggiungi la seguente funzione dove vedi il commento // 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)
}
}
Nella sezione precedente sono presenti diversi elementi, quindi esaminiamoli in dettaglio per capire meglio cosa sta succedendo.
val baseOptionsBuilder = BaseOptions.builder()
.setModelAssetPath("mnist.tflite")
// Describe additional options
val optionsBuilder = ImageClassifierOptions.builder()
.setRunningMode(RunningMode.IMAGE)
.setBaseOptions(baseOptionsBuilder.build())
Questo blocco definirà i parametri utilizzati da ImageClassifier. Ciò include il modello memorizzato all'interno dell'app (mnist.tflite) in BaseOptions e RunningMode in ImageClassifierOptions, che in questo caso è IMAGE, ma VIDEO e LIVE_STREAM sono opzioni aggiuntive disponibili. Gli altri parametri disponibili sono MaxResults, che limita il modello alla restituzione di un numero massimo di risultati, e ScoreThreshold, che imposta la confidenza minima che il modello deve avere in un risultato prima di restituirlo.
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)
}
Dopo aver creato le opzioni di configurazione, puoi creare il nuovo ImageClassifier passando un contesto e le opzioni. Se qualcosa va storto durante la procedura di inizializzazione, viene restituito un errore tramite DigitClassifierListener.
- Poiché vogliamo inizializzare ImageClassifier prima di utilizzarlo, puoi aggiungere un blocco init per chiamare setupDigitClassifier().
init {
setupDigitClassifier()
}
- Infine, scorri verso il basso fino al commento // STEP 5 create classify function e aggiungi il seguente codice. Questa funzione accetta una bitmap, che in questo caso è la cifra disegnata, la converte in un oggetto immagine MediaPipe (MPImage) e poi la classifica utilizzando ImageClassifier, oltre a registrare il tempo necessario per l'inferenza, prima di restituire i risultati tramite 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)
}
}
E questo è tutto per il file di assistenza. Nella sezione successiva, completerai i passaggi finali per iniziare a classificare i numeri estratti.
4. Eseguire l'inferenza con MediaPipe Tasks
Puoi iniziare questa sezione aprendo la classe DigitCanvasFragment in Android Studio, dove verrà eseguito tutto il lavoro.
- In fondo al file dovresti vedere un commento che dice // STEP 6 Set up listener. Qui aggiungerai le funzioni onResults() e onError() associate al listener.
// 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() è particolarmente importante perché mostra i risultati ricevuti da ImageClassifier. Poiché questo callback viene attivato da un thread in background, dovrai eseguire gli aggiornamenti della UI sul thread dell'interfaccia utente di Android.
- Poiché stai aggiungendo nuove funzioni da un'interfaccia nel passaggio precedente, devi anche aggiungere la dichiarazione di implementazione nella parte superiore della classe.
class DigitCanvasFragment : Fragment(), DigitClassifierHelper.DigitClassifierListener
- Verso la parte superiore della classe dovresti vedere un commento che dice // STEP 7a Initialize classifier. Qui inserirai la dichiarazione per DigitClassifierHelper.
// STEP 7a Initialize classifier.
private lateinit var digitClassifierHelper: DigitClassifierHelper
- Passando a // STEP 7b Initialize classifier (Inizializza classificatore), puoi inizializzare digitClassifierHelper all'interno della funzione 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
)
- Per gli ultimi passaggi, trova il commento // STEP 8a*: classify* e aggiungi il seguente codice per chiamare una nuova funzione che aggiungerai tra poco. Questo blocco di codice attiverà la classificazione quando sollevi il dito dall'area di disegno nell'app.
// STEP 8a: classify
classifyDrawing()
- Infine, cerca il commento // STEP 8b classify per aggiungere la nuova funzione classifyDrawing(). Viene estratto un bitmap dal canvas, che viene poi passato a DigitClassifierHelper per eseguire la classificazione e ricevere i risultati nella funzione di interfaccia onResults().
// STEP 8b classify
private fun classifyDrawing() {
val bitmap = fragmentDigitCanvasBinding.digitCanvas.getBitmap()
digitClassifierHelper.classify(bitmap)
}
5. Esegui il deployment e testa l'app
Dopo tutto questo, dovresti avere un'app funzionante in grado di classificare le cifre disegnate sullo schermo. Procedi con il deployment dell'app su un emulatore Android o su un dispositivo Android fisico per testarla.
- Fai clic su Esegui (
) nella barra degli strumenti di Android Studio per eseguire l'app. - Disegna una cifra qualsiasi sul blocco da disegno e verifica se l'app riesce a riconoscerla. Deve mostrare sia la cifra che il modello ritiene sia stata disegnata, sia il tempo impiegato per prevederla.

6. Complimenti!
Ce l'hai fatta! In questo codelab hai imparato ad aggiungere la classificazione delle immagini a un'app per Android e, in particolare, a classificare le cifre disegnate a mano utilizzando il modello MNIST.
Passaggi successivi
- Ora che puoi classificare le cifre, potresti voler addestrare il tuo modello per classificare le lettere disegnate, gli animali o un numero infinito di altri elementi. Puoi trovare la documentazione per l'addestramento di un nuovo modello di classificazione delle immagini con MediaPipe Model Maker nella pagina developers.google.com/mediapipe.
- Scopri le altre attività MediaPipe disponibili per Android, tra cui il rilevamento dei punti di riferimento del volto, il riconoscimento dei gesti e la classificazione audio.
Non vediamo l'ora di scoprire tutte le cose fantastiche che realizzerai.