-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathkinarmzip2mat.m
More file actions
303 lines (218 loc) · 8.13 KB
/
kinarmzip2mat.m
File metadata and controls
303 lines (218 loc) · 8.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
function kinarmzip2mat(filename)
% function that convert .zip files from the KINARM platform to .mat files
% structured in a way that is compatible with the mat2bin.py script.
% ---
% O.Codol - codol.olivier@gmail.com
% 08-Oct-2021
% ---
%=============================
% HANDLE INPUT
%=============================
% convert to char if input is string
if isstring(filename)
filename = char(filename);
end
% check for input type
if ~ischar(filename)
error('input must be a string or char array indicating the full path of a file or directory.')
end
% if input is not a .zip or .kinarm file, it is assumed to be a directory,
% so this script is called recursively for each .zip or .kinarm file
% contained in the directory
is_zip = strcmpi(filename(end-3:end), '.zip');
is_kin = strcmpi(filename(end-6:end), '.kinarm');
is_dir = ~(is_zip | is_kin);
if is_dir
cd(filename)
filelist = ls;
filelist = mat2cell(filelist, ones(size(filelist,1),1), size(filelist,2));
ix1 = cellfun(@(x) contains(x, '.zip'), filelist);
ix2 = cellfun(@(x) contains(x, '.kinarm'), filelist);
ix = ix1 | ix2;
filelist = cellfun(@(x) regexprep(x, ' ', ''), filelist(ix), 'UniformOutput', false);
nfiles = numel(filelist);
for file = 1:nfiles-1
kinarmzip2mat(filelist{file})
end
filename = filelist{end};
is_zip = strcmpi(filename(end-3:end), '.zip');
is_kin = strcmpi(filename(end-6:end), '.kinarm');
end
%=============================
% ECTRACT DATA
%=============================
data_in = zip_load(filename);
sorted_data = sort_trials(data_in,'execution');
data = KINARM_add_hand_kinematics(sorted_data.c3d);
%=============================
% EXPERIMENT INFO
%=============================
SESSION_DATA.SAMPLERATE = data(1).HAND.RATE;
SESSION_DATA.HAND_UNIT = data(1).HAND.UNITS;
SESSION_DATA.ACTIVE_ARM = data(1).EXPERIMENT.ACTIVE_ARM;
SESSION_DATA.START_DATE_TIME = data(1).EXPERIMENT.START_DATE_TIME;
SESSION_DATA.TASK_PROGRAM = data(1).EXPERIMENT.TASK_PROGRAM;
SESSION_DATA.TASK_PROTOCOL = data(1).EXPERIMENT.TASK_PROTOCOL;
SESSION_DATA.USE_REPEAT_TRIAL_FLAG = data(1).EXPERIMENT.USE_REPEAT_TRIAL_FLAG;
SESSION_DATA.REFRESH_RATE = data(1).VIDEO_SETTINGS.REFRESH_RATE;
OUTPUT{1} = 'SESSION_DATA';
if isfield(data(1), 'LOAD_TABLE')
LOAD_TABLE = rmfield(data(1).LOAD_TABLE, {'COLUMN_ORDER', 'USED', 'DESCRIPTIONS'});
OUTPUT{numel(OUTPUT)+1} = 'LOAD_TABLE';
end
if isfield(data(1), 'LOAD_TABLE')
TARGET_TABLE = rmfield(data(1).TARGET_TABLE,...
{'COLUMN_ORDER', 'USED', 'DESCRIPTIONS', 'FRAME_OF_REFERENCE', 'FRAME_OF_REFERENCE_LIST'});
OUTPUT{numel(OUTPUT)+1} = 'TARGET_TABLE';
end
BLOCK_TABLE = rmfield(data(1).BLOCK_TABLE, {'USED', 'DESCRIPTIONS'});
BLOCK_TABLE.TP_LIST = reshape( BLOCK_TABLE.TP_LIST, [], 1);
BLOCK_TABLE.CATCH_TP_LIST = reshape( BLOCK_TABLE.CATCH_TP_LIST, [], 1);
OUTPUT{numel(OUTPUT)+1} = 'BLOCK_TABLE';
TP_TABLE = rmfield(data(1).TP_TABLE, {'COLUMN_ORDER', 'USED', 'DESCRIPTIONS'});
OUTPUT{numel(OUTPUT)+1} = 'TP_TABLE';
%=============================
% TRIAL INFO
%=============================
ntrials = numel(data);
nt = 0;
for t = 1:ntrials; nt = nt + get_frame_count(data(t)); end
[timestamps, trial, values] = deal(nan(nt, 1));
TRIAL_DATA = struct();
TRIAL_DATA.trial = uint16((1:ntrials)');
TRIAL_DATA.n_timestamps = uint16(zeros(ntrials,1));
TRIAL_DATA.time = cell(ntrials,1);
TRIAL_DATA.is_error = uint8(zeros(ntrials,1));
TRIAL_DATA.tp_row = uint16(zeros(ntrials,1));
if isfield(data(1), 'EVENTS')
eventsExist = true;
[~, tags] = sortEvents(data(1));
headers = [...
cellfun(@(x) cat(2,'EVENT_TIME_SEC_',x), tags, 'UniformOutput', 0);...
cellfun(@(x) cat(2,'EVENT_TIMESTAMP_',x), tags, 'UniformOutput', 0);...
cellfun(@(x) cat(2,'EVENT_ORDER_',x), tags, 'UniformOutput', 0)];
headers = cellfun(@(x) matlab.lang.makeValidName(x), headers, 'UniformOutput', 0);
for h = 1:numel(headers); TRIAL_DATA.(headers{h}) = nan(ntrials,1); end
else
eventsExist = false;
end
row_i = 1;
for t = 1:ntrials
nt = get_frame_count(data(t));
row_j = row_i + nt - 1;
timestamps(row_i:row_j) = 1:nt;
trial(row_i:row_j) = repmat(t, nt, 1);
TRIAL_DATA.n_timestamps(t) = uint16(nt);
TRIAL_DATA.time{t} = data(t).TRIAL.TIME(1:8);
TRIAL_DATA.is_error(t) = uint8(data(t).TRIAL.IS_ERROR);
TRIAL_DATA.tp_row(t) = uint16(data(t).TRIAL.TP);
if eventsExist
events = reshape( sortEvents(data(t)), [], 1);
for h = 1:numel(headers)
TRIAL_DATA.(headers{h})(t) = events(h);
end
end
row_i = row_j + 1;
end
%=============================
% TIME SERIES DATA
%=============================
fnames = fieldnames(data(1));
nfields = numel(fnames);
TIME_SERIES_DATA = struct('timestamp', uint16(timestamps), 'trial', uint16(trial));
for f = 1:nfields
row_i = 1;
fname = fnames{f};
% check if time series
if strcmpi(class(data(1).(fname)), 'double') % &&...
% ~startsWith(fname, 'Left_') &&...
% ~startsWith(fname, 'Gaze_')
for t = 1:ntrials
nt = get_frame_count(data(t));
row_j = row_i + nt - 1;
values(row_i:row_j) = data(t).(fname);
row_i = row_j + 1;
end
TIME_SERIES_DATA.(fname) = compress(values);
end
end
%=============================
% SAVE DATA INTO A (TEMPORARY) .MAT FILE
%=============================
if is_zip
outputname = regexprep(data_in.file_name, '.zip', '.mat');
elseif is_kin
outputname = regexprep(data_in.file_name, '.kinarm', '.mat');
end
save(outputname,'TIME_SERIES_DATA', 'TRIAL_DATA', 'SESSION_DATA', OUTPUT{:})
end
function content = compress(content)
original_class = class(content);
classtypes = {'double'; 'single'; 'int64'; 'int32'; 'int16'; 'int8';...
'uint64'; 'uint32'; 'uint16'; 'uint8'};
k = find(strcmpi(classtypes, original_class)) + 1;
while k <= numel(classtypes)
% compress data one classtype above
compressed_content = cast(content, classtypes{k});
if strcmpi(class(content),'double')
abs_err = abs(compressed_content - content);
else
% to allow comparisons across int classes
decompressed_content = cast(compressed_content, original_class);
abs_err = abs(decompressed_content - content);
end
max_abs_err = max(abs_err(:));
if max_abs_err == 0
content = compressed_content;
original_class = class(content);
else
k = numel(classtypes) + 1; % break the loop
end
k = k + 1;
end
end
function [events, event_names] = sortEvents(data)
samplerate = data.ANALOG.RATE;
% check the dimensions of the labels - need to transpose the labels if they
% are column arrays
[row, col] = size(data(1).EVENTS.LABELS{1});
if row > 1 & col == 1
occured_events = cellfun(@(x) deblank(x'), data.EVENTS.LABELS ,'uniformoutput',false)';% Remove whitespaces
else
occured_events = cellfun(@(x) deblank(x), data.EVENTS.LABELS ,'uniformoutput',false)';% Remove whitespaces
end
event_names = data.EVENT_DEFINITIONS.LABELS';% Get all the possible events
n_event = length(event_names);
events = nan(n_event,3);
for m = 1:n_event
event_order = find(strcmpi(event_names(m),occured_events)); % order of occurence
if ~isempty(event_order) % If event does exist...
time = data.EVENTS.TIMES(event_order); %...find its time in sec...
index = round(time * samplerate); % index of event occurrence
events(m,:) = [ time(end), index(end), event_order(end) ]; % take last occurence if several
end
end
end
function frame_count = get_frame_count(S)
if isfield(S.HAND, 'LONG_FRAMES')
frame_field = 'LONG_FRAMES';
else
frame_field = 'FRAMES';
end
frame_count = S.HAND.(frame_field);
% in some versions of dexterit-e, the HAND structure does not hold the
% number of frames recorded in the trial.
if frame_count <= 0
fnames = fieldnames(S);
found = false;
while ~found
for f = 1:numel(S)
fname = fnames{f};
if strcmpi(class(S.(fname)), 'double')
frame_count = numel(S.(fname));
found = true;
end
end
end
end
end