Firebase is back at Google I/O on May 10! Register now

在 Apple 平台上使用 Firebase Auth 和 Functions 使用 Cloud Vision 安全地識別圖像中的文本

透過集合功能整理內容 你可以依據偏好儲存及分類內容。

為了從您的應用調用 Google Cloud API,您需要創建一個中間 REST API 來處理授權並保護秘密值(例如 API 密鑰)。然後,您需要在您的移動應用程序中編寫代碼以驗證此中間服務並與之通信。

創建此 REST API 的一種方法是使用 Firebase Authentication and Functions,它為您提供了一個託管的無服務器網關,用於處理身份驗證的 Google Cloud API,並且可以使用預構建的 SDK 從您的移動應用程序調用。

本指南演示瞭如何使用此技術從您的應用程序調用 Cloud Vision API。此方法將允許所有經過身份驗證的用戶通過您的 Cloud 項目訪問 Cloud Vision 計費服務,因此在繼續之前考慮此身份驗證機制是否足以滿足您的用例。

在你開始之前

配置您的項目

如果您尚未將 Firebase 添加到您的應用程序,請按照入門指南中的步驟進行操作。

使用 Swift Package Manager 安裝和管理 Firebase 依賴項。

  1. 在 Xcode 中,打開您的應用程序項目,導航至File > Add Packages
  2. 出現提示時,添加 Firebase Apple 平台 SDK 存儲庫:
  3.   https://github.com/firebase/firebase-ios-sdk
  4. 選擇 Firebase ML 庫。
  5. 完成後,Xcode 將自動開始在後台解析和下載您的依賴項。

接下來,執行一些應用內設置:

  1. 在您的應用中,導入 Firebase:

    迅速

    import FirebaseMLModelDownloader

    目標-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. 克隆或下載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 添加到您的應用

上面部署的可調用函數將拒絕來自您應用的未經身份驗證的用戶的任何請求。如果您還沒有這樣做,則需要將 Firebase Auth 添加到您的應用中。

向您的應用程序添加必要的依賴項

使用 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 Cloud Vision 請求的可調用函數。

  1. 首先,初始化 Cloud Functions 的一個實例:

    迅速

    lazy var functions = Functions.functions()
    

    目標-C

    @property(strong, nonatomic) FIRFunctions *functions;
    
  2. 創建請求。 Cloud Vision API 支持兩種類型的文本檢測: TEXT_DETECTIONDOCUMENT_TEXT_DETECTION 。請參閱Cloud Vision 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. 從識別文本塊中提取文本

如果文本識別操作成功,將在任務結果中返回BatchAnnotateImagesResponse的 JSON 響應。文本註釋可以在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];
  }
}