[PYTHON] J'ai essayé de déplacer le modèle 3D en faisant quelque chose comme la capture de mouvement avec juste un ordinateur portable + une caméra Web

Aperçu

Cet article Si vous utilisez un modèle d'estimation de posture dont le traitement est raisonnablement léger, vous pouvez effectuer une capture de mouvement avec juste un ordinateur portable et une caméra Web (caméra intégrée). Je pense que je peux le faire, alors je l'ai essayé. Le flux est presque le même que celui de l'article original.

Les choses nécessaires

procédure

Côté Python

1. Préparation du modèle d'estimation de la posture

Veuillez cloner ce qui suit https://github.com/ildoonet/tf-pose-estimation/tree/master En utilisant cela, la posture de la personne dans l'image bidimensionnelle peut être estimée.

2. Restauration des informations 3D

Vous avez besoin d'informations 3D pour faire quelque chose comme la capture de mouvement.

  1. Le modèle de ne peut obtenir que des informations bidimensionnelles. Par conséquent, utilisez le processus dans la branche de développement du référentiel pour obtenir les informations 3D. (On dirait que c'était à l'origine en master, mais c'est parti)

Veuillez cloner ce qui suit https://github.com/ildoonet/tf-pose-estimation/tree/devel Puis déplacez le dossier devel / src / lifting vers master

3. Préparation du serveur WebSocket

Dans ce système, le côté Python effectue des traitements tels que l'estimation de la posture d'une personne, et le côté Unity affiche uniquement le modèle 3D. Cette fois, nous implémenterons la partie communication de l'information de Python et Unity à l'aide de WebSocket.

Exécutez la commande suivante pip install git+https://github.com/Pithikos/python-websocket-server

server.py


import logging

import cv2
import json
import numpy as np
import common

from tf_pose.estimator import TfPoseEstimator
from tf_pose.networks import get_graph_path, model_wh
from websocket_server import WebsocketServer
from lifting.prob_model import Prob3dPose

PORT = 5000
HOST = '127.0.0.1'

# logger_setup
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter(' %(module)s -  %(asctime)s - %(levelname)s - %(message)s'))
logger.addHandler(handler)


def create_json(pose3d):
    global old_data

    data = {'body_parts': []}

    """
    // 0 :Hip
    // 1 :RHip
    // 2 :RKnee
    // 3 :RFoot
    // 4 :LHip
    // 5 :LKnee
    // 6 :LFoot
    // 7 :Spine
    // 8 :Thorax
    // 9 :Neck/Nose
    // 10:Head
    // 11:LShoulder
    // 12:LElbow
    // 13:LWrist
    // 14:RShoulder
    // 15:RElbow
    // 16:RWrist
    """

    for i in range(17):
        data['body_parts'].append({'id': i, 'x': pose3d[0][0][i], 'y': pose3d[0][2][i], 'z': pose3d[0][1][i]})

    old_data = data
    return data


def new_client(client, server):
    logger.info('NewClient {}:{} has left.'.format(client['address'][0], client['address'][1]))


def client_left(client, server):
    logger.info('Client {}:{} has left.'.format(client['address'][0], client['address'][1]))


def message_received(client, server, message):
    _, image = cam.read()

    humans = e.inference(image, resize_to_default=(w > 0 and h > 0), upsample_size=4.0)

    pose_2d_mpiis = []
    visibilities = []

    standard_w = 640
    standard_h = 480

    try:
        pose_2d_mpii, visibility = common.MPIIPart.from_coco(humans[0])
        pose_2d_mpiis.append([(int(x * standard_w + 0.5), int(y * standard_h + 0.5)) for x, y in pose_2d_mpii])
        visibilities.append(visibility)
        pose_2d_mpiis = np.array(pose_2d_mpiis)
        visibilities = np.array(visibilities)
        transformed_pose2d, weights = poseLifting.transform_joints(pose_2d_mpiis, visibilities)
        pose_3d = poseLifting.compute_3d(transformed_pose2d, weights)
        print(pose_3d)
        server.send_message(client, json.dumps(create_json(pose_3d)))

    except :
        server.send_message(client, json.dumps(old_data))


