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
: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 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:
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 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.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("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)
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("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 ):
Kotlin+KTX
val imageContext = JsonObject() val languageHints = JsonArray() languageHints.add("en") imageContext.add("languageHints", languageHints) request.add("imageContext", imageContext)
Java
JsonObject imageContext = new JsonObject(); JsonArray languageHints = new JsonArray(); languageHints.add("en"); imageContext.add("languageHints", languageHints); request.add("imageContext", imageContext);
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. 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:
Kotlin+KTX
val annotation = task.result!!.asJsonArray[0].asJsonObject["fullTextAnnotation"].asJsonObject
System.out.format("%nComplete annotation:")
System.out.format("%n%s", annotation["text"].asString)
Java
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:
Kotlin+KTX
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
}
}
Java
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);
}
}