示例#1
0
    def test_TRCFileAdapter(self):
        table = osim.TimeSeriesTableVec3(
            os.path.join(test_dir, 'futureOrientationInverseKinematics.trc'))
        assert table.getNumRows() == 1202
        assert table.getNumColumns() == 2

        table = osim.TimeSeriesTableVec3(
            os.path.join(test_dir, 'dataWithNaNsOfDifferentCases.trc'))
        assert table.getNumRows() == 5
        assert table.getNumColumns() == 14
示例#2
0
def computeMarkersReference(predictedSolution):
    model = createDoublePendulumModel()
    model.initSystem()
    states = predictedSolution.exportToStatesStorage()

    # Workaround for a bug in Storage::operator=().
    columnLabels = model.getStateVariableNames()
    columnLabels.insert(0, "time")
    states.setColumnLabels(columnLabels)

    statesTraj = osim.StatesTrajectory.createFromStatesStorage(model, states)

    markerTrajectories = osim.TimeSeriesTableVec3()
    markerTrajectories.setColumnLabels(["/markerset/m0", "/markerset/m1"])

    for state in statesTraj:
        model.realizePosition(state)
        m0 = model.getComponent("markerset/m0")
        m1 = model.getComponent("markerset/m1")
        markerTrajectories.appendRow(
            state.getTime(),
            osim.RowVectorVec3(
                [m0.getLocationInGround(state),
                 m1.getLocationInGround(state)]))

    # Assign a weight to each marker.
    markerWeights = osim.SetMarkerWeights()
    markerWeights.cloneAndAppend(osim.MarkerWeight("/markerset/m0", 1))
    markerWeights.cloneAndAppend(osim.MarkerWeight("/markerset/m1", 5))

    return osim.MarkersReference(markerTrajectories, markerWeights)
示例#3
0
 def test_TimeSeriesTableVec3(self):
     table = osim.TimeSeriesTableVec3()
     # Set columns labels.
     table.setColumnLabels(['0', '1', '2'])
     assert table.getColumnLabels() == ('0', '1', '2')
     # Append a row to the table.
     row = osim.RowVectorOfVec3(
         [osim.Vec3(1, 2, 3),
          osim.Vec3(4, 5, 6),
          osim.Vec3(7, 8, 9)])
     table.appendRow(0.1, row)
     assert table.getNumRows() == 1
     assert table.getNumColumns() == 3
     row0 = table.getRowAtIndex(0)
     assert (str(row0[0]) == str(row[0]) and str(row0[1]) == str(row[1])
             and str(row0[2]) == str(row[2]))
     # Append another row to the table.
     row = osim.RowVectorOfVec3(
         [osim.Vec3(2, 4, 6),
          osim.Vec3(8, 10, 12),
          osim.Vec3(14, 16, 18)])
     table.appendRow(0.2, row)
     assert table.getNumRows() == 2
     assert table.getNumColumns() == 3
     row1 = table.getRow(0.2)
     assert (str(row1[0]) == str(row[0]) and str(row1[1]) == str(row[1])
             and str(row1[2]) == str(row[2]))
     # Append another row to the table with a timestamp
     # less than the previous one. Exception expected.
     try:
         table.appendRow(0.15, row)
         assert False
     except RuntimeError:
         pass
示例#4
0
    def to_trc(self, filename):
        """
        Write a trc file from a Markers3dOsim
        Parameters
        ----------
        filename : string
            path of the file to write
        """
        filename = Path(filename)
        # Make sure the directory exists, otherwise create it
        if not filename.parents[0].is_dir():
            filename.parents[0].mkdir()

        # Make sure the metadata are set
        if not self.get_rate:
            raise ValueError(
                'get_rate is empty. Please fill with `your_variable.get_rate = 100.0` for example'
            )
        if not self.get_unit:
            raise ValueError(
                'get_unit is empty. Please fill with `your_variable.get_unit = "mm"` for example'
            )
        if not self.get_labels:
            raise ValueError(
                'get_labels is empty. Please fill with `your_variable.get_labels = ["M1", "M2"]` for example'
            )

        table = osim.TimeSeriesTableVec3()

        # set metadata
        table.setColumnLabels(self.get_labels)
        table.addTableMetaDataString('DataRate', str(self.get_rate))
        table.addTableMetaDataString('Units', self.get_unit)

        time_vector = np.arange(start=0,
                                stop=1 / self.get_rate * self.shape[2],
                                step=1 / self.get_rate)

        for iframe in range(self.shape[-1]):
            a = np.round(self.get_frame(iframe)[:-1, ...], decimals=4)
            row = osim.RowVectorOfVec3([
                osim.Vec3(a[0, i], a[1, i], a[2, i])
                for i in range(a.shape[-1])
            ])
            table.appendRow(time_vector[iframe], row)

        adapter = osim.TRCFileAdapter()
        adapter.write(table, str(filename))
