Google 致力于为黑人社区推动种族平等。查看具体举措

在 iOS 上使用 Firebase Auth 和 Firebase Functions 安全地识别地标

如需从应用中调用 Google Cloud API,您需要创建一个中间 REST API 来处理授权并保护 API 密钥等密钥值。然后,您需要在移动应用中编写代码,用于向此中间服务进行身份验证并与其通信。

创建此 REST API 的其中一个方法是使用 Firebase Authentication 和 Firebase 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 API 页面
    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

部署 Callable 函数

接下来,部署将用于衔接您的应用与 Cloud Vision API 的 Cloud Functions 函数。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 添加到您的应用

上面部署的 Callable 函数将拒绝未经身份验证的应用用户的任何请求。如果您尚未 将 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. 调用 Callable 函数来识别地标

要识别图片中的地标,请调用传递 JSON Cloud Vision 请求的 Callable 函数。

  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

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