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

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

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

ตัวเลือกการรวมโมเดล
รวมไว้ในแอป
  • โมเดลเป็นส่วนหนึ่งของแพ็กเกจ
  • โมเดลพร้อมใช้งานทันทีแม้ว่าอุปกรณ์ Apple จะออฟไลน์อยู่ก็ตาม
  • ไม่ต้องมีโปรเจ็กต์ Firebase
โฮสต์ด้วย Firebase

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

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

    สำหรับการรวมโมเดลกับแอปของคุณ ให้ทำดังนี้

    pod 'GoogleMLKit/ImageLabelingCustom'
    

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

    pod 'GoogleMLKit/ImageLabelingCustom'
    pod 'GoogleMLKit/LinkFirebase'
    
  2. หลังจากติดตั้งหรืออัปเดต Pod ของโปรเจ็กต์แล้ว ให้เปิดโปรเจ็กต์ 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 โดยอย่าลืมเลือกสร้างการอ้างอิงโฟลเดอร์เมื่อดำเนินการ ไฟล์โมเดลและข้อมูลเมตา จะรวมอยู่ในชุดแอปและพร้อมใช้งานกับ 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 เป็นสีเทาหรือซ่อนไว้ จนกว่าจะยืนยันว่าดาวน์โหลดโมเดลแล้ว

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

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