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. Devi quindi scrivere codice nella tua app mobile per autenticarti e comunicare con questo servizio intermedio.
Un modo per creare questa API REST è utilizzare Firebase Authentication e Functions, che ti offre un gateway serverless gestito per le API 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 Cloud Vision fatturati 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 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, procedi nel seguente modo: ora:
- Apri l'app Firebase ML API della console Firebase.
-
Se non hai ancora eseguito l'upgrade del progetto al piano tariffario Blaze, fai clic su Esegui l'upgrade per farlo. Ti verrà chiesto di eseguire l'upgrade solo se il progetto non è nel piano Blaze.
Solo i progetti a livello di Blaze possono utilizzare le API basate su cloud.
- Se le API basate su cloud non sono già abilitate, fai clic su Abilita basate su cloud per le API.
- Configura le chiavi API Firebase esistenti per non consentire l'accesso all'API Cloud Vision:
- Apri la pagina Credenziali della console Cloud.
- Per ogni chiave API nell'elenco, apri la visualizzazione di modifica e nella Restrizioni, aggiungi tutte le API disponibili tranne Cloud Vision API nell'elenco.
Esegui il deployment della funzione richiamabile
Quindi, esegui il deployment della funzione Cloud Functions che utilizzerai per collegare la tua app
API 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. Puoi modificare la funzione in base a requisiti diversi.
Per eseguire il deployment della funzione:
- Clonare o scaricare il reposito di Functions-samples
e passa alla directory
Node-1st-gen/vision-annotate-image
:git clone https://github.com/firebase/functions-samples
cd Node-1st-gen/vision-annotate-image
- Installa le dipendenze:
cd functions
npm install
cd ..
- Se non hai l'interfaccia a riga di comando di Firebase, installala.
- Inizializza un progetto Firebase in
vision-annotate-image
. Quando richiesto, seleziona il progetto dall'elenco.firebase init
- Esegui il deployment della funzione:
firebase deploy --only functions:annotateImage
Aggiungi Firebase Auth alla tua app
La funzione richiamabile di cui sopra rifiuterà qualsiasi richiesta da parte degli 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
<project>/<app-module>/build.gradle.kts
o
<project>/<app-module>/build.gradle
):
implementation("com.google.firebase:firebase-functions:21.1.0") implementation("com.google.code.gson:gson:2.8.6")
1. Prepara l'immagine di input
Per chiamare Cloud Vision, l'immagine deve essere formattata come stringa con codifica base64. Per elaborare un'immagine da un URI file salvato:- Recupera l'immagine come oggetto
Bitmap
:Kotlin+KTX
var bitmap: Bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri)
Java
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
- Se vuoi, riduci le dimensioni dell'immagine per risparmiare larghezza di banda. Consulta le
dimensioni consigliate per le immagini di Cloud Vision.
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
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
// Scale down bitmap size bitmap = scaleBitmapDown(bitmap, 640)
Java
// Scale down bitmap size bitmap = scaleBitmapDown(bitmap, 640);
- Converti l'oggetto bitmap in una stringa codificata in base64:
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)
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);
L'immagine rappresentata dall'oggetto
Bitmap
deve essere in posizione verticale, senza alcuna rotazione aggiuntiva.
2. Richiamare la funzione richiamabile per riconoscere i punti di riferimento
Per riconoscere i punti di riferimento in un'immagine, invoca la funzione richiamabile passando una richiesta JSON Cloud Vision.Innanzitutto, inizializza un'istanza di Cloud Functions:
Kotlin+KTX
private lateinit var functions: FirebaseFunctions // ... functions = Firebase.functions
Java
private FirebaseFunctions mFunctions; // ... mFunctions = FirebaseFunctions.getInstance();
Definisci un metodo per richiamare la funzione:
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)) } }
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())); } }); }
Crea una richiesta JSON con Type (Tipo)
LANDMARK_DETECTION
: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("LANDMARK_DETECTION")) val features = JsonArray() features.add(feature) request.add("features", features)
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("LANDMARK_DETECTION")); JsonArray features = new JsonArray(); features.add(feature); request.add("features", features);
Infine, richiama la funzione:
Kotlin+KTX
annotateImage(request.toString()) .addOnCompleteListener { task -> if (!task.isSuccessful) { // Task failed with an exception // ... } else { // Task completed successfully // ... } }
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 // ... } } });
3. Ricevere informazioni sui punti di riferimento riconosciuti
Se l'operazione di riconoscimento dei punti di riferimento va a buon fine, nel risultato dell'attività verrà restituita una risposta JSON di BatchAnnotateImagesResponse. Ogni oggetto nell'arraylandmarkAnnotations
rappresenta un punto di riferimento riconosciuto nell'immagine. Per ogni punto di riferimento,
puoi ottenere le coordinate di delimitazione nell'immagine di input, il nome del punto di riferimento
la latitudine e la longitudine, l'ID entità del Knowledge Graph (se disponibile) e
il punteggio di confidenza della corrispondenza. Ad esempio:
Kotlin+KTX
for (label in task.result!!.asJsonArray[0].asJsonObject["landmarkAnnotations"].asJsonArray) {
val labelObj = label.asJsonObject
val landmarkName = labelObj["description"]
val entityId = labelObj["mid"]
val score = labelObj["score"]
val bounds = labelObj["boundingPoly"]
// Multiple locations are possible, e.g., the location of the depicted
// landmark and the location the picture was taken.
for (loc in labelObj["locations"].asJsonArray) {
val latitude = loc.asJsonObject["latLng"].asJsonObject["latitude"]
val longitude = loc.asJsonObject["latLng"].asJsonObject["longitude"]
}
}
Java
for (JsonElement label : task.getResult().getAsJsonArray().get(0).getAsJsonObject().get("landmarkAnnotations").getAsJsonArray()) {
JsonObject labelObj = label.getAsJsonObject();
String landmarkName = labelObj.get("description").getAsString();
String entityId = labelObj.get("mid").getAsString();
float score = labelObj.get("score").getAsFloat();
JsonObject bounds = labelObj.get("boundingPoly").getAsJsonObject();
// Multiple locations are possible, e.g., the location of the depicted
// landmark and the location the picture was taken.
for (JsonElement loc : labelObj.get("locations").getAsJsonArray()) {
JsonObject latLng = loc.getAsJsonObject().get("latLng").getAsJsonObject();
double latitude = latLng.get("latitude").getAsDouble();
double longitude = latLng.get("longitude").getAsDouble();
}
}