Estimación de Postura ? | Python – MediaPipe – OpenCV

Por Administrador

Hasta aquí hemos hablado ya de MediaPipe hands (para la detección de manos y dedos), MediaPipe face detection (para la detección de rostros) y MediaPipe face mesh (malla facial). Cada una de estas soluciones vaya que nos ha sorprendido y además hemos aprendido como acceder a las detecciones obtenidas. En este tutorial vamos a tratar otra de las soluciones para Python, MediaPipe Pose. 

CONTENIDO

  • MediaPipe Pose
    • Modelos empleados en MediaPipe Pose
      • Person/pose Detection Model (BlazePose Detector) 
      • Pose Landmark Model (BlazePose GHUM 3D) 
    • Opciones de configuración
    • Datos de salida que obtenemos al usar MediaPipe Pose
    • ¡Vamos con la programación!
      • Estimación de postura con MediaPipe en imágenes
      • Estimación de postura con MediaPipe en videos
  • Referencias

MediaPipe Pose

Figura 1: Ejemplo del uso de MediaPipe Pose para la estimación de postura

Esta es una solución de machine learning de alta fidelidad, que permite obtener 33 puntos de referencia 3D de todo el cuerpo, teniendo como entrada, imágenes o fotogramas en rgb. Además esta solución alcanza resultados en tiempo real en la mayoría de teléfonos móviles modernos, computadores o laptops, e incluso en la web. 

Modelos empleados en MediaPipe Pose

Para la obtención de los 33 puntos de referencia, esta solución emplea un proceso similar al visto en MediaPpe Hands y en MediaPipe FaceMesh, trabajando con dos modelos. El primer modelo se encargará de ubicar a la persona dentro de la imagen, mientras que el segundo ubicará los 33 puntos claves o puntos de referencia.

Person/pose Detection Model (BlazePose Detector) 

Figura 2: Ilustración del proceso de detección de una persona con MediaPipe.

Se emplea en un principio un modelo para la detección de la persona o postura, blazePose. Que por cierto está basado en el modelo BlazeFace que se emplea en mediaPipe face detection. Entonces, este modelo ubica a la persona dentro de la imagen, es decir la región de interés y recorta dicha región. 

Pose Landmark Model (BlazePose GHUM 3D) 

Figura 3: Ilustración de la obtención de 33 puntos claves con MediaPipe.

Una vez que se tiene la imagen recortada donde está presente la persona, se procede a aplicar el pose landmark model, que nos permitirá obtener la ubicación de 33 puntos distribuidos por todo el cuerpo. Cada uno de estos tendrá un nombre asociado que más adelante veremos como acceder. 

Figura 4: 33 puntos de referencia distribuidos por el cuerpo detectado. (Fuente)

Si tenemos como entrada a un video, al igual que en anteriores tutoriales, el detector de la persona se aplicará en los primeros fotogramas y luego se realizará la obtención de los puntos clave, para que en fotogramas posteriores dichos puntos sean rastreados, de tal forma que solo se invoque al detector de personas cuando se hayan perdido dichos puntos. 

En sí, este es un resumen del proceso de detección que podemos encontrar en la documentación de MediaPipe Pose, por lo que si quieres una explicación más extendida, te recomiendo que visites su web y su repositorio

Opciones de configuración 

STATIC_IMAGE_MODE (Por defecto false) 

Si a este le asignamos False, entonces trata a las imágenes de entrada como un video, de tal forma que intentará detectar a la persona más prominente y los puntos de referencia en los primeros fotogramas. Mientras que para localizar la nueva ubicación de los puntos, se procederá a realizar el seguimiento o tracking de los puntos de referencia. 

Si se especifica como True, el detector se aplica en cada imagen, por lo que es mejor usarlo en imágenes que no estén relacionadas. 

MODEL_COMPLEXITY (Por defecto 1) 

Puede tomar valores de 0, 1 o 2. Que corresponden a la complejidad del modelo pose landmark. Y lo que nos dice la documentación es que la precisión y latencia generalmente aumenta con la complejidad del modelo. 

SMOOTH_LANDMARKS (Por defecto true)

Si se establece como True, se filtra los puntos de referencia de la postura a través de diferentes imágenes de entrada para reducir las fluctuaciones. Este es ignorado cuando static_image_mode es igual a True.

MIN_DETECTION_CONFIDENCE (Por defecto 0.5)

Valor mínimo de confianza del modelo de detección de personas para que la detección se considere correcta. 

MIN_TRACKING_CONFIDENCE (Por defecto 0.5)

