Безопасное распознавание текста на изображениях с помощью Cloud Vision с использованием аутентификации Firebase и функций на платформах Apple

Чтобы вызвать API Google Cloud из вашего приложения, вам необходимо создать промежуточный REST API, который обрабатывает авторизацию и защищает секретные значения, такие как ключи API. Затем вам нужно написать код в своем мобильном приложении для аутентификации и связи с этой промежуточной службой.

Один из способов создания этого REST API — использование Firebase Authentication and Functions, который предоставляет вам управляемый бессерверный шлюз к Google Cloud API, который обрабатывает аутентификацию и может быть вызван из вашего мобильного приложения с помощью предварительно созданных SDK.

В этом руководстве показано, как использовать этот метод для вызова API Cloud Vision из вашего приложения. Этот метод позволит всем прошедшим проверку подлинности пользователям получить доступ к платным услугам Cloud Vision через ваш облачный проект, поэтому, прежде чем продолжить, подумайте, достаточен ли этот механизм аутентификации для вашего варианта использования.

Прежде чем вы начнете

Настройте свой проект

Если вы еще не добавили Firebase в свое приложение, сделайте это, выполнив действия, описанные в руководстве по началу работы .

Используйте Swift Package Manager для установки зависимостей Firebase и управления ими.

  1. В Xcode, открыв проект приложения, выберите «Файл» > «Добавить пакеты» .
  2. При появлении запроса добавьте репозиторий Firebase SDK для платформ Apple:
  3.   https://github.com/firebase/firebase-ios-sdk.git
  4. Выберите библиотеку Firebase ML.
  5. Добавьте флаг -ObjC в раздел «Другие флаги компоновщика» настроек сборки вашей цели.
  6. По завершении Xcode автоматически начнет разрешать и загружать ваши зависимости в фоновом режиме.

Затем выполните некоторые настройки в приложении:

  1. Импортируйте Firebase в свое приложение:

    Быстрый

    import FirebaseMLModelDownloader

    Цель-C

    @import FirebaseMLModelDownloader;

Еще несколько шагов настройки, и мы готовы к работе:

  1. Если вы еще не включили облачные API для своего проекта, сделайте это сейчас:

    1. Откройте страницу API Firebase ML в консоли Firebase.
    2. Если вы еще не обновили свой проект до тарифного плана Blaze, нажмите «Обновить» , чтобы сделать это. (Вам будет предложено выполнить обновление, только если ваш проект не входит в план Blaze.)

      Только проекты уровня Blaze могут использовать облачные API.

    3. Если облачные API еще не включены, нажмите «Включить облачные API» .
  2. Настройте существующие ключи API Firebase, чтобы запретить доступ к Cloud Vision API:
    1. Откройте страницу «Учетные данные» облачной консоли.
    2. Для каждого ключа API в списке откройте представление редактирования и в разделе «Ограничения ключей» добавьте в список все доступные API, кроме Cloud Vision API.

Развертывание вызываемой функции

Затем разверните облачную функцию, которую вы будете использовать для соединения вашего приложения и Cloud Vision API. Репозиторий functions-samples содержит пример, который вы можете использовать.

По умолчанию доступ к Cloud Vision API через эту функцию позволит только прошедшим проверку подлинности пользователям вашего приложения получить доступ к Cloud Vision API. Вы можете изменить функцию для различных требований.

Чтобы развернуть функцию:

  1. Клонируйте или загрузите репозиторий функций-образцов и перейдите в каталог Node-1st-gen/vision-annotate-image :
    git clone https://github.com/firebase/functions-samples
    cd Node-1st-gen/vision-annotate-image
    
  2. Установите зависимости:
    cd functions
    npm install
    cd ..
    
  3. Если у вас нет Firebase CLI, установите его .
  4. Инициализируйте проект Firebase в каталоге vision-annotate-image . При появлении запроса выберите свой проект в списке.
    firebase init
  5. Разверните функцию:
    firebase deploy --only functions:annotateImage

Добавьте Firebase Auth в свое приложение

Вызываемая функция, развернутая выше, отклонит любой запрос от неаутентифицированных пользователей вашего приложения. Если вы еще этого не сделали, вам нужно будет добавить Firebase Auth в свое приложение.

Добавьте необходимые зависимости в ваше приложение

Используйте Swift Package Manager для установки библиотеки Cloud Functions for Firebase.

Теперь вы готовы начать распознавать текст на изображениях.

1. Подготовьте входное изображение

Чтобы вызвать Cloud Vision, изображение должно быть отформатировано как строка в кодировке Base64. Чтобы обработать UIImage :

Быстрый

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

Цель-C

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

2. Вызов вызываемой функции для распознавания текста.

Чтобы распознать ориентиры на изображении, вызовите вызываемую функцию, передав запрос JSON Cloud Vision .

  1. Сначала инициализируйте экземпляр Cloud Functions:

    Быстрый

    lazy var functions = Functions.functions()
    

    Цель-C

    @property(strong, nonatomic) FIRFunctions *functions;
    
  2. Создайте запрос. API Cloud Vision поддерживает два типа обнаружения текста: TEXT_DETECTION и DOCUMENT_TEXT_DETECTION . См. документацию Cloud Vision OCR , чтобы узнать о разнице между двумя вариантами использования.

    Быстрый

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

    Цель-C

    NSDictionary *requestData = @{
      @"image": @{@"content": base64encodedImage},
      @"features": @{@"type": @"TEXT_DETECTION"},
      @"imageContext": @{@"languageHints": @[@"en"]}
    };
    
  3. Наконец, вызовите функцию:

    Быстрый

    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]
        }
        // ...
      }
    }
    

    Цель-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. Извлечение текста из блоков распознанного текста.

Если операция распознавания текста завершится успешно, в результате задачи будет возвращен ответ BatchAnnotateImagesResponse в формате JSON. Текстовые аннотации можно найти в объекте fullTextAnnotation .

Вы можете получить распознанный текст в виде строки в text поле. Например:

Быстрый

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

Цель-C

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

Вы также можете получить информацию, относящуюся к областям изображения. Для каждого block , paragraph , word и symbol вы можете получить текст, распознанный в регионе, и ограничивающие координаты региона. Например:

Быстрый

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

Цель-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];
  }
}