ติดป้ายกำกับรูปภาพด้วยโมเดลที่ฝึกด้วย AutoML บนแพลตฟอร์มของ Apple

หลังจากฝึกโมเดลของคุณเองโดยใช้ AutoML Vision Edge แล้ว คุณจะใช้โมเดลดังกล่าวในแอปเพื่อติดป้ายกำกับรูปภาพได้

การผสานรวมโมเดลที่ฝึกจาก AutoML Vision Edge ทำได้ 2 วิธี คุณสามารถรวมโมเดลโดยคัดลอกไฟล์ของโมเดลไปยังโปรเจ็กต์ Xcode หรือจะดาวน์โหลดแบบไดนามิกจาก Firebase ก็ได้

ตัวเลือกการรวมโมเดล
รวมอยู่ในแอป
  • โมเดลเป็นส่วนหนึ่งของแพ็กเกจ
  • โมเดลจะพร้อมใช้งานทันที แม้ว่าอุปกรณ์ Apple จะออฟไลน์อยู่ก็ตาม
  • ไม่ต้องมีโปรเจ็กต์ Firebase
โฮสต์ด้วย Firebase
  • โฮสต์โมเดลโดยการอัปโหลดไปยัง Firebase Machine Learning
  • ลดขนาด App Bundle
  • ระบบจะดาวน์โหลดโมเดลตามคําขอ
  • พุชการอัปเดตโมเดลโดยไม่ต้องเผยแพร่แอปอีกครั้ง
  • การทดสอบ A/B ที่ง่ายดายด้วย Firebase Remote Config
  • ต้องมีโปรเจ็กต์ Firebase

ก่อนเริ่มต้น

  1. รวมไลบรารี ML Kit ไว้ใน Podfile ดังนี้

    สําหรับการรวมโมเดลกับแอป ให้ทําดังนี้

    pod 'GoogleMLKit/ImageLabelingCustom'
    

    หากต้องการดาวน์โหลดโมเดลจาก Firebase แบบไดนามิก ให้เพิ่มLinkFirebase ทรัพยากร Dependency ต่อไปนี้

    pod 'GoogleMLKit/ImageLabelingCustom'
    pod 'GoogleMLKit/LinkFirebase'
    
  2. หลังจากติดตั้งหรืออัปเดต Pods ของโปรเจ็กต์แล้ว ให้เปิดโปรเจ็กต์ Xcode โดยใช้ .xcworkspace ML Kit ใช้งานได้ใน Xcode เวอร์ชัน 12.2 ขึ้นไป

  3. หากต้องการดาวน์โหลดโมเดล โปรดตรวจสอบว่าคุณได้เพิ่ม Firebase ลงในโปรเจ็กต์ Android แล้ว หากยังไม่ได้ดำเนินการดังกล่าว ซึ่งไม่จําเป็นเมื่อคุณรวมโมเดล

1. โหลดโมเดล

กำหนดค่าแหล่งข้อมูลโมเดลในเครื่อง

วิธีรวมโมเดลกับแอป

  1. แตกโมเดลและข้อมูลเมตาของโมเดลออกจากไฟล์ ZIP ที่คุณดาวน์โหลดจากคอนโซล Firebase ลงในโฟลเดอร์

    your_model_directory
      |____dict.txt
      |____manifest.json
      |____model.tflite
    

    ไฟล์ทั้ง 3 ไฟล์ต้องอยู่ในโฟลเดอร์เดียวกัน เราขอแนะนำให้คุณใช้ไฟล์ตามที่ดาวน์โหลดมาโดยไม่มีการแก้ไข (รวมถึงชื่อไฟล์)

  2. คัดลอกโฟลเดอร์ไปยังโปรเจ็กต์ Xcode โดยอย่าลืมเลือกสร้างการอ้างอิงโฟลเดอร์เมื่อดำเนินการดังกล่าว ไฟล์โมเดลและข้อมูลเมตาจะรวมอยู่ใน App Bundle และพร้อมใช้งานสำหรับ ML Kit

  3. สร้างออบเจ็กต์ LocalModel โดยระบุเส้นทางไปยังไฟล์ Manifest ของโมเดล

    Swift

    guard let manifestPath = Bundle.main.path(
        forResource: "manifest",
        ofType: "json",
        inDirectory: "your_model_directory"
    ) else { return true }
    let localModel = LocalModel(manifestPath: manifestPath)
    

    Objective-C

    NSString *manifestPath =
        [NSBundle.mainBundle pathForResource:@"manifest"
                                      ofType:@"json"
                                 inDirectory:@"your_model_directory"];
    MLKLocalModel *localModel =
        [[MLKLocalModel alloc] initWithManifestPath:manifestPath];
    

กำหนดค่าแหล่งข้อมูลรูปแบบที่โฮสต์ใน Firebase