if __name__ == '__main__':
    # main
    w, h = model_wh("432x368")
    e = TfPoseEstimator(get_graph_path("mobilenet_thin"), target_size=(432, 368), trt_bool=False)
    poseLifting = Prob3dPose('lifting/models/prob_model_params.mat')

    cam = cv2.VideoCapture(0)

    old_data = {}

    server = WebsocketServer(port=PORT, host=HOST)
    server.set_fn_new_client(new_client)
    server.set_fn_client_left(client_left)
    server.set_fn_message_received(message_received)
    server.run_forever()

Maintenant, le côté Python est prêt

Côté unité

1. Préparation du modèle 3D

Tout d'abord, préparons le modèle 3D que vous souhaitez déplacer. Cette fois, j'ai utilisé le modèle "Unity-Chan!" De l'Asset Store.

2. Installation des bibliothèques requises

Clonez SAFullBodyIK et déplacez-le dans le dossier Assets. Clonez et compilez également https://github.com/sta/websocket-sharp afin de pouvoir recevoir WebSocket. Ce qui suit est facile à comprendre comment construire https://qiita.com/oishihiroaki/items/bb2977c72052f5dd5bd9

2. Code côté Unity

J'ai emprunté le code de Source de référence de cet article.

IKSetting.cs


using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using WebSocketSharp;
using WebSocketSharp.Net;

public class IKSetting : MonoBehaviour {
    private BodyParts bodyParts;
    private string receivedJson;
    private WebSocket ws;

    [SerializeField, Range(10, 120)]
    float FrameRate;
    public List<Transform> BoneList = new List<Transform>();
    GameObject FullbodyIK;
    Vector3[] points = new Vector3[17];
    Vector3[] NormalizeBone = new Vector3[12];
    float[] BoneDistance = new float[12];
    float Timer;
    int[,] joints = new int[,] { { 0, 1 }, { 1, 2 }, { 2, 3 }, { 0, 4 }, { 4, 5 }, { 5, 6 }, { 0, 7 }, { 7, 8 }, { 8, 9 }, { 9, 10 }, { 8, 11 }, { 11, 12 }, { 12, 13 }, { 8, 14 }, { 14, 15 }, { 15, 16 } };
    int[,] BoneJoint = new int[,] { { 0, 2 }, { 2, 3 }, { 0, 5 }, { 5, 6 }, { 0, 9 }, { 9, 10 }, { 9, 11 }, { 11, 12 }, { 12, 13 }, { 9, 14 }, { 14, 15 }, { 15, 16 } };
    int[,] NormalizeJoint = new int[,] { { 0, 1 }, { 1, 2 }, { 0, 3 }, { 3, 4 }, { 0, 5 }, { 5, 6 }, { 5, 7 }, { 7, 8 }, { 8, 9 }, { 5, 10 }, { 10, 11 }, { 11, 12 } };
    int NowFrame = 0;

    float[] x = new float[17];
    float[] y = new float[17];
    float[] z = new float[17];

    bool isReceived = false;

    // Use this for initialization
    void Start () {

        ws = new WebSocket("ws://localhost:5000/");
        ws.OnOpen += (sender, e) =>
        {
            Debug.Log("WebSocket Open");
        };
        ws.OnMessage += (sender, e) =>
        {
            receivedJson = e.Data;
            Debug.Log("Data: " + e.Data);
            isReceived = true;
        };
        ws.OnError += (sender, e) =>
        {
            Debug.Log("WebSocket Error Message: " + e.Message);
        };
        ws.OnClose += (sender, e) =>
        {
            Debug.Log("WebSocket Close");
        };
        ws.Connect();

        ws.Send("");
    }

    // Update is called once per frame
    void Update () {

        Timer += Time.deltaTime;
        ws.Send("");

        if (Timer > (1 / FrameRate))
        {
            Timer = 0;
            PointUpdate();
        }
        if (!FullbodyIK)
        {
            IKFind();
        }
        else
        {
            IKSet();
        }
    }

    void OnDestroy()
    {
        ws.Close();
        ws = null;
    }

