Detecta poses con ML Kit en iOS

El Kit de AA proporciona dos SDKs optimizados para la detección de poses.

Nombre del SDKPoseDetectionPoseDetectionAccurate
ImplementaciónLos recursos del detector base se vinculan de forma estática a tu app en el tiempo de compilación.Los recursos para el detector preciso se vinculan de forma estática a tu app en el tiempo de compilación.
Tamaño de la appHasta 29.6 MBHasta 33.2 MB
RendimientoiPhone X: ~45 FPSiPhone X: ~29 FPS

Probar

Antes de comenzar

  1. Incluye los siguientes pods de ML Kit en tu 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. Después de instalar o actualizar los pods de tu proyecto, abre el proyecto de Xcode con su xcworkspace. El Kit de AA es compatible con Xcode 13.2.1 o versiones posteriores.

1. Crea una instancia de PoseDetector.

Para detectar una pose en una imagen, primero crea una instancia de PoseDetector y, de manera opcional, especifica la configuración del detector.

PoseDetector opciones

Modo de detección

PoseDetector funciona en dos modos de detección. Asegúrate de elegir el que coincida con tu caso de uso.

stream (predeterminada)
El detector de poses primero detectará a la persona más prominente en la imagen y, luego, ejecutará la detección de poses. En los fotogramas posteriores, no se realizará el paso de detección de personas, a menos que la persona se oculte o ya no se detecte con alta confianza. El detector de poses intentará hacer un seguimiento de la persona más prominente y mostrar su pose en cada inferencia. Esto reduce la latencia y suaviza la detección. Usa este modo cuando quieras detectar la postura en una transmisión de video.
singleImage
El detector de poses detectará a una persona y, luego, ejecutará la detección de poses. El paso de detección de personas se ejecutará para cada imagen, por lo que la latencia será más alta y no habrá seguimiento de personas. Usa este modo cuando uses la detección de poses en imágenes estáticas o cuando no se desee realizar un seguimiento.

Especifica las opciones del detector de poses:

// 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 último, obtén una instancia de PoseDetector. Pasa las opciones que especificaste:

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

2. Prepara la imagen de entrada

Para detectar poses, haz lo siguiente con cada imagen o fotograma de video. Si habilitaste el modo de transmisión, debes crear objetos VisionImage a partir de varias CMSampleBuffer.

Crea un objeto VisionImage con una UIImage o una CMSampleBuffer.

Si usas una UIImage, sigue estos pasos:

  • Crea un objeto VisionImage con UIImage. Asegúrate de especificar la .orientation correcta.
    let image = VisionImage(image: UIImage)
    visionImage.orientation = image.imageOrientation
    MLKVisionImage *visionImage = [[MLKVisionImage alloc] initWithImage:image];
    visionImage.orientation = image.imageOrientation;

Si usas una CMSampleBuffer, sigue estos pasos:

  • Especifica la orientación de los datos de imagen contenidos en CMSampleBuffer.

    Para obtener la orientación de la imagen, haz lo siguiente:

    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;
      }
    }
          
  • Crea un objeto VisionImage con el objeto CMSampleBuffer y la orientación:
    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. Procesa la imagen

Pasa la VisionImage a uno de los métodos de procesamiento de imágenes del detector de poses. Puedes usar el método asíncrono process(image:) o el método síncrono results().

Haz lo siguiente para detectar objetos de manera síncrona:

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.

Haz lo siguiente para detectar objetos de manera asíncrona:

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. Obtén información sobre la postura detectada

Si se detecta una persona en la imagen, la API de detección de poses pasa un array de objetos Pose al controlador de finalización o lo muestra, según si llamaste al método asíncrono o al síncrono.

Si la persona no estaba completamente dentro de la imagen, el modelo asigna las coordenadas de los puntos de referencia faltantes fuera del marco y les asigna valores bajos de InFrameConfidence.

Si no se detectó ninguna persona, el array estará vacío.

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

Sugerencias para mejorar el rendimiento

La calidad de los resultados depende de la calidad de la imagen de entrada:

  • Para que el Kit de AA detecte la pose con precisión, la persona en la imagen debe representarse con datos de píxeles suficientes. Para obtener el mejor rendimiento, el sujeto debe tener al menos 256 × 256 píxeles.
  • Si detectas una pose en una aplicación en tiempo real, te recomendamos tener en cuenta las dimensiones generales de las imágenes de entrada. Las imágenes más pequeñas se pueden procesar más rápido. Así que, para reducir la latencia, captura imágenes con resoluciones más bajas, pero ten en cuenta los requisitos de resolución anteriores y asegúrate de que el sujeto ocupe la mayor parte de la imagen que sea posible.
  • Un enfoque de imagen deficiente también puede afectar la exactitud. Si no obtienes resultados aceptables, pídele al usuario que vuelva a capturar la imagen.

Si quieres usar la detección de pose en una aplicación en tiempo real, sigue estos lineamientos para lograr los mejores fotogramas por segundo:

  • Usa el SDK de PoseDetection base y el modo de detección stream.
  • Considera capturar imágenes con una resolución más baja. Sin embargo, también ten en cuenta los requisitos de dimensiones de imágenes de esta API.
  • Para procesar fotogramas de video, usa la API síncrona results(in:) del detector. Llama a este método desde la función captureOutput(_, didOutput:from:) de AVCaptureVideoDataOutputSampleBufferDelegate para obtener resultados de forma síncrona desde el fotograma de video determinado. Mantén alwaysDiscardsLateVideoFrames de AVCaptureVideoDataOutput como verdadero para limitar las llamadas al detector. Si hay un fotograma de video nuevo disponible mientras se ejecuta el detector, se descartará.
  • Si usas la salida del detector para superponer gráficos en la imagen de entrada, primero obtén el resultado de ML Kit y, luego, procesa la imagen y la superposición en un solo paso. De esta manera, procesas la superficie de visualización solo una vez por cada fotograma de entrada procesado. Consulta las clases previewOverlayView y MLKDetectionOverlayView en la app de ejemplo para ver un ejemplo.

Próximos pasos