在 iOS 上使用 AutoML 訓練模型來為圖片加上標籤

使用 AutoML Vision Edge 訓練自己的模型後,您可以在應用程式中使用它來標記圖像。

在你開始之前

  1. 如果您尚未將 Firebase 新增至您的應用程式中,請按照入門指南中的步驟進行操作。
  2. 在 Podfile 中包含 ML Kit 函式庫:
    pod 'Firebase/MLVision', '6.25.0'
    pod 'Firebase/MLVisionAutoML', '6.25.0'
    
    安裝或更新專案的 Pod 後,請務必使用其.xcworkspace開啟 Xcode 專案。
  3. 在您的應用程式中,導入 Firebase:

    迅速

    import Firebase

    Objective-C

    @import Firebase;

1.載入模型

ML Kit 在裝置上執行 AutoML 產生的模型。但是,您可以將 ML Kit 配置為從 Firebase 遠端載入模型、從本機儲存載入模型,或從兩者遠端載入模型。

透過在 Firebase 上託管模型,您可以更新模型而無需發布新的應用版本,並且可以使用遠端配置和 A/B 測試向不同的使用者群組動態提供不同的模型。

如果您選擇僅透過 Firebase 託管來提供模型,而不是將其與您的應用程式捆綁在一起,則可以減少應用程式的初始下載大小。但請記住,如果模型未與您的應用程式捆綁在一起,則在您的應用程式首次下載模型之前,任何與模型相關的功能都將不可用。

透過將模型與應用程式捆綁在一起,您可以確保應用程式的 ML 功能在 Firebase 託管模型不可用時仍然有效。

配置 Firebase 託管的模型來源

若要使用遠端託管模型,請建立AutoMLRemoteModel對象,並指定您在發布模型時為其指派的名稱:

迅速

let remoteModel = AutoMLRemoteModel(
    name: "your_remote_model"  // The name you assigned in the Firebase console.
)

Objective-C

FIRAutoMLRemoteModel *remoteModel = [[FIRAutoMLRemoteModel alloc]
    initWithName:@"your_remote_model"];  // The name you assigned in the Firebase console.

然後,啟動模型下載任務,指定允許下載的條件。如果裝置上沒有模型,或者有更新版本的模型可用,則任務將從 Firebase 非同步下載模型:

迅速

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

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

Objective-C

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

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

許多應用程式在其初始化程式碼中啟動下載任務,但您可以在需要使用模型之前隨時執行此操作。

配置本地模型來源

要將模型與您的應用程式捆綁在一起:

  1. 將您從 Firebase 控制台下載的 zip 檔案中的模型及其元資料提取到以下資料夾:
    your_model_directory
      |____dict.txt
      |____manifest.json
      |____model.tflite
    
    所有三個檔案必須位於同一資料夾中。我們建議您直接使用下載的文件,不要進行修改(包括文件名稱)。
  2. 將該資料夾複製到您的 Xcode 項目,執行此操作時請注意選擇「建立資料夾參考」 。模型檔案和元資料將包含在應用程式套件中並可供 ML Kit 使用。
  3. 建立AutoMLLocalModel對象,指定模型清單檔案的路徑:

    迅速

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

    Objective-C

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

從您的模型建立影像標記器

配置模型來源後,從其中一個建立VisionImageLabeler物件。

如果您只有本地捆綁的模型,只需從AutoMLLocalModel物件建立一個標記器並配置您想要的置信度分數閾值(請參閱評估您的模型):

迅速

let options = VisionOnDeviceAutoMLImageLabelerOptions(localModel: localModel)
options.confidenceThreshold = 0  // Evaluate your model in the Firebase console
                                 // to determine an appropriate value.
let labeler = Vision.vision().onDeviceAutoMLImageLabeler(options: options)

Objective-C

FIRVisionOnDeviceAutoMLImageLabelerOptions *options =
    [[FIRVisionOnDeviceAutoMLImageLabelerOptions alloc] initWithLocalModel:localModel];
