Join us in person and online for Firebase Summit on October 18, 2022. Learn how Firebase can help you accelerate app development, release your app with confidence, and scale with ease. Register now

AppleプラットフォームでFirebaseAuthとFunctionsを使用して、CloudVisionで画像内のテキストを安全に認識します

コレクションでコンテンツを整理 必要に応じて、コンテンツの保存と分類を行います。

アプリから Google Cloud API を呼び出すには、承認を処理し、API キーなどの秘密の値を保護する中間 REST API を作成する必要があります。次に、モバイル アプリでコードを記述して、この中間サービスを認証し、通信する必要があります。

この REST API を作成する 1 つの方法は、Firebase Authentication and Functions を使用することです。これにより、Google Cloud API へのマネージド サーバーレス ゲートウェイが提供されます。このゲートウェイは、認証を処理し、ビルド済みの SDK を使用してモバイル アプリから呼び出すことができます。

このガイドでは、この手法を使用してアプリから Cloud Vision API を呼び出す方法を示します。この方法では、すべての認証済みユーザーが Cloud プロジェクトを介して Cloud Vision の有料サービスにアクセスできるようになるため、先に進む前に、この認証メカニズムがユースケースに十分かどうかを検討してください。

あなたが始める前に

プロジェクトを構成する

アプリに Firebase をまだ追加していない場合は、スタート ガイドの手順に従って追加してください。

Swift Package Manager を使用して、Firebase の依存関係をインストールおよび管理します。

  1. Xcode で、アプリ プロジェクトを開いた状態で、 File > Add Packagesに移動します。
  2. プロンプトが表示されたら、Firebase Apple プラットフォーム SDK リポジトリを追加します。
  3.   https://github.com/firebase/firebase-ios-sdk
  4. Firebase ML ライブラリを選択します。
  5. 完了すると、Xcode はバックグラウンドで依存関係の解決とダウンロードを自動的に開始します。

次に、アプリ内セットアップを実行します。

  1. アプリで、Firebase をインポートします。

    迅速

    import FirebaseMLModelDownloader

    Objective-C

    @import FirebaseMLModelDownloader;

さらにいくつかの構成手順を実行すると、準備が整います。

  1. プロジェクトでクラウドベースの API をまだ有効にしていない場合は、ここで有効にします。

    1. Firebase コンソールのFirebase ML API ページを開きます。
    2. プロジェクトを Blaze 料金プランにまだアップグレードしていない場合は、[アップグレード] をクリックしてアップグレードします。 (プロジェクトが Blaze プランに含まれていない場合にのみ、アップグレードするように求められます。)

      Blaze レベルのプロジェクトのみがクラウドベースの API を使用できます。

    3. クラウドベースの API がまだ有効になっていない場合は、[クラウドベースの API を有効にする] をクリックします。
  2. Cloud Vision API へのアクセスを禁止するように、既存の Firebase API キーを構成します。
    1. Cloud コンソールの資格情報ページを開きます。
    2. リスト内の API キーごとに編集ビューを開き、[キーの制限] セクションで、Cloud Vision API を除く使用可能なすべての API をリストに追加します。

呼び出し可能な関数をデプロイする

次に、アプリと Cloud Vision API をブリッジするために使用する Cloud Function をデプロイします。 functions-samplesリポジトリには、使用できる例が含まれています。

デフォルトでは、この関数を介して Cloud Vision API にアクセスすると、アプリの認証済みユーザーのみが Cloud Vision API にアクセスできます。さまざまな要件に合わせて関数を変更できます。

関数をデプロイするには:

  1. functions-samples リポジトリを複製またはダウンロードし、 vision-annotate-imageディレクトリに変更します:
    git clone https://github.com/firebase/functions-samples
    cd vision-annotate-image
    
  2. 依存関係のインストール:
    cd functions
    npm install
    cd ..
    
  3. Firebase CLI がない場合は、インストールします。
  4. vision-annotate-imageディレクトリで Firebase プロジェクトを初期化します。プロンプトが表示されたら、リストからプロジェクトを選択します。
    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.0f) else { return }
let base64encodedImage = imageData.base64EncodedString()

Objective-C

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

2. callable 関数を呼び出してテキストを認識する

画像内のランドマークを認識するには、 JSON Cloud Vision リクエストを渡して呼び出し可能な関数を呼び出します。

  1. まず、Cloud Functions のインスタンスを初期化します。

    迅速

    lazy var functions = Functions.functions()
    

    Objective-C

    @property(strong, nonatomic) FIRFunctions *functions;
    
  2. リクエストを作成します。 Cloud Vision API は、 TEXT_DETECTIONDOCUMENT_TEXT_DETECTIONの 2種類のテキスト検出をサポートしています。 2 つのユース ケースの違いについては、 Cloud Vision OCR ドキュメントを参照してください。

    迅速

    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. 最後に、関数を呼び出します。

    迅速

    functions.httpsCallable("annotateImage").call(requestData) { (result, error) in
      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]
        }
        // ...
      }
      // Function completed succesfully
    }
    

    Objective-C

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

3. 認識されたテキストのブロックからテキストを抽出する

テキスト認識操作が成功すると、タスクの結果でBatchAnnotateImagesResponseの JSON 応答が返されます。テキスト注釈はfullTextAnnotationオブジェクトにあります。

認識されたテキストをtextフィールドの文字列として取得できます。例えば:

迅速

guard let annotation = (result?.data as? [String: Any])?["fullTextAnnotation"] as? [String: Any] else { return }
print("%nComplete annotation:")
let text = annotation["text"] as? String ?? ""
print("%n\(text)")

Objective-C

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

画像の領域に固有の情報を取得することもできます。 blockparagraphword 、および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
}

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