在 iOS 上使用自訂分類模型來偵測、追蹤及分類物件

您可以使用 ML Kit 偵測及追蹤連續影片畫面中的物件。

將圖片傳送至 ML Kit 時,模型最多可偵測圖片中的五個物件 以及每個物件在圖片中的位置。偵測 中的物件時 影片串流,每個物件都有專屬 ID,可用來追蹤 每個影格都不成問題

您可以利用自訂圖片分類模型,將屬於 。請參閱「使用 ML Kit 自訂模型」一文 我們對於模型相容性需求的指引、如何找到預先訓練模型 以及如何訓練自有模型

整合自訂模型的方式有兩種。您可以將模型 將檔案放入應用程式的素材資源資料夾,或者可以透過動態方式下載 。下表比較這兩種選項。

套裝組合模型 託管模型
這個模型屬於應用程式 .ipa 檔案的一部分, 會使應用程式變大 這個模型不屬於應用程式的 .ipa 檔案。是 由上傳到 代管 Firebase 機器學習
可立即使用型號,即使 Android 裝置離線也沒問題 模型會隨選下載
不需要 Firebase 專案 需要 Firebase 專案
您必須重新發布應用程式才能更新模型 不必重新發布應用程式即可推送模型更新
沒有內建 A/B 測試 使用 Firebase 遠端設定輕鬆進行 A/B 測試

立即試用

事前準備

  1. 在 Podfile 中加入 ML Kit 程式庫:

    將模型與應用程式綁定:

    pod 'GoogleMLKit/ObjectDetectionCustom', '3.2.0'
    

    如要從 Firebase 動態下載模型,請新增 LinkFirebase 依附元件:

    pod 'GoogleMLKit/ObjectDetectionCustom', '3.2.0'
    pod 'GoogleMLKit/LinkFirebase', '3.2.0'
    
  2. 安裝或更新專案的 Pod 後,請開啟 Xcode 專案 使用 .xcworkspace。Xcode 13.2.1 版支援 ML Kit 或更高版本。

  3. 如要下載模型,請務必 將 Firebase 加進您的 iOS 專案, 如果尚未建立如果您將 模型

1. 載入模型

設定本機模型來源

將模型與應用程式組合如下:

  1. 將模型檔案 (通常結尾為 .tflite.lite) 複製到 Xcode 專案時,請務必選取 Copy bundle resources。 模型檔案就會包含在應用程式套件中,並可供 ML Kit 使用。

  2. 建立 LocalModel 物件,指定模型檔案的路徑:

    Swift

    let localModel = LocalModel(path: localModelFilePath)

    Objective-C

    MLKLocalModel *localModel =
        [[MLKLocalModel alloc] initWithPath:localModelFilePath];
,瞭解如何調查及移除這項存取權。

設定 Firebase 託管的模型來源

如要使用遠端託管的模型,請建立 CustomRemoteModel 物件。 請指定您在發布模型時為其指派的名稱:

Swift

let firebaseModelSource = FirebaseModelSource(
    name: "your_remote_model") // The name you assigned in
                               // the Firebase console.
let remoteModel = CustomRemoteModel(remoteModelSource: firebaseModelSource)

Objective-C

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

接著,啟動模型下載工作,並指定在 您要允許下載的應用程式。如果裝置上沒有該型號,或者是新型號 就能以非同步方式下載該模型 建立 Vertex AI 模型

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] downloadModel:remoteModel
                                       conditions:downloadConditions];

許多應用程式會在初始化程式碼中啟動下載工作,但您 這個模型會在您需要使用模型前執行

2. 設定物件偵測工具

設定模型來源後,請設定物件偵測工具 針對 CustomObjectDetectorOptions 物件的用途建構應用程式。您可以變更 以下設定:

物件偵測器設定
偵測模式 STREAM_MODE (預設) |SINGLE_IMAGE_MODE

STREAM_MODE (預設) 中,物件偵測工具會執行 低延遲,但可能產生不完整的結果 (例如 未指定定界框或類別標籤) 每個回呼函式的保留時間。此外,在「STREAM_MODE」中: 偵測工具會指派追蹤 ID 給物件,您可以用來 跨影格追蹤物件使用此模式追蹤 或低延遲的重要時機 觀看串流影片

SINGLE_IMAGE_MODE 中,物件偵測工具會傳回 會測量物件的定界框。如果發生以下情況: 也會啟用分類功能 方塊和類別標籤因此 可能更長的時間偵測。此外,在 SINGLE_IMAGE_MODE,未指派追蹤 ID。使用 因此若延遲時間不重要且也不想處理 但只有部分結果

