使用 Apple 平台上的 Firebase 驗證和函式,透過 Cloud Vision 安全地識別地標

如要從應用程式呼叫 Google Cloud API,您需要建立中介 REST API,負責處理授權並保護 API 金鑰等密鑰值。接著,您需要在行動應用程式中編寫程式碼,以驗證及與這個中介服務通訊。

如要建立這項 REST API,其中一種方法是使用 Firebase Authentication 和 Functions,這會提供 Google Cloud API 的受管理無伺服器閘道,可處理驗證作業,並透過預先建構的 SDK 從行動應用程式呼叫。

本指南說明如何使用這項技術,從應用程式呼叫 Cloud Vision API。所有通過驗證的使用者都能透過您的 Cloud 專案存取 Cloud Vision 付費服務,因此請先評估這種驗證機制是否符合您的使用情況,再繼續操作。

事前準備

設定專案

如果尚未將 Firebase 新增至應用程式,請按照入門指南中的步驟操作。

使用 Swift Package Manager 安裝及管理 Firebase 依附元件。

  1. 在 Xcode 中保持開啟應用程式專案,然後依序點選「File」(檔案) 和「Add Packages」(新增 Package)
  2. 系統提示時,請新增 Firebase Apple 平台 SDK 存放區:
  3.   https://github.com/firebase/firebase-ios-sdk.git
  4. 選擇 Firebase ML 程式庫。
  5. -ObjC 標記新增至目標建構設定的「Other Linker Flags」部分。
  6. 完成後,Xcode 會自動開始在背景中解析並下載依附元件。

接著,請在應用程式中進行一些設定:

  1. 在應用程式中匯入 Firebase:

    Swift

    import FirebaseMLModelDownloader

    Objective-C

    @import FirebaseMLModelDownloader;

再完成幾個設定步驟,即可開始使用:

  1. 如果尚未為專案啟用雲端 API,請立即啟用:

    1. Firebase 控制台中開啟「APIs」(API) Firebase ML 頁面
    2. 如果尚未將專案升級至即付即用 Blaze 定價方案,請按一下「升級」。只有在專案未採用 Blaze 定價方案時,系統才會提示您升級。

      只有採用 Blaze 定價方案的專案才能使用雲端 API。

    3. 如果尚未啟用雲端 API,請按一下「啟用雲端 API」
  2. 設定現有的 Firebase API 金鑰,禁止存取 Cloud Vision API:
    1. 開啟 Cloud 控制台的「憑證」頁面。
    2. 針對清單中的每個 API 金鑰,開啟編輯檢視畫面,然後在「金鑰限制」部分中,將所有可用的 API (Cloud Vision API 除外) 新增至清單。

部署可呼叫函式

接著,部署用於連結應用程式和 Cloud Vision API 的 Cloud Function。functions-samples 存放區包含可供使用的範例。

根據預設,透過這個函式存取 Cloud Vision API 時,只有通過驗證的應用程式使用者才能存取 Cloud Vision API。您可以根據不同需求修改函式。

如何部署函式:

  1. 複製或下載 functions-samples 存放區,然後變更為 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. vision-annotate-image 目錄中初始化 Firebase 專案。系統顯示提示訊息時,請在清單中選取您的專案。
    firebase init
  5. 部署函式:
    firebase deploy --only functions:annotateImage

將 Firebase Authentication 新增至應用程式

上述部署的可呼叫函式會拒絕來自應用程式未經驗證使用者的任何要求。如尚未完成,您需要在應用程式中新增 Firebase Authentication

在應用程式中新增必要依附元件

使用 Swift Package Manager 安裝 Cloud Functions for Firebase 程式庫。

1. 準備輸入圖片

如要呼叫 Cloud Vision,圖片必須採用 Base64 編碼字串格式。如要處理 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. 叫用可呼叫函式來辨識地標

如要辨識圖片中的地標,請呼叫可呼叫的函式,並傳遞 JSON Cloud Vision 要求

  1. 首先,請初始化 Cloud Functions 的執行個體:

    Swift

    lazy var functions = Functions.functions()
    

    Objective-C

    @property(strong, nonatomic) FIRFunctions *functions;
    
  2. 建立要求,並將「Type」(類型) 設為 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

    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. 取得辨識出的地標相關資訊

如果地標辨識作業成功,工作結果會傳回 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"];
  }
}