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 faturados do Cloud Vision por meio do seu projeto do Cloud. Portanto, considere se esse mecanismo de autenticação é suficiente para o 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 começar a reconhecer texto em 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 reconhecer texto
Para reconhecer texto 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. A API Cloud Vision oferece suporte a dois tipos de detecção de texto:
TEXT_DETECTION
eDOCUMENT_TEXT_DETECTION
. Consulte os documentos de OCR do Cloud Vision para saber a diferença entre os dois casos de uso.// 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("type", JsonPrimitive("TEXT_DETECTION"))
// Alternatively, for DOCUMENT_TEXT_DETECTION:
// feature.add("type", JsonPrimitive("DOCUMENT_TEXT_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("type", new JsonPrimitive("TEXT_DETECTION"));
// Alternatively, for DOCUMENT_TEXT_DETECTION:
//feature.add("type", new JsonPrimitive("DOCUMENT_TEXT_DETECTION"));
JsonArray features = new JsonArray();
features.add(feature);
request.add("features", features);Opcionalmente, forneça dicas de idioma para ajudar na detecção de idioma (consulte idiomas suportados ):
val imageContext = JsonObject()
val languageHints = JsonArray()
languageHints.add("en")
imageContext.add("languageHints", languageHints)
request.add("imageContext", imageContext)JsonObject imageContext = new JsonObject();
JsonArray languageHints = new JsonArray();
languageHints.add("en");
imageContext.add("languageHints", languageHints);
request.add("imageContext", imageContext);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. Extraia texto de blocos de texto reconhecidos
Se a operação de reconhecimento de texto for bem-sucedida, uma resposta JSON de BatchAnnotateImagesResponse será retornada no resultado da tarefa. As anotações de texto podem ser encontradas no objetofullTextAnnotation
. Você pode obter o texto reconhecido como uma string no campo text
. Por exemplo:
val annotation = task.result!!.asJsonArray[0].asJsonObject["fullTextAnnotation"].asJsonObject
System.out.format("%nComplete annotation:")
System.out.format("%n%s", annotation["text"].asString)
JsonObject annotation = task.getResult().getAsJsonArray().get(0).getAsJsonObject().get("fullTextAnnotation").getAsJsonObject();
System.out.format("%nComplete annotation:%n");
System.out.format("%s%n", annotation.get("text").getAsString());
Você também pode obter informações específicas de regiões da imagem. Para cada block
, paragraph
, word
e symbol
, você pode obter o texto reconhecido na região e as coordenadas delimitadoras da região. Por exemplo:
for (page in annotation["pages"].asJsonArray) {
var pageText = ""
for (block in page.asJsonObject["blocks"].asJsonArray) {
var blockText = ""
for (para in block.asJsonObject["paragraphs"].asJsonArray) {
var paraText = ""
for (word in para.asJsonObject["words"].asJsonArray) {
var wordText = ""
for (symbol in word.asJsonObject["symbols"].asJsonArray) {
wordText += symbol.asJsonObject["text"].asString
System.out.format(
"Symbol text: %s (confidence: %f)%n",
symbol.asJsonObject["text"].asString,
symbol.asJsonObject["confidence"].asFloat,
)
}
System.out.format(
"Word text: %s (confidence: %f)%n%n",
wordText,
word.asJsonObject["confidence"].asFloat,
)
System.out.format("Word bounding box: %s%n", word.asJsonObject["boundingBox"])
paraText = String.format("%s%s ", paraText, wordText)
}
System.out.format("%nParagraph: %n%s%n", paraText)
System.out.format("Paragraph bounding box: %s%n", para.asJsonObject["boundingBox"])
System.out.format("Paragraph Confidence: %f%n", para.asJsonObject["confidence"].asFloat)
blockText += paraText
}
pageText += blockText
}
}
for (JsonElement page : annotation.get("pages").getAsJsonArray()) {
StringBuilder pageText = new StringBuilder();
for (JsonElement block : page.getAsJsonObject().get("blocks").getAsJsonArray()) {
StringBuilder blockText = new StringBuilder();
for (JsonElement para : block.getAsJsonObject().get("paragraphs").getAsJsonArray()) {
StringBuilder paraText = new StringBuilder();
for (JsonElement word : para.getAsJsonObject().get("words").getAsJsonArray()) {
StringBuilder wordText = new StringBuilder();
for (JsonElement symbol : word.getAsJsonObject().get("symbols").getAsJsonArray()) {
wordText.append(symbol.getAsJsonObject().get("text").getAsString());
System.out.format("Symbol text: %s (confidence: %f)%n", symbol.getAsJsonObject().get("text").getAsString(), symbol.getAsJsonObject().get("confidence").getAsFloat());
}
System.out.format("Word text: %s (confidence: %f)%n%n", wordText.toString(), word.getAsJsonObject().get("confidence").getAsFloat());
System.out.format("Word bounding box: %s%n", word.getAsJsonObject().get("boundingBox"));
paraText.append(wordText.toString()).append(" ");
}
System.out.format("%nParagraph:%n%s%n", paraText);
System.out.format("Paragraph bounding box: %s%n", para.getAsJsonObject().get("boundingBox"));
System.out.format("Paragraph Confidence: %f%n", para.getAsJsonObject().get("confidence").getAsFloat());
blockText.append(paraText);
}
pageText.append(blockText);
}
}