Detectar poses com o Kit de ML no iOS

O Kit de ML oferece dois SDKs otimizados para detecção de pose.

Nome do SDKPoseDetectionPoseDetectionAccurate
ImplementaçãoOs recursos do detector de base são vinculados de forma estática ao app no momento da build.Os recursos do detector preciso são vinculados de forma estática ao app no momento da criação.
Tamanho do appAté 29,6 MBAté 33,2 MB
DesempenhoiPhone X: ~45FPSiPhone X: ~29FPS

Faça um teste

Antes de começar

  1. Inclua os seguintes pods do Kit de ML no Podfile:

    # If you want to use the base implementation:
    pod 'GoogleMLKit/PoseDetection', '7.0.0'
    
    # If you want to use the accurate implementation:
    pod 'GoogleMLKit/PoseDetectionAccurate', '7.0.0'
    
  2. Depois de instalar ou atualizar os pods do projeto, abra o projeto do Xcode usando o xcworkspace. O Kit de ML é compatível com a versão 13.2.1 ou mais recente do Xcode.

1. Criar uma instância de PoseDetector

Para detectar uma pose em uma imagem, primeiro crie uma instância de PoseDetector e especifique as configurações do detector, se quiser.

PoseDetector opções

Modo de detecção

O PoseDetector opera em dois modos de detecção. Escolha a opção que corresponde ao seu caso de uso.

stream (padrão)
O detector de pose primeiro detecta a pessoa mais proeminente na imagem e depois executa a detecção de pose. Em frames subsequentes, a etapa de detecção de pessoas não será realizada, a menos que a pessoa fique obscura ou não seja mais detectada com alta confiança. O detector de pose vai tentar rastrear a pessoa mais proeminente e retornar a pose dela em cada inferência. Isso reduz a latência e agiliza a detecção. Use esse modo quando quiser detectar a pose em um stream de vídeo.
singleImage
O detector de pose vai detectar uma pessoa e executar a detecção de pose. A etapa de detecção de pessoas será executada para cada imagem, portanto, a latência será maior e não haverá rastreamento de pessoas. Use esse modo ao usar a detecção de pose em imagens estáticas ou quando o rastreamento não for necessário.

Especifique as opções do detector de pose:

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

Por fim, receba uma instância de PoseDetector. Transmita as opções especificadas:

let poseDetector = PoseDetector.poseDetector(options: options)
MLKPoseDetector *poseDetector =
    [MLKPoseDetector poseDetectorWithOptions:options];

2. Preparar a imagem de entrada

Para detectar poses, siga as etapas abaixo para cada imagem ou frame de vídeo. Se você tiver ativado o modo de stream, precisará criar objetos VisionImage usando CMSampleBuffer.

Crie um objeto VisionImage usando um UIImage ou um CMSampleBuffer.

Se você usa um UIImage, siga estas etapas:

  • Crie um objeto VisionImage com o UIImage. Especifique a .orientation correta.
    let image = VisionImage(image: UIImage)
    visionImage.orientation = image.imageOrientation
    MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image];
    visionImage.orientation = image.imageOrientation;

Se você usa um CMSampleBuffer, siga estas etapas:

  • Especifique a orientação dos dados da imagem contidos no CMSampleBuffer.

    Para conferir a orientação da imagem:

    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
      }
    }
          
    - (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;
      }
    }
          
  • Crie um objeto VisionImage usando o objeto CMSampleBuffer e a orientação:
    let image = VisionImage(buffer: sampleBuffer)
    image.orientation = imageOrientation(
      deviceOrientation: UIDevice.current.orientation,
      cameraPosition: cameraPosition)
     MLKVisionImage *image = [[MLKVisionImage alloc] initWithBuffer:sampleBuffer];
     image.orientation =
       [self imageOrientationFromDeviceOrientation:UIDevice.currentDevice.orientation
                                    cameraPosition:cameraPosition];

3. Processar a imagem

Transmita o VisionImage para um dos métodos de processamento de imagem do detector de poses. É possível usar o método process(image:) assíncrono ou o método results() síncrono.

Para detectar objetos de modo síncrono:

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

Para detectar objetos de modo assíncrono:

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.
}
[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. Receber informações sobre a pose detectada

Se uma pessoa for detectada na imagem, a API de detecção de pose vai transmitir uma matriz de objetos Pose para o gerenciador de conclusão ou retornar a matriz, dependendo se você chamou o método assíncrono ou síncrono.

Se a pessoa não estiver completamente dentro da imagem, o modelo vai atribuir as coordenadas dos pontos de referência ausentes fora do frame e vai atribuir a elas valores de InFrameConfidence baixos.

Se nenhuma pessoa for detectada, a matriz vai estar vazia.

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

Dicas para melhorar a performance

A qualidade dos resultados depende da qualidade da imagem de entrada:

  • Para que o Kit de ML detecte a pose com precisão, a pessoa na imagem precisa ser representada por dados de pixel suficientes. Para ter a melhor performance, o assunto precisa ter pelo menos 256 x 256 pixels.
  • Se você detectar a pose em um aplicativo em tempo real, considere as dimensões gerais das imagens de entrada. Já que as imagens menores podem ser processadas mais rapidamente, reduza a latência capturando imagens em resoluções menores, mas lembre-se dos requisitos de resolução acima e faça o objeto ocupar o máximo possível da imagem.
  • Uma imagem com foco inadequado também pode afetar a precisão. Se os resultados não forem aceitáveis, peça para o usuário recapturar a imagem.

Se você quiser usar a detecção de pose em um aplicativo em tempo real, siga estas diretrizes para conseguir as melhores taxas de frames:

  • Use o SDK básico do PoseDetection e o modo de detecção stream.
  • Capture imagens em uma resolução menor. No entanto, lembre-se também dos requisitos de dimensão de imagem da API.
  • Para processar frames de vídeo, use a API síncrona results(in:) do detector. Chame esse método da função captureOutput(_, didOutput:from:) da AVCaptureVideoDataOutputSampleBufferDelegate para receber resultados síncronos do frame de vídeo fornecido. Mantenha AVCaptureVideoDataOutput alwaysDiscardsLateVideoFrames como verdadeiro para limitar as chamadas ao detector. Se um novo frame de vídeo ficar disponível durante a execução do detector, ele será descartado.
  • Se você usar a saída do detector para sobrepor elementos gráficos na imagem de entrada, primeiro acesse o resultado do Kit de ML e, em seguida, renderize a imagem e a sobreposição em uma única etapa. Ao fazer isso, você renderiza a superfície de exibição apenas uma vez para cada frame de entrada processado. Consulte as classes previewOverlayView e MLKDetectionOverlayView no app de exemplo da demonstração para conferir um exemplo.

Próximas etapas