Обнаруживайте, отслеживайте и классифицируйте объекты с помощью собственной модели классификации на iOS.

Вы можете использовать ML Kit для обнаружения и отслеживания объектов в последовательных видеокадрах.

Когда вы передаете изображение в ML Kit, он обнаруживает до пяти объектов на изображении, а также положение каждого объекта на изображении. При обнаружении объектов в видеопотоках каждый объект имеет уникальный идентификатор, по которому можно отслеживать объект от кадра к кадру.

Вы можете использовать пользовательскую модель классификации изображений для классификации обнаруженных объектов. Обратитесь к разделу Пользовательские модели с комплектом ML, чтобы узнать о требованиях к совместимости моделей, где найти предварительно обученные модели и как обучать собственные модели.

Существует два способа интеграции пользовательской модели. Вы можете связать модель, поместив ее в папку ресурсов вашего приложения, или динамически загрузить ее из Firebase. В следующей таблице сравниваются два варианта.

Модель в комплекте Размещенная модель
Модель является частью файла .ipa вашего приложения, что увеличивает его размер. Модель не является частью .ipa файла вашего приложения. Он размещается путем загрузки в Firebase Machine Learning .
Модель доступна сразу, даже когда Android-устройство находится в автономном режиме. Модель скачивается по запросу.
Нет необходимости в проекте Firebase Требуется проект Firebase
Вам необходимо повторно опубликовать свое приложение, чтобы обновить модель. Отправляйте обновления модели без повторной публикации приложения.
Нет встроенного A/B-тестирования. Простое A/B-тестирование с помощью Firebase Remote Config

Попробуйте это

Прежде чем начать

  1. Включите библиотеки ML Kit в свой подфайл:

    Для объединения модели с вашим приложением:

    pod 'GoogleMLKit/ObjectDetectionCustom', '3.2.0'
    

    Для динамической загрузки модели из Firebase добавьте зависимость LinkFirebase :

    pod 'GoogleMLKit/ObjectDetectionCustom', '3.2.0'
    pod 'GoogleMLKit/LinkFirebase', '3.2.0'
    
  2. После установки или обновления модулей вашего проекта откройте проект Xcode, используя его .xcworkspace . ML Kit поддерживается в Xcode версии 13.2.1 или выше.

  3. Если вы хотите загрузить модель , обязательно добавьте Firebase в свой проект iOS , если вы еще этого не сделали. Это не требуется при объединении модели.

1. Загрузите модель

Настройте источник локальной модели

Чтобы связать модель с вашим приложением:

  1. Скопируйте файл модели (обычно заканчивающийся на .tflite или .lite ) в свой проект Xcode, при этом не забывая выбирать Copy bundle resources . Файл модели будет включен в пакет приложения и доступен в ML Kit.

  2. Создайте объект LocalModel , указав путь к файлу модели:

    Быстрый

    let localModel = LocalModel(path: localModelFilePath)

    Цель-C

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

Настройте источник модели, размещенный в Firebase

Чтобы использовать удаленно размещенную модель, создайте объект CustomRemoteModel , указав имя, которое вы присвоили модели при ее публикации:

Быстрый

let firebaseModelSource = FirebaseModelSource(
    name: "your_remote_model") // The name you assigned in
                               // the Firebase console.
let remoteModel = CustomRemoteModel(remoteModelSource: firebaseModelSource)

Цель-C

MLKFirebaseModelSource *firebaseModelSource =
    [[MLKFirebaseModelSource alloc]
        initWithName:@"your_remote_model"]; // The name you assigned in
                                            // the Firebase console.
MLKCustomRemoteModel *remoteModel =
    [[MLKCustomRemoteModel alloc]
        initWithRemoteModelSource:firebaseModelSource];

Затем запустите задачу загрузки модели, указав условия, при которых вы хотите разрешить загрузку. Если модели нет на устройстве или доступна более новая версия модели, задача асинхронно загрузит модель из Firebase:

Быстрый

let downloadConditions = ModelDownloadConditions(
  allowsCellularAccess: true,
  allowsBackgroundDownloading: true
)

let downloadProgress = ModelManager.modelManager().download(
  remoteModel,
  conditions: downloadConditions
)

Цель-C

MLKModelDownloadConditions *downloadConditions =
    [[MLKModelDownloadConditions alloc] initWithAllowsCellularAccess:YES
                                         allowsBackgroundDownloading:YES];

NSProgress *downloadProgress =
    [[MLKModelManager modelManager] downloadModel:remoteModel
                                       conditions:downloadConditions];

Многие приложения запускают задачу загрузки в своем коде инициализации, но вы можете сделать это в любой момент, прежде чем вам понадобится использовать модель.

