Bilder mit einem benutzerdefinierten Modell unter iOS mit Labels versehen

Mit ML Kit können Sie Entitäten in einem Bild erkennen und mit Labels versehen. Diese API unterstützt eine Vielzahl benutzerdefinierter Bildklassifizierungsmodelle. Weitere Informationen zu den Anforderungen an die Modellkompatibilität, zur Suche nach vortrainierten Modellen und zum Trainieren Ihrer eigenen Modelle finden Sie unter Benutzerdefinierte Modelle mit ML Kit.

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

Gebündeltes Modell Gehostetes Modell
Das Modell ist Teil des APK Ihrer App, das seine Größe vergrößert. Das Modell ist nicht Teil Ihres APK. Sie wird durch Hochladen in Firebase Machine Learning gehostet.
Das Modell ist sofort verfügbar, auch wenn das Android-Gerät offline ist Das Modell wird bei Bedarf heruntergeladen
Kein Firebase-Projekt erforderlich Firebase-Projekt erforderlich
Du musst deine App noch einmal veröffentlichen, um das Modell zu aktualisieren Modellaktualisierungen ohne erneute Veröffentlichung Ihrer App veröffentlichen
Keine integrierten A/B-Tests Einfache A/B-Tests mit Firebase Remote Config

Testen

Hinweis

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

    So bündeln Sie ein Modell mit Ihrer App:

    pod 'GoogleMLKit/ImageLabelingCustom', '3.2.0'
    

    Fügen Sie die Abhängigkeit LinkFirebase hinzu, um ein Modell dynamisch aus Firebase herunterzuladen:

    pod 'GoogleMLKit/ImageLabelingCustom', '3.2.0'
    pod 'GoogleMLKit/LinkFirebase', '3.2.0'
    
  2. Nachdem Sie die Pods Ihres Projekts installiert oder aktualisiert haben, öffnen Sie das Xcode-Projekt mit dessen .xcworkspace. ML Kit wird ab Xcode Version 13.2.1 unterstützt.

  3. Wenn Sie ein Modell herunterladen möchten, achten Sie darauf, dass Sie Ihrem iOS-Projekt Firebase hinzufügen, falls Sie dies noch nicht getan haben. Dies 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 (normalerweise auf .tflite oder .lite enden) in Ihr Xcode-Projekt und wählen Sie dabei Copy bundle resources aus. Die Modelldatei ist im App Bundle enthalten und für ML Kit verfügbar.

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

    Swift

    let localModel = LocalModel(path: localModelFilePath)

    Objective-C

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

Von Firebase gehostete Modellquelle konfigurieren

Wenn Sie das remote gehostete Modell verwenden möchten, erstellen Sie ein RemoteModel-Objekt und geben Sie den Namen an, den Sie dem Modell bei der Veröffentlichung zugewiesen haben:

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

Starten Sie dann die Aufgabe zum Modelldownload und geben Sie die Bedingungen an, unter denen Sie das Herunterladen zulassen möchten. Wenn sich das Modell nicht auf dem Gerät befindet oder eine neuere Version des Modells verfügbar ist, lädt die Aufgabe das Modell asynchron aus Firebase herunter:

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

Viele Anwendungen starten die Downloadaufgabe in ihrem Initialisierungscode. Sie können dies jedoch jederzeit tun, bevor Sie das Modell verwenden müssen.

Image-Labelersteller konfigurieren

Nachdem Sie die Modellquellen konfiguriert haben, erstellen Sie aus einer dieser Quellen ein ImageLabeler-Objekt.

Folgende Optionen sind verfügbar:

Optionen
confidenceThreshold

Minimaler Konfidenzwert der erkannten Labels. Wenn nichts anderes festgelegt ist, wird jeder Klassifikatorgrenzwert verwendet, der in den Metadaten des Modells angegeben ist. Wenn das Modell keine Metadaten enthält oder die Metadaten keinen Klassifikatorschwellenwert angeben, wird der Standardgrenzwert 0,0 verwendet.

maxResultCount

Maximale Anzahl der Labels, die zurückgegeben werden sollen. Wenn nicht festgelegt, wird der Standardwert 10 verwendet.

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

Swift

let options = CustomImageLabelerOptions(localModel: localModel)
options.confidenceThreshold = NSNumber(value: 0.0)
let imageLabeler = ImageLabeler.imageLabeler(options: options)

Objective-C

MLKCustomImageLabelerOptions *options =
    [[MLKCustomImageLabelerOptions alloc] initWithLocalModel:localModel];
