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

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

Zanim zaczniesz

  1. Jeśli nie masz jeszcze dodanej usługi Firebase do swojej aplikacji, wykonaj czynności opisane we wprowadzeniu.
  2. Dodaj biblioteki ML Kit do pliku Podfile:
    pod 'Firebase/MLVision', '6.25.0'
    pod 'Firebase/MLVisionAutoML', '6.25.0'
    
    Po zainstalowaniu lub zaktualizowaniu podów projektu pamiętaj, aby otworzyć projekt Xcode za pomocą pliku .xcworkspace.
  3. W aplikacji zaimportuj Firebase:

    Swift

    import Firebase

    Objective-C

    @import Firebase;

1. Wczytaj model

ML Kit uruchamia modele wygenerowane przez AutoML na urządzeniu. Możesz jednak skonfigurować ML Kit, aby ładował model zdalnie z Firebase, z pamięci lokalnej lub z obu tych miejsc.

Hostując model w Firebase, możesz go aktualizować bez publikowania nowej wersji aplikacji. Dodatkowo możesz korzystać ze Zdalnej konfiguracji i Testów A/B do dynamicznego udostępniania różnych modeli różnym zbiorom użytkowników.

Jeśli zdecydujesz się udostępniać model tylko przez hosting w Firebase, a nie spakować go z aplikacją, możesz zmniejszyć początkowy rozmiar pobieranej aplikacji. Pamiętaj jednak, że jeśli model nie jest połączony z aplikacją, funkcje związane z modelem nie będą dostępne, dopóki aplikacja nie pobierze modelu po raz pierwszy.

Jeśli połączysz model z aplikacją, będziesz mieć pewność, że jej funkcje systemów uczących się będą nadal działać, gdy model hostowany w Firebase będzie niedostępny.

Skonfiguruj źródło modelu hostowanego w Firebase

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

Swift

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

Objective-C

