Reconnaître du texte dans des images de manière sécurisée avec Cloud Vision à l'aide de Firebase Auth et de Functions sur les plates-formes Apple

Pour appeler une API Google Cloud depuis votre application, vous devez créer un pipeline intermédiaire API REST qui gère les autorisations et protège les valeurs secrètes telles que les clés API Vous devez ensuite écrire du code dans votre application mobile pour vous authentifier et communiquer avec ce service intermédiaire.

Pour créer cette API REST, vous pouvez utiliser Firebase Authentication et Functions, qui vous fournit une passerelle gérée et sans serveur vers les API Google Cloud qui gère l'authentification et peut être appelée depuis votre application mobile avec des SDK prédéfinis.

Ce guide explique comment utiliser cette technique pour appeler l'API Cloud Vision à partir de votre application. Cette méthode permet à tous les utilisateurs authentifiés d'accéder aux services facturés Cloud Vision via votre projet Cloud. Réfléchissez donc à ce mécanisme d'authentification avant de continuer.

Avant de commencer

Configurer votre projet

Si vous n'avez pas encore ajouté Firebase à votre application, suivez les les étapes décrites dans le guide de démarrage.

Utilisez Swift Package Manager pour installer et gérer les dépendances Firebase.

  1. Dans Xcode, à partir de votre projet d'application ouvert, accédez à File > Add Packages (Fichier > Ajouter des packages).
  2. Lorsque vous y êtes invité, ajoutez le dépôt du SDK des plates-formes Firebase pour Apple :
  3.   https://github.com/firebase/firebase-ios-sdk.git
  4. Sélectionnez la bibliothèque Firebase ML.
  5. Ajoutez l'indicateur -ObjC à la section Other Linker Flags (Autres indicateurs Linker) des paramètres de compilation de votre cible.
  6. Lorsque vous avez terminé, Xcode commence à résoudre et à télécharger automatiquement vos dépendances en arrière-plan.

Ensuite, effectuez la configuration dans l'application:

  1. Dans votre application, importez Firebase :

    Swift

    import FirebaseMLModelDownloader

    Objective-C

    @import FirebaseMLModelDownloader;

Suivez quelques étapes de configuration supplémentaires pour être prêt :

  1. Si vous n'avez pas encore activé les API basées sur le cloud pour votre projet, faites-le maintenant :

    1. Ouvrez la page API Firebase ML de la console Firebase.
    2. Si vous n'avez pas encore migré votre projet vers le forfait Blaze, cliquez sur Mettre à niveau. (Vous ne serez invité à effectuer la mise à niveau que si votre projet n'est pas associé au forfait Blaze.)

      Seuls les projets de niveau Blaze peuvent utiliser les API basées sur le cloud.

    3. Si les API cloud ne sont pas déjà activées, cliquez sur Activer les API cloud.
  2. Configurez vos clés API Firebase existantes pour interdire l'accès au cloud API Vision:
    1. Ouvrez la page Identifiants de la console Cloud.
    2. Pour chaque clé API de la liste, ouvrez la vue d'édition, puis dans la vue Section "Restrictions", ajouter toutes les API disponibles à l'exception de Cloud Vision à la liste.

Déployer la fonction appelable

Déployez ensuite la fonction Cloud que vous utiliserez pour relier votre application et la API Vision. Le dépôt functions-samples contient un exemple que vous pouvez utiliser.

Par défaut, l'accès à l'API Cloud Vision via cette fonction autorise l'accès à l'API Cloud Vision uniquement pour les utilisateurs authentifiés. Vous pouvez modifier la fonction pour différentes exigences.

Pour déployer la fonction, procédez comme suit :

  1. Clonez ou téléchargez le dépôt functions-samples et accédez au répertoire Node-1st-gen/vision-annotate-image :
    git clone https://github.com/firebase/functions-samples
    cd Node-1st-gen/vision-annotate-image
    
  2. Installez les dépendances :
    cd functions
    npm install
    cd ..
  3. Si vous ne disposez pas de la CLI Firebase, installez-la.
  4. Initialiser un projet Firebase dans vision-annotate-image . Lorsque vous y êtes invité, sélectionnez votre projet dans la liste.
    firebase init
  5. Déployez la fonction :
    firebase deploy --only functions:annotateImage

Ajouter Firebase Auth à votre application

La fonction appelable déployée ci-dessus refusera toute requête provenant d'utilisateurs non authentifiés de votre application. Si vous ne l'avez pas déjà fait, vous devez ajouter Firebase Auth à votre application.

Ajouter les dépendances nécessaires à votre application

Utilisez Swift Package Manager pour installer la bibliothèque Cloud Functions for Firebase.

Vous êtes maintenant prêt à commencer à reconnaître du texte dans des images.

1. Préparer l'image d'entrée

Pour appeler Cloud Vision, l'image doit être mise en forme sous la forme d'une chaîne encodée en base64. Pour traiter une UIImage:

Swift

guard let imageData = uiImage.jpegData(compressionQuality: 1.0) else { return }
let base64encodedImage = imageData.base64EncodedString()

Objective-C

NSData *imageData = UIImageJPEGRepresentation(uiImage, 1.0f);
NSString *base64encodedImage =
  [imageData base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength];

2. Appeler la fonction appelable pour reconnaître le texte

