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

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

Zanim zaczniesz

  1. Jeśli nie dodałeś jeszcze Firebase do swojej aplikacji, zrób to, wykonując czynności opisane w przewodniku wprowadzającym .
  2. Dołącz biblioteki ML Kit do swojego Podfile:
    pod 'Firebase/MLVision', '6.25.0'
    pod 'Firebase/MLVisionAutoML', '6.25.0'
    
    Po zainstalowaniu lub zaktualizowaniu Podów swojego projektu pamiętaj o otwarciu projektu Xcode przy użyciu jego .xcworkspace .
  3. W swojej aplikacji zaimportuj Firebase:

    Szybki

    import Firebase

    Cel C

    @import Firebase;

1. Załaduj model

ML Kit uruchamia modele wygenerowane przez AutoML na urządzeniu. Możesz jednak skonfigurować ML Kit tak, aby ładował Twój model zdalnie z Firebase, z magazynu lokalnego lub z obu.

Hostując model w Firebase, możesz aktualizować model bez wydawania nowej wersji aplikacji, a także możesz używać zdalnej konfiguracji i testów A/B do dynamicznego udostępniania różnych modeli różnym grupom użytkowników.

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

Łącząc model z aplikacją, możesz mieć pewność, że funkcje uczenia maszynowego w aplikacji będą nadal działać, gdy model hostowany przez Firebase nie będzie dostępny.

Skonfiguruj źródło modelu hostowane w Firebase

Aby użyć modelu hostowanego zdalnie, utwórz obiekt AutoMLRemoteModel , podając nazwę, którą przypisałeś modelowi podczas jego publikacji:

Szybki

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

Cel 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, na jakich chcesz zezwolić na pobieranie. Jeśli modelu nie ma na urządzeniu lub jeśli dostępna jest nowsza wersja modelu, zadanie asynchronicznie pobierze model z Firebase:

Szybki

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

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

Cel C

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

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

Wiele aplikacji rozpoczyna zadanie pobierania w kodzie inicjującym, ale można to zrobić w dowolnym momencie, zanim będzie konieczne użycie modelu.

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 trzy pliki muszą znajdować się w tym samym folderze. Zalecamy używanie plików w takiej postaci, w jakiej je pobrałeś, bez modyfikacji (w tym nazw plików).
  2. Skopiuj folder do projektu Xcode, pamiętając o zaznaczeniu opcji Utwórz odniesienia do folderu, gdy to zrobisz. Plik modelu i metadane zostaną uwzględnione w pakiecie aplikacji i dostępne dla ML Kit.
  3. Utwórz obiekt AutoMLLocalModel , podając ścieżkę do pliku manifestu modelu:

    Szybki

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

    Cel C

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

Utwórz etykietę obrazu ze swojego modelu

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

Jeśli masz tylko model powiązany lokalnie, po prostu utwórz etykietę na podstawie obiektu AutoMLLocalModel i skonfiguruj wymagany próg wyniku zaufania (zobacz Oceń swój model ):

Szybki

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)

Cel 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. Możesz sprawdzić status zadania pobierania modelu, korzystając z metody isModelDownloaded(remoteModel:) menedżera modeli.

Chociaż musisz to potwierdzić jedynie przed uruchomieniem modułu etykietowania, jeśli masz zarówno model hostowany zdalnie, jak i model powiązany lokalnie, sensowne może być wykonanie tej kontroli podczas tworzenia instancji VisionImageLabeler : utwórz etykietę z modelu zdalnego, jeśli jest został pobrany, a w przeciwnym razie z modelu lokalnego.

Szybki

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)

Cel 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 funkcje związane z modelem — na przykład wyszarz lub ukryj część interfejsu użytkownika — do czasu potwierdzenia, że ​​model został pobrany.

Stan pobierania modelu możesz uzyskać podłączając obserwatorów do domyślnego Centrum powiadomień. Pamiętaj, aby użyć słabego odniesienia do self w bloku obserwatora, ponieważ pobieranie może zająć trochę czasu, a obiekt źródłowy może zostać zwolniony do czasu zakończenia pobierania. Na przykład:

Szybki

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]
    // ...
}

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

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 przy użyciu UIImage lub CMSampleBufferRef .

Aby użyć UIImage :

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

    Szybki

    let image = VisionImage(image: uiImage)

    Cel C

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

Aby użyć CMSampleBufferRef :

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

    Aby uzyskać orientację obrazu:

    Szybki

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

    Cel 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:

    Szybki

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

    Cel 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 przy użyciu obiektu CMSampleBufferRef i metadanych rotacji:

    Szybki

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

    Cel C

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

3. Uruchom etykietę obrazu

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

Szybki

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

    // Task succeeded.
    // ...
}

Cel C

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

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

Jeśli etykietowanie obrazu powiedzie się, tablica obiektów VisionImageLabel zostanie przekazana do procedury obsługi uzupełniania. Z każdego obiektu można uzyskać informację o cesze rozpoznanej na obrazie.

Na przykład:

Szybki

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

Cel C

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

Wskazówki, jak poprawić wydajność w czasie rzeczywistym

  • Przepustnica wzywa do detektora. Jeżeli w trakcie działania detektora pojawi się nowa klatka wideo, usuń ją.
  • Jeśli używasz wyjścia detektora do nakładania grafiki na obraz wejściowy, najpierw uzyskaj wynik z ML Kit, a następnie wyrenderuj obraz i nakładkę w jednym kroku. W ten sposób renderujesz na powierzchnię wyświetlacza tylko raz dla każdej klatki wejściowej. Zobacz przykładowe klasy PreviewOverlayView i FIRDetectionOverlayView w przykładowej aplikacji prezentacyjnej.