偵測並追蹤多個物件 false (預設) |true

偵測及追蹤最多五個物件 明顯的物件 (預設)。

將物件分類 false (預設) |true

是否使用提供的 和自訂分類器模型使用自訂分類 您必須先將這個參數設為 true

分類可信度門檻

偵測到標籤的最低可信度分數。如果未設定 系統會使用模型中繼資料指定的分類器門檻。 如果模型未包含任何中繼資料,或中繼資料不包含 指定分類器門檻,預設門檻為 0.0

每個物件的標籤數量上限

偵測工具的每個物件標籤數量上限 傳回。如未設定,系統會使用預設值 10。

如果您只有本機組合模型,請在 您的 LocalModel 物件:

Swift

let options = CustomObjectDetectorOptions(localModel: localModel)
options.detectorMode = .singleImage
options.shouldEnableClassification = true
options.shouldEnableMultipleObjects = true
options.classificationConfidenceThreshold = NSNumber(value: 0.5)
options.maxPerObjectLabelCount = 3

Objective-C

MLKCustomObjectDetectorOptions *options =
    [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel];
options.detectorMode = MLKObjectDetectorModeSingleImage;
options.shouldEnableClassification = YES;
options.shouldEnableMultipleObjects = YES;
options.classificationConfidenceThreshold = @(0.5);
options.maxPerObjectLabelCount = 3;

如果您使用的是遠端託管的模型,則須檢查該模型是否已 執行前已下載完成您可以查看模型下載狀態 工作使用模型管理員的 isModelDownloaded(remoteModel:) 方法。

雖然您只需要在執行物件偵測器之前確認這一點 如果您同時擁有遠端託管和本機封裝模型 要在將 ObjectDetector 例項化時執行這項檢查:請建立 從遠端模型下載 反之。

Swift

var options: CustomObjectDetectorOptions!
if (ModelManager.modelManager().isModelDownloaded(remoteModel)) {
  options = CustomObjectDetectorOptions(remoteModel: remoteModel)
} else {
  options = CustomObjectDetectorOptions(localModel: localModel)
}
options.detectorMode = .singleImage
options.shouldEnableClassification = true
options.shouldEnableMultipleObjects = true
options.classificationConfidenceThreshold = NSNumber(value: 0.5)
options.maxPerObjectLabelCount = 3

Objective-C

MLKCustomObjectDetectorOptions *options;
if ([[MLKModelManager modelManager] isModelDownloaded:remoteModel]) {
  options = [[MLKCustomObjectDetectorOptions alloc] initWithRemoteModel:remoteModel];
} else {
  options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel];
}
options.detectorMode = MLKObjectDetectorModeSingleImage;
options.shouldEnableClassification = YES;
options.shouldEnableMultipleObjects = YES;
options.classificationConfidenceThreshold = @(0.5);
options.maxPerObjectLabelCount = 3;

如果只有遠端託管的模型,請停用模型相關 功能,例如顯示為灰色或隱藏部分 UI,直到 您確認模型已下載完成

您可以將觀察器附加至預設值,取得模型下載狀態 通知中心。請務必在觀察器中對 self 使用較弱的參照 因此,下載作業可能需要一些時間,而且原始物件可 下載完成後就會釋出空間。例如:

Swift

NotificationCenter.default.addObserver(
    forName: .mlkitModelDownloadDidSucceed,
    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: .mlkitModelDownloadDidFail,
    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];
            }];

物件偵測及追蹤 API 已針對這兩種核心用途進行最佳化 案件:

  • 即時偵測和追蹤相機中最顯眼的物體 觀景窗。
  • 偵測靜態圖片中的多個物件。

如何針對這些用途設定 API:

Swift

// Live detection and tracking
let options = CustomObjectDetectorOptions(localModel: localModel)
options.shouldEnableClassification = true
options.maxPerObjectLabelCount = 3

// Multiple object detection in static images
let options = CustomObjectDetectorOptions(localModel: localModel)
options.detectorMode = .singleImage
options.shouldEnableMultipleObjects = true
options.shouldEnableClassification = true
options.maxPerObjectLabelCount = 3

Objective-C

// Live detection and tracking
MLKCustomObjectDetectorOptions *options =
    [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel];
options.shouldEnableClassification = YES;
options.maxPerObjectLabelCount = 3;

// Multiple object detection in static images
MLKCustomObjectDetectorOptions *options =
    [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel];
