function test_suite = test_vol_coordinates
% tests for cosmo_vol_coordinates
%
% # For CoSMoMVPA's copyright information and license terms, #
% # see the COPYING file distributed with CoSMoMVPA. #
try % assignment of 'localfunctions' is necessary in Matlab >= 2016
test_functions = localfunctions();
catch % no problem; early Matlab versions can use initTestSuite fine
end
initTestSuite;
function test_vol_coordinates_()
ds = cosmo_synthetic_dataset();
[ns, nf] = size(ds.samples);
% sample some features
rp = randperm(nf);
nsel = round(nf / 2);
rp = rp(1:nsel);
ds_sel = cosmo_slice(ds, rp, 2);
% voxel to world space
xyz = cosmo_vol_coordinates(ds_sel);
assertEqual(size(ds_sel.samples), [ns nsel]);
xyz_ = cosmo_vol_coordinates(ds_sel, 1:nsel);
assertEqual(xyz, xyz_);
selsel = randperm(nsel);
xyz__ = cosmo_vol_coordinates(ds_sel, selsel);
assertEqual(xyz(:, selsel), xyz__);
% check coordinate transform
ijk1 = [ds_sel.fa.i; ds_sel.fa.j; ds_sel.fa.k; ones(1, nsel)];
xyz1 = ds.a.vol.mat * ijk1;
assertElementsAlmostEqual(xyz1(1:3, :), xyz_);
% double the dataset
ds_sel2 = cosmo_stack({ds_sel, ds_sel}, 2);
xyz2 = cosmo_vol_coordinates(ds_sel2);
assertEqual(xyz2, [xyz xyz]);
% world to voxel space
fa_indices = cosmo_vol_coordinates(ds_sel, xyz(:, selsel));
assertVectorsAlmostEqual(fa_indices, selsel);
% duplicate features cannot be matched back
assertExceptionThrown(@()cosmo_vol_coordinates(ds_sel2, ...
xyz(:, selsel)), '');
function test_vol_coordinates_afni()
% test coordinates using AFNI's 3dmerge
if cosmo_skip_test_if_no_external('afni_bin')
return
end
% make an fMRI dataset
dim_sizes = [60 50 30];
dim_values = cellfun(@(x) 1:x, num2cell(dim_sizes), 'UniformOutput', false);
dim_labels = {'i', 'j', 'k'};
vox_sizes = [3 3 3];
origin = -dim_sizes .* vox_sizes * .4; % slightly off-center
nsamples = 2;
data4d = zeros([nsamples, dim_sizes]);
ds = cosmo_flatten(data4d, dim_labels, dim_values);
ds.sa.labels = cellfun(@(x) sprintf('sample%d', x), ...
num2cell(1:nsamples)', ...
'UniformOutput', false);
ds.sa.stats = repmat({'Ftest(10,1)'}, nsamples, 1);
ds.a.vol.mat = [diag(vox_sizes), origin'; [0 0 0 1]];
ds.a.vol.dim = dim_sizes;
ds.sa.labels = ds.sa.labels(:, 1);
center_distance = 20;
offsets = [3 7 11]; % some offsets in x, y, z directions
xyz = bsxfun(@plus, cosmo_cartprod(repmat({[-1, 1] * center_distance}, 1, 3)), ...
offsets);
ncoord = size(xyz, 1);
fa_indices = cosmo_vol_coordinates(ds, xyz');
xyz = cosmo_vol_coordinates(ds, fa_indices); % recompute
ds.samples(:, fa_indices) = repmat(1:ncoord, nsamples, 1); % 1 to 8
% make temporary directory
tmp_dir = cosmo_make_temp_filename();
tmp_dir_cleaner = onCleanup(@()remove_dir_helper(tmp_dir));
mkdir(tmp_dir);
% generate AFNI files
ext_postfix = {'.nii', {''}
'+orig', {'.BRIK', '.BRIK.gz', '.HEAD'}};
for j = 1:size(ext_postfix, 1)
ext = ext_postfix{j, 1};
postfixes = ext_postfix{j, 2};
get_fn = @(x) fullfile(tmp_dir, [x ext]);
get_to_delete = @(x, exts) cellfun(@(y) ...
sprintf('%s%s', ...
get_fn(x), y), ...
postfixes, ...
'UniformOutput', ...
false);
base_fn = get_fn('base');
cosmo_map2fmri(ds, base_fn);
orients = {'lpi', 'rai', 'asr'};
for k = 1:numel(orients)
orient = orients{k};
fn = get_fn(orient);
cmd = sprintf('3dresample -overwrite -orient %s -prefix %s -input %s', ...
orient, fn, base_fn);
r = unix(cmd);
assert(r == 0);
cmd = sprintf(['3dclust -orient LPI -1clip .5 0 0 %s''[0]'' '...
'2>/dev/null | grep --invert-match ''#'''], fn);
[r, v] = unix(cmd);
assert(r == 0);
to_delete = get_to_delete(orient, postfixes);
cmd = sprintf('rm -f %s', cosmo_strjoin(to_delete, ' '));
unused = unix(cmd);
s = sscanf(v, '%f');
table = reshape(s, 16, []);
% sort by voxel value
[unused, i] = sort(table(11, :));
table = table(:, i);
line1 = ones(1, ncoord);
line0 = 0 * line1;
line_inc = 1:ncoord;
expected_table = [line1; xyz; kron(xyz, [1 1]'); ...
line_inc; line0; line_inc; xyz];
assertEqual(table, expected_table);
end
end
function test_test_vol_coordinates_exceptions()
aet = @(varargin)assertExceptionThrown(@() ...
cosmo_vol_coordinates(varargin{:}), '');
ds = cosmo_synthetic_dataset();
% not 1xP or 3xP
aet(ds, ones(2));
aet(ds, [1; 2]);
% not in fdim
ds_bad = ds;
ds_bad = cosmo_dim_transpose(ds_bad, 'i', 1);
aet(ds_bad, 1);
% size mismatch
ds_bad = ds;
ds_bad.a.vol.dim = ds_bad.a.vol.dim + 1;
aet(ds_bad, 1);
% no matrix
ds_bad = ds;
ds_bad.a.vol = rmfield(ds_bad.a.vol, 'mat');
aet(ds_bad, 1);
% not 4x4 matrix
ds_bad = ds;
ds_bad.a.vol.mat = eye(5);
aet(ds_bad, 1);
function remove_dir_helper(tmp_dir)
if cosmo_wtf('is_octave')
rmdir_state = confirm_recursive_rmdir();
state_resetter = onCleanup(@()confirm_recursive_rmdir(rmdir_state));
confirm_recursive_rmdir(false, 'local');
end
rmdir(tmp_dir, 's');