def addVirtualMarkersStatic(staticTRC = None, outputTRC = 'static_withVirtualMarkers.trc'):
    
    # Convenience function for adding virtual markers to static trial.
    #
    # Input:    staticTRC - .trc filename for static trial to add markers to
    #           outputTRC - optional input for filename for output .trc file
    #
    # Hip joint centres are placed according to the method outlined by
    # Harrington et al. (2007), J Biomech, 40: 595-602. 
    #
    # Knee joint centres are placed at the mid point of the femoral epicondyle
    # markers.
    #
    # Pseudo ankle joint centres are placed at the mid points of the malleoli
    # markers.
    #
    # A marker at the mid point of the two metatarsal markers.
    #
    # Markers around the foot and ankle are projected to the floor to assist
    # in aligning the scaling parameters with the axis they are relevant for
    # scaling.
    #
    # Markers at the middle of the set of upper torso and pelvis markers used
    # to help scale torso and pelvis length.
    #
    # Note that the application of this is only relevant to the markerset used
    # with this data. Different labelling of markers will require adaptating
    # this function to make it work.
    
    #Check for input
    if staticTRC is None:
        raise ValueError('Input of staticTRC is required')
    
    #Use the Vec3 TimeSeriesTable to read the Vec3 type data file.
    staticTable = osim.TimeSeriesTableVec3(staticTRC)
    #Convert to more easily readable object for easier manipulation
    #Get number of column labels and data rows
    nLabels = staticTable.getNumColumns()
    nRows = staticTable.getNumRows()
    #Pre-allocate numpy array based on data size
    dataArray = np.zeros((nRows,nLabels*3))
    #Loop through labels and rows and get data
    for iLabel in range(0,nLabels):
        #Get current column label
        currLabel = staticTable.getColumnLabel(iLabel)
        for iRow in range(0,nRows):
            dataArray[iRow,iLabel*3] = staticTable.getDependentColumn(currLabel).getElt(0,iRow).get(0)
            dataArray[iRow,iLabel*3+1] = staticTable.getDependentColumn(currLabel).getElt(0,iRow).get(1)
            dataArray[iRow,iLabel*3+2] = staticTable.getDependentColumn(currLabel).getElt(0,iRow).get(2)
    
    #Convert numpy array to pandas dataframe and add column labels
    #Get column labels
    colLabels = list()
    for iLabel in range(0,nLabels):
        colLabels.append(staticTable.getColumnLabel(iLabel)+'_x')
        colLabels.append(staticTable.getColumnLabel(iLabel)+'_y')
        colLabels.append(staticTable.getColumnLabel(iLabel)+'_z')
    #Convert to dataframe
    static_df = pd.DataFrame(data = dataArray,columns=colLabels)
    
    #Get the pelvis marker data
    #In this step we convert back to the traditional Vicon coordinate system
    #and millimetres. It was easier to do this than mess around with the hip 
    #joint centre calculations
    RASIS = np.zeros((nRows,3))
    RASIS[:,0] = static_df['RASI_z']*1000
    RASIS[:,1] = static_df['RASI_x']*1000
    RASIS[:,2] = static_df['RASI_y']*1000
    LASIS = np.zeros((nRows,3))
    LASIS[:,0] = static_df['LASI_z']*1000
    LASIS[:,1] = static_df['LASI_x']*1000
    LASIS[:,2] = static_df['LASI_y']*1000
    RPSIS = np.zeros((nRows,3))
    RPSIS[:,0] = static_df['RPSI_z']*1000
    RPSIS[:,1] = static_df['RPSI_x']*1000
    RPSIS[:,2] = static_df['RPSI_y']*1000
    LPSIS = np.zeros((nRows,3))
    LPSIS[:,0] = static_df['LPSI_z']*1000
    LPSIS[:,1] = static_df['LPSI_x']*1000
    LPSIS[:,2] = static_df['LPSI_y']*1000
    
    #Calculate hip joint centre at each time step
    #Pre-allocate size
    RHJC = np.zeros((nRows,3))
    LHJC = np.zeros((nRows,3))
    #Loop through sample points
    for t in range(0,nRows):
        #Right handed pelvis reference system definition
        SACRUM = (RPSIS[t,:] + LPSIS[t,:]) / 2
        
        #Global Pelvis Center position
        OP = (LASIS[t,:] + RASIS[t,:]) / 2  
        PROVV = (RASIS[t,:] - SACRUM) / np.linalg.norm(RASIS[t,:] - SACRUM,2)
        IB = (RASIS[t,:] - LASIS[t,:]) / np.linalg.norm(RASIS[t,:] - LASIS[t,:],2)
        KB = np.cross(IB,PROVV)                               
        KB = KB / np.linalg.norm(KB,2)
        JB = np.cross(KB,IB)
        JB = JB / np.linalg.norm(JB,2)
        OB = OP
        
        #rotation+ traslation in homogeneous coordinates (4x4)
        addPelvis = np.array([0,0,0,1])
        pelvis = np.hstack((IB.reshape(3,1),
                            JB.reshape(3,1),
                            KB.reshape(3,1),
                            OB.reshape(3,1)))
        pelvis = np.vstack((pelvis,addPelvis))
        
        #Trasformation into pelvis coordinate system (CS)
        OPB = np.linalg.inv(pelvis) @ np.vstack((OB.reshape(3,1),np.array([1])))    
        PW = np.linalg.norm(RASIS[t,:] - LASIS[t,:])
        PD = np.linalg.norm(SACRUM - OP)
        
        #Harrington formulae (starting from pelvis center)
        diff_ap = -0.24 * PD - 9.9
        diff_v = -0.30 * PW - 10.9
        diff_ml = 0.33 * PW + 7.3    
        
        #vector that must be subtract to OP to obtain hjc in pelvis CS
        vett_diff_pelvis_sx = np.array([-diff_ml,diff_ap,diff_v,1])
        vett_diff_pelvis_dx = np.array([diff_ml,diff_ap,diff_v,1])
        
        #hjc in pelvis CS (4x4)
        rhjc_pelvis = OPB[:,0] + vett_diff_pelvis_dx  
        lhjc_pelvis = OPB[:,0] + vett_diff_pelvis_sx 
        
        #Transformation Local to Global
        RHJC[t,:] = pelvis[0:3,0:3] @ rhjc_pelvis[0:3] + OB
        LHJC[t,:] = pelvis[0:3,0:3] @ lhjc_pelvis[0:3] + OB

    #Add to data frame
    #Convert back to appropriate units and coordinate system here too
    static_df['RHJC_x'] = RHJC[:,1] / 1000
    static_df['RHJC_y'] = RHJC[:,2] / 1000
    static_df['RHJC_z'] = RHJC[:,0] / 1000
    static_df['LHJC_x'] = LHJC[:,1] / 1000
    static_df['LHJC_y'] = LHJC[:,2] / 1000
    static_df['LHJC_z'] = LHJC[:,0] / 1000
    
    #Create mid point markers
    
    #Mid epicondyles
    static_df['RKJC_x'] = (static_df['RLFC_x'] + static_df['RMFC_x']) / 2
    static_df['RKJC_y'] = (static_df['RLFC_y'] + static_df['RMFC_y']) / 2
    static_df['RKJC_z'] = (static_df['RLFC_z'] + static_df['RMFC_z']) / 2
    static_df['LKJC_x'] = (static_df['LLFC_x'] + static_df['LMFC_x']) / 2
    static_df['LKJC_y'] = (static_df['LLFC_y'] + static_df['LMFC_y']) / 2
    static_df['LKJC_z'] = (static_df['LLFC_z'] + static_df['LMFC_z']) / 2
    
    #Mid malleoli
    static_df['RAJC_x'] = (static_df['RLMAL_x'] + static_df['RMMAL_x']) / 2
    static_df['RAJC_y'] = (static_df['RLMAL_y'] + static_df['RMMAL_y']) / 2
    static_df['RAJC_z'] = (static_df['RLMAL_z'] + static_df['RMMAL_z']) / 2
    static_df['LAJC_x'] = (static_df['LLMAL_x'] + static_df['LMMAL_x']) / 2
    static_df['LAJC_y'] = (static_df['LLMAL_y'] + static_df['LMMAL_y']) / 2
    static_df['LAJC_z'] = (static_df['LLMAL_z'] + static_df['LMMAL_z']) / 2
    
    #Mid metatarsal
    static_df['RMT3_x'] = (static_df['RMT1_x'] + static_df['RMT5_x']) / 2
    static_df['RMT3_y'] = (static_df['RMT1_y'] + static_df['RMT5_y']) / 2
    static_df['RMT3_z'] = (static_df['RMT1_z'] + static_df['RMT5_z']) / 2
    static_df['LMT3_x'] = (static_df['LMT1_x'] + static_df['LMT5_x']) / 2
    static_df['LMT3_y'] = (static_df['LMT1_y'] + static_df['LMT5_y']) / 2
    static_df['LMT3_z'] = (static_df['LMT1_z'] + static_df['LMT5_z']) / 2
    
    #Create projections of foot and ankle markers to floor (i.e. y = 0)
    
    #Heel markers
    static_df['fRCAL_x'] = static_df['RCAL_x']
    static_df['fRCAL_y'] = np.zeros((len(static_df),1))
    static_df['fRCAL_z'] = static_df['RCAL_z']
    static_df['fLCAL_x'] = static_df['LCAL_x']
    static_df['fLCAL_y'] = np.zeros((len(static_df),1))
    static_df['fLCAL_z'] = static_df['LCAL_z']
    
    #Ankle joint centres
    static_df['fRAJC_x'] = static_df['RAJC_x']
    static_df['fRAJC_y'] = np.zeros((len(static_df),1))
    static_df['fRAJC_z'] = static_df['RAJC_z']
    static_df['fLAJC_x'] = static_df['LAJC_x']
    static_df['fLAJC_y'] = np.zeros((len(static_df),1))
    static_df['fLAJC_z'] = static_df['LAJC_z']
    
    #Toe markers
    static_df['fRMT1_x'] = static_df['RMT1_x']
    static_df['fRMT1_y'] = np.zeros((len(static_df),1))
    static_df['fRMT1_z'] = static_df['RMT1_z']
    static_df['fLMT1_x'] = static_df['LMT1_x']
    static_df['fLMT1_y'] = np.zeros((len(static_df),1))
    static_df['fLMT1_z'] = static_df['LMT1_z']
    static_df['fRMT5_x'] = static_df['RMT5_x']
    static_df['fRMT5_y'] = np.zeros((len(static_df),1))
    static_df['fRMT5_z'] = static_df['RMT5_z']
    static_df['fLMT5_x'] = static_df['LMT5_x']
    static_df['fLMT5_y'] = np.zeros((len(static_df),1))
    static_df['fLMT5_z'] = static_df['LMT5_z']
    static_df['fRMT3_x'] = static_df['RMT3_x']
    static_df['fRMT3_y'] = np.zeros((len(static_df),1))
    static_df['fRMT3_z'] = static_df['RMT3_z']
    static_df['fLMT3_x'] = static_df['LMT3_x']
    static_df['fLMT3_y'] = np.zeros((len(static_df),1))
    static_df['fLMT3_z'] = static_df['LMT3_z']
    
    #Extra mid points for torso and pelvis markers
    
    #Mid torso
    static_df['MTOR_x'] = (((static_df['RACR_x'] + static_df['LACR_x']) / 2) + ((static_df['C7_x'] + static_df['CLAV_x']) / 2)) / 2
    static_df['MTOR_y'] = (((static_df['RACR_y'] + static_df['LACR_y']) / 2) + ((static_df['C7_y'] + static_df['CLAV_y']) / 2)) / 2
    static_df['MTOR_z'] = (((static_df['RACR_z'] + static_df['LACR_z']) / 2) + ((static_df['C7_z'] + static_df['CLAV_z']) / 2)) / 2
    
    #Mid pelvis
    static_df['MPEL_x'] = (((static_df['RASI_x'] + static_df['LASI_x']) / 2) + ((static_df['RPSI_x'] + static_df['LPSI_x']) / 2)) / 2
    static_df['MPEL_y'] = (((static_df['RASI_y'] + static_df['LASI_y']) / 2) + ((static_df['RPSI_y'] + static_df['LPSI_y']) / 2)) / 2
    static_df['MPEL_z'] = (((static_df['RASI_z'] + static_df['LASI_z']) / 2) + ((static_df['RPSI_z'] + static_df['LPSI_z']) / 2)) / 2
    
    #Build the new time series Vec3 table
    
    #Get the time array
    time = staticTable.getIndependentColumn()
    
    #Set the label names
    #Get the original labels
    markerLabels = list(staticTable.getColumnLabels())
    #Append new marker labels
    markerLabels.append('RHJC')
    markerLabels.append('LHJC')
    markerLabels.append('RKJC')
    markerLabels.append('LKJC')
    markerLabels.append('RAJC')
    markerLabels.append('LAJC')
    markerLabels.append('RMT3')
    markerLabels.append('LMT3')
    markerLabels.append('fRCAL')
    markerLabels.append('fLCAL')
    markerLabels.append('fRAJC')
    markerLabels.append('fLAJC')
    markerLabels.append('fRMT1')
    markerLabels.append('fLMT1')
    markerLabels.append('fRMT5')
    markerLabels.append('fLMT5')
    markerLabels.append('fRMT3')
    markerLabels.append('fLMT3')
    markerLabels.append('MTOR')
    markerLabels.append('MPEL')
    
    #Initialise time series table of vec3 format
    newTable = osim.TimeSeriesTableVec3()
    
    #Set labels in table
    newLabels = osim.StdVectorString()
    for ii in range(0,len(markerLabels)):
        newLabels.append(markerLabels[ii])
    newTable.setColumnLabels(newLabels)
    
    #Build the time series table
    #Loop through rows and allocate data
    for iRow in range(0,nRows):
        #Create a blank opensim row vector
        row = osim.RowVectorVec3(len(markerLabels))
        #Create and fill row data
        for iCol in range(0,len(markerLabels)):
            #Identify the dataframe labels for the current marker label
            xLabel = markerLabels[iCol]+'_x'
            yLabel = markerLabels[iCol]+'_y'
            zLabel = markerLabels[iCol]+'_z'
            #Set data in row
            row.getElt(0,iCol).set(0,static_df[xLabel][iRow])
            row.getElt(0,iCol).set(1,static_df[yLabel][iRow])
            row.getElt(0,iCol).set(2,static_df[zLabel][iRow])
        #Append row to table
        newTable.appendRow(iRow,row)
        #Set time value
        newTable.setIndependentValueAtIndex(iRow,time[iRow])
            
    #Update the meta data in the new table based on the original
    
    #Get meta data keys
    metaKeys = list(staticTable.getTableMetaDataKeys())
    
    #Loop through keys and append to new table
    for ii in range(0,len(metaKeys)):
        #Get current meta data string
        currMeta = metaKeys[ii]
        #Append with relevant value
        newTable.addTableMetaDataString(currMeta,staticTable.getTableMetaDataAsString(currMeta))
        
    #Write the new table to file
    osim.TRCFileAdapter().write(newTable,outputTRC)
    
    #Print confirm message
    print('Virtual markers added to new .trc file: '+outputTRC)
