التعرف على النص في الصور بشكل آمن باستخدام Cloud Vision باستخدام Firebase Auth والوظائف على منصات Apple

لاستدعاء Google Cloud API من تطبيقك، تحتاج إلى إنشاء واجهة REST API وسيطة تتعامل مع التفويض وتحمي القيم السرية مثل مفاتيح واجهة برمجة التطبيقات. ستحتاج بعد ذلك إلى كتابة التعليمات البرمجية في تطبيق الهاتف المحمول الخاص بك للمصادقة على هذه الخدمة الوسيطة والتواصل معها.

تتمثل إحدى طرق إنشاء REST API في استخدام مصادقة Firebase ووظائفها، والتي تمنحك بوابة مُدارة بدون خادم إلى Google Cloud APIs التي تتعامل مع المصادقة ويمكن استدعاؤها من تطبيق الهاتف المحمول الخاص بك باستخدام حزم SDK المُنشأة مسبقًا.

يوضح هذا الدليل كيفية استخدام هذه التقنية لاستدعاء Cloud Vision API من تطبيقك. ستسمح هذه الطريقة لجميع المستخدمين المعتمدين بالوصول إلى خدمات Cloud Vision التي تتم فوترتها من خلال مشروع Cloud الخاص بك، لذا فكر فيما إذا كانت آلية المصادقة هذه كافية لحالة الاستخدام الخاصة بك قبل المتابعة.

قبل ان تبدأ

تكوين المشروع الخاص بك

إذا لم تكن قد أضفت 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

    ج موضوعية

    @import FirebaseMLModelDownloader;

بعض خطوات التكوين الإضافية، ونحن على استعداد للبدء:

  1. إذا لم تكن قد قمت بالفعل بتمكين واجهات برمجة التطبيقات المستندة إلى السحابة لمشروعك، فقم بذلك الآن:

    1. افتح صفحة Firebase ML APIs لوحدة تحكم Firebase.
    2. إذا لم تكن قد قمت بالفعل بترقية مشروعك إلى خطة تسعير Blaze، فانقر فوق ترقية للقيام بذلك. (سيُطلب منك الترقية فقط إذا لم يكن مشروعك مدرجًا في خطة Blaze.)

      يمكن فقط للمشاريع على مستوى Blaze استخدام واجهات برمجة التطبيقات المستندة إلى السحابة.

    3. إذا لم تكن واجهات برمجة التطبيقات المستندة إلى السحابة ممكّنة بالفعل، فانقر على تمكين واجهات برمجة التطبيقات المستندة إلى السحابة .
  2. قم بتكوين مفاتيح Firebase API الموجودة لديك لمنع الوصول إلى Cloud Vision API:
    1. افتح صفحة بيانات الاعتماد الخاصة بوحدة التحكم السحابية.
    2. بالنسبة لكل مفتاح API في القائمة، افتح طريقة عرض التحرير، وفي قسم قيود المفاتيح، أضف جميع واجهات برمجة التطبيقات المتاحة باستثناء Cloud Vision API إلى القائمة.

نشر الدالة القابلة للاستدعاء

بعد ذلك، قم بنشر وظيفة السحابة التي ستستخدمها لربط تطبيقك وCloud Vision API. يحتوي مستودع 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. قم بتهيئة مشروع Firebase في دليل vision-annotate-image . عندما يُطلب منك ذلك، حدد مشروعك في القائمة.
    firebase init
  5. نشر الوظيفة:
    firebase deploy --only functions:annotateImage

أضف مصادقة Firebase إلى تطبيقك

سترفض الوظيفة القابلة للاستدعاء المنشورة أعلاه أي طلب من المستخدمين غير المصادق عليهم لتطبيقك. إذا لم تكن قد قمت بذلك بالفعل، فستحتاج إلى إضافة Firebase Auth إلى تطبيقك.

أضف التبعيات الضرورية إلى تطبيقك

استخدم Swift Package Manager لتثبيت Cloud Functions لمكتبة Firebase.

أنت الآن جاهز لبدء التعرف على النص في الصور.

1. قم بإعداد صورة الإدخال

من أجل الاتصال بـ Cloud Vision، يجب تنسيق الصورة كسلسلة بترميز base64. لمعالجة UIImage :

سويفت

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

ج موضوعية

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

2. قم باستدعاء الوظيفة القابلة للاستدعاء للتعرف على النص

للتعرف على المعالم في صورة ما، قم باستدعاء الوظيفة القابلة للاستدعاء لتمرير طلب JSON Cloud Vision .

  1. أولاً، قم بتهيئة مثيل Cloud Functions:

    سويفت

    lazy var functions = Functions.functions()
    

    ج موضوعية

    @property(strong, nonatomic) FIRFunctions *functions;
    
  2. قم بإنشاء الطلب. تدعم Cloud Vision API نوعين من اكتشاف النص: TEXT_DETECTION و DOCUMENT_TEXT_DETECTION . راجع مستندات Cloud Vision OCR لمعرفة الفرق بين حالتي الاستخدام.

    سويفت

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

    ج موضوعية

    NSDictionary *requestData = @{
      @"image": @{@"content": base64encodedImage},
      @"features": @{@"type": @"TEXT_DETECTION"},
      @"imageContext": @{@"languageHints": @[@"en"]}
    };
    
  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]
        }
        // ...
      }
    }
    

    ج موضوعية

    [[_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. استخرج النص من كتل النص التي تم التعرف عليها

إذا نجحت عملية التعرف على النص، فسيتم إرجاع استجابة JSON الخاصة بـ BatchAnnotateImagesResponse في نتيجة المهمة. يمكن العثور على التعليقات التوضيحية النصية في كائن fullTextAnnotation .

يمكنك الحصول على النص الذي تم التعرف عليه كسلسلة في حقل text . على سبيل المثال:

سويفت

let annotation = result.flatMap { $0.data as? [String: Any] }
    .flatMap { $0["fullTextAnnotation"] }
    .flatMap { $0 as? [String: Any] }
guard let annotation = annotation else { return }

if let text = annotation["text"] as? String {
  print("Complete annotation: \(text)")
}

ج موضوعية

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

يمكنك أيضًا الحصول على معلومات خاصة بمناطق الصورة. بالنسبة لكل block paragraph word symbol ، يمكنك التعرف على النص في المنطقة والإحداثيات المحيطة بالمنطقة. على سبيل المثال:

سويفت

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
  }
}

ج موضوعية

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