    void PointUpdate()
    {
        if (NowFrame < 600)
        {
            NowFrame++;
            if (isReceived)
            {
                bodyParts = JsonUtility.FromJson<BodyParts>(receivedJson);
                for (int i = 0; i < 17; i++)
                {
                    x[i] = bodyParts.body_parts[i].x;
                    y[i] = bodyParts.body_parts[i].y;
                    z[i] = bodyParts.body_parts[i].z;
                }

                isReceived = false;
            }

            for (int i = 0; i < 17; i++)
            {
                points[i] = new Vector3(x[i], y[i], -z[i]);
                Debug.Log(points[i]);
            }

            for (int i = 0; i < 12; i++)
            {
                NormalizeBone[i] = (points[BoneJoint[i, 1]] - points[BoneJoint[i, 0]]).normalized;
            }
        }
    }

    void IKFind()
    {
        FullbodyIK = GameObject.Find("FullBodyIK");
        if (FullbodyIK)
        {
            for (int i = 0; i < Enum.GetNames(typeof(OpenPoseRef)).Length; i++)
            {
                Transform obj = GameObject.Find(Enum.GetName(typeof(OpenPoseRef), i)).transform;
                if (obj)
                {
                    BoneList.Add(obj);
                }
            }
            for (int i = 0; i < Enum.GetNames(typeof(NormalizeBoneRef)).Length; i++)
            {
                BoneDistance[i] = Vector3.Distance(BoneList[NormalizeJoint[i, 0]].position, BoneList[NormalizeJoint[i, 1]].position);
            }
        }
    }

    void IKSet()
    {
        if (Math.Abs(points[0].x) < 1000 && Math.Abs(points[0].y) < 1000 && Math.Abs(points[0].z) < 1000)
        {
            BoneList[0].position = points[0] * 0.001f + Vector3.up * 0.8f;
        }
        for (int i = 0; i < 12; i++)
        {
            BoneList[NormalizeJoint[i, 1]].position = Vector3.Lerp(
                BoneList[NormalizeJoint[i, 1]].position,
                BoneList[NormalizeJoint[i, 0]].position + BoneDistance[i] * NormalizeBone[i]
                , 0.05f
            );
            DrawLine(BoneList[NormalizeJoint[i, 0]].position, BoneList[NormalizeJoint[i, 1]].position, Color.red);
        }
        for (int i = 0; i < joints.Length / 2; i++)
        {
            DrawLine(points[joints[i, 0]] * 0.001f + new Vector3(-1, 0.8f, 0), points[joints[i, 1]] * 0.001f + new Vector3(-1, 0.8f, 0), Color.blue);
        }
    }

    void DrawLine(Vector3 s, Vector3 e, Color c)
    {
        Debug.DrawLine(s, e, c);
    }
}

enum OpenPoseRef
{
    Hips,
    LeftKnee, LeftFoot,
    RightKnee, RightFoot,
    Neck, Head,
    RightArm, RightElbow, RightWrist,
    LeftArm, LeftElbow, LeftWrist
};

enum NormalizeBoneRef
{
    Hip2LeftKnee, LeftKnee2LeftFoot,
    Hip2RightKnee, RightKnee2RightFoot,
    Hip2Neck, Neck2Head,
    Neck2RightArm, RightArm2RightElbow, RightElbow2RightWrist,
    Neck2LeftArm, LeftArm2LeftElbow, LeftElbow2LeftWrist
};

[System.Serializable]
public class BodyParts
{
    public Position[] body_parts;
}

[System.Serializable]
public class Position
{
    public int id;
    public float x;
    public float y;
    public float z;
}

C'est tout pour la préparation.

Courir

Après avoir exécuté server.py, appuyez sur le bouton Lecture du côté Unity.

Résumé

Il s'est déplacé plus rapidement que prévu, bien qu'il y ait eu un certain décalage. Si vous recherchez un modèle plus léger, vous pouvez le trouver, il semble donc y avoir une prédiction d'amélioration.

Recommended Posts

