Oznaczanie obrazów etykietami za pomocą modelu wytrenowanego w AutoML na iOS

Po wytrenowaniu własnego modelu za pomocą AutoML Vision Edge, możesz używać go w aplikacji do oznaczania obrazów etykietami.

Modele wytrenowane w AutoML Vision Edge można zintegrować na 2 sposoby. Możesz połączyć model, kopiując jego pliki do projektu Xcode, lub pobrać go dynamicznie z Firebase.

Opcje łączenia modeli
Połączony z aplikacją
  • Model jest częścią pakietu.
  • Model jest dostępny od razu, nawet gdy urządzenie z iOS jest offline.
  • Nie musisz mieć projektu w Firebase.
Hostowany w Firebase

Wypróbuj

Zanim zaczniesz

1. Dodaj biblioteki ML Kit do pliku Podfile:

Aby połączyć model z aplikacją:
    pod 'GoogleMLKit/ImageLabelingAutoML'
    
Aby dynamicznie pobrać model z Firebase, dodaj zależność LinkFirebase:
    pod 'GoogleMLKit/ImageLabelingAutoML'
    pod 'GoogleMLKit/LinkFirebase'
    
2. Po zainstalowaniu lub zaktualizowaniu podów projektu otwórz projekt Xcode za pomocą pliku .xcworkspacecode>. ML Kit jest obsługiwany w Xcode w wersji 13.2.1 lub nowszej. 3. Jeśli chcesz pobrać model, upewnij się, że dodasz Firebase do projektu iOS, jeśli jeszcze tego nie zrobisz. Nie jest to wymagane, gdy łączysz model.

1. Wczytaj model

Skonfiguruj lokalne źródło modelu

Aby połączyć model z aplikacją:

1. Wyodrębnij model i jego metadane z archiwum ZIP pobranego z konsoli Firebase do folderu:
    your_model_directory
      |____dict.txt
      |____manifest.json
      |____model.tflite
    
Wszystkie 3 pliki muszą znajdować się w tym samym folderze. Zalecamy używanie plików w postaci pobranej, bez modyfikacji (w tym nazw plików).

2. Skopiuj folder do projektu Xcode, pamiętając, aby wybrać opcję Create folder references (Utwórz odniesienia do folderów). Plik modelu i metadane zostaną uwzględnione w pakiecie aplikacji i będą dostępne w ML Kit.

3. Utwórz obiekt AutoMLImageLabelerLocalModel, podając ścieżkę do pliku manifestu modelu:

Swift

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

Objective-C

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

Skonfiguruj źródło modelu hostowanego w Firebase

Aby użyć modelu hostowanego zdalnie, utwórz obiekt AutoMLImageLabelerRemoteModel, podając nazwę przypisaną do modelu podczas jego publikowania:

Swift

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

Objective-C

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

Następnie rozpocznij pobieranie modelu, określając warunki, w których chcesz zezwolić na pobieranie. Jeśli model nie znajduje się na urządzeniu lub jest dostępna jego nowsza wersja, zadanie asynchronicznie pobierze model z Firebase:

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

Wiele aplikacji rozpoczyna pobieranie w kodzie inicjującym, ale możesz to zrobić w dowolnym momencie przed użyciem modelu.

Utwórz etykietę obrazu na podstawie modelu

Po skonfigurowaniu źródeł modelu utwórz na jego podstawie obiekt ImageLabeler.

Jeśli masz tylko model połączony lokalnie, utwórz etykietę na podstawie obiektu AutoMLImageLabelerLocalModel i skonfiguruj próg wyniku wiarygodności , który chcesz wymagać (patrz Ocena modelu):

Swift

let options = AutoMLImageLabelerOptions(localModel: localModel)
options.confidenceThreshold = NSNumber(value: 0.0)  // Evaluate your model in the Firebase console
                                                    // to determine an appropriate value.
let imageLabeler = ImageLabeler.imageLabeler(options: options)

Objective-C

MLKAutoMLImageLabelerOptions *options =
    [[MLKAutoMLImageLabelerOptions alloc] initWithLocalModel:localModel];
options.confidenceThreshold = @(0.0);  // Evaluate your model in the Firebase console
                                       // to determine an appropriate value.
MLKImageLabeler *imageLabeler =
    [MLKImageLabeler imageLabelerWithOptions:options];

Jeśli masz model hostowany zdalnie, przed jego uruchomieniem musisz sprawdzić, czy został pobrany. Stan pobierania modelu możesz sprawdzić za pomocą metody isModelDownloaded(remoteModel:) menedżera modeli.

