Catch up on everthing we announced at this year's Firebase Summit. Learn more

在 Apple 平台上使用 Firebase Auth 和 Functions 使用 Cloud Vision 安全识别图像中的文本

为了从您的应用程序调用 Google Cloud API,您需要创建一个中间 REST API 来处理授权并保护 API 密钥等秘密值。然后,您需要在您的移动应用程序中编写代码来验证此中间服务并与之通信。

创建此 REST API 的一种方法是使用 Firebase 身份验证和函数,它为您提供了一个托管的、无服务器的 Google Cloud API 网关,该网关处理身份验证并可从您的移动应用程序中使用预构建的 SDK 进行调用。

本指南演示了如何使用此技术从您的应用程序调用 Cloud Vision API。此方法将允许所有经过身份验证的用户通过您的 Cloud 项目访问 Cloud Vision 计费服务,因此在继续之前请考虑此身份验证机制是否足以满足您的用例。

在你开始之前

配置您的项目

如果您尚未添加火力地堡到您的应用程序,通过遵循的步骤做这样的入门指南

使用 Swift Package Manager 安装和管理 Firebase 依赖项。

  1. 在Xcode中,您的应用项目打开,导航到File>斯威夫特包>添加包的依赖
  2. 出现提示时,添加 Firebase Apple 平台 SDK 存储库:
  3.   https://github.com/firebase/firebase-ios-sdk
      
  4. 选择 Firebase ML 库。
  5. 完成后,Xcode 将在后台自动开始解析和下载您的依赖项。

接下来,执行一些应用程序内设置:

  1. 在您的应用中,导入 Firebase:

    迅速

    import Firebase

    目标-C

    @import Firebase;

再执行几个配置步骤,我们就可以开始了:

  1. 如果您尚未为您的项目启用基于云的 API,请立即启用:

    1. 打开火力地堡ML API页面的火力地堡控制台。
    2. 如果您尚未升级您的项目以大火定价计划,单击升级到这样做。 (仅当您的项目不在 Blaze 计划中时,系统才会提示您升级。)

      只有 Blaze 级别的项目可以使用基于云的 API。

    3. 如果基于云的API尚未启用,单击启用基于云的API。
  2. 配置您现有的 Firebase API 密钥以禁止访问 Cloud Vision API:
    1. 打开证书云控制台的页面。
    2. 对于列表中的每个API密钥,打开编辑视图,并在重点限制部分,加入所有可用的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. 如果你不具备火力地堡CLI,安装它
  4. 初始化在一个火力地堡项目vision-annotate-image目录。出现提示时,在列表中选择您的项目。
    firebase init
  5. 部署功能:
    firebase deploy --only functions:annotateImage

将 Firebase 身份验证添加到您的应用

上面部署的可调用函数将拒绝来自您的应用程序的未经身份验证的用户的任何请求。如果你还没有这样做的话,你将需要火力地堡验证添加到您的应用程序。

向您的应用程序添加必要的依赖项

使用 Swift Package Manager 安装 Cloud Functions for Firebase 库。

现在您已准备好开始识别图像中的文本。

1.准备输入图像

为了调用 Cloud Vision,图像必须格式化为 base64 编码的字符串。要处理UIImage

迅速

guard let imageData = uiImage.jpegData(compressionQuality: 1.0f) else { return }
let base64encodedImage = imageData.base64EncodedString()

目标-C

NSData *imageData = UIImageJPEGRepresentation(uiImage, 1.0f);
NSString *base64encodedImage =
  [imageData base64EncodedStringWithOptions:NSDataBase64Encoding76CharacterLineLength];

2.调用callable函数识别文本

识别图像中的地标,调用可调用函数传递JSON云视觉请求

  1. 首先,初始化 Cloud Functions 的一个实例:

    迅速

    lazy var functions = Functions.functions()
    

    目标-C

    @property(strong, nonatomic) FIRFunctions *functions;
    
  2. 创建请求。云愿景API支持两种类型的文字检测: TEXT_DETECTIONDOCUMENT_TEXT_DETECTION 。见云愿景OCR文档的两个用例之间的差异。

    迅速

    let requestData = [
      "image": ["content": base64encodedImage],
      "features": ["type": "TEXT_DETECTION"],
      "imageContext": ["languageHints": ["en"]]
    ]
    

    目标-C

    NSDictionary *requestData = @{
      @"image": @{@"content": base64encodedImage},
      @"features": @{@"type": @"TEXT_DETECTION"},
      @"imageContext": @{@"languageHints": @[@"en"]}
    };
    
  3. 最后,调用函数:

    迅速

    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
    }
    

    目标-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.从识别的文本块中提取文本

