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

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

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

Для классификации обнаруженных объектов можно использовать собственную модель классификации изображений. Подробную информацию о требованиях к совместимости моделей, местах поиска предварительно обученных моделей и способах обучения собственных моделей см. в разделе «Создание пользовательских моделей с помощью ML Kit».

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

Комплексная модель Хостинговая модель
Модель является частью файла .ipa вашего приложения, что увеличивает его размер. Модель не является частью файла .ipa вашего приложения. Она размещается путем загрузки в Cloud Storage. Мы рекомендуем использовать Cloud Storage для Firebase .
Данная модель доступна сразу же, даже когда устройство Android находится в автономном режиме. Ваше приложение должно содержать код для загрузки модели по запросу.
Нет необходимости в проекте Firebase. Требуется проект Firebase (если используется Cloud Storage for Firebase).
Для обновления модели необходимо повторно опубликовать приложение. Обновляйте модель приложения, не переиздавая его.
Встроенного A/B-тестирования нет. A/B-тестирование с использованием Firebase Remote Config

Попробуйте!

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

  1. Включите библиотеки ML Kit в свой Podfile:

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

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

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

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

Чтобы включить модель в ваше приложение:

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

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

    Быстрый

    let localModel = LocalModel(path: localModelFilePath)

    Objective-C

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

Настройте удаленно размещенный источник модели.

Для использования модели, размещенной удаленно, необходимо загрузить файл модели в локальное хранилище устройства, используя собственную логику приложения, а затем загрузить его как локальную модель. Мы рекомендуем использовать Cloud Storage for Firebase для размещения модели. Подробности реализации см. в руководстве по миграции Firebase ML в Cloud Storage .

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

Objective-C

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

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

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

Быстрый

// Path where your download logic saves the model
let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let localModelURL = documentDirectory.appendingPathComponent("my_remote_model.tflite")

let model: LocalModel
if FileManager.default.fileExists(atPath: localModelURL.path) {
  // Use the downloaded model
  model = LocalModel(path: localModelURL.path)
} else {
  // Fall back to bundled model
  guard let bundledModelPath = Bundle.main.path(forResource: "model", ofType: "tflite") else { return }
  model = LocalModel(path: bundledModelPath)
}

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

Objective-C

NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *localModelPath = [documentsDirectory stringByAppendingPathComponent:@"my_remote_model.tflite"];

MLKLocalModel *model;
if ([NSFileManager.defaultManager fileExistsAtPath:localModelPath]) {
  // Use the downloaded model
  model = [[MLKLocalModel alloc] initWithPath:localModelPath];
} else {
  // Fall back to bundled model
  NSString *bundledModelPath = [NSBundle.mainBundle pathForResource:@"model" ofType:@"tflite"];
  model = [[MLKLocalModel alloc] initWithPath:bundledModelPath];
}

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

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

Быстрый

let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
let localModelURL = documentDirectory.appendingPathComponent("my_remote_model.tflite")
if FileManager.default.fileExists(atPath: localModelURL.path) {
  // Model is already cached, initialize immediately
  self.initializeDetector(with: localModelURL)
} else {
  // Model is not yet available, show loading UI and start download
  self.showLoadingUI()
  let storage = Storage.storage()
  let modelRef = storage.reference(forURL: "gs://YOUR_BUCKET/path/to/model.tflite")
  modelRef.write(toFile: localModelURL) { url, error in
    self.hideLoadingUI()
    if let error = error {
      // Handle download error
      self.showErrorUI()
    } else if let modelURL = url {
      // Download success, initialize detector
      self.initializeDetector(with: modelURL)
    }
  }
}

func initializeDetector(with modelURL: URL) {
  let localModel = LocalModel(path: modelURL.path)
  let options = CustomObjectDetectorOptions(localModel: localModel)
  options.detectorMode = .singleImage
  options.shouldEnableClassification = true
  options.shouldEnableMultipleObjects = true
  self.objectDetector = ObjectDetector.objectDetector(options: options)
  // Enable ML features in UI
  self.enableMLFeatures()
}

Objective-C

NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *localModelPath = [documentsDirectory stringByAppendingPathComponent:@"my_remote_model.tflite"];
NSURL *localModelURL = [NSURL fileURLWithPath:localModelPath];

if ([NSFileManager.defaultManager fileExistsAtPath:localModelPath]) {
  // Model is already cached, initialize immediately
  [self initializeDetectorWithURL:localModelURL];
} else {
  // Model is not yet available, show loading UI and start download
  [self showLoadingUI];

  FIRStorage *storage = [FIRStorage storage];
  FIRStorageReference *modelRef = [storage referenceForURL:@"gs://YOUR_BUCKET/path/to/model.tflite"];

  [modelRef writeToFile:localModelURL
             completion:^(NSURL * _Nullable URL, NSError * _Nullable error) {
               [self hideLoadingUI];
               if (error != nil) {
                 // Handle download error
                 [self showErrorUI];
               } else {
                 // Download success, initialize detector
                 [self initializeDetectorWithURL:URL];
               }
             }];
}

- (void)initializeDetectorWithURL:(NSURL *)modelURL {
  MLKLocalModel *localModel = [[MLKLocalModel alloc] initWithPath:modelURL.path];
  MLKCustomObjectDetectorOptions *options = [[MLKCustomObjectDetectorOptions alloc] initWithLocalModel:localModel];
  options.detectorMode = MLKObjectDetectorModeSingleImage;
  options.shouldEnableClassification = YES;
  options.shouldEnableMultipleObjects = YES;
  self.objectDetector = [MLKObjectDetector objectDetectorWithOptions:options];

  // Enable ML features in UI
  [self enableMLFeatures];
}

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

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

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

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

  • Создайте объект VisionImage с использованием UIImage . Убедитесь, что указана правильная .orientation ).

    Быстрый

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

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

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

    Быстрый

    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. Создайте и запустите детектор объектов.

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

    Быстрый

    let objectDetector = ObjectDetector.objectDetector(options: options)

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

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

    Objective-C

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

5. Получите информацию об объектах с соответствующими обозначениями.

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

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

frame Объект CGRect , указывающий положение объекта на изображении.
trackingID Целочисленное значение, идентифицирующее объект на разных изображениях, или `nil` в режиме SINGLE_IMAGE_MODE.
labels
label.text Текстовое описание метки. Возвращается только в том случае, если метаданные модели LiteRT содержат описания меток.
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")
}

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

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

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

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

Также ознакомьтесь с демонстрационным приложением ML Kit Material Design и коллекцией шаблонов Material Design для функций, использующих машинное обучение .

Повышение производительности

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

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

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