หากต้องการใช้รูปแบบที่โฮสต์จากระยะไกล ให้สร้างออบเจ็กต์ CustomRemoteModel โดยระบุชื่อที่คุณกำหนดให้กับรูปแบบเมื่อเผยแพร่

Swift

// Initialize the model source with the name you assigned in
// the Firebase console.
let remoteModelSource = FirebaseModelSource(name: "your_remote_model")
let remoteModel = CustomRemoteModel(remoteModelSource: remoteModelSource)

Objective-C

// Initialize the model source with the name you assigned in
// the Firebase console.
MLKFirebaseModelSource *firebaseModelSource =
    [[MLKFirebaseModelSource alloc] initWithName:@"your_remote_model"];
MLKCustomRemoteModel *remoteModel =
    [[MLKCustomRemoteModel alloc] initWithRemoteModelSource:firebaseModelSource];

จากนั้นเริ่มงานการดาวน์โหลดโมเดลโดยระบุเงื่อนไขที่คุณต้องการอนุญาตให้ดาวน์โหลด หากโมเดลไม่อยู่ในอุปกรณ์ หรือหากมีโมเดลเวอร์ชันใหม่กว่า แท็บจะดาวน์โหลดโมเดลจาก Firebase แบบไม่พร้อมกัน โดยทำดังนี้

Swift

let downloadConditions = ModelDownloadConditions(
  allowsCellularAccess: true,
  allowsBackgroundDownloading: true
)

let downloadProgress = ModelManager.modelManager().download(
  remoteModel,
  conditions: downloadConditions
)

Objective-C

MLKModelDownloadConditions *downloadConditions =
    [[MLKModelDownloadConditions alloc] initWithAllowsCellularAccess:YES
                                         allowsBackgroundDownloading:YES];

NSProgress *downloadProgress =
    [[MLKModelManager modelManager] downloadRemoteModel:remoteModel
                                             conditions:downloadConditions];

แอปจํานวนมากจะเริ่มการดาวน์โหลดในโค้ดเริ่มต้น แต่คุณก็เริ่มได้ทุกเมื่อก่อนที่จะต้องใช้โมเดล

สร้างเครื่องมือติดป้ายกำกับรูปภาพจากโมเดล

หลังจากกําหนดค่าแหล่งที่มาของโมเดลแล้ว ให้สร้างออบเจ็กต์ ImageLabeler จากแหล่งที่มาแหล่งใดแหล่งหนึ่ง

หากมีเพียงโมเดลที่รวมอยู่ในเครื่อง ให้สร้างเครื่องติดป้ายกำกับจากออบเจ็กต์ LocalModel แล้วกําหนดค่าเกณฑ์คะแนนความเชื่อมั่นที่ต้องการ (ดูประเมินโมเดล)

Swift

let options = CustomImageLabelerOptions(localModel: localModel)
options.confidenceThreshold = NSNumber(value: 0.0)  // Evaluate your model in the Cloud console
                                                    // to determine an appropriate value.
let imageLabeler = ImageLabeler.imageLabeler(options)

Objective-C

CustomImageLabelerOptions *options =
    [[CustomImageLabelerOptions alloc] initWithLocalModel:localModel];
options.confidenceThreshold = @(0.0f);  // Evaluate your model in the Cloud console
                                        // to determine an appropriate value.
MLKImageLabeler *imageLabeler =
    [MLKImageLabeler imageLabelerWithOptions:options];

หากมีโมเดลที่โฮสต์จากระยะไกล คุณจะต้องตรวจสอบว่าได้ดาวน์โหลดโมเดลแล้วก่อนที่จะเรียกใช้ คุณตรวจสอบสถานะของงานดาวน์โหลดรูปแบบได้โดยใช้isModelDownloaded(remoteModel:)วิธีการของตัวจัดการรูปแบบ

แม้ว่าคุณจะต้องยืนยันข้อมูลนี้ก่อนเรียกใช้โปรแกรมติดป้ายกำกับ แต่หากมีทั้งโมเดลที่โฮสต์จากระยะไกลและโมเดลที่รวมอยู่ในเครื่อง ก็อาจต้องทำการตรวจสอบนี้เมื่อสร้างอินสแตนซ์ ImageLabeler: สร้างโปรแกรมติดป้ายกำกับจากโมเดลระยะไกลหากมีการดาวน์โหลดไว้ และจากโมเดลในเครื่องหากไม่ได้ดาวน์โหลด

Swift

var options: CustomImageLabelerOptions
if (ModelManager.modelManager().isModelDownloaded(remoteModel)) {
  options = CustomImageLabelerOptions(remoteModel: remoteModel)
} else {
  options = CustomImageLabelerOptions(localModel: localModel)
}
options.confidenceThreshold = NSNumber(value: 0.0)  // Evaluate your model in the Firebase console
                                                    // to determine an appropriate value.
