Rileva e monitora gli oggetti con ML Kit 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, insieme alla posizione di ciascuno di essi. Durante il rilevamento degli oggetti nei flussi video, ogni oggetto ha un ID univoco che consente di monitorare l'oggetto da un frame all'altro. Facoltativamente, puoi abilitare la classificazione di oggetti approssimativi, che etichettano gli oggetti con descrizioni generiche.

Prova

Prima di iniziare

  1. Includi i seguenti pod ML Kit nel podfile:
    pod 'GoogleMLKit/ObjectDetection', '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 12.4 o successiva.

1. Configura il rilevatore di oggetti

Per rilevare e monitorare gli oggetti, devi prima creare un'istanza di ObjectDetector e, se vuoi, specificare le impostazioni del rilevatore che vuoi modificare rispetto a quelle predefinite.

  1. Configura il rilevatore di oggetti per il tuo caso d'uso con un oggetto ObjectDetectorOptions. Puoi modificare le seguenti impostazioni:

    Impostazioni rilevatore di oggetti
    Modalità di rilevamento .stream (valore predefinito) | .singleImage

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

    In modalità a immagine singola, 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 modalità immagine singola, gli ID di monitoraggio non vengono 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 in categorie approssimative. Quando è abilitato, il rilevatore di oggetti classifica gli oggetti nelle seguenti categorie: articoli di moda, cibo, articoli per la casa, luoghi e piante.

    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 in un'immagine statica.

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

Swift

// Live detection and tracking
let options = ObjectDetectorOptions()
options.shouldEnableClassification = true

// Multiple object detection in static images
let options = ObjectDetectorOptions()
options.detectorMode = .singleImage
options.shouldEnableMultipleObjects = true
options.shouldEnableClassification = true

Objective-C

// Live detection and tracking
MLKObjectDetectorOptions *options = [[MLKObjectDetectorOptions alloc] init];
options.shouldEnableClassification = YES;

// Multiple object detection in static images
MLKObjectDetectorOptions *options = [[MLKOptions alloc] init];
options.detectorMode = MLKObjectDetectorModeSingleImage;
options.shouldEnableMultipleObjects = YES;
options.shouldEnableClassification = YES;
  1. Ottieni un'istanza di ObjectDetector:

Swift

let objectDetector = ObjectDetector.objectDetector()

// Or, to change the default settings:
let objectDetector = ObjectDetector.objectDetector(options: options)

Objective-C

MLKObjectDetector *objectDetector = [MLKObjectDetector objectDetector];

// Or, to change the default settings:
MLKObjectDetector *objectDetector = [MLKObjectDetector objectDetectorWithOptions:options];

2. Prepara l'immagine di input

Per rilevare e tracciare oggetti, procedi nel seguente modo per ogni immagine o frame video. Se hai abilitato la modalità flusso, devi creare VisionImage oggetti dagli CMSampleBuffer.

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

3. Elabora l'immagine

Passa VisionImage a uno dei metodi di elaborazione delle immagini del rilevatore di oggetti. Puoi utilizzare il metodo process(image:) asincrono o il metodo sincrono results().

Per rilevare gli oggetti in modo asincrono:

Swift

objectDetector.process(image) { objects, error in
  guard error == nil else {
    // Error.
    return
  }
  guard !objects.isEmpty else {
    // No objects detected.
    return
  }

  // Success. Get object info here.
  // ...
}

Objective-C

[objectDetector processImage:image
                  completion:^(NSArray * _Nullable objects,
                               NSError * _Nullable error) {
                    if (error == nil) {
                      return;
                    }
                    if (objects.count == 0) {
                      // No objects detected.
                      return;
                    }

                    // Success. Get object info here.
                  }];

Per rilevare gli oggetti in modo sincrono:

Swift

var objects: [Object]
do {
  objects = try objectDetector.results(in: image)
} catch let error {
  print("Failed to detect object with error: \(error.localizedDescription).")
  return
}
guard !objects.isEmpty else {
  print("Object detector returned no results.")
  return
}

// Success. Get object info here.

Objective-C

NSError *error;
NSArray *objects = [objectDetector resultsInImage:image error:&error];
if (error == nil) {
  return;
}
if (objects.count == 0) {
  // No objects detected.
  return;
}

// Success. Get object info here.

4. Visualizza informazioni sugli oggetti rilevati

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 oppure "nil" in modalità a immagine singola.
labels Un array di etichette che descrivono l'oggetto restituito dal rilevatore. La proprietà è vuota se l'opzione del rilevatore shouldEnableClassification è impostata su false.

Swift

// objects contains one item if multiple object detection wasn't enabled.
for object in objects {
  let frame = object.frame
  let trackingID = object.trackingID

  // If classification was enabled:
  let description = object.labels.enumerated().map { (index, label) in
    "Label \(index): \(label.text), \(label.confidence)"
    }.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];
    ...
  }
}

Migliorare l'usabilità e le prestazioni

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.

Inoltre, dai un'occhiata alla raccolta Pattern per funzionalità basate sul machine learning di Material Design.

Quando utilizzi la modalità di streaming in un'applicazione in tempo reale, segui queste linee guida per ottenere le frequenze fotogrammi migliori:

  • Non utilizzare il rilevamento di più oggetti in modalità flusso, poiché la maggior parte dei dispositivi non sarà in grado di produrre frequenze fotogrammi adeguate.
  • Disabilita la classificazione se non ti serve.
  • 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.