Join us for Firebase Summit on November 10, 2021. Tune in to learn how Firebase can help you accelerate app development, release with confidence, and scale with ease. Register

Firebase Auth と Functions を使用して Cloud Vision でランドマークを安全に認識する(iOS)

アプリから Google Cloud API を呼び出すには、認可を処理し、API キーなどのシークレット値を保護するための中間 REST API を作成する必要があります。次に、モバイルアプリでこの中間サービスに対する認証と通信を行うためのコードを記述します。

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

このガイドでは、この手法を使用してアプリから Cloud Vision API を呼び出す方法について説明します。この手法では、すべての認証済みユーザーが Cloud のプロジェクト経由で Cloud Vision の課金サービスにアクセスできます。そのため、続行する前に、目的のユースケースにおいてこの認証メカニズムで十分かどうかを考慮してください。

始める前に

プロジェクトを構成する

  1. まだアプリに Firebase を追加していない場合は、スタートガイドの手順に沿って追加してください。
  2. Podfile に Firebase を含めます。プロジェクトの Pod をインストールまたは更新した後に、.xcworkspace を使用して Xcode プロジェクトを開くようにしてください。
  3. アプリに Firebase をインポートします。

    Swift

    import Firebase

    Objective-C

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

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

      Blaze レベルのプロジェクトだけが Cloud ベースの API を使用できます。

    3. Cloud ベースの API がまだ有効になっていない場合は、[Cloud ベースの API を有効化] をクリックします。
  5. 既存の Firebase API キーを構成して、Cloud Vision API へのアクセスを許可しないようにします。
    1. Cloud Console の [認証情報] ページを開きます。
    2. リスト内の各 API キーについて、編集ビューを開き、[キーの制限] セクションで Cloud Vision API を除く使用可能なすべての API をリストに追加します。

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

次に、アプリと Cloud Vision API の間のブリッジとして使用する Cloud Functions の関数をデプロイします。functions-samples リポジトリには、使用可能なサンプルが含まれています。

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

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

  1. 関数サンプル リポジトリのクローンを作成するか、ダウンロードして、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 を追加していない場合は、Firebase Auth を追加する必要があります。

必要な依存関係をアプリに追加する

  1. Firebase Functions ライブラリの依存関係を Podfile に追加します。
    pod 'Firebase/Functions'
  2. 依存関係をインストールします。
    pod install

1. 入力画像を準備する

Cloud Vision を呼び出すには、画像を base64 でエンコードされた文字列としてフォーマットする必要があります。以下の方法で UIImage を処理します。

Swift

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. 呼び出し可能関数を呼び出し、ランドマークを認識する

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

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

    Swift

    lazy var functions = Functions.functions()
    

    Objective-C

    @property(strong, nonatomic) FIRFunctions *functions;
    
  2. リクエストを作成し、タイプとして LANDMARK_DETECTION を設定します。

    Swift

    let requestData = [
      "image": ["content": base64encodedImage],
      "features": ["maxResults": 5, "type": "LANDMARK_DETECTION"]
    ]
    

    Objective-C

    NSDictionary *requestData = @{
      @"image": @{@"content": base64encodedImage},
      @"features": @{@"maxResults": @5, @"type": @"LANDMARK_DETECTION"}
    };
    
  3. 関数を呼び出します。

    Swift

    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 レスポンスが返されます。landmarkAnnotations 配列内の各オブジェクトは、画像内で認識されたランドマークを表します。ランドマークごとに、入力画像の境界座標、ランドマーク名、緯度と経度、ナレッジグラフ エンティティ ID(使用できる場合)、一致の信頼スコアを取得できます。次に例を示します。

Swift

if let labelArray = (result?.data as? [String: Any])?["landmarkAnnotations"] as? [[String:Any]] {
  for labelObj in labelArray {
    let landmarkName = labelObj["description"]
    let entityId = labelObj["mid"]
    let score = labelObj["score"]
    let bounds = labelObj["boundingPoly"]
    // Multiple locations are possible, e.g., the location of the depicted
    // landmark and the location the picture was taken.
    guard let locations = labelObj["locations"] as? [[String: [String: Any]]] else { continue }
    for location in locations {
      let latitude = location["latLng"]?["latitude"]
      let longitude = location["latLng"]?["longitude"]
    }
  }
}

Objective-C

NSArray *labelArray = result.data[@"landmarkAnnotations"];
for (NSDictionary *labelObj in labelArray) {
  NSString *landmarkName = labelObj[@"description"];
  NSString *entityId = labelObj[@"mid"];
  NSNumber *score = labelObj[@"score"];
  NSArray *bounds = labelObj[@"boundingPoly"];
  // Multiple locations are possible, e.g., the location of the depicted
  // landmark and the location the picture was taken.
  NSArray *locations = labelObj[@"locations"];
  for (NSDictionary *location in locations) {
    NSNumber *latitude = location[@"latLng"][@"latitude"];
    NSNumber *longitude = location[@"latLng"][@"longitude"];
  }
}