test fmri dataset

function test_suite = test_fmri_dataset()
% tests for cosmo_fmri_dataset
%
% #   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_base_fmri_dataset()
    ds=get_base_dataset();
    assert_dataset_equal(ds, get_expected_dataset());

function test_afni_fmri_dataset()
    if cosmo_skip_test_if_no_external('afni')
        return;
    end

    ds=get_base_dataset();
    afni=cosmo_map2fmri(ds,'-afni');
    afni_expected=get_expected_afni();

    assertAlmostEqualWithTol(afni.img, afni_expected.img);
    afni_no_img=rmfield(afni,'img');
    afni_expected_no_img=rmfield(afni_expected,'img');
    assertEqual(afni_no_img,afni_expected_no_img);

    ds_afni=cosmo_fmri_dataset(afni);
    assert_dataset_equal(ds,ds_afni);

function test_nii_sform_fmri_dataset()
    ds=get_base_dataset();
    nii=cosmo_map2fmri(ds,'-nii');
    nii_expected=get_expected_nii_sform();
    assertEqual(nii.hdr, nii_expected.hdr);
    assertAlmostEqualWithTol(nii.img,nii_expected.img);
    ds_nii=cosmo_fmri_dataset(nii);
    assert_dataset_equal(ds,ds_nii);

