Leitfaden zur Bildsegmentierung für iOS

Mit der Aufgabe „Bildsegmentierung“ können Sie Bilder anhand vordefinierter Kategorien in Regionen unterteilen und visuelle Effekte wie Hintergrundunschärfe anwenden. In dieser Anleitung erfahren Sie, wie Sie den Bildsegmenter mit iOS-Apps verwenden.

Das in dieser Anleitung beschriebene Codebeispiel ist auf GitHub verfügbar.

In der Webdemo können Sie sich diese Aufgabe in Aktion ansehen. Weitere Informationen zu den Funktionen, Modellen und Konfigurationsoptionen dieser Aufgabe finden Sie in der Übersicht.

Codebeispiel

Das Codebeispiel für MediaPipe Tasks enthält eine einfache Implementierung einer Bildsegmentierungs-App für iOS.

Im Beispiel wird ein Bildsegmenter implementiert, der Kategoriemasken ausgibt. Dabei wird die Kamera eines iOS-Geräts verwendet, um die Bildsegmentierung in einem Live-Kamerafeed oder in Bildern und Videos aus der Gerätegalerie durchzuführen.

Sie können die App als Ausgangspunkt für Ihre eigene iOS-App verwenden oder sich an ihr orientieren, wenn Sie eine vorhandene App ändern. Der Beispielcode für den Bildsegmenter wird auf GitHub gehostet.

Code herunterladen

In der folgenden Anleitung wird beschrieben, wie Sie mit dem Befehlszeilentool git eine lokale Kopie des Beispielcodes erstellen.

So laden Sie den Beispielcode herunter:

  1. Klonen Sie das Git-Repository mit dem folgenden Befehl:

    git clone https://github.com/google-ai-edge/mediapipe-samples/
    
  2. Optional können Sie Ihre Git-Instanz für die Verwendung einer spärlichen Überprüfung konfigurieren, sodass nur die Dateien für die Beispielanwendung „Image Segmenter“ vorhanden sind:

    cd mediapipe
    git sparse-checkout init --cone
    git sparse-checkout set examples/image_segmentation/ios/
    

Nachdem Sie eine lokale Version des Beispielcodes erstellt haben, können Sie die MediaPipe-Aufgabenbibliothek installieren, das Projekt mit Xcode öffnen und die App ausführen. Eine Anleitung finden Sie im Einrichtungsleitfaden für iOS.

Schlüsselkomponenten

Die folgenden Dateien enthalten den wichtigsten Code für die Beispielanwendung „Image Segmenter“:

  • ImageSegmenterService.swift: Initialisiert den Bildsegmenter, verarbeitet die Modellauswahl und führt Inferenzen auf den Eingabedaten aus.
  • CameraViewController.swift: Implementiert die Benutzeroberfläche für den Eingabemodus des Live-Kamerafeeds und visualisiert die Ergebnisse.
  • MediaLibraryViewController.swift: Hier wird die Benutzeroberfläche für den Eingabemodus für Standbilder und Videodateien implementiert und die Ergebnisse werden visualisiert.

Einrichtung

In diesem Abschnitt werden die wichtigsten Schritte zur Einrichtung Ihrer Entwicklungsumgebung und Codeprojekte für die Verwendung des Bildsegmentierungstools beschrieben. Allgemeine Informationen zum Einrichten Ihrer Entwicklungsumgebung für die Verwendung von MediaPipe-Aufgaben, einschließlich Anforderungen an die Plattformversion, finden Sie im Einrichtungsleitfaden für iOS.

Abhängigkeiten

Der Bildsegmenter verwendet die MediaPipeTasksVision-Bibliothek, die mit CocoaPods installiert werden muss. Die Bibliothek ist sowohl mit Swift- als auch mit Objective-C-Apps kompatibel und erfordert keine zusätzliche sprachspezifische Einrichtung.

Eine Anleitung zum Installieren von CocoaPods unter macOS findest du in der Installationsanleitung für CocoaPods. Eine Anleitung zum Erstellen einer Podfile mit den erforderlichen Pods für Ihre App finden Sie unter CocoaPods verwenden.

