Oznaczaj obrazy etykietami z użyciem modelu wytrenowanego przez AutoML na urządzeniu z iOS

Po wytrenowaniu własnego modelu z użyciem AutoML Vision Edge możesz użyć go w aplikacji do oznaczania obrazów etykietami.

Istnieją 2 sposoby integracji modeli wytrenowanych w AutoML Vision Edge. Możesz połączyć model, kopiując jego pliki do projektu Xcode, lub dynamicznie pobierać go z Firebase.

Opcje grupowania modeli
Grupowanie w Twojej aplikacji
  • Model jest częścią pakietu
  • Model jest natychmiast dostępny, nawet jeśli urządzenie z iOS jest offline
  • Nie potrzeba projektu Firebase
Hostowane w Firebase

Wypróbuj

Zanim zaczniesz

1. Dodaj biblioteki ML Kit w pliku Podfile:

Aby połączyć model w pakiet z aplikacją:
    pod 'GoogleMLKit/ImageLabelingAutoML'
    
Aby dynamicznie pobierać 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, używając jego .xcworkspacekodu>. Pakiet ML Kit jest obsługiwany w Xcode w wersji 13.2.1 lub nowszej. 3. Jeśli chcesz pobrać model, dodaj Firebase do projektu iOS, jeśli jeszcze nie zostało to zrobione. Nie jest to wymagane podczas pakowania modelu.

1. Wczytywanie modelu

Skonfiguruj źródło modelu lokalnego

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 takiej postaci, w jakiej zostały pobrane, bez modyfikacji (w tym nazw plików).

2. Skopiuj folder do projektu Xcode, zaznaczając przy tym opcję Utwórz odwołania do folderów. Plik modelu i metadane zostaną dołączone do pakietu 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];

Skonfiguruj źródło modelu hostowane w Firebase

Aby użyć modelu hostowanego zdalnie, utwórz obiekt AutoMLImageLabelerRemoteModel z nazwą przypisaną do modelu w momencie jego opublikowania:

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 zadanie pobierania modelu, określając warunki, które mają mieć wpływ na pobieranie. Jeśli modelu nie ma na urządzeniu lub dostępna jest 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 zadanie pobierania w kodzie inicjowania, ale możesz to zrobić w dowolnym momencie, zanim będzie można używać modelu.

Tworzenie osoby oznaczającej obrazy na podstawie modelu

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

Jeśli masz tylko model dołączony lokalnie, po prostu utwórz osobę oznaczającą etykietami na podstawie obiektu AutoMLImageLabelerLocalModel i skonfiguruj wymagany próg ufności (zobacz Ocena trybu:

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ł on pobrany. Stan zadania pobierania modelu możesz sprawdzić za pomocą metody isModelDownloaded(remoteModel:) menedżera modeli.

Chociaż musisz to potwierdzić przed uruchomieniem narzędzia do etykietowania, jeśli masz zarówno model hostowany zdalnie, jak i model połączony lokalnie, warto to sprawdzić podczas tworzenia wystąpienia obiektu ImageLabeler: utwórz etykietę z modelu zdalnego (jeśli został on pobrany) lub z modelu lokalnego, w przeciwnym razie.

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 nim – na przykład wyszarzanie lub ukrycie części interfejsu użytkownika do czasu potwierdzenia, że model został pobrany.

Stan pobierania modelu możesz uzyskać, 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 trochę potrwać, a obiekt źródłowy może zostać zwolniony do czasu zakończenia pobierania. 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 konta UIImage, wykonaj te czynności:

  • Utwórz obiekt VisionImage z UIImage. Pamiętaj, aby podać prawidłowy atrybut .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 konta CMSampleBuffer, wykonaj te czynności:

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

    Aby określić orientację zdjęcia:

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

Synchronizacja:

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 obiektach z etykietami

Jeśli operacja oznaczania obrazów etykietami się powiedzie, zwróci tablicę ImageLabel. Każdy element ImageLabel reprezentuje coś, co zostało oznaczone na obrazie. Możesz uzyskać opis tekstowy 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ć etykietami obrazy w aplikacji czasu rzeczywistego, postępuj zgodnie z tymi wskazówkami, aby uzyskać najlepszą liczbę klatek:

  • Do przetwarzania klatek wideo użyj synchronicznego interfejsu API results(in:) wzorca. Wywołaj tę metodę z funkcji captureOutput(_, didOutput:from:) obiektu AVCaptureVideoDataOutputSampleBufferDelegate, aby synchronicznie pobierać wyniki z danej klatki wideo. Pozostaw alwaysDiscardsLateVideoFrames instancji AVCaptureVideoDataOutput jako true, aby ograniczyć wywołania do wzorca. Jeśli podczas działania wykrywacza pojawi się nowa klatka wideo, zostanie usunięta.
  • Jeśli używasz danych wyjściowych detektora do nakładania grafiki na obraz wejściowy, najpierw pobierz wynik z ML Kit, a następnie wyrenderuj obraz i nakładkę w jednym kroku. Dzięki temu renderujesz na wyświetlaczu tylko raz dla każdej przetworzonej ramki wejściowej. Przykład znajdziesz w sekcji updatePreviewOverlayViewWithLastFrame w krótkim wprowadzeniu do ML Kit.