FIRAutoMLRemoteModel *remoteModel = [[FIRAutoMLRemoteModel 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 muszą zostać spełnione, aby można było pobierać dane. Jeśli modelu nie ma na urządzeniu lub jeśli jest dostępna jego nowsza wersja, zadanie pobierze go asynchronicznie z Firebase:

Swift

let downloadConditions = ModelDownloadConditions(
  allowsCellularAccess: true,
  allowsBackgroundDownloading: true
)

let downloadProgress = ModelManager.modelManager().download(
  remoteModel,
  conditions: downloadConditions
)

Objective-C

FIRModelDownloadConditions *downloadConditions =
    [[FIRModelDownloadConditions alloc] initWithAllowsCellularAccess:YES
                                         allowsBackgroundDownloading:YES];

NSProgress *downloadProgress =
    [[FIRModelManager modelManager] downloadRemoteModel:remoteModel
                                             conditions:downloadConditions];

Wiele aplikacji rozpoczyna zadanie pobierania w kodzie inicjowania, ale możesz to zrobić w dowolnym momencie, zanim trzeba będzie użyć modelu.

Skonfiguruj źródło modelu lokalnego

Aby połączyć model z aplikacją:

  1. Rozpakuj 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 korzystanie z plików w takiej postaci, w jakiej zostały pobrane, bez modyfikacji (dotyczy to również nazw plików).
  2. Skopiuj folder do projektu Xcode, zaznaczając opcję Utwórz odwołania do folderów. Plik modelu i metadane zostaną uwzględnione w pakiecie aplikacji i dostępne dla ML Kit.
  3. Utwórz obiekt AutoMLLocalModel, określając ścieżkę do pliku manifestu modelu:

    Swift

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

    Objective-C

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

Tworzenie osoby oznaczającej obrazy na podstawie modelu

Po skonfigurowaniu źródeł modeli utwórz na ich podstawie obiekt VisionImageLabeler.

Jeśli masz tylko model dołączony lokalnie, po prostu utwórz osobę oznaczającą etykietami na podstawie obiektu AutoMLLocalModel i skonfiguruj próg wskaźnika ufności, którego chcesz wymagać (zobacz Ocena modelu):

Swift

let options = VisionOnDeviceAutoMLImageLabelerOptions(localModel: localModel)
options.confidenceThreshold = 0  // Evaluate your model in the Firebase console
                                 // to determine an appropriate value.
let labeler = Vision.vision().onDeviceAutoMLImageLabeler(options: options)

Objective-C

FIRVisionOnDeviceAutoMLImageLabelerOptions *options =
    [[FIRVisionOnDeviceAutoMLImageLabelerOptions alloc] initWithLocalModel:localModel];
options.confidenceThreshold = 0;  // Evaluate your model in the Firebase console
                                  // to determine an appropriate value.
FIRVisionImageLabeler *labeler =
    [[FIRVision vision] onDeviceAutoMLImageLabelerWithOptions:options];

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

Chociaż musisz to potwierdzić przed uruchomieniem osoby oznaczającej etykietami, jeśli masz zarówno model hostowany zdalnie, jak i model umieszczony lokalnie, warto przeprowadzić tę kontrolę podczas tworzenia instancji VisionImageLabeler: utwórz osobę oznaczającą etykietami na podstawie modelu zdalnego (jeśli został pobrany), a w przeciwnym razie z modelu lokalnego.

Swift

var options: VisionOnDeviceAutoMLImageLabelerOptions?
if (ModelManager.modelManager().isModelDownloaded(remoteModel)) {
  options = VisionOnDeviceAutoMLImageLabelerOptions(remoteModel: remoteModel)
} else {
  options = VisionOnDeviceAutoMLImageLabelerOptions(localModel: localModel)
}
options.confidenceThreshold = 0  // Evaluate your model in the Firebase console
                                 // to determine an appropriate value.
let labeler = Vision.vision().onDeviceAutoMLImageLabeler(options: options)

Objective-C

VisionOnDeviceAutoMLImageLabelerOptions *options;
if ([[FIRModelManager modelManager] isModelDownloaded:remoteModel]) {
  options = [[FIRVisionOnDeviceAutoMLImageLabelerOptions alloc] initWithRemoteModel:remoteModel];
} else {
  options = [[FIRVisionOnDeviceAutoMLImageLabelerOptions alloc] initWithLocalModel:localModel];
}
options.confidenceThreshold = 0.0f;  // Evaluate your model in the Firebase console
                                     // to determine an appropriate value.
FIRVisionImageLabeler *labeler = [[FIRVision vision] onDeviceAutoMLImageLabelerWithOptions:options];

Jeśli masz tylko model hostowany zdalnie, wyłącz związane z nim funkcje – na przykład wyszarzanie lub ukrycie części interfejsu użytkownika – do czasu potwierdzenia pobrania modelu.

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ć uwolniony do zakończenia pobierania. Przykład:

Swift

NotificationCenter.default.addObserver(
    forName: .firebaseMLModelDownloadDidSucceed,
    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: .firebaseMLModelDownloadDidFail,
    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:FIRModelDownloadDidSucceedNotification
                object:nil
                 queue:nil
            usingBlock:^(NSNotification *_Nonnull note) {
              if (weakSelf == nil | note.userInfo == nil) {
                return;
              }
              __strong typeof(self) strongSelf = weakSelf;

              FIRRemoteModel *model = note.userInfo[FIRModelDownloadUserInfoKeyRemoteModel];
              if ([model.name isEqualToString:@"your_remote_model"]) {
                // The model was downloaded and is available on the device
              }
            }];

[NSNotificationCenter.defaultCenter
    addObserverForName:FIRModelDownloadDidFailNotification
                object:nil
                 queue:nil
            usingBlock:^(NSNotification *_Nonnull note) {
              if (weakSelf == nil | note.userInfo == nil) {
                return;
              }
              __strong typeof(self) strongSelf = weakSelf;

              NSError *error = note.userInfo[FIRModelDownloadUserInfoKeyError];
            }];

2. Przygotowywanie obrazu wejściowego

Następnie dla każdego obrazu, który chcesz oznaczyć etykietą, utwórz obiekt VisionImage, korzystając z jednej z opcji opisanych w tej sekcji, i przekaż go do instancji VisionImageLabeler (opisanej w następnej sekcji).

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

Aby użyć karty UIImage:

  1. W razie potrzeby obróć obraz, by jego właściwość imageOrientation miała wartość .up.
  2. Utwórz obiekt VisionImage przy użyciu prawidłowo wykonanej rotacji w UIImage. Nie określaj żadnych metadanych rotacji – należy użyć wartości domyślnej .topLeft.

    Swift

    let image = VisionImage(image: uiImage)

    Objective-C

    FIRVisionImage *image = [[FIRVisionImage alloc] initWithImage:uiImage];

Aby użyć karty CMSampleBufferRef:

  1. Utwórz obiekt VisionImageMetadata, który określa orientację danych obrazu zawartych w buforze CMSampleBufferRef.

    Aby sprawdzić orientację obrazu:

    Swift

    func imageOrientation(
        deviceOrientation: UIDeviceOrientation,
        cameraPosition: AVCaptureDevice.Position
        ) -> VisionDetectorImageOrientation {
        switch deviceOrientation {
        case .portrait:
            return cameraPosition == .front ? .leftTop : .rightTop
        case .landscapeLeft:
            return cameraPosition == .front ? .bottomLeft : .topLeft
        case .portraitUpsideDown:
            return cameraPosition == .front ? .rightBottom : .leftBottom
        case .landscapeRight:
            return cameraPosition == .front ? .topRight : .bottomRight
        case .faceDown, .faceUp, .unknown:
            return .leftTop
        }
    }

    Objective-C

    - (FIRVisionDetectorImageOrientation)
        imageOrientationFromDeviceOrientation:(UIDeviceOrientation)deviceOrientation
                               cameraPosition:(AVCaptureDevicePosition)cameraPosition {
      switch (deviceOrientation) {
        case UIDeviceOrientationPortrait:
          if (cameraPosition == AVCaptureDevicePositionFront) {
            return FIRVisionDetectorImageOrientationLeftTop;
          } else {
            return FIRVisionDetectorImageOrientationRightTop;
          }
        case UIDeviceOrientationLandscapeLeft:
          if (cameraPosition == AVCaptureDevicePositionFront) {
            return FIRVisionDetectorImageOrientationBottomLeft;
          } else {
            return FIRVisionDetectorImageOrientationTopLeft;
          }
        case UIDeviceOrientationPortraitUpsideDown:
          if (cameraPosition == AVCaptureDevicePositionFront) {
            return FIRVisionDetectorImageOrientationRightBottom;
          } else {
            return FIRVisionDetectorImageOrientationLeftBottom;
          }
        case UIDeviceOrientationLandscapeRight:
          if (cameraPosition == AVCaptureDevicePositionFront) {
            return FIRVisionDetectorImageOrientationTopRight;
          } else {
            return FIRVisionDetectorImageOrientationBottomRight;
          }
        default:
          return FIRVisionDetectorImageOrientationTopLeft;
      }
    }

    Następnie utwórz obiekt metadanych:

    Swift

    let cameraPosition = AVCaptureDevice.Position.back  // Set to the capture device you used.
    let metadata = VisionImageMetadata()
    metadata.orientation = imageOrientation(
        deviceOrientation: UIDevice.current.orientation,
        cameraPosition: cameraPosition
    )

    Objective-C

    FIRVisionImageMetadata *metadata = [[FIRVisionImageMetadata alloc] init];
    AVCaptureDevicePosition cameraPosition =
        AVCaptureDevicePositionBack;  // Set to the capture device you used.
    metadata.orientation =
        [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                     cameraPosition:cameraPosition];
  2. Utwórz obiekt VisionImage, używając obiektu CMSampleBufferRef i metadanych rotacji:

    Swift

    let image = VisionImage(buffer: sampleBuffer)
    image.metadata = metadata

    Objective-C

    FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
    image.metadata = metadata;

3. Uruchamianie oznaczania obrazów

Aby oznaczyć etykietami obiekty na obrazie, przekaż obiekt VisionImage do metody process() interfejsu VisionImageLabeler:

Swift

labeler.process(image) { labels, error in
    guard error == nil, let labels = labels else { return }

    // Task succeeded.
    // ...
}

Objective-C

[labeler
    processImage:image
      completion:^(NSArray<FIRVisionImageLabel *> *_Nullable labels, NSError *_Nullable error) {
        if (error != nil || labels == nil) {
          return;
        }

        // Task succeeded.
        // ...
      }];

Jeśli oznaczenie obrazów zostanie oznaczone etykietami, do modułu obsługi uzupełniania zostanie przekazana tablica obiektów VisionImageLabel. Możesz uzyskać o nim informacje o obiekcie rozpoznanym na obrazie.

Przykład:

Swift

for label in labels {
    let labelText = label.text
    let confidence = label.confidence
}

Objective-C

for (FIRVisionImageLabel *label in labels) {
  NSString *labelText = label.text;
  NSNumber *confidence = label.confidence;
}

Wskazówki dotyczące poprawy skuteczności w czasie rzeczywistym

  • Ogranicz wywołania do detektora. Jeśli podczas działania detektora dostępna będzie nowa klatka wideo, upuść ją.
  • Jeśli używasz danych wyjściowych detektora do nakładania grafiki na obraz wejściowy, najpierw pobierz wynik z ML Kit, a potem wyrenderuj obraz i nakładkę w jednym kroku. Dzięki temu renderowanie na powierzchni wyświetlania będzie odbywać się tylko raz na każdą klatkę wejściową. Przykład znajdziesz w klasach previewOverlayView i FIRDetectionOverlayView w przykładowej aplikacji z funkcją prezentacji.