Détecter, suivre et classer des objets avec un modèle de classification personnalisé 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 utiliser un modèle de classification d'images personnalisé pour classer les objets détectés. Consultez la page Modèles personnalisés avec ML Kit pour obtenir des conseils sur les exigences de compatibilité des modèles, où trouver des modèles pré-entraînés et comment entraîner vos propres modèles.

Il existe deux façons d'intégrer un modèle personnalisé. Vous pouvez regrouper le modèle en le plaçant dans le dossier d'éléments de votre application ou le télécharger dynamiquement à partir de Firebase. Le tableau suivant compare les deux options.

Modèle groupé Modèle hébergé
Le modèle fait partie du fichier .ipa de votre application, ce qui augmente sa taille. Le modèle ne fait pas partie du fichier .ipa de votre application. Il est hébergé en l'important dans Firebase Machine Learning.
Le modèle est disponible immédiatement, même lorsque l'appareil Android est hors connexion Le modèle est téléchargé à la demande
Aucun projet Firebase n'est nécessaire Nécessite un projet Firebase
Vous devez republier votre application pour mettre à jour le modèle. Déployer les mises à jour du modèle sans republier votre application
Aucun test A/B intégré Tests A/B faciles avec Firebase Remote Config

Essayer

Avant de commencer

  1. Incluez les bibliothèques ML Kit dans votre fichier Podfile:

    Pour regrouper un modèle dans votre application:

    pod 'GoogleMLKit/ObjectDetectionCustom', '3.2.0'
    

    Pour télécharger un modèle de manière dynamique à partir de Firebase, ajoutez la dépendance LinkFirebase:

    pod 'GoogleMLKit/ObjectDetectionCustom', '3.2.0'
    pod 'GoogleMLKit/LinkFirebase', '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 13.2.1 ou version ultérieure.

  3. Si vous souhaitez télécharger un modèle, veillez à ajouter Firebase à votre projet iOS, si ce n'est pas déjà fait. Cette opération n'est pas nécessaire lorsque vous regroupez le modèle.

1. Charger le modèle

Configurer une source de modèle locale

Pour regrouper le modèle avec votre application:

  1. Copiez le fichier de modèle (se terminant généralement par .tflite ou .lite) dans votre projet Xcode, en veillant à sélectionner Copy bundle resources. Le fichier de modèle sera inclus dans l'app bundle et sera disponible pour ML Kit.

  2. Créez un objet LocalModel en spécifiant le chemin d'accès au fichier de modèle:

    Swift

    let localModel = LocalModel(path: localModelFilePath)

    Objective-C

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

Configurer une source de modèle hébergée par Firebase

Pour utiliser le modèle hébergé à distance, créez un objet CustomRemoteModel en spécifiant le nom que vous avez attribué au modèle lorsque vous l'avez publié:

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

Lancez ensuite la tâche de téléchargement du modèle en spécifiant les conditions dans lesquelles vous souhaitez autoriser le téléchargement. Si le modèle ne se trouve pas sur l'appareil ou si une version plus récente du modèle est disponible, la tâche télécharge le modèle de manière asynchrone à partir de 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];

De nombreuses applications lancent la tâche de téléchargement dans leur code d'initialisation, mais vous pouvez le faire à tout moment avant de devoir utiliser le modèle.

2. Configurer le détecteur d'objets

Après avoir configuré vos sources de modèle, configurez le détecteur d'objets pour votre cas d'utilisation avec un objet CustomObjectDetectorOptions. Vous pouvez modifier les paramètres suivants:

Paramètres du détecteur d'objets
Mode de détection STREAM_MODE (par défaut) | SINGLE_IMAGE_MODE

Dans STREAM_MODE (par défaut), le détecteur d'objets s'exécute avec une faible latence, mais peut produire des résultats incomplets (tels que des cadres de délimitation ou des libellés de catégorie non spécifiés) lors des premiers appels du détecteur. En outre, dans STREAM_MODE, 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.

Dans SINGLE_IMAGE_MODE, 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, dans SINGLE_IMAGE_MODE, 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 à l'aide du modèle de classificateur personnalisé fourni. Pour utiliser votre modèle de classification personnalisé, vous devez définir la valeur sur true.

Seuil de confiance de la classification

Score de confiance minimal des étiquettes détectées. Si cette règle n'est pas configurée, tout seuil de classification spécifié par les métadonnées du modèle sera utilisé. Si le modèle ne contient aucune métadonnée ou si les métadonnées ne spécifient pas de seuil de classificateur, un seuil par défaut de 0,0 sera utilisé.

Nombre maximal d'étiquettes par objet

Nombre maximal d'étiquettes par objet que le détecteur renvoie. Si cette règle n'est pas configurée, la valeur par défaut (10) est utilisée.

Si vous ne disposez que d'un modèle groupé localement, il vous suffit de créer un détecteur d'objets à partir de votre objet 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;

Si vous disposez d'un modèle hébergé à distance, vous devez vérifier qu'il a été téléchargé avant de l'exécuter. Vous pouvez vérifier l'état de la tâche de téléchargement du modèle à l'aide de la méthode isModelDownloaded(remoteModel:) du gestionnaire de modèles.

Bien que vous deviez seulement vérifier cela avant d'exécuter le détecteur d'objets, si vous disposez à la fois d'un modèle hébergé à distance et d'un modèle groupé localement, il peut être judicieux d'effectuer cette vérification lors de l'instanciation de ObjectDetector: créez un détecteur à partir du modèle distant s'il a été téléchargé, et à défaut à partir du modèle local.

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;

Si vous ne disposez que d'un modèle hébergé à distance, vous devez désactiver les fonctionnalités liées au modèle (par exemple, griser ou masquer une partie de votre interface utilisateur) jusqu'à ce que vous confirmiez le téléchargement du modèle.

Vous pouvez obtenir l'état de téléchargement du modèle en associant des observateurs au centre de notifications par défaut. Veillez à utiliser une référence faible à self dans le bloc d'observateur, car les téléchargements peuvent prendre un certain temps et l'objet d'origine peut être libéré avant la fin du téléchargement. Exemple :

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 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 à partir d'une image statique.

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

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

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

4. Créer et exécuter le détecteur d'objets

  1. Créez un détecteur d'objets:

    Swift

    let objectDetector = ObjectDetector.objectDetector(options: options)

    Objective-C

    MLKObjectDetector *objectDetector = [MLKObjectDetector objectDetectorWithOptions:options];
  2. Ensuite, utilisez le détecteur:

    De manière asynchrone:

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

    De manière synchrone:

    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. Obtenir des informations sur les objets étiqueté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 SINGLE_IMAGE_MODE.
labels
label.text Description sous forme de texte du libellé. Ce champ n'est renvoyé que si les métadonnées du modèle TensorFlow Lite contiennent des descriptions de libellés.
label.index Index du libellé parmi tous les libellés acceptés par le classificateur.
label.confidence Valeur de confiance de la classification d'objets.

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

Assurer une expérience utilisateur optimale

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 [Application de vitrine ML Kit Material][showcase-link]{: .external} et la collection Modèles de Material Design pour les fonctionnalités basées sur le machine learning.

Amélioration des performances

Si vous souhaitez utiliser la détection d'objets dans une application en temps réel, suivez ces consignes pour obtenir les meilleures fréquences d'images:

  • Lorsque vous utilisez le mode streaming dans une application en temps réel, n'ayez pas recours à la détection d'objets multiples, car la plupart des appareils ne sont pas en mesure de produire des fréquences d'images adéquates.

  • 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.