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,62 @@
#!/bin/bash
#Written by Ammar Qammaz a.k.a AmmarkoV - 2020
# This bash script uses gnuplot and R so make sure to :
# sudo apt-get install gnuplot r-base
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
cd "$DIR"
ORIG_DIR=`pwd`
function getStatistics
{
#use R to generate statistics
#sudo apt-get install r-base
R -q -e "x <- read.csv('$1', header = F); summary(x); sd(x[ , 1])" > $2
cat $1 | wc -l >> $2
}
IN="$1"
OUTPUT_IMAGE="AllDistancesFrequency_$2.png"
echo "plotAllJointsDistanceFrequency.sh $IN $OUTPUT_IMAGE"
getStatistics $IN $IN-statsraw.txt
cat $IN-statsraw.txt | grep -E 'Min|Qu|Median|Mean|Max' > $IN-stats.txt
STATS=`cat $IN-stats.txt`
MEAN=`cat $IN-stats.txt | grep "Mean" | cut -d':' -f2 `
MEDIAN=`cat $IN-stats.txt | grep "Median" | cut -d':' -f2 `
MAXIMUM=`cat $IN-stats.txt | grep "Max" | cut -d':' -f2 `
PLACETEXT=`cat $IN-stats.txt | grep "3rd" | cut -d':' -f2 `
LOW_LIMIT="50";
LIMIT="150";
NUMBER_OF_RECORDS=`wc -l $1 | cut -d' ' -f1`
GNUPLOT_CMD="set terminal png; \
set output \"$OUTPUT_IMAGE\"; set yrange [0:1];\
set title \"Frequency precision diagram $IN \";\
set xlabel \"Distance Of Joints(mm)\"; \
set ylabel \"Frequency Of Value\"; \
set arrow from $MEAN, graph 0 to $MEAN, graph 1 nohead; \
set label \"Mean Value of $MEAN mm \" at $MEAN,0.40; \
set arrow from $MEDIAN, graph 0 to $MEDIAN, graph 1 nohead; \
set label \"Median Value of $MEDIAN mm \" at $MEDIAN,0.30; \
set label \"$STATS\" at $PLACETEXT,0.85; \
binwidth=3;\
bin(x,width)=width*floor(x/width);\
plot [0:] '$IN' using (bin(\$1,binwidth)):(1.0) smooth cnorm t 'smooth cumulative'"
echo "WE WILL RUN "
echo "gnuplot -e \"$GNUPLOT_CMD\""
echo " "
echo " "
gnuplot -e "$GNUPLOT_CMD"
rm $IN-statsraw.txt
rm $IN-stats.txt
exit 0
@@ -0,0 +1,108 @@
#!/usr/bin/env python3
#Written by Ammar Qammaz a.k.a AmmarkoV - 2020
import h5py
import numpy as np
import csv
import os
import sys
from scipy.spatial import procrustes
from scipy.linalg import orthogonal_procrustes
#Taken from https://github.com/una-dinosauria/3d-pose-baseline/blob/master/src/procrustes.py
def compute_similarity_transform(X, Y, compute_optimal_scale=False):
"""
A port of MATLAB's `procrustes` function to Numpy.
Adapted from http://stackoverflow.com/a/18927641/1884420
Args
X: array NxM of targets, with N number of points and M point dimensionality
Y: array NxM of inputs
compute_optimal_scale: whether we compute optimal scale or force it to be 1
Returns:
d: squared error after transformation
Z: transformed Y
T: computed rotation
b: scaling
c: translation
"""
muX = X.mean(0)
muY = Y.mean(0)
X0 = X - muX
Y0 = Y - muY
ssX = (X0**2.).sum()
ssY = (Y0**2.).sum()
# centred Frobenius norm
normX = np.sqrt(ssX)
normY = np.sqrt(ssY)
# scale to equal (unit) norm
X0 = X0 / normX
Y0 = Y0 / normY
# optimum rotation matrix of Y
A = np.dot(X0.T, Y0)
U,s,Vt = np.linalg.svd(A,full_matrices=False)
V = Vt.T
T = np.dot(V, U.T)
# Make sure we have a rotation
detT = np.linalg.det(T)
V[:,-1] *= np.sign( detT )
s[-1] *= np.sign( detT )
T = np.dot(V, U.T)
traceTA = s.sum()
if compute_optimal_scale: # Compute optimum scaling of Y.
b = traceTA * normX / normY
d = 1 - traceTA**2
Z = normX*traceTA*np.dot(Y0, T) + muX
else: # If no scaling allowed
b = 1
d = 1 + ssY/ssX - 2 * traceTA * normY / normX
Z = normY*np.dot(Y0, T) + muX
c = muX - b*np.dot(muY, T)
return d, Z, T, b, c
def pointListsReturnAvgDistance(A,B):
numberOfPoints=A.shape[0]
if (A.shape[0]!=B.shape[0]):
print("Error comparing point lists of different length")
return inf
distance=0.0
for i in range(0,numberOfPoints):
#---------
xA=A[i][0]
yA=A[i][1]
zA=A[i][2]
#---------
xB=B[i][0]
yB=B[i][1]
zB=B[i][2]
#---------
xAB=xA-xB
yAB=yA-yB
zAB=zA-zB
#Pythagorean theorem for 3 dimensions
#distance = squareRoot( xAB^2 + yAB^2 + zAB^2 )
distance+=np.sqrt( (xAB*xAB) + (yAB*yAB) + (zAB*zAB) )
return distance/numberOfPoints
@@ -0,0 +1,622 @@
#!/usr/bin/env python3
#Written by Ammar Qammaz a.k.a AmmarkoV - 2020
from align2DPoints import pointListsReturnAvgDistance,compute_similarity_transform
from drawPointClouds import findJointID,get3DDistance,setupDrawing,drawLimbDimensions,drawLimbError,drawFrameError,drawAfterEndOfComparison
from writeCSVResults import writeCSVFileResults,appendRAWResultsForGNUplot
import numpy as np
import gc
import os
import sys
import csv
import time
import array
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from mpl_toolkits.mplot3d import axes3d, Axes3D
class bcolors:
HEADER = '\033[95m'
OKBLUE = '\033[94m'
OKGREEN = '\033[92m'
WARNING = '\033[93m'
FAIL = '\033[91m'
ENDC = '\033[0m'
BOLD = '\033[1m'
UNDERLINE = '\033[4m'
#I have added a seperate list with the joints we want to compare
#to avoid the weird parent lists when you remove one joint
JOINTS_TO_COMPARE=list()
#JOINTS_TO_COMPARE.append('Nose') #0
JOINTS_TO_COMPARE.append('Neck') #1
JOINTS_TO_COMPARE.append('RShoulder') #2
JOINTS_TO_COMPARE.append('RElbow') #3
JOINTS_TO_COMPARE.append('RWrist') #4
JOINTS_TO_COMPARE.append('LShoulder') #5
JOINTS_TO_COMPARE.append('LElbow') #6
JOINTS_TO_COMPARE.append('LWrist') #7
JOINTS_TO_COMPARE.append('MidHip') #8
JOINTS_TO_COMPARE.append('RHip') #9
JOINTS_TO_COMPARE.append('RKnee') #10
JOINTS_TO_COMPARE.append('RAnkle') #11
JOINTS_TO_COMPARE.append('LHip') #12
JOINTS_TO_COMPARE.append('LKnee') #13
JOINTS_TO_COMPARE.append('LAnkle') #14
#These are our labels they have to match what we want to process
#If you don't want something to be part of the measurment remove it from here
#as well as from the JOINT_PARENTS list
#---------------------------------------------------------------------
JOINT_LABELS=list()
JOINT_LABELS.append('Nose') #0
JOINT_LABELS.append('Neck') #1
JOINT_LABELS.append('RShoulder') #2
JOINT_LABELS.append('RElbow') #3
JOINT_LABELS.append('RWrist') #4
JOINT_LABELS.append('LShoulder') #5
JOINT_LABELS.append('LElbow') #6
JOINT_LABELS.append('LWrist') #7
JOINT_LABELS.append('MidHip') #8
JOINT_LABELS.append('RHip') #9
JOINT_LABELS.append('RKnee') #10
JOINT_LABELS.append('RAnkle') #11
JOINT_LABELS.append('LHip') #12
JOINT_LABELS.append('LKnee') #13
JOINT_LABELS.append('LAnkle') #14
JOINT_LABELS.append('REye') #15
JOINT_LABELS.append('LEye') #16
JOINT_LABELS.append('REar') #17
JOINT_LABELS.append('LEar') #18
JOINT_LABELS.append('LBigToe') #19
JOINT_LABELS.append('LSmallToe') #20
JOINT_LABELS.append('LHeel') #21
JOINT_LABELS.append('RBigToe') #22
JOINT_LABELS.append('RSmallToe') #23
JOINT_LABELS.append('RHeel') #24
#These are our parents they have to match JOINT_LABELS
#If you don't want something to be part of the measurment remove it from here
#as well as from the JOINT_LABELS list
#---------------------------------------------------------------------
JOINT_PARENTS=list()
JOINT_PARENTS.append(findJointID("Neck",JOINT_LABELS)) #0 Parent of Nose is Neck
JOINT_PARENTS.append(findJointID("MidHip",JOINT_LABELS)) #1 Parent of Neck is MidHip
JOINT_PARENTS.append(findJointID("Neck",JOINT_LABELS)) #2 Parent of RShoulder is Neck
JOINT_PARENTS.append(findJointID("RShoulder",JOINT_LABELS)) #3 Parent of RElbow is RShoulder
JOINT_PARENTS.append(findJointID("RElbow",JOINT_LABELS)) #4 etc ...
JOINT_PARENTS.append(findJointID("Neck",JOINT_LABELS)) #5
JOINT_PARENTS.append(findJointID("LShoulder",JOINT_LABELS)) #6
JOINT_PARENTS.append(findJointID("LElbow",JOINT_LABELS)) #7
JOINT_PARENTS.append(findJointID("MidHip",JOINT_LABELS)) #8
JOINT_PARENTS.append(findJointID("MidHip",JOINT_LABELS)) #9
JOINT_PARENTS.append(findJointID("RHip",JOINT_LABELS)) #10
JOINT_PARENTS.append(findJointID("RKnee",JOINT_LABELS)) #11
JOINT_PARENTS.append(findJointID("MidHip",JOINT_LABELS)) #12
JOINT_PARENTS.append(findJointID("LHip",JOINT_LABELS)) #13
JOINT_PARENTS.append(findJointID("LKnee",JOINT_LABELS)) #14
JOINT_PARENTS.append(findJointID("Nose",JOINT_LABELS)) #15
JOINT_PARENTS.append(findJointID("Nose",JOINT_LABELS)) #16
JOINT_PARENTS.append(findJointID("Nose",JOINT_LABELS)) #17
JOINT_PARENTS.append(findJointID("Nose",JOINT_LABELS)) #18
JOINT_PARENTS.append(findJointID("LAnkle",JOINT_LABELS)) #19
JOINT_PARENTS.append(findJointID("LAnkle",JOINT_LABELS)) #20
JOINT_PARENTS.append(findJointID("LAnkle",JOINT_LABELS)) #21
JOINT_PARENTS.append(findJointID("RAnkle",JOINT_LABELS)) #22
JOINT_PARENTS.append(findJointID("RAnkle",JOINT_LABELS)) #23
JOINT_PARENTS.append(findJointID("RAnkle",JOINT_LABELS)) #24
#These are our labels that based on JOINT_PARENTS auto-create the appropriate labels
#---------------------------------------------------------------------
JOINT_PARENT_LABELS=list()
for i in range(0,len(JOINT_LABELS)):
JOINT_PARENT_LABELS.append(JOINT_LABELS[JOINT_PARENTS[i]])
#---------------------------------------------------------------------
def checkIfFileExists(filename):
return os.path.isfile(filename)
def convert_bytes(num):
"""
this function will convert bytes to MB.... GB... etc
"""
step_unit = 1000.0 #1024 bad the size
for x in ['bytes', 'KB', 'MB', 'GB', 'TB']:
if num < step_unit:
return "%3.1f %s" % (num, x)
num /= step_unit
def getNumberOfLines(filename):
print("Counting number of lines in file ",filename)
with open(filename) as f:
return sum(1 for line in f)
#---------------------------------------------------------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------------------------------------------------------
def readCSVFile(filename,memPercentage,csvDelimiter,useHalfFloats,groupOutput):
print("CSV file :",filename,"..\n")
if (not checkIfFileExists(filename)):
print( bcolors.WARNING + "Input file "+filename+" does not exist, cannot read ground truth.." + bcolors.ENDC)
print("Current Directory was "+os.getcwd())
sys.exit(0)
start = time.time()
dtypeSelected=np.dtype(np.float32)
dtypeSelectedByteSize=int(dtypeSelected.itemsize)
if (useHalfFloats):
dtypeSelected=np.dtype(np.float16)
dtypeSelectedByteSize=int(dtypeSelected.itemsize)
progress=0.0
sampleNumber=0
receivedHeader=0
inputNumberOfColumns=0
inputLabels=list()
#-------------------------------------------------------------------------------------------------
numberOfSamplesInput=getNumberOfLines(filename)-2
print(" Input file has ",numberOfSamplesInput," training samples\n")
#-------------------------------------------------------------------------------------------------
numberOfSamples = numberOfSamplesInput
numberOfSamplesLimit=int(numberOfSamples*memPercentage)
#-------------------------------------------------------------------------------------------------
if (memPercentage==0.0):
print("readGroundTruthFile was asked to occupy 0 memory so this probably means we just want one record")
numberOfSamplesLimit=2
if (memPercentage>1.0):
print("Memory Limit will be interpreted as a raw value..")
numberOfSamplesLimit=int(memPercentage)
#-------------------------------------------------------------------------------------------------
thisInput = array.array('f')
#---------------------------------
fi = open(filename, "r")
readerIn = csv.reader( fi , delimiter=csvDelimiter, skipinitialspace=True)
for rowIn in readerIn:
#------------------------------------------------------
if (not receivedHeader): #use header to get labels
#------------------------------------------------------
inputNumberOfColumns=len(rowIn)
#Make sure CSV files that end with delimiter are correctly handled..
if (inputNumberOfColumns>0):
if (rowIn[inputNumberOfColumns-1]==''):
inputNumberOfColumns=inputNumberOfColumns-1
#-----------------------------------------------------
inputLabels = list(rowIn[i] for i in range(0,inputNumberOfColumns) )
print("Number of Input elements : ",len(inputLabels))
#------------------------------------------------------
if (memPercentage==0):
print("Will only return labels\n")
return {'labels':inputLabels};
#i=0
#print("class Input(Enum):")
#for label in inputLabels:
# print(" ",label," = ",i," #",int(i/3))
# print(" ",label,"=",int(i/3))
# i=i+1
#---------------------------------
# Allocate Lists
#---------------------------------
for i in range(inputNumberOfColumns):
thisInput.append(0.0)
#---------------------------------
#---------------------------------
# Allocate Numpy Arrays
#---------------------------------
inputSize=0
startCompressed=0
inputSize=inputSize+inputNumberOfColumns
startCompressed=inputNumberOfColumns
npInputBytesize=0+numberOfSamplesLimit * inputSize * dtypeSelectedByteSize
print(" Input file on disk has a shape of [",numberOfSamples,",",inputSize,"]")
print(" Input we will read has a shape of [",numberOfSamplesLimit,",",inputSize,"]")
print(" Input will occupy ",convert_bytes(npInputBytesize)," of RAM\n")
npInput = np.full([numberOfSamplesLimit,inputSize],fill_value=0,dtype=dtypeSelected,order='C')
#----------------------------------------------------------------------------------------------------------
receivedHeader=1
#sys.exit(0)
else:
#-------------------------------------------
# First convert our string INPUT to floats
#-------------------------------------------
for i in range(inputNumberOfColumns):
thisInput[i]=float(rowIn[i])
#-------------------------------------------
for num in range(0,inputNumberOfColumns):
npInput[sampleNumber,num]=float(thisInput[num]);
#-------------------------------------------
sampleNumber=sampleNumber+1
if (numberOfSamples>0):
progress=sampleNumber/numberOfSamplesLimit
if (sampleNumber%1000==0) :
progressString = "%0.2f"%float(100*progress)
print("\rReading from disk (",sampleNumber,") - ",progressString," % \r", end="", flush=True)
if (numberOfSamplesLimit<=sampleNumber):
print("\rStopping reading file to obey memory limit given by parameter --mem ",memPercentage,"\n")
break
#-------------------------------------------
fi.close()
del readerIn
gc.collect()
print("\n read, Samples: ",sampleNumber,", was expecting ",numberOfSamples," samples\n")
print(npInput.shape)
totalNumberOfBytes=npInput.nbytes;
totalNumberOfGigaBytes=totalNumberOfBytes/1073741824;
print("Size Occupied by data = ",totalNumberOfGigaBytes," GB \n")
end = time.time()
print("Time elapsed : ",(end-start)/60," mins")
#---------------------------------------------------------------------
if (groupOutput==0):
#New better dictionary
output = dict()
for i in range(0,len(inputLabels)):
lowerCaseName = inputLabels[i].lower()
#print("Joint ",lowerCaseName)
output[lowerCaseName]=list()
for frameID in range(0,len(npInput)):
output[lowerCaseName].append(npInput[frameID][i])
return output
else:
#This is the old dictionary way (better for tensorflow training)
return {'label':inputLabels, 'body':npInput };
#---------------------------------------------------------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------------------------------------------------------
#Main() program
#-------------------------------------
averageErrorDistances=list()
averageMotionEstimationDistancesBetweenFrames=list()
#These are labeling infos
#Used to correctly pack the results
#-------------------------
subject="S?"
action="Unknown"
subaction="?"
camera="?"
actionLabel="?"
addedPixelNoise=0
#These are configuration parameters
#-------------------------------------------------------------------
everyNFrames=0 # Run on every frame of the CSV files by default
drawPlot=0 # Don't Draw plot by default
doProcrustes=1 # Do procrustes analysis
procrustesScale=1 # Let procrustes also scale the point clouds
ground="h36.csv" # A default h36.csv filename
groundDelim=',' # By default the h36.csv has a ',' delimiter
output="out.csv" # A default output filename
outputDelim=';' # Damien's output has ';' delimiters, mine has ','
ourScaleX=1.0
ourScaleY=1.0
ourScaleZ=1.0
#Modifiers on comparisons..
#------------------------------------------------------------------------------------------------
if (len(sys.argv)>1):
#print('Argument List:', str(sys.argv))
for i in range(0, len(sys.argv)):
if (sys.argv[i]=="--ourscale"):
ourScaleX=float(sys.argv[i+1])
ourScaleY=float(sys.argv[i+2])
ourScaleZ=float(sys.argv[i+3])
if (sys.argv[i]=="--draw"):
drawPlot=1
if (sys.argv[i]=="--from"):
output=sys.argv[i+1]
outputDelim=sys.argv[i+2]
if (sys.argv[i]=="--ground"):
ground=sys.argv[i+1]
groundDelim=sys.argv[i+2]
if (sys.argv[i]=="--noprocrustes"):
doProcrustes=0
if (sys.argv[i]=="--every"):
everyNFrames=int(sys.argv[i+1])
if (sys.argv[i]=="--info"):
subject=sys.argv[i+1]
action=sys.argv[i+2]
camera=sys.argv[i+3]
addedPixelNoise=float(sys.argv[i+4])
print("\n Infos Set ",sys.argv[i+1])
actionLabel=action
s=actionLabel.split("-")
subaction=s[1]
action=s[0]
action=action.replace('/','')
subaction=subaction.replace('/','')
actionLabel=actionLabel.replace('/','')
camera=camera.replace('/','')
print("Subject ",subject," Action ",action," Subaction ",subaction," Camera ",camera)
#------------------------------------------------------------------------------------------------
if (drawPlot):
print("Using matplotlib:",matplotlib.__version__)
# === Plot and animate ===
fig = plt.figure()
fig.set_size_inches(19.2, 10.8, forward=True)
ax = fig.add_subplot(2, 2, 1, projection='3d')
ax2 = fig.add_subplot(2, 2, 2)
ax3 = fig.add_subplot(2, 2, 3)
ax4 = fig.add_subplot(2, 2, 4)
fig.subplots_adjust(left=0.05, bottom=0.05, right=0.95, top=0.95)
ax.set_xlabel('X Axis')
ax.set_ylabel('Y Axis')
ax.set_zlabel('Z Axis')
ax.view_init(90, 90)
out=readCSVFile(output,1.0,outputDelim,0,0)
ground=readCSVFile(ground,1.0,groundDelim,0,0)
print("Output number of keys : %u " % len(out.keys()) )
print("GroundTruth number of keys : %u " % len(ground.keys()) )
if ( len(out.keys()) != len(ground.keys()) ):
print(bcolors.WARNING,"Inconsistent keys",bcolors.ENDC)
#sys.exit(0)
totalError=0.0
totalSamples=0
numberOfJointsToCompare = len(JOINTS_TO_COMPARE)
numberOfFrames = len(out['3dx_nose'])
#-----------------------
alljointDistances=list()
#-----------------------
jointDistance=list()
for jointID in range(0,numberOfJointsToCompare):
jointDistance.append(list())
#-----------------------
#------------------------------------------------------------------------------------------------
if (len(sys.argv)>1):
#print('Argument List:', str(sys.argv))
for i in range(0, len(sys.argv)):
if (sys.argv[i]=="--frames"):
numberOfFrames=int(sys.argv[i+1])
#------------------------------------------------------------------------------------------------
#We want to loop over every frame that we loaded..
for frameID in range(0,numberOfFrames):
#In many papers they run the comparison ever N frames
#So depending on this setting you can actually execute
#the comparison computation everyNFrames
#-----------------------------------------------------
if ( (everyNFrames==0) or (frameID%everyNFrames==0) ):
executeComputations=1
else:
executeComputations=0
#-----------------------------------------------------
if (executeComputations==1):
ourPointCloud=list()
h36PointCloud=list()
xlineStart=list()
xlineEnd=list()
ylineStart=list()
ylineEnd=list()
zlineStart=list()
zlineEnd=list()
#Align midhip to make sure we start from a sane position
offsetX=(ourScaleX*out['3dx_midhip'][frameID])-ground['3dx_midhip'][frameID]
offsetY=(ourScaleY*out['3dy_midhip'][frameID])-ground['3dy_midhip'][frameID]
offsetZ=(ourScaleZ*out['3dz_midhip'][frameID])-ground['3dz_midhip'][frameID]
for jointIDUnresolved in range(0,numberOfJointsToCompare):
#------------------------------------------------------------------
#Our Joints to compare list has a different indexing
jointID = findJointID(JOINTS_TO_COMPARE[jointIDUnresolved],JOINT_LABELS)
if (jointID==-1):
print(bcolors.WARNING,"Failed to resolve joint: ",JOINTS_TO_COMPARE[jointIDUnresolved],bcolors.ENDC)
sys.exit(0)
#-----------------------------------------------------------
jointKey = JOINT_LABELS[jointID].lower()
parentKey = JOINT_PARENT_LABELS[jointID].lower()
#print("Frame %u / Key %s ( prev %s ) "% (frameID,jointKey,parentKey) )
#------------------------------------------------------------------
#We select the joint and push it to the point cloud for this particular frame
xM=(ourScaleX*out['3dx_%s' % jointKey][frameID])-offsetX
yM=(ourScaleY*out['3dy_%s' % jointKey][frameID])-offsetY
zM=(ourScaleZ*out['3dz_%s' % jointKey][frameID])-offsetZ
ourPointCloud.append([xM,yM,zM])
#These lineStarts/lineEnds and parentKeys help draw lines connecting
#the dots in skeletons
xParentM=(ourScaleX*out['3dx_%s' % parentKey][frameID])-offsetX
yParentM=(ourScaleY*out['3dy_%s' % parentKey][frameID])-offsetY
zParentM=(ourScaleZ*out['3dz_%s' % parentKey][frameID])-offsetZ
xlineStart.append(xM)
ylineStart.append(yM)
zlineStart.append(zM)
xlineEnd.append(xParentM)
ylineEnd.append(yParentM)
zlineEnd.append(zParentM)
#------------------------------------------------------------------
#We select the h36 joint and push it to the point cloud for this particular frame
xH=ground['3dx_%s' % jointKey][frameID]
yH=ground['3dy_%s' % jointKey][frameID]
zH=ground['3dz_%s' % jointKey][frameID]
h36PointCloud.append([xH,yH,zH])
#These lineStarts/lineEnds and parentKeys help draw lines connecting
#the dots in skeletons
xParentH=ground['3dx_%s' % parentKey][frameID]
yParentH=ground['3dy_%s' % parentKey][frameID]
zParentH=ground['3dz_%s' % parentKey][frameID]
xlineStart.append(xH)
ylineStart.append(yH)
zlineStart.append(zH)
xlineEnd.append(xParentH)
ylineEnd.append(yParentH)
zlineEnd.append(zParentH)
#------------------------------------------------------------------
#------------------------------------------------------------------
#We package our lists in numpy to be able to easily manipulate them
#------------------------------------------------------------------
np_h36PointCloud = np.asarray(h36PointCloud,dtype=np.float32)
np_ourPointCloud = np.asarray(ourPointCloud,dtype=np.float32)
#This is the main comparison after using procrustes and transforming the pointcloud
#to align it or when just doing plain old euclidean distance
#--------------------------------------------------------------------------------
if (doProcrustes):
d, Z, T, b, c = compute_similarity_transform(np_h36PointCloud,np_ourPointCloud,compute_optimal_scale=procrustesScale)
#disparity=np.sqrt(d) #d: squared error after transformation
#print("compute_similarity_transform : ",disparity)
#Our point cloud is brought to the same translation and rotation as h36 point cloud
np_ourPointCloud = (b*np_ourPointCloud.dot(T))+c
disparity = pointListsReturnAvgDistance(np_ourPointCloud,np_h36PointCloud)
else:
disparity = pointListsReturnAvgDistance(np_ourPointCloud,np_h36PointCloud)
#--------------------------------------------------------------------------------
#We want to calculate Mean Per Joint Position Error (MPJPE)
#to do so we have to calculate the position error of each of the joints in our point cloud
#sum it up and then divide it through the number of samples
for jID in range(0,numberOfJointsToCompare):
#------------------------------------------------------------------
#We use the np_ourPointCloud and np_h36PointCloud so that if procrustes analysis is enabled it will be used..
perJointDisparity=get3DDistance(
np_ourPointCloud[jID][0],np_ourPointCloud[jID][1],np_ourPointCloud[jID][2],
np_h36PointCloud[jID][0],np_h36PointCloud[jID][1],np_h36PointCloud[jID][2]
)
totalError+=perJointDisparity
totalSamples+=1
#We also keep every sample on a list to do an analysis in the end
alljointDistances.append(perJointDisparity)
jointDistance[jID].append(perJointDisparity)
#------------------------------------------------------------------
print("Frame %u / Disparity %f " % (frameID,disparity))
averageErrorDistances.append(disparity)
#Draw the plot that summarizes what is happening if --draw is given
if (drawPlot):
drawAfterEndOfComparison(
JOINTS_TO_COMPARE,
JOINT_LABELS,
JOINT_PARENTS,
JOINT_PARENT_LABELS,
plt,ax,ax2,ax3,ax4,
xlineStart,xlineEnd,
ylineStart,ylineEnd,
zlineStart,zlineEnd,
np_ourPointCloud,
np_h36PointCloud,
frameID,
numberOfFrames,
everyNFrames,
disparity,
averageErrorDistances,
averageMotionEstimationDistancesBetweenFrames
)
#Since this always appends results be sure to remove the gnuplot.raw
#file before starting a new session..
appendRAWResultsForGNUplot("gnuplot.raw",alljointDistances)
#Final results we want is Mean Per Joint Position Error (MPJPE) so we rely on the perJointDisparities etc..
#The result unit depends on the input to the script but it should be in millimeters for MocapNET output
#to change units you can use the --ourscale X Y Z commandline parameter, however millimeters of accuracy
#is the norm for 3D pose estimation work..
print("\nMean Per Joint Error for ",totalSamples," samples is ",totalError/totalSamples)
median = np.median(alljointDistances)
mean = np.mean(alljointDistances)
average = np.average(alljointDistances)
std = np.std(alljointDistances)
var = np.var(alljointDistances)
print("Mean is ",mean," Std is ",std," Var is ",var)
writeHeader=0
if not os.path.exists("results.csv"):
writeHeader=1
writeCSVFileResults(
JOINTS_TO_COMPARE,
"results.csv",
writeHeader,
alljointDistances,
jointDistance,
numberOfJointsToCompare,
subject,
action,
subaction,
camera,
actionLabel,
addedPixelNoise
)
if (drawPlot):
os.system("ffmpeg -framerate 25 -i p%05d.png -s 1920x1080 -y -r 30 -pix_fmt yuv420p -threads 8 lastcomp.mp4 && rm ./p*.png") #
sys.exit(0)
@@ -0,0 +1,233 @@
#!/usr/bin/env python3
#Written by Ammar Qammaz a.k.a AmmarkoV - 2020
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from mpl_toolkits.mplot3d import axes3d, Axes3D
print("Using matplotlib:",matplotlib.__version__)
def pointListReturnXYZListForScatterPlot(A):
numberOfPoints=A.shape[0]
xs=list()
ys=list()
zs=list()
for i in range(0,numberOfPoints):
xs.append(A[i][0])
ys.append(A[i][1])
zs.append(A[i][2])
return xs,ys,zs
def setupDrawing(fig,ax,ax2,ax3,ax4):
# === Plot and animate ===
fig = plt.figure()
fig.set_size_inches(19.2, 10.8, forward=True)
ax = fig.add_subplot(2, 2, 1, projection='3d')
ax2 = fig.add_subplot(2, 2, 2)
ax3 = fig.add_subplot(2, 2, 3)
ax4 = fig.add_subplot(2, 2, 4)
fig.subplots_adjust(left=0.05, bottom=0.05, right=0.95, top=0.95)
ax.set_xlabel('X Axis')
ax.set_ylabel('Y Axis')
ax.set_zlabel('Z Axis')
ax.view_init(90, 90)
def get3DDistance(jX,jY,jZ,pX,pY,pZ):
return np.sqrt( ((jX-pX)*(jX-pX)) + ((jY-pY)*(jY-pY)) + ((jZ-pZ)*(jZ-pZ)) )
def findJointID(jointName,labels):
for i in range(0,len(labels)):
if (jointName==labels[i]):
return i
print("Cannot find joint ",jointName," !")
return -1
#-----------------------------------------------------------------------------------------------------------------------
def drawLimbDimensions(JOINTS_TO_COMPARE,JOINT_LABELS,JOINT_PARENTS,JOINT_PARENT_LABELS,h36mPoints,ourPoints,numberOfJoints,ax2):
labels = list()
h36m_distances = list()
mnet_distances = list()
for jointID in range(0,len(JOINTS_TO_COMPARE)):
#---------------------------------------
jointIDGlobal = findJointID(JOINTS_TO_COMPARE[jointID],JOINT_LABELS)
jointParentID=findJointID(JOINT_PARENT_LABELS[JOINT_PARENTS[jointIDGlobal]],JOINTS_TO_COMPARE)
#---------------------------------------
if (jointParentID!=-1) and (jointIDGlobal!=-1):
labels.append(JOINT_LABELS[jointIDGlobal])
jX=h36mPoints[jointID][0]
jY=h36mPoints[jointID][1]
jZ=h36mPoints[jointID][2]
pX=h36mPoints[jointParentID][0]
pY=h36mPoints[jointParentID][1]
pZ=h36mPoints[jointParentID][2]
distances=get3DDistance(jX,jY,jZ,pX,pY,pZ)
h36m_distances.append(distances)
#---------------------------------------
jX=ourPoints[jointID][0]
jY=ourPoints[jointID][1]
jZ=ourPoints[jointID][2]
pX=ourPoints[jointParentID][0]
pY=ourPoints[jointParentID][1]
pZ=ourPoints[jointParentID][2]
distances=get3DDistance(jX,jY,jZ,pX,pY,pZ)
mnet_distances.append(distances)
#---------------------------------------
#-------------------------------------------------
x = np.arange(len(labels)) # the label locations
width = 0.35 # the width of the bars
rects1 = ax2.bar(x - width/2, h36m_distances, width, label='H36M')
rects2 = ax2.bar(x + width/2, mnet_distances, width, label='Our Method')
# Add some text for labels, title and custom x-axis tick labels, etc.
ax2.set_ylim(auto=False,bottom=0,top=800)
ax2.set_ylabel('Limb dimensions in millimeters')
ax2.set_title('Comparison of H36M limb dimensions')
ax2.set_xticks(x)
ax2.set_xticklabels(labels, rotation=45, rotation_mode="anchor")
ax2.legend()
#autolabel(rects1)
#autolabel(rects2)
#-----------------------------------------------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------------------------------------------
def drawLimbError(JOINTS_TO_COMPARE,JOINT_LABELS,JOINT_PARENTS,JOINT_PARENT_LABELS,h36mPoints,ourPoints,numberOfJoints,ax3):
labels = list()
error_distances = list()
for jointID in range(0,len(JOINTS_TO_COMPARE)):
#---------------------------------------
jointIDGlobal = findJointID(JOINTS_TO_COMPARE[jointID],JOINT_LABELS)
#---------------------------------------
if (jointIDGlobal!=-1):
labels.append(JOINT_LABELS[jointIDGlobal])
jX=h36mPoints[jointID][0]
jY=h36mPoints[jointID][1]
jZ=h36mPoints[jointID][2]
pX=ourPoints[jointID][0]
pY=ourPoints[jointID][1]
pZ=ourPoints[jointID][2]
distances=get3DDistance(jX,jY,jZ,pX,pY,pZ)
error_distances.append(distances)
#---------------------------------------
#-------------------------------------------------
x = np.arange(len(labels)) # the label locations
width = 0.35 # the width of the bars
rects1 = ax3.bar(x - width/2, error_distances, width, label='Error in millimeters')
# Add some text for labels, title and custom x-axis tick labels, etc.
ax3.set_ylim(auto=False,bottom=0,top=250)
ax3.set_ylabel('Error in millimeters')
ax3.set_title('Comparison of 3D estimation error')
ax3.set_xticks(x)
ax3.set_xticklabels(labels, rotation=45, rotation_mode="anchor")
ax3.legend()
#-----------------------------------------------------------------------------------------------------------------------
#-----------------------------------------------------------------------------------------------------------------------
def drawFrameError(averageErrorDistances,averageMotionEstimationDistancesBetweenFrames,ax4):
#minimum=np.min(averageErrorDistances)
#ax4.plot((0,len(averageErrorDistances)), (minimum,minimum),label='Minimum Error')
#maximum=np.max(averageErrorDistances)
#ax4.plot((0,len(averageErrorDistances)), (maximum,maximum),label='Maximum Error')
#median=np.median(averageErrorDistances)
#ax4.plot((0,len(averageErrorDistances)), (median,median),label='Median Error (%0.2f mm)'% median)
average=np.average(averageErrorDistances)
ax4.plot((0,len(averageErrorDistances)), (average,average),label='Average of average errors (%0.2f mm)' % average)
ax4.plot(averageErrorDistances, label='Our method average error in millimeters')
ax4.plot(averageMotionEstimationDistancesBetweenFrames, label='Distance of average joint from previous frame')
#ax4.set_ylim(auto=False,bottom=0,top=250)
ax4.set_xlabel('Experiment frame number')
ax4.set_ylabel('Millimeters')
ax4.set_title('Average 3D estimation error per frame')
#ax4.set_xticklabels(labels, rotation=45, rotation_mode="anchor")
ax4.legend()
#-----------------------------------------------------------------------------------------------------------------------
def drawAfterEndOfComparison(
JOINTS_TO_COMPARE,
JOINT_LABELS,
JOINT_PARENTS,
JOINT_PARENT_LABELS,
plt,ax,ax2,ax3,ax4,
xlineStart,xlineEnd,
ylineStart,ylineEnd,
zlineStart,zlineEnd,
ourPointCloud,
h36PointCloud,
frameID,
numberOfFrames,
everyNFrames,
disparity,
averageErrorDistances,
averageMotionEstimationDistancesBetweenFrames
):
plt.cla()
ax.cla()
ax2.cla()
ax3.cla()
ax4.cla()
#Print Skeletons and their connected lines..
#for i in range(0,len(xlineStart)):
# ax.plot([xlineStart[i],xlineEnd[i]],[ylineStart[i],ylineEnd[i]],zs=[zlineStart[i],zlineEnd[i]])
#--------------------------------------------
xs, ys, zs = pointListReturnXYZListForScatterPlot(ourPointCloud)
ax.scatter(xs, ys, zs)
xs, ys, zs = pointListReturnXYZListForScatterPlot(h36PointCloud)
ax.scatter(xs, ys, zs)
#Secondary plots for limb lengths etc..
numberOfJoints = len(ourPointCloud)
drawLimbDimensions(JOINTS_TO_COMPARE,JOINT_LABELS,JOINT_PARENTS,JOINT_PARENT_LABELS,h36PointCloud,ourPointCloud,numberOfJoints,ax2)
drawLimbError(JOINTS_TO_COMPARE,JOINT_LABELS,JOINT_PARENTS,JOINT_PARENT_LABELS,h36PointCloud,ourPointCloud,numberOfJoints,ax3)
drawFrameError(averageErrorDistances,averageMotionEstimationDistancesBetweenFrames,ax4)
#-------------------------
ax.text2D(0.05, 0.95, "Frame %u/%u - Increment %u - Procrustes Average Error %0.2f mm"%(frameID,numberOfFrames,everyNFrames,disparity) , transform=ax.transAxes)
#ax.text2D(0.05, 0.05, "RHand %0.2f mm / LHand %0.2f mm / RFoot %0.2f mm / LFoot %0.2f mm "%(currentDistances[4],currentDistances[7],currentDistances[11],currentDistances[14]) , transform=ax.transAxes)
#-------------------------
ax.set_xlim(auto=False,left=-600,right=300)
ax.set_ylim(auto=False,bottom=-1600,top=200)
ax.set_zlim(auto=False,bottom=2000,top=6000)
ax.set_xlabel('X Axis')
ax.set_ylabel('Y Axis')
ax.set_zlabel('Z Axis')
#-------------------------
plt.show(block=False)
plt.savefig('p%05u.png'%frameID)
#fig.canvas.draw()
plt.pause(0.001)
@@ -0,0 +1,110 @@
#!/usr/bin/env python3
#Written by Ammar Qammaz a.k.a AmmarkoV - 2020
import numpy as np
#-----------------------------------------------------------------------------------------------------------------------
def writeCSVFileResults(
JOINT_LABELS,
outputFile,
addHeader,
globalJointDistances,
perJointDistance,
numberOfJoints,
subject,
action,
subaction,
camera,
actionLabel,
addedPixelNoise
):
#Write header----------------
if (addHeader):
file = open(outputFile,"w")
file.write("Subject,")
file.write("Action,")
file.write("ActionLabel,")
file.write("Subaction,")
file.write("Camera,")
file.write("Noise,")
file.write("Global_Median,")
#file.write("Global_Mean,")
file.write("Global_Average,")
file.write("Global_Std,")
file.write("Global_Var,")
for jointID in range(0,numberOfJoints):
file.write("%s_Median,"%JOINT_LABELS[jointID])
#file.write("%s_Mean,"%JOINT_LABELS[jointID])
file.write("%s_Average,"%JOINT_LABELS[jointID])
file.write("%s_Std,"%JOINT_LABELS[jointID])
file.write("%s_Var"%JOINT_LABELS[jointID])
if (jointID!=numberOfJoints-1):
file.write(",")
else:
file.write("\n")
else:
file = open(outputFile,"a")
#----------------------------
median=np.median(globalJointDistances)
mean=np.mean(globalJointDistances)
average=np.average(globalJointDistances)
std=np.std(globalJointDistances)
var=np.var(globalJointDistances)
print("\nGlobal Median:",median," Average:",average," Std:",std,"Var:",var) #file.write(subject)
file.write(subject)
file.write(",")
file.write(action)
file.write(",")
file.write(actionLabel)
file.write(",")
file.write(subaction)
file.write(",")
file.write(camera)
file.write(",")
file.write(str(addedPixelNoise))
file.write(",")
file.write(str(median))
file.write(",")
#file.write(str(mean))
#file.write(",")
file.write(str(average))
file.write(",")
file.write(str(std))
file.write(",")
file.write(str(var))
file.write(",")
for jointID in range(0,numberOfJoints):
median=np.median(perJointDistance[jointID])
mean=np.mean(perJointDistance[jointID])
average=np.average(perJointDistance[jointID])
std=np.std(perJointDistance[jointID])
var=np.var(perJointDistance[jointID])
print("Joint ",JOINT_LABELS[jointID]," Median:",median," Mean:",mean," Average:",average," Std:",std,"Var:",var)
file.write(str(median))
file.write(",")
#file.write(str(mean))
#file.write(",")
file.write(str(average))
file.write(",")
file.write(str(std))
file.write(",")
file.write(str(var))
if (jointID!=numberOfJoints-1):
file.write(",")
else:
file.write("\n")
file.close()
#--------------------------------------------
#-----------------------------------------------------------------------------------------------------------------------
def appendRAWResultsForGNUplot(outputFile,globalJointDistances):
fileHandler = open(outputFile, "a")
for measurement in globalJointDistances:
fileHandler.write(str(measurement))
fileHandler.write("\n")
fileHandler.close()