options.confidenceThreshold = @(0.0);
MLKImageLabeler *imageLabeler =
    [MLKImageLabeler imageLabelerWithOptions:options];

Wenn Sie ein remote gehostetes Modell haben, müssen Sie prüfen, ob es heruntergeladen wurde, bevor Sie es ausführen. Sie können den Status der Modelldownloadaufgabe mit der Methode isModelDownloaded(remoteModel:) des Modellmanagers prüfen.

Sie müssen dies zwar nur vor dem Ausführen des Labelerstellers bestätigen, aber wenn Sie sowohl ein remote gehostetes Modell als auch ein lokal gebündeltes Modell haben, kann es sinnvoll sein, diese Prüfung bei der Instanziierung von ImageLabeler durchzuführen: Erstellen Sie einen Labeler für das Remote-Modell, wenn es heruntergeladen wurde, und anschließend aus dem lokalen Modell.

Swift

var options: CustomImageLabelerOptions!
if (ModelManager.modelManager().isModelDownloaded(remoteModel)) {
  options = CustomImageLabelerOptions(remoteModel: remoteModel)
} else {
  options = CustomImageLabelerOptions(localModel: localModel)
}
options.confidenceThreshold = NSNumber(value: 0.0)
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.0);
MLKImageLabeler *imageLabeler =
    [MLKImageLabeler imageLabelerWithOptions:options];

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

Sie können den Downloadstatus des Modells abrufen, indem Sie Betrachter an das Standardbenachrichtigungscenter anhängen. Achten Sie darauf, einen schwachen Verweis auf self im Beobachterblock zu verwenden, da Downloads einige Zeit in Anspruch nehmen können und das ursprüngliche Objekt freigegeben werden kann, wenn der Download abgeschlossen ist. Beispiel:

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

2. Eingabebild vorbereiten

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

Wenn Sie UIImage verwenden, gehen Sie so vor:

  • Erstellen Sie ein VisionImage-Objekt mit UIImage. Achten Sie darauf, die richtige .orientation anzugeben.

    Swift

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

    Objective-C

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

Wenn Sie CMSampleBuffer verwenden, gehen Sie so vor:

  • Gib die Ausrichtung der Bilddaten an, die in CMSampleBuffer enthalten sind.

    So erhalten Sie die Bildausrichtung:

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

3. Image-Labelersteller ausführen

Übergeben Sie das Objekt image an die Methode process() von ImageLabeler, um Objekte in einem Bild mit Labels zu versehen.

Asynchron:

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 *_Nullable labels,
                   NSError *_Nullable error) {
        if (label.count == 0) {
            // Handle the error.
            return;
        }
        // Show results.
     }];

Synchron:

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 *labels =
    [imageLabeler resultsInImage:image error:&error];
// Show results or handle the error.

4. Informationen zu Elementen mit Labels abrufen

Wenn der Vorgang zur Labelerstellung für Bilder erfolgreich ist, wird ein Array von ImageLabel zurückgegeben. Jeder ImageLabel steht für etwas, das im Bild mit einem Label versehen wurde. Sie können die Textbeschreibung jedes Labels (sofern in den Metadaten der TensorFlow Lite-Modelldatei verfügbar), den Konfidenzwert und den Index abrufen. Beispiel:

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

Tipps zur Verbesserung der Echtzeitleistung

Wenn Sie Bilder in einer Echtzeitanwendung mit Labels versehen möchten, beachten Sie die folgenden Best Practices, um die besten Framerates zu erzielen:

  • Verwenden Sie zur Verarbeitung von Videoframes die synchrone API des Detektors results(in:). Rufen Sie diese Methode über die captureOutput(_, didOutput:from:)-Funktion der AVCaptureVideoDataOutputSampleBufferDelegate auf, um synchron Ergebnisse aus dem angegebenen Videoframe zu erhalten. Behalten Sie alwaysDiscardsLateVideoFrames von AVCaptureVideoDataOutput als true bei, um Aufrufe an den Detektor zu drosseln. Wenn während der Ausführung des Detektors ein neuer Videoframe verfügbar ist, wird er verworfen.
  • Wenn Sie die Ausgabe des Detektors verwenden, um Grafiken auf dem Eingabebild einzublenden, rufen Sie zuerst das Ergebnis aus ML Kit ab und rendern dann das Bild und das Overlay in einem einzigen Schritt. Dadurch wird die Anzeige für jeden verarbeiteten Eingabeframe nur einmal auf der Anzeigeoberfläche gerendert. Ein Beispiel findest du in der Kurzanleitung zu updatePreviewOverlayViewWithLastFrame.