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, celui-ci détecte jusqu'à cinq objets dans l'image ainsi que la position de chaque objet dans l'image. Lorsque vous détectez des objets dans flux vidéo, chaque objet possède un identifiant unique que vous pouvez utiliser pour suivre d'une image à l'autre.

Vous pouvez utiliser un modèle de classification d'images personnalisé pour classer les objets détecté. Consultez Modèles personnalisés avec ML Kit pour découvrir 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 grouper le modèle en le plaçant dans le dossier de composants de votre application, ou vous pouvez le télécharger depuis 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, qui augmente sa taille. Le modèle ne fait pas partie du fichier .ipa de votre application. Il est hébergé en 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 publier à nouveau votre application pour mettre à jour le modèle Déployer les mises à jour du modèle sans publier à nouveau votre application
Pas de tests A/B intégrés Tests A/B faciles avec Firebase Remote Config

Essayer

Avant de commencer

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

    Pour regrouper un modèle avec votre application:

    pod 'GoogleMLKit/ObjectDetectionCustom', '15.5.0'
    

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

    pod 'GoogleMLKit/ObjectDetectionCustom', '15.5.0'
    pod 'GoogleMLKit/LinkFirebase', '15.5.0'
    
  2. Après avoir installé ou mis à jour les pods de votre projet, ouvrez votre projet Xcode à l'aide de son .xcworkspace. ML Kit est compatible avec Xcode version 13.2.1 ou supérieur.

  3. Si vous souhaitez télécharger un modèle, assurez-vous ajouter Firebase à votre projet iOS ; si vous ne l'avez pas déjà fait. Cela n'est pas nécessaire lorsque vous regroupez les du modèle.

1. Charger le modèle

Configurer la source d'un modèle local

Pour empaqueter le modèle avec votre application:

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

  2. Créez l'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é sur 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 lors de sa publication:

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

Ensuite, démarrez la tâche de téléchargement du modèle, en spécifiant les conditions dans lesquelles que vous souhaitez autoriser le téléchargement. Si le modèle ne figure pas sur l'appareil, ou si un modèle plus récent du modèle est disponible, la tâche téléchargera de manière asynchrone depuis 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, vous pouvez le faire à tout moment avant d'avoir besoin d'utiliser le modèle.

2. Configurer le détecteur d'objets

Une fois les sources de modèle configurées, configurez le détecteur d'objets pour votre 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 (comme cadres de délimitation ou étiquettes de catégorie non spécifiés) sur les premiers les appels du détecteur. Dans STREAM_MODE, le détecteur attribue des identifiants de suivi aux objets, que vous pouvez utiliser pour suivre des objets sur des frames. Utilisez ce mode pour suivre ou lorsqu'une faible latence est importante, par exemple des flux vidéo en temps réel.

Dans SINGLE_IMAGE_MODE, le détecteur d'objets renvoie le résultat après la détermination du cadre de délimitation de l'objet. Si vous activer également la classification. Le résultat est renvoyé après la délimitation case et libellé de catégorie sont tous deux disponibles. Par conséquent, la latence de détection est potentiellement plus élevée. Dans SINGLE_IMAGE_MODE, aucun ID de suivi n'est attribué. Utilisez ce mode si la latence n'est pas critique et que vous ne voulez pas 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 seulement les plus objet proéminent (par défaut).

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

Indique si les objets détectés doivent être classés ou non à l'aide de la classe un modèle de classificateur personnalisé. Pour utiliser votre classification personnalisée : vous devez le définir sur true.

Seuil de confiance de la classification

Score de confiance minimal des étiquettes détectées. Si ce champ n'est pas défini, spécifié par les métadonnées du modèle sera utilisé. Si le modèle ne contient pas de métadonnées spécifiez un seuil de classificateur, un seuil par défaut de 0.0 utilisé.

Nombre maximal d'étiquettes par objet

