Add MocapNET

This commit is contained in:
Justin John
2023-12-15 04:22:41 +05:30
parent 8434144ded
commit 65e8934d5e
336 changed files with 100584 additions and 0 deletions
@@ -0,0 +1,276 @@
#include "artifactRecognition.hpp"
#include "../../../../dependencies/InputParser/InputParser_C.h"
#include "../../../../dependencies/RGBDAcquisition/tools/AmMatrix/matrixCalculations.h"
#include <iostream>
#include <stdio.h>
#define NORMAL "\033[0m"
#define BLACK "\033[30m" /* Black */
#define RED "\033[31m" /* Red */
#define GREEN "\033[32m" /* Green */
#define YELLOW "\033[33m" /* Yellow */
int initializeArtifactsFromFile(struct sceneArtifacts * scene,const char * filename)
{
fprintf(stderr,"Initializing from %s .. \n",filename);
FILE * fp = fopen(filename,"r");
if (fp!=0)
{
fprintf(stderr,GREEN "Successfully opened map file %s\n" NORMAL,filename);
char * line = NULL;
size_t len = 0;
ssize_t read;
scene->numberOfArtifacts=0;
unsigned int id=0;
struct InputParserC * ipc = InputParser_Create(8096,5);
InputParser_SetDelimeter(ipc,0,',');
InputParser_SetDelimeter(ipc,1,'(');
InputParser_SetDelimeter(ipc,2,')');
InputParser_SetDelimeter(ipc,3,10);
InputParser_SetDelimeter(ipc,4,13);
do {
read = getline(&line, &len, fp);
if (line!=0)
{
int numberOfArguments = InputParser_SeperateWords(ipc,line,1);
if ( InputParser_WordCompareNoCaseAuto(ipc,0,"AREA2D") )
{
if (scene->numberOfArtifacts+1<NUMBER_OF_ARTIFACTS)
{
++scene->numberOfArtifacts;
if (scene->numberOfArtifacts>1)
{
++id;
}
InputParser_GetWord(ipc,1,scene->artifact[id].label,512);
fprintf(stderr,"New 2D Area declared ( %s )..!\n",scene->artifact[id].label);
scene->artifact[id].y1 = InputParser_GetWordFloat(ipc,2);
scene->artifact[id].x1 = InputParser_GetWordFloat(ipc,3);
scene->artifact[id].y2 = InputParser_GetWordFloat(ipc,4);
scene->artifact[id].x2 = InputParser_GetWordFloat(ipc,5);
scene->artifact[id].is3D=0;
scene->artifact[id].active=0;
scene->artifact[id].hasAction=1;
}
} else
if ( InputParser_WordCompareNoCaseAuto(ipc,0,"OBJECT3D") )
{
if (scene->numberOfArtifacts+1<NUMBER_OF_ARTIFACTS)
{
++scene->numberOfArtifacts;
if (scene->numberOfArtifacts>1)
{
++id;
}
InputParser_GetWord(ipc,1,scene->artifact[id].label,512);
fprintf(stderr,"New 3D Object declared ( %s )..!\n",scene->artifact[id].label);
scene->artifact[id].y1 = InputParser_GetWordFloat(ipc,2);
scene->artifact[id].x1 = InputParser_GetWordFloat(ipc,3);
scene->artifact[id].z1 = InputParser_GetWordFloat(ipc,4);
scene->artifact[id].y2 = InputParser_GetWordFloat(ipc,5);
scene->artifact[id].x2 = InputParser_GetWordFloat(ipc,6);
scene->artifact[id].z2 = InputParser_GetWordFloat(ipc,7);
scene->artifact[id].is3D=1;
scene->artifact[id].active=0;
scene->artifact[id].hasAction=1;
}
} else
if ( InputParser_WordCompareNoCaseAuto(ipc,0,"ACTIVATE_ON_POSITION") )
{
fprintf(stderr,"%s will activate on position\n",scene->artifact[id].label);
scene->artifact[id].activatesOnPosition=1;
} else
if ( InputParser_WordCompareNoCaseAuto(ipc,0,"ACTIVATE_ON_LOOK") )
{
fprintf(stderr,"%s will activate on look\n",scene->artifact[id].label);
scene->artifact[id].activatesOnLook=1;
} else
if ( InputParser_WordCompareNoCaseAuto(ipc,0,"ACTIVATE_ON_GESTURE") )
{
fprintf(stderr,"%s will activate on gesture\n",scene->artifact[id].label);
scene->artifact[id].activateOnGesture=1;
} else
if ( InputParser_WordCompareNoCaseAuto(ipc,0,"ACTIVATE_ACTION") )
{
InputParser_GetWord(ipc,1,scene->artifact[id].actionToExecuteOnActivation,512);
fprintf(stderr,"New Command for %s (%s) !\n",scene->artifact[id].label,scene->artifact[id].actionToExecuteOnActivation);
scene->artifact[id].hasAction=1;
} else
if ( InputParser_WordCompareNoCaseAuto(ipc,0,"DEACTIVATE_ACTION") )
{
InputParser_GetWord(ipc,1,scene->artifact[id].actionToExecuteOnDeactivation,512);
fprintf(stderr,"New Command for %s (%s) !\n",scene->artifact[id].label,scene->artifact[id].actionToExecuteOnDeactivation);
scene->artifact[id].hasAction=1;
}
}
}
while( (line!=0) && (read>0) );
InputParser_Destroy(ipc);
fclose(fp);
return 1;
}
return 0;
}
int check3DArtifactCollision(struct artifactData * artifact,float x1, float y1, float z1,float x2, float y2, float z2)
{
if (artifact->is3D)
{
float initial[4]={x1,y1,z1,1.0};
float target[4]={x2,y2,z2,1.0};
//--------------------------------------------------------------
float v0[4] = {
std::min(artifact->x1,artifact->x2),
std::min(artifact->y1,artifact->y2),
(float) (artifact->z1+artifact->z2)/2,
1.0
};
//--------------------------------------------------------------
float v1[4] = {
std::min(artifact->x1,artifact->x2),
std::max(artifact->y1,artifact->y2),
(float) (artifact->z1+artifact->z2)/2,
1.0
};
//--------------------------------------------------------------
float v2[4] = {
std::max(artifact->x1,artifact->x2),
std::max(artifact->y1,artifact->y2),
(float) (artifact->z1+artifact->z2)/2,
1.0
};
//--------------------------------------------------------------
float v3[4] = {
std::max(artifact->x1,artifact->x2),
std::min(artifact->y1,artifact->y2),
(float) (artifact->z1+artifact->z2)/2,
1.0
};
//--------------------------------------------------------------
if (rayIntersectsRectangle(initial,target,v0,v1,v2,v3))
{
return 1;
}
}
return 0;
}
int checkArtifactCollision(struct artifactData * artifact,float x, float y, float r)
{
if (
(artifact->x1<x) && (artifact->y1<y) && (x<artifact->x2) && (y<artifact->y2)
)
{
/*
fprintf(stderr,"Artifact collision %0.2f %0.2f %0.2f %0.2f -> %0.2f %0.2f \n",
artifact->x1,artifact->y1,
artifact->x2,artifact->y2,
x,y);
*/
return 1;
}
/*
fprintf(stderr,"NO collision %0.2f %0.2f %0.2f %0.2f -> %0.2f %0.2f \n",
artifact->x1,artifact->y1,
artifact->x2,artifact->y2,
x,y); */
return 0;
}
// Given three colinear points p, q, r, the function checks if
// point q lies on line segment 'pr'
int onSegment(float pX,float pY, float qX,float qY, float rX, float rY)
{
if (qX <= std::max(pX, rX) && qX >= std::min(pX, rX) && qY <= std::max(pY, rY) && qY >= std::min(pY, rY)) { return 1; }
return 0;
}
// To find orientation of ordered triplet (p, q, r).
// The function returns following values
// 0 --> p, q and r are colinear
// 1 --> Clockwise
// 2 --> Counterclockwise
int orientation(float pX,float pY, float qX,float qY, float rX, float rY)
{
// See https://www.geeksforgeeks.org/orientation-3-ordered-points/
// for details of below formula.
int val = (qY - pY) * (rX - qX) - (qX - pX) * (rY - qY);
if (val == 0) return 0; // colinear
return (val > 0)? 1: 2; // clock or counterclock wise
}
// The main function that returns true if line segment 'p1q1'
// and 'p2q2' intersect.
int doIntersect(float p1X,float p1Y, float q1X, float q1Y,float p2X,float p2Y,float q2X, float q2Y)
{
// Find the four orientations needed for general and
// special cases
int o1 = orientation(p1X,p1Y, q1X,q1Y, p2X,p2Y);
int o2 = orientation(p1X,p1Y, q1X,q1Y, q2X,q2Y);
int o3 = orientation(p2X,p2Y, q2X,q2Y, p1X,p1Y);
int o4 = orientation(p2X,p2Y, q2X,q2Y, q1X,q1Y);
// General case
if (o1 != o2 && o3 != o4)
return 1;
// Special Cases
// p1, q1 and p2 are colinear and p2 lies on segment p1q1
if (o1 == 0 && onSegment(p1X,p1Y, p2X,p2Y, q1X,q1Y)) return 1;
// p1, q1 and q2 are colinear and q2 lies on segment p1q1
if (o2 == 0 && onSegment(p1X,p1Y, q2X,q2Y, q1X,q1Y)) return 1;
// p2, q2 and p1 are colinear and p1 lies on segment p2q2
if (o3 == 0 && onSegment(p2X,p2Y, p1X,p1Y, q2X,q2Y)) return 1;
// p2, q2 and q1 are colinear and q1 lies on segment p2q2
if (o4 == 0 && onSegment(p2X,p2Y, q1X,q1Y, q2X,q2Y)) return 1;
return 0; // Doesn't fall in any of the above cases
}
int checkArtifactDirection(struct artifactData * artifact,float x1, float y1, float x2,float y2)
{
unsigned int check=0;
check+=doIntersect(x1,y1,x2,y2,artifact->x1,artifact->y1,artifact->x2,artifact->y1) ;
if (check==0) { check+=doIntersect(x1,y1,x2,y2,artifact->x1,artifact->y2,artifact->x2,artifact->y2) ; }
if (check==0) { check+=doIntersect(x1,y1,x2,y2,artifact->x1,artifact->y1,artifact->x1,artifact->y2) ; }
if (check==0) { check+=doIntersect(x1,y1,x2,y2,artifact->x2,artifact->y1,artifact->x2,artifact->y2) ; }
return check;
}
@@ -0,0 +1,58 @@
#pragma once
/** @file artifactRecognition.hpp
* @brief MocapNET Artifact recognition is implemented here. Artifacts are places in 3D space that can trigger specific events for applications that need human computer interaction
* @author Ammar Qammaz (AmmarkoV)
*/
#include <iostream>
#include <vector>
#define NUMBER_OF_ARTIFACTS 100
struct artifactData
{
float x1;
float y1;
float z1;
float x2;
float y2;
float z2;
char is3D;
char activatesOnOrientation;
char activatesOnLocation;
char activatesOnGestures;
char hasAction;
char active;
char activatesOnLook;
char activatesOnPosition;
char activateOnGesture;
char label[512];
char actionToExecuteOnActivation[512];
char actionToExecuteOnDeactivation[512];
};
/**
* @brief recorded gestures that can be used
*/
struct sceneArtifacts
{
int numberOfArtifacts;
struct artifactData artifact[NUMBER_OF_ARTIFACTS];
};
//int initializeArtifacts(struct sceneArtifacts * scene);
int initializeArtifactsFromFile(struct sceneArtifacts * scene,const char * filename);
int check3DArtifactCollision(struct artifactData * artifact,float x1, float y1, float z1,float x2, float y2, float z2);
int checkArtifactCollision(struct artifactData * artifact,float x, float y, float r);
int checkArtifactDirection(struct artifactData * artifact,float x1, float y1, float x2,float y2);
int doIntersect(float p1X,float p1Y, float q1X, float q1Y,float p2X,float p2Y,float q2X, float q2Y) ;
@@ -0,0 +1,252 @@
#include "gestureRecognition.hpp"
#include "../mocapnet2.hpp"
#include "../tools.hpp"
#include "../IO/bvh.hpp"
#include "../IO/csvRead.hpp"
#include "poseRecognition.hpp"
#define NORMAL "\033[0m"
#define BLACK "\033[30m" /* Black */
#define RED "\033[31m" /* Red */
#define GREEN "\033[32m" /* Green */
#define YELLOW "\033[33m" /* Yellow */
int addToMotionHistory(struct PoseHistory * poseHistoryStorage,std::vector<float> pose)
{
poseHistoryStorage->history.push_back(pose);
if (poseHistoryStorage->history.size() > poseHistoryStorage->maxPoseHistory)
{
poseHistoryStorage->history.erase(poseHistoryStorage->history.begin());
}
return 1;
}
int dumpMotionHistory(const char * filename,struct PoseHistory * poseHistoryStorage)
{
return writeBVHFile(filename,0,0,poseHistoryStorage->history);
}
int updateGestureActivity(std::vector<float> vecA,std::vector<float> vecB,std::vector<char> &active,float threshold)
{
if (vecA.size()!=vecB.size())
{
fprintf(stderr,"updateGestureActivity cannot compare different sized vectors..\n");
return 0;
}
if (vecA.size()!=active.size())
{
fprintf(stderr,"updateGestureActivity cannot compare without allocated active vector..\n");
return 0;
}
//Always skip X pos, Y pos, Z pos , and rotation
for (int i=6; i<active.size(); i++)
{
float difference=abs(vecA[i]-vecB[i]);
if (difference>threshold)
{
active[i]=1;
}
}
return 1;
}
int automaticallyObserveActiveJointsInGesture(struct RecordedGesture * gesture)
{
//fprintf(stderr,"automaticallyObserveActiveJointsInGesture:");
if (gesture->gesture.size()==0)
{
fprintf(stderr,"Failed to automatically observe active joints\n");
return 0;
}
std::vector<float> initialPose = gesture->gesture[0];
gesture->usedJoints.clear();
for (int jointID=0; jointID<initialPose.size(); jointID++)
{
gesture->usedJoints.push_back(0);
}
for (int gestureID=0; gestureID<gesture->gesture.size(); gestureID++)
{
updateGestureActivity(
initialPose,
gesture->gesture[gestureID],
gesture->usedJoints,
5
);
}
//fprintf(stderr,"done\n");
return 1;
}
int loadGestures(struct GestureDatabase * gestureDB)
{
unsigned int gestureID;
char gesturePath[512]= {0};
gestureDB->gestureChecksPerformed=0;
for (gestureID=0; gestureID<hardcodedGestureNumber; gestureID++)
{
snprintf(gesturePath,512,"dataset/gestures/%s",hardcodedGestureName[gestureID]);
fprintf(stderr,"Loading Gesture %u -> %s : ",gestureID,gesturePath);
gestureDB->gesture[gestureID].gesture=loadBVHFileMotionFrames(gesturePath);
//gestureDB->gesture[gestureID].bvhMotionGesture=loadBVHFile(gesturePath);
//if (gestureDB->gesture[gestureID].bvhMotionGesture!=0)
if (gestureDB->gesture[gestureID].gesture.size()>0)
{
fprintf(stderr,"Gesture %s has %lu frames with %lu fields each\n",gesturePath,gestureDB->gesture[gestureID].gesture.size(),gestureDB->gesture[gestureID].gesture[0].size());
gestureDB->gesture[gestureID].loaded=1;
gestureDB->gesture[gestureID].gestureCallback=0;
snprintf(gestureDB->gesture[gestureID].label,128,"%s",hardcodedGestureName[gestureID]);
gestureDB->numberOfLoadedGestures +=1;
//Populate
//gestureDB->gesture[gestureID].gesture;
if (automaticallyObserveActiveJointsInGesture(&gestureDB->gesture[gestureID]))
{
for (int jointID=0; jointID<gestureDB->gesture[gestureID].usedJoints.size(); jointID++)
{
if (gestureDB->gesture[gestureID].usedJoints[jointID])
{
fprintf(stderr,YELLOW "Joint %u/%s is going to be used for gesture #%u\n" NORMAL,jointID,MocapNETOutputArrayNames[jointID],gestureID);
}
}
}
else
{
fprintf(stderr,YELLOW "Failure observing active joints\n" NORMAL);
}
fprintf(stderr,GREEN "Success , loaded %lu frames\n" NORMAL,gestureDB->gesture[gestureID].gesture.size());
}
else
{
fprintf(stderr,RED "Failure\n" NORMAL);
}
}
return (gestureDB->numberOfLoadedGestures>0);
}
int compareHistoryWithGesture(
struct RecordedGesture * gesture,
struct PoseHistory * poseHistoryStorage,
unsigned int checkSerialNumber,
float currentFramerate,
float targetFramerate,
float percentageForDetection,
float threshold
)
{
unsigned int matchingFrames=0;
if (poseHistoryStorage->history.size()==0)
{
fprintf(stderr,"Requested comparison with empty history..\n");
return 0;
}
if (gesture->lastActivation>checkSerialNumber)
{
fprintf(stderr,RED "compareHistoryWithGesture: inverted timestmaps, something is terribly wrong..\n" NORMAL);
}
else
{
if (checkSerialNumber-gesture->lastActivation<GESTURE_ACTIVATION_COOLDOWN_IN_FRAMES)
{
fprintf(stderr,YELLOW "gesture %s on cooldown..\n" NORMAL,gesture->label);
return 0;
}
}
//TODO: Compensate for different targetFramerate / currentFramerate here..!
//In case of the currentFramerate being larger than the targetFramerate there should be more frames checked to increase accuracy
//In case of the currentFramerate being smaller than the targetFramerate there should be skipped frames
if (poseHistoryStorage->history.size()>=gesture->gesture.size())
{
unsigned int frameID=0;
unsigned int historyStart=poseHistoryStorage->history.size() - gesture->gesture.size();
//unsigned int jointNumber=poseHistoryStorage->history[0].size();
//unsigned int jointID=0;
for (frameID=0; frameID<gesture->gesture.size(); frameID++)
{
matchingFrames+=areTwoBVHFramesCloseEnough(gesture->gesture[frameID],poseHistoryStorage->history[frameID+historyStart],gesture->usedJoints,threshold);
}
gesture->percentageComplete = (float) matchingFrames/gesture->gesture.size();
float percentComplete = 100*gesture->percentageComplete;
if (percentComplete > 100 )
{
fprintf(stderr,RED);
fprintf(stderr,"There is something wrong in the gesture files ( size = %lu )\n ",gesture->gesture.size());
}
else if (percentComplete >= percentageForDetection )
{
fprintf(stderr,GREEN);
gesture->lastActivation=checkSerialNumber;
}
else if (percentComplete >= 50.0 )
{
fprintf(stderr,YELLOW);
}
fprintf(stderr,"Gesture %s - %0.2f %% %u/%lu\n" NORMAL,gesture->label,percentComplete,matchingFrames,gesture->gesture.size());
//If we have more than 75% or whatever is the target percentage correct trigger..!
return (percentComplete >= percentageForDetection );
}
return 0;
}
int compareHistoryWithKnownGestures(struct GestureDatabase * gestureDB,struct PoseHistory * poseHistoryStorage,float percentageForDetection,float threshold)
{
unsigned long now=GetTickCountMicrosecondsMN();
unsigned long previousRun=gestureDB->previousGestureCheckTimestamp;
gestureDB->previousGestureCheckTimestamp = now;
float currentFramerate = convertStartEndTimeFromMicrosecondsToFPS(previousRun,now);
float targetFramerate=16.0;
unsigned int gestureID=0;
gestureDB->gestureChecksPerformed+=1;
for (gestureID=0; gestureID<gestureDB->numberOfLoadedGestures; gestureID++)
{
if (
compareHistoryWithGesture(
&gestureDB->gesture[gestureID],
poseHistoryStorage,
gestureDB->gestureChecksPerformed,
currentFramerate,
targetFramerate,
percentageForDetection,
threshold
)
)
{
return 1+gestureID;
}
}
return 0;
}
@@ -0,0 +1,93 @@
#pragma once
/** @file gestureRecognition.hpp
* @brief MocapNET Gesture recognition is implemented here
* @author Ammar Qammaz (AmmarkoV)
*/
#include <iostream>
#include <vector>
/**
* @brief This is an array of names for the hardcoded gestures included in the dataset/gestures/ subdirectory
* Please remember to update hardcodedPoseNumber, its value should be the number of arguments in this array
* If you have arguments 0 - 10 you should set hardcodedGestureNumber to 11
*/
static const char * hardcodedGestureName[] =
{
"help.bvh", //0
"push.bvh", //1
"lefthandcircle.bvh", //2
"righthandcircle.bvh", //3
"waveleft.bvh", //4
"doubleclap.bvh", //5
"waveright.bvh", //6
"leftkick.bvh", //8
"rightkick.bvh", //9
"tpose.bvh", //10
"handsup.bvh", //7
"", //
"" //13
//hardcodedGestureNumber should be kept in sync
};
/**
* @brief This needs to be kept in sync with hardcodedGestureName */
const unsigned int hardcodedGestureNumber=10;
/**
* @brief Gesture tuning controls for the brave..
**/
const unsigned int GESTURE_ACTIVATION_COOLDOWN_IN_FRAMES=50;
const float GESTURE_COMPLETION_PERCENT=80.0;
const float GESTURE_ANGLE_SENSITIVITY=25.0;
/**
* @brief history of poses
*/
struct PoseHistory
{
unsigned int maxPoseHistory;
std::vector<std::vector<float> > history;
};
/**
* @brief recorded gestures that can be used
*/
struct RecordedGesture
{
unsigned int lastActivation;
float percentageComplete;
char loaded;
char label[128];
std::vector<std::vector<float> > gesture;
std::vector<char> usedJoints;
void * gestureCallback;
};
/**
* @brief gesture detection context, to facilitate gestures
*/
struct GestureDatabase
{
unsigned int gestureChecksPerformed;
unsigned int numberOfLoadedGestures;
struct RecordedGesture gesture[hardcodedGestureNumber];
unsigned long previousGestureCheckTimestamp;
};
int loadGestures(struct GestureDatabase * gestureDB);
int addToMotionHistory(struct PoseHistory * poseHistoryStorage,std::vector<float> pose);
int dumpMotionHistory(const char * filename,struct PoseHistory * poseHistoryStorage);
int compareHistoryWithKnownGestures(struct GestureDatabase * gestureDB,struct PoseHistory * poseHistoryStorage,float percentageForDetection,float threshold);
@@ -0,0 +1,663 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "../mocapnet2.hpp"
#include "parseCommandlineOptions.hpp"
#include "../IO/bvh.hpp"
#include "../tools.hpp"
#include "../../../JointEstimator2D/jointEstimator2D.hpp"
#include "../IO/jsonMocapNETHelpers.hpp"
#include "../IO/csvRead.hpp"
#define NORMAL "\033[0m"
#define BLACK "\033[30m" /* Black */
#define RED "\033[31m" /* Red */
#define GREEN "\033[32m" /* Green */
#define YELLOW "\033[33m" /* Yellow */
const char outputPathStatic[]="out.bvh";
void defaultMocapNET2Options(struct MocapNET2Options * options)
{
memset(options,0,sizeof(struct MocapNET2Options));
options->isJSONFile=0;
options->isCSVFile=0;
options->webcamSource = 0;
options->path=0;
options->datasetPath=0;
options->jointEstimatorUsed = JOINT_2D_ESTIMATOR_OPENPOSE; // JOINT_2D_ESTIMATOR_FORTH;
options->doUpperBody=1;
options->doLowerBody=1;
options->doFace=0;
options->doHands=0;
options->forceFront=0;
options->forceLeft=0;
options->forceRight=0;
options->forceBack=0;
options->visualizationType=0;
//Default IK options
options->useInverseKinematics=0;
options->learningRate=0.01;
options->spring =20.0;
options->iterations=5;
options->epochs=30;
//Test under Simulated Gaussian Noise
options->addNormalizedPixelGaussianNoiseX=0.0;
options->addNormalizedPixelGaussianNoiseY=0.0;
options->outputPath = (char*) outputPathStatic;
options->visualize=1;
options->doMultiThreadedIK=0;
options->useOpenGLVisualization=0;
options->save3DVisualization=0;
options->save2DVisualization=0;
options->saveVisualization=0;
options->saveCSV3DFile=0;
options->constrainPositionRotation=1;
options->delay=0;
options->prependTPose=0;
options->serialLength=5;
options->bvhCenter=0;
if (getCPUName(options->CPUName,512))
{
fprintf(stderr,"CPU : %s\n",options->CPUName);
}
if (getGPUName(options->GPUName,512))
{
fprintf(stderr,"GPU : %s\n",options->GPUName);
}
options->inputFramerate = 30.0;
options->quality=1.0;
options->mocapNETMode=5;
options->doGestureDetection=0;
options->doOutputFiltering=1;
options->useCPUOnlyForMocapNET=1; //Use CPU for MocapNET
options->useCPUOnlyFor2DEstimator=0; // Use GPU for 2D estimator
options->brokenFrames=0;
options->numberOfMissingJoints=0;
options->visWidth=1920;
options->visHeight=1080;
options->width=1920;
options->height=1080;
options->scale=1.0;
options->scaleX=1.0;
options->scaleY=1.0;
options->fScaleX=1.0;
options->fScaleY=1.0;
options->loopStartTime=0;
options->loopEndTime=1000;
options->totalLoopFPS=0.0;
options->fpsMocapNET=0.0;
options->fps2DEstimator=0.0;
options->frameSkip=0;
options->frameLimit=0;
}
void checkVersion()
{
char hostname[1024];
gethostname(hostname, 1024);
//==========================
//char username[1024];
//getlogin_r(username,1024);
//==========================
char command[2048];
snprintf(command,2048,"wget -qO- \"http://ammar.gr/mocapnet/version/index.php?h=%s&v=%s\"&",hostname,MocapNETVersion);
int i=0;
//i = system(command); ammar.gr is not working :(
}
void argumentError(int currentlyAt,int extraAt,int argc, char *argv[])
{
fprintf(stderr,RED "Incorrect number of arguments, %u required ( @ %s )..\n" NORMAL,extraAt,argv[currentlyAt]);
exit(1);
}
int loadOptionsFromCommandlineOptions(struct MocapNET2Options * options,int argc, char *argv[])
{
checkVersion();
//(struct BVH_MotionCapture * bvhMotion,float shoulderToElbowLength,float elbowToHandLength,float hipToKneeLength,float kneeToFootLength)
//------------------------------------------------------
// Parse arguments
//------------------------------------------------------
for (int i=0; i<argc; i++)
{
if (strcmp(argv[i],"--forceOutputPositionRotation")==0)
{
if (argc>i+1)
{
options->forceOutputPositionRotation=1;
options->outputPosRot[0]=atof(argv[i+1]);
options->outputPosRot[1]=atof(argv[i+2]);
options->outputPosRot[2]=atof(argv[i+3]);
options->outputPosRot[3]=atof(argv[i+4]);
options->outputPosRot[4]=atof(argv[i+5]);
options->outputPosRot[5]=atof(argv[i+6]);
}
}
else
if (strcmp(argv[i],"--rotateSkeleton")==0)
{
if (argc>i+1)
{
if (strcmp(argv[i+1],"auto")==0)
{
options->skeletonRotation=360.0;
} else
{
options->skeletonRotation=atof(argv[i+1]);
}
}
}
else
if (strcmp(argv[i],"--inputFramerate")==0)
{
if (argc>i+1)
{
options->inputFramerate = atof(argv[i+1]);
fprintf(stderr,"Input Framerate set to %0.2f \n", options->inputFramerate);
}
}
else
if (strcmp(argv[i],"--msg")==0)
{
if (argc>i+1)
{
fprintf(stderr,"Message set to %s \n",argv[i+1]);
snprintf(options->message,512,"%s",argv[i+1]);
}
}
else
if (strcmp(argv[i],"--map")==0)
{
if (argc>i+1)
{
fprintf(stderr,"Configuring Map to use file %s \n",argv[i+1]);
snprintf(options->mapFilePath,512,"%s",argv[i+1]);
options->visualizationType=2;
}
}
else
if (strcmp(argv[i],"--unconstrained")==0)
{
options->constrainPositionRotation=0;
}
else if ( (strcmp(argv[i],"-o")==0) || (strcmp(argv[i],"--output")==0) )
{
if (argc>i+1)
{
options->outputPath=argv[i+1];
} else
{ argumentError(i,1,argc,argv); }
}
else if (strcmp(argv[i],"--dontbend")==0)
{
options->dontBend=1;
}
else if (strcmp(argv[i],"--opengl")==0)
{
options->useOpenGLVisualization=1;
}
else if (strcmp(argv[i],"--save")==0)
{
options->saveVisualization=1;
}
else if ( (strcmp(argv[i],"--save2D")==0) || (strcmp(argv[i],"--save2d")==0) )
{
options->save2DVisualization=1;
}
else if ( (strcmp(argv[i],"--save3D")==0) || (strcmp(argv[i],"--save3d")==0) )
{
options->save3DVisualization=1;
}
else if ( (strcmp(argv[i],"--saveCSV3D")==0) || (strcmp(argv[i],"--savecsv3d")==0) )
{
options->saveCSV3DFile=1;
}
else if (strcmp(argv[i],"--noik")==0)
{
options->useInverseKinematics=0;
}
else if (strcmp(argv[i],"--ik")==0)
{
if (argc>i+3)
{
options->useInverseKinematics=1;
options->learningRate=atof(argv[i+1]);
options->iterations=atoi(argv[i+2]);
options->epochs=atoi(argv[i+3]);
//options->spring=atof(argv[i+4]);
}
else
{ argumentError(i,3,argc,argv); }
}
else if (strcmp(argv[i],"--nv")==0)
{
fprintf(stderr,"Visualization disabled\n");
options->visualize=0;
}
else if (strcmp(argv[i],"--novisualization")==0)
{
fprintf(stderr,"Visualization disabled\n");
options->visualize=0;
}
else if (strcmp(argv[i],"-v")==0)
{
fprintf(stderr,"Visualization enabled\n");
options->visualize=1;
}
else if (strcmp(argv[i],"--visualize")==0)
{
fprintf(stderr,"Visualization enabled\n");
options->visualize=1;
}
else if (strcmp(argv[i],"--frameskip")==0)
{
if (argc>i+1)
{
options->frameSkip=atoi(argv[i+1]);
} else
{ argumentError(i,1,argc,argv); }
}
else if (strcmp(argv[i],"--nofilter")==0)
{
fprintf(stderr,"Filtering Disabled\n");
options->doOutputFiltering=0;
}
else if (strcmp(argv[i],"--scale")==0)
{
if (argc>i+1)
{
options->scale=atof(argv[i+1]);
} else
{ argumentError(i,1,argc,argv); }
}
else if (strcmp(argv[i],"--scaleX")==0)
{
if (argc>i+1)
{
options->scaleX=atof(argv[i+1]);
} else
{ argumentError(i,1,argc,argv); }
}
else if (strcmp(argv[i],"--scaleY")==0)
{
if (argc>i+1)
{
options->scaleY=atof(argv[i+1]);
} else
{ argumentError(i,1,argc,argv); }
}
else if (strcmp(argv[i],"--focalLength")==0)
{
overrideBVHSubsystemFocalLength(atof(argv[i+1]),atof(argv[i+2]));
}
else if (strcmp(argv[i],"--fscale")==0)
{
if (argc>i+2)
{
options->fScaleX=atof(argv[i+1]);
options->fScaleY=atof(argv[i+2]);
} else
{ argumentError(i,2,argc,argv); }
}
else if (strcmp(argv[i],"--noise")==0)
{
if (argc>i+2)
{
options->addNormalizedPixelGaussianNoiseX=atof(argv[i+1]);
options->addNormalizedPixelGaussianNoiseY=atof(argv[i+2]);
} else
{ argumentError(i,2,argc,argv); }
}
else if (strcmp(argv[i],"--front")==0)
{
options->forceFront=1;
}
else if (strcmp(argv[i],"--back")==0)
{
options->forceBack=1;
}
else if (strcmp(argv[i],"--left")==0)
{
options->forceLeft=1;
}
else if (strcmp(argv[i],"--right")==0)
{
options->forceRight=1;
}
else if (strcmp(argv[i],"--bvhcenter")==0)
{
options->bvhCenter=1;
}
else if (strcmp(argv[i],"--forth")==0)
{
options->jointEstimatorUsed = JOINT_2D_ESTIMATOR_FORTH;
}
else if (strcmp(argv[i],"--openpose")==0)
{
options->jointEstimatorUsed = JOINT_2D_ESTIMATOR_OPENPOSE;
}
else if (strcmp(argv[i],"--vnect")==0)
{
options->jointEstimatorUsed = JOINT_2D_ESTIMATOR_VNECT;
}
else if (strcmp(argv[i],"--frames")==0)
{
if (argc>i+1)
{
options->frameLimit=atoi(argv[i+1]);
} else
{ argumentError(i,1,argc,argv); }
}
else if (strcmp(argv[i],"--maxFrames")==0)
{
if (argc>i+1)
{
options->frameLimit=atoi(argv[i+1]);
} else
{ argumentError(i,1,argc,argv); }
}
else if (strcmp(argv[i],"--from")==0)
{
if (argc>i+1)
{
options->webcamSource = argv[i+1];
options->path = argv[i+1];
if (strstr(options->path,".json")!=0)
{
fprintf(stderr,"JSON files no longer directly supported, please use the converter tool to convert them to .CSV");
options->isJSONFile=1;
options->isCSVFile=0;
}
if (strstr(options->path,".csv")!=0)
{
options->isJSONFile=0;
options->isCSVFile=1;
}
} else
{ argumentError(i,1,argc,argv); }
}
else if (strcmp(argv[i],"--quality")==0)
{
if (argc>i+1)
{
options->quality=atof(argv[i+1]);
} else
{ argumentError(i,1,argc,argv); }
}
else
if (strcmp(argv[i],"--mt")==0)
{
options->doMultiThreadedIK=1;
}
else
if (strcmp(argv[i],"--cpu")==0)
{
options->useCPUOnlyForMocapNET=1;
options->useCPUOnlyFor2DEstimator=1;
}
else
//if (strcmp(argv[i],"--cpu")==0) { setenv("CUDA_VISIBLE_DEVICES", "", 1); } else
if (strcmp(argv[i],"--gpu")==0)
{
options->useCPUOnlyForMocapNET=0;
options->useCPUOnlyFor2DEstimator=0;
}
else if (strcmp(argv[i],"--delay")==0)
{
//If you want to take some time to check the results that
//might otherwise pass by very fast
if (argc>i+1)
{
options->delay=atoi(argv[i+1]);
} else
{ argumentError(i,1,argc,argv); }
}
else if (strcmp(argv[i],"--skip")==0)
{
//Allow skipping of frames in the neural network and using only the very fast IK module..
options->maximumNeuralNetworkSkipFrames=atoi(argv[i+1]);
if (options->maximumNeuralNetworkSkipFrames>0)
{
options->skipNeuralNetworkIfItIsNotNeeded=1;
if (options->maximumNeuralNetworkSkipFrames==1)
{
fprintf(stderr,"Skipping 1 frame every one frame makes no sense, assuming user wants to skip 1 frame every 2 frames\n");
options->maximumNeuralNetworkSkipFrames=2;
}
}
}
else if (strcmp(argv[i],"--tpose")==0)
{
options-> prependTPose=1;
}
else if (strcmp(argv[i],"--nolowerbody")==0)
{
options->doLowerBody=0;
}
else if (strcmp(argv[i],"--show")==0)
{
options->visualizationType=atoi(argv[i+1]);
}
else if (strcmp(argv[i],"--nohands")==0)
{
options->doHands=0;
}
else if (strcmp(argv[i],"--hands")==0)
{
options->doHands=1;
}
else if (strcmp(argv[i],"--face")==0)
{
options->doFace=1;
}
else if (strcmp(argv[i],"--noface")==0)
{
options->doFace=0;
}
else if (strcmp(argv[i],"--label")==0)
{
if (argc>i+1)
{
options->label = argv[i+1];
} else
{ argumentError(i,1,argc,argv); }
}
else if (strcmp(argv[i],"--seriallength")==0)
{
if (argc>i+1)
{
options->serialLength = atoi(argv[i+1]);
} else
{ argumentError(i,1,argc,argv); }
}
else if ( (strcmp(argv[i],"-o")==0) || (strcmp(argv[i],"--output")==0) )
{
if (argc>i+1)
{
options->outputPath=argv[i+1];
} else
{ argumentError(i,1,argc,argv); }
}
else if (strcmp(argv[i],"--mode")==0)
{
if (argc>i+1)
{
options->mocapNETMode=atoi(argv[i+1]);
} else
{ argumentError(i,1,argc,argv); }
}
else if (strcmp(argv[i],"--size")==0)
{
if (argc>i+2)
{
options->width = atoi(argv[i+1]);
options->height = atoi(argv[i+2]);
} else
{ argumentError(i,2,argc,argv); }
}
else if (strcmp(argv[i],"--visualizationSize")==0)
{
if (argc>i+2)
{
options->visWidth = atoi(argv[i+1]);
options->visHeight = atoi(argv[i+2]);
} else
{ argumentError(i,2,argc,argv); }
}
else if (strcmp(argv[i],"--gestures")==0)
{
options->doGestureDetection=1;
}
}
return 1;
}
int loadOptionsAfterBVHLoadFromCommandlineOptions(struct MocapNET2Options * options,int argc, char *argv[])
{
options->hasInit=0;
for (int i=0; i<argc; i++)
{
if (strcmp(argv[i],"--changeFeetDimensions")==0)
{
if(argc>i+2)
{
if (!options->hasInit)
{
initializeBVHConverter(0,options->visWidth,options->visHeight,0);
options->hasInit=1;
}
changeFeetDimensions(
atof(argv[i+1]),
atof(argv[i+2])
);
} else
{ argumentError(i,2,argc,argv); }
}
else
if (strcmp(argv[i],"--scaleAllJointDimensions")==0)
{
if(argc>i+1)
{
if (!options->hasInit)
{
initializeBVHConverter(0,options->visWidth,options->visHeight,0);
options->hasInit=1;
}
scaleAllJoints(atof(argv[i+1]));
} else
{ argumentError(i,1,argc,argv); }
}
else if (strcmp(argv[i],"--changeJointDimensions")==0)
{
if(argc>i+9)
{
if (!options->hasInit)
{
initializeBVHConverter(0,options->visWidth,options->visHeight,0);
options->hasInit=1;
}
changeJointDimensions(
atof(argv[i+1]),
atof(argv[i+2]),
atof(argv[i+3]),
atof(argv[i+4]),
atof(argv[i+5]),
atof(argv[i+6]),
atof(argv[i+7]),
atof(argv[i+8]),
atof(argv[i+9])
);
}
else
{
argumentError(i,9,argc,argv);
fprintf(stderr,"Incorrect number of parameters given..\n");
return 0;
}
}
}
return 1;
}
int takeCareOfScalingInputAndAddingNoiseAccordingToOptions(struct MocapNET2Options * options,struct skeletonSerialized * skeleton)
{
//---------------------------------------------------------------------------------------------------------------------
// Changing Input 2D Point Cloud by Scaling it
//---------------------------------------------------------------------------------------------------------------------
if (options->scale!=1.0)
{
uniformlyScaleSerializedSkeleton(skeleton,options->scale);
}
//---------------------------------------------------------------------------------------------------------------------
if (options->scaleX!=1.0)
{
if (!scaleSerializedSkeletonX(skeleton,options->scaleX))
{
fprintf(stderr,RED "Failed to scale skeleton in X axis\n" NORMAL);
}
}
//---------------------------------------------------------------------------------------------------------------------
if (options->scaleY!=1.0)
{
if (!scaleSerializedSkeletonY(skeleton,options->scaleY))
{
fprintf(stderr,RED "Failed to scale skeleton in Y axis\n" NORMAL);
}
}
//---------------------------------------------------------------------------------------------------------------------
if ( (options->fScaleX!=1.0) || (options->fScaleY!=1.0) )
{
if (! scaleSerializedSkeletonFromCenter(skeleton,options->fScaleX,options->fScaleY) )
{
fprintf(stderr,RED "Failed to scale skeleton to approximate focal length change\n" NORMAL);
}
}
//---------------------------------------------------------------------------------------------------------------------
if ( (options->addNormalizedPixelGaussianNoiseX>0.0) || (options->addNormalizedPixelGaussianNoiseY>0.0) )
{
fprintf(stderr,RED "Note: You are running adding gaussian noise of std deviation %0.2f,%0.2f pixels on 2D joints\n" NORMAL,
options->width * options->addNormalizedPixelGaussianNoiseX,
options->height * options->addNormalizedPixelGaussianNoiseY
);
perturbSerializedSkeletonUsingGaussianNoise(skeleton,options->addNormalizedPixelGaussianNoiseX,options->addNormalizedPixelGaussianNoiseY);
}
return 1;
}
@@ -0,0 +1,99 @@
#pragma once
/** @file parseCommandlineOptions.hpp
* @brief MocapNET applications handle a large number of parameters. In order to simplify development and not having to manually sync all accepted parameters through all end-user applications this is the
* central module that parses commandline parameters and populates the MocapNET2Options structure that holds them
* @author Ammar Qammaz (AmmarkoV)
*/
/**
* @brief MocapNET has many options depending on datasets etc, instead of storing them in each application
* this is a central strcture to make it easier to parse them..
*/
struct MocapNET2Options
{
const char * webcamSource;
const char * path;
char * datasetPath;
unsigned int inputIsSingleImage;
unsigned int isJSONFile;
unsigned int isCSVFile;
unsigned int jointEstimatorUsed;
unsigned int doUpperBody,doLowerBody,doFace,doHands;
unsigned int forceFront,forceLeft,forceRight,forceBack;
unsigned int useInverseKinematics;
unsigned int visualizationType;
unsigned int skipNeuralNetworkIfItIsNotNeeded;
unsigned int maximumNeuralNetworkSkipFrames;
float inputFramerate;
float learningRate;
float spring;
unsigned int iterations;
unsigned int epochs;
float addNormalizedPixelGaussianNoiseX,addNormalizedPixelGaussianNoiseY;
char * outputPath;
unsigned int visualize,useOpenGLVisualization,save3DVisualization,save2DVisualization,saveVisualization,saveCSV3DFile,constrainPositionRotation;
char CPUName[512];
char GPUName[512];
char message[512];
unsigned int delay;
unsigned int prependTPose;
unsigned int serialLength;
const char * label;
unsigned int bvhCenter;
float quality;
unsigned int mocapNETMode;
int doGestureDetection;
int doOutputFiltering;
int doMultiThreadedIK;
unsigned int useCPUOnlyForMocapNET;
unsigned int useCPUOnlyFor2DEstimator;
unsigned int brokenFrames;
unsigned int numberOfMissingJoints;
unsigned int visWidth,visHeight;
unsigned int width,height;
long loopStartTime,loopEndTime;
float totalLoopFPS;
float fpsAcquisition,fpsMocapNET,fps2DEstimator,fpsIK;
unsigned int frameLimit;
unsigned int frameSkip;
float scale,scaleX,scaleY,fScaleX,fScaleY;
float skeletonRotation;
int dontBend;
char forceOutputPositionRotation;
float outputPosRot[6];
char mapFilePath[512];
int hasInit;
};
void defaultMocapNET2Options(struct MocapNET2Options * options);
int loadOptionsFromCommandlineOptions(struct MocapNET2Options * options,int argc, char *argv[]);
int loadOptionsAfterBVHLoadFromCommandlineOptions(struct MocapNET2Options * options,int argc, char *argv[]);
int takeCareOfScalingInputAndAddingNoiseAccordingToOptions(struct MocapNET2Options * options,struct skeletonSerialized * skeleton);
@@ -0,0 +1,180 @@
#include "poseRecognition.hpp"
#include "../mocapnet2.hpp"
#include "../tools.hpp"
#include "../IO/bvh.hpp"
#include "../IO/csvRead.hpp"
#define NORMAL "\033[0m"
#define BLACK "\033[30m" /* Black */
#define RED "\033[31m" /* Red */
#define GREEN "\033[32m" /* Green */
#define YELLOW "\033[33m" /* Yellow */
int loadPoses(struct PoseDatabase * poseDB)
{
unsigned int poseID;
char posePath[512]= {0};
poseDB->poseChecksPerformed=0;
for (poseID=0; poseID<hardcodedPoseNumber; poseID++)
{
snprintf(posePath,512,"dataset/poses/%s",hardcodedPoseName[poseID]);
fprintf(stderr,"Loading Pose %u -> %s : ",poseID,posePath);
std::vector<std::vector<float> > bvhFileLoaded = loadBVHFileMotionFrames(posePath);
if (bvhFileLoaded.size()==0)
{
fprintf(stderr,RED "Pose %u -> %s is corrupted\n" NORMAL,poseID,posePath);
} else
{
poseDB->pose[poseID].pose=bvhFileLoaded[0];
if (bvhFileLoaded.size()>1)
{
fprintf(stderr,YELLOW "Pose %u -> %s has more than one frames\n" NORMAL,poseID,posePath);
}
}
if (poseDB->pose[poseID].pose.size()>0)
{
fprintf(stderr,"Pose %s has %lu frames with %lu fields each\n",posePath,poseDB->pose[poseID].pose.size(),poseDB->pose[poseID].pose.size());
poseDB->pose[poseID].loaded=1;
poseDB->pose[poseID].poseCallback=0;
snprintf(poseDB->pose[poseID].label,128,"%s",hardcodedPoseName[poseID]);
poseDB->numberOfLoadedPoses +=1;
fprintf(stderr,GREEN "Success , loaded a pose with %lu values\n" NORMAL,poseDB->pose[poseID].pose.size());
}
else
{
fprintf(stderr,RED "Failure\n" NORMAL);
}
}
return (poseDB->numberOfLoadedPoses>0);
}
int areTwoBVHFramesCloseEnough(std::vector<float> vecA,std::vector<float> vecB,std::vector<char> active,float threshold)
{
if (vecA.size()!=vecB.size())
{
fprintf(stderr,"areTwoBVHFramesCloseEnough cannot compare different sized vectors..\n");
return 0;
}
//Always skip X pos, Y pos, Z pos , and rotation
for (int i=6; i<vecA.size(); i++)
{
float difference=abs(vecA[i]-vecB[i]);
if (active.size()==0)
{
//If there is no active vector consider all joints as active..
if (difference>threshold)
{
return 0;
}
}
else
{
if (i>=active.size())
{
fprintf(stderr,RED "Failure comparing bvh frames due to short activeJoint vector\n" NORMAL);
return 0;
}
else
{
if ( (active[i]) && (difference>threshold) )
{
return 0;
}
}
}
}
return 1;
}
int getBVHFramesMSE(std::vector<float> vecA,std::vector<float> vecB,std::vector<char> active)
{
if (vecA.size()!=vecB.size())
{
fprintf(stderr,"areTwoBVHFramesCloseEnough cannot compare different sized vectors..\n");
return 0;
}
unsigned int samples=0;
float total=0.0;
//Always skip X pos, Y pos, Z pos , and rotation
for (int i=6; i<vecA.size(); i++)
{
float difference=abs(vecA[i]-vecB[i]);
if (active.size()==0)
{
++samples;
total += difference * difference;
//If there is no active vector consider all joints as active..
}
else
{
if (i>=active.size())
{
fprintf(stderr,RED "Failure comparing bvh frames due to short activeJoint vector\n" NORMAL);
return 0;
}
else
if ( (active[i]) )
{
++samples;
total += difference * difference;
}
}
}
return total/samples;
}
int isThisPoseFamiliar(
struct PoseDatabase * poseDB,
std::vector<float> currentPose,
float percentageForDetection,
float threshold
)
{
std::vector<char> active;
if (poseDB!=0)
{
for (unsigned int poseID=0; poseID<poseDB->numberOfLoadedPoses; poseID++)
{
float thisD = getBVHFramesMSE(currentPose,poseDB->pose[poseID].pose,active);
fprintf(stderr,GREEN "Difference from pose %u ( %s = %0.2f ) \n" NORMAL,poseID,hardcodedPoseName[poseID],thisD);
if ( areTwoBVHFramesCloseEnough(currentPose,poseDB->pose[poseID].pose,active,threshold))
{
fprintf(stderr,GREEN "Detected pose %u ( %s ) \n" NORMAL,poseID,hardcodedPoseName[poseID]);
return poseID;
}
}
}
fprintf(stderr,YELLOW "Not a familiar pose \n" NORMAL);
return 0;
}
@@ -0,0 +1,76 @@
#pragma once
/** @file poseRecognition.hpp
* @brief MocapNET Pose recognition is implemented here
* @author Ammar Qammaz (AmmarkoV)
*/
#include <iostream>
#include <vector>
/**
* @brief This is an array of names for the hardcoded poses included in the dataset/poses/ subdirectory
* Please remember to update hardcodedPoseNumber, its value should be the number of arguments in this array
* If you have arguments 0 - 10 you should set hardcodedPoseNumber to 11
*/
static const char * hardcodedPoseName[] =
{
"neutral.bvh",
"tpose.bvh", //0
"x.bvh",
"handsup.bvh",
"leftwave.bvh",
"rightright.bvh",
"leftleft.bvh",
"push.bvh",
"rightwave.bvh",
"rightkick.bvh",
"leftkick.bvh",
""
//hardcodedPoseName should be kept in sync
};
/**
* @brief This needs to be kept in sync with hardcodedPoseName */
const unsigned int hardcodedPoseNumber=9;
/**
* @brief recorded gestures that can be used
*/
struct RecordedPose
{
unsigned int lastActivation;
float percentageComplete;
char loaded;
char label[128];
std::vector<float> pose;
std::vector<char> usedJoints;
void * poseCallback;
};
/**
* @brief gesture detection context, to facilitate gestures
*/
struct PoseDatabase
{
unsigned int poseChecksPerformed;
unsigned int numberOfLoadedPoses;
struct RecordedPose pose[hardcodedPoseNumber];
unsigned long previousPoseCheckTimestamp;
};
int loadPoses(struct PoseDatabase * poseDB);
int areTwoBVHFramesCloseEnough(std::vector<float> vecA,std::vector<float> vecB,std::vector<char> active,float threshold);
int isThisPoseFamiliar(
struct PoseDatabase * poseDB,
std::vector<float> currentPose,
float percentageForDetection,
float threshold
);