Chociaż musisz to potwierdzić tylko przed uruchomieniem etykiety, jeśli masz zarówno model hostowany zdalnie, jak i model połączony lokalnie, warto przeprowadzić to sprawdzenie podczas tworzenia instancji ImageLabeler: utwórz etykietę na podstawie modelu zdalnego, jeśli został pobrany, a w przeciwnym razie na podstawie modelu lokalnego.

Swift

var options: AutoMLImageLabelerOptions!
if (ModelManager.modelManager().isModelDownloaded(remoteModel)) {
  options = AutoMLImageLabelerOptions(remoteModel: remoteModel)
} else {
  options = AutoMLImageLabelerOptions(localModel: localModel)
}
options.confidenceThreshold = NSNumber(value: 0.0)  // Evaluate your model in the Firebase console
                                                    // to determine an appropriate value.
let imageLabeler = ImageLabeler.imageLabeler(options: options)

Objective-C

MLKAutoMLImageLabelerOptions *options;
if ([[MLKModelManager modelManager] isModelDownloaded:remoteModel]) {
  options = [[MLKAutoMLImageLabelerOptions alloc] initWithRemoteModel:remoteModel];
} else {
  options = [[MLKAutoMLImageLabelerOptions alloc] initWithLocalModel:localModel];
}
options.confidenceThreshold = @(0.0);  // Evaluate your model in the Firebase console
                                       // to determine an appropriate value.
MLKImageLabeler *imageLabeler =
    [MLKImageLabeler imageLabelerWithOptions:options];

Jeśli masz tylko model hostowany zdalnie, wyłącz funkcje związane z modelem – na przykład wyszarz lub ukryj część interfejsu – dopóki nie potwierdzisz, że model został pobrany.

Stan pobierania modelu możesz sprawdzić, dołączając obserwatorów do domyślnego centrum powiadomień. Pamiętaj, aby w bloku obserwatora używać słabego odniesienia do self, ponieważ pobieranie może potrwać dłuższy czas, a obiekt źródłowy może zostać zwolniony, zanim pobieranie się zakończy. Na przykład:

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. Przygotuj obraz wejściowy

Utwórz obiekt VisionImage za pomocą UIImage lub CMSampleBuffer.

Jeśli używasz UIImage, wykonaj te czynności:

  • Utwórz obiekt VisionImage za pomocą UIImage. Pamiętaj, aby określić prawidłową wartość .orientation.

    Swift

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

    Objective-C

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

Jeśli używasz CMSampleBuffer, wykonaj te czynności:

  • Określ orientację danych obrazu zawartych w CMSampleBuffer.

    Aby uzyskać orientację obrazu:

    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;
      }
    }
          
  • Utwórz obiekt VisionImage za pomocą obiektu CMSampleBuffer i orientacji:

    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. Uruchom etykietę obrazu

Asynchronicznie:

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

Synchronicznie:

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. Uzyskaj informacje o oznaczonych obiektach

Jeśli operacja oznaczania obrazu etykietami się powiedzie, zwróci tablicę ImageLabel. Każda ImageLabel reprezentuje coś, co zostało oznaczone na obrazie. Możesz uzyskać tekstowy opis każdej etykiety (jeśli jest dostępny w metadanych pliku modelu TensorFlow Lite), wynik wiarygodności i indeks. Na przykład:

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

Wskazówki dotyczące zwiększania skuteczności w czasie rzeczywistym

Jeśli chcesz oznaczać obrazy etykietami w aplikacji działającej w czasie rzeczywistym, postępuj zgodnie z tymi wskazówkami, aby uzyskać najlepszą liczbę klatek na sekundę:

  • Do przetwarzania klatek wideo użyj synchronicznego interfejsu API detektora results(in:). Wywołaj tę metodę z funkcji AVCaptureVideoDataOutputSampleBufferDelegate's captureOutput(_, didOutput:from:) protokołu, aby synchronicznie uzyskać wyniki z danej klatki wideo. Ustaw wartość alwaysDiscardsLateVideoFrames protokołu AVCaptureVideoDataOutput na true, aby ograniczyć wywołania detektora. Jeśli podczas działania detektora pojawi się nowa klatka wideo, zostanie ona pominięta.
  • Jeśli używasz danych wyjściowych detektora do nakładania grafiki na obraz wejściowy, najpierw uzyskaj wynik z ML Kit, a następnie w jednym kroku wyrenderuj obraz i nałóż na niego grafikę. Dzięki temu renderujesz na powierzchni wyświetlacza tylko raz dla każdej przetworzonej klatki wejściowej. Przykład znajdziesz w funkcji updatePreviewOverlayViewWithLastFrame w krótkim wprowadzeniu do ML Kit.