2. Настройте детектор объектов

После настройки источников модели настройте детектор объектов для вашего варианта использования с помощью объекта CustomObjectDetectorOptions . Вы можете изменить следующие настройки:

Настройки детектора объектов
Режим обнаружения STREAM_MODE (по умолчанию) | SINGLE_IMAGE_MODE

В режиме STREAM_MODE (по умолчанию) детектор объектов работает с низкой задержкой, но может давать неполные результаты (например, неуказанные ограничивающие рамки или метки категорий) при первых нескольких вызовах детектора. Кроме того, в STREAM_MODE детектор присваивает объектам идентификаторы отслеживания, которые можно использовать для отслеживания объектов между кадрами. Используйте этот режим, если вы хотите отслеживать объекты или когда важна низкая задержка, например, при обработке видеопотоков в реальном времени.

В SINGLE_IMAGE_MODE детектор объектов возвращает результат после определения ограничивающей рамки объекта. Если вы также включите классификацию, результат будет возвращен после того, как будут доступны ограничивающая рамка и метка категории. Как следствие, задержка обнаружения потенциально выше. Кроме того, в SINGLE_IMAGE_MODE не назначаются идентификаторы отслеживания. Используйте этот режим, если задержка не критична и вы не хотите иметь дело с частичными результатами.

Обнаружение и отслеживание нескольких объектов false (по умолчанию) | true

Обнаруживать и отслеживать до пяти объектов или только самый заметный объект (по умолчанию).

Классифицировать объекты false (по умолчанию) | true

Следует ли классифицировать обнаруженные объекты с помощью предоставленной пользовательской модели классификатора. Чтобы использовать собственную модель классификации, вам необходимо установить для этого параметра значение true .

Порог достоверности классификации

Минимальный показатель достоверности обнаруженных меток. Если не установлено, будет использоваться любое пороговое значение классификатора, указанное в метаданных модели. Если модель не содержит метаданных или в метаданных не указан порог классификатора, будет использоваться порог по умолчанию, равный 0,0.

Максимальное количество ярлыков на объект

Максимальное количество меток на объект, возвращаемое детектором. Если не установлено, будет использоваться значение по умолчанию 10.

Если у вас есть только локально связанная модель, просто создайте детектор объектов из вашего объекта LocalModel :

Быстрый

let options = CustomObjectDetectorOptions(localModel: localModel)
options.detectorMode = .singleImage
options.shouldEnableClassification = true
options.shouldEnableMultipleObjects = true
options.classificationConfidenceThreshold = NSNumber(value: 0.5)
options.maxPerObjectLabelCount = 3

Цель-C

MLKCustomObjectDetectorOptions *options =
    [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel];
options.detectorMode = MLKObjectDetectorModeSingleImage;
options.shouldEnableClassification = YES;
options.shouldEnableMultipleObjects = YES;
options.classificationConfidenceThreshold = @(0.5);
options.maxPerObjectLabelCount = 3;

Если у вас есть удаленно размещенная модель, вам придется убедиться, что она загружена, прежде чем запускать ее. Вы можете проверить состояние задачи загрузки модели с помощью метода isModelDownloaded(remoteModel:) менеджера моделей.

Хотя вам нужно подтвердить это только перед запуском детектора объектов, если у вас есть как удаленно размещенная модель, так и локально связанная модель, возможно, имеет смысл выполнить эту проверку при создании экземпляра ObjectDetector : создайте детектор из удаленной модели, если оно скачано, а иначе из локальной модели.

Быстрый

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

Цель-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;

Если у вас есть только удаленно размещенная модель, вам следует отключить функции, связанные с моделью (например, сделать их серыми или скрыть часть пользовательского интерфейса), пока вы не подтвердите, что модель загружена.

Вы можете получить статус загрузки модели, присоединив наблюдателей к Центру уведомлений по умолчанию. Обязательно используйте слабую ссылку на self в блоке наблюдателя, поскольку загрузка может занять некоторое время, а исходный объект может быть освобожден к моменту завершения загрузки. Например:

Быстрый

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

Цель-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];
            }];

API обнаружения и отслеживания объектов оптимизирован для этих двух основных случаев использования:

  • Обнаружение и отслеживание самого заметного объекта в видоискателе камеры в реальном времени.
  • Обнаружение нескольких объектов на статическом изображении.

Чтобы настроить API для этих случаев использования:

Быстрый

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

Цель-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. Подготовьте входное изображение.

Создайте объект VisionImage используя UIImage или CMSampleBuffer .

Если вы используете UIImage , выполните следующие действия:

  • Создайте объект VisionImage с помощью UIImage . Обязательно укажите правильную .orientation .

    Быстрый

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

    Цель-C

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

