Etykietowanie obrazów za pomocą modelu wytrenowanego w AutoML na urządzeniach z 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. Model możesz spakować, kopiując jego pliki do projektu Xcode, lub pobrać go dynamicznie z Firebase.

Opcje pakietów modeli
Dołączone do aplikacji
  • Model jest częścią pakietu
  • Model jest dostępny od razu, nawet gdy urządzenie z iOS jest offline.
  • Nie musisz mieć projektu Firebase
Hostowane w Firebase
  • Hostuj model, przesyłając go do Firebase Machine Learning.
  • Zmniejsza rozmiar pakietu aplikacji
  • Model jest pobierany na żądanie
  • Wysyłanie aktualizacji modelu bez ponownego publikowania aplikacji
  • Łatwe testy A/B za pomocą Zdalnej konfiguracji Firebase
  • Wymaga projektu Firebase

Wypróbuj

Zanim zaczniesz

1. Dodaj biblioteki ML Kit do pliku Podfile:

Aby dołączyć model do aplikacji:
    pod 'GoogleMLKit/ImageLabelingAutoML'
    
Aby dynamicznie pobierać model z Firebase, dodaj zależność LinkFirebase:
    pod 'GoogleMLKit/ImageLabelingAutoML'
    pod 'GoogleMLKit/LinkFirebase'
    
2. Po zainstalowaniu lub zaktualizowaniu Pods w projekcie otwórz projekt Xcode za pomocą polecenia .xcworkspacecode>. ML Kit jest obsługiwany w Xcode w wersji 13.2.1 lub nowszej. 3. Jeśli chcesz pobrać model, pamiętaj, aby dodać Firebase do projektu iOS, jeśli jeszcze tego nie zrobisz. Nie jest to wymagane, gdy pakujesz model.

1. Wczytywanie modelu

Konfigurowanie źródła modelu lokalnego

Aby połączyć model z aplikacją:

1. Wyodrębnij model i jego metadane z pobranego z konsoli Firebase archiwum ZIP 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, w jakiej zostały pobrane, bez modyfikacji (w tym nazw plików).

2. Skopiuj folder do projektu Xcode, pamiętając, aby wybrać opcję Utwórz odwołania do folderów. Plik modelu i metadane zostaną uwzględnione w pakiecie aplikacji i będą dostępne dla 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];

Konfigurowanie źródła modelu hostowanego w Firebase

Aby użyć modelu hostowanego zdalnie, utwórz obiekt AutoMLImageLabelerRemoteModel, podając nazwę, którą przypisano do modelu podczas 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 jakich chcesz zezwolić na pobieranie. Jeśli modelu nie ma na urządzeniu lub dostępna jest 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.

Tworzenie narzędzia do oznaczania obrazów na podstawie modelu

Po skonfigurowaniu źródeł modelu utwórz z jednego z nich obiekt ImageLabeler.

Jeśli masz tylko model lokalny, utwórz narzędzie do etykietowania z obiektu AutoMLImageLabelerLocalModel i skonfiguruj wymagany próg poziomu ufności (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 potwierdzić to tylko przed uruchomieniem narzędzia do etykietowania, jeśli masz zarówno model hostowany zdalnie, jak i model dołączony lokalnie, warto przeprowadzić to sprawdzenie podczas tworzenia instancji narzędzia do etykietowania: utwórz narzędzie do etykietowania na podstawie modelu zdalnego, jeśli został on pobrany, a w przeciwnym razie na podstawie modelu lokalnego.ImageLabeler

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, np. wyszarz lub ukryj część interfejsu, dopóki nie potwierdzisz, że model został pobrany.

Stan pobierania modelu możesz uzyskać, dołączając obserwatorów do domyślnego Centrum powiadomień. W bloku obserwatora używaj słabego odwołania do self, ponieważ pobieranie może potrwać, 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. Przygotowywanie obrazu wejściowego

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

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

  • Utwórz obiekt VisionImage z wartością UIImage. Pamiętaj, aby podać prawidłowy .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 elemencie 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, używając 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. Uruchamianie narzędzia do etykietowania obrazów

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. Uzyskiwanie informacji o oznaczonych obiektach

Jeśli operacja etykietowania obrazu się powiedzie, zwróci tablicę obiektów ImageLabel. Każdy symbol 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), wskaźnik ufnoś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 poprawy skuteczności w czasie rzeczywistym

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

  • Do przetwarzania klatek wideo użyj results(in:)synchronicznego interfejsu API detektora. Wywołaj tę metodę z funkcji AVCaptureVideoDataOutputSampleBufferDelegate captureOutput(_, didOutput:from:), aby synchronicznie uzyskać wyniki z danej klatki filmu. Ustaw AVCaptureVideoDataOutput's alwaysDiscardsLateVideoFrames na true, aby ograniczyć liczbę wywołań detektora. Jeśli podczas działania detektora pojawi się nowa klatka wideo, zostanie ona odrzucona.
  • 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świetlania tylko raz dla każdej przetworzonej klatki wejściowej. Przykład znajdziesz w funkcji updatePreviewOverlayViewWithLastFrame w przykładowym projekcie ML Kit.