DHG, SHREC, OnlineDHG datasets

1. Introduction

Homepage:

http://www-rech.telecom-lille.fr/shrec2017-hand/

http://www-rech.telecom-lille.fr/DHGdataset/

PDF SHREC'17:

PDF DHG14/28:

2. Data download

Download the three datasets:

# ---------------------------------------------------------
# Step 1. Download hand gesture datasets
# ---------------------------------------------------------
# --------------------------
# SHREC2017 dataset
#     http://www-rech.telecom-lille.fr/shrec2017-hand/
# --------------------------
mkdir dataset_shrec2017
wget http://www-rech.telecom-lille.fr/shrec2017-hand/HandGestureDataset_SHREC2017.tar.gz -O SHREC2017.tar.gz
tar -xzf SHREC2017.tar.gz -C dataset_shrec2017

# --------------------------
# DHG14/28 dataset
#     http://www-rech.telecom-lille.fr/DHGdataset/
# --------------------------
# Note: you should register on http://www-rech.telecom-lille.fr/DHGdataset/ before downloading the dataset
mkdir dataset_dhg1428
wget http://www-rech.telecom-lille.fr/DHGdataset/DHG2016.zip
unzip DHG2016.zip -d dataset_dhg1428

# --------------------------
# Online DHG dataset
#     http://www-rech.telecom-lille.fr/shrec2017-hand/
# --------------------------
mkdir dataset_onlinedhg
wget http://www-rech.telecom-lille.fr/shrec2017-hand/OnlineDHG.zip
unzip OnlineDHG.zip -d dataset_onlinedhg

3. Data preprocessing

Resize the sequences the data across time:

# ---------------------------------------------------------
# Step 2. Utils
# ---------------------------------------------------------
import glob
import numpy
import pickle
import joblib
from scipy import ndimage as ndimage
from sklearn.model_selection import train_test_split


def resize_gestures(input_gestures, final_length=100):
    """
    Resize the time series by interpolating them to the same length

    Input:
        - input_gestures: list of numpy.ndarray tensors.
              Each tensor represents a single gesture.
              Gestures can have variable durations.
              Each tensor has a shape: (duration, channels)
              where duration is the duration of the individual gesture
                    channels = 44 = 2 * 22 if recorded in 2D and
                    channels = 66 = 3 * 22 if recorded in 3D 
    Output:
        - output_gestures: one numpy.ndarray tensor.
              The output tensor has a shape: (records, final_length, channels)
              where records = len(input_gestures)
                   final_length is the common duration of all gestures
                   channels is the same as above 
    """
    # please use python3. if you still use python2, important note: redefine the classic division operator / by importing it from the __future__ module
    output_gestures = numpy.array([numpy.array([ndimage.zoom(x_i.T[j], final_length / len(x_i), mode='reflect') for j in range(numpy.size(x_i, 1))]).T for x_i in input_gestures])
    return output_gestures