Fügen Sie den Pod „MediaPipeTasksVision“ in der Datei Podfile mit dem folgenden Code hinzu:

target 'MyImageSegmenterApp' do
  use_frameworks!
  pod 'MediaPipeTasksVision'
end

Wenn Ihre App Unit-Testziele enthält, finden Sie im Einrichtungsleitfaden für iOS weitere Informationen zur Einrichtung Ihrer Podfile.

Modell

Für die MediaPipe-Aufgabe „Bildsegmentierung“ ist ein trainiertes Modell erforderlich, das mit dieser Aufgabe kompatibel ist. Weitere Informationen zu den verfügbaren trainierten Modellen für den Bildsegmenter finden Sie in der Aufgabenübersicht im Abschnitt „Modelle“.

Wählen Sie ein Modell aus, laden Sie es herunter und fügen Sie es mit Xcode Ihrem Projektverzeichnis hinzu. Eine Anleitung zum Hinzufügen von Dateien zu Ihrem Xcode-Projekt finden Sie unter Dateien und Ordner in Ihrem Xcode-Projekt verwalten.

Verwenden Sie die Property BaseOptions.modelAssetPath, um den Pfad zum Modell in Ihrem App-Bundle anzugeben. Ein Codebeispiel finden Sie im nächsten Abschnitt.

Aufgabe erstellen

Sie können die Aufgabe „Image Segmenter“ erstellen, indem Sie einen ihrer Initialisierer aufrufen. Der ImageSegmenter(options:)-Initialisierer akzeptiert Werte für die Konfigurationsoptionen.

Wenn Sie keinen Image Segmenter benötigen, der mit benutzerdefinierten Konfigurationsoptionen initialisiert wird, können Sie mit der ImageSegmenter(modelPath:)-Initialisierung einen Image Segmenter mit den Standardoptionen erstellen. Weitere Informationen zu den Konfigurationsoptionen finden Sie unter Konfigurationsübersicht.

Die Aufgabe „Bildsegmentierung“ unterstützt drei Eingabedatentypen: Standbilder, Videodateien und Live-Videostreams. Standardmäßig initialisiert ImageSegmenter(modelPath:) eine Aufgabe für Standbilder. Wenn Sie möchten, dass Ihre Aufgabe für die Verarbeitung von Videodateien oder Live-Videostreams initialisiert wird, geben Sie mit ImageSegmenter(options:) den Ausführungsmodus für Video oder Livestream an. Für den Livestream-Modus ist außerdem die zusätzliche Konfigurationsoption imageSegmenterLiveStreamDelegate erforderlich, mit der der Bildsegmenter die Ergebnisse der Bildsegmentierung asynchron an den delegierten Dienst senden kann.

Wählen Sie den Tab für den aktuellen Ausführungsmodus aus, um zu erfahren, wie Sie die Aufgabe erstellen und die Inferenz ausführen.

Swift

Bild

import MediaPipeTasksVision

let modelPath = Bundle.main.path(forResource: "model",
                                      ofType: "tflite")

let options = ImageSegmenterOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .image
options.shouldOutputCategoryMask = true
options.shouldOutputConfidenceMasks = false

let imageSegmenter = try ImageSegmenter(options: options)
    

Video

import MediaPipeTasksVision

let modelPath = Bundle.main.path(forResource: "model",
                                      ofType: "tflite")

let options = ImageSegmenterOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .video
options.shouldOutputCategoryMask = true
options.shouldOutputConfidenceMasks = false

let imageSegmenter = try ImageSegmenter(options: options)
    

Livestream

import MediaPipeTasksVision

// Class that conforms to the `imageSegmenterLiveStreamDelegate` protocol and
// implements the method that the image segmenter calls once it finishes
// performing segmentation of each input frame.
class ImageSegmenterResultProcessor: NSObject, ImageSegmenterLiveStreamDelegate {