options.confidenceThreshold = 0;  // Evaluate your model in the Firebase console
                                  // to determine an appropriate value.
FIRVisionImageLabeler *labeler =
    [[FIRVision vision] onDeviceAutoMLImageLabelerWithOptions:options];

如果您有遠端託管模型,則必須在運行之前檢查它是否已下載。您可以使用模型管理器的isModelDownloaded(remoteModel:)方法檢查模型下載任務的狀態。

儘管您只需在運行貼標機之前確認這一點,但如果您同時擁有遠端託管模型和本地捆綁模型,則在實例化VisionImageLabeler時執行此檢查可能是有意義的:如果是,則從遠端模型創建貼標機已下載,否則從本機模型下載。

迅速

var options: VisionOnDeviceAutoMLImageLabelerOptions?
if (ModelManager.modelManager().isModelDownloaded(remoteModel)) {
  options = VisionOnDeviceAutoMLImageLabelerOptions(remoteModel: remoteModel)
} else {
  options = VisionOnDeviceAutoMLImageLabelerOptions(localModel: localModel)
}
options.confidenceThreshold = 0  // Evaluate your model in the Firebase console
                                 // to determine an appropriate value.
let labeler = Vision.vision().onDeviceAutoMLImageLabeler(options: options)

Objective-C

VisionOnDeviceAutoMLImageLabelerOptions *options;
if ([[FIRModelManager modelManager] isModelDownloaded:remoteModel]) {
  options = [[FIRVisionOnDeviceAutoMLImageLabelerOptions alloc] initWithRemoteModel:remoteModel];
} else {
  options = [[FIRVisionOnDeviceAutoMLImageLabelerOptions alloc] initWithLocalModel:localModel];
}
options.confidenceThreshold = 0.0f;  // Evaluate your model in the Firebase console
                                     // to determine an appropriate value.
FIRVisionImageLabeler *labeler = [[FIRVision vision] onDeviceAutoMLImageLabelerWithOptions:options];

如果您只有遠端託管模型,則應停用與模型相關的功能(例如,灰顯或隱藏部分 UI),直到確認模型已下載。

您可以透過將觀察者附加到預設通知中心來取得模型下載狀態。請務必在觀察者區塊中使用對self弱引用,因為下載可能需要一些時間,並且在下載完成時可以釋放原始物件。例如:

迅速