Pour reconnaître les points de repère dans une image, appelez la fonction appelable en transmettant une Requête Cloud Vision au format JSON.

  1. Commencez par initialiser une instance de Cloud Functions :

    Swift

    lazy var functions = Functions.functions()
    

    Objective-C

    @property(strong, nonatomic) FIRFunctions *functions;
    
  2. Créez la requête. L'API Cloud Vision accepte deux types de détection de texte : TEXT_DETECTION et DOCUMENT_TEXT_DETECTION. Consultez la documentation sur l'OCR de Cloud Vision. les différences entre les deux cas d'utilisation.

    Swift

    let requestData = [
      "image": ["content": base64encodedImage],
      "features": ["type": "TEXT_DETECTION"],
      "imageContext": ["languageHints": ["en"]]
    ]
    

    Objective-C

    NSDictionary *requestData = @{
      @"image": @{@"content": base64encodedImage},
      @"features": @{@"type": @"TEXT_DETECTION"},
      @"imageContext": @{@"languageHints": @[@"en"]}
    };
    
  3. Enfin, appelez la fonction :

    Swift

    do {
      let result = try await functions.httpsCallable("annotateImage").call(requestData)
      print(result)
    } catch {
      if let error = error as NSError? {
        if error.domain == FunctionsErrorDomain {
          let code = FunctionsErrorCode(rawValue: error.code)
          let message = error.localizedDescription
          let details = error.userInfo[FunctionsErrorDetailsKey]
        }
        // ...
      }
    }
    

    Objective-C

    [[_functions HTTPSCallableWithName:@"annotateImage"]
                              callWithObject:requestData
                                  completion:^(FIRHTTPSCallableResult * _Nullable result, NSError * _Nullable error) {
            if (error) {
              if ([error.domain isEqualToString:@"com.firebase.functions"]) {
                FIRFunctionsErrorCode code = error.code;
                NSString *message = error.localizedDescription;
                NSObject *details = error.userInfo[@"details"];
              }
              // ...
            }
            // Function completed succesfully
            // Get information about labeled objects
    
          }];
    

3. Extraire du texte à partir de blocs de texte reconnu

Si l'opération de reconnaissance de texte aboutit, une réponse JSON de type BatchAnnotateImagesResponse est renvoyée dans le résultat de la tâche. Les annotations de texte se trouvent dans l'objet fullTextAnnotation.

Vous pouvez obtenir le texte reconnu sous forme de chaîne dans le champ text. Exemple :

Swift

let annotation = result.flatMap { $0.data as? [String: Any] }
    .flatMap { $0["fullTextAnnotation"] }
    .flatMap { $0 as? [String: Any] }
guard let annotation = annotation else { return }

if let text = annotation["text"] as? String {
  print("Complete annotation: \(text)")
}

Objective-C

NSDictionary *annotation = result.data[@"fullTextAnnotation"];
if (!annotation) { return; }
NSLog(@"\nComplete annotation:");
NSLog(@"\n%@", annotation[@"text"]);

Vous pouvez également obtenir des informations spécifiques aux zones de l'image. Pour chaque block, paragraph, word et symbol, vous pouvez faire reconnaître le texte dans la région. et les coordonnées de délimitation de la région. Exemple :

Swift

guard let pages = annotation["pages"] as? [[String: Any]] else { return }
for page in pages {
  var pageText = ""
  guard let blocks = page["blocks"] as? [[String: Any]] else { continue }
  for block in blocks {
    var blockText = ""
    guard let paragraphs = block["paragraphs"] as? [[String: Any]] else { continue }
    for paragraph in paragraphs {
      var paragraphText = ""
      guard let words = paragraph["words"] as? [[String: Any]] else { continue }
      for word in words {
        var wordText = ""
        guard let symbols = word["symbols"] as? [[String: Any]] else { continue }
        for symbol in symbols {
          let text = symbol["text"] as? String ?? ""
          let confidence = symbol["confidence"] as? Float ?? 0.0
          wordText += text
          print("Symbol text: \(text) (confidence: \(confidence)%n")
        }
        let confidence = word["confidence"] as? Float ?? 0.0
        print("Word text: \(wordText) (confidence: \(confidence)%n%n")
        let boundingBox = word["boundingBox"] as? [Float] ?? [0.0, 0.0, 0.0, 0.0]
        print("Word bounding box: \(boundingBox.description)%n")
        paragraphText += wordText
      }
      print("%nParagraph: %n\(paragraphText)%n")
      let boundingBox = paragraph["boundingBox"] as? [Float] ?? [0.0, 0.0, 0.0, 0.0]
      print("Paragraph bounding box: \(boundingBox)%n")
      let confidence = paragraph["confidence"] as? Float ?? 0.0
      print("Paragraph Confidence: \(confidence)%n")
      blockText += paragraphText
    }
    pageText += blockText
  }
}

Objective-C

for (NSDictionary *page in annotation[@"pages"]) {
  NSMutableString *pageText = [NSMutableString new];
  for (NSDictionary *block in page[@"blocks"]) {
    NSMutableString *blockText = [NSMutableString new];
    for (NSDictionary *paragraph in block[@"paragraphs"]) {
      NSMutableString *paragraphText = [NSMutableString new];
      for (NSDictionary *word in paragraph[@"words"]) {
        NSMutableString *wordText = [NSMutableString new];
        for (NSDictionary *symbol in word[@"symbols"]) {
          NSString *text = symbol[@"text"];
          [wordText appendString:text];
          NSLog(@"Symbol text: %@ (confidence: %@\n", text, symbol[@"confidence"]);
        }
        NSLog(@"Word text: %@ (confidence: %@\n\n", wordText, word[@"confidence"]);
        NSLog(@"Word bounding box: %@\n", word[@"boundingBox"]);
        [paragraphText appendString:wordText];
      }
      NSLog(@"\nParagraph: \n%@\n", paragraphText);
      NSLog(@"Paragraph bounding box: %@\n", paragraph[@"boundingBox"]);
      NSLog(@"Paragraph Confidence: %@\n", paragraph[@"confidence"]);
      [blockText appendString:paragraphText];
    }
    [pageText appendString:blockText];
  }
}