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

如要從應用程式呼叫 Google Cloud API,您必須建立用於處理授權並保護密鑰值 (例如 API 金鑰) 的中繼 REST API。接著,您必須在行動應用程式中編寫程式碼,以便驗證這個中繼服務並與其通訊。

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

本指南示範如何使用這項技巧,從應用程式呼叫 Cloud Vision API。這個方法會允許所有通過驗證的使用者透過您的 Cloud 專案存取 Cloud Vision 付費服務,因此請先評估這項驗證機制是否足以滿足您的用途,再繼續操作。

事前準備

設定專案

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

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

接下來,進行一些應用程式內設定:

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

    Swift

    import FirebaseMLModelDownloader

    Objective-C

    @import FirebaseMLModelDownloader;

還差幾個步驟就可以開始使用了:

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

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

      只有 Blaze 層級的專案可以使用以雲端為基礎的 API。

    3. 如果雲端型 API 尚未啟用,請點選「啟用雲端式 API」
  2. 設定現有的 Firebase API 金鑰以禁止存取 Cloud Vision API:
    1. 開啟 Cloud 控制台的「Credentials」(憑證) 頁面。
    2. 對於清單中的每個 API 金鑰,請開啟編輯檢視畫面,然後在「金鑰限制」專區中,將 Cloud Vision API「以外」的所有可用 API 加入清單。

部署可呼叫函式

接下來,請部署要用來橋接應用程式和 Cloud Vision API 的 Cloud 函式。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 驗證新增至應用程式

上方部署的可呼叫函式會拒絕應用程式中未經驗證使用者的任何要求。如果您尚未這麼做,您必須將 Firebase 驗證新增至應用程式

為應用程式新增必要的依附元件

使用 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"];
  }
}