Rileva e traccia oggetti con ML Kit su iOS

Puoi utilizzare ML Kit per rilevare e tracciare oggetti tra fotogrammi di video.

Quando si passano immagini ML Kit, ML Kit restituisce, per ciascuna immagine, un elenco di un massimo di cinque oggetti rilevati e la relativa posizione nell'immagine. Quando si rilevano oggetti nei flussi video, ogni oggetto ha un ID che è possibile utilizzare per tracciare l'oggetto nelle immagini. Facoltativamente, puoi anche abilitare la classificazione grossolana degli oggetti, che etichetta gli oggetti con descrizioni di categoria ampie.

Prima di iniziare

  1. Se non hai già aggiunto Firebase alla tua app, fallo seguendo i passaggi nella guida introduttiva .
  2. Includi le librerie ML Kit nel tuo Podfile:
    pod 'Firebase/MLVision', '6.25.0'
    pod 'Firebase/MLVisionObjectDetection', '6.25.0'
    
    Dopo aver installato o aggiornato i Pod del tuo progetto, assicurati di aprire il tuo progetto Xcode utilizzando il relativo .xcworkspace .
  3. Nella tua app, importa Firebase:

    Veloce

    import Firebase

    Obiettivo-C

    @import Firebase;

1. Configurare il rilevatore di oggetti

Per iniziare a rilevare e tracciare gli oggetti, crea prima un'istanza di VisionObjectDetector , specificando facoltativamente le impostazioni del rilevatore che desideri modificare rispetto a quelle predefinite.

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

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

    Nella modalità flusso (impostazione predefinita), il rilevatore di oggetti viene eseguito con una latenza molto bassa, ma potrebbe produrre risultati incompleti (come riquadri di delimitazione o categoria non specificati) alle prime invocazioni del rilevatore. Inoltre, in modalità flusso, il rilevatore assegna ID di tracciamento agli oggetti, che è possibile utilizzare per tracciare gli oggetti attraverso i fotogrammi. Utilizza questa modalità quando desideri tenere traccia degli oggetti o quando è importante una bassa latenza, ad esempio quando si elaborano flussi video in tempo reale.

    Nella modalità immagine singola, il rilevatore di oggetti attende finché il riquadro di delimitazione dell'oggetto rilevato e (se è stata abilitata la classificazione) la categoria sono disponibili prima di restituire un risultato. Di conseguenza, la latenza di rilevamento è potenzialmente più elevata. Inoltre, nella modalità immagine singola, gli ID di tracciamento non vengono assegnati. Utilizza questa modalità se la latenza non è critica e non vuoi gestire risultati parziali.

    Rileva e traccia più oggetti false (predefinito) | true

    Se rilevare e tracciare fino a cinque oggetti o solo l'oggetto più importante (impostazione predefinita).

    Classificare gli oggetti false (predefinito) | true

    Se classificare o meno gli oggetti rilevati in categorie grossolane. Quando abilitato, il rilevatore di oggetti classifica gli oggetti nelle seguenti categorie: articoli di moda, cibo, articoli per la casa, luoghi, piante e sconosciuti.

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

    • Rilevamento e tracciamento in tempo reale dell'oggetto più importante nel mirino della fotocamera
    • Rilevamento di più oggetti in un'immagine statica

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

    Veloce

    // Live detection and tracking
    let options = VisionObjectDetectorOptions()
    options.detectorMode = .stream
    options.shouldEnableMultipleObjects = false
    options.shouldEnableClassification = true  // Optional
    
    // Multiple object detection in static images
    let options = VisionObjectDetectorOptions()
    options.detectorMode = .singleImage
    options.shouldEnableMultipleObjects = true
    options.shouldEnableClassification = true  // Optional
    

    Obiettivo-C

    // Live detection and tracking
    FIRVisionObjectDetectorOptions *options = [[FIRVisionObjectDetectorOptions alloc] init];
    options.detectorMode = FIRVisionObjectDetectorModeStream;
    options.shouldEnableMultipleObjects = NO;
    options.shouldEnableClassification = YES;  // Optional
    
    // Multiple object detection in static images
    FIRVisionObjectDetectorOptions *options = [[FIRVisionObjectDetectorOptions alloc] init];
    options.detectorMode = FIRVisionObjectDetectorModeSingleImage;
    options.shouldEnableMultipleObjects = YES;
    options.shouldEnableClassification = YES;  // Optional
    
  2. Ottieni un'istanza di FirebaseVisionObjectDetector :

    Veloce

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

    Obiettivo-C

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

2. Eseguire il rilevatore di oggetti

