Para chamar uma API do Google Cloud no seu aplicativo, você precisa criar uma API REST intermediária que processe a autorização e proteja valores secretos, como chaves de API. Em seguida, você precisa escrever o código em seu aplicativo móvel para autenticar e se comunicar com esse serviço intermediário.
Uma maneira de criar essa API REST é usar o Firebase Authentication and Functions, que fornece um gateway gerenciado e sem servidor para APIs do Google Cloud que gerencia a autenticação e pode ser chamado de seu aplicativo móvel com SDKs pré-criados.
Este guia demonstra como usar essa técnica para chamar a API Cloud Vision do seu aplicativo. Esse método permitirá que todos os usuários autenticados acessem os serviços cobrados do Cloud Vision por meio do seu projeto do Cloud. Portanto, considere se esse mecanismo de autenticação é suficiente para seu caso de uso antes de continuar.
Antes de você começar
Configure seu projeto
- Adicione o Firebase ao seu projeto Android , caso ainda não o tenha feito.
Se você ainda não habilitou APIs baseadas em nuvem para seu projeto, faça-o agora:
- Abra a página APIs do Firebase ML do console do Firebase.
Se você ainda não atualizou seu projeto para o plano de preços Blaze, clique em Atualizar para fazer isso. (Você será solicitado a atualizar somente se o seu projeto não estiver no plano Blaze.)
Somente projetos no nível Blaze podem usar APIs baseadas em nuvem.
- Se as APIs baseadas em nuvem ainda não estiverem habilitadas, clique em Habilitar APIs baseadas em nuvem .
- Configure suas chaves existentes da API Firebase para proibir o acesso à API Cloud Vision:
- Abra a página Credenciais do console do Cloud.
- Para cada chave de API na lista, abra a visualização de edição e, na seção Restrições de chave, adicione à lista todas as APIs disponíveis , exceto a API Cloud Vision.
Implantar a função que pode ser chamada
Em seguida, implante a função do Cloud que você usará para conectar seu aplicativo e a API Cloud Vision. O repositório de functions-samples
contém um exemplo que você pode usar.
Por padrão, acessar a API Cloud Vision por meio desta função permitirá que apenas usuários autenticados do seu aplicativo acessem a API Cloud Vision. Você pode modificar a função para diferentes requisitos.
Para implantar a função:
- Clone ou baixe o repositório de amostras de funções e mude para o diretório
Node-1st-gen/vision-annotate-image
:git clone https://github.com/firebase/functions-samples
cd Node-1st-gen/vision-annotate-image
- Instale dependências:
cd functions
npm install
cd ..
- Se você não tiver a CLI do Firebase, instale-a .
- Inicialize um projeto do Firebase no diretório
vision-annotate-image
. Quando solicitado, selecione seu projeto na lista.firebase init
- Implante a função:
firebase deploy --only functions:annotateImage
Adicione o Firebase Auth ao seu aplicativo
A função chamável implantada acima rejeitará qualquer solicitação de usuários não autenticados do seu aplicativo. Se ainda não tiver feito isso, você precisará adicionar o Firebase Auth ao seu aplicativo.
Adicione as dependências necessárias ao seu aplicativo
<project>/<app-module>/build.gradle.kts
ou <project>/<app-module>/build.gradle
):implementation("com.google.firebase:firebase-functions:20.4.0") implementation("com.google.code.gson:gson:2.8.6")
1. Prepare a imagem de entrada
Para chamar o Cloud Vision, a imagem deve ser formatada como uma string codificada em base64. Para processar uma imagem de um URI de arquivo salvo:- Obtenha a imagem como um objeto
Bitmap
:Kotlin+KTX
var bitmap: Bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri)
Java
Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);
- Opcionalmente, reduza a imagem para economizar largura de banda. Consulte os tamanhos de imagem recomendados pelo 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);
- Converta o objeto bitmap em uma string codificada em 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);
A imagem representada pelo objeto
Bitmap
deve estar na vertical, sem necessidade de rotação adicional. 2. Invoque a função que pode ser chamada para reconhecer pontos de referência
Para reconhecer pontos de referência em uma imagem, invoque a função que pode ser chamada, passando uma solicitação JSON Cloud Vision .Primeiro, inicialize uma instância do Cloud Functions:
Kotlin+KTX
private lateinit var functions: FirebaseFunctions // ... functions = Firebase.functions
Java
private FirebaseFunctions mFunctions; // ... mFunctions = FirebaseFunctions.getInstance();
Defina um método para invocar a função:
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())); } }); }
Crie uma solicitação JSON com o 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);
Finalmente, invoque a função:
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. Obtenha informações sobre os pontos de referência reconhecidos
Se a operação de reconhecimento de ponto de referência for bem-sucedida, uma resposta JSON de BatchAnnotateImagesResponse será retornada no resultado da tarefa. Cada objeto nalandmarkAnnotations
representa um ponto de referência que foi reconhecido na imagem. Para cada ponto de referência, você pode obter suas coordenadas delimitadoras na imagem de entrada, o nome do ponto de referência, sua latitude e longitude, seu ID de entidade do Knowledge Graph (se disponível) e a pontuação de confiança da correspondência. Por exemplo: 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();
}
}