let imageLabeler = ImageLabeler.imageLabeler(options: options)

Objective-C

MLKCustomImageLabelerOptions *options;
if ([[MLKModelManager modelManager] isModelDownloaded:remoteModel]) {
  options = [[MLKCustomImageLabelerOptions alloc] initWithRemoteModel:remoteModel];
} else {
  options = [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:localModel];
}
options.confidenceThreshold = @(0.0f);  // Evaluate your model in the Firebase console
                                        // to determine an appropriate value.
MLKImageLabeler *imageLabeler =
    [MLKImageLabeler imageLabelerWithOptions:options];

หากมีเพียงโมเดลที่โฮสต์ระยะไกล คุณควรปิดใช้ฟังก์ชันการทำงานที่เกี่ยวข้องกับโมเดล เช่น ปิดใช้หรือซ่อน UI บางส่วน จนกว่าคุณจะยืนยันว่าดาวน์โหลดโมเดลแล้ว

คุณดูสถานะการดาวน์โหลดโมเดลได้โดยแนบผู้สังเกตการณ์กับศูนย์การแจ้งเตือนเริ่มต้น โปรดใช้การอ้างอิงแบบไม่แน่นอนกับ self ในบล็อกการสังเกตการณ์ เนื่องจากการดาวน์โหลดอาจใช้เวลาสักครู่ และระบบอาจเพิ่มพื้นที่ว่างสำหรับวัตถุต้นทางเมื่อการดาวน์โหลดเสร็จสิ้น เช่น

Swift

NotificationCenter.default.addObserver(
    forName: .mlkitMLModelDownloadDidSucceed,
    object: nil,
    queue: nil
) { [weak self] notification in
    guard let strongSelf = self,
        let userInfo = notification.userInfo,
        let model = userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue]
            as? RemoteModel,
        model.name == "your_remote_model"
        else { return }
    // The model was downloaded and is available on the device
}

NotificationCenter.default.addObserver(
    forName: .mlkitMLModelDownloadDidFail,
    object: nil,
    queue: nil
) { [weak self] notification in
    guard let strongSelf = self,
        let userInfo = notification.userInfo,
        let model = userInfo[ModelDownloadUserInfoKey.remoteModel.rawValue]
            as? RemoteModel
        else { return }
    let error = userInfo[ModelDownloadUserInfoKey.error.rawValue]
    // ...
}

Objective-C

__weak typeof(self) weakSelf = self;

[NSNotificationCenter.defaultCenter
    addObserverForName:MLKModelDownloadDidSucceedNotification
                object:nil
                 queue:nil
            usingBlock:^(NSNotification *_Nonnull note) {
              if (weakSelf == nil | note.userInfo == nil) {
                return;
              }
              __strong typeof(self) strongSelf = weakSelf;

              MLKRemoteModel *model = note.userInfo[MLKModelDownloadUserInfoKeyRemoteModel];
              if ([model.name isEqualToString:@"your_remote_model"]) {
                // The model was downloaded and is available on the device
              }
            }];

[NSNotificationCenter.defaultCenter
    addObserverForName:MLKModelDownloadDidFailNotification
                object:nil
                 queue:nil
            usingBlock:^(NSNotification *_Nonnull note) {
              if (weakSelf == nil | note.userInfo == nil) {
                return;
              }
              __strong typeof(self) strongSelf = weakSelf;

              NSError *error = note.userInfo[MLKModelDownloadUserInfoKeyError];
            }];

2. เตรียมรูปภาพอินพุต

สร้างออบเจ็กต์ VisionImage โดยใช้ UIImage หรือ CMSampleBufferRef

หากคุณใช้ UIImage ให้ทำตามขั้นตอนต่อไปนี้

  • สร้างออบเจ็กต์ VisionImage ด้วย UIImage ตรวจสอบว่าได้ระบุ .orientation ที่ถูกต้อง

    Swift

    let image = VisionImage(image: uiImage)
    visionImage.orientation = image.imageOrientation

    Objective-C

    MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image];
    visionImage.orientation = image.imageOrientation;

