Objekte mit einem benutzerdefinierten Klassifizierungsmodell unter iOS erkennen, verfolgen und klassifizieren

Mit ML Kit können Sie Objekte in aufeinanderfolgenden Videoframes erkennen und verfolgen.

Wenn Sie ein Bild an ML Kit übergeben, werden bis zu fünf Objekte im Bild sowie die Position der einzelnen Objekte im Bild erkannt. Bei der Objekterkennung in Videostreams hat jedes Objekt eine eindeutige ID, mit der Sie das Objekt von Frame zu Frame verfolgen können.

Sie können ein benutzerdefiniertes Bildklassifizierungsmodell verwenden, um die erkannten Objekte zu klassifizieren. Unter Benutzerdefinierte Modelle mit ML Kit finden Sie Informationen zu den Anforderungen an die Modellkompatibilität, zu vortrainierten Modellen und dazu, wie Sie eigene Modelle trainieren.

Es gibt zwei Möglichkeiten, ein benutzerdefiniertes Modell einzubinden. Sie können das Modell bündeln, indem Sie es in den Asset-Ordner Ihrer App einfügen, oder Sie können es dynamisch aus Cloud Storage herunterladen. In der folgenden Tabelle werden die beiden Optionen verglichen.

Gebündeltes Modell Gehostetes Modell
Das Modell ist Teil der .ipa-Datei Ihrer App, wodurch die Größe der Datei zunimmt. Das Modell ist nicht Teil der .ipa-Datei Ihrer App. Es wird gehostet, indem es in Cloud Storage hochgeladen wird. Wir empfehlen die Verwendung von Cloud Storage for Firebase.
Das Modell ist sofort verfügbar, auch wenn das Android-Gerät offline ist. Ihre App muss Code enthalten, um das Modell bei Bedarf herunterzuladen.
Kein Firebase-Projekt erforderlich Firebase-Projekt erforderlich (bei Verwendung von Cloud Storage for Firebase)
Sie müssen Ihre App noch einmal veröffentlichen, um das Modell zu aktualisieren. Modellaktualisierungen pushen, ohne die App noch einmal zu veröffentlichen
Keine integrierten A/B-Tests A/B-Tests mit Firebase Remote Config

Jetzt ausprobieren

Hinweis

  1. Fügen Sie die ML Kit-Bibliotheken in Ihre Podfile ein:

    pod 'GoogleMLKit/ObjectDetectionCustom', '8.0.0'
    
  2. Nachdem Sie die Pods Ihres Projekts installiert oder aktualisiert haben, öffnen Sie Ihr Xcode-Projekt mit der Datei .xcworkspace. ML Kit wird in Xcode Version 13.2.1 oder höher unterstützt.

  3. Wenn Sie ein Modell mit Cloud Storage for Firebase herunterladen möchten, stellen Sie sicher, dass Sie Firebase zu Ihrem iOS-Projekt hinzufügen, falls Sie das noch nicht getan haben. Das ist nicht erforderlich, wenn Sie das Modell bündeln.

1. Modell laden

Lokale Modellquelle konfigurieren

So bündeln Sie das Modell mit Ihrer App:

  1. Kopieren Sie die Modelldatei (endet normalerweise auf .tflite oder .lite) in Ihr Xcode-Projekt und wählen Sie dabei Copy bundle resources aus. Die Modelldatei wird in das App-Bundle aufgenommen und ist für ML Kit verfügbar.

  2. Erstellen Sie ein LocalModel-Objekt und geben Sie den Pfad zur Modelldatei an:

    Swift

    let localModel = LocalModel(path: localModelFilePath)

    Objective-C

    MLKLocalModel *localModel =
        [[MLKLocalModel alloc] initWithPath:localModelFilePath];
.

Remote gehostete Modellquelle konfigurieren

Wenn Sie das remote gehostete Modell verwenden möchten, müssen Sie die Modelldatei mit Ihrer eigenen App-Logik in den lokalen Speicher des Geräts herunterladen und sie dann als lokales Modell laden. Wir empfehlen, Cloud Storage for Firebase zum Hosten eines Modells zu verwenden. Implementierungsdetails finden Sie in der Migrationsanleitung von Firebase ML zu Cloud Storage.

2. Objektdetektor konfigurieren