J'ai essayé de déplacer le modèle 3D en faisant quelque chose comme la capture de mouvement avec juste un ordinateur portable + une caméra Web
J'ai essayé de faire quelque chose comme un chatbot avec le modèle Seq2Seq de TensorFlow
J'ai essayé de créer un modèle avec l'exemple d'Amazon SageMaker Autopilot
J'ai essayé de créer facilement une image 3D de haute précision avec une seule photo [0]. (Confirmé comment capturer l'espace, l'ombre)
J'ai essayé d'écrire dans un modèle de langage profondément appris
J'ai essayé de déplacer le ballon
Je voulais résoudre le problème ABC164 A ~ D avec Python
J'ai essayé de prédire le nombre de personnes infectées au niveau national de la nouvelle corona avec un modèle mathématique
J'ai essayé de communiquer avec un serveur distant par communication Socket avec Python.
J'ai trouvé un moyen de créer un modèle 3D à partir d'une photo.
765 J'ai essayé d'identifier les trois familles professionnelles par CNN (avec Chainer 2.0.0)
J'ai essayé de vérifier le résultat du test A / B avec le test du chi carré
J'ai essayé de prédire le comportement du nouveau virus corona avec le modèle SEIR.
# J'ai essayé quelque chose comme Vlookup avec Python # 2
J'ai essayé de créer facilement une image 3D de haute précision avec une seule photo [-1]. (La zone cachée est-elle vraiment visible?)
Je pensais que je pouvais créer un bon éditeur gitignore, alors j'ai essayé de faire quelque chose comme MVP pour le moment
J'ai essayé de déplacer GAN (mnist) avec keras
J'ai essayé de sauvegarder les données avec discorde
J'ai essayé de détecter rapidement un mouvement avec OpenCV
J'ai trouvé un moyen de créer un modèle 3D à partir d'une photo.0 Projection vers l'espace 3D
J'ai essayé d'entraîner la fonction péché avec chainer
J'ai essayé de déplacer l'apprentissage automatique (détection d'objet) avec TouchDesigner
J'ai essayé de déplacer Faster R-CNN rapidement avec pytorch
J'ai essayé de toucher un fichier CSV avec Python
J'ai essayé de résoudre Soma Cube avec python
J'ai essayé de résoudre le problème avec Python Vol.1
Django super introduction par les débutants Python! Partie 6 J'ai essayé d'implémenter la fonction de connexion
J'ai trouvé un moyen de créer un modèle 3D à partir d'une photo Partie 04 Générer des polygones
J'ai essayé de créer une API RESTful en connectant l'API rapide du framework Python explosif à MySQL.
J'ai créé une classe pour obtenir le résultat de l'analyse par MeCab dans ndarray avec python
Jour 71, j'ai essayé de prédire combien de temps cette autolimitation se poursuivra avec le modèle SIR
J'ai essayé de créer une caméra de surveillance à détection de mouvement avec OpenCV en utilisant une caméra WEB avec Raspberry Pi
[Démarrage du shell] J'ai essayé d'afficher le shell sur le téléviseur avec un G-cluster à carte Linux bon marché
J'ai aussi essayé d'imiter la fonction monade et la monade d'état avec le générateur en Python
Créez un tableau à deux dimensions en ajoutant une ligne à la fin d'un tableau vide avec numpy
J'ai écrit un doctest dans "J'ai essayé de simuler la probabilité d'un jeu de bingo avec Python"
J'ai essayé d'utiliser PI Fu pour générer un modèle 3D d'une personne à partir d'une image
J'ai essayé de prédire les ventes de logiciels de jeux avec VARISTA en me référant à l'article du Codexa
Une personne qui veut résoudre le problème D avec ABC d'AtCoder a essayé de gratter
J'ai essayé d'implémenter une ligne moyenne mobile de volume avec Quantx
J'ai essayé de mettre en œuvre le modèle de base du réseau neuronal récurrent
J'ai essayé de trouver l'entropie de l'image avec python
J'ai essayé de simuler la propagation de l'infection avec Python
J'ai essayé d'analyser les émotions de tout le roman "Weather Child" ☔️
J'ai essayé de créer automatiquement un rapport avec la chaîne de Markov
J'ai essayé de notifier les informations de retard de train avec LINE Notify
J'ai essayé de remplacer le disque dur Windows 10 par un SSD plus petit
J'ai fait une fonction pour vérifier le modèle de DCGAN
J'ai essayé de résoudre le problème d'optimisation des combinaisons avec Qiskit
J'ai essayé de commencer avec Hy ・ Définir une classe
J'ai essayé de classer MNIST par GNN (avec PyTorch géométrique)
J'ai essayé de trier une colonne FizzBuzz aléatoire avec un tri à bulles.
J'ai essayé d'implémenter SSD avec PyTorch maintenant (édition du modèle)
J'ai essayé de déverrouiller l'entrée 2 lock sésame d'une simple pression sur le bouton AWS IoT