Как определять позы с помощью ML Kit на iOS

ML Kit предоставляет два оптимизированных SDK для распознавания поз.

Название SDKPoseDetectionPoseDetectionAccurate
РеализацияОбъекты для базового детектора статически связываются с приложением во время сборки.Объекты для точного детектора статически связываются с приложением во время сборки.
Размер приложенияДо 29,6 МБДо 33,2 МБ
ЭффективностьiPhone X: ~45 кадров/сiPhone X: ~29 кадров/с

Попробовать

Подготовка

  1. Добавьте в Podfile следующие поды ML Kit:

    # If you want to use the base implementation:
    pod 'GoogleMLKit/PoseDetection', '8.0.0'
    
    # If you want to use the accurate implementation:
    pod 'GoogleMLKit/PoseDetectionAccurate', '8.0.0'
    
  2. После установки или обновления pod-файлов проекта откройте проект Xcode, используя файл xcworkspace. ML Kit поддерживается в Xcode версии 13.2.1 или выше.

1. Создайте экземпляр PoseDetector.

Чтобы распознать позу на изображении, сначала создайте экземпляр PoseDetector и при необходимости задайте настройки детектора.

PoseDetector варианта

Режим обнаружения

PoseDetector работает в двух режимах обнаружения. Обязательно выберите тот, который соответствует вашему варианту использования.

stream (по умолчанию)
Детектор поз сначала обнаружит наиболее заметного человека на изображении, а затем запустит обнаружение позы. В последующих кадрах этап обнаружения человека не будет проводиться, если только человек не будет скрыт или больше не будет обнаруживаться с высокой степенью уверенности. Детектор поз попытается отследить наиболее заметного человека и вернуть его позу в каждом выводе. Это уменьшает задержку и делает распознавание более плавным. Используйте этот режим, если хотите распознавать позы в видеопотоке.
singleImage
Детектор поз обнаруживает человека, а затем определяет его позу. Этап обнаружения людей будет выполняться для каждого изображения, поэтому задержка будет выше, а отслеживание людей не будет выполняться. Используйте этот режим при использовании позы обнаружение на статических изображениях или там, где отслеживание нежелательно.

Укажите параметры детектора позы:

Swift

// Base pose detector with streaming, when depending on the PoseDetection SDK
let options = PoseDetectorOptions()
options.detectorMode = .stream

// Accurate pose detector on static images, when depending on the
// PoseDetectionAccurate SDK
let options = AccuratePoseDetectorOptions()
options.detectorMode = .singleImage

Objective-C

// Base pose detector with streaming, when depending on the PoseDetection SDK
MLKPoseDetectorOptions *options = [[MLKPoseDetectorOptions alloc] init];
options.detectorMode = MLKPoseDetectorModeStream;

// Accurate pose detector on static images, when depending on the
// PoseDetectionAccurate SDK
MLKAccuratePoseDetectorOptions *options =
    [[MLKAccuratePoseDetectorOptions alloc] init];
options.detectorMode = MLKPoseDetectorModeSingleImage;

Наконец, получите экземпляр PoseDetector. Передайте указанные вами параметры:

Swift

let poseDetector = PoseDetector.poseDetector(options: options)

Objective-C

MLKPoseDetector *poseDetector =
    [MLKPoseDetector poseDetectorWithOptions:options];

2. Подготовьте входное изображение

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

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

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

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

    Swift

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

    Objective-C

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

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

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

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

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

    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. Обработка изображения

Передайте VisionImage в один из методов обработки изображений детектора поз. Вы можете использовать асинхронный метод process(image:) или синхронный метод results().

Чтобы распознавать объекты синхронно:

Swift

var results: [Pose]
do {
  results = try poseDetector.results(in: image)
} catch let error {
  print("Failed to detect pose with error: \(error.localizedDescription).")
  return
}
guard let detectedPoses = results, !detectedPoses.isEmpty else {
  print("Pose detector returned no results.")
  return
}

// Success. Get pose landmarks here.

Objective-C

NSError *error;
NSArray *poses = [poseDetector resultsInImage:image error:&error];
if (error != nil) {
  // Error.
  return;
}
if (poses.count == 0) {
  // No pose detected.
  return;
}

// Success. Get pose landmarks here.

Чтобы распознавать объекты асинхронно:

Swift

poseDetector.process(image) { detectedPoses, error in
  guard error == nil else {
    // Error.
    return
  }
  guard !detectedPoses.isEmpty else {
    // No pose detected.
    return
  }

  // Success. Get pose landmarks here.
}

Objective-C

[poseDetector processImage:image
                completion:^(NSArray * _Nullable poses,
                             NSError * _Nullable error) {
                    if (error != nil) {
                      // Error.
                      return;
                    }
                    if (poses.count == 0) {
                      // No pose detected.
                      return;
                    }

                    // Success. Get pose landmarks here.
                  }];

4. Получение информации об обнаруженной позе

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

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

Если человек не обнаружен, массив пуст.

Swift

for pose in detectedPoses {
  let leftAnkleLandmark = pose.landmark(ofType: .leftAnkle)
  if leftAnkleLandmark.inFrameLikelihood > 0.5 {
    let position = leftAnkleLandmark.position
  }
}

Objective-C

for (MLKPose *pose in detectedPoses) {
  MLKPoseLandmark *leftAnkleLandmark =
      [pose landmarkOfType:MLKPoseLandmarkTypeLeftAnkle];
  if (leftAnkleLandmark.inFrameLikelihood > 0.5) {
    MLKVision3DPoint *position = leftAnkleLandmark.position;
  }
}

Советы по повышению эффективности

Качество результатов зависит от качества исходного изображения:

  • Чтобы ML Kit мог точно определить позу, человек на изображении должен быть представлен достаточным количеством пиксельных данных; для лучшей производительности объект должен быть размером не менее 256x256 пикселей.
  • Если вы обнаруживаете позу в приложении реального времени, вы также можете рассмотреть общие размеры входных изображений. Изображения меньшего размера обрабатываются быстрее, поэтому, чтобы сократить задержку, делайте снимки с более низким разрешением, но помните о требованиях к разрешению, указанных выше, и следите за тем, чтобы объект занимал как можно большую часть изображения.
  • Плохая фокусировка изображения также может повлиять на точность. Если результаты неудовлетворительны, попросите пользователя сделать снимок заново.

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

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

Дальнейшие действия