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

為了從您的應用程式呼叫 Google Cloud API,您需要建立一個中間 REST API 來處理授權並保護 API 金鑰等秘密值。然後,您需要在行動應用程式中編寫程式碼以對此中間服務進行身份驗證並與其進行通訊。

建立此REST API 的一種方法是使用Firebase 驗證和功能,它為您提供了一個通往Google Cloud API 的託管無伺服器網關,該網關處理身份驗證,並可以使用預先建置的SDK 從行動應用程式進行調用。

本指南示範如何使用此技術從您的應用程式呼叫 Cloud Vision API。此方法將允許所有經過驗證的使用者透過您的雲端專案存取 Cloud Vision 計費服務,因此在繼續之前請考慮此身份驗證機制是否足以滿足您的用例。

在你開始之前

配置您的項目

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

使用 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. -ObjC標誌新增至目標建置設定的「其他連結器標誌」部分。
  6. 完成後,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. 配置現有 Firebase API 金鑰以禁止存取 Cloud Vision API:
    1. 開啟雲端控制台的憑證頁面。
    2. 對於清單中的每個 API 金鑰,開啟編輯視圖,然後在金鑰限制部分中,將除 Cloud Vision API之外的所有可用 API 新增至清單。

部署可呼叫函數

接下來,部署將用於橋接應用程式和 Cloud Vision API 的 Cloud Function。 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. 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.0) 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. 建立一個請求,並將Type設為LANDMARK_DETECTION

    迅速

    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. 最後,呼叫該函數:

    迅速

    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(如果有)以及匹配的置信度分數。例如:

迅速

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