Nombre maximal d'étiquettes par objet que le détecteur retour. Si cette règle n'est pas configurée, la valeur par défaut de 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 devrez vérifier qu'il a été téléchargée avant de l’exécuter. Vous pouvez vérifier l'état du téléchargement du modèle à l'aide de la méthode isModelDownloaded(remoteModel:) du gestionnaire de modèles.

Il suffit de vérifier avant d'exécuter le détecteur d'objets, vous disposez à la fois d'un modèle hébergé à distance et d'un modèle groupé localement, cela peut rendre d'effectuer cette vérification lors de l'instanciation de ObjectDetector: créez une du modèle distant s'il a été téléchargé et à partir du modèle local sinon.

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 paramètres (par exemple, griser ou masquer une partie de l'interface utilisateur), vous confirmez que le modèle a été téléchargé.

Vous pouvez obtenir l'état du téléchargement du modèle en associant des observateurs au modèle Centre de notifications. Veillez à utiliser une référence faible à self dans l'observateur , car les téléchargements peuvent prendre un certain temps et que l'objet d'origine peut être libérées une fois le téléchargement terminé. 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 utilisations principales cas:

  • Détection et suivi en direct de l'objet le plus proéminent de la caméra viseur.
  • La 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 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 la méthode Objet CMSampleBuffer et 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. Utilisez ensuite 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'image aboutit, soit il transmet une liste de Object au gestionnaire d'achèvement ou renvoie la liste, en fonction de que vous ayez appelé la méthode synchrone ou asynchrone.

Chaque Object contient les propriétés suivantes:

frame Un CGRect indiquant la position de l'objet dans l'image.
trackingID Entier qui identifie l'objet dans toutes les images, ou "nil" dans SINGLE_IMAGE_MODE
labels
label.text Texte descriptif du libellé. N'est renvoyé que si TensorFlow Les métadonnées du modèle Lite contiennent des descriptions d'étiquettes.
label.index Index de l'étiquette parmi toutes les étiquettes prises en charge par 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];
  }
}

Garantir une expérience utilisateur optimale

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

  • La réussite d'une détection d'objets dépend de sa complexité visuelle. Dans pour être détectés, les objets dotés d'un petit nombre de caractéristiques visuelles peuvent avoir besoin pour occuper une plus grande partie de l'image. Vous devez fournir aux utilisateurs des conseils sur en capturant une entrée qui fonctionne bien avec le type d'objets que vous souhaitez détecter.
  • Quand vous utilisez la classification, si vous souhaitez détecter les objets qui ne tombent pas correctement dans les catégories prises en charge, implémenter un traitement spécial pour les d'objets.

Consultez également les [Application de présentation Material Design de ML Kit][showcase-link]{: .external } et Material Design Modèles pour la collection de caractéristiques 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 pour obtenir des fréquences d'images optimales:

  • Lorsque vous utilisez le mode de traitement par flux dans une application en temps réel, n'utilisez pas plusieurs la détection d'objets, 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. Appeler cette méthode à partir de AVCaptureVideoDataOutputSampleBufferDelegate <ph type="x-smartling-placeholder"></ph> captureOutput(_, didOutput:from:) pour obtenir les résultats d'une vidéo donnée de manière synchrone. cadre. Conserver <ph type="x-smartling-placeholder"></ph> de AVCaptureVideoDataOutput alwaysDiscardsLateVideoFrames en tant que true afin de limiter les appels au détecteur. Si un nouveau l'image vidéo devient disponible pendant l'exécution du détecteur, elle est ignorée.
  • Si vous utilisez la sortie du détecteur pour superposer des graphiques sur l'image d'entrée, récupérez d'abord le résultat à partir de ML Kit, puis effectuez le rendu de l'image. et les superposer en une seule étape. Cela vous permet d'afficher sur la surface d'affichage une seule fois pour chaque trame d'entrée traitée. Affichez la vue updatePreviewOverlayViewWithLastFrame. dans l'exemple de démarrage rapide de ML Kit.