Spaces:
Runtime error
Runtime error
import pandas as pd | |
import numpy as np | |
import tensorflow as tf | |
import json | |
import os | |
# Convert the variables to the correct data type | |
# Load the variables from the JSON file | |
current_directory = os.path.dirname(os.path.abspath(__file__)) | |
# Construct the path to variables.json | |
json_file_path = os.path.join(current_directory, 'variables.json') | |
with open(json_file_path, 'r') as json_file: | |
variables_dict = json.load(json_file) | |
# Lips Landmark Face Ids | |
LIPS_LANDMARK_IDXS = variables_dict['LIPS_LANDMARK_IDXS'] | |
N_TARGET_FRAMES = variables_dict['N_TARGET_FRAMES'] | |
N_DIMS0 = variables_dict['N_TARGET_FRAMES'] | |
# Read data from a CSV file | |
csv_file_path = os.path.join(current_directory, 'landmarks.csv') | |
df = pd.read_csv(csv_file_path) | |
def get_idxs(df, words_pos, words_neg=[], ret_names=True, idxs_pos=None): | |
""" | |
Given a DataFrame and a list of words, this function will find all the column names | |
that contain all the words in 'words_pos' and none of the words in 'words_neg'. | |
Parameters: | |
df (pandas.DataFrame): Dataframe to search for column names | |
words_pos (list of str): List of words that column names should contain | |
words_neg (list of str, optional): List of words that column names should not contain. Default is empty list. | |
ret_names (bool, optional): Whether to return column names. Default is True. | |
idxs_pos (list of int, optional): Column indices to search within. Default is None, which means search all columns. | |
Returns: | |
idxs (np.array): Column indices where column names meet the criteria | |
names (np.array): Column names that meet the criteria. Only returned if 'ret_names' is True. | |
""" | |
idxs = [] | |
names = [] | |
for w in words_pos: | |
for col_idx, col in enumerate(df.columns): | |
# Exclude Non Landmark Columns | |
if col in ['frame']: | |
continue | |
col_idx = int(col.split('_')[-1]) | |
# Check if column name contains all words | |
if (w in col) and (idxs_pos is None or col_idx in idxs_pos) and all([w not in col for w in words_neg]): | |
idxs.append(col_idx) | |
names.append(col) | |
# Convert to Numpy arrays | |
idxs = np.array(idxs) | |
names = np.array(names) | |
# Returns either both column indices and names | |
if ret_names: | |
return idxs, names | |
# Or only columns indices | |
else: | |
return idxs | |
# Get the indices of columns of interest | |
LEFT_HAND_IDXS0, LEFT_HAND_NAMES0 = get_idxs(df, ['left_hand'], ['z']) | |
RIGHT_HAND_IDXS0, RIGHT_HAND_NAMES0 = get_idxs(df, ['right_hand'], ['z']) | |
LIPS_IDXS0, LIPS_NAMES0 = get_idxs(df, ['face'], ['z'], idxs_pos=LIPS_LANDMARK_IDXS) | |
COLUMNS0 = np.concatenate((LEFT_HAND_NAMES0, RIGHT_HAND_NAMES0, LIPS_NAMES0)) | |
N_COLS0 = len(COLUMNS0) | |
N_COLS = N_COLS0 | |
class PreprocessLayerNonNaN(tf.keras.layers.Layer): | |
""" | |
This is a custom layer in Keras that replaces NaN values in the input tensor with 0. | |
""" | |
def __init__(self): | |
super(PreprocessLayerNonNaN, self).__init__() | |
def call(self, data0): | |
""" | |
This method is called when the layer instance is called with some inputs. | |
Parameters: | |
data0 (Tensor): Input tensor | |
Returns: | |
data (Tensor): Output tensor with the same shape as the input, but with NaN values replaced with 0 | |
""" | |
# Fill NaN Values With 0 | |
data = tf.where(tf.math.is_nan(data0), 0.0, data0) | |
# Hacky | |
data = data[None] | |
# Empty Hand Frame Filtering | |
hands = tf.slice(data, [0, 0, 0], [-1, -1, 84]) | |
hands = tf.abs(hands) | |
mask = tf.reduce_sum(hands, axis=2) | |
mask = tf.not_equal(mask, 0) | |
data = data[mask][None] | |
data = tf.squeeze(data, axis=[0]) | |
return data | |
class PreprocessLayer(tf.keras.layers.Layer): | |
""" | |
This is a custom layer in Keras that pre-processes the input data in a specific way, | |
which includes filling NaN values with 0, filtering empty frames and resizing frames. | |
""" | |
def __init__(self): | |
super(PreprocessLayer, self).__init__() | |
def call(self, data0, resize=True): | |
""" | |
This method is called when the layer instance is called with some inputs. | |
Parameters: | |
data0 (Tensor): Input tensor | |
resize (bool, optional): Whether to resize the frames. Default is True. | |
Returns: | |
data (Tensor): Output tensor after pre-processing | |
""" | |
# Fill NaN Values With 0 | |
data = tf.where(tf.math.is_nan(data0), 0.0, data0) | |
# Empty Hand Frame Filtering | |
hands = tf.slice(data, [0, 0, 0], [-1, -1, 84]) | |
hands = tf.abs(hands) | |
mask = tf.reduce_sum(hands, axis=2) | |
mask = tf.not_equal(mask, 0) | |
data = data[mask][None] | |
# Pad Zeros | |
N_FRAMES = len(data[0]) | |
if N_FRAMES < N_TARGET_FRAMES: | |
data = tf.concat(( | |
data, | |
tf.zeros([1, N_TARGET_FRAMES - N_FRAMES, N_COLS], dtype=tf.float32) | |
), axis=1) | |
# Downsample | |
data = tf.image.resize( | |
data, | |
[1, N_TARGET_FRAMES], | |
method=tf.image.ResizeMethod.BILINEAR, | |
) | |
# Squeeze Batch Dimension | |
data = tf.squeeze(data, axis=[0]) | |
return data | |
df = df[COLUMNS0] # select only columns of interest equal to N_COLS0 | |
hand_tracking_sequence = df.values.reshape(1, -1, N_COLS0) # reshape after converting DataFrame to numpy array | |
preprocess_layer_instance = PreprocessLayer() # instantiate PreprocessLayer class | |
processed_sequence = preprocess_layer_instance(hand_tracking_sequence) # call instance with data | |
# print(f'input sequence shape: {hand_tracking_sequence.shape}') | |
# print(f'processed sequence shape: {processed_sequence.shape}') | |