Если вы используете CMSampleBuffer , выполните следующие действия:

  • Укажите ориентацию данных изображения, содержащихся в CMSampleBuffer .

    Чтобы получить ориентацию изображения:

    Быстрый

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

    Цель-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;
      }
    }
          
  • Создайте объект VisionImage используя объект CMSampleBuffer и ориентацию:

    Быстрый

    let image = VisionImage(buffer: sampleBuffer)
    image.orientation = imageOrientation(
      deviceOrientation: UIDevice.current.orientation,
      cameraPosition: cameraPosition)

    Цель-C

     MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer];
     image.orientation =
       [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                    cameraPosition:cameraPosition];

4. Создайте и запустите детектор объектов.

  1. Создайте новый детектор объектов:

    Быстрый

    let objectDetector = ObjectDetector.objectDetector(options: options)

    Цель-C

    MLKObjectDetector *objectDetector = [MLKObjectDetector objectDetectorWithOptions:options];
  2. Затем используйте детектор:

    Асинхронно:

    Быстрый

    objectDetector.process(image) { objects, error in
        guard error == nil, let objects = objects, !objects.isEmpty else {
            // Handle the error.
            return
        }
        // Show results.
    }

    Цель-C

    [objectDetector
        processImage:image
          completion:^(NSArray *_Nullable objects,
                       NSError *_Nullable error) {
            if (objects.count == 0) {
                // Handle the error.
                return;
            }
            // Show results.
         }];

    Синхронно:

    Быстрый

    var objects: [Object]
    do {
        objects = try objectDetector.results(in: image)
    } catch let error {
        // Handle the error.
        return
    }
    // Show results.

    Цель-C

    NSError *error;
    NSArray *objects =
        [objectDetector resultsInImage:image error:&error];
    // Show results or handle the error.

5. Получить информацию о помеченных объектах

Если вызов процессора изображений успешен, он либо передает список Object s обработчику завершения, либо возвращает список, в зависимости от того, вызвали ли вы асинхронный или синхронный метод.

Каждый Object содержит следующие свойства:

frame CGRect , указывающий положение объекта на изображении.
trackingID Целое число, которое идентифицирует объект на всех изображениях, или «ноль» в SINGLE_IMAGE_MODE.
labels
label.text Текстовое описание метки. Возвращается только в том случае, если метаданные модели TensorFlow Lite содержат описания меток.
label.index Индекс метки среди всех меток, поддерживаемых классификатором.
label.confidence Доверительная ценность классификации объектов.

Быстрый

// 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")
}

Цель-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];
  }
}

Обеспечение отличного пользовательского опыта

Для обеспечения наилучшего пользовательского опыта следуйте этим рекомендациям в своем приложении:

  • Успешное обнаружение объекта зависит от визуальной сложности объекта. Чтобы быть обнаруженными, объектам с небольшим количеством визуальных особенностей может потребоваться занимать большую часть изображения. Вы должны предоставить пользователям рекомендации по захвату входных данных, которые хорошо работают с объектами того типа, которые вы хотите обнаружить.
  • Если при использовании классификации вы хотите обнаружить объекты, которые не попадают в поддерживаемые категории, реализуйте специальную обработку неизвестных объектов.

Также ознакомьтесь с [демонстрационным приложением ML Kit Material Design][showcase-link]{: .external } и коллекцией шаблонов Material Design для функций машинного обучения .

Улучшение производительности

Если вы хотите использовать обнаружение объектов в приложении реального времени, следуйте этим рекомендациям для достижения наилучшей частоты кадров:

  • Когда вы используете режим потоковой передачи в приложении реального времени, не используйте обнаружение нескольких объектов, поскольку большинство устройств не смогут обеспечить адекватную частоту кадров.

  • Для обработки видеокадров используйте синхронный API results(in:) детектора. Вызовите этот метод из функции captureOutput(_, didOutput:from:) AVCaptureVideoDataOutputSampleBufferDelegate , чтобы синхронно получить результаты из данного видеокадра. Оставьте для AVCaptureVideoDataOutput значение alwaysDiscardsLateVideoFrames как true , чтобы ограничить вызовы детектора. Если во время работы детектора появится новый видеокадр, он будет удален.
  • Если вы используете выходные данные детектора для наложения графики на входное изображение, сначала получите результат из ML Kit, затем визуализируйте изображение и наложите его за один шаг. При этом вы выполняете рендеринг на поверхность дисплея только один раз для каждого обработанного входного кадра. Пример см. в updatePreviewOverlayViewWithLastFrame в образце быстрого запуска ML Kit.