function test_nii_qform_fmri_dataset()
    ds=get_base_dataset();
    nii=get_expected_nii_qform();
    ds_nii=cosmo_fmri_dataset(nii);
    assert_dataset_equal(ds,ds_nii);

    % check qform with illegal pixdims
    nii.hdr.dime.pixdim(1:4)=-1;
    nii.hdr.hist.quatern_b=1;
    nii.hdr.hist.quatern_c=0;
    nii.hdr.hist.quatern_d=0;

    ds_nii_alt=cosmo_fmri_dataset(nii);
    m=ds_nii_alt.a.vol.mat;
    assertEqual(diag(m),[1 -1 1 1]');
    assertEqual(m(1:3,4),[-2 0 -2]');


function test_nii_pixdim_fmri_dataset()
    ds=get_base_dataset();
    nii=get_expected_nii_pixdim();
    ds_nii=cosmo_fmri_dataset(nii);
    ds_nii.a.vol.mat(:,4)=ds.a.vol.mat(:,4);
    assert_dataset_equal(ds,ds_nii);

    nii.hdr.dime.scl_inter=200;
    nii.hdr.dime.scl_slope=17;

    ds_nii_scl=cosmo_fmri_dataset(nii);
    assertElementsAlmostEqual(17*ds.samples+200,ds_nii_scl.samples,...
                                    'absolute',1e-3);


function test_nii_sform_afni_5d_singleton_dataset()
    ds=get_base_dataset();
    nii=get_expected_nii_sform();

    % header: move 4th to 5th dimension
    nii.hdr.dime.dim(6)=nii.hdr.dime.dim(5);
    nii.hdr.dime.dim(5)=1;

    % data: move 4th to 5th dimension
    orig_size=size(nii.img);
    nii.img=reshape(nii.img,[orig_size(1:3) 1 orig_size(4)]);

    % test this test: verify size is ok
    assertEqual(nii.hdr.dime.dim(2:6),size(nii.img));

    ds_nii=cosmo_fmri_dataset(nii);
    assert_dataset_equal(ds,ds_nii);

function test_nii_sform_afni_5d_nonsingleton_dataset()
    ds=get_base_dataset();
    nii=get_expected_nii_sform();

    % header: add extra dimension
    nrep=ceil(rand()*3+2);
    nii.hdr.dime.dim(6)=nrep;

    % data: move 4th to 5th dimension
    img_rep=repmat({nii.img},1,nrep);
    nii.img=cat(5,img_rep{:});

    % test this test: verify size is ok
    assertEqual(nii.hdr.dime.dim(2:6),size(nii.img));

    % must not support this
    assertExceptionThrown(@()cosmo_fmri_dataset(nii),'');


function test_nii_sform_and_qform_fmri_dataset()
    ds=get_base_dataset();
    nii=get_expected_nii_sform_and_qform();
    ds_nii=cosmo_fmri_dataset(nii);
    assert_dataset_equal(ds,ds_nii);

    nii.hdr.hist.srow_x(1)=1;
    assertExceptionThrown(@()cosmo_fmri_dataset(nii),'');
    ds_nii_qform=cosmo_fmri_dataset(nii,'nifti_form','qform');
    assertEqual(ds_nii_qform,ds_nii);
    assertExceptionThrown(@()cosmo_fmri_dataset(nii,'nifti_form','X'),'');


function test_bv_vmr_fmri_dataset()
    if ~can_test_bv()
        return
    end
    uint_ds=get_uint8_dataset();
    bv_vmr=cosmo_map2fmri(uint_ds,'-bv_vmr');

    cleaner=onCleanup(@()bv_vmr.ClearObject());
    neuroelf_bless_wrapper(bv_vmr);

    assert_bv_equal(bv_vmr, get_expected_bv_vmr());
    ds_bv_vmr=cosmo_fmri_dataset(bv_vmr,'mask',false);
    ds_bv_vmr_lpi=cosmo_fmri_reorient(ds_bv_vmr,'LPI');
    assert_dataset_equal(uint_ds,ds_bv_vmr_lpi,'rotation');


function test_bv_vmp_fmri_dataset()
    if ~can_test_bv()
        return
    end

    ds=get_base_dataset();
    bv_vmp=cosmo_map2fmri(ds,'-bv_vmp');

    cleaner=onCleanup(@()bv_vmp.ClearObject());
    neuroelf_bless_wrapper(bv_vmp);

    assert_bv_equal(bv_vmp, get_expected_bv_vmp());
    ds_bv_vmp=cosmo_fmri_dataset(bv_vmp);
    ds_bv_vmp_lpi=cosmo_fmri_reorient(ds_bv_vmp,'LPI');
    assert_dataset_equal(ds,ds_bv_vmp_lpi,'translation');


function test_bv_msk_fmri_dataset()
    if ~can_test_bv()
        return
    end

    uint_ds=get_uint8_dataset();
    bv_msk=cosmo_map2fmri(uint_ds,'-bv_msk');

    cleaner=onCleanup(@()bv_msk.ClearObject());
    neuroelf_bless_wrapper(bv_msk);

    assert_bv_equal(bv_msk, get_expected_bv_msk());

    ds_bv_msk=cosmo_fmri_dataset(bv_msk,'mask',false);
    ds_bv_msk_lpi=cosmo_fmri_reorient(ds_bv_msk,'LPI');
    assert_dataset_equal(uint_ds,ds_bv_msk_lpi,'translation');


function test_bv_vtc_fmri_dataset()
    if ~can_test_bv()
        return
    end

    ds=get_base_dataset();
    ds.samples=round(ds.samples);
    ds.samples(ds.samples<0)=0;

    xff_bv_vtc=get_expected_xff_bv_vtc();
    cleaner=onCleanup(@()xff_bv_vtc.ClearObject());
    neuroelf_bless_wrapper(xff_bv_vtc);

    ds_bv_vtc=cosmo_fmri_dataset(xff_bv_vtc);
    ds_bv_glm_lpi=cosmo_fmri_reorient(ds_bv_vtc,'LPI');

    assert_dataset_equal(ds,ds_bv_glm_lpi,'translation');


function test_bv_glm_subject_fmri_dataset()
    if ~can_test_bv()
        return
    end

    % no support for map2fmri, so just test with neuroelf struct
    ds=get_base_dataset();

    xff_bv_glm=get_expected_xff_bv_glm();
    cleaner=onCleanup(@()xff_bv_glm.ClearObject());
    neuroelf_bless_wrapper(xff_bv_glm);

    ds_bv_glm=cosmo_fmri_dataset(xff_bv_glm);
    ds_bv_glm_lpi=cosmo_fmri_reorient(ds_bv_glm,'LPI');

    assert_dataset_equal(ds,ds_bv_glm_lpi,'translation');


function test_mask_fmri_dataset()
    aet=@(varargin)assertExceptionThrown(@()...
                        cosmo_fmri_dataset(varargin{:}),'');
    ds=cosmo_synthetic_dataset();
    ds.samples(:,[2 6])=0;

    x=cosmo_fmri_dataset(ds,'mask','-auto');
    assertEqual(x,cosmo_slice(ds,[1 3 4 5],2));
    x1=cosmo_fmri_dataset(ds,'mask',true);
    assertEqual(x1,x);
    x2=cosmo_fmri_dataset(ds,'mask',false);
    assertEqual(x2,ds);


    aet(ds,'mask','foo');
    aet(ds,'mask','-foo');
    aet(ds,'mask',ds);
    aet(ds,'mask',struct);

    ds.samples(1,2)=1;
    aet(ds,'mask','-auto');

    x3=cosmo_fmri_dataset(ds,'mask','-any');
    assertEqual(x3,x);

    x4=cosmo_fmri_dataset(ds,'mask','-all');
    assertEqual(x4,cosmo_slice(ds,[1 2 3 4 5],2));

    msk_ds=cosmo_slice(cosmo_slice(x,randperm(size(x.samples,2)),2),1);
    x5=cosmo_fmri_dataset(ds,'mask',msk_ds);
    assertEqual(x5,x);


function test_pymvpa_fmri_dataset()
    pymvpa_ds=get_pymvpa_dataset();
    ds=cosmo_fmri_dataset(pymvpa_ds);
    assert_dataset_equal(ds, get_expected_dataset(), 'translation');


function test_pymvpa_3d_string_array()
    py_ds=get_pymvpa_dataset();

    sa_labels={'foo';'bar'};
    sa_labels_3d=reshape(strvcat(sa_labels),[2 1 3]);
    py_ds.sa.labels=sa_labels_3d;

    fa_labels={'foox','barx','foobazx','1','2','3'};
    fa_labels_3d=reshape(strvcat(fa_labels),[1 6 7]);
    py_ds.fa.labels=fa_labels_3d;

    ds=cosmo_fmri_dataset(py_ds);
    assertEqual(ds.sa.labels,sa_labels);
    assertEqual(ds.fa.labels,fa_labels);


function test_meeg_source_fmri_dataset()
    ds=cosmo_synthetic_dataset('type','source');
    res=cosmo_fmri_dataset(ds);
    assertEqual(ds.samples,res.samples);
    assertEqual(diag(res.a.vol.mat),[10 10 10 1]');

function test_set_sa_fmri_dataset()
    ds=cosmo_synthetic_dataset();
    res=cosmo_fmri_dataset(ds,'targets',1:6,'chunks',3);
    assertEqual(res.sa.targets,(1:6)');
    assertEqual(res.sa.chunks,ones(6,1)*3);

    assertExceptionThrown(@()cosmo_fmri_dataset(ds,'targets',[1 2]),'');

function test_nan_warning_fmri_dataset()
    ds=cosmo_synthetic_dataset();
    ds.samples(1)=NaN;
    msk_ds=cosmo_slice(ds,2);

    orig_warning_state=cosmo_warning();
    warning_state_resetter=onCleanup(@()cosmo_warning(orig_warning_state));
    cosmo_warning('off');

    cosmo_fmri_dataset(ds,'mask',msk_ds);
    warning_state=cosmo_warning();

    warning_pos=strmatch('The input dataset has NaN',...
                    warning_state.shown_warnings);
    assert(~isempty(warning_pos));


function tf=can_test_bv()
    tf=~cosmo_skip_test_if_no_external('neuroelf');

function ds=get_base_dataset()
    ds=cosmo_synthetic_dataset('ntargets',2,'nchunks',1);

function ds=get_uint8_dataset()
    ds=cosmo_synthetic_dataset('ntargets',2,'nchunks',1);
    ds=cosmo_slice(ds,1);
    mn=min(ds.samples);
    mx=max(ds.samples);
    ds.samples=uint8((ds.samples-mn)/(mx-mn)*255);

function ds=get_pymvpa_dataset()
    ds=cosmo_synthetic_dataset('ntargets',2,'nchunks',1);

    % set .a
    ds.a.imgaffine=ds.a.vol.mat;
    ds.a.imgaffine(1:3,4)=ds.a.imgaffine(1:3,4)+...
                        ds.a.imgaffine(1:3,1:3)*ones(3,1);
    ds.a.voxel_eldim=single([3 3]);
    ds.a.voxel_dim=int64(ds.a.vol.dim(:));
    ds.a=rmfield(ds.a,'vol');
    ds.a=rmfield(ds.a,'fdim');

    % set .fa
    voxel_indices=int64([ds.fa.i; ds.fa.j; ds.fa.k]-1);
    ds.fa=struct();
    ds.fa.voxel_indices=voxel_indices;

    % set .samples
    ds.samples=single(ds.samples);



function assertAlmostEqualWithTol(x,y)
    assertElementsAlmostEqual(double(x),double(y),'relative',1e-3);


function assert_dataset_equal(x,y,opt)
    if nargin<3 || isempty(opt)
        opt='';
    end
    % dataset equality, without .sa
    assertAlmostEqualWithTol(x.samples, y.samples);
    assertEqual(x.fa, y.fa)

    switch opt
        case 'rotation'
            % BV VMR cannot store orientation, just check the rotation part
            assertEqual(x.a.fdim, y.a.fdim);
            assertAlmostEqualWithTol(x.a.vol.dim,y.a.vol.dim);
            assertAlmostEqualWithTol(x.a.vol.mat(1:3,1:3),...
                            y.a.vol.mat(1:3,1:3));
        case 'translation'
            % BV VMP or PyMVPA cannot store xform, just check the matrix
            assertEqual(x.a.fdim, y.a.fdim);
            assertAlmostEqualWithTol(x.a.vol.dim,y.a.vol.dim);
            assertAlmostEqualWithTol(x.a.vol.mat,y.a.vol.mat);
        otherwise
            assertEqual(x.a, y.a)
    end


function assert_bv_equal(x_xff, y_struct)
    % BV equality; first argument is xff class, second argument a struct
    % passes if all fields in y_struct have the same value in x_xff;
    % x_xff can have more fields than y_struct
    keys=fieldnames(y_struct);
    for k=1:numel(keys)
        key=keys{k};
        y=y_struct.(key);
        x=x_xff.(key);
        if isstruct(x)
            for j=1:numel(x)
                assert_bv_equal(x(j),y(j))
            end
        else
            assertAlmostEqualWithTol(x,y);
        end

    end

function xff_bv_glm=get_expected_xff_bv_glm()
    xff_bv_glm=xff('new:glm');
    xff_bv_glm.NrOfSubjects=1;
    xff_bv_glm.NrOfPredictors=2;
    xff_bv_glm.Predictor=struct('Name1',{'1','2'},...
                                'Name2',{'1','2'},...
                                'RGB',{[255 0 0] [0 255 0]});
    xff_bv_glm.Resolution=2;
    xff_bv_glm.XStart = 127;
    xff_bv_glm.XEnd = 131;
    xff_bv_glm.YStart = 129;
    xff_bv_glm.YEnd = 131;
    xff_bv_glm.ZStart = 125;
    xff_bv_glm.ZEnd = 131;

    data=zeros(2,1,3,2);
    data_size=size(data);
    mat_size=data_size(1:3);
    data(:,:,:,1)=reshape([ -0.2040   -1.0504   -0.2617 ...
                            -3.6849    1.3494    2.0317], mat_size);
    data(:,:,:,2)=reshape([0.4823 -1.3265 2.3387 ...
                            1.7235 -0.3973 0.5838], mat_size);
    xff_bv_glm.GLMData.BetaMaps=data;

function xff_bv_vtc=get_expected_xff_bv_vtc()
    xff_bv_vtc=xff('new:vtc');
    xff_bv_vtc.NrOfVolumes=2;

    xff_bv_vtc.Resolution=2;
    xff_bv_vtc.XStart = 127;
    xff_bv_vtc.XEnd = 131;
    xff_bv_vtc.YStart = 129;
    xff_bv_vtc.YEnd = 131;
    xff_bv_vtc.ZStart = 125;
    xff_bv_vtc.ZEnd = 131;

    data_orig=zeros(2,1,3,2);
    data_size=size(data_orig);
    mat_size=data_size(1:3);
    data_orig(:,:,:,1)=reshape([ -0.2040   -1.0504   -0.2617 ...
                            -3.6849    1.3494    2.0317], mat_size);
    data_orig(:,:,:,2)=reshape([0.4823 -1.3265 2.3387 ...
                            1.7235 -0.3973 0.5838], mat_size);

    data=uint16(data_orig);
    xff_bv_vtc.VTCData=zeros(2,2,1,3,'uint16');
    xff_bv_vtc.VTCData(1,:,:,:)=data(:,:,:,1);
    xff_bv_vtc.VTCData(2,:,:,:)=data(:,:,:,2);


function bv_msk=get_expected_bv_msk()
    bv_msk=struct();
    bv_msk.Resolution= 2 ;
    bv_msk.XStart = 127;
    bv_msk.XEnd = 131;
    bv_msk.YStart = 129;
    bv_msk.YEnd = 131;
    bv_msk.ZStart = 125;
    bv_msk.ZEnd = 131;

    data=zeros(2,1,3,'uint8');
    data(:,:,1) = [ 155 118 ];
    data(:,:,2) = [ 153 0 ];
    data(:,:,3) = [ 225 255 ];
    bv_msk.Mask=data;

function bv_vmp=get_expected_bv_vmp()
    bv_vmp=struct();
    bv_vmp.NrOfMaps = 2;
    bv_vmp.OverallMapType = 1;
    bv_vmp.OverallNrOfLags = [];
    bv_vmp.NrOfTimePoints = 0;
    bv_vmp.NrOfMapParameters = 0;
    bv_vmp.ShowParamsRangeFrom = 0;
    bv_vmp.ShowParamsRangeTo = 0;
    bv_vmp.FingerprintParamsRangeFrom = 0;
    bv_vmp.FingerprintParamsRangeTo = 0;
    bv_vmp.Resolution = 2;
    bv_vmp.XStart = 127;
    bv_vmp.XEnd = 131;
    bv_vmp.YStart = 129;
    bv_vmp.YEnd = 131;
    bv_vmp.ZStart = 125;
    bv_vmp.ZEnd = 131;

    map1.VMPData=reshape([-0.2040   -1.0504   -0.2617   ...
                            -3.6849    1.3494    2.0317],[2 1 3]);
    map2.VMPData=reshape([0.4823 -1.3265 2.3387 ...
                            1.7235 -0.3973 0.5838],[2 1 3]);
    bv_vmp.Map=cat(1,map1,map2);

function bv_vmr=get_expected_bv_vmr()

    bv_vmr=struct();
    bv_vmr.FileVersion = 3;
    bv_vmr.DimX = 2;
    bv_vmr.DimY = 1;
    bv_vmr.DimZ = 3;
    bv_vmr.VMRData16 = [];
    bv_vmr.OffsetX = 0;
    bv_vmr.OffsetY = 0;
    bv_vmr.OffsetZ = 0;
    bv_vmr.FramingCube = 256;
    bv_vmr.PosInfoVerified = false;
    bv_vmr.RowDirX = 0;
    bv_vmr.RowDirY = 1;
    bv_vmr.RowDirZ = 0;
    bv_vmr.ColDirX = 0;
    bv_vmr.ColDirY = 0;
    bv_vmr.ColDirZ = -1;
    bv_vmr.NRows = 256;
    bv_vmr.NCols = 256;
    bv_vmr.FoVRows = 256;
    bv_vmr.FoVCols = 256;
    bv_vmr.SliceThickness = 1;
    bv_vmr.GapThickness = 0;
    bv_vmr.ReferenceSpace = 0;
    bv_vmr.VoxResX = 2;
    bv_vmr.VoxResY = 2;
    bv_vmr.VoxResZ = 2;
    bv_vmr.VoxResInTalairach = false;
    bv_vmr.VoxResVerified = false;

    data=zeros(2,1,3,'uint8');
    data(:,:,1) = [ 155 118 ];
    data(:,:,2) = [ 153 0 ];
    data(:,:,3) = [ 225 255 ];
    bv_vmr.VMRData=data;



function afni=get_expected_afni()

    % header
    afni=struct();
    afni.SCENE_DATA = [ 0 11 1 ];
    afni.TYPESTRING = '3DIM_HEAD_FUNC' ;
    afni.BRICK_TYPES = [ 3 3 ];
    afni.BRICK_STATS = [];
    afni.BRICK_FLOAT_FACS = [ ];
    afni.DATASET_RANK = [ 3 2 ];
    afni.DATASET_DIMENSIONS = [ 3 2 1 ];
    afni.ORIENT_SPECIFIC = [ 1 2 4 ];
    afni.DELTA = [ -2 -2 2 ];
    afni.ORIGIN = [ 1 1 -1 ];
    afni.SCALE = 0;
    afni.BRICK_LABS = [];
    afni.BRICK_KEYWORDS = [];
    afni.BRICK_STATAUX = [];
    afni.STAT_AUX = [];
    afni.SCALE=0;
    afni.NOTES_COUNT=0;
    afni.WARP_TYPE=[0 0];
    afni.FileFormat='BRIK';
    [unused, unused, endian_ness] = computer();
    afni.BYTEORDER_STRING = sprintf('%sSB_FIRST',endian_ness );

    % data
    data=zeros(3,2,1,2);
    data(:,:,1,1)=[ 2.0317    1.3494;-3.6849   -0.2617;-1.0504   -0.2040];
    data(:,:,1,2)=[ 0.5838   -0.3973; 1.7235    2.3387;-1.3265    0.4823];

    % add to image
    afni.img=data;

function ds=get_expected_dataset()
    ds=struct();
    ds.samples=[ 2.0317   -3.6849   -1.0504    1.3494   -0.2617   -0.2040;
                 0.5838    1.7235   -1.3265   -0.3973    2.3387    0.4823];
    ds.fa.i=[1 2 3 1 2 3];
    ds.fa.j=[1 1 1 2 2 2];
    ds.fa.k=[1 1 1 1 1 1];
    ds.a.fdim.labels={'i';'j';'k'};
    ds.a.fdim.values={[1 2 3];[1 2];1};
    mat=diag([2 2 2 1]);
    mat(1:3,4)=-3;
    ds.a.vol.mat=mat;
    ds.a.vol.dim=[3 2 1];
    ds.a.vol.xform='scanner_anat';


function nii=get_expected_nii_pixdim()
    nii=get_expected_nii_helper();
    fields_to_clear={'quatern_b','quatern_c','quatern_d',...
                        'qoffset_x','qoffset_y','qoffset_z',...
                        'srow_x','srow_y','srow_z'};
    nii.hdr.hist=clear_fields(nii.hdr.hist,fields_to_clear);

function nii=get_expected_nii_sform_and_qform()
    nii=get_expected_nii_helper();
    nii.hdr.hist.sform_code = 1;
    nii.hdr.hist.qform_code = 1;

function nii=get_expected_nii_sform()
    nii=get_expected_nii_helper();
    nii.hdr.hist.sform_code = 1;

    fields_to_clear={'quatern_b','quatern_c','quatern_d',...
                        'qoffset_x','qoffset_y','qoffset_z'};
    nii.hdr.hist=clear_fields(nii.hdr.hist,fields_to_clear);

function nii=get_expected_nii_qform()
    nii=get_expected_nii_helper();
    nii.hdr.hist.qform_code = 1;

    fields_to_clear={'srow_x','srow_y','srow_z'};
    nii.hdr.hist=clear_fields(nii.hdr.hist,fields_to_clear);

function nii=get_expected_nii_helper()
    hdr=struct();
    hdr.dime.datatype = 16;
    hdr.dime.dim = [ 4 3 2 1 2 1 1 1 ];
    hdr.dime.pixdim = [ 0 2 2 2 0 0 0 0 ];
    hdr.dime.intent_p1 = 0;
    hdr.dime.intent_p2 = 0;
    hdr.dime.intent_p3 = 0;
    hdr.dime.intent_code = 0;
    hdr.dime.slice_start = 0;
    hdr.dime.slice_duration = 0;
    hdr.dime.slice_end = 0;
    hdr.dime.scl_slope = 0;
    hdr.dime.scl_inter = 0;
    hdr.dime.slice_code = 0;
    hdr.dime.cal_max = 0;
    hdr.dime.cal_min = 0;
    hdr.dime.toffset = 0;
    hdr.dime.xyzt_units = 10;
    hdr.hk.sizeof_hdr = 348;
    hdr.hk.data_type='';
    hdr.hk.db_name='';
    hdr.hk.extents = 0;
    hdr.hk.session_error = 0;
    hdr.hk.regular='r';
    hdr.hk.dim_info = 0;

    hdr.hist.descrip='';
    hdr.hist.aux_file='';
    hdr.hist.intent_name='';
    hdr.hist.srow_x = [ 2 0 0 -1 ];
    hdr.hist.srow_y = [ 0 2 0 -1 ];
    hdr.hist.srow_z = [ 0 0 2 -1 ];
    hdr.hist.quatern_c = 0;
    hdr.hist.originator = [ 2 1 1 1 ];

    hdr.hist.sform_code = 0;
    hdr.hist.qform_code = 0;

    % set up qform
    m=[hdr.hist.srow_x;hdr.hist.srow_y;hdr.hist.srow_z];
    quatern_a=.5*sqrt(1+m(1,1)+m(2,2)+m(3,3));
    hdr.hist.quatern_b = .25*(m(3,2)-m(2,3)) / quatern_a;
    hdr.hist.quatern_c = .25*(m(1,3)-m(3,1)) / quatern_a;
    hdr.hist.quatern_d = .25*(m(2,1)-m(1,2)) / quatern_a;

    hdr.hist.qoffset_x = m(1,4);
    hdr.hist.qoffset_y = m(2,4);
    hdr.hist.qoffset_z = m(3,4);

    data=zeros(3,2,1,2);
    data(:,:,1,1)=[ 2.0317  1.3494; -3.6849 -0.2617; -1.0504   -0.2040];
    data(:,:,1,2)=[ 0.5838 -0.3973;  1.7235  2.3387; -1.3265    0.4823];

    nii=struct();
    nii.hdr=hdr;
    nii.img=single(data);

function s=clear_fields(s,keys)
    n=numel(keys);
    for k=1:n
        key=keys{k};
        v=s.(key);
        v(:)=0;
        s.(key)=v;
    end

function result=neuroelf_bless_wrapper(arg)
    % deals with recent neuroelf (>v1.1), where bless is deprecated
    s=warning('off','neuroelf:xff:deprecated');
    resetter=onCleanup(@()warning(s));

    result=bless(arg);