Riconosci il testo nelle immagini in modo sicuro con Cloud Vision utilizzando Firebase Auth e Functions su Android

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 il codice nella tua app mobile per autenticarti e comunicare con questo servizio intermedio.

Un modo per creare questa API REST consiste nell'utilizzare Firebase Authentication and Functions, che ti offre un gateway serverless gestito per le API di 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 fatturati di Cloud Vision 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 tuo progetto

  1. Se non l'hai già, aggiungi Firebase al progetto Android .
  2. Se non hai già abilitato le API basate su cloud per il tuo progetto, fallo ora:

    1. Aprire la pagina di Firebase ML API della console Firebase.
    2. Se non avete già aggiornato il vostro progetto per il piano tariffario Blaze, fare clic su Aggiorna per farlo. (Ti verrà richiesto di eseguire l'aggiornamento solo se il tuo progetto non è nel piano Blaze.)

      Solo i progetti di livello Blaze possono utilizzare le API basate su cloud.

    3. Se le API basate su cloud non sono già abilitati, fare clic su Attiva API basate su cloud.
  3. Configura le tue chiavi API Firebase esistenti per impedire l'accesso all'API Cloud Vision:
    1. Aprire la Credenziali pagina della console Cloud.
    2. Per ogni chiave API nella lista, aprire la vista editing, e nella sezione Restrizioni chiave, aggiungere tutte le API disponibili ad eccezione del Cloud API Vision alla lista.

Distribuire la funzione richiamabile

Successivamente, distribuisci la funzione cloud che utilizzerai per collegare la tua app e l'API Cloud Vision. L' functions-samples repository contiene un esempio è possibile 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. È possibile modificare la funzione per esigenze diverse.

Per distribuire la funzione:

  1. Clone o scaricare le funzioni-campioni pronti contro termine e passare alla vision-annotate-image di repertorio:
    git clone https://github.com/firebase/functions-samples
    cd vision-annotate-image
    
  2. Installare le dipendenze:
    cd functions
    npm install
    cd ..
    
  3. Se non si dispone del Firebase CLI, installarlo .
  4. Inizializzare un progetto di Firebase nella vision-annotate-image directory. Quando richiesto, seleziona il tuo progetto nell'elenco.
    firebase init
  5. Distribuire la funzione:
    firebase deploy --only functions:annotateImage

Aggiungi Firebase Auth alla tua app

La funzione richiamabile distribuita sopra rifiuterà qualsiasi richiesta proveniente da utenti non autenticati della tua app. Se non lo avete già fatto, è necessario aggiungere Firebase Auth per la vostra applicazione.

Aggiungi le dipendenze necessarie alla tua app

  • Aggiungere le dipendenze per le funzioni Firebase e GSON Android librerie al modulo (a livello di app) File Gradle (di solito app / build.gradle):
    implementation 'com.google.firebase:firebase-functions:20.0.1'
    implementation 'com.google.code.gson:gson:2.8.6'
    
  • Ora sei pronto per iniziare a riconoscere il testo nelle immagini.

    1. Preparare 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 di file salvato:
    1. Ottenere l'immagine come Bitmap oggetto:

      Giava

      Bitmap bitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uri);

      Kotlin+KTX

      var bitmap: Bitmap = MediaStore.Images.Media.getBitmap(contentResolver, uri)
    2. Facoltativamente, ridimensiona l'immagine per risparmiare sulla larghezza di banda. Vedere le dimensioni delle immagini raccomandato Vision Cloud.

      Giava

      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

      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)
      }

      Giava

      // Scale down bitmap size
      bitmap = scaleBitmapDown(bitmap, 640);

      Kotlin+KTX

      // Scale down bitmap size
      bitmap = scaleBitmapDown(bitmap, 640)
    3. Converti l'oggetto bitmap in una stringa codificata base64:

      Giava

      // 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);

      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)
    4. L'immagine rappresentata dalla Bitmap oggetto deve essere in posizione verticale, senza rotazione supplementare richiesta.

    2. Invocare la funzione richiamabile per riconoscere il testo

    Per riconoscere il testo in un'immagine, richiamare la funzione callable, passando una richiesta JSON Vision Cloud .

    1. Innanzitutto, inizializza un'istanza di Cloud Functions:

      Giava

      private FirebaseFunctions mFunctions;
      // ...
      mFunctions = FirebaseFunctions.getInstance();
      

      Kotlin+KTX

      private lateinit var functions: FirebaseFunctions
      // ...
      functions = Firebase.functions
      
    2. Definire un metodo per invocare la funzione:

      Giava

      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()));
                      }
                  });
      }
      

      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))
                  }
      }
      
    3. Crea la richiesta JSON. Il Cloud API Vision supporta due tipi di rilevazione del testo: TEXT_DETECTION e DOCUMENT_TEXT_DETECTION . Vedi le Vision OCR Docs cloud per la differenza tra i due casi d'uso.

      Giava

      // 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);
      

      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)
      

      Facoltativamente, fornire suggerimenti linguistici per assistere con rilevamento della lingua (vedi lingue supportate ):

      Giava

      JsonObject imageContext = new JsonObject();
      JsonArray languageHints = new JsonArray();
      languageHints.add("en");
      imageContext.add("languageHints", languageHints);
      request.add("imageContext", imageContext);
      

      Kotlin+KTX

      val imageContext = JsonObject()
      val languageHints = JsonArray()
      languageHints.add("en")
      imageContext.add("languageHints", languageHints)
      request.add("imageContext", imageContext)
      
    4. Infine, invoca la funzione:

      Giava

      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
                          // ...
                      }
                  }
              });
      

      Kotlin+KTX

      annotateImage(request.toString())
              .addOnCompleteListener { task ->
                  if (!task.isSuccessful) {
                      // Task failed with an exception
                      // ...
                  } else {
                      // Task completed successfully
                      // ...
                  }
              }
      

    3. Estrai il testo da blocchi di testo riconosciuto

    Se l'operazione di riconoscimento del testo riesce, una risposta JSON di BatchAnnotateImagesResponse verrà restituito nel risultato dell'attività. Le annotazioni di testo possono essere trovati nel fullTextAnnotation oggetto.

    È possibile ottenere il testo riconosciuto come una stringa nel text campo. Per esempio:

    Giava

    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());
    

    Kotlin+KTX

    val annotation = task.result!!.asJsonArray[0].asJsonObject["fullTextAnnotation"].asJsonObject
    System.out.format("%nComplete annotation:")
    System.out.format("%n%s", annotation["text"].asString)
    

    È inoltre possibile ottenere informazioni specifiche per le regioni dell'immagine. Per ogni block , paragraph , word e symbol , è possibile ottenere il testo riconosciuto nella regione e le coordinate di delimitazione della regione. Per esempio:

    Giava

    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);
        }
    }
    

    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
        }
    }