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")
Agora você está pronto para rotular imagens.
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
:var bitmap: Bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri)
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.
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)
}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);
}// Scale down bitmap size
bitmap = scaleBitmapDown(bitmap, 640)// Scale down bitmap size
bitmap = scaleBitmapDown(bitmap, 640); - Converta o objeto bitmap em uma string codificada em base64:
// 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)// 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 rotular a imagem
Para rotular objetos 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:
private lateinit var functions: FirebaseFunctions
// ...
functions = Firebase.functionsprivate FirebaseFunctions mFunctions;
// ...
mFunctions = FirebaseFunctions.getInstance();Defina um método para invocar a função:
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))
}
}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 a solicitação JSON com Type definido como
LABEL_DETECTION
:// 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)// 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);Finalmente, invoque a função:
annotateImage(request.toString())
.addOnCompleteListener { task ->
if (!task.isSuccessful) {
// Task failed with an exception
// ...
} else {
// Task completed successfully
// ...
}
}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 objetos rotulados
Se a operação de rotulagem da imagem for bem-sucedida, uma resposta JSON de BatchAnnotateImagesResponse será retornada no resultado da tarefa. Cada objeto na matrizlabelAnnotations
representa algo que foi rotulado na imagem. Para cada rótulo, você pode obter a descrição do texto do rótulo, seu ID de entidade do Knowledge Graph (se disponível) e a pontuação de confiança da correspondência. Por exemplo: 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"]
}
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();
}