options.detectorMode = MLKObjectDetectorModeSingleImage;
options.shouldEnableMultipleObjects = YES;
options.shouldEnableClassification = YES;
options.maxPerObjectLabelCount = 3;

3. 準備輸入圖片

使用 UIImageVisionImage CMSampleBuffer

如果您使用 UIImage,請按照下列步驟操作:

  • 使用 UIImage 建立 VisionImage 物件。請務必指定正確的 .orientation

    Swift

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

    Objective-C

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

如果您使用 CMSampleBuffer,請按照下列步驟操作:

  • 指定 CMSampleBuffer

    如何取得圖片方向:

    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 cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationLeftMirrored
                                                                : UIImageOrientationRight;
    
        case UIDeviceOrientationLandscapeLeft:
          return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationDownMirrored
                                                                : UIImageOrientationUp;
        case UIDeviceOrientationPortraitUpsideDown:
          return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationRightMirrored
                                                                : UIImageOrientationLeft;
        case UIDeviceOrientationLandscapeRight:
          return cameraPosition == AVCaptureDevicePositionFront ? UIImageOrientationUpMirrored
                                                                : UIImageOrientationDown;
        case UIDeviceOrientationUnknown:
        case UIDeviceOrientationFaceUp:
        case UIDeviceOrientationFaceDown:
          return UIImageOrientationUp;
      }
    }
          
  • 使用VisionImage CMSampleBuffer 物件和方向:

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

4. 建立並執行物件偵測工具

  1. 建立新的物件偵測工具:

    Swift

    let objectDetector = ObjectDetector.objectDetector(options: options)

    Objective-C

    MLKObjectDetector *objectDetector = [MLKObjectDetector objectDetectorWithOptions:options];
  2. 然後使用偵測工具:

    非同步:

    Swift

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

    Objective-C

    [objectDetector
        processImage:image
          completion:^(NSArray *_Nullable objects,
                       NSError *_Nullable error) {
            if (objects.count == 0) {
                // Handle the error.
                return;
            }
            // Show results.
         }];

    同步:

    Swift

    var objects: [Object]
    do {
        objects = try objectDetector.results(in: image)
    } catch let error {
        // Handle the error.
        return
    }
    // Show results.

    Objective-C

    NSError *error;
    NSArray *objects =
        [objectDetector resultsInImage:image error:&error];
    // Show results or handle the error.

5. 取得加上標籤的物件相關資訊

如果對圖片處理器的呼叫成功,則會傳遞 Object 傳送至完成處理常式,或傳回清單,視實際情況而定 無論您呼叫的是非同步或同步方法

每個 Object 都包含下列屬性:

frame CGRect:指出物件在 圖片。
trackingID 一個整數,用來在圖片中識別物件,或是在圖片中識別「nil」 SINGLE_IMAGE_MODE。
labels
label.text 標籤的文字說明。只有在 TensorFlow 的情況下才會傳回 Lite 模型的中繼資料包含標籤說明。
label.index 此標籤在 分類器。
label.confidence 物件分類的可信度值。

Swift

// objects contains one item if multiple object detection wasn't enabled.
for object in objects {
  let frame = object.frame
  let trackingID = object.trackingID
  let description = object.labels.enumerated().map { (index, label) in
    "Label \(index): \(label.text), \(label.confidence), \(label.index)"
  }.joined(separator: "\n")
}

Objective-C

// The list of detected objects contains one item if multiple object detection
// wasn't enabled.
for (MLKObject *object in objects) {
  CGRect frame = object.frame;
  NSNumber *trackingID = object.trackingID;
  for (MLKObjectLabel *label in object.labels) {
    NSString *labelString =
        [NSString stringWithFormat:@"%@, %f, %lu",
                                   label.text,
                                   label.confidence,
                                   (unsigned long)label.index];
  }
}

確保良好的使用者體驗

為獲得最佳使用者體驗,請在應用程式中遵循以下規範:

  • 是否成功偵測物件,取決於物件的視覺複雜度。於 不過,如果物體具有少量視覺特徵 將圖片放大出來您應該向使用者提供 以便擷取適用於目標物件種類的輸入資料。
  • 使用分類功能時,您可以偵測不會下降的物件 完整地新增至支援的類別,並針對不明狀況導入特殊處理 如需儲存大量結構化物件 建議使用 Cloud Bigtable

此外,也請參閱 [ML Kit Material Design 展示應用程式][showcase-link]{: .external } 和 質感設計 機器學習輔助功能的模式集合。

提升效能

如要在即時應用程式中使用物件偵測,請按照這些 實現最佳影格速率: