Rileva, monitora e classifica gli oggetti con un modello di classificazione personalizzato su iOS

Puoi utilizzare ML Kit per rilevare e tenere traccia degli oggetti in fotogrammi video successivi.

Quando passi un'immagine a ML Kit, questo rileva fino a cinque oggetti nell'immagine e la posizione di ciascun oggetto. Durante il rilevamento degli oggetti nei flussi video, ogni oggetto ha un ID univoco che può essere utilizzato per tracciare l'oggetto da un frame all'altro.

Puoi utilizzare un modello di classificazione delle immagini personalizzato per classificare gli oggetti che vengono rilevati. Consulta Modelli personalizzati con ML Kit per linee guida sui requisiti di compatibilità dei modelli, dove trovare i modelli preaddestrati e come addestrare i tuoi modelli.

Esistono due modi per integrare un modello personalizzato. Puoi raggruppare il modello inserendolo nella cartella degli asset dell'app oppure scaricarlo dinamicamente da Firebase. La tabella seguente mette a confronto le due opzioni.

Modello in bundle Modello ospitato
Il modello fa parte del file .ipa dell'app, che ne aumenta le dimensioni. Il modello non fa parte del file .ipa dell'app. L'hosting avviene tramite caricamento su Firebase Machine Learning.
Il modello è subito disponibile, anche quando il dispositivo Android è offline Il modello viene scaricato on demand
Non è necessario un progetto Firebase Richiede un progetto Firebase
Devi ripubblicare la tua app per aggiornare il modello Esegui il push degli aggiornamenti del modello senza ripubblicare l'app
Nessun test A/B integrato Semplici test A/B con Firebase Remote Config

Prova

Prima di iniziare

  1. Includi le librerie ML Kit nel podfile:

    Per raggruppare un modello con la tua app:

    pod 'GoogleMLKit/ObjectDetectionCustom', '3.2.0'
    

    Per scaricare in modo dinamico un modello da Firebase, aggiungi la dipendenza LinkFirebase:

    pod 'GoogleMLKit/ObjectDetectionCustom', '3.2.0'
    pod 'GoogleMLKit/LinkFirebase', '3.2.0'
    
  2. Dopo aver installato o aggiornato i pod del progetto, apri il progetto Xcode utilizzando il relativo .xcworkspace. ML Kit è supportato in Xcode versione 13.2.1 o successiva.

  3. Se vuoi scaricare un modello, assicurati di aggiungere Firebase al progetto iOS se non l'hai ancora fatto. Questa operazione non è necessaria quando raccogli il modello.

1. Carica il modello

Configura l'origine di un modello locale

Per raggruppare il modello con la tua app:

  1. Copia il file del modello (che di solito termina con .tflite o .lite) nel tuo progetto Xcode, assicurandoti di selezionare Copy bundle resources al momento dell'operazione. Il file del modello sarà incluso nell'app bundle e disponibile per ML Kit.

  2. Crea l'oggetto LocalModel, specificando il percorso del file del modello:

    Swift

    let localModel = LocalModel(path: localModelFilePath)

    Objective-C

    MLKLocalModel *localModel =
        [[MLKLocalModel alloc] initWithPath:localModelFilePath];

Configura un'origine del modello ospitata da Firebase

Per utilizzare il modello ospitato in remoto, crea un oggetto CustomRemoteModel, specificando il nome assegnato al modello quando lo hai pubblicato:

Swift

let firebaseModelSource = FirebaseModelSource(
    name: "your_remote_model") // The name you assigned in
                               // the Firebase console.
let remoteModel = CustomRemoteModel(remoteModelSource: firebaseModelSource)

Objective-C

MLKFirebaseModelSource *firebaseModelSource =
    [[MLKFirebaseModelSource alloc]
        initWithName:@"your_remote_model"]; // The name you assigned in
                                            // the Firebase console.
MLKCustomRemoteModel *remoteModel =
    [[MLKCustomRemoteModel alloc]
        initWithRemoteModelSource:firebaseModelSource];

Poi avvia l'attività di download del modello, specificando le condizioni in cui vuoi consentire il download. Se il modello non è presente sul dispositivo o se è disponibile una versione più recente del modello, l'attività scaricherà il modello in modo asincrono da 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];

Molte app avviano l'attività di download nel codice di inizializzazione, ma puoi farlo in qualsiasi momento prima di utilizzare il modello.

2. Configura il rilevatore di oggetti

Dopo aver configurato le origini del modello, configura il rilevatore di oggetti per il tuo caso d'uso con un oggetto CustomObjectDetectorOptions. Puoi modificare le seguenti impostazioni:

Impostazioni rilevatore di oggetti
Modalità di rilevamento STREAM_MODE (valore predefinito) | SINGLE_IMAGE_MODE

In STREAM_MODE (impostazione predefinita), il rilevatore di oggetti viene eseguito con bassa latenza, ma potrebbe produrre risultati incompleti (ad esempio riquadri di delimitazione o etichette di categoria non specificati) durante le prime chiamate al rilevatore. Inoltre, in STREAM_MODE, il rilevatore assegna agli oggetti ID di monitoraggio che possono essere utilizzati per tenere traccia degli oggetti tra i frame. Utilizza questa modalità quando desideri monitorare gli oggetti o quando la bassa latenza è importante, ad esempio durante l'elaborazione di stream video in tempo reale.

In SINGLE_IMAGE_MODE, il rilevatore di oggetti restituisce il risultato dopo aver determinato il riquadro di delimitazione dell'oggetto. Se abiliti anche la classificazione, viene restituito il risultato dopo che il riquadro di delimitazione e l'etichetta della categoria sono entrambi disponibili. Di conseguenza, la latenza del rilevamento è potenzialmente più elevata. Inoltre, in SINGLE_IMAGE_MODE, gli ID monitoraggio non sono assegnati. Utilizza questa modalità se la latenza non è critica e non vuoi avere a che fare con risultati parziali.

Rileva e monitora più oggetti false (valore predefinito) | true

Indica se rilevare e tracciare fino a cinque oggetti o solo l'oggetto più in evidenza (impostazione predefinita).

Classificare gli oggetti false (valore predefinito) | true

Indica se classificare o meno gli oggetti rilevati utilizzando il modello di classificazione personalizzato fornito. Per utilizzare il tuo modello di classificazione personalizzato, devi impostarlo su true.

Soglia di confidenza della classificazione

Punteggio di affidabilità minimo delle etichette rilevate. Se non viene impostata, verrà utilizzata qualsiasi soglia del classificatore specificata dai metadati del modello. Se il modello non contiene metadati o se i metadati non specificano una soglia di classificazione, verrà utilizzata una soglia predefinita pari a 0,0.

Numero massimo di etichette per oggetto

Numero massimo di etichette per oggetto che il rilevatore restituirà. Se non viene configurato, verrà utilizzato il valore predefinito 10.

Se hai solo un modello in bundle localmente, crea un rilevatore di oggetti dal tuo oggetto LocalModel:

Swift

let options = CustomObjectDetectorOptions(localModel: localModel)
options.detectorMode = .singleImage
options.shouldEnableClassification = true
options.shouldEnableMultipleObjects = true
options.classificationConfidenceThreshold = NSNumber(value: 0.5)
options.maxPerObjectLabelCount = 3

Objective-C

MLKCustomObjectDetectorOptions *options =
    [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel];
options.detectorMode = MLKObjectDetectorModeSingleImage;
options.shouldEnableClassification = YES;
options.shouldEnableMultipleObjects = YES;
options.classificationConfidenceThreshold = @(0.5);
options.maxPerObjectLabelCount = 3;

Se hai un modello ospitato in remoto, devi verificare che sia stato scaricato prima di eseguirlo. Puoi controllare lo stato dell'attività di download del modello utilizzando il metodo isModelDownloaded(remoteModel:) del gestore modelli.

Anche se devi confermare questo stato prima di eseguire il rilevatore di oggetti, se disponi sia di un modello ospitato in remoto sia di un modello in bundle localmente, potrebbe essere opportuno eseguire questo controllo quando crei l'istanza di ObjectDetector: crea un rilevatore dal modello remoto se è stato scaricato e dal modello locale in altro modo.

Swift

var options: CustomObjectDetectorOptions!
if (ModelManager.modelManager().isModelDownloaded(remoteModel)) {
  options = CustomObjectDetectorOptions(remoteModel: remoteModel)
} else {
  options = CustomObjectDetectorOptions(localModel: localModel)
}
options.detectorMode = .singleImage
options.shouldEnableClassification = true
options.shouldEnableMultipleObjects = true
options.classificationConfidenceThreshold = NSNumber(value: 0.5)
options.maxPerObjectLabelCount = 3

Objective-C

MLKCustomObjectDetectorOptions *options;
if ([[MLKModelManager modelManager] isModelDownloaded:remoteModel]) {
  options = [[MLKCustomObjectDetectorOptions alloc] initWithRemoteModel:remoteModel];
} else {
  options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel];
}
options.detectorMode = MLKObjectDetectorModeSingleImage;
options.shouldEnableClassification = YES;
options.shouldEnableMultipleObjects = YES;
options.classificationConfidenceThreshold = @(0.5);
options.maxPerObjectLabelCount = 3;

Se hai solo un modello ospitato in remoto, devi disattivare le funzionalità correlate al modello, ad esempio rendere non selezionabili o nascondere parte dell'interfaccia utente, finché non confermi che il modello è stato scaricato.

Puoi ottenere lo stato di download del modello collegando osservatori al Centro notifiche predefinito. Assicurati di utilizzare un riferimento debole a self nel blocco dell'osservatore, poiché i download possono richiedere del tempo e l'oggetto di origine può essere liberato al termine del download. Ad esempio:

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

L'API di rilevamento e monitoraggio degli oggetti è ottimizzata per questi due casi d'uso principali:

  • Rilevamento e tracciamento in tempo reale dell'oggetto più in evidenza nel mirino della fotocamera.
  • Il rilevamento di più oggetti da un'immagine statica.

Per configurare l'API per questi casi d'uso:

Swift

// Live detection and tracking
let options = CustomObjectDetectorOptions(localModel: localModel)
options.shouldEnableClassification = true
options.maxPerObjectLabelCount = 3

// Multiple object detection in static images
let options = CustomObjectDetectorOptions(localModel: localModel)
options.detectorMode = .singleImage
options.shouldEnableMultipleObjects = true
options.shouldEnableClassification = true
options.maxPerObjectLabelCount = 3

Objective-C

// Live detection and tracking
MLKCustomObjectDetectorOptions *options =
    [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel];
options.shouldEnableClassification = YES;
options.maxPerObjectLabelCount = 3;

// Multiple object detection in static images
MLKCustomObjectDetectorOptions *options =
    [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel];
options.detectorMode = MLKObjectDetectorModeSingleImage;
options.shouldEnableMultipleObjects = YES;
options.shouldEnableClassification = YES;
options.maxPerObjectLabelCount = 3;

3. Prepara l'immagine di input

Crea un oggetto VisionImage utilizzando UIImage o CMSampleBuffer.

Se usi un UIImage, segui questi passaggi:

  • Crea un oggetto VisionImage con il UIImage. Assicurati di specificare il valore .orientation corretto.

    Swift

    let image = VisionImage(image: UIImage)
    visionImage.orientation = image.imageOrientation

    Objective-C

    MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image];
    visionImage.orientation = image.imageOrientation;

Se usi un CMSampleBuffer, segui questi passaggi:

  • Specifica l'orientamento dei dati immagine contenuti in CMSampleBuffer.

    Per ottenere l'orientamento dell'immagine:

    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;
      }
    }
          
  • Crea un oggetto VisionImage utilizzando l'oggetto CMSampleBuffer e l'orientamento:

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

4. Crea ed esegui il rilevatore di oggetti

  1. Crea un nuovo rilevatore di oggetti:

    Swift

    let objectDetector = ObjectDetector.objectDetector(options: options)

    Objective-C

    MLKObjectDetector *objectDetector = [MLKObjectDetector objectDetectorWithOptions:options];
  2. Quindi, utilizza il rilevatore:

    In modo asincrono:

    Swift

    objectDetector.process(image) { objects, error in
        guard error == nil, let objects = objects, !objects.isEmpty else {
            // Handle the error.
            return
        }
        // Show results.
    }

    Objective-C

    [objectDetector
        processImage:image
          completion:^(NSArray *_Nullable objects,
                       NSError *_Nullable error) {
            if (objects.count == 0) {
                // Handle the error.
                return;
            }
            // Show results.
         }];

    In modo sincrono:

    Swift

    var objects: [Object]
    do {
        objects = try objectDetector.results(in: image)
    } catch let error {
        // Handle the error.
        return
    }
    // Show results.

    Objective-C

    NSError *error;
    NSArray *objects =
        [objectDetector resultsInImage:image error:&error];
    // Show results or handle the error.

5. Recuperare informazioni sugli oggetti etichettati

Se la chiamata al processore di immagini ha esito positivo, passa un elenco di Object al gestore di completamento oppure restituisce l'elenco, a seconda che tu abbia chiamato il metodo asincrono o sincrono.

Ogni Object contiene le seguenti proprietà:

frame Un CGRect che indica la posizione dell'oggetto nell'immagine.
trackingID Un numero intero che identifica l'oggetto nelle immagini o "nil" in SINGLE_IMAGE_MODE.
labels
label.text La descrizione testuale dell'etichetta. Errore restituito solo se i metadati del modello TensorFlow Lite contengono descrizioni di etichette.
label.index L'indice dell'etichetta tra tutte le etichette supportate dal classificatore.
label.confidence Il valore di confidenza della classificazione degli oggetti.

Swift

// objects contains one item if multiple object detection wasn't enabled.
for object in objects {
  let frame = object.frame
  let trackingID = object.trackingID
  let description = object.labels.enumerated().map { (index, label) in
    "Label \(index): \(label.text), \(label.confidence), \(label.index)"
  }.joined(separator: "\n")
}

Objective-C

// The list of detected objects contains one item if multiple object detection
// wasn't enabled.
for (MLKObject *object in objects) {
  CGRect frame = object.frame;
  NSNumber *trackingID = object.trackingID;
  for (MLKObjectLabel *label in object.labels) {
    NSString *labelString =
        [NSString stringWithFormat:@"%@, %f, %lu",
                                   label.text,
                                   label.confidence,
                                   (unsigned long)label.index];
  }
}

Garantire un'ottima esperienza utente

Per un'esperienza utente ottimale, segui queste linee guida nella tua app:

  • Il corretto rilevamento degli oggetti dipende dalla complessità visiva dell'oggetto. Per essere rilevati, gli oggetti con un numero ridotto di caratteristiche visive potrebbero dover occupare una parte più ampia dell'immagine. Fornisci agli utenti indicazioni su come acquisire input che funzionino bene con il tipo di oggetti che vuoi rilevare.
  • Quando utilizzi la classificazione, se vuoi rilevare oggetti che non rientrano perfettamente nelle categorie supportate, implementa una gestione speciale per gli oggetti sconosciuti.

Dai anche un'occhiata alla [app ML Kit Material Design Showcase][showcase-link]{: .external } e alla raccolta Pattern per funzionalità basate sul machine learning di Material Design.

Miglioramento delle prestazioni

Se vuoi utilizzare il rilevamento di oggetti in un'applicazione in tempo reale, segui queste linee guida per ottenere le frequenze fotogrammi migliori:

  • Quando utilizzi la modalità flusso in un'applicazione in tempo reale, non utilizzare il rilevamento di più oggetti, poiché la maggior parte dei dispositivi non sarà in grado di produrre frequenze fotogrammi adeguate.

  • Per elaborare i fotogrammi video, utilizza l'API sincrona results(in:) del rilevatore. Richiama questo metodo dalla funzione captureOutput(_, didOutput:from:) di AVCaptureVideoDataOutputSampleBufferDelegate per ottenere in modo sincrono i risultati dal frame video specificato. Mantieni il valore true per alwaysDiscardsLateVideoFrames di AVCaptureVideoDataOutput per limitare le chiamate al rilevatore. Se un nuovo frame video diventa disponibile mentre il rilevatore è in esecuzione, questo verrà eliminato.
  • Se utilizzi l'output del rilevatore per sovrapporre gli elementi grafici all'immagine di input, recupera prima il risultato da ML Kit, quindi esegui il rendering dell'immagine e dell'overlay in un solo passaggio. In questo modo, esegui il rendering sulla piattaforma di visualizzazione solo una volta per ogni frame di input elaborato. Per un esempio, vedi updatePreviewOverlayViewWithLastFrame nell'esempio della guida rapida di ML Kit.