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

Gdy wytrenujesz własny model przy użyciu AutoML Vision Edge, możesz używać go w swojej aplikacji, aby oznaczać obrazy etykietami.

Istnieją 2 sposoby integracji modeli wytrenowanych w AutoML Vision Edge. Dostępne opcje spakować model przez skopiowanie plików modelu do projektu Xcode lub może dynamicznie pobrać je z Firebase.

Opcje grupowania modeli
Pakiet w aplikacji
  • Model jest częścią pakietu
  • Model jest dostępny od razu, nawet gdy urządzenie z iOS jest offline
  • Nie potrzeba projektu Firebase
Hostowane w Firebase

Wypróbuj

Zanim zaczniesz

1. Umieść biblioteki ML Kit w pliku Podfile:

Aby dołączyć model do aplikacji:
    pod 'GoogleMLKit/ImageLabelingAutoML'
    
Aby dynamicznie pobierać model z Firebase, dodaj LinkFirebase zależność:
    pod 'GoogleMLKit/ImageLabelingAutoML'
    pod 'GoogleMLKit/LinkFirebase'
    
2. Po zainstalowaniu lub zaktualizowaniu podów swojego projektu otwórz projekt Xcode za pomocą jego .xcworkspacekodu>. ML Kit jest obsługiwany w Xcode wersji 13.2.1 lub nowszej. 3. Jeśli chcesz pobrać model, dodaj Firebase do swojego projektu na iOS, jeśli jeszcze nie zostało to zrobione. Nie jest to wymagane, gdy łączysz model atrybucji.

1. Wczytaj model

Skonfiguruj źródło modelu lokalnego

Aby połączyć model z aplikacją:

1. Wyodrębnij model i jego metadane z pobranego archiwum ZIP 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życie plików podczas pobierania, bez modyfikacji (łącznie z nazwami plików).

2. Skopiuj folder do projektu Xcode, wybierając go 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, określając ścieżkę do plik 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żywać modelu hostowanego zdalnie, utwórz AutoMLImageLabelerRemoteModel z nazwą przypisaną do modelu podczas jego publikacji:

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 uruchom zadanie pobierania modelu, określając warunki, które którym chcesz zezwolić na pobieranie. Jeśli nie ma modelu na urządzeniu lub jest on nowszy gdy dostępna będzie wersja modelu, 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, możesz to zrobić w dowolnym momencie, zanim trzeba będzie skorzystać z modelu.

Tworzenie osoby oznaczającej obrazy na podstawie modelu

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

Jeśli masz tylko model scalony lokalnie, po prostu utwórz osobę oznaczającą etykietami na podstawie AutoMLImageLabelerLocalModel obiekt i skonfiguruj wskaźnik ufności próg, który chcesz zastosować (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, musisz sprawdzić, czy został pobrane przed uruchomieniem. Stan pobierania modelu możesz sprawdzić za pomocą metody isModelDownloaded(remoteModel:) menedżera modeli.

Chociaż trzeba to potwierdzić tylko przed uruchomieniem osoby oznaczającej etykietami, korzystają zarówno z modelu hostowanego zdalnie, jak i z pakietu lokalnego, może to sprawić, warto przeprowadzić tę kontrolę przy tworzeniu wystąpienia ImageLabeler: utwórz z modelu zdalnego, jeśli został on pobrany, oraz 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 powiązany z nim model funkcji – na przykład wyszarzenia lub ukrycia części interfejsu – do potwierdzasz, że model został pobrany.

Stan pobierania modelu możesz sprawdzić, dołączając obserwatorów do wartości domyślnej. Centrum powiadomień. Pamiętaj, aby w obserwatorium używać słabego odniesienia do self bo pobieranie może trochę potrwać, a źródłowy obiekt zwolniony do momentu 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 UIImage, wykonaj te czynności:

  • Utwórz obiekt VisionImage za pomocą UIImage. Pamiętaj, by określić 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 zdjęć zawartych w pliku CMSampleBuffer

    Aby sprawdzić 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ą CMSampleBuffer obiekt i orientacja:

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

Synchronnie:

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

Jeśli operacja oznaczania obrazów etykietami zakończy się powodzeniem, funkcja zwróci tablicę ImageLabel Każdy element ImageLabel reprezentuje coś, oznaczone etykietą na zdjęciu. Opis każdej etykiety (jeśli jest dostępny w metadane 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 instrukcjami wytycznych dotyczących uzyskiwania najlepszej liczby klatek na sekundę:

  • Do przetwarzania klatek wideo używaj synchronicznego interfejsu API results(in:) detektora. Zadzwoń do nas tę metodę z AVCaptureVideoDataOutputSampleBufferDelegate . captureOutput(_, didOutput:from:), aby synchronicznie pobierać wyniki dotyczące danego filmu ramki. Zachowaj AVCaptureVideoDataOutput: alwaysDiscardsLateVideoFrames jako true, aby ograniczyć wywołania detektora. Jeśli nowy gdy klatka wideo jest dostępna, gdy jest uruchomiony detektor, zostanie usunięta.
  • Jeśli użyjesz danych wyjściowych detektora do nakładania grafiki na obrazu wejściowego, najpierw pobierz wynik z ML Kit, a następnie wyrenderuj obraz i nakładanie nakładek w jednym kroku. W ten sposób renderowanie na powierzchni tylko raz na każdą przetworzoną ramkę wejściową. Zobacz updatePreviewOverlayViewWithLastFrame. znajdziesz na przykład w krótkim wprowadzeniu do korzystania z ML Kit.