  func imageSegmenter(
    _ imageSegmenter: ImageSegmenter,
    didFinishSegmentation result: ImageSegmenterResult?,
    timestampInMilliseconds: Int,
    error: Error?) {

    // Process the image segmentation result or errors here.

  }
}

let modelPath = Bundle.main.path(forResource: "model",
                                      ofType: "tflite")

let options = ImageSegmenterOptions()
options.baseOptions.modelAssetPath = modelPath
options.runningMode = .liveStream
options.shouldOutputCategoryMask = true
options.shouldOutputConfidenceMasks = false

// Set `imageSegmenterLiveStreamDelegate` to the object of the class that
// confirms to the `ImageSegmenterLiveStreamDelegate` protocol.
let processor = ImageSegmenterResultProcessor()
options.imageSegmenterLiveStreamDelegate = processor

let imageSegmenter = try ImageSegmenter(options: options)
    

Objective-C

Bild

@import MediaPipeTasksVision;

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];

MPPImageSegmenterOptions *options = [[MPPImageSegmenterOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeImage;
options.shouldOutputCategoryMask = YES;
options.shouldOutputConfidenceMasks = NO;

MPPImageSegmenter *imageSegmenter =
  [[MPPImageSegmenter alloc] initWithOptions:options error:nil];
    

Video

@import MediaPipeTasksVision;

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];

MPPImageSegmenterOptions *options = [[MPPImageSegmenterOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeVideo;
options.shouldOutputCategoryMask = YES;
options.shouldOutputConfidenceMasks = NO;

MPPImageSegmenter *imageSegmenter =
  [[MPPImageSegmenter alloc] initWithOptions:options error:nil];
    

Livestream

@import MediaPipeTasksVision;

// Class that conforms to the `MPPImageSegmenterLiveStreamDelegate` protocol
// and implements the method that the image segmenter calls once it finishes
// performing segmentation of each input frame.

@interface APPImageSegmenterResultProcessor : NSObject 

@end

@implementation APPImageSegmenterResultProcessor

-   (void)imageSegmenter:(MPPImageSegmenter *)imageSegmenter
    didFinishSegmentationWithResult:(MPPImageSegmenterResult *)imageSegmenterResult
         timestampInMilliseconds:(NSInteger)timestampInMilliseconds
                           error:(NSError *)error {

    // Process the image segmentation result or errors here.

}

@end

NSString *modelPath = [[NSBundle mainBundle] pathForResource:@"model"
                                                      ofType:@"tflite"];

MPPImageSegmenterOptions *options = [[MPPImageSegmenterOptions alloc] init];
options.baseOptions.modelAssetPath = modelPath;
options.runningMode = MPPRunningModeLiveStream;
options.shouldOutputCategoryMask = YES;
options.shouldOutputConfidenceMasks = NO;

// Set `imageSegmenterLiveStreamDelegate` to the object of the class that
// confirms to the `MPPImageSegmenterLiveStreamDelegate` protocol.
APPImageSegmenterResultProcessor *processor =
  [APPImageSegmenterResultProcessor new];
options.imageSegmenterLiveStreamDelegate = processor;

MPPImageSegmenter *imageSegmenter =
  [[MPPImageSegmenter alloc] initWithOptions:options error:nil];
    

Mit der Beispielcodeimplementierung des Bildsegmenters kann der Nutzer zwischen den Verarbeitungsmodi wechseln. Dieser Ansatz macht den Code zum Erstellen von Aufgaben komplizierter und ist möglicherweise nicht für Ihren Anwendungsfall geeignet.

Konfigurationsoptionen

Für diese Aufgabe stehen die folgenden Konfigurationsoptionen für iOS-Apps zur Verfügung:

Option Beschreibung Wertebereich Standardwert
runningMode Legt den Ausführungsmodus für die Aufgabe fest. Es gibt drei Modi:

IMAGE: Der Modus für Eingaben mit einem einzelnen Bild.

VIDEO: Der Modus für decodierte Frames eines Videos.

LIVE_STREAM: Der Modus für einen Livestream von Eingabedaten, z. B. von einer Kamera. In diesem Modus muss ImageSegmenterLiveStreamDelegate auf eine Instanz einer Klasse festgelegt sein, die die ImageSegmenterLiveStreamDelegate implementiert, um die Segmentierungsergebnisse asynchron zu empfangen.
{RunningMode.image, RunningMode.video, RunningMode.liveStream} RunningMode.image
shouldOutputCategoryMask Wenn dieser Wert auf True festgelegt ist, enthält die Ausgabe eine Segmentierungsmaske als uint8-Bild, bei der jeder Pixelwert den Wert der Gewinnerkategorie angibt. {True, False} False
shouldOutputConfidenceMasks Wenn dieser Parameter auf True festgelegt ist, enthält die Ausgabe eine Segmentierungsmaske als Bild mit Gleitkommawerten. Dabei steht jeder Gleitkommawert für die Konfidenzkarte der Kategorie. {True, False} True
displayNamesLocale Legt die Sprache der Labels fest, die für die Anzeigenamen verwendet werden, die in den Metadaten des Modells der Aufgabe angegeben sind, sofern verfügbar. Der Standardwert ist en für Englisch. Mit der TensorFlow Lite Metadata Writer API können Sie den Metadaten eines benutzerdefinierten Modells lokalisierte Labels hinzufügen. Gebietscode de
result_callback Legt fest, dass der Ergebnisempfänger die Segmentierungsergebnisse asynchron empfängt, wenn sich der Bildsegmenter im Modus LIVE_STREAM befindet. Kann nur verwendet werden, wenn der Ausführungsmodus auf LIVE_STREAM festgelegt ist.

Wenn der Ausführungsmodus auf LIVE_STREAM festgelegt ist, benötigt der Bildsegmenter die zusätzliche Konfigurationsoption imageSegmenterLiveStreamDelegate, mit der er die Ergebnisse der Bildsegmentierung asynchron liefern kann. Der Delegate muss die Methode imageSegmenter(_:didFinishSegmentation:timestampInMilliseconds:error:) implementieren, die vom Bildsegmentierungstool aufgerufen wird, nachdem die Ergebnisse der Segmentierung jedes Frames verarbeitet wurden.

Optionsname Beschreibung Wertebereich Standardwert
imageSegmenterLiveStreamDelegate Ermöglicht es dem Bildsegmentierungstool, die Ergebnisse der Bildsegmentierung asynchron im Livestream-Modus zu erhalten. Die Klasse, deren Instanz auf diese Property festgelegt ist, muss die Methode imageSegmenter(_:didFinishSegmentation:timestampInMilliseconds:error:) implementieren. Nicht zutreffend Nicht festgelegt

Daten vorbereiten

Sie müssen das Eingabebild oder den Eingabeframe in ein MPImage-Objekt konvertieren, bevor Sie es an den Bildsegmenter übergeben. MPImage unterstützt verschiedene Arten von iOS-Bildformaten und kann sie in jedem Betriebsmodus für die Inferenz verwenden. Weitere Informationen zu MPImage finden Sie in der MPImage API.

Wählen Sie ein iOS-Bildformat entsprechend Ihrem Anwendungsfall und dem erforderlichen Ausführungsmodus aus.MPImage unterstützt die iOS-Bildformate UIImage, CVPixelBuffer und CMSampleBuffer.

UIImage

Das UIImage-Format eignet sich gut für die folgenden Laufmodi:

  • Bilder: Bilder aus einem App-Bundle, einer Nutzergalerie oder einem Dateisystem, das als UIImage-Bild formatiert ist, können in ein MPImage-Objekt umgewandelt werden.

  • Videos: Mit dem AVAssetImageGenerator können Sie Videoframes im CGImage-Format extrahieren und dann in UIImage-Bilder konvertieren.

Swift

// Load an image on the user's device as an iOS `UIImage` object.

// Convert the `UIImage` object to a MediaPipe's Image object having the default
// orientation `UIImage.Orientation.up`.
let image = try MPImage(uiImage: image)
    

Objective-C

// Load an image on the user's device as an iOS `UIImage` object.

// Convert the `UIImage` object to a MediaPipe's Image object having the default
// orientation `UIImageOrientationUp`.
MPImage *image = [[MPPImage alloc] initWithUIImage:image error:nil];
    

Im Beispiel wird eine MPImage mit der Standardausrichtung UIImage.Orientation.Up initialisiert. Sie können ein MPImage mit einem beliebigen der unterstützten Werte von UIImage.Orientation initialisieren. Der Bildsegmenter unterstützt keine gespiegelten Ausrichtungen wie .upMirrored, .downMirrored, .leftMirrored und .rightMirrored.

Weitere Informationen zu UIImage finden Sie in der Apple Developer-Dokumentation zu UIImage.

CVPixelBuffer

Das CVPixelBuffer-Format eignet sich gut für Anwendungen, die Frames generieren und das iOS-Framework CoreImage zur Verarbeitung verwenden.

Das CVPixelBuffer-Format eignet sich gut für die folgenden Laufmodi:

  • Bilder: Apps, die nach einer Verarbeitung mit dem CoreImage-Framework von iOS CVPixelBuffer-Bilder generieren, können im Modus „Bild ausführen“ an den Bildsegmenter gesendet werden.

  • Videos: Videoframes können zur Verarbeitung in das CVPixelBuffer-Format konvertiert und dann im Videomodus an den Bildsegmenter gesendet werden.

  • Livestream: Frames, die von Apps mit einer iOS-Kamera generiert werden, können zur Verarbeitung in das CVPixelBuffer-Format konvertiert werden, bevor sie im Livestream-Modus an den Bildsegmenter gesendet werden.

Swift

// Obtain a CVPixelBuffer.

// Convert the `CVPixelBuffer` object to a MediaPipe's Image object having the default
// orientation `UIImage.Orientation.up`.
let image = try MPImage(pixelBuffer: pixelBuffer)
    

Objective-C

// Obtain a CVPixelBuffer.

// Convert the `CVPixelBuffer` object to a MediaPipe's Image object having the
// default orientation `UIImageOrientationUp`.
MPImage *image = [[MPPImage alloc] initWithUIImage:image error:nil];
    

Weitere Informationen zu CVPixelBuffer findest du in der CVPixelBuffer-Entwicklerdokumentation von Apple.

CMSampleBuffer

Im CMSampleBuffer-Format werden Mediensamples eines einheitlichen Medientyps gespeichert. Es eignet sich gut für den Livestream-Ausführungsmodus. Live-Frames von iOS-Kameras werden asynchron im CMSampleBuffer-Format von iOS AVCaptureVideoDataOutput bereitgestellt.

Swift

// Obtain a CMSampleBuffer.

// Convert the `CMSampleBuffer` object to a MediaPipe's Image object having the default
// orientation `UIImage.Orientation.up`.
let image = try MPImage(sampleBuffer: sampleBuffer)
    

Objective-C

// Obtain a `CMSampleBuffer`.

// Convert the `CMSampleBuffer` object to a MediaPipe's Image object having the
// default orientation `UIImageOrientationUp`.
MPImage *image = [[MPPImage alloc] initWithSampleBuffer:sampleBuffer error:nil];
    

Weitere Informationen zu CMSampleBuffer findest du in der CMSampleBuffer-Entwicklerdokumentation von Apple.

Aufgabe ausführen

Verwenden Sie zum Ausführen des Bildsegmentierungstools die segment()-Methode, die dem zugewiesenen Ausführungsmodus entspricht:

  • Standbild: segment(image:)
  • Video: segment(videoFrame:timestampInMilliseconds:)
  • Livestream: segmentAsync(image:timestampInMilliseconds:)

Die folgenden Codebeispiele zeigen einfache Beispiele für die Ausführung des Bildsegmentierungstools in diesen verschiedenen Ausführungsmodi:

Swift

Bild

let result = try imageSegmenter.segment(image: image)
    

Video

let result = try imageSegmenter.segment(
  videoFrame: image,
  timestampInMilliseconds: timestamp)
    

Livestream

try imageSegmenter.segmentAsync(
  image: image,
  timestampInMilliseconds: timestamp)
    

Objective-C

Bild

MPPImageSegmenterResult *result =
  [imageSegmenter segmentImage:image error:nil];
    

Video

MPPImageSegmenterResult *result =
  [imageSegmenter segmentVideoFrame:image
            timestampInMilliseconds:timestamp
                              error:nil];
    

Livestream

BOOL success =
  [imageSegmenter segmentAsyncImage:image
            timestampInMilliseconds:timestamp
                              error:nil];
    

Im Codebeispiel für den Bildsegmenter werden die Implementierungen der einzelnen Modi segment(image:), segment(videoFrame:timestampInMilliseconds:) und segmentAsync(image:timestampInMilliseconds:) genauer erläutert.

Wichtige Hinweise:

  • Wenn Sie den Video- oder Livestream-Modus verwenden, müssen Sie der Aufgabe „Bildsegmentierung“ auch den Zeitstempel des Eingabeframes angeben.

  • Wenn die Ausführung im Bild- oder Videomodus erfolgt, blockiert die Aufgabe „Bildsegmentierung“ den aktuellen Thread, bis die Verarbeitung des Eingabebilds oder ‑frames abgeschlossen ist. Um das Blockieren des aktuellen Threads zu vermeiden, führen Sie die Verarbeitung in einem Hintergrund-Thread mithilfe der iOS-Frameworks Dispatch oder NSOperation aus.

  • Wenn die Ausführung im Livestream-Modus erfolgt, gibt die Aufgabe „Bildsegmentierung“ sofort ein Ergebnis zurück und blockiert den aktuellen Thread nicht. Nach der Verarbeitung jedes Eingabeframes wird die Methode imageSegmenter(_:didFinishSegmentation:timestampInMilliseconds:error:) mit dem Bildsegmentierungstool aufgerufen. Der Bildsegmenter ruft diese Methode asynchron in einer speziellen seriellen Dispatch-Warteschlange auf. Wenn Sie die Ergebnisse auf der Benutzeroberfläche anzeigen möchten, senden Sie sie nach der Verarbeitung an die Hauptwarteschlange. Wenn die Funktion segmentAsync aufgerufen wird, während die Aufgabe „Bildsegmentierung“ gerade einen anderen Frame verarbeitet, wird der neue Eingabeframe vom Bildsegmentierungstool ignoriert.

Ergebnisse verarbeiten und anzeigen

Nach der Ausführung der Inferenz gibt die Aufgabe „Bildsegmentierung“ ein ImageSegmenterResult-Objekt zurück, das die Ergebnisse der Segmentierungsaufgabe enthält. Der Inhalt der Ausgabe hängt vom Ausgabetyp ab, den Sie bei der Konfiguration der Aufgabe festgelegt haben.

Die folgenden Bilder zeigen eine Visualisierung der Aufgabenausgabe für eine Kategoriewertmaske. Der Bereich der Kategoriemaske ist [0, 255] und jeder Pixelwert entspricht dem Index der Gewinnerkategorie der Modellausgabe. Der Index der Gewinnerkategorie hat die höchste Punktzahl unter den Kategorien, die das Modell erkennen kann.

Zwei Mädchen reiten auf einem Pferd und ein Mädchen geht neben dem Pferd Die Bildmaske, die die Form der Mädchen und des Pferdes aus dem vorherigen Bild umreißt. Die Form aller drei Mädchen und des Pferdes ist korrekt maskiert.

Originalbild und Kategoriemaskenausgabe. Quellbild aus dem Pascal VOC 2012-Dataset.

Im Beispielcode für den Bildsegmenter wird gezeigt, wie die Ergebnisse des Bildsegmenters angezeigt werden. Weitere Informationen finden Sie im Codebeispiel.