如果文本识别操作成功,一个JSON响应BatchAnnotateImagesResponse将在任务的结果返回。文本注释可以在找到fullTextAnnotation对象。

你可以得到识别的文本作为一个字符串text字段。例如:

迅速

guard let annotation = (result?.data as? [String: Any])?["fullTextAnnotation"] as? [String: Any] else { return }
print("%nComplete annotation:")
let text = annotation["text"] as? String ?? ""
print("%n\(text)")

目标-C

NSDictionary *annotation = result.data[@"fullTextAnnotation"];
if (!annotation) { return; }
NSLog(@"\nComplete annotation:");
NSLog(@"\n%@", annotation[@"text"]);

您还可以获得特定于图像区域的信息。对于每一个blockparagraphwordsymbol ,你可以在该地区公认的文字和该地区的边界坐标。例如:

迅速

guard let pages = annotation["pages"] as? [[String: Any]] else { return }
for page in pages {
var pageText = ""
guard let blocks = page["blocks"] as? [[String: Any]] else { continue }
for block in blocks {
    var blockText = ""
    guard let paragraphs = block["paragraphs"] as? [[String: Any]] else { continue }
    for paragraph in paragraphs {
    var paragraphText = ""
    guard let words = paragraph["words"] as? [[String: Any]] else { continue }
    for word in words {
        var wordText = ""
        guard let symbols = word["symbols"] as? [[String: Any]] else { continue }
        for symbol in symbols {
        let text = symbol["text"] as? String ?? ""
        let confidence = symbol["confidence"] as? Float ?? 0.0
        wordText += text
        print("Symbol text: \(text) (confidence: \(confidence)%n")
        }
        let confidence = word["confidence"] as? Float ?? 0.0
        print("Word text: \(wordText) (confidence: \(confidence)%n%n")
        let boundingBox = word["boundingBox"] as? [Float] ?? [0.0, 0.0, 0.0, 0.0]
        print("Word bounding box: \(boundingBox.description)%n")
        paragraphText += wordText
    }
    print("%nParagraph: %n\(paragraphText)%n")
    let boundingBox = paragraph["boundingBox"] as? [Float] ?? [0.0, 0.0, 0.0, 0.0]
    print("Paragraph bounding box: \(boundingBox)%n")
    let confidence = paragraph["confidence"] as? Float ?? 0.0
    print("Paragraph Confidence: \(confidence)%n")
    blockText += paragraphText
    }
    pageText += blockText
}

目标-C

for (NSDictionary *page in annotation[@"pages"]) {
  NSMutableString *pageText = [NSMutableString new];
  for (NSDictionary *block in page[@"blocks"]) {
    NSMutableString *blockText = [NSMutableString new];
    for (NSDictionary *paragraph in block[@"paragraphs"]) {
      NSMutableString *paragraphText = [NSMutableString new];
      for (NSDictionary *word in paragraph[@"words"]) {
        NSMutableString *wordText = [NSMutableString new];
        for (NSDictionary *symbol in word[@"symbols"]) {
          NSString *text = symbol[@"text"];
          [wordText appendString:text];
          NSLog(@"Symbol text: %@ (confidence: %@\n", text, symbol[@"confidence"]);
        }
        NSLog(@"Word text: %@ (confidence: %@\n\n", wordText, word[@"confidence"]);
        NSLog(@"Word bounding box: %@\n", word[@"boundingBox"]);
        [paragraphText appendString:wordText];
      }
      NSLog(@"\nParagraph: \n%@\n", paragraphText);
      NSLog(@"Paragraph bounding box: %@\n", paragraph[@"boundingBox"]);
      NSLog(@"Paragraph Confidence: %@\n", paragraph[@"confidence"]);
      [blockText appendString:paragraphText];
    }
    [pageText appendString:blockText];
  }
}