หากคุณใช้ CMSampleBufferRef ให้ทำตามขั้นตอนต่อไปนี้

  • ระบุการวางแนวของข้อมูลรูปภาพที่อยู่ในบัฟเฟอร์ CMSampleBufferRef

    วิธีดูการวางแนวรูปภาพ

    Swift

    func imageOrientation(
      deviceOrientation: UIDeviceOrientation,
      cameraPosition: AVCaptureDevice.Position
    ) -> UIImage.Orientation {
      switch deviceOrientation {
      case .portrait:
        return cameraPosition == .front ? .leftMirrored : .right
      case .landscapeLeft:
        return cameraPosition == .front ? .downMirrored : .up
      case .portraitUpsideDown:
        return cameraPosition == .front ? .rightMirrored : .left
      case .landscapeRight:
        return cameraPosition == .front ? .upMirrored : .down
      case .faceDown, .faceUp, .unknown:
        return .up
      }
    }
          

    Objective-C

    - (UIImageOrientation)
      imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                             cameraPosition:(AVCaptureDevicePosition)cameraPosition {
      switch (deviceOrientation) {
        case UIDeviceOrientationPortrait:
          return position == AVCaptureDevicePositionFront ? UIImageOrientationLeftMirrored
                                                          : UIImageOrientationRight;
    
        case UIDeviceOrientationLandscapeLeft:
          return position == AVCaptureDevicePositionFront ? UIImageOrientationDownMirrored
                                                          : UIImageOrientationUp;
        case UIDeviceOrientationPortraitUpsideDown:
          return position == AVCaptureDevicePositionFront ? UIImageOrientationRightMirrored
                                                          : UIImageOrientationLeft;
        case UIDeviceOrientationLandscapeRight:
          return position == AVCaptureDevicePositionFront ? UIImageOrientationUpMirrored
                                                          : UIImageOrientationDown;
        case UIDeviceOrientationUnknown:
        case UIDeviceOrientationFaceUp:
        case UIDeviceOrientationFaceDown:
          return UIImageOrientationUp;
      }
    }
          
  • สร้างออบเจ็กต์ VisionImage โดยใช้ออบเจ็กต์ CMSampleBufferRef และการวางแนว

    Swift

    let image = VisionImage(buffer: sampleBuffer)
    image.orientation = imageOrientation(
      deviceOrientation: UIDevice.current.orientation,
      cameraPosition: cameraPosition)

    Objective-C

     MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer];
     image.orientation =
       [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                    cameraPosition:cameraPosition];

3. เรียกใช้โปรแกรมติดป้ายกำกับรูปภาพ

แบบไม่พร้อมกัน

Swift

imageLabeler.process(image) { labels, error in
    guard error == nil, let labels = labels, !labels.isEmpty else {
        // Handle the error.
        return
    }
    // Show results.
}

Objective-C

[imageLabeler
    processImage:image
      completion:^(NSArray<MLKImageLabel *> *_Nullable labels,
                   NSError *_Nullable error) {
        if (label.count == 0) {
            // Handle the error.
            return;
        }
        // Show results.
     }];

แบบซิงโครนัส

Swift

var labels: [ImageLabel]
do {
    labels = try imageLabeler.results(in: image)
} catch let error {
    // Handle the error.
    return
}
// Show results.

Objective-C

NSError *error;
NSArray<MLKImageLabel *> *labels =
    [imageLabeler resultsInImage:image error:&error];
// Show results or handle the error.

4. ดูข้อมูลเกี่ยวกับวัตถุที่ติดป้ายกำกับ

หากการดำเนินการติดป้ายกำกับรูปภาพสำเร็จ ระบบจะแสดงผลอาร์เรย์ของ ImageLabel แต่ละ ImageLabel แสดงถึงสิ่งที่ติดป้ายกำกับในรูปภาพ คุณสามารถดูคำอธิบายแบบข้อความของป้ายกำกับแต่ละรายการ (หากมีในข้อมูลเมตาของไฟล์โมเดล TensorFlow Lite) คะแนนความเชื่อมั่น และดัชนี เช่น

Swift

for label in labels {
  let labelText = label.text
  let confidence = label.confidence
  let index = label.index
}

Objective-C

for (MLKImageLabel *label in labels) {
  NSString *labelText = label.text;
  float confidence = label.confidence;
  NSInteger index = label.index;
}

เคล็ดลับในการปรับปรุงประสิทธิภาพแบบเรียลไทม์

หากต้องการติดป้ายกำกับรูปภาพในแอปพลิเคชันแบบเรียลไทม์ ให้ทำตามหลักเกณฑ์ต่อไปนี้เพื่อให้ได้อัตราเฟรมที่ดีที่สุด

  • จำกัดการเรียกใช้เครื่องตรวจจับ หากเฟรมวิดีโอใหม่พร้อมใช้งานขณะที่ตัวตรวจจับทำงานอยู่ ให้วางเฟรม
  • หากคุณใช้เอาต์พุตของเครื่องมือตรวจจับเพื่อวางกราฟิกซ้อนทับบนรูปภาพอินพุต ให้รับผลลัพธ์ก่อน จากนั้นจึงแสดงผลรูปภาพและวางซ้อนในขั้นตอนเดียว ซึ่งจะทำให้คุณแสดงผลไปยังพื้นผิวการแสดงผลเพียงครั้งเดียวสำหรับเฟรมอินพุตแต่ละเฟรม ดูตัวอย่างได้จากคลาส previewOverlayView และ FIRDetectionOverlayView ในแอปตัวอย่างของ Showcase