def load_gestures(dataset='dhg', root='./dataset_dhg1428', version_x='3D', version_y='both', resize_gesture_to_length=100):
    """
    Get the 3D or 2D pose gestures sequences, and their associated labels.

    Ouput:
        - a tuple of (gestures, labels) or (gestures, labels_14, labels_28)
              where gestures is either a numpy.ndarray tensor or
                                       a list of numpy.ndarray tensors,
                                       depending on if the gestures have been resized or not.
              Each tensor represents a single gesture.
              Gestures can have variable durations.
              Each tensor has a shape: (duration, channels) where channels is either 44 (= 2 * 22) or 66 (=3 * 22)
    """

    # SHREC 2017 (on Google Colab):
    # root = '/content/dataset_shrec2017/HandGestureDataset_SHREC2017'
    # DHG 14/28 (on Google Colab):
    # root = '/content/dataset_dhg1428'
    if dataset == 'dhg':
      assert 'dataset_dhg' in root
    if dataset == 'shrec':
      assert 'dataset_shrec' in root

    if version_x == '3D':
        if dataset == 'dhg':
            pattern = root + '/gesture_*/finger_*/subject_*/essai_*/skeleton_world.txt'
        elif dataset == 'shrec':
            pattern = root + '/gesture_*/finger_*/subject_*/essai_*/skeletons_world.txt'
    else:
        if dataset == 'dhg':
            pattern = root + '/gesture_*/finger_*/subject_*/essai_*/skeleton_image.txt'
        elif dataset == 'shrec':
            pattern = root + '/gesture_*/finger_*/subject_*/essai_*/skeletons_image.txt'

    gestures_filenames = sorted(glob.glob(pattern))
    gestures = [numpy.genfromtxt(f) for f in gestures_filenames]
    if resize_gesture_to_length is not None:
        gestures = resize_gestures(gestures, final_length=resize_gesture_to_length)

    labels_14 = [int(filename.split('/')[-5].split('_')[1]) for filename in gestures_filenames]
    labels_28 = [int(filename.split('/')[-4].split('_')[1]) for filename in gestures_filenames]
    labels_28 = [labels_14[idx_gesture] if n_fingers_used == 1 else 14 + labels_14[idx_gesture] for idx_gesture, n_fingers_used in enumerate(labels_28)]

    if version_y == '14' or version_y == 14:
        return gestures, labels_14
    elif version_y == '28' or version_y == 28:
        return gestures, labels_28
    elif version_y == 'both':
        return gestures, labels_14, labels_28


def write_data(data, filepath):
    """Save the dataset to a file. Note: data is a dict with keys 'x_train', ..."""
    with open(filepath, 'wb') as output_file:
        # pickle.dump(data, output_file)
        joblib.dump(data, output_file)


def load_shrec_dhg_data(filepath='./shrec_data.pckl'):
    """
    Returns hand gesture sequences (X) and their associated labels (Y).
    Each sequence has two different labels.
    The first label  Y describes the gesture class out of 14 possible gestures (e.g. swiping your hand to the right).
    The second label Y describes the gesture class out of 28 possible gestures (e.g. swiping your hand to the right with your index pointed, or not pointed).
    """
    file = open(filepath, 'rb')
    # data = pickle.load(file, encoding='latin1')  # <<---- change to 'latin1' to 'utf8' if the data does not load
    data = joblib.load(file)
    file.close()
    return data['x_train'], data['x_test'], data['y_train_14'], data['y_train_28'], data['y_test_14'], data['y_test_28']

Generate dhg_data.pckl or shrec_data.pckl with:

# ---------------------------------------------------------
# Step 3. Save the dataset(s) you need
# ---------------------------------------------------------
# Example A: 2D version of the SHREC gestures, untouched, and only the 14-label version of the labels
# x_2d_shrec, y_shrec_14 = load_gestures(dataset='shrec',
#                                        root='/tmp/dataset_shrec2017/HandGestureDataset_SHREC2017/',
#                                        version_x='2D',
#                                        version_y='14',
#                                        resize_gesture_to_length=None)

# Example B: 3D version of the DHG gestures, resized to 100 timesteps
gestures, labels_14, labels_28 = load_gestures(dataset='dhg',
                                               root='/tmp/dataset_dhg1428/',
                                               version_x='3D',
                                               version_y='both',
                                               resize_gesture_to_length=100)

# Split the dataset into train and test sets if you want:
x_train, x_test, y_train_14, y_test_14, y_train_28, y_test_28 = train_test_split(gestures, labels_14, labels_28, test_size=0.30)

# Save the dataset
data = {
    'x_train': x_train,
    'x_test': x_test,
    'y_train_14': y_train_14,
    'y_train_28': y_train_28,
    'y_test_14': y_test_14,
    'y_test_28': y_test_28
}
write_data(data, filepath='dhg_data.pckl')

4. Data loading

# ---------------------------------------------------------
# Step 4. Use the dataset(s)
# ---------------------------------------------------------
x_train, x_test, y_train_14, y_train_28, y_test_14, y_test_28 = load_shrec_dhg_data('dhg_data.pckl')

5. As images

See image_dhg_shrec.md