Per chiamare un'API Google Cloud dalla tua app, devi creare un'API REST intermedia che gestisca l'autorizzazione e protegga i valori segreti come le chiavi API. È quindi necessario scrivere il codice nell'app mobile per autenticarsi e comunicare con questo servizio intermedio.
Un modo per creare questa API REST è utilizzare l'autenticazione e le funzioni Firebase, che ti offre un gateway gestito e serverless per le API di Google Cloud che gestisce l'autenticazione e può essere chiamato dalla tua app mobile con SDK predefiniti.
Questa guida mostra come utilizzare questa tecnica per chiamare l'API Cloud Vision dalla tua app. Questo metodo consentirà a tutti gli utenti autenticati di accedere ai servizi fatturati Cloud Vision tramite il tuo progetto Cloud, quindi valuta se questo meccanismo di autenticazione è sufficiente per il tuo caso d'uso prima di procedere.
Prima di iniziare
Configura il tuo progetto
- Se non l'hai già fatto, aggiungi Firebase al tuo progetto Android .
Se non hai già abilitato le API basate su cloud per il tuo progetto, fallo ora:
- Apri la pagina API Firebase ML della console Firebase.
Se non hai già aggiornato il tuo progetto al piano tariffario Blaze, fai clic su Aggiorna per farlo. (Ti verrà richiesto di eseguire l'aggiornamento solo se il tuo progetto non è nel piano Blaze.)
Solo i progetti di livello Blaze possono utilizzare le API basate su cloud.
- Se le API basate su cloud non sono già abilitate, fai clic su Abilita API basate su cloud .
- Configura le tue chiavi API Firebase esistenti per impedire l'accesso all'API Cloud Vision:
- Apri la pagina Credenziali della console Cloud.
- Per ciascuna chiave API nell'elenco, apri la visualizzazione di modifica e, nella sezione Restrizioni chiave, aggiungi all'elenco tutte le API disponibili tranne l'API Cloud Vision.
Distribuire la funzione richiamabile
Quindi, distribuisci la funzione cloud che utilizzerai per collegare la tua app e l'API Cloud Vision. Il repository functions-samples
contiene un esempio che puoi utilizzare.
Per impostazione predefinita, l'accesso all'API Cloud Vision tramite questa funzione consentirà solo agli utenti autenticati della tua app di accedere all'API Cloud Vision. È possibile modificare la funzione per esigenze diverse.
Per distribuire la funzione:
- Clona o scarica il repository functions-samples e passa alla directory
vision-annotate-image
:git clone https://github.com/firebase/functions-samples
cd vision-annotate-image
- Installa le dipendenze:
cd functions
npm install
cd ..
- Se non hai la CLI di Firebase, installala .
- Inizializza un progetto Firebase nella directory
vision-annotate-image
. Quando richiesto, seleziona il tuo progetto nell'elenco.firebase init
- Distribuire la funzione:
firebase deploy --only functions:annotateImage
Aggiungi Firebase Auth alla tua app
La funzione richiamabile distribuita sopra rifiuterà qualsiasi richiesta da utenti non autenticati della tua app. Se non l'hai già fatto, dovrai aggiungere Firebase Auth alla tua app.
Aggiungi le dipendenze necessarie alla tua app
implementation 'com.google.firebase:firebase-functions:20.1.0' implementation 'com.google.code.gson:gson:2.8.6'
Ora sei pronto per etichettare le immagini.
1. Preparare l'immagine di input
Per poter chiamare Cloud Vision, l'immagine deve essere formattata come stringa con codifica base64. Per elaborare un'immagine da un file URI salvato:- Ottieni l'immagine come oggetto
Bitmap
:Java
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
Kotlin+KTX
var bitmap: Bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri)
- Facoltativamente, ridimensionare l'immagine per risparmiare sulla larghezza di banda. Consulta le dimensioni delle immagini consigliate da Cloud Vision.
Java
private Bitmap scaleBitmapDown(Bitmap bitmap, int maxDimension) { int originalWidth = bitmap.getWidth(); int originalHeight = bitmap.getHeight(); int resizedWidth = maxDimension; int resizedHeight = maxDimension; if (originalHeight > originalWidth) { resizedHeight = maxDimension; resizedWidth = (int) (resizedHeight * (float) originalWidth / (float) originalHeight); } else if (originalWidth > originalHeight) { resizedWidth = maxDimension; resizedHeight = (int) (resizedWidth * (float) originalHeight / (float) originalWidth); } else if (originalHeight == originalWidth) { resizedHeight = maxDimension; resizedWidth = maxDimension; } return Bitmap.createScaledBitmap(bitmap, resizedWidth, resizedHeight, false); }
Kotlin+KTX
private fun scaleBitmapDown(bitmap: Bitmap, maxDimension: Int): Bitmap { val originalWidth = bitmap.width val originalHeight = bitmap.height var resizedWidth = maxDimension var resizedHeight = maxDimension if (originalHeight > originalWidth) { resizedHeight = maxDimension resizedWidth = (resizedHeight * originalWidth.toFloat() / originalHeight.toFloat()).toInt() } else if (originalWidth > originalHeight) { resizedWidth = maxDimension resizedHeight = (resizedWidth * originalHeight.toFloat() / originalWidth.toFloat()).toInt() } else if (originalHeight == originalWidth) { resizedHeight = maxDimension resizedWidth = maxDimension } return Bitmap.createScaledBitmap(bitmap, resizedWidth, resizedHeight, false) }
Java
// Scale down bitmap size bitmap = scaleBitmapDown(bitmap, 640);
Kotlin+KTX
// Scale down bitmap size bitmap = scaleBitmapDown(bitmap, 640)
- Converti l'oggetto bitmap in una stringa con codifica base64:
Java
// Convert bitmap to base64 encoded string ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream); byte[] imageBytes = byteArrayOutputStream.toByteArray(); String base64encoded = Base64.encodeToString(imageBytes, Base64.NO_WRAP);
Kotlin+KTX
// Convert bitmap to base64 encoded string val byteArrayOutputStream = ByteArrayOutputStream() bitmap.compress(Bitmap.CompressFormat.JPEG, 100, byteArrayOutputStream) val imageBytes: ByteArray = byteArrayOutputStream.toByteArray() val base64encoded = Base64.encodeToString(imageBytes, Base64.NO_WRAP)
L'immagine rappresentata dall'oggetto
Bitmap
deve essere verticale, senza necessità di rotazione aggiuntiva.2. Richiamare la funzione richiamabile per etichettare l'immagine
Per etichettare oggetti in un'immagine, richiama la funzione richiamabile passando una richiesta JSON Cloud Vision .Innanzitutto, inizializza un'istanza di Cloud Functions:
Java
private FirebaseFunctions mFunctions; // ... mFunctions = FirebaseFunctions.getInstance();
Kotlin+KTX
private lateinit var functions: FirebaseFunctions // ... functions = Firebase.functions
Definire un metodo per invocare la funzione:
Java
private Task<JsonElement> annotateImage(String requestJson) { return mFunctions .getHttpsCallable("annotateImage") .call(requestJson) .continueWith(new Continuation<HttpsCallableResult, JsonElement>() { @Override public JsonElement then(@NonNull Task<HttpsCallableResult> task) { // This continuation runs on either success or failure, but if the task // has failed then getResult() will throw an Exception which will be // propagated down. return JsonParser.parseString(new Gson().toJson(task.getResult().getData())); } }); }
Kotlin+KTX
private fun annotateImage(requestJson: String): Task<JsonElement> { return functions .getHttpsCallable("annotateImage") .call(requestJson) .continueWith { task -> // This continuation runs on either success or failure, but if the task // has failed then result will throw an Exception which will be // propagated down. val result = task.result?.data JsonParser.parseString(Gson().toJson(result)) } }
Crea la richiesta JSON con il tipo impostato su
LABEL_DETECTION
:Java
// Create json request to cloud vision JsonObject request = new JsonObject(); // Add image to request JsonObject image = new JsonObject(); image.add("content", new JsonPrimitive(base64encoded)); request.add("image", image); //Add features to the request JsonObject feature = new JsonObject(); feature.add("maxResults", new JsonPrimitive(5)); feature.add("type", new JsonPrimitive("LABEL_DETECTION")); JsonArray features = new JsonArray(); features.add(feature); request.add("features", features);
Kotlin+KTX
// Create json request to cloud vision val request = JsonObject() // Add image to request val image = JsonObject() image.add("content", JsonPrimitive(base64encoded)) request.add("image", image) //Add features to the request val feature = JsonObject() feature.add("maxResults", JsonPrimitive(5)) feature.add("type", JsonPrimitive("LABEL_DETECTION")) val features = JsonArray() features.add(feature) request.add("features", features)
Infine, invoca la funzione:
Java
annotateImage(request.toString()) .addOnCompleteListener(new OnCompleteListener<JsonElement>() { @Override public void onComplete(@NonNull Task<JsonElement> task) { if (!task.isSuccessful()) { // Task failed with an exception // ... } else { // Task completed successfully // ... } } });
Kotlin+KTX
annotateImage(request.toString()) .addOnCompleteListener { task -> if (!task.isSuccessful) { // Task failed with an exception // ... } else { // Task completed successfully // ... } }
3. Ottieni informazioni sugli oggetti etichettati
Se l'operazione di etichettatura dell'immagine ha esito positivo, nel risultato dell'attività verrà restituita una risposta JSON di BatchAnnotateImagesResponse . Ogni oggetto nella matricelabelAnnotations
rappresenta qualcosa che è stato etichettato nell'immagine. Per ogni etichetta, puoi ottenere la descrizione testuale dell'etichetta, il suo ID entità Knowledge Graph (se disponibile) e il punteggio di affidabilità della corrispondenza. Per esempio: Java
for (JsonElement label : task.getResult().getAsJsonArray().get(0).getAsJsonObject().get("labelAnnotations").getAsJsonArray()) {
JsonObject labelObj = label.getAsJsonObject();
String text = labelObj.get("description").getAsString();
String entityId = labelObj.get("mid").getAsString();
float score = labelObj.get("score").getAsFloat();
}
Kotlin+KTX
for (label in task.result!!.asJsonArray[0].asJsonObject["labelAnnotations"].asJsonArray) {
val labelObj = label.asJsonObject
val text = labelObj["description"]
val entityId = labelObj["mid"]
val confidence = labelObj["score"]
}