Nachdem Sie Ihre Modellquellen konfiguriert haben, konfigurieren Sie den Objektdetektor für Ihren Anwendungsfall mit einem CustomObjectDetectorOptions-Objekt. Sie können die folgenden Einstellungen ändern:

Einstellungen für den Objektdetektor
Erkennungsmodus STREAM_MODE (Standard) | SINGLE_IMAGE_MODE

In STREAM_MODE (Standard) wird der Objektdetektor mit niedriger Latenz ausgeführt, kann aber bei den ersten Aufrufen des Detektors unvollständige Ergebnisse liefern (z. B. nicht angegebene Begrenzungsrahmen oder Kategorielabels). Außerdem weist der Detektor in STREAM_MODE, Objekten Tracking-IDs zu, mit denen Sie Objekte in verschiedenen Frames verfolgen können. Verwenden Sie diesen Modus, wenn Sie Objekte verfolgen möchten oder wenn eine niedrige Latenz wichtig ist, z. B. bei der Echtzeitverarbeitung von Videostreams.

In SINGLE_IMAGE_MODE, gibt der Objektdetektor das Ergebnis zurück, nachdem der Begrenzungsrahmen des Objekts bestimmt wurde. Wenn Sie auch die Klassifizierung aktivieren, wird das Ergebnis zurückgegeben, nachdem sowohl der Begrenzungs rahmen als auch das Kategorielabel verfügbar sind. Daher ist die Latenz bei der Erkennung möglicherweise höher. Außerdem werden in SINGLE_IMAGE_MODE, keine Tracking-IDs zugewiesen. Verwenden Sie diesen Modus, wenn die Latenz nicht kritisch ist und Sie keine Teilergebnisse erhalten möchten.

Mehrere Objekte erkennen und verfolgen false (Standard) | true

Gibt an, ob bis zu fünf Objekte oder nur das wichtigste Objekt erkannt und verfolgt werden sollen (Standard).

Objekte klassifizieren false (Standard) | true

Gibt an, ob erkannte Objekte mit dem bereitgestellten benutzerdefinierten Klassifikatormodell klassifiziert werden sollen. Wenn Sie Ihr benutzerdefiniertes Klassifizierungs modell verwenden möchten, müssen Sie diese Option auf true setzen.

Konfidenzwert für die Klassifizierung

Mindestkonfidenzwert für erkannte Labels. Wenn nicht festgelegt, wird ein beliebiger Klassifikatorschwellenwert verwendet, der in den Metadaten des Modells angegeben ist. Wenn das Modell keine Metadaten enthält oder in den Metadaten kein Klassifikatorschwellenwert angegeben ist, wird ein Standardschwellenwert von 0,0 verwendet.

Maximale Anzahl von Labels pro Objekt

Maximale Anzahl von Labels pro Objekt, die der Detektor zurückgibt. Wenn nicht festgelegt, wird der Standardwert 10 verwendet.

Wenn Sie nur ein lokal gebündeltes Modell haben, erstellen Sie einfach einen Objektdetektor aus Ihrem LocalModel-Objekt:

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;

Wenn Sie ein remote gehostetes Modell haben, müssen Sie prüfen, ob es heruntergeladen wurde, bevor Sie es ausführen.

Sie müssen dies zwar nur vor dem Ausführen des Objektdetektors bestätigen, aber wenn Sie sowohl ein remote gehostetes als auch ein lokal gebündeltes Modell haben, ist es möglicherweise sinnvoll, diese Prüfung beim Instanziieren des ObjectDetector durchzuführen: Erstellen Sie einen Detektor aus dem Remote-Modell, wenn es heruntergeladen wurde, und andernfalls aus dem lokalen Modell.

Swift

// Path where your download logic saves the model
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let localModelURL = documentDirectory.appendingPathComponent("my_remote_model.tflite")

let model: LocalModel
if FileManager.default.fileExists(atPath: localModelURL.path) {
  // Use the downloaded model
  model = LocalModel(path: localModelURL.path)
} else {
  // Fall back to bundled model
  guard let bundledModelPath = Bundle.main.path(forResource: "model", ofType: "tflite") else { return }
  model = LocalModel(path: bundledModelPath)
}

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

Objective-C

NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *localModelPath = [documentsDirectory stringByAppendingPathComponent:@"my_remote_model.tflite"];

MLKLocalModel *model;
if ([NSFileManager.defaultManager fileExistsAtPath:localModelPath]) {
  // Use the downloaded model
  model = [[MLKLocalModel alloc] initWithPath:localModelPath];
} else {
  // Fall back to bundled model
  NSString *bundledModelPath = [NSBundle.mainBundle pathForResource:@"model" ofType:@"tflite"];
  model = [[MLKLocalModel alloc] initWithPath:bundledModelPath];
}

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

Wenn Sie nur ein remote gehostetes Modell haben, sollten Sie modellbezogene Funktionen deaktivieren, z. B. Teile der Benutzeroberfläche ausblenden oder ausgrauen, bis Sie bestätigen, dass das Modell heruntergeladen wurde.

Swift

let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let localModelURL = documentDirectory.appendingPathComponent("my_remote_model.tflite")
if FileManager.default.fileExists(atPath: localModelURL.path) {
  // Model is already cached, initialize immediately
  self.initializeDetector(with: localModelURL)
} else {
  // Model is not yet available, show loading UI and start download
  self.showLoadingUI()
  let storage = Storage.storage()
  let modelRef = storage.reference(forURL: "gs://YOUR_BUCKET/path/to/model.tflite")
  modelRef.write(toFile: localModelURL) { url, error in
    self.hideLoadingUI()
    if let error = error {
      // Handle download error
      self.showErrorUI()
    } else if let modelURL = url {
      // Download success, initialize detector
      self.initializeDetector(with: modelURL)
    }
  }
}

func initializeDetector(with modelURL: URL) {
  let localModel = LocalModel(path: modelURL.path)
  let options = CustomObjectDetectorOptions(localModel: localModel)
  options.detectorMode = .singleImage
  options.shouldEnableClassification = true
  options.shouldEnableMultipleObjects = true
  self.objectDetector = ObjectDetector.objectDetector(options: options)
  // Enable ML features in UI
  self.enableMLFeatures()
}

Objective-C

NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *localModelPath = [documentsDirectory stringByAppendingPathComponent:@"my_remote_model.tflite"];
NSURL *localModelURL = [NSURL fileURLWithPath:localModelPath];

if ([NSFileManager.defaultManager fileExistsAtPath:localModelPath]) {
  // Model is already cached, initialize immediately
  [self initializeDetectorWithURL:localModelURL];
} else {
  // Model is not yet available, show loading UI and start download
  [self showLoadingUI];

  FIRStorage *storage = [FIRStorage storage];
  FIRStorageReference *modelRef = [storage referenceForURL:@"gs://YOUR_BUCKET/path/to/model.tflite"];

  [modelRef writeToFile:localModelURL
             completion:^(NSURL * _Nullable URL, NSError * _Nullable error) {
               [self hideLoadingUI];
               if (error != nil) {
                 // Handle download error
                 [self showErrorUI];
               } else {
                 // Download success, initialize detector
                 [self initializeDetectorWithURL:URL];
               }
             }];
}

- (void)initializeDetectorWithURL:(NSURL *)modelURL {
  MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithPath:modelURL.path];
  MLKCustomObjectDetectorOptions *options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel];
  options.detectorMode = MLKObjectDetectorModeSingleImage;
  options.shouldEnableClassification = YES;
  options.shouldEnableMultipleObjects = YES;
  self.objectDetector = [MLKObjectDetector objectDetectorWithOptions:options];

  // Enable ML features in UI
  [self enableMLFeatures];
}

Die API für Objekterkennung und -tracking ist für diese beiden Hauptanwendungsfälle optimiert:

  • Live-Erkennung und -Tracking des wichtigsten Objekts im Sucher der Kamera.
  • Erkennung mehrerer Objekte aus einem statischen Bild.

So konfigurieren Sie die API für diese Anwendungsfälle:

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. Eingabebild vorbereiten

Erstellen Sie ein VisionImage-Objekt mit einem UIImage oder einem CMSampleBuffer.

Wenn Sie ein UIImage verwenden, gehen Sie so vor:

  • Erstellen Sie ein VisionImage-Objekt mit dem UIImage. Geben Sie die richtige .orientation an.

    Swift

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

    Objective-C

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

