mirror of
https://github.com/storytold/storyteller-ml.git
synced 2026-10-15 00:03:06 +00:00
Add MocapNET
This commit is contained in:
+276
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+58
@@ -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) ;
|
||||
+252
@@ -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);
|
||||
+663
@@ -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;
|
||||
}
|
||||
+99
@@ -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
|
||||
);
|
||||
Reference in New Issue
Block a user