NotificationCenter.default.addObserver(
    forName: .firebaseMLModelDownloadDidSucceed,
    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: .firebaseMLModelDownloadDidFail,
    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:FIRModelDownloadDidSucceedNotification
                object:nil
                 queue:nil
            usingBlock:^(NSNotification *_Nonnull note) {
              if (weakSelf == nil | note.userInfo == nil) {
                return;
              }
              __strong typeof(self) strongSelf = weakSelf;

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

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

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

2. 準備輸入影像

然後,對於要標記的每個圖像,使用本節中描述的選項之一建立VisionImage對象,並將其傳遞給VisionImageLabeler的實例(在下一節中描述)。

使用UIImageCMSampleBufferRef建立VisionImage物件。

使用UIImage

  1. 如有必要,旋轉影像,使其imageOrientation屬性為.up
  2. 使用正確旋轉的UIImage建立VisionImage物件。不要指定任何旋轉元資料 - 必須使用預設值.topLeft

    迅速

    let image = VisionImage(image: uiImage)

    Objective-C

    FIRVisionImage *image = [[FIRVisionImage alloc] initWithImage:uiImage];

使用CMSampleBufferRef

  1. 建立一個VisionImageMetadata對象,該對象指定CMSampleBufferRef緩衝區中包含的圖像資料的方向。

    若要取得影像方向:

    迅速

    func imageOrientation(
        deviceOrientation: UIDeviceOrientation,
        cameraPosition: AVCaptureDevice.Position
        ) -> VisionDetectorImageOrientation {
        switch deviceOrientation {
        case .portrait:
            return cameraPosition == .front ? .leftTop : .rightTop
        case .landscapeLeft:
            return cameraPosition == .front ? .bottomLeft : .topLeft
        case .portraitUpsideDown:
            return cameraPosition == .front ? .rightBottom : .leftBottom
        case .landscapeRight:
            return cameraPosition == .front ? .topRight : .bottomRight
        case .faceDown, .faceUp, .unknown:
            return .leftTop
        }
    }

    Objective-C

    - (FIRVisionDetectorImageOrientation)
        imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                               cameraPosition:(AVCaptureDevicePosition)cameraPosition {
      switch (deviceOrientation) {
        case UIDeviceOrientationPortrait:
          if (cameraPosition == AVCaptureDevicePositionFront) {
            return FIRVisionDetectorImageOrientationLeftTop;
          } else {
            return FIRVisionDetectorImageOrientationRightTop;
          }
        case UIDeviceOrientationLandscapeLeft:
          if (cameraPosition == AVCaptureDevicePositionFront) {
            return FIRVisionDetectorImageOrientationBottomLeft;
          } else {
            return FIRVisionDetectorImageOrientationTopLeft;
          }
        case UIDeviceOrientationPortraitUpsideDown:
          if (cameraPosition == AVCaptureDevicePositionFront) {
            return FIRVisionDetectorImageOrientationRightBottom;
          } else {
            return FIRVisionDetectorImageOrientationLeftBottom;
          }
        case UIDeviceOrientationLandscapeRight:
          if (cameraPosition == AVCaptureDevicePositionFront) {
            return FIRVisionDetectorImageOrientationTopRight;
          } else {
            return FIRVisionDetectorImageOrientationBottomRight;
          }
        default:
          return FIRVisionDetectorImageOrientationTopLeft;
      }
    }

    然後,建立元資料物件:

    迅速

    let cameraPosition = AVCaptureDevice.Position.back  // Set to the capture device you used.
    let metadata = VisionImageMetadata()
    metadata.orientation = imageOrientation(
        deviceOrientation: UIDevice.current.orientation,
        cameraPosition: cameraPosition
    )

    Objective-C

    FIRVisionImageMetadata *metadata = [[FIRVisionImageMetadata alloc] init];
    AVCaptureDevicePosition cameraPosition =
        AVCaptureDevicePositionBack;  // Set to the capture device you used.
    metadata.orientation =
        [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                     cameraPosition:cameraPosition];
  2. 使用CMSampleBufferRef物件和旋轉元資料建立VisionImage物件:

    迅速

    let image = VisionImage(buffer: sampleBuffer)
    image.metadata = metadata

    Objective-C

    FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
    image.metadata = metadata;

3. 運行影像標記器

要標記影像中的對象,請將VisionImage物件傳遞給VisionImageLabelerprocess()方法:

迅速

labeler.process(image) { labels, error in
    guard error == nil, let labels = labels else { return }

    // Task succeeded.
    // ...
}

Objective-C

[labeler
    processImage:image
      completion:^(NSArray<FIRVisionImageLabel *> *_Nullable labels, NSError *_Nullable error) {
        if (error != nil || labels == nil) {
          return;
        }

        // Task succeeded.
        // ...
      }];

如果影像標記成功, VisionImageLabel物件的陣列將傳遞到完成處理程序。從每個物件中,您可以獲得有關圖像中識別的特徵的資訊。

例如:

迅速

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

Objective-C

for (FIRVisionImageLabel *label in labels) {
  NSString *labelText = label.text;
  NSNumber *confidence = label.confidence;
}

提升即時效能的技巧

  • 對檢測器的節流呼叫。如果偵測器運作時有新的視訊幀可用,則丟棄該幀。
  • 如果您使用偵測器的輸出將圖形疊加在輸入影像上,請先從 ML Kit 取得結果,然後一步渲染影像並疊加。透過這樣做,每個輸入幀只需渲染到顯示表面一次。有關範例,請參閱展示範例應用程式中的PreviewOverlayViewFIRDetectionOverlayView類別。