Valor mínimo de confianza del modelo de seguimiento de puntos de referencia para que los pose landmarks se consideren rastreados con éxito, de lo contrario la detección de personas se invocará automáticamente en la siguiente imagen de entrada. 

Este es ignorado si static_image_mode es igual a True. 

Datos de salida que obtenemos al usar MediaPipe Pose

POSE_LANDMARKS 

Una lista de puntos de referencia de la postura. Cada uno de ellos contará con sus coordenadas x, y y z. Además tendremos visibility, este es un valor comprendido entre 0 y 1 que indica la probabilidad de que el punto de referencia sea visible (presente y no ocluido) en la imagen. 

¡Vamos con la programación!

Para una explicación más detallada de los programas que veremos a continuación, por favor dirígete al videotutorial de mi canal.

Estimación de postura con MediaPipe en imágenes

import cv2
import mediapipe as mp

mp_drawing = mp.solutions.drawing_utils
mp_pose = mp.solutions.pose

with mp_pose.Pose(
    static_image_mode=True) as pose:

    image = cv2.imread("image_0001.jpg")
    height, width, _ = image.shape
    image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

    results = pose.process(image_rgb)
    print("Pose landmarks:", results.pose_landmarks)

    if results.pose_landmarks is not None:
        print(int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_SHOULDER].x * width))
        x1 = int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_SHOULDER].x * width)
        y1 = int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_SHOULDER].y * height)

        x2 = int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_ELBOW].x * width)
        y2 = int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_ELBOW].y * height)

        x3 = int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_WRIST].x * width)
        y3 = int(results.pose_landmarks.landmark[mp_pose.PoseLandmark.RIGHT_WRIST].y * height)

        x4 = int(results.pose_landmarks.landmark[11].x * width)
        y4 = int(results.pose_landmarks.landmark[11].y * height)

        x5 = int(results.pose_landmarks.landmark[13].x * width)
        y5 = int(results.pose_landmarks.landmark[13].y * height)

        x6 = int(results.pose_landmarks.landmark[15].x * width)
        y6 = int(results.pose_landmarks.landmark[15].y * height)

        cv2.line(image, (x1, y1), (x2, y2), (255, 255, 255), 3)
        cv2.line(image, (x2, y2), (x3, y3), (255, 255, 255), 3)
        cv2.circle(image, (x1, y1), 6, (128, 0, 255), -1)
        cv2.circle(image, (x2, y2), 6, (128, 0, 255), -1)
        cv2.circle(image, (x3, y3), 6, (128, 0, 255), -1)

        cv2.line(image, (x4, y4), (x5, y5), (255, 255, 255), 3)
        cv2.line(image, (x5, y5), (x6, y6), (255, 255, 255), 3)
        cv2.circle(image, (x4, y4), 6, (255, 191, 0), -1)
        cv2.circle(image, (x5, y5), 6, (255, 191, 0), -1)
        cv2.circle(image, (x6, y6), 6, (255, 191, 0), -1)

        '''
        mp_drawing.draw_landmarks(image, results.pose_landmarks,
            mp_pose.POSE_CONNECTIONS,
            mp_drawing.DrawingSpec(color=(128, 0, 250), thickness=2, circle_radius=3),
            mp_drawing.DrawingSpec(color=(255, 255, 255), thickness=2))
        '''
    cv2.imshow("Image", image)
    cv2.waitKey(0)
cv2.destroyAllWindows()

Estimación de postura con MediaPipe en video

import cv2
import mediapipe as mp

mp_drawing = mp.solutions.drawing_utils
mp_pose = mp.solutions.pose

#cap = cv2.VideoCapture("video_0002.mp4")
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)

with mp_pose.Pose(
    static_image_mode=False) as pose:

    while True:
        ret, frame = cap.read()
        if ret == False:
            break
        frame = cv2.flip(frame, 1)
        height, width, _ = frame.shape
        frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
        results = pose.process(frame_rgb)

        if results.pose_landmarks is not None:
            mp_drawing.draw_landmarks(
                frame, results.pose_landmarks, mp_pose.POSE_CONNECTIONS,
                mp_drawing.DrawingSpec(color=(128, 0, 250), thickness=2, circle_radius=3),
                mp_drawing.DrawingSpec(color=(255, 255, 255), thickness=2))

        cv2.imshow("Frame", frame)
        if cv2.waitKey(1) & 0xFF == 27:
            break

cap.release()
cv2.destroyAllWindows()

Referencias