Per rilevare e tracciare gli oggetti, procedere come segue per ciascuna immagine o fotogramma video. Se hai abilitato la modalità flusso, devi creare oggetti VisionImage da CMSampleBufferRef s.

  1. Crea un oggetto VisionImage utilizzando UIImage o CMSampleBufferRef .

    Per utilizzare un'immagine UIImage :

    1. Se necessario, ruotare l'immagine in modo che la relativa proprietà imageOrientation sia .up .
    2. Crea un oggetto VisionImage utilizzando UIImage ruotato correttamente. Non specificare alcun metadato di rotazione: è necessario utilizzare il valore predefinito, .topLeft .

      Veloce

      let image = VisionImage(image: uiImage)

      Obiettivo-C

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

    Per utilizzare un CMSampleBufferRef :

    1. Crea un oggetto VisionImageMetadata che specifica l'orientamento dei dati dell'immagine contenuti nel buffer CMSampleBufferRef .

      Per ottenere l'orientamento dell'immagine:

      Veloce

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

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

      Quindi, crea l'oggetto metadati:

      Veloce

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

      Obiettivo-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. Crea un oggetto VisionImage utilizzando l'oggetto CMSampleBufferRef e i metadati di rotazione:

      Veloce

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

      Obiettivo-C

      FIRVisionImage *image = [[FIRVisionImage alloc] initWithBuffer:sampleBuffer];
      image.metadata = metadata;
  2. Passa VisionImage a uno dei metodi di elaborazione delle immagini del rilevatore di oggetti. È possibile utilizzare il metodo asincrono process(image:) o il metodo synchronous results() .

    Per rilevare gli oggetti in modo asincrono:

    Veloce

    objectDetector.process(image) { detectedObjects, error in
      guard error == nil else {
        // Error.
        return
      }
      guard let detectedObjects = detectedObjects, !detectedObjects.isEmpty else {
        // No objects detected.
        return
      }
    
      // Success. Get object info here.
      // ...
    }
    

    Obiettivo-C

    [objectDetector processImage:image
                      completion:^(NSArray<FIRVisionObject *> * _Nullable objects,
                                   NSError * _Nullable error) {
                        if (error == nil) {
                          return;
                        }
                        if (objects == nil | objects.count == 0) {
                          // No objects detected.
                          return;
                        }
    
                        // Success. Get object info here.
                        // ...
                      }];
    

    Per rilevare gli oggetti in modo sincrono:

    Veloce

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

    Obiettivo-C

    NSError *error;
    NSArray<FIRVisionObject *> *objects = [objectDetector resultsInImage:image
                                                                   error:&error];
    if (error == nil) {
      return;
    }
    if (objects == nil | objects.count == 0) {
      // No objects detected.
      return;
    }
    
    // Success. Get object info here.
    // ...
    
  3. Se la chiamata al processore di immagini ha esito positivo, passa un elenco di VisionObject al gestore di completamento o restituisce l'elenco, a seconda che tu abbia chiamato il metodo asincrono o sincrono.

    Ogni VisionObject 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. Zero in modalità immagine singola.
    classificationCategory La categoria grossolana dell'oggetto. Se il rilevatore di oggetti non ha la classificazione abilitata, questo è sempre .unknown .
    confidence Il valore di confidenza della classificazione dell'oggetto. Se il rilevatore di oggetti non ha la classificazione abilitata o l'oggetto è classificato come sconosciuto, questo è nil .

    Veloce

    // detectedObjects contains one item if multiple object detection wasn't enabled.
    for obj in detectedObjects {
      let bounds = obj.frame
      let id = obj.trackingID
    
      // If classification was enabled:
      let category = obj.classificationCategory
      let confidence = obj.confidence
    }
    

    Obiettivo-C

    // The list of detected objects contains one item if multiple
    // object detection wasn't enabled.
    for (FIRVisionObject *obj in objects) {
      CGRect bounds = obj.frame;
      if (obj.trackingID) {
        NSInteger id = obj.trackingID.integerValue;
      }
    
      // If classification was enabled:
      FIRVisionObjectCategory category = obj.classificationCategory;
      float confidence = obj.confidence.floatValue;
    }
    

Migliorare l'usabilità e le prestazioni

Per la migliore esperienza utente, segui queste linee guida nella tua app:

  • Il successo del rilevamento degli oggetti dipende dalla complessità visiva dell'oggetto. Gli oggetti con un numero limitato di caratteristiche visive potrebbero dover occupare una parte più ampia dell'immagine per essere rilevati. Dovresti fornire agli utenti indicazioni sull'acquisizione di input che funzioni bene con il tipo di oggetti che desideri rilevare.
  • Quando si utilizza la classificazione, se si desidera rilevare oggetti che non rientrano esattamente nelle categorie supportate, implementare una gestione speciale per gli oggetti sconosciuti.

Inoltre, dai un'occhiata all'[app vetrina ML Kit Material Design][showcase-link]{: .external } e alla raccolta di funzionalità basate sul machine learning sui modelli di progettazione dei materiali.

Quando utilizzi la modalità streaming in un'applicazione in tempo reale, segui queste linee guida per ottenere i migliori framerate:

  • Non utilizzare il rilevamento di più oggetti in modalità streaming, poiché la maggior parte dei dispositivi non sarà in grado di produrre framerate adeguati.

  • Disabilita la classificazione se non ne hai bisogno.

  • Limita le chiamate al rilevatore. Se un nuovo fotogramma video diventa disponibile mentre il rilevatore è in funzione, rilasciare il fotogramma.
  • Se si utilizza l'output del rilevatore per sovrapporre la grafica all'immagine in input, ottenere prima il risultato da ML Kit, quindi eseguire il rendering dell'immagine e sovrapporre in un unico passaggio. In questo modo, viene eseguito il rendering sulla superficie di visualizzazione solo una volta per ciascun fotogramma di input. Per un esempio, vedi le classi PreviewOverlayView e FIRDetectionOverlayView nell'app di esempio vetrina.