Wenn Sie einen CMSampleBuffer verwenden, gehen Sie so vor:

  • Geben Sie die Ausrichtung der Bilddaten im CMSampleBuffer an.

    So rufen Sie die Bildausrichtung ab:

    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;
      }
    }
          
  • Erstellen Sie ein VisionImage-Objekt mit dem CMSampleBuffer-Objekt und der Ausrichtung:

    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. Objektdetektor erstellen und ausführen

  1. Erstellen Sie einen neuen Objektdetektor:

    Swift

    let objectDetector = ObjectDetector.objectDetector(options: options)

    Objective-C

    MLKObjectDetector *objectDetector = [MLKObjectDetector objectDetectorWithOptions:options];
  2. Verwenden Sie dann den Detektor:

    Asynchron:

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

    Synchron:

    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. Informationen zu gelabelten Objekten abrufen

Wenn der Aufruf des Bildprozessors erfolgreich ist, wird entweder eine Liste von Object-Objekten an den Completion-Handler übergeben oder die Liste wird zurückgegeben, je nachdem, ob Sie die asynchrone oder synchrone Methode aufgerufen haben.

Jedes Object enthält die folgenden Properties:

frame Ein CGRect das die Position des Objekts im Bild angibt.
trackingID Eine Ganzzahl, die das Objekt in verschiedenen Bildern identifiziert, oder `nil` im SINGLE_IMAGE_MODE.
labels
label.text Die Textbeschreibung des Labels. Wird nur zurückgegeben, wenn die Metadaten des LiteRT-Modells Labelbeschreibungen enthalten.
label.index Der Index des Labels unter allen Labels, die vom Klassifikator unterstützt werden.
label.confidence Der Konfidenzwert der Objektklassifizierung.

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

Für eine optimale Nutzererfahrung sorgen

Für eine optimale Nutzererfahrung sollten Sie in Ihrer App die folgenden Richtlinien beachten:

  • Die erfolgreiche Objekterkennung hängt von der visuellen Komplexität des Objekts ab. Damit Objekte mit wenigen visuellen Merkmalen erkannt werden können, müssen sie möglicherweise einen größeren Teil des Bildes einnehmen. Sie sollten Nutzern eine Anleitung zum Erfassen von Eingaben geben, die gut zu den Objekten passen, die Sie erkennen möchten.
  • Wenn Sie die Klassifizierung verwenden und Objekte erkennen möchten, die nicht eindeutig in die unterstützten Kategorien fallen, implementieren Sie eine spezielle Verarbeitung für unbekannte Objekte.

Sehen Sie sich auch die ML Kit Material Design-Showcase-App und die Material Design Muster für Funktionen mit maschinellem Lernen an.

Leistungsoptimierung

Wenn Sie die Objekterkennung in einer Echtzeitanwendung verwenden möchten, beachten Sie diese Richtlinien, um die besten Frameraten zu erzielen:

  • Verwenden Sie im Streamingmodus in einer Echtzeitanwendung keine Objekterkennung für mehrere Objekte, da die meisten Geräte keine angemessenen Frameraten erzeugen können.

  • Verwenden Sie für die Verarbeitung von Videoframes die synchrone API results(in:) des Detektors. Rufen Sie diese Methode über die AVCaptureVideoDataOutputSampleBufferDelegate's captureOutput(_, didOutput:from:) Funktion auf, um synchron Ergebnisse aus dem angegebenen Video frame abzurufen. Lassen Sie AVCaptureVideoDataOutput's alwaysDiscardsLateVideoFrames auf true gesetzt, um Aufrufe an den Detektor zu drosseln. Wenn ein neuer Videoframe verfügbar wird, während der Detektor ausgeführt wird, wird er verworfen.
  • Wenn Sie die Ausgabe des Detektors verwenden, um Grafiken auf das Eingabebild zu legen, rufen Sie zuerst das Ergebnis von ML Kit ab und rendern Sie dann das Bild und das Overlay in einem Schritt. So rendern Sie die Anzeigeoberfläche nur einmal für jeden verarbeiteten Eingabeframe. Ein Beispiel finden Sie unter updatePreviewOverlayViewWithLastFrame im ML Kit-Schnellstartbeispiel.