def addVirtualMarkersDynamic(staticTRC = None, dynamicTRC = None,
                             outputTRC = 'dynamic_withVirtualMarkers.trc'):
    
    # Convenience function for adding virtual markers to dynamic trial.
    #
    # Input:    staticTRC - .trc filename for static trial with virtual markers added
    #           dynamicTRC - .trc filename for dynamic trial to add markers to
    #           outputTRC - optional input for filename for output .trc file
    #
    # Hip joint centres are placed according to the method outlined by
    # Harrington et al. (2007), J Biomech, 40: 595-602. 
    #
    # A 3d trilateration function from https://github.com/akshayb6/trilateration-in-3d
    # is used to create virtual markers at positions on the scaled model based 
    # on distances and positions of four other markers.
    #
    # Gaps in marker data come across as zeros in the .trc file, so any calculations
    # over where missing markers are present will be wrong. These areas should
    # not be used in the actual trial assessment without appropriate filling.
    
    #Check for input
    if staticTRC is None:
        raise ValueError('Input of staticTRC is required')
    if dynamicTRC is None:
        raise ValueError('Input of dynamicTRC is required')
    
    #Use the Vec3 TimeSeriesTable to read the Vec3 type data file.
    dynamicTable = osim.TimeSeriesTableVec3(dynamicTRC)
    
    #Convert to more easily readable object for easier manipulation
    #Get number of column labels and data rows
    nLabels = dynamicTable.getNumColumns()
    nRows = dynamicTable.getNumRows()
    #Pre-allocate numpy array based on data size
    dataArray = np.zeros((nRows,nLabels*3))
    #Loop through labels and rows and get data
    for iLabel in range(0,nLabels):
        #Get current column label
        currLabel = dynamicTable.getColumnLabel(iLabel)
        for iRow in range(0,nRows):
            dataArray[iRow,iLabel*3] = dynamicTable.getDependentColumn(currLabel).getElt(0,iRow).get(0)
            dataArray[iRow,iLabel*3+1] = dynamicTable.getDependentColumn(currLabel).getElt(0,iRow).get(1)
            dataArray[iRow,iLabel*3+2] = dynamicTable.getDependentColumn(currLabel).getElt(0,iRow).get(2)
    
    #Convert numpy array to pandas dataframe and add column labels
    #Get column labels
    colLabels = list()
    for iLabel in range(0,nLabels):
        colLabels.append(dynamicTable.getColumnLabel(iLabel)+'_x')
        colLabels.append(dynamicTable.getColumnLabel(iLabel)+'_y')
        colLabels.append(dynamicTable.getColumnLabel(iLabel)+'_z')
    #Convert to dataframe
    dynamic_df = pd.DataFrame(data = dataArray,columns=colLabels)
    
    #Get the pelvis marker data
    #In this step we convert back to the traditional Vicon coordinate system
    #and millimetres. It was easier to do this than mess around with the hip 
    #joint centre calculations
    RASIS = np.zeros((nRows,3))
    RASIS[:,0] = dynamic_df['RASI_z']*1000
    RASIS[:,1] = dynamic_df['RASI_x']*1000
    RASIS[:,2] = dynamic_df['RASI_y']*1000
    LASIS = np.zeros((nRows,3))
    LASIS[:,0] = dynamic_df['LASI_z']*1000
    LASIS[:,1] = dynamic_df['LASI_x']*1000
    LASIS[:,2] = dynamic_df['LASI_y']*1000
    RPSIS = np.zeros((nRows,3))
    RPSIS[:,0] = dynamic_df['RPSI_z']*1000
    RPSIS[:,1] = dynamic_df['RPSI_x']*1000
    RPSIS[:,2] = dynamic_df['RPSI_y']*1000
    LPSIS = np.zeros((nRows,3))
    LPSIS[:,0] = dynamic_df['LPSI_z']*1000
    LPSIS[:,1] = dynamic_df['LPSI_x']*1000
    LPSIS[:,2] = dynamic_df['LPSI_y']*1000
    
    #Calculate hip joint centre at each time step
    #Pre-allocate size
    RHJC = np.zeros((nRows,3))
    LHJC = np.zeros((nRows,3))
    #Loop through sample points
    for t in range(0,nRows):
        #Right handed pelvis reference system definition
        SACRUM = (RPSIS[t,:] + LPSIS[t,:]) / 2
        
        #Global Pelvis Center position
        OP = (LASIS[t,:] + RASIS[t,:]) / 2  
        PROVV = (RASIS[t,:] - SACRUM) / np.linalg.norm(RASIS[t,:] - SACRUM,2)
        IB = (RASIS[t,:] - LASIS[t,:]) / np.linalg.norm(RASIS[t,:] - LASIS[t,:],2)
        KB = np.cross(IB,PROVV)                               
        KB = KB / np.linalg.norm(KB,2)
        JB = np.cross(KB,IB)
        JB = JB / np.linalg.norm(JB,2)
        OB = OP
        
        #rotation+ traslation in homogeneous coordinates (4x4)
        addPelvis = np.array([0,0,0,1])
        pelvis = np.hstack((IB.reshape(3,1),
                            JB.reshape(3,1),
                            KB.reshape(3,1),
                            OB.reshape(3,1)))
        pelvis = np.vstack((pelvis,addPelvis))
        
        #Trasformation into pelvis coordinate system (CS)
        OPB = np.linalg.inv(pelvis) @ np.vstack((OB.reshape(3,1),np.array([1])))    
        PW = np.linalg.norm(RASIS[t,:] - LASIS[t,:])
        PD = np.linalg.norm(SACRUM - OP)
        
        #Harrington formulae (starting from pelvis center)
        diff_ap = -0.24 * PD - 9.9
        diff_v = -0.30 * PW - 10.9
        diff_ml = 0.33 * PW + 7.3    
        
        #vector that must be subtract to OP to obtain hjc in pelvis CS
        vett_diff_pelvis_sx = np.array([-diff_ml,diff_ap,diff_v,1])
        vett_diff_pelvis_dx = np.array([diff_ml,diff_ap,diff_v,1])
        
        #hjc in pelvis CS (4x4)
        rhjc_pelvis = OPB[:,0] + vett_diff_pelvis_dx  
        lhjc_pelvis = OPB[:,0] + vett_diff_pelvis_sx 
        
        #Transformation Local to Global
        RHJC[t,:] = pelvis[0:3,0:3] @ rhjc_pelvis[0:3] + OB
        LHJC[t,:] = pelvis[0:3,0:3] @ lhjc_pelvis[0:3] + OB

    #Add to data frame
    #Convert back to appropriate units and coordinate system here too
    dynamic_df['RHJC_x'] = RHJC[:,1] / 1000
    dynamic_df['RHJC_y'] = RHJC[:,2] / 1000
    dynamic_df['RHJC_z'] = RHJC[:,0] / 1000
    dynamic_df['LHJC_x'] = LHJC[:,1] / 1000
    dynamic_df['LHJC_y'] = LHJC[:,2] / 1000
    dynamic_df['LHJC_z'] = LHJC[:,0] / 1000
    
    #Create mid point markers
    
    #Mid torso
    dynamic_df['MTOR_x'] = (((dynamic_df['RACR_x'] + dynamic_df['LACR_x']) / 2) + ((dynamic_df['C7_x'] + dynamic_df['CLAV_x']) / 2)) / 2
    dynamic_df['MTOR_y'] = (((dynamic_df['RACR_y'] + dynamic_df['LACR_y']) / 2) + ((dynamic_df['C7_y'] + dynamic_df['CLAV_y']) / 2)) / 2
    dynamic_df['MTOR_z'] = (((dynamic_df['RACR_z'] + dynamic_df['LACR_z']) / 2) + ((dynamic_df['C7_z'] + dynamic_df['CLAV_z']) / 2)) / 2
    
    #Mid pelvis
    dynamic_df['MPEL_x'] = (((dynamic_df['RASI_x'] + dynamic_df['LASI_x']) / 2) + ((dynamic_df['RPSI_x'] + dynamic_df['LPSI_x']) / 2)) / 2
    dynamic_df['MPEL_y'] = (((dynamic_df['RASI_y'] + dynamic_df['LASI_y']) / 2) + ((dynamic_df['RPSI_y'] + dynamic_df['LPSI_y']) / 2)) / 2
    dynamic_df['MPEL_z'] = (((dynamic_df['RASI_z'] + dynamic_df['LASI_z']) / 2) + ((dynamic_df['RPSI_z'] + dynamic_df['LPSI_z']) / 2)) / 2
    
    #Create virtual markers using 3d trilateration
    
    #Define the 3d trilateration function
    #Trilateration calculations (from: https://github.com/akshayb6/trilateration-in-3d)
    def trilateration3D(p1 = None, p2 = None, p3 = None, p4 = None,
                        d1 = None, d2 = None, d3 = None, d4 = None):
        
        # Inputs
        # p1, p2, p3 and p4 - 3 column x nrow array of the 3d marker points
        # d1, d2, d3 and d4 - distances to the virtual marker in the static trial from the corresponding points
        
        #Check inputs
        if p1 is None or p2 is None or p3 is None or p4 is None:
            raise ValueError('All 4 points need to be specified as nd array')
        if d1 is None or d2 is None or d3 is None or d4 is None:
            raise ValueError('All 4 distances need to be specified as floats')
        
        #Allocate numpy array for results
        virtualMarker = np.zeros((len(p1),3))
        
        #Run calculations
        #Loop through numpy arrays
        for pp in range(0,len(p1)):
            e_x = (p2[pp]-p1[pp])/np.linalg.norm(p2[pp]-p1[pp])
            i = np.dot(e_x,(p3[pp]-p1[pp]))
            e_y = (p3[pp]-p1[pp]-(i*e_x))/(np.linalg.norm(p3[pp]-p1[pp]-(i*e_x)))
            e_z = np.cross(e_x,e_y)
            d = np.linalg.norm(p2[pp]-p1[pp])
            j = np.dot(e_y,(p3[pp]-p1[pp]))
            x = ((d1**2)-(d2**2)+(d**2))/(2*d)
            y = (((d1**2)-(d3**2)+(i**2)+(j**2))/(2*j))-((i/j)*(x))
            z1 = np.sqrt(d1**2-x**2-y**2)
            z2 = np.sqrt(d1**2-x**2-y**2)*(-1)
            ans1 = p1[pp]+(x*e_x)+(y*e_y)+(z1*e_z)
            ans2 = p1[pp]+(x*e_x)+(y*e_y)+(z2*e_z)
            dist1 = np.linalg.norm(p4[pp]-ans1)
            dist2 = np.linalg.norm(p4[pp]-ans2)
            if np.abs(d4-dist1) < np.abs(d4-dist2):
                virtualMarker[pp,:] = ans1
            else: 
                virtualMarker[pp,:] = ans2
        #Return calculated virtual marker from function
        return virtualMarker            
    
    #Load in the static marker file to identify average marker distances
    #Use the Vec3 TimeSeriesTable to read the Vec3 type data file.
    staticTable = osim.TimeSeriesTableVec3(staticTRC)
    #Convert to more easily readable object for easier manipulation
    #Get number of column labels and data rows
    nLabelsStatic = staticTable.getNumColumns()
    nRowsStatic = staticTable.getNumRows()
    #Pre-allocate numpy array based on data size
    dataArrayStatic = np.zeros((nRowsStatic,nLabelsStatic*3))
    #Loop through labels and rows and get data
    for iLabel in range(0,nLabelsStatic):
        #Get current column label
        currLabel = staticTable.getColumnLabel(iLabel)
        for iRow in range(0,nRowsStatic):
            dataArrayStatic[iRow,iLabel*3] = staticTable.getDependentColumn(currLabel).getElt(0,iRow).get(0)
            dataArrayStatic[iRow,iLabel*3+1] = staticTable.getDependentColumn(currLabel).getElt(0,iRow).get(1)
            dataArrayStatic[iRow,iLabel*3+2] = staticTable.getDependentColumn(currLabel).getElt(0,iRow).get(2)
    
    #Convert numpy array to pandas dataframe and add column labels
    #Get column labels
    colLabelsStatic = list()
    for iLabel in range(0,nLabelsStatic):
        colLabelsStatic.append(staticTable.getColumnLabel(iLabel)+'_x')
        colLabelsStatic.append(staticTable.getColumnLabel(iLabel)+'_y')
        colLabelsStatic.append(staticTable.getColumnLabel(iLabel)+'_z')
    #Convert to dataframe
    static_df = pd.DataFrame(data = dataArrayStatic,columns=colLabelsStatic)
    
    #Right and left knee joint centres
    #This will use the location and distances of the hip joint centre, and thigh
    #cluster markers
    
    #Calculate the mean distances for the four markers to the added knee joint centre
    
    # #Right limb
    # #Calculate distances from relevant markers
    # RHJC_RKJC_d = np.sqrt(np.square((np.mean(static_df['RKJC_x']) - np.mean(static_df['RHJC_x']))) + 
    #                       np.square((np.mean(static_df['RKJC_y']) - np.mean(static_df['RHJC_y']))) + 
    #                       np.square((np.mean(static_df['RKJC_z']) - np.mean(static_df['RHJC_z']))))
    # RTH1_RKJC_d = np.sqrt(np.square((np.mean(static_df['RKJC_x']) - np.mean(static_df['RTH1_x']))) + 
    #                       np.square((np.mean(static_df['RKJC_y']) - np.mean(static_df['RTH1_y']))) + 
    #                       np.square((np.mean(static_df['RKJC_z']) - np.mean(static_df['RTH1_z']))))
    # RTH2_RKJC_d = np.sqrt(np.square((np.mean(static_df['RKJC_x']) - np.mean(static_df['RTH2_x']))) + 
    #                       np.square((np.mean(static_df['RKJC_y']) - np.mean(static_df['RTH2_y']))) + 
    #                       np.square((np.mean(static_df['RKJC_z']) - np.mean(static_df['RTH2_z']))))
    # RTH3_RKJC_d = np.sqrt(np.square((np.mean(static_df['RKJC_x']) - np.mean(static_df['RTH3_x']))) + 
    #                       np.square((np.mean(static_df['RKJC_y']) - np.mean(static_df['RTH3_y']))) + 
    #                       np.square((np.mean(static_df['RKJC_z']) - np.mean(static_df['RTH3_z']))))
    # #Run trilateration function
    # RKJC = trilateration3D(p1 = dynamic_df[['RHJC_x','RHJC_y','RHJC_z']].to_numpy(),
    #                        p2 = dynamic_df[['RTH1_x','RTH1_y','RTH1_z']].to_numpy(),
    #                        p3 = dynamic_df[['RTH2_x','RTH2_y','RTH2_z']].to_numpy(),
    #                        p4 = dynamic_df[['RTH3_x','RTH3_y','RTH3_z']].to_numpy(),
    #                        d1 = RHJC_RKJC_d, d2 = RTH1_RKJC_d, d3 = RTH2_RKJC_d, d4 = RTH3_RKJC_d)
    # #Allocate to dataframe
    # dynamic_df['RKJC_x'] = RKJC[:,0]
    # dynamic_df['RKJC_y'] = RKJC[:,1]
    # dynamic_df['RKJC_z'] = RKJC[:,2]
    
    # #Left limb
    # #Calculate distances from relevant markers
    # LHJC_LKJC_d = np.sqrt(np.square((np.mean(static_df['LKJC_x']) - np.mean(static_df['LHJC_x']))) + 
    #                       np.square((np.mean(static_df['LKJC_y']) - np.mean(static_df['LHJC_y']))) + 
    #                       np.square((np.mean(static_df['LKJC_z']) - np.mean(static_df['LHJC_z']))))
    # LTH1_LKJC_d = np.sqrt(np.square((np.mean(static_df['LKJC_x']) - np.mean(static_df['LTH1_x']))) + 
    #                       np.square((np.mean(static_df['LKJC_y']) - np.mean(static_df['LTH1_y']))) + 
    #                       np.square((np.mean(static_df['LKJC_z']) - np.mean(static_df['LTH1_z']))))
    # LTH2_LKJC_d = np.sqrt(np.square((np.mean(static_df['LKJC_x']) - np.mean(static_df['LTH2_x']))) + 
    #                       np.square((np.mean(static_df['LKJC_y']) - np.mean(static_df['LTH2_y']))) + 
    #                       np.square((np.mean(static_df['LKJC_z']) - np.mean(static_df['LTH2_z']))))
    # LTH3_LKJC_d = np.sqrt(np.square((np.mean(static_df['LKJC_x']) - np.mean(static_df['LTH3_x']))) + 
    #                       np.square((np.mean(static_df['LKJC_y']) - np.mean(static_df['LTH3_y']))) + 
    #                       np.square((np.mean(static_df['LKJC_z']) - np.mean(static_df['LTH3_z']))))
    # #Run trilateration function
    # LKJC = trilateration3D(p1 = dynamic_df[['LHJC_x','LHJC_y','LHJC_z']].to_numpy(),
    #                        p2 = dynamic_df[['LTH1_x','LTH1_y','LTH1_z']].to_numpy(),
    #                        p3 = dynamic_df[['LTH2_x','LTH2_y','LTH2_z']].to_numpy(),
    #                        p4 = dynamic_df[['LTH3_x','LTH3_y','LTH3_z']].to_numpy(),
    #                        d1 = LHJC_LKJC_d, d2 = LTH1_LKJC_d, d3 = LTH2_LKJC_d, d4 = LTH3_LKJC_d)
    # #Allocate to dataframe
    # dynamic_df['LKJC_x'] = LKJC[:,0]
    # dynamic_df['LKJC_y'] = LKJC[:,1]
    # dynamic_df['LKJC_z'] = LKJC[:,2]
    
    ##### NOTE: trialteration doesn't work that great, markers move around...

    #Build the new time series Vec3 table
    
    #Get the time array
    time = dynamicTable.getIndependentColumn()
    
    #Set the label names
    #Get the original labels
    markerLabels = list(dynamicTable.getColumnLabels())
    #Append new marker labels
    markerLabels.append('RHJC')
    markerLabels.append('LHJC')
    # markerLabels.append('RKJC')
    # markerLabels.append('LKJC')
    markerLabels.append('MTOR')
    markerLabels.append('MPEL')
    
    #Initialise time series table of vec3 format
    newTable = osim.TimeSeriesTableVec3()
    
    #Set labels in table
    newLabels = osim.StdVectorString()
    for ii in range(0,len(markerLabels)):
        newLabels.append(markerLabels[ii])
    newTable.setColumnLabels(newLabels)
    
    #Build the time series table
    #Loop through rows and allocate data
    for iRow in range(0,nRows):
        #Create a blank opensim row vector
        row = osim.RowVectorVec3(len(markerLabels))
        #Create and fill row data
        for iCol in range(0,len(markerLabels)):
            #Identify the dataframe labels for the current marker label
            xLabel = markerLabels[iCol]+'_x'
            yLabel = markerLabels[iCol]+'_y'
            zLabel = markerLabels[iCol]+'_z'
            #Set data in row
            row.getElt(0,iCol).set(0,dynamic_df[xLabel][iRow])
            row.getElt(0,iCol).set(1,dynamic_df[yLabel][iRow])
            row.getElt(0,iCol).set(2,dynamic_df[zLabel][iRow])
        #Append row to table
        newTable.appendRow(iRow,row)
        #Set time value
        newTable.setIndependentValueAtIndex(iRow,time[iRow])
            
    #Update the meta data in the new table based on the original
    
    #Get meta data keys
    metaKeys = list(dynamicTable.getTableMetaDataKeys())
    
    #Loop through keys and append to new table
    for ii in range(0,len(metaKeys)):
        #Get current meta data string
        currMeta = metaKeys[ii]
        #Append with relevant value
        newTable.addTableMetaDataString(currMeta,dynamicTable.getTableMetaDataAsString(currMeta))
        
    #Write the new table to file
    osim.TRCFileAdapter().write(newTable,outputTRC)
    
    #Print confirm message
    print('Virtual markers added to new .trc file: '+outputTRC)