Détecter et suivre des objets avec ML Kit sur iOS

Vous pouvez utiliser ML Kit pour détecter et suivre des objets dans des images vidéo successives.

Lorsque vous transmettez une image à ML Kit, il détecte jusqu'à cinq objets dans l'image, ainsi que la position de chaque objet dans l'image. Lors de la détection d'objets dans des flux vidéo, chaque objet possède un identifiant unique que vous pouvez utiliser pour suivre l'objet d'une image à l'autre. Vous pouvez également activer la classification approximative d'objets, qui attribue des étiquettes aux objets avec des descriptions de catégorie générales.

Essayer

Avant de commencer

  1. Incluez les pods ML Kit suivants dans votre Podfile :
    pod 'GoogleMLKit/ObjectDetection', '3.2.0'
    
  2. Après avoir installé ou mis à jour les pods de votre projet, ouvrez votre projet Xcode à l'aide de son fichier .xcworkspace. ML Kit est compatible avec Xcode 12.4 ou version ultérieure.

1. Configurer le détecteur d'objets

Pour détecter et suivre des objets, créez d'abord une instance de ObjectDetector et spécifiez éventuellement les paramètres de détecteur que vous souhaitez modifier par rapport aux valeurs par défaut.

  1. Configurez le détecteur d'objets pour votre cas d'utilisation avec un objet ObjectDetectorOptions. Vous pouvez modifier les paramètres suivants:

    Paramètres du détecteur d'objets
    Mode de détection .stream (par défaut) | .singleImage

    En mode flux (par défaut), le détecteur d'objets s'exécute avec une latence très faible, mais peut produire des résultats incomplets (comme des catégories ou des cadres de délimitation non spécifiés) lors des premiers appels du détecteur. De plus, en mode flux, le détecteur attribue des ID de suivi aux objets, que vous pouvez utiliser pour suivre des objets sur plusieurs frames. Utilisez ce mode lorsque vous souhaitez suivre des objets ou lorsqu'une faible latence est importante, par exemple pour traiter des flux vidéo en temps réel.

    En mode Image unique, le détecteur d'objets renvoie le résultat une fois le cadre de délimitation de l'objet déterminé. Si vous activez également la classification, elle renvoie le résultat une fois que le cadre de délimitation et le libellé de catégorie sont tous deux disponibles. La latence de détection est donc potentiellement plus élevée. De plus, en mode Image unique, les ID de suivi ne sont pas attribués. Utilisez ce mode si la latence n'est pas critique et que vous ne souhaitez pas gérer des résultats partiels.

    Détecter et suivre plusieurs objets false (par défaut) | true

    Permet de détecter et de suivre jusqu'à cinq objets ou uniquement l'objet le plus visible (par défaut).

    Classer des objets false (par défaut) | true

    Indique si les objets détectés doivent être classés dans des catégories approximatives. Lorsqu'il est activé, le détecteur d'objets classe les objets dans les catégories suivantes: articles de mode, alimentation, articles pour la maison, lieux et plantes.

    L'API de détection et de suivi des objets est optimisée pour ces deux principaux cas d'utilisation:

    • Détection et suivi en direct de l'objet le plus proéminent dans le viseur de l'appareil photo.
    • Détection de plusieurs objets dans une image statique.

    Pour configurer l'API pour ces cas d'utilisation:

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. Obtenez une instance de 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. Préparer l'image d'entrée

Pour détecter et suivre des objets, procédez comme suit pour chaque image ou image de vidéo. Si vous avez activé le mode de diffusion, vous devez créer des objets VisionImage à partir de CMSampleBuffer.

Créez un objet VisionImage à l'aide d'un UIImage ou d'un CMSampleBuffer.

Si vous utilisez un UIImage, procédez comme suit:

  • Créez un objet VisionImage avec UIImage. Veillez à spécifier le bon .orientation.

    Swift

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

    Objective-C

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

Si vous utilisez un CMSampleBuffer, procédez comme suit:

  • Spécifiez l'orientation des données d'image contenues dans le fichier CMSampleBuffer.

    Pour obtenir l'orientation de l'image:

    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;
      }
    }
          
  • Créez un objet VisionImage à l'aide de l'objet CMSampleBuffer et de l'orientation:

    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. Traiter l'image

Transmettez VisionImage à l'une des méthodes de traitement d'images du détecteur d'objets. Vous pouvez utiliser la méthode process(image:) asynchrone ou la méthode results() synchrone.

Pour détecter des objets de manière asynchrone, procédez comme suit:

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

Pour détecter des objets de manière synchrone:

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. Obtenir des informations sur les objets détectés

Si l'appel au processeur d'images réussit, il transmet une liste de Object au gestionnaire d'achèvement ou la renvoie, selon que vous avez appelé la méthode asynchrone ou synchrone.

Chaque Object contient les propriétés suivantes:

frame CGRect indiquant la position de l'objet dans l'image.
trackingID Entier qui identifie l'objet sur toutes les images, ou "nil" en mode Image unique.
labels Tableau d'étiquettes décrivant l'objet renvoyé par le détecteur. La propriété est vide si l'option de détecteur shouldEnableClassification est définie sur 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];
    ...
  }
}

Améliorer la convivialité et les performances

Pour une expérience utilisateur optimale, suivez ces consignes dans votre application:

  • La réussite de la détection d'objets dépend de la complexité visuelle de l'objet. Pour être détectés, les objets dotés d'un petit nombre de caractéristiques visuelles devront peut-être occuper une plus grande partie de l'image. Vous devez fournir aux utilisateurs des conseils sur la capture des entrées qui fonctionnent bien avec le type d'objets que vous souhaitez détecter.
  • Lorsque vous utilisez la classification, si vous souhaitez détecter des objets qui ne correspondent pas correctement aux catégories compatibles, mettez en œuvre un traitement spécial pour les objets inconnus.

Consultez également la collection Modèles de caractéristiques basées sur le machine learning dans Material Design.

Lorsque vous utilisez le mode streaming dans une application en temps réel, suivez ces consignes pour obtenir les meilleures fréquences d'images:

  • N'utilisez pas la détection d'objets multiples en mode de streaming, car la plupart des appareils ne pourront pas produire des fréquences d'images adéquates.
  • Désactivez la classification si vous n'en avez pas besoin.
  • Pour traiter les images vidéo, utilisez l'API synchrone results(in:) du détecteur. Appelez cette méthode à partir de la fonction captureOutput(_, didOutput:from:) de AVCaptureVideoDataOutputSampleBufferDelegate pour obtenir de manière synchrone les résultats de l'image vidéo donnée. Conservez la valeur alwaysDiscardsLateVideoFrames de AVCaptureVideoDataOutput définie sur true pour limiter les appels au détecteur. Si une nouvelle image vidéo devient disponible alors que le détecteur est en cours d'exécution, elle sera ignorée.
  • Si vous utilisez la sortie du détecteur pour superposer des graphiques sur l'image d'entrée, obtenez d'abord le résultat de ML Kit, puis affichez l'image et la superposition en une seule étape. Ainsi, vous n'effectuez le rendu sur la surface d'affichage qu'une seule fois pour chaque trame d'entrée traitée. Consultez la section updatePreviewOverlayViewWithLastFrame dans l'exemple de démarrage rapide de ML Kit pour en voir un exemple.