function [tablePathwayRxns] = runFindSparsePathway(model,modelType,start,stop,via,CandidateSubSystem,specificMetabolites2Ignore);
% This function takes in a model (either metabolic model or a whole-body
% model), a start metabolite, and a stop metabolite and finds a shortest
% pathway
ignoreMetabolites = {'atp','h','coa','pi','h2o','o2','nh4','h2o2','nadph','nadh','nadp','nad','adp','udp','na1','hco3',...
    'ppi', 'imp','xmp','amp','ppa','fad','fadh2','oh1','co2','gmp','so4','pap','paps','mg2','zn2',...
    'cl','ca2','cu2','adpcbl','fe3','vitd3','i','caro','mn2','fe2','avite1','btn','ascb_L','phyQ','pnto_R','pydx','k'};

%% do not change hereafter

ignoreMetabolites = [specificMetabolites2Ignore';ignoreMetabolites'];
tablePathwayRxns ='';
s=' ';
osenseStr = 'max';

model = addReaction(model,['start_', start],'reactionFormula',[start,s,'<=>']);
model = addReaction(model,['stop_', stop],'reactionFormula',[stop,s,'<=>']);
printRxnFormula(model,model.rxns(end-1:end))

model2 = model;
compartments = {'e','c','m','n','p','r','l','g','x'};
ignoreMet = [];
for i = 1 : length(compartments)
    ignoreM =strcat(ignoreMetabolites,'[',compartments{i},']');
    ignoreMet = [ignoreMet;ignoreM'];
end
if strcmp(modelType,'WBM')
    sex = model.sex;
    % load list of organs
    OrganLists;
    % expand list of compartments for biofluids
    biofluids = {'bc','bcK','bp','d','u','lu','luLI','luSI','fe','csf','luC','a','aL','sw','luI','bpC','bpL'};

    for i = 1 : length(biofluids)
        ignoreM =strcat(ignoreMetabolites,'[',biofluids{i},']');
        ignoreMet = [ignoreMet;ignoreM'];
    end
    clear ignoreM
    ignoreMet2 =[ignoreMet];
    for i = 1 : length(OrgansListExt)
        ignoreM =strcat(OrgansListExt{i},'_',ignoreMet);
        ignoreMet2 = [ignoreMet2;ignoreM];
    end
    ignoreMet = ignoreMet2;
end

[model2, rxnRemoveList] = removeMetabolites(model2, ignoreMet,true,'legacy');

%Excl_R = [find(contains(model2.rxns,'DM_'));find(contains(model2.rxns,'sink_'));];
%model2 = changeRxnBounds(model2,model2.rxns(Excl_R),0,'b');

% needed for WBM
% set whole
if strcmp(modelType,'WBM')
    model2 = changeRxnBounds(model2,'Whole_body_objective_rxn',0,'l');
    model2 = changeRxnBounds(model2,'Whole_body_objective_rxn',1,'u');
end
%remove all constraints
model2.lb(find(model2.lb<0))=-10000000;
model2.ub(find(model2.ub<0))=0;
model2.lb(find(model2.lb>0))=0;
model2.ub(find(model2.ub>0))=10000000;

% remove coupling constraints if present
if isfield(model2,'C')
    model2 = rmfield(model2,'C');
    model2 = rmfield(model2,'ctrs');
end

% does not work for WBM

%model = changeRxnBounds(model,model.rxns(find(contains(model.rxns,'EX_'))),-1,'l');

model2 = changeObjective(model2,['stop_', stop]);
model2 = changeRxnBounds(model2,['start_', start],-1000,'b');
model2 = changeRxnBounds(model2,['stop_', stop],1000,'l');

if ~isempty(via)
    % enforce flux through via reactions

 %   model2 = changeRxnBounds(model2,via,-1*ones(length(via),1),'u');
    model2 = changeRxnBounds(model2,via,1*ones(length(via),1),'l');
end

FBA = optimizeCbModel(model2);

if FBA.origStat == 1 %problem is feasible
    %%
    param.printLevel = 0;
    param.regularizeOuter = 1;
    rxnPenalty = ones(length(model2.rxns),1);
    % not implemented yet
    %RS = findRxnsFromSubSystem(model2,CandidateSubSystem);
    %rxnPenalty(ismember(model2.rxns,RS)) = 0;
    rxnPenalty(ismember(model2.rxns,['stop_', stop])) = -1;
    rxnPenalty(ismember(model2.rxns,['start_', start])) = -1;
    rxnPenalty(ismember(model2.rxns,via)) = -1;
    [solution,sparseRxnBool2] = findSparsePathway(model2,rxnPenalty,param);
    sparse = model2.rxns(sparseRxnBool2)
    nnz(sparseRxnBool2)
    nnz(solution.v)
    pathwayRxnsAbbr = sparse;
    pathwayRxnsFormulae = printRxnFormula(model,'rxnAbbrList',sparse,'printFlag',0);
    tablePathwayRxns = table(pathwayRxnsAbbr,pathwayRxnsFormulae);
    nnz(solution.v)
    if 1
        % without ignore metabolites
        [involvedMets, deadEnds] = draw_by_rxn(model2, sparse, 'true');
    else
        % with ignored metabolites
        [involvedMets, deadEnds] = draw_by_rxn(model, sparse, 'true');
    end
else
    fprintf('Problem is infeasible.\n');
end


function [cls,M1] = spm_preproc_write8(res,tc,bf,df,mrf,cleanup,bb,vx)
% Write out VBM preprocessed data
% FORMAT [cls,M1] = spm_preproc_write8(res,tc,bf,df,mrf,cleanup,bb,vx)
%__________________________________________________________________________
% Copyright (C) 2008-2016 Wellcome Trust Centre for Neuroimaging

% John Ashburner
% $Id: spm_preproc_write8.m 6881 2016-09-19 09:48:54Z john $

% Prior adjustment factor.
% This is a fudge factor to weaken the effects of the tissue priors.  The
% idea here is that the bias from the tissue priors probably needs to be
% reduced because of the spatial smoothing typically used in VBM studies.
% Having the optimal bias/variance tradeoff for each voxel is not the same
% as having the optimal tradeoff for weighted averages over several voxels.

if isfield(res,'mg')
    lkp = res.lkp;
    Kb  = max(lkp);
else
    Kb  = size(res.intensity(1).lik,2);
end
N   = numel(res.image);

if nargin<2, tc = true(Kb,4); end % native, import, warped, warped-mod
if nargin<3, bf = false(N,2); end % field, corrected
if nargin<4, df = false(1,2); end % inverse, forward
if nargin<5, mrf = 1;         end % MRF parameter
if nargin<6, cleanup = 1;     end % Run the ad hoc cleanup

% Read essentials from tpm (it will be cleared later)
tpm = res.tpm;
if ~isstruct(tpm) || ~isfield(tpm, 'bg1'),
    tpm = spm_load_priors8(tpm);
end
d1        = size(tpm.dat{1});
d1        = d1(1:3);
M1        = tpm.M;

% Define orientation and field of view of any "normalised" space
% data that may be generated (wc*.nii, mwc*.nii, rc*.nii & y_*.nii).
if nargin>=7 && any(isfinite(bb(:)))
    % If a bounding box is supplied, combine this with the closest
    % bounding box derived from the dimensions and orientations of
    % the tissue priors.
    if nargin<7, bb = NaN(2,3);   end % Default to TPM bounding box
    if nargin<8, vx = NaN;        end % Default to TPM voxel size
    [bb1,vx1] = spm_get_bbox(tpm.V(1), 'old');
    bb(~isfinite(bb)) = bb1(~isfinite(bb));
    if ~isfinite(vx), vx = abs(prod(vx1))^(1/3); end
    bb(1,:) = vx*round(bb(1,:)/vx);
    bb(2,:) = vx*round(bb(2,:)/vx);
    odim    = abs(round((bb(2,1:3)-bb(1,1:3))/vx))+1;

    mm  = [[bb(1,1) bb(1,2) bb(1,3)
            bb(2,1) bb(1,2) bb(1,3)
            bb(1,1) bb(2,2) bb(1,3)
            bb(2,1) bb(2,2) bb(1,3)
            bb(1,1) bb(1,2) bb(2,3)
            bb(2,1) bb(1,2) bb(2,3)
            bb(1,1) bb(2,2) bb(2,3)
            bb(2,1) bb(2,2) bb(2,3)]'; ones(1,8)];
    vx3 = [[1       1       1
            odim(1) 1       1
            1       odim(2) 1
            odim(1) odim(2) 1
            1       1       odim(3)
            odim(1) 1       odim(3)
            1       odim(2) odim(3)
            odim(1) odim(2) odim(3)]'; ones(1,8)];
    mat    = mm/vx3;
else
    % Use the actual dimensions and orientations of
    % the tissue priors.
    odim = tpm.V(1).dim;
    mat  = tpm.V(1).mat;
end

[pth,nam] = fileparts(res.image(1).fname);
ind  = res.image(1).n;
d    = res.image(1).dim(1:3);

[x1,x2,o] = ndgrid(1:d(1),1:d(2),1);
x3 = 1:d(3);

chan(N) = struct('B1',[],'B2',[],'B3',[],'T',[],'Nc',[],'Nf',[],'ind',[]);
for n=1:N
    d3         = [size(res.Tbias{n}) 1];
    chan(n).B3 = spm_dctmtx(d(3),d3(3),x3);
    chan(n).B2 = spm_dctmtx(d(2),d3(2),x2(1,:)');
    chan(n).B1 = spm_dctmtx(d(1),d3(1),x1(:,1));
    chan(n).T  = res.Tbias{n};

    % Need to fix writing of bias fields or bias corrected images, when the data used are 4D.
    [pth1,nam1] = fileparts(res.image(n).fname);
    chan(n).ind = res.image(n).n;

    if bf(n,2)
        chan(n).Nc      = nifti;
        chan(n).Nc.dat  = file_array(fullfile(pth1,['m', nam1, '.nii']),...
                                     res.image(n).dim(1:3),...
                                     [spm_type('float32') spm_platform('bigend')],...
                                     0,1,0);
        chan(n).Nc.mat  = res.image(n).mat;
        chan(n).Nc.mat0 = res.image(n).mat;
        chan(n).Nc.descrip = 'Bias corrected';
        create(chan(n).Nc);
    end

    if bf(n,1),
        chan(n).Nf      = nifti;
        chan(n).Nf.dat  = file_array(fullfile(pth1,['BiasField_', nam1, '.nii']),...
                                     res.image(n).dim(1:3),...
                                     [spm_type('float32') spm_platform('bigend')],...
                                     0,1,0);
        chan(n).Nf.mat  = res.image(n).mat;
        chan(n).Nf.mat0 = res.image(n).mat;
        chan(n).Nf.descrip = 'Estimated Bias Field';
        create(chan(n).Nf);
    end
end

do_cls   = any(tc(:)) || nargout>=1;
tiss(Kb) = struct('Nt',[]);
for k1=1:Kb
    if tc(k1,4) || any(tc(:,3)) || tc(k1,2) || nargout>=1,
        do_cls  = true;
    end
    if tc(k1,1),
        tiss(k1).Nt      = nifti;
        tiss(k1).Nt.dat  = file_array(fullfile(pth,['c', num2str(k1), nam, '.nii']),...
                                      res.image(n).dim(1:3),...
                                      [spm_type('uint8') spm_platform('bigend')],...
                                      0,1/255,0);
        tiss(k1).Nt.mat  = res.image(n).mat;
        tiss(k1).Nt.mat0 = res.image(n).mat;
        tiss(k1).Nt.descrip = ['Tissue class ' num2str(k1)];
        create(tiss(k1).Nt);
        do_cls = true;
    end
end

prm     = [3 3 3 0 0 0];
Coef    = cell(1,3);
Coef{1} = spm_bsplinc(res.Twarp(:,:,:,1),prm);
Coef{2} = spm_bsplinc(res.Twarp(:,:,:,2),prm);
Coef{3} = spm_bsplinc(res.Twarp(:,:,:,3),prm);

do_defs = any(df);
do_defs = do_cls | do_defs;
if do_defs
    if df(1)
        [pth,nam] =fileparts(res.image(1).fname);
        Ndef      = nifti;
        Ndef.dat  = file_array(fullfile(pth,['iy_', nam, '.nii']),...
                               [res.image(1).dim(1:3),1,3],...
                               [spm_type('float32') spm_platform('bigend')],...
                               0,1,0);
        Ndef.mat  = res.image(1).mat;
        Ndef.mat0 = res.image(1).mat;
        Ndef.descrip = 'Inverse Deformation';
        create(Ndef);
    end
    if df(2) || any(any(tc(:,[2,3,4]))) || nargout>=1,
        y = zeros([res.image(1).dim(1:3),3],'single');
    end
end

spm_progress_bar('init',length(x3),['Working on ' nam],'Planes completed');
M = M1\res.Affine*res.image(1).mat;

if do_cls
    Q = zeros([d(1:3),Kb],'single');
end

for z=1:length(x3)

    % Bias corrected image
    cr = cell(1,N);
    for n=1:N
        f          = spm_sample_vol(res.image(n),x1,x2,o*x3(z),0);
        bf         = exp(transf(chan(n).B1,chan(n).B2,chan(n).B3(z,:),chan(n).T));
        cr{n}      = bf.*f;
        if ~isempty(chan(n).Nc),
            % Write a plane of bias corrected data
            chan(n).Nc.dat(:,:,z,chan(n).ind(1),chan(n).ind(2)) = cr{n};
        end
        if ~isempty(chan(n).Nf),
            % Write a plane of bias field
            chan(n).Nf.dat(:,:,z,chan(n).ind(1),chan(n).ind(2)) = bf;
        end
    end

    if do_defs
        % Compute the deformation (mapping voxels in image to voxels in TPM)
        [t1,t2,t3] = defs(Coef,z,res.MT,prm,x1,x2,x3,M);

        if exist('Ndef','var')
            % Write out the deformation to file, adjusting it so mapping is
            % to voxels (voxels in image to mm in TPM)
            tmp = M1(1,1)*t1 + M1(1,2)*t2 + M1(1,3)*t3 + M1(1,4);
            Ndef.dat(:,:,z,1,1) = tmp;
            tmp = M1(2,1)*t1 + M1(2,2)*t2 + M1(2,3)*t3 + M1(2,4);
            Ndef.dat(:,:,z,1,2) = tmp;
            tmp = M1(3,1)*t1 + M1(3,2)*t2 + M1(3,3)*t3 + M1(3,4);
            Ndef.dat(:,:,z,1,3) = tmp;
        end

        if exist('y','var')
            % If needed later, save in variable y
            y(:,:,z,1) = t1;
            y(:,:,z,2) = t2;
            y(:,:,z,3) = t3;
        end

        if do_cls
            % Generate variable Q if tissue classes are needed
           %msk = (f==0) | ~isfinite(f);

            if isfield(res,'mg')
                % Parametric representation of intensity distributions
                q   = zeros([d(1:2) Kb]);
                q1  = likelihoods(cr,[],res.mg,res.mn,res.vr);
                q1  = reshape(q1,[d(1:2),numel(res.mg)]);
                b   = spm_sample_priors8(tpm,t1,t2,t3);
                wp  = res.wp;
                s   = zeros(size(b{1}));
                for k1 = 1:Kb
                    b{k1} = wp(k1)*b{k1};
                    s     = s + b{k1};
                end
                for k1=1:Kb
                    q(:,:,k1) = sum(q1(:,:,lkp==k1),3).*(b{k1}./s);
                end
            else
                % Nonparametric representation of intensity distributions
                q   = spm_sample_priors8(tpm,t1,t2,t3);
                wp  = res.wp;
                s   = zeros(size(q{1}));
                for k1 = 1:Kb
                    q{k1} = wp(k1)*q{k1};
                    s     = s + q{k1};
                end
                for k1 = 1:Kb
                    q{k1} = q{k1}./s;
                end
                q   = cat(3,q{:});

                for n=1:N
                    tmp = round(cr{n}*res.intensity(n).interscal(2) + res.intensity(n).interscal(1));
                    tmp = min(max(tmp,1),size(res.intensity(n).lik,1));
                    for k1=1:Kb
                        likelihood = res.intensity(n).lik(:,k1);
                        q(:,:,k1)  = q(:,:,k1).*likelihood(tmp);
                    end
                end
            end
            Q(:,:,z,:) = reshape(q,[d(1:2),1,Kb]);
        end
    end
    spm_progress_bar('set',z);
end
spm_progress_bar('clear');

cls   = cell(1,Kb);
if do_cls
    P = zeros([d(1:3),Kb],'uint8');
    if mrf==0
        % Normalise to sum to 1
        sQ = (sum(Q,4)+eps)/255;
        for k1=1:size(Q,4)
            P(:,:,:,k1) = uint8(round(Q(:,:,:,k1)./sQ));
        end
        clear sQ
    else
        % Use a MRF cleanup procedure
        nmrf_its = 10;
        spm_progress_bar('init',nmrf_its,['MRF: Working on ' nam],'Iterations completed');
        G   = ones([Kb,1],'single')*mrf;
        vx2 = 1./single(sum(res.image(1).mat(1:3,1:3).^2));
        %save PQG P Q G tiss Kb x3 ind
        for iter=1:nmrf_its
            spm_mrf(P,Q,G,vx2);
            spm_progress_bar('set',iter);
        end
    end
    clear Q

    if cleanup
        % Use an ad hoc brain cleanup procedure
        if size(P,4)>5
            P = clean_gwc(P,cleanup);
        else
            warning('Cleanup not done.');
        end
    end

    % Write tissues if necessary
    for k1=1:Kb
        if ~isempty(tiss(k1).Nt)
            for z=1:length(x3)
                tmp = double(P(:,:,z,k1))/255;
                tiss(k1).Nt.dat(:,:,z,ind(1),ind(2)) = tmp;
            end
        end
    end
    spm_progress_bar('clear');

    % Put tissue classes into a cell array...
    for k1=1:Kb
        if tc(k1,4) || any(tc(:,3)) || tc(k1,2) || nargout>=1
            cls{k1} = P(:,:,:,k1);
        end
    end
    clear P % ...and remove the original 4D array
end

clear tpm
M0  = res.image(1).mat;

if any(tc(:,2))
    % "Imported" tissue class images

    % Generate mm coordinates of where deformations map from
    x      = affind(rgrid(d),M0);

    % Generate mm coordinates of where deformation maps to
    y1     = affind(y,M1);

    % Procrustes analysis to compute the closest rigid-body
    % transformation to the deformation, weighted by the
    % interesting tissue classes.
    ind    = find(tc(:,2)); % Saved tissue classes
    [dummy,R]  = spm_get_closest_affine(x,y1,single(cls{ind(1)})/255);
    clear x y1

    mat0   = R\mat; % Voxel-to-world of original image space

    [pth,nam] = fileparts(res.image(1).fname);
    fwhm   = max(vx./sqrt(sum(res.image(1).mat(1:3,1:3).^2))-1,0.01);
    for k1=1:size(tc,1)
        if tc(k1,2)

            % Low pass filtering to reduce aliasing effects in downsampled images,
            % then reslice and write to disk
            tmp1    = decimate(single(cls{k1}),fwhm);
            Ni      = nifti;
            Ni.dat  = file_array(fullfile(pth,['rc', num2str(k1), nam, '.nii']),...
                                 odim,...
                                 [spm_type('float32') spm_platform('bigend')],...
                                 0,1,0);
            Ni.mat         = mat;
            Ni.mat_intent  = 'Aligned';
            Ni.mat0        = mat0;
            Ni.mat0_intent = 'Aligned';
            Ni.descrip     = ['Imported Tissue ' num2str(k1)];
            create(Ni);

            for i=1:odim(3)
                tmp           = spm_slice_vol(tmp1,M0\mat0*spm_matrix([0 0 i]),odim(1:2),[1,NaN])/255;
                Ni.dat(:,:,i) = tmp;
            end
            clear tmp1
        end
    end
end

if any(tc(:,3)) || any(tc(:,4)) || nargout>=1 || df(2)
    % Adjust stuff so that warped data (and deformations) have the
    % desired bounding box and voxel sizes, instead of being the same
    % as those of the tissue probability maps.
    M = mat\M1;
    for i=1:size(y,3)
        t1         = y(:,:,i,1);
        t2         = y(:,:,i,2);
        t3         = y(:,:,i,3);
        y(:,:,i,1) = M(1,1)*t1 + M(1,2)*t2 + M(1,3)*t3 + M(1,4);
        y(:,:,i,2) = M(2,1)*t1 + M(2,2)*t2 + M(2,3)*t3 + M(2,4);
        y(:,:,i,3) = M(3,1)*t1 + M(3,2)*t2 + M(3,3)*t3 + M(3,4);
    end
    M1 = mat;
    d1 = odim;
end

if any(tc(:,3)) || any(tc(:,4)) || nargout>=1

    if any(tc(:,3))
        C = zeros([d1,Kb],'single');
    end

    spm_progress_bar('init',Kb,'Warped Tissue Classes','Classes completed');
    for k1 = 1:Kb
        if ~isempty(cls{k1})
            c = single(cls{k1})/255;
            if any(tc(:,3))
                [c,w]       = spm_diffeo('push',c,y,d1(1:3));
                vx          = sqrt(sum(M1(1:3,1:3).^2));
                spm_field('boundary',1);
                C(:,:,:,k1) = spm_field(w,c,[vx  1e-6 1e-4 0  3 2]);
                clear w
            else
                c      = spm_diffeo('push',c,y,d1(1:3));
            end
            if nargout>=1
                cls{k1} = c;
            end
            if tc(k1,4)
                N      = nifti;
                N.dat  = file_array(fullfile(pth,['mwc', num2str(k1), nam, '.nii']),...
                                    d1,...
                                    [spm_type('float32') spm_platform('bigend')],...
                                    0,1,0);
                N.mat  = M1;
                N.mat0 = M1;
                N.descrip = ['Jac. sc. warped tissue class ' num2str(k1)];
                create(N);
                N.dat(:,:,:) = c*abs(det(M0(1:3,1:3))/det(M1(1:3,1:3)));
            end
            spm_progress_bar('set',k1);
        end
    end
    spm_progress_bar('Clear');

    if any(tc(:,3))
        spm_progress_bar('init',Kb,'Writing Warped Tis Cls','Classes completed');
        C = max(C,eps);
        s = sum(C,4);
        for k1=1:Kb
            if tc(k1,3)
                N      = nifti;
                N.dat  = file_array(fullfile(pth,['wc', num2str(k1), nam, '.nii']),...
                                    d1,'uint8',0,1/255,0);
                N.mat  = M1;
                N.mat0 = M1;
                N.descrip = ['Warped tissue class ' num2str(k1)];
                create(N);
                N.dat(:,:,:) = C(:,:,:,k1)./s;
            end
            spm_progress_bar('set',k1);
        end
        spm_progress_bar('Clear');
        clear C s
    end
end

if df(2)
    y         = spm_diffeo('invdef',y,d1,eye(4),M0);
    y         = spm_extrapolate_def(y,M1);
    N         = nifti;
    N.dat     = file_array(fullfile(pth,['y_', nam, '.nii']),...
                           [d1,1,3],'float32',0,1,0);
    N.mat     = M1;
    N.mat0    = M1;
    N.descrip = 'Deformation';
    create(N);
    N.dat(:,:,:,:,:) = reshape(y,[d1,1,3]);
end

return;

%==========================================================================
% function [x1,y1,z1] = defs(sol,z,MT,prm,x0,y0,z0,M)
%==========================================================================
function [x1,y1,z1] = defs(sol,z,MT,prm,x0,y0,z0,M)
iMT = inv(MT);
x1  = x0*iMT(1,1)+iMT(1,4);
y1  = y0*iMT(2,2)+iMT(2,4);
z1  = (z0(z)*iMT(3,3)+iMT(3,4))*ones(size(x1));
x1a = x0    + spm_bsplins(sol{1},x1,y1,z1,prm);
y1a = y0    + spm_bsplins(sol{2},x1,y1,z1,prm);
z1a = z0(z) + spm_bsplins(sol{3},x1,y1,z1,prm);
x1  = M(1,1)*x1a + M(1,2)*y1a + M(1,3)*z1a + M(1,4);
y1  = M(2,1)*x1a + M(2,2)*y1a + M(2,3)*z1a + M(2,4);
z1  = M(3,1)*x1a + M(3,2)*y1a + M(3,3)*z1a + M(3,4);
return;

%==========================================================================
% function t = transf(B1,B2,B3,T)
%==========================================================================
function t = transf(B1,B2,B3,T)
if ~isempty(T)
    d2 = [size(T) 1];
    t1 = reshape(reshape(T, d2(1)*d2(2),d2(3))*B3', d2(1), d2(2));
    t  = B1*t1*B2';
else
    t = zeros(size(B1,1),size(B2,1),size(B3,1));
end
return;

%==========================================================================
% function p = likelihoods(f,bf,mg,mn,vr)
%==========================================================================
function p = likelihoods(f,bf,mg,mn,vr)
K  = numel(mg);
N  = numel(f);
M  = numel(f{1});
cr = zeros(M,N);
for n=1:N
    if isempty(bf)
        cr(:,n) = double(f{n}(:));
    else
        cr(:,n) = double(f{n}(:).*bf{n}(:));
    end
end
p  = ones(numel(f{1}),K);
for k=1:K
    amp    = mg(k)/sqrt((2*pi)^N * det(vr(:,:,k)));
    d      = bsxfun(@minus,cr,mn(:,k)')/chol(vr(:,:,k));
    p(:,k) = amp*exp(-0.5*sum(d.*d,2)) + eps;
end
return;

%==========================================================================
% function dat = decimate(dat,fwhm)
%==========================================================================
function dat = decimate(dat,fwhm)
% Convolve the volume in memory (fwhm in voxels).
lim = ceil(2*fwhm);
x  = -lim(1):lim(1); x = spm_smoothkern(fwhm(1),x); x  = x/sum(x);
y  = -lim(2):lim(2); y = spm_smoothkern(fwhm(2),y); y  = y/sum(y);
z  = -lim(3):lim(3); z = spm_smoothkern(fwhm(3),z); z  = z/sum(z);
i  = (length(x) - 1)/2;
j  = (length(y) - 1)/2;
k  = (length(z) - 1)/2;
spm_conv_vol(dat,dat,x,y,z,-[i j k]);
return;

%==========================================================================
% function y1 = affind(y0,M)
%==========================================================================
function y1 = affind(y0,M)
y1 = zeros(size(y0),'single');
for d=1:3
    y1(:,:,:,d) = y0(:,:,:,1)*M(d,1) + y0(:,:,:,2)*M(d,2) + y0(:,:,:,3)*M(d,3) + M(d,4);
end
return;

%==========================================================================
% function x = rgrid(d)
%==========================================================================
function x = rgrid(d)
x = zeros([d(1:3) 3],'single');
[x1,x2] = ndgrid(single(1:d(1)),single(1:d(2)));
for i=1:d(3)
    x(:,:,i,1) = x1;
    x(:,:,i,2) = x2;
    x(:,:,i,3) = single(i);
end
return;

%==========================================================================
% function [P] = clean_gwc(P,level)
%==========================================================================
function [P] = clean_gwc(P,level)
if nargin<2, level = 1; end

b    = P(:,:,:,2);

% Build a 3x3x3 seperable smoothing kernel
%--------------------------------------------------------------------------
kx=[0.75 1 0.75];
ky=[0.75 1 0.75];
kz=[0.75 1 0.75];
sm=sum(kron(kron(kz,ky),kx))^(1/3);
kx=kx/sm; ky=ky/sm; kz=kz/sm;

th1 = 0.15;
if level==2, th1 = 0.2; end
% Erosions and conditional dilations
%--------------------------------------------------------------------------
niter  = 32;
niter2 = 32;
spm_progress_bar('Init',niter+niter2,'Extracting Brain','Iterations completed');
for j=1:niter
    if j>2, th=th1; else th=0.6; end  % Dilate after two its of erosion
    for i=1:size(b,3)
        gp       = double(P(:,:,i,1));
        wp       = double(P(:,:,i,2));
        bp       = double(b(:,:,i))/255;
        bp       = (bp>th).*(wp+gp);
        b(:,:,i) = uint8(round(bp));
    end
    spm_conv_vol(b,b,kx,ky,kz,-[1 1 1]);
    spm_progress_bar('Set',j);
end

% Also clean up the CSF.
if niter2 > 0
    c = b;
    for j=1:niter2
        for i=1:size(b,3)
            gp       = double(P(:,:,i,1));
            wp       = double(P(:,:,i,2));
            cp       = double(P(:,:,i,3));
            bp       = double(c(:,:,i))/255;
            bp       = (bp>th).*(wp+gp+cp);
            c(:,:,i) = uint8(round(bp));
        end
        spm_conv_vol(c,c,kx,ky,kz,-[1 1 1]);
        spm_progress_bar('Set',j+niter);
    end
end

th = 0.05;
for i=1:size(b,3)
    slices = cell(1,size(P,4));
    for k1=1:size(P,4)
        slices{k1} = double(P(:,:,i,k1))/255;
    end
    bp        = double(b(:,:,i))/255;
    bp        = ((bp>th).*(slices{1}+slices{2}))>th;
    slices{1} = slices{1}.*bp;
    slices{2} = slices{2}.*bp;

    if niter2>0
        cp        = double(c(:,:,i))/255;
        cp        = ((cp>th).*(slices{1}+slices{2}+slices{3}))>th;
        slices{3} = slices{3}.*cp;
    end
    slices{5} = slices{5}+1e-4; % Add a little to the soft tissue class
    tot       = zeros(size(bp))+eps;
    for k1=1:size(P,4)
        tot   = tot + slices{k1};
    end
    for k1=1:size(P,4)
        P(:,:,i,k1) = uint8(round(slices{k1}./tot*255));
    end
end
spm_progress_bar('Clear');


function [interp] = ft_sourceinterpolate(cfg, functional, anatomical)

% FT_SOURCEINTERPOLATE interpolates source activity or statistical maps onto the
% voxels or vertices of an anatomical description of the brain.  Both the functional
% and the anatomical data can either describe a volumetric 3D regular grid, a
% triangulated description of the cortical sheet or an arbitrary cloud of points.
%
% The functional data in the output data will be interpolated at the locations at
% which the anatomical data are defined. For example, if the anatomical data was
% volumetric, the output data is a volume-structure, containing the resliced source
% and the anatomical volume that can be visualized using FT_SOURCEPLOT or written to
% file using FT_SOURCEWRITE.
%
% The following scenarios can be considered:
%
% - Both functional data and anatomical data are defined on 3D regular grids, for
%   example with a low-res grid for the functional data and a high-res grid for the
%   anatomy.
%
% - The functional data is defined on a 3D regular grid and the anatomical data is
%   defined on an irregular point cloud, which can be a 2D triangulated surface mesh.
%
% - The functional data is defined on an irregular point cloud, which can be a 2D
%   triangulated surface mesh, and the anatomical data is defined on a 3D regular grid.
%
% - Both the functional and the anatomical data are defined on an irregular point
%   cloud, which can be a 2D triangulated mesh.
%
% - The functional data is defined on a low-resolution 2D triangulated surface mesh and the
%   anatomical data is defined on a high-resolution 2D triangulated surface mesh, where the
%   low-res vertices form a subset of the high-res vertices. This allows for mesh-based
%   interpolation. The algorithm currently implemented is so-called 'smudging' as it is
%   also applied by the MNE-suite software.
%
% Use as
%   [interp] = ft_sourceinterpolate(cfg, source, anatomy)
%   [interp] = ft_sourceinterpolate(cfg, stat,   anatomy)
% where
%   source  is the output of FT_SOURCEANALYSIS
%   stat    is the output of FT_SOURCESTATISTICS
%   anatomy is the output of FT_READ_MRI, or one of the FT_VOLUMExxx functions,
%           or a cortical sheet that was read with FT_READ_HEADSHAPE,
%           or a regular 3D grid created with FT_PREPARE_SOURCEMODEL.
%
% The configuration should contain:
%   cfg.parameter     = string or cell-array with the functional parameter(s) to be interpolated
%   cfg.downsample    = integer number (default = 1, i.e. no downsampling)
%   cfg.interpmethod  = string, can be 'nearest', 'linear', 'cubic',  'spline', 'sphere_avg', 'sphere_weighteddistance', or 'smudge' (default = 'linear for interpolating two 3D volumes, 'nearest' for all other cases)
%
% For interpolating two 3D regular grids or volumes onto each other the supported
% interpolation methods are 'nearest', 'linear', 'cubic' or 'spline'. For all other
% cases the supported interpolation methods are 'nearest', 'sphere_avg',
% 'sphere_weighteddistance' or 'smudge'.
%
% The functional and anatomical data should be expressed in the same
% coordinate sytem, i.e. either both in MEG headcoordinates (NAS/LPA/RPA)
% or both in SPM coordinates (AC/PC).
%
% To facilitate data-handling and distributed computing you can use
%   cfg.inputfile   =  ...
%   cfg.outputfile  =  ...
% If you specify one of these (or both) the input data will be read from a *.mat
% file on disk and/or the output data will be written to a *.mat file. These mat
% files should contain only a single variable, corresponding with the
% input/output structure.
%
% See also FT_READ_MRI, FT_READ_HEADSHAPE, FT_SOURCEPLOT, FT_SOURCEANALYSIS,
% FT_SOURCEWRITE

% Copyright (C) 2003-2007, Robert Oostenveld
% Copyright (C) 2011-2014, Jan-Mathijs Schoffelen
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    FieldTrip is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

% these are used by the ft_preamble/ft_postamble function and scripts
ft_revision = '$Id$';
ft_nargin   = nargin;
ft_nargout  = nargout;

% do the general setup of the function
ft_defaults
ft_preamble init
ft_preamble debug
ft_preamble loadvar functional anatomical
ft_preamble provenance functional anatomical

% the ft_abort variable is set to true or false in ft_preamble_init
if ft_abort
  return
end

% this is not supported any more as of 26/10/2011
if ischar(anatomical)
  ft_error('please use cfg.inputfile instead of specifying the input variable as a sting');
end

% check if the input cfg is valid for this function
cfg = ft_checkconfig(cfg, 'unused',     {'keepinside' 'voxelcoord'});
cfg = ft_checkconfig(cfg, 'deprecated', {'sourceunits', 'mriunits'});
cfg = ft_checkconfig(cfg, 'required',   'parameter');
cfg = ft_checkconfig(cfg, 'renamedval', {'parameter', 'avg.pow', 'pow'});
cfg = ft_checkconfig(cfg, 'renamedval', {'parameter', 'avg.coh', 'coh'});
cfg = ft_checkconfig(cfg, 'renamedval', {'parameter', 'avg.mom', 'mom'});

% set the defaults
cfg.downsample   = ft_getopt(cfg, 'downsample', 1);
cfg.feedback     = ft_getopt(cfg, 'feedback', 'text');
cfg.interpmethod = ft_getopt(cfg, 'interpmethod', []);   % cfg.interpmethod depends on how the interpolation should be done and actual defaults will be specified below

% replace pnt by pos
anatomical = fixpos(anatomical);
functional = fixpos(functional);

% ensure the functional data to be in double precision, the maxdepth parameter ensure double precision up to the content of functional.avg.mom{:}, avoiding too much recursion
functional = ft_struct2double(functional, 3);

if (strcmp(cfg.interpmethod, 'nearest') || strcmp(cfg.interpmethod, 'mode')) && (ft_datatype(functional, 'volume+label') || ft_datatype(functional, 'source+label') || ft_datatype(functional, 'mesh+label'))
  % the first input argument describes a parcellation or segmentation with tissue labels
  isAtlasFun = true;
else
  isAtlasFun = false;
end

if isfield(anatomical, 'transform') && isfield(anatomical, 'dim')
  % anatomical volume
  isUnstructuredAna  = false;
elseif isfield(anatomical, 'pos') && isfield(anatomical, 'dim')
  % positions that can be mapped onto a 3D regular grid
  isUnstructuredAna  = false;
elseif isfield(anatomical, 'pos')
  % anatomical data that consists of a mesh, but no smudging possible
  isUnstructuredAna  = true;
end

if isfield(functional, 'transform') && isfield(functional, 'dim')
  % functional volume
  isUnstructuredFun  = false;
elseif isfield(functional, 'pos') && isfield(functional, 'dim')
  % positions that can be mapped onto a 3D regular grid
  isUnstructuredFun  = false;
else
  isUnstructuredFun  = true;
end

if isUnstructuredAna
  anatomical = ft_checkdata(anatomical, 'datatype', {'source', 'source+label', 'mesh'}, 'insidestyle', 'logical', 'feedback', 'yes', 'hasunit', 'yes');
else
  anatomical = ft_checkdata(anatomical, 'datatype', {'volume', 'volume+label'}, 'insidestyle', 'logical', 'feedback', 'yes', 'hasunit', 'yes');
end

if isUnstructuredFun
  functional = ft_checkdata(functional, 'datatype', 'source', 'insidestyle', 'logical', 'feedback', 'yes', 'hasunit', 'yes');
else
  functional = ft_checkdata(functional, 'datatype', 'volume', 'insidestyle', 'logical', 'feedback', 'yes', 'hasunit', 'yes');
end

% select the parameters from the data, this needs to be done here, because after running checkdata, the parameterselection fails if the numeric data has nfreq/ntime/etc>1
cfg.parameter = parameterselection(cfg.parameter, functional);

% ensure that the functional data has the same unit as the anatomical data
functional = ft_convert_units(functional, anatomical.unit);

if isfield(functional, 'coordsys') && isfield(anatomical, 'coordsys') && ~isequal(functional.coordsys, anatomical.coordsys)
  % FIXME is this different when smudged or not?
  % ft_warning('the coordinate systems are not aligned');
  % ft_error('the coordinate systems are not aligned');
end

if ~isUnstructuredAna && cfg.downsample~=1
  % downsample the anatomical volume
  tmpcfg = keepfields(cfg, {'downsample', 'showcallinfo', 'trackcallinfo', 'trackusage', 'trackdatainfo', 'trackmeminfo', 'tracktimeinfo', 'checksize'});
  tmpcfg.parameter = 'anatomy';
  anatomical = ft_volumedownsample(tmpcfg, anatomical);
  % restore the provenance information and put back cfg.parameter
  tmpparameter = cfg.parameter;
  [cfg, anatomical] = rollback_provenance(cfg, anatomical);
  cfg.parameter = tmpparameter;
end

% collect the functional volumes that should be converted
dat_name = {};
dat_array = {};
for i=1:length(cfg.parameter)
  if ~iscell(getsubfield(functional, cfg.parameter{i}))
    dat_name{end+1} = cfg.parameter{i};
    dat_array{end+1} = getsubfield(functional, cfg.parameter{i});
  else
    fprintf('not interpolating %s, since it is represented in a cell-array\n', cfg.parameter{i});
  end
end

% hmmmm, if the input data contains a time and/or freq dimension, then the output
% may be terribly blown up; most convenient would be to output only the
% smudging matrix, and project the data when plotting

if isUnstructuredFun && isUnstructuredAna && isfield(anatomical, 'orig') && isfield(anatomical.orig, 'pos') && isfield(anatomical.orig, 'tri')
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % functional data defined on subset of vertices in an anatomical mesh
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  % FIXME this should not be decided on the basis of the data structures but on the basis of the cfg.interpmethod option
  % FIXME the distribution of 3 geometries over the 2 structures is weird
  % FIXME a (perhaps extreme) application of this would be to interpolate data from parcels on the sheet, i.e. an inverse parcellation

  % anatomical data consists of a decimated triangulated mesh, containing
  % the original description, allowing for smudging.

  % smudge the low resolution functional data according to the strategy in
  % MNE-suite (chapter 8.3 of the manual)

  interpmat = interp_ungridded(anatomical.pos, anatomical.orig.pos, 'projmethod', 'smudge', 'triout', anatomical.orig.tri);
  interpmat(~anatomical.inside(:), :) = 0;

  % start with an empty structure, keep only some fields
  interp = keepfields(functional, {'time', 'freq'});
  interp = copyfields(anatomical, interp, {'coordsys', 'unit'});
  interp = copyfields(anatomical.orig, interp, {'pos', 'tri'});

  % identify the inside voxels after interpolation
  nzeros     = sum(interpmat~=0,2);
  newinside  = (nzeros~=0);
  newoutside = (nzeros==0);

  interp.inside = false(size(anatomical.pos,1),1);
  interp.inside(newinside) = true;

  % interpolate all functional data
  for i=1:length(dat_name)
    fprintf('interpolating %s\n', dat_name{i});

    dimord = getdimord(functional, dat_name{i});
    dimtok = tokenize(dimord, '_');
    dimf   = getdimsiz(functional, dat_name{i});
    dimf(end+1:length(dimtok)) = 1; % there can be additional trailing singleton dimensions

    % should be 3-D array, can have trailing singleton dimensions
    if numel(dimf)<2
      dimf(2) = 1;
    end
    if numel(dimf)<3
      dimf(3) = 1;
    end

    allav = zeros([size(anatomical.orig.pos,1), dimf(2:end)]);
    for k=1:dimf(2)
      for m=1:dimf(3)
        fv     = double_ifnot(dat_array{i}(:,k,m));
        av     = interpmat*fv;
        av(newoutside) = nan;
        allav(:,k,m)   = av;
      end
    end
    interp = setsubfield(interp, dat_name{i}, allav);
  end

elseif isUnstructuredFun && isUnstructuredAna
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % functional data defined on a point cloud/mesh, anatomy on a point cloud/mesh
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  % set default interpmethod for this situation
  cfg.interpmethod = ft_getopt(cfg, 'interpmethod', 'nearest');
  cfg.sphereradius = ft_getopt(cfg, 'sphereradius', 0.5);
  cfg.power        = ft_getopt(cfg, 'power',        1);

  interpmat = interp_ungridded(functional.pos, anatomical.pos, 'projmethod', cfg.interpmethod, 'sphereradius', cfg.sphereradius, 'power', cfg.power); % FIXME include other key-value pairs as well
  interpmat(~anatomical.inside(:), :) = 0;

  % start with an empty structure, keep only some fields
  interp = keepfields(functional, {'time', 'freq'});
  interp = copyfields(anatomical, interp, {'pos', 'tri', 'dim', 'transform', 'coordsys', 'unit'});

  % identify the inside voxels after interpolation
  nzeros     = sum(interpmat~=0,2);
  newinside  = (nzeros~=0);
  newoutside = (nzeros==0);

  interp.inside = false(size(anatomical.pos,1),1);
  interp.inside(newinside) = true;

  % interpolate all functional data
  for i=1:length(dat_name)
    fprintf('interpolating %s\n', dat_name{i});

    dimord = getdimord(functional, dat_name{i});
    dimtok = tokenize(dimord, '_');
    dimf   = getdimsiz(functional, dat_name{i});
    dimf(end+1:length(dimtok)) = 1; % there can be additional trailing singleton dimensions

    % should be 3-D array, can have trailing singleton dimensions
    if numel(dimf)<2
      dimf(2) = 1;
    end
    if numel(dimf)<3
      dimf(3) = 1;
    end

    allav = zeros([size(anatomical.pos,1), dimf(2:end)]);
    for k=1:dimf(2)
      for m=1:dimf(3)
        fv     = double_ifnot(dat_array{i}(:,k,m));
        av     = interpmat*fv;
        av(newoutside) = nan;
        allav(:,k,m)   = av;
      end
    end
    interp = setsubfield(interp, dat_name{i}, allav);
  end

elseif isUnstructuredFun && ~isUnstructuredAna
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % functional data defined on a point cloud/mesh, anatomy on a volume
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  % set default interpmethod for this situation
  cfg.interpmethod = ft_getopt(cfg, 'interpmethod', 'nearest');
  cfg.sphereradius = ft_getopt(cfg, 'sphereradius', 0.5);
  cfg.power        = ft_getopt(cfg, 'power',        1);

  [ax, ay, az] = voxelcoords(anatomical.dim, anatomical.transform);
  anatomical.pos = [ax(:) ay(:) az(:)];
  clear ax ay az

  tmp = interp_ungridded(functional.pos, anatomical.pos(anatomical.inside,:), 'projmethod', cfg.interpmethod, 'sphereradius', cfg.sphereradius, 'power', cfg.power); % FIXME include other key-value pairs as well
  interpmat( anatomical.inside(:), :) = tmp;
  interpmat(~anatomical.inside(:), :) = 0;

  % start with an empty structure, keep only some fields
  interp = keepfields(functional, {'time', 'freq'});
  interp = copyfields(anatomical, interp, {'pos', 'tri', 'dim', 'transform', 'coordsys', 'unit', 'anatomy'});

  % identify the inside voxels after interpolation
  nzeros     = sum(interpmat~=0,2);
  newinside  = (nzeros~=0);
  newoutside = (nzeros==0);

  interp.inside = false(anatomical.dim);
  interp.inside(newinside) = true;

  % interpolate all functional data
  for i=1:length(dat_name)
    fprintf('interpolating %s\n', dat_name{i});

    dimord = getdimord(functional, dat_name{i});
    dimtok = tokenize(dimord, '_');
    dimf   = getdimsiz(functional, dat_name{i});
    dimf(end+1:length(dimtok)) = 1; % there can be additional trailing singleton dimensions

    % should be 3-D array, can have trailing singleton dimensions
    if numel(dimf)<2
      dimf(2) = 1;
    end
    if numel(dimf)<3
      dimf(3) = 1;
    end

    av    = zeros([anatomical.dim            ]);
    allav = zeros([anatomical.dim dimf(2:end)]);

    for k=1:dimf(2)
      for m=1:dimf(3)
        fv     = double_ifnot(dat_array{i}(:,k,m));
        av(:)  = interpmat*fv;
        av(newoutside)   = nan;
        allav(:,:,:,k,m) = av;
      end
    end
    if isfield(interp, 'freq') || isfield(interp, 'time')
      % the output should be a source representation, not a volume
      allav = reshape(allav, prod(anatomical.dim), dimf(2), dimf(3));
    end
    interp = setsubfield(interp, dat_name{i}, allav);
  end

  if ~isfield(interp, 'freq') && ~isfield(interp, 'time')
      % the output should be a volume representation, not a source
    interp = rmfield(interp, 'pos');
  end

elseif ~isUnstructuredFun && isUnstructuredAna
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % functional data defined on a volume, anatomy on a point cloud/mesh
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  % set default interpmethod for this situation
  cfg.interpmethod = ft_getopt(cfg, 'interpmethod', 'nearest');
  cfg.sphereradius = ft_getopt(cfg, 'sphereradius', []);
  cfg.power        = ft_getopt(cfg, 'power',        1);

  % interpolate the 3D volume onto the anatomy
  if ~strcmp(cfg.interpmethod, 'project')
    % use interp_gridded
    [interpmat, dummy] = interp_gridded(functional.transform, zeros(functional.dim), anatomical.pos, 'projmethod', cfg.interpmethod, 'sphereradius', cfg.sphereradius, 'inside', functional.inside, 'power', cfg.power);

    % use interp_ungridded
    % interpmat = interp_ungridded(functional.pos, anatomical.pos, 'projmethod', cfg.interpmethod, 'sphereradius', cfg.sphereradius, 'inside', functional.inside, 'power', cfg.power);
  else
    % do the interpolation below, the current implementation of the
    % 'project' method does not output an interpmat (and is therefore quite
    % inefficient

    % set the defaults
    cfg.projvec        = ft_getopt(cfg, 'projvec',       1);
    cfg.projweight     = ft_getopt(cfg, 'projweight',    ones(size(cfg.projvec)));
    cfg.projcomb       = ft_getopt(cfg, 'projcomb',      'mean'); % or max
    cfg.projthresh     = ft_getopt(cfg, 'projthresh',    []);
  end

  % start with an empty structure, keep some fields
  interp = keepfields(functional, {'time', 'freq'});
  interp = copyfields(anatomical, interp, {'pos', 'tri', 'dim', 'transform', 'coordsys', 'unit'});

  % identify the inside voxels after interpolation
  interp.inside    = true(size(anatomical.pos,1),1);

  % interpolate all functional data
  for i=1:length(dat_name)
    fprintf('interpolating %s\n', dat_name{i});

    dimord = getdimord(functional, dat_name{i});
    dimtok = tokenize(dimord, '_');
    dimf   = getdimsiz(functional, dat_name{i});
    dimf(end+1:length(dimtok)) = 1; % there can be additional trailing singleton dimensions

    if prod(functional.dim)==dimf(1)
      % convert into 3-D, 4-D or 5-D array
      dimf        = [functional.dim dimf(2:end)];
      dat_array{i} = reshape(dat_array{i}, dimf);
    end

    % should be 5-D array, can have trailing singleton dimensions
    if numel(dimf)<4
      dimf(4) = 1;
    end
    if numel(dimf)<5
      dimf(5) = 1;
    end

    allav = zeros([size(anatomical.pos,1), dimf(4:end)]);
    if ~strcmp(cfg.interpmethod, 'project')
      for k=1:dimf(4)
        for m=1:dimf(5)
          fv    = double_ifnot(dat_array{i}(:,:,:,k,m)); % ensure double precision to allow sparse multiplication
          fv    = fv(functional.inside(:));
          av    = interpmat*fv;
          allav(:,k,m) = av;
        end
      end
    else
      for k=1:dimf(4)
        for m=1:dimf(5)
          fv   = double_ifnot(dat_array{i}(:,:,:,k,m));
          av   = interp_gridded(functional.transform, fv, anatomical.pos, 'dim', functional.dim, 'projmethod', 'project', 'projvec', cfg.projvec, 'projweight', cfg.projweight, 'projcomb', cfg.projcomb, 'projthresh', cfg.projthresh);
          allav(:,k,m) = av;
        end
      end
    end
    interp = setsubfield(interp, dat_name{i}, allav);
  end

elseif ~isUnstructuredFun && ~isUnstructuredAna
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % functional data defined on a volume, anatomy on a differently sampled volume
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

  % set default interpmethod for this situation
  cfg.interpmethod = ft_getopt(cfg, 'interpmethod', 'linear');
  if isequal(cfg.interpmethod, 'mode') && isAtlasFun
    % use a mode-based interpolation, i.e. a majority vote of the nearby
    % voxel locations.

    % first to a nearest interpolation of the voxel coordinates the other
    % way around
    tmp = anatomical;
    [tx, ty, tz] = voxelcoords(tmp.dim, tmp.transform);
    tmp.tx = tx;
    tmp.ty = ty;
    tmp.tz = tz;

    tmpcfg = [];
    tmpcfg.interpmethod = 'nearest';
    tmpcfg.parameter = {'tx' 'ty' 'tz'};
    tmpint = ft_sourceinterpolate(tmpcfg, tmp, functional);

    [ix,i1,i2] = intersect([tx(:) ty(:) tz(:)],[tmpint.tx(:) tmpint.ty(:) tmpint.tz(:)],'rows');

    interp = keepfields(anatomical, {'pos', 'dim', 'transform', 'coordsys', 'unit',  'anatomy'});
    interp.inside = false(interp.dim);
    interp.inside(i1) = true;

    for k = 1:numel(cfg.parameter)
      interp.(cfg.parameter{k}) = nan(interp.dim);
      fun = functional.(cfg.parameter{k});
      for m = 1:numel(i1)
        values = reshape(fun(tmpint.tx==tx(i1(m)) & tmpint.ty==ty(i1(m)) & tmpint.tz==tz(i1(m))),[],1);
        [M,f,c] = mode(values);
        if numel(c)==1
          interp.(cfg.parameter{k})(i1(m)) = M;
        else
          ft_warning('multiple modes per voxel, returning NaN');
          interp.(cfg.parameter{k})(i1(m)) = nan;
        end
      end
    end

  elseif isequal(cfg.interpmethod, 'mode') && ~isAtlasFun
    ft_error('the interpolation method ''mode'' is only supported for parcellations');

  else
    % start with an empty structure, keep some fields
    interp = keepfields(functional, {'time', 'freq'});
    interp = copyfields(anatomical, interp, {'pos', 'dim', 'transform', 'coordsys', 'unit', 'anatomy'});

    % convert the anatomical voxel positions into voxel indices into the functional volume
    anatomical.transform = functional.transform \ anatomical.transform;
    functional.transform = eye(4);

    [fx, fy, fz] = voxelcoords(functional.dim, functional.transform);
    [ax, ay, az] = voxelcoords(anatomical.dim, anatomical.transform);

    % estimate the subvolume of the anatomy that is spanned by the functional volume
    minfx = 1;
    minfy = 1;
    minfz = 1;
    maxfx = functional.dim(1);
    maxfy = functional.dim(2);
    maxfz = functional.dim(3);
    sel = ax(:)>=minfx & ...
      ax(:)<=maxfx & ...
      ay(:)>=minfy & ...
      ay(:)<=maxfy & ...
      az(:)>=minfz & ...
      az(:)<=maxfz;
    fprintf('selecting subvolume of %.1f%%\n', 100*sum(sel)./prod(anatomical.dim));

    if all(functional.inside(:))
      % keep all voxels marked as inside
      interp.inside = true(anatomical.dim);
    else
      % reslice and interpolate inside
      interp.inside = zeros(anatomical.dim);
      % interpolate with method nearest
      interp.inside( sel) = my_interpn(double(functional.inside), ax(sel), ay(sel), az(sel), 'nearest', cfg.feedback);
      interp.inside(~sel) = 0;
      interp.inside = logical(interp.inside);
    end

    % prepare the grid that is used in the interpolation
    fg = [fx(:) fy(:) fz(:)];
    clear fx fy fz

    % reslice and interpolate all functional volumes
    for i=1:length(dat_name)
      fprintf('reslicing and interpolating %s\n', dat_name{i});

      dimord = getdimord(functional, dat_name{i});
      dimtok = tokenize(dimord, '_');
      dimf   = getdimsiz(functional, dat_name{i});
      dimf(end+1:length(dimtok)) = 1; % there can be additional trailing singleton dimensions

      if prod(functional.dim)==dimf(1)
        % convert into 3-D, 4-D or 5-D array
        dimf = [functional.dim dimf(2:end)];
        dat_array{i} = reshape(dat_array{i}, dimf);
      end

      % should be 5-D array, can have trailing singleton dimensions
      if numel(dimf)<4
        dimf(4) = 1;
      end
      if numel(dimf)<5
        dimf(5) = 1;
      end

      av    = zeros([anatomical.dim            ]);
      allav = zeros([anatomical.dim dimf(4:end)]);
      functional.inside = functional.inside(:,:,:,1,1);

      if any(dimf(4:end)>1) && ~strcmp(cfg.feedback, 'none')
        % this is needed to prevent feedback to be displayed for every time-frequency point
        ft_warning('disabling feedback');
        cfg.feedback = 'none';
      end

      for k=1:dimf(4)
        for m=1:dimf(5)
          fv = double_ifnot(dat_array{i}(:,:,:,k,m));
          % av( sel) = my_interpn(fx, fy, fz, fv, ax(sel), ay(sel), az(sel), cfg.interpmethod, cfg.feedback);
          if islogical(dat_array{i})
            % interpolate always with method nearest
            av( sel) = my_interpn(fv, ax(sel), ay(sel), az(sel), 'nearest', cfg.feedback);
            av = logical(av);
          else
            if ~all(functional.inside(:))
              % extrapolate the outside of the functional volumes for better interpolation at the edges
              fv(~functional.inside) = griddatan(fg(functional.inside(:), :), fv(functional.inside(:)), fg(~functional.inside(:), :), 'nearest');
            end
            % interpolate functional onto anatomical grid
            av( sel) = my_interpn(fv, ax(sel), ay(sel), az(sel), cfg.interpmethod, cfg.feedback);
            av(~sel) = nan;
            av(~interp.inside) = nan;
          end
          allav(:,:,:,k,m) = av;
        end
      end
      if isfield(interp, 'freq') || isfield(interp, 'time')
        % the output should be a source representation, not a volume
        allav = reshape(allav, prod(anatomical.dim), dimf(4), dimf(5));
      end
      interp = setsubfield(interp, dat_name{i}, allav);
      % keep the description of the labels in the segmentation/parcellation
      if strcmp(cfg.interpmethod, 'nearest') && isfield(functional, [dat_name{i} 'label'])
        interp.([dat_name{i} 'label']) = functional.([dat_name{i} 'label']);
      end
    end

  end

end % computing the interpolation according to the input data

if isfield(interp, 'freq') || isfield(interp, 'time')
  % the output should be a source representation, not a volumetric representation
  if ~isfield(interp, 'pos')
    [x, y, z] = voxelcoords(interp.dim, interp.transform);
    interp.pos = [x(:) y(:) z(:)];
  end
end

if isAtlasFun
  for i=1:numel(dat_name)
    % keep the labels that describe the different tissue types
    interp = copyfields(functional, interp, [dat_name{i} 'label']);
    % replace NaNs that fall outside the labeled area with zero
    tmp = interp.(dat_name{i});
    tmp(isnan(tmp)) = 0;
    interp.(dat_name{i}) = tmp;
  end
  % remove the inside field if present
  interp = removefields(interp, 'inside');
end

if exist('interpmat', 'var')
  cfg.interpmat = interpmat;
  cfg.interpmat; % access it once to fool the cfg-tracking
end

% do the general cleanup and bookkeeping at the end of the function
ft_postamble debug
ft_postamble previous   functional anatomical
ft_postamble provenance interp
ft_postamble history    interp
ft_postamble savevar    interp

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION this function computes the location of all voxels in head
% coordinates in a memory efficient manner
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [x, y, z] = voxelcoords(dim, transform)

xgrid     = 1:dim(1);
ygrid     = 1:dim(2);
zgrid     = 1:dim(3);
npix      = prod(dim(1:2));  % number of voxels in a single slice

x = zeros(dim);
y = zeros(dim);
z = zeros(dim);
X = zeros(1,npix);
Y = zeros(1,npix);
Z = zeros(1,npix);
E = ones(1,npix);
% determine the voxel locations per slice
for i=1:dim(3)
  [X(:), Y(:), Z(:)] = ndgrid(xgrid, ygrid, zgrid(i));
  tmp = transform*[X; Y; Z; E];
  x((1:npix)+(i-1)*npix) = tmp(1,:);
  y((1:npix)+(i-1)*npix) = tmp(2,:);
  z((1:npix)+(i-1)*npix) = tmp(3,:);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION for memory efficient interpolation
% the only reason for this function is that it does the interpolation in smaller chuncks
% this prevents memory problems that I often encountered here
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% function [av] = my_interpn(fx, fy, fz, fv, ax, ay, az, interpmethod, feedback);
function [av] = my_interpn(fv, ax, ay, az, interpmethod, feedback)

num = numel(ax);            % total number of voxels
blocksize = floor(num/20);  % number of voxels to interpolate at once, split it into 20 chuncks
lastblock = 0;              % boolean flag for while loop
sel = 1:blocksize;          % selection of voxels that are interpolated, this is the first chunck
av  = zeros(size(ax));
ft_progress('init', feedback, 'interpolating');
while (1)
  ft_progress(sel(1)/num, 'interpolating %.1f%%\n', 100*sel(1)/num);
  if sel(end)>=num
    sel = sel(1):num;
    lastblock = 1;
  end
  av(sel) = interpn(fv, ax(sel), ay(sel), az(sel), interpmethod);
  if lastblock
    break
  end
  sel = sel + blocksize;
end
ft_progress('close');

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION to cast array to double precision, only if needed
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function out = double_ifnot(in)

if ~isa(in, 'double')
  out = double(in);
else
  out = in;
end


function [out] = spm_eeg_displayECD(Pos,Orient,Var,Names,options)
% Plot dipole positions onto the SPM canonical mesh
% FORMAT [out] = spm_eeg_displayECD(Pos,Orient,Var,Names,options)
%
% IN (admissible choices):
%   - Pos: a 3xndip matrix containing the positions of the dipoles in
%   the canonical frame of reference
%   - Orient: the same with dipole orientations
%   - Var: the same with position variance
%   - Names: the same with dipole names
%   - options: an optional structure containing
%       .hfig: the handle of the display figure
%       .tag: the tag to be associated with the created UI objects
%       .add: binary variable ({0}, 1: just add dipole in the figure .hfig)
%
% OUT:
%   - out: a structure containing the handles of the object in the figure
%   (including the mesh, the dipoles, the transparency slider, etc...)
%__________________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% Jean Daunizeau
% $Id: spm_eeg_displayECD.m 5737 2013-11-10 20:23:49Z karl $

% checks and defaults
%--------------------------------------------------------------------------
hfig       = [];
ParentAxes = [];
query      = [];
handles    = [];
tag        = '';
try, options; catch, options = [];      end
try, hfig        = options.hfig;        end
try, tag         = options.tag;         end
try, ParentAxes  = options.ParentAxes;  end
try, query       = options.query;       end
try, handles     = options.handles;     end
try
    figure(hfig);
catch
    hfig  = spm_figure('GetWin','Graphics');
    spm_figure('Clear',hfig);
    ParentAxes = axes('parent',hfig);
end
try
    markersize = options.markersize;
catch
    markersize = 20;
end
try
    meshsurf = options.meshsurf;
catch
    meshsurf = fullfile(spm('Dir'),'canonical','cortex_5124.surf.gii');
end

if isscalar(Var), Var = Pos*0 + Var^2;   end
try, Pos{1};    catch, Pos = {Pos};      end
try, Orient{1}; catch, Orient = {Orient};end
try, Var{1};    catch, Var = {Var};      end

ndip = size(Pos{1},2);
if ~exist('Names','var') || isempty(Names)
    for i=1:ndip
        Names{i} = num2str(i);
    end
end

col = ['b','g','r','c','m','y','k','w'];
tmp = ceil(ndip./numel(col));
col = repmat(col,1,tmp);
pa  = get(ParentAxes,'position');

if ndip > 0

    if isempty(query)
        opt.hfig = hfig;
        opt.ParentAxes = ParentAxes;
        opt.visible = 'off';
        pos2 = [pa(1),pa(2)+0.25*pa(4),0.03,0.5*pa(4)];
        out  = spm_eeg_render(meshsurf,opt);
        handles.mesh = out.handles.p;
        handles.BUTTONS.transp = out.handles.transp;
        handles.hfig = out.handles.fi;
        handles.ParentAxes  = out.handles.ParentAxes;
        set(handles.mesh,...
            'facealpha',0.1,...
            'visible','on')
        set(handles.BUTTONS.transp,...
            'value',0.1,...
            'position',pos2,...
            'visible','on')
    end

    set(ParentAxes,'nextplot','add')
    for j=1:length(Pos)
        for i =1:ndip
            try
                set(handles.hp(j,i),...
                    'xdata',Pos{j}(1,i),...
                    'ydata',Pos{j}(2,i),...
                    'zdata',Pos{j}(3,i));
            catch
                handles.hp(j,i) = plot3(handles.ParentAxes,...
                    Pos{j}(1,i),Pos{j}(2,i),Pos{j}(3,i),...
                    [col(i),'.'],...
                    'markerSize',markersize,...
                    'visible','off');
            end
            try
                no = sqrt(sum(Orient{j}(:,i).^2));
                if no > 0
                    Oi = 1e1.*Orient{j}(:,i)./no;
                else
                    Oi = 1e-5*ones(3,1);
                end
                try
                    set(handles.hq(j,i),...
                        'xdata',Pos{j}(1,i),...
                        'ydata',Pos{j}(2,i),...
                        'zdata',Pos{j}(3,i),...
                        'udata',Oi(1),...
                        'vdata',Oi(2),...
                        'wdata',Oi(3))
                catch
                    handles.hq(j,i) = quiver3(handles.ParentAxes,...
                        Pos{j}(1,i),Pos{j}(2,i),Pos{j}(3,i),...
                        Oi(1),Oi(2),Oi(3),col(i),...
                        'lineWidth',2,'visible','off');
                end
                if isequal(query,'add')
                    set(handles.hq(j,i),...
                        'LineStyle','--',...
                        'lineWidth',1)
                end
            end
            [x,y,z]= ellipsoid(Pos{j}(1,i),Pos{j}(2,i),Pos{j}(3,i),...
                1.*sqrt(Var{j}(1,i)),1.*sqrt(Var{j}(2,i)),1.*sqrt(Var{j}(1,i)),20);
            try
                set(handles.hs(j,i),...
                    'xdata',x,...
                    'ydata',y,...
                    'zdata',z);
            catch
                handles.hs(j,i) = surf(handles.ParentAxes,...
                    x,y,z,...
                    'edgecolor','none',...
                    'facecolor',col(i),...
                    'facealpha',0.2,...
                    'visible','off');
            end
            try
                set(handles.ht(j,i),...
                    'position',Pos{j}(:,i));
            catch
                handles.ht(j,i) = text(...
                    Pos{j}(1,i),Pos{j}(2,i),Pos{j}(3,i),...
                    Names{i},...
                    'Parent',handles.ParentAxes,...
                    'visible','off');
            end
        end
    end

    if length(Pos) > 1

        try, set(handles.hp(end,:),'visible','on'); end
        try, set(handles.hq(end,:),'visible','on'); end
        try, set(handles.hs(end,:),'visible','on'); end
        try, set(handles.ht(end,:),'visible','on'); end

        handles.uic(1) = uicontrol(handles.fi,...
            'units','normalized',...
            'position',[0.45,0.5,0.2,0.03],...
            'style','radio','string','Show priors',...
            'callback',@doChange1,...
            'BackgroundColor',[1 1 1],...
            'tooltipstring','Display prior locations',...
            'userdata',handles,'value',0,...
            'BusyAction','cancel',...
            'Interruptible','off',...
            'tag','plotEEG');

        handles.uic(2) = uicontrol(handles.fi,...
            'units','normalized',...
            'position',[0.45,0.53,0.2,0.03],...
            'style','radio','string','Show posteriors',...
            'callback',@doChange2,...
            'BackgroundColor',[1 1 1],...
            'tooltipstring','Display posterior locations',...
            'userdata',handles,'value',1,...
            'BusyAction','cancel',...
            'Interruptible','off',...
            'tag','plotEEG');

    else

        try, set(handles.hp(1,:),'visible','on'); end
        try, set(handles.hq(1,:),'visible','on'); end
        try, set(handles.hs(1,:),'visible','on'); end
        try, set(handles.ht(1,:),'visible','on'); end

    end

end

try
    clear out
    out.handles = handles;
catch
    out = [];
end

%==========================================================================
function doChange1(i1,i2)
val = get(i1,'value');
handles = get(i1,'userdata');
if ~val
    try, set(handles.hp(1,:),'visible','off'); end
    try, set(handles.hq(1,:),'visible','off'); end
    try, set(handles.hs(1,:),'visible','off'); end
    try, set(handles.ht(1,:),'visible','off'); end
else
    try, set(handles.hp(1,:),'visible','on');  end
    try, set(handles.hq(1,:),'visible','on');  end
    try, set(handles.hs(1,:),'visible','on');  end
    try, set(handles.ht(1,:),'visible','on');  end
end

%==========================================================================
function doChange2(i1,i2)
val = get(i1,'value');
handles = get(i1,'userdata');
if ~val
    try, set(handles.hp(2,:),'visible','off'); end
    try, set(handles.hq(2,:),'visible','off'); end
    try, set(handles.hs(2,:),'visible','off'); end
    try, set(handles.ht(2,:),'visible','off'); end
else
    try, set(handles.hp(2,:),'visible','on');  end
    try, set(handles.hq(2,:),'visible','on');  end
    try, set(handles.hs(2,:),'visible','on');  end
    try, set(handles.ht(2,:),'visible','on');  end
end


function dicomrt_plotgammamap2dvol(gamma,gamma_xmesh,gamma_ymesh,gamma_zmesh,ct,ct_xmesh,ct_ymesh,ct_zmesh,VOI,voi2use)
% dicomrt_plotgammamap2dvol(gamma,gamma_xmesh,gamma_ymesh,gamma_zmesh,ct,ct_xmesh,ct_ymesh,ct_zmesh,VOI,voi2use)
%
% Plot gamma maps.
%
% gamma is the ganmma matrix
% gamma_xmesh,gamma_ymesh,gamma_zmesh are x-y-z coordinates of the center of the gamma-pixel
% ct is the 3D CT dataset (OPTIONAL)
% ct_xmesh, ct_ymesh, ct_zmesh are the coordinates of the ct voxels (OPTIONAL)
% VOI is an OPTIONAL cell array which contain the patients VOIs as read by dicomrt_loadVOI
% voi2use is an OPTIONAL number pointing to the number of VOIs to be displayed
%
% Example:
%
% dicomrt_plotgammamap2dvol(gamma2dvol,xmesh,ymesh,zmesh,demo1_ct,ct_xmesh,ct_ymesh,ct_zmesh,demo1_voi,9);
%
% plot the gamma map in gamma2dvol on top of a ct slice and with contour of VOI 9.
%
% See also dicomrt_GAMMAcal2D, dicomrt_GAMMAcal2DVol, dicomrt_rendervoi
%
% Copyright (C) 2002 Emiliano Spezi (emiliano.spezi@physics.org)

% Check number of argument and set-up some parameters and variables
error(nargchk(4,11,nargin))

if size(gamma,3)==1
    error('dicomrt_plotgammamap2dvol: Format not supported, try dicomrt_plotgammamap. Exit now !');
end

if exist('VOI')==1 & exist('voi2use')==1
    [mask_gamma]=dicomrt_mask(VOI,gamma,gamma_xmesh,gamma_ymesh,gamma_zmesh,voi2use,'n');
else
    mask_gamma=gamma;
end

if size(gamma,3)~=length(VOI{voi2use,2})
    disp('Warning: the size of gamma does not match the size of the VOI!');
    disp('There is the possibility that the gamma map was calculated for a different VOI.');
    goahead=input('dicomrt_plotgammamap2dvol: Do you want to continue (y/n) ? (n=default)','s');
    if goahead~='y' | goahead~='Y'
        error('dicomrt_plotgammamap2dvol: Exiting as requested');
    elseif isempty(goahead)==1
        error('dicomrt_plotgammamap2dvol: Exiting as requested');
    end
end

if length(size(ct))==2 & ct==0
    for k=1:size(gamma,3) % Loop over gamma slices
        % plot ct map
        figure;
        set(gcf,'Name',['dicomrt_plotgammamap2dvol: ',inputname(1),', Z= ',num2str(gamma_zmesh(k))]);
        set(gcf,'NumberTitle','off');
        if exist('VOI')==1 & exist('voi2use')==1
            % plot voi on the same slice
            p=plot3(VOI{voi2use,2}{k,1}(:,1),VOI{voi2use,2}{k,1}(:,2),...
                VOI{voi2use,2}{k,1}(:,3));
            % set VOI contour to Z=-1 to display together with gamma map
            p_handle=get(p);
            ZData_temp_2=p_handle.ZData;
            for i=1:length(ZData_temp_2)
                ZData_temp_2(i)=-1;
            end
            set(p, 'Color', 'k','LineWidth', 1.5);
            set(p,'ZData',ZData_temp_2);
        end
        hold;
        % plot gamma map
        %mesh(mask_gamma(:,:,k),'XData',gamma_xmesh,'YData',gamma_ymesh);
        % ms=surf(mask_gamma(:,:,k));
        ms=surfl(mask_gamma(:,:,k));
        shading interp;
        set(ms,'XData',gamma_xmesh,'YData',gamma_ymesh);
        % add labels
        xlabel('X axis (cm)','Fontsize',12);
        ylabel('Y axis (cm)','Fontsize',12);
        zlabel('\gamma','Fontsize',14);
        title(['\gamma map slice: ',num2str(k)],'Fontsize',18);
        axis tight;
    end
else
    % rebuilding mesh matrices
    [xmin,xmax,ymin,ymax,zmin,zmax]=dicomrt_voiboundaries(ct_xmesh,ct_ymesh, ct_zmesh,VOI,voi2use);
    [ct3d_xmesh,ct3d_ymesh,ct3d_zmesh]=dicomrt_rebuildmatrix(ct_xmesh(xmin:xmax),ct_ymesh(ymin:ymax),ct_zmesh);
    for k=1:size(gamma,3) % Loop over gamma slices
        % plot ct map
        figure;
        set(gcf,'Name',['dicomrt_plotgammamap2dvol: ',inputname(1),', Z= ',num2str(ct_zmesh(dicomrt_findsliceVECT(gamma_zmesh,k,ct_zmesh)))]);
        set(gcf,'NumberTitle','off');
        handle=slice(ct3d_xmesh,ct3d_ymesh,ct3d_zmesh,ct(ymin:ymax,xmin:xmax,:),[],[],ct_zmesh(dicomrt_findsliceVECT(gamma_zmesh,k,ct_zmesh)));
        % set ct map to Z=-1 to display together with gamma map
        set(handle,'FaceColor','interp','EdgeColor','none','DiffuseStrength',.8);
        gco_handle=get(handle);
        ZData_temp_1=gco_handle.ZData;
        for i=1:size(ZData_temp_1,1)
            for j=1:size(ZData_temp_1,2)
                ZData_temp_1(i,j)=-1;
            end
        end
        set(handle,'ZData',ZData_temp_1);
        hold;
        if exist('VOI')==1 & exist('voi2use')==1
            % plot voi on the same slice
            if k==23
                disp(num2str(k));
            end
            p=plot3(VOI{voi2use,2}{k,1}(:,1),VOI{voi2use,2}{k,1}(:,2),...
                VOI{voi2use,2}{k,1}(:,3));
            % set VOI contour to Z=-1 to display together with gamma map
            p_handle=get(p);
            ZData_temp_2=p_handle.ZData;
            for i=1:length(ZData_temp_2)
                ZData_temp_2(i)=-1;
            end
            set(p, 'Color', 'w','LineWidth', 1.5);
            set(p,'ZData',ZData_temp_2);
        end
        % plot gamma map
        mesh(mask_gamma(:,:,k),'XData',gamma_xmesh,'YData',gamma_ymesh);
        % add labels
        xlabel('X axis (cm)','Fontsize',12);
        ylabel('Y axis (cm)','Fontsize',12);
        zlabel('\gamma','Fontsize',14);
        title(['\gamma map slice: ',num2str(k)],'Fontsize',18);
        axis tight;
    end
end


function run_demoTracklets(base_dir,calib_dir)
% KITTI RAW DATA DEVELOPMENT KIT
%
% This tool displays the images and the object labels for the benchmark and
% provides an entry point for writing your own interface to the data set.
% Before running this tool, set root_dir to the directory where you have
% downloaded the dataset. 'root_dir' must contain the subdirectory
% 'training', which in turn contains 'image_2', 'label_2' and 'calib'.
% For more information about the data format, please look into readme.txt.
%
% Input arguments:
% base_dir .... absolute path to sequence base directory (ends with _sync)
% calib_dir ... absolute path to directory that contains calibration files
%
% Usage:
%   SPACE: next frame
%   '-':   last frame
%   'x':   +10 frames
%   'y':   -10 frames
%   'q':   quit
%
% Occlusion Coding:
%   green:  not occluded
%   yellow: partly occluded
%   red:    fully occluded
%   white:  unknown

% clear and close everything
clear all; close all; dbstop error; clc;
disp('======= KITTI DevKit Demo =======');

% options (modify this to select your sequence)
% the base_dir must contain:
%   - the data directories (image_00, image_01, ..)
%   - the tracklet file (tracklet_labels.xml)
% the calib directory must contain:
%   - calib_cam_to_cam.txt
%   - calib_velo_to_cam.txt
% cameras:
%   - 0 = left grayscale
%   - 1 = right grayscale
%   - 2 = left color
%   - 3 = right color
if nargin<1
  base_dir  = '/mnt/karlsruhe_dataset/2011_09_26/2011_09_26_drive_0009_sync';
end
if nargin<2
  calib_dir = '/mnt/karlsruhe_dataset/2011_09_26';
end
cam       = 2; % 0-based index

% get image sub-directory
image_dir = fullfile(base_dir, sprintf('/image_%02d/data', cam));

% get number of images for this dataset
nimages = length(dir(fullfile(image_dir, '*.png')));

% set up figure
gh = visualization('init',image_dir);

% read calibration for the day
[veloToCam, K] = loadCalibration(calib_dir);

% read tracklets for the selected sequence
tracklets = readTrackletsMex([base_dir '/tracklet_labels.xml']);

% extract tracklets
% LOCAL OBJECT COORDINATE SYSTEM:
%   x -> facing right
%   y -> facing forward
%   z -> facing up
for it = 1:numel(tracklets)

  % shortcut for tracklet dimensions
  w = tracklets{it}.w;
  h = tracklets{it}.h;
  l = tracklets{it}.l;

  % set bounding box corners
  corners(it).x = [l/2, l/2, -l/2, -l/2, l/2, l/2, -l/2, -l/2]; % front/back
  corners(it).y = [w/2, -w/2, -w/2, w/2, w/2, -w/2, -w/2, w/2]; % left/right
  corners(it).z = [0,0,0,0,h,h,h,h];

  % get translation and orientation
  t{it} = [tracklets{it}.poses(1,:); tracklets{it}.poses(2,:); tracklets{it}.poses(3,:)];
  rz{it} = wrapToPi(tracklets{it}.poses(6,:));
  occlusion{it} = tracklets{it}.poses(8,:);
end

% 3D bounding box faces (indices for corners)
face_idx = [ 1,2,6,5   % front face
             2,3,7,6   % left face
             3,4,8,7   % back face
             4,1,5,8]; % right face

% main loop (start at first image of sequence)
img_idx = 0;
while 1

  % visualization update for next frame
  visualization('update',image_dir,gh,img_idx,nimages);

  % compute bounding boxes for visible tracklets
  for it = 1:numel(tracklets)

    % get relative tracklet frame index (starting at 0 with first appearance;
    % xml data stores poses relative to the first frame where the tracklet appeared)
    pose_idx = img_idx-tracklets{it}.first_frame+1; % 0-based => 1-based MATLAB index

    % only draw tracklets that are visible in current frame
    if pose_idx<1 || pose_idx>(size(tracklets{it}.poses,2))
      continue;
    end

    % compute 3d object rotation in velodyne coordinates
    % VELODYNE COORDINATE SYSTEM:
    %   x -> facing forward
    %   y -> facing left
    %   z -> facing up
    R = [cos(rz{it}(pose_idx)), -sin(rz{it}(pose_idx)), 0;
         sin(rz{it}(pose_idx)),  cos(rz{it}(pose_idx)), 0;
                             0,                      0, 1];

    % rotate and translate 3D bounding box in velodyne coordinate system
    corners_3D      = R*[corners(it).x;corners(it).y;corners(it).z];
    corners_3D(1,:) = corners_3D(1,:) + t{it}(1,pose_idx);
    corners_3D(2,:) = corners_3D(2,:) + t{it}(2,pose_idx);
    corners_3D(3,:) = corners_3D(3,:) + t{it}(3,pose_idx);
    corners_3D      = (veloToCam{cam+1}*[corners_3D; ones(1,size(corners_3D,2))]);

    % generate an orientation vector and compute coordinates in velodyneCS
    orientation_3D      = R*[0.0, 0.7*l; 0.0, 0.0; 0.0, 0.0];
    orientation_3D(1,:) = orientation_3D(1,:) + t{it}(1, pose_idx);
    orientation_3D(2,:) = orientation_3D(2,:) + t{it}(2, pose_idx);
    orientation_3D(3,:) = orientation_3D(3,:) + t{it}(3, pose_idx);
    orientation_3D      = (veloToCam{cam+1}*[orientation_3D; ones(1,size(orientation_3D,2))]);

    % only draw 3D bounding box for objects in front of the image plane
    if any(corners_3D(3,:)<0.5) || any(orientation_3D(3,:)<0.5)
      continue;
    end

    % project the 3D bounding box into the image plane
    corners_2D     = projectToImage(corners_3D, K);
    orientation_2D = projectToImage(orientation_3D, K);
    drawBox3D(gh,occlusion{it}(pose_idx),corners_2D,face_idx,orientation_2D)

    % compute and draw the 2D bounding box from the 3D box projection
    box.x1 = min(corners_2D(1,:));
    box.x2 = max(corners_2D(1,:));
    box.y1 = min(corners_2D(2,:));
    box.y2 = max(corners_2D(2,:));
    drawBox2D(gh,box,occlusion{it}(pose_idx),tracklets{it}.objectType)
  end

  % force drawing and tiny user interface
  waitforbuttonpress;
  key = get(gcf,'CurrentCharacter');
  switch lower(key)
    case 'q',  break;                                 % quit
    case '-',  img_idx = max(img_idx-1,  0);          % previous frame
    case 'x',  img_idx = min(img_idx+100,nimages-1);  % +100 frames
    case 'y',  img_idx = max(img_idx-100,0);          % -100 frames
    otherwise, img_idx = min(img_idx+1,  nimages-1);  % next frame
  end

end

% clean up
close all;


function out = mtimes( a,b )
%MTIMES for nested cells
%
% (c) Thomas Kuestner
% ---------------------------------------------------------------------

% determine input types
inType = {class(a), class(b)};
if(strcmp(inType{1},'TRAFO') && strcmp(inType{2},'TRAFO'))
    [outA, idx] = flattenCellMatrix(a.data);
    [outB, idxB] = flattenCellMatrix(b.data);
    meta = a.meta;
    meta_b = b.meta;
    clear 'a' 'b'

    if(~isempty(idx) && ~isempty(idxB) && ~all(cellfun(@(x,y) isequal(x,y), idx, idxB)))
        error('TRAFO::mtimes: Nested cells must have the same size');
    end
    if(iscell(meta) && iscell(meta_b))
        if(~isempty(meta) && ~isempty(meta_b))
            if(iscell(meta{1}) && iscell(meta_b{1}))
                if(~all(cellfun(@(x,y) isequal(x,y), flattenCellMatrix(meta), flattenCellMatrix(meta_b))))
                    error('TRAFO::mtimes: Unequal reconstruction information');
                end
            else
                if(~all(cellfun(@(x,y) isequal(x,y), meta, meta_b)))
                    error('TRAFO::mtimes: Unequal reconstruction information');
                end
            end
        end
    else
        if ~isequal(meta, meta_b)
            error('TRAFO::mtimes: Unequal reconstruction information');
        end
    end

    out = cellfun(@(x,y) x*y, outA, outB, 'UniformOutput', false);

elseif(strcmp(inType{1},'TRAFO')) % assume b is double/uint/int
    [out, idx] = flattenCellMatrix(a.data);
    meta = a.meta;
    clear 'a'

    out = cellfun(@(x) x * b, out, 'UniformOutput', false);

elseif(strcmp(inType{2},'TRAFO'))
    [out, idx] = flattenCellMatrix(b.data);
    meta = b.meta;
    clear 'b'

    out = cellfun(@(x) a * x, out, 'UniformOutput', false);

else
    error('TRAFO::mtimes: Impossible constellation!');
end

out = reconFlatCellMatrix(out,idx);
out = TRAFO(out,meta); % return TRAFO object again without modifying input

end


% USERDATAPNT  User data for SLAMTB - Example with points.
%   This is a particular case of USERDATA.M. It is intended for
%   demonstration of the SLAM toolbox with points. Try the following point
%   types in Opt.init.initType:
%       'idpPnt'    Inverse depth points
%       'ahmPnt'    Anchored homogeneous points
%       'hmgPnt'    Homogeneous points
%
%   See also USERDATA.

%   Copyright 2008-2009 Joan Sola @ LAAS-CNRS.

% Time variables
%   - sampling time, first and last frames
Time = struct(...
  'dt',                   .1,...          % sampling time, seconds
  'firstFrame',           1,...           % first frame #
  'lastFrame',            400);           % last frame #

% Simulated world
%   - Simulation landmark sets, playground dimensions
World = struct(...
  'points',           thickCloister(-6,6,-6,6,1,7),... % 3d point landmarks - see THICKCLOISTER.
  'segments',         []);  % 3D segments - see HOUSE.

% Robot things with their controls
%   - each robot's type and initial configuration, and controls.
%   - motion models (add new model strings if you need more):
%       'constVel'    6D Constant velocity model
%       'odometry'    6D Odometry model
%   - See EULERANGLES for orientations specifications.
Robot{1} = struct(...                     % ODOMETRY EXAMPLE
  'id',                 1,...           % robot identifier
  'name',               'Dala',...      % robot name
  'type',               'atrv',...      % type of robot
  'motion',             'odometry',...  % motion model
  'position',           [0;-5;0],...     % robot position in map
  'orientationDegrees', [0;0;0],...     % orientation, in degrees, roll pitch yaw.
  'positionStd',        [0;0;0],...     % position error, std
  'orientationStd',     [0;0;0],...     % orient. error, std, in degrees
  'dx',                 [.08;0;0],...     % position increment 8
  'daDegrees',          [0;0;0.9],...     % angle increment, degrees 9
  'dxStd',              0.005*[1;1;1],...  % odo linear error std
  'daStd',              0.05*[1;1;1]);     % odo ang error std, degrees

% Robot{2} = struct(...                     % CONSTANT VELOCITY EXAMPLE
%   'id',                 3,...           % robot identifier
%   'name',               'Dala',...      % robot name
%   'type',               'atrv',...      % type of robot
%   'motion',             'constVel',...  % motion model
%   'position',           [1;0;0],...     % robot position in map
%   'orientationDegrees', [0;0;45],...    % orientation, in degrees, roll pitch yaw.
%   'positionStd',        [0;0;0],...     % position error, std
%   'orientationStd',     [0;0;0],...     % orient. error, std, degrees
%   'velocity',           [1;0;0],...     % lin. velocity
%   'angularVelDegrees',  [0;0;10],...    % ang. velocity, in degrees
%   'velStd',             [0;0;0],...     % lin. vel. error, std
%   'angVelStd',          [0;0;0],...     % ang. vel. error, std, degrees
%   'dv',                 [0;0;0],...     % veolcity increment
%   'dwDegrees',          [0;0;0],...     % ang. vel. increment, degrees
%   'dvStd',              [0;0;0],...     % vel perturbation std
%   'dwStd',              [0;0;1]);       % ang vel pert. std, degrees

% Sensor things
%   - each sensor's type and parameters, noise, non-measurable prior.
%   - Sensor types (add new type strings if you need more):
%       'pinHole'   Pin-hole camera
%   - See EULERANGLES for orientations specifications.
Sensor{1} = struct(...
  'id',                 1,...           % sensor identifier
  'name',               'Micropix',...  % sensor name
  'type',               'pinHole',...   % type of sensor
  'robot',              1,...           % robot where it is mounted
  'position',           [0;0;.6],...    % position in robot
  'orientationDegrees', [-90;0;-90],... % orientation in robot, roll pitch yaw
  'positionStd',        [0;0;0],...     % position error std
  'orientationStd',     [0;0;0],...     % orient. error std
  'imageSize',          [640;480],...   % image size
  'pixErrorStd',        1.0,...         % pixel error std
  'intrinsic',          [320;240;320;320],... % intrinsic params [u0 v0 au av]
  'distortion',         [],...          % distortion params
  'frameInMap',         false,...       % add sensor frame in slam map?
  'imGrid',             struct(...      % grid for Active Search
    'numCells',         [8;6],...         % number of H and V grid cells
    'skipOuter',        true));           % skip outer cells for initialization?

% Sensor{2} = struct(...
%   'id',                 2,...           % sensor identifier
%   'name',               'Micropix',...      % sensor name
%   'type',               'pinHole',...   % type of sensor
%   'robot',              1,...           % robot where it is mounted
%   'position',           [0;-0.15;.6],...     % position in robot
%   'orientationDegrees', [-90;0;-90],...      % orientation in robot, roll pitch yaw
%   'positionStd',        [0;0;0],...     % position error std
%   'orientationStd',     [1.5;1.5;1.5],...     % orient. error std
%   'imageSize',          [640;480],...   % image size
%   'pixErrorStd',        1.0,...         % pixel error std
%   'intrinsic',          [320;240;320;320],... % intrinsic params
%   'distortion',         [],...          % distortion params
%   'frameInMap',         false);         % add sensor frame in slam map?

% Omnidirectional camera model -> MegaPixel Fish Eye lens as example (FoV is ~190 deg )
% k           = [668 438 1 0 0]';
% invProjDist = [4.473e+2 -0.000e+0 -1.068e-3 1.184e-6 -1.856e-9]';
% projDist    = [6.447e+2 -3.410e+2 -2.901e+1 -5.770e+1 1.849e+1 5.415e+0 5.065e+1 -5.614e+1 1.591e+1 0 0]';
% Sensor{1} = struct(...
%   'id',                 1,...           % sensor identifier
%   'name',               'FrontCam',...  % sensor name
%   'type',               'omniCam',...   % type of sensor
%   'robot',              1,...           % robot where it is mounted
%   'position',           [0.2;0;1.2],... % position in robot
%   'orientationDegrees', [-120;0;-90],...% orientation in robot, roll pitch yaw.
%   'positionStd',        [0;0;0],...     % position error std
%   'orientationStd',     [0;0;0],...     % orient. error std
%   'imageSize',          [1280;800],...  % image size
%   'pixErrorStd',        1.0,...         % pixel error std
%   'intrinsic',          k,...           % intrinsic params: [xc yc c d e]';
%   'distortion',         projDist,...    % distortion params -> polynom for projection to cam sensor
%   'invDistortion',      invProjDist,... % distortion params -> polynom for inv proj from cam sensor
%   'frameInMap',         false,...       % add sensor frame in slam map?
%   'imGrid',             struct(...      % grid for Active Search
%     'numCells',         [8;6],...       % number of H and V grid cells
%     'skipOuter',        true));         % skip outer cells for initialization?

% Estimation options
Opt = struct(...
  'map',              struct(...    % options for the map
    'numLmks',        73,...         % number of 3d landmarks
    'lmkSize',        7),...         % Size of landmark
  'correct',          struct(...    % options for lmk correction
    'reprojectLmks',  true,...       % reproject lmks after active search?
    'reparametrize',  true,...       % reparametrize lmk?
    'nUpdates',       10,...         % max simultaneus updates
    'MD2th',          9,...          % Threshold on Mahalanobis distance squared
    'linTestIdp',     0.1,...        % threshold on IDP linearity test
    'lines',          struct(...     % options for line corrections
      'innType',      'ortDst',...    % innovation type for lines
      'extPolicy',    false,...       % line extending policy ?
      'extSwitch',    10)),...        % extension policy switch point in pixels
  'init',             struct(...    % Options for initialization
    'nbrInits',       [10 1],...      % number of inits [firstFrame, otherFrames]
    'initType',       'ahmPnt',...   % Type of lmk to use for init
    'idpPnt',         struct(...     % inverse-distance prior
      'nonObsMean',   0.01,...         % mean of non obs
      'nonObsStd',    0.5),...         % std of non obs
    'plkLin',         struct(...     % Plucker prior
      'nonObsMean',   [.1;0],...      % mean of non obs
      'nonObsStd',    [.25;1])),...   % std of non obs
  'obs',              struct(...    % Observation options
    'lines',          struct(...     % lines options
      'minLength',    20)));          % minimum segment length

% Simulation options
%   - random
SimOpt = struct(...
  'random',           struct(...      % random generator options
    'newSeed',        true,...         % select new random seed?
    'fixedSeed',      1,...            % random seed for non-random runs
    'seed',           []),...          % current seed
  'obs',              Opt.obs);       % Observation options

% Figure options
%   - view, projection, video, ellipses.
%   - figure projections - mapProj:
%       'persp'     Perspective
%       'ortho'     Orthographic
%   - 3D figure views - mapView - see MAPOBSERVER.
%       [a,e,f]     Custom azimuth/elevation/FOV vector. Distance automatic
%       [a,e,f,d]   custom az/el/fov/distance vector.
%   - 3D figure predefined views (edit mapObserver.m to create/edit views):
%       'top'       Top view
%       'side'      Side view
%       'view'      Generic view
%       'normal'    Normal view
%   - objects colors - two options for color specification:
%       'rgbcmykw'  1-char predifined Matlab colors
%       [r g b]     RGB color vector. [0 0 0] is black, [1 1 1] is white.
FigOpt = struct(...
  'renderer',       'opengl',...    % renderer
  'rendPeriod',     1,...           % frames to skip for faster processing
  'createVideo',    false,...       % create video sequences?
  'map',            struct(...      % map figure options
    'size',         [320 240],...   % map figure size
    'lims',        struct(...       % playground limits
      'xMin',            -10,...
      'xMax',             10,...
      'yMin',            -10,...
      'yMax',             10,...
      'zMin',            -10,...
      'zMax',             10),...
    'proj',         'persp',...     % projection of the 3d figure
    'view',         'view',...      % viewpoint of the 3d figure [30 45 40 20]
    'orbit',        [0 0],...       % AZ and EL orbit angle increments
    'showSimLmk',   false,...       % show simulated landmarks?
    'showEllip',    true,...        % show ellipsoids?
    'colors',       struct(...      % map figure colors
      'border',     [1 1 1],...      %   [r g b]
      'axes',       [0 0 0],...      % with:
      'bckgnd',     [1 1 1],...      %   [0 0 0] black
      'simLmk',     .3*[1 1 1],...   %   [1 1 1] white
      'defPnt',     struct(...       % euclidean point colors
        'mean',     'b',...           % mean dot
        'ellip',    [.7 .7 1]),...    % ellipsoid
      'othPnt',     struct(...       % other point colors
        'mean',     'r',...           % mean dot
        'ellip',    [1 .7 .7]),...    % ellipsoid
      'defLin',     struct(...       % Plucker line colors
        'mean',     [0 .8 0],...      % mean line
        'ellip',    [.6 1 .6]),...    % ellipsoid
      'othLin',     struct(...       % Plucker line colors
        'mean',     [.8 0 0],...      % mean line
        'ellip',    [1 .6 .6]),...    % ellipsoid
      'simu',       'b',...          %   or 'r', 'b', etc.
      'est',        'g',...          % estimated robots and sensors
      'ground',     [.8 .8 .8],...   % simulated robots and sensors
      'label',      [.0 .5 0])),...  % landmark ID labels
  'sensor',         struct(...      % sensor figures options
    'size',         [320 240],...    % sensor figure size
    'showEllip',    false,...        % show ellipses?
    'colors',       struct(...       % Sensor figure colors:
      'border',     .8*[1 1 1],...    %
      'axes',       [0 0 0],...       %
      'bckgnd',     [1 1 1],...       %
      'raw',        .3*[1 1 1],...    %
      'defPnt',     struct(...       % Default point colors
        'updated',  'c',...           % updated
        'predicted','b'),...          % predicted
      'othPnt',     struct(...       % other point colors
        'updated',  'r',...           % updated
        'predicted','m'),...          % predicted
      'defLin',     struct(...       % Default line colors
        'meas',     'b',...           % measurement
        'mean',     'g',...           % mean line
        'ellip',    'y'),...          % ellipsoid
      'othLin',     struct(...       % other line colors
        'meas',     'b',...           % measurement
        'mean',     'm',...           % mean line
        'ellip',    'r'),...          % ellipsoid
      'label',      [.5 .5 .5])));    %

% Experiment options
%   - site name, series gathered, estimation run number
ExpOpt = struct(...
  'root',         '~/SLAM/',...         % root directory
  'site',         'simu',...            % Name of the site
  'dataRun',      1,...                 % Run # on this site
  'estimateRun',  1,...                 % slam run for data and site
  'lmkTypes',     Opt.init.initType,... % types of landmarks used
  'sensingType',  'mono',...            % sensing mode
  'mappingType',  'single');            % mapping mode

% ========== End of function - Start GPL license ==========

%   # START GPL LICENSE

%---------------------------------------------------------------------
%
%   This file is part of SLAMTB, a SLAM toolbox for Matlab.
%
%   SLAMTB is free software: you can redistribute it and/or modify
%   it under the terms of the GNU General Public License as published by
%   the Free Software Foundation, either version 3 of the License, or
%   (at your option) any later version.
%
%   SLAMTB is distributed in the hope that it will be useful,
%   but WITHOUT ANY WARRANTY; without even the implied warranty of
%   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%   GNU General Public License for more details.
%
%   You should have received a copy of the GNU General Public License
%   along with SLAMTB.  If not, see <http://www.gnu.org/licenses/>.
%
%---------------------------------------------------------------------

%   SLAMTB is Copyright:
%   Copyright (c) 2008-2010, Joan Sola @ LAAS-CNRS,
%   Copyright (c) 2010-2013, Joan Sola,
%   Copyright (c) 2014-2015, Joan Sola @ IRI-UPC-CSIC,
%   SLAMTB is Copyright 2009
%   by Joan Sola, Teresa Vidal-Calleja, David Marquez and Jean Marie Codol
%   @ LAAS-CNRS.
%   See on top of this file for its particular copyright.

%   # END GPL LICENSE


function [sDataCT] = correctPatient30_HN(pathDATA)
% -------------------------------------------------------------------------
% function [sDataCT] = correctPatient30_HN(pathDATA)
% -------------------------------------------------------------------------
% DESCRIPTION:
% Reason of existence of the function: the RTstruct of the CT scan of
% patient 30 is incomplete (most probably it was previously incorrectly
% processed by MIM). The problem is solved by copying and processing the
% 'contour' structure of the PET scan.
% -------------------------------------------------------------------------
% INPUTS:
% - pathDATA: Full path to the 'DATA' folder of the HN workspace.
% -------------------------------------------------------------------------
% OUTPUTS:
% - sData: Corrected sData.
% -------------------------------------------------------------------------
% AUTHOR(S): Martin Vallieres <mart.vallieres@gmail.com>
% -------------------------------------------------------------------------
% HISTORY:
% - Creation: July 2015
%--------------------------------------------------------------------------
% STATEMENT:
% This file is part of <https://github.com/mvallieres/radiomics/>,
% a package providing MATLAB programming tools for radiomics analysis.
% --> Copyright (C) 2015  Martin Vallieres
%
%    This package is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    This package is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with this package.  If not, see <http://www.gnu.org/licenses/>.
% -------------------------------------------------------------------------

startpath = pwd;
cd(pathDATA)
load('Patient30_CT'), sDataCT = sData;
load('Patient30_PET'), sDataPET = sData;

% ADJUSTING CT CONTOURS ACCORDING TO PET CONTOURS.
sDataCT{2}.scan.contour = sDataPET{2}.scan.contour;
nContour = length(sDataPET{2}.scan.contour);
downF = sDataCT{2}.scan.pixelW/sDataPET{2}.scan.pixelW;
offsetStart = round(abs(sDataPET{3}(1).ImagePositionPatient(2)-sDataCT{3}(1).ImagePositionPatient(2))*sDataCT{2}.scan.pixelW);
offsetEnd = round((size(sDataCT{2}.scan.volume,1)*sDataCT{2}.scan.pixelW - size(sDataPET{2}.scan.volume,1)*sDataPET{2}.scan.pixelW)/sDataCT{2}.scan.pixelW)-offsetStart;
for i = 1:nContour
    sDataCT{2}.scan.contour(i).boxBound(1:2,1) = round((sDataPET{2}.scan.contour(i).boxBound(1:2,1)/downF + offsetStart)); sDataCT{2}.scan.contour(i).boxBound(1:2,2) = round((sDataPET{2}.scan.contour(i).boxBound(1:2,2)/downF + offsetEnd));
    boxBound = sDataCT{2}.scan.contour(i).boxBound;
    nSlices = size(sDataCT{2}.scan.contour(i).boxMask,3);
    sDataCT{2}.scan.contour(i).boxMask = zeros(boxBound(1,2)-boxBound(1,1)+1,boxBound(2,2)-boxBound(2,1)+1,nSlices);
    for j = 1:nSlices
        sDataCT{2}.scan.contour(i).boxMask(:,:,j) = imresize(sDataPET{2}.scan.contour(i).boxMask(:,:,j),[boxBound(1,2)-boxBound(1,1)+1,boxBound(2,2)-boxBound(2,1)+1],'nearest');
    end
end

cd(startpath)
end


%% Demo: White balancing all images in a directory
%
% Copyright (c) 2018-present, Mahmoud Afifi
% York University, Canada
% mafifi@eecs.yorku.ca | m.3afifi@gmail.com
%
% This source code is licensed under the license found in the
% LICENSE file in the root directory of this source tree.
% All rights reserved.
%
% Please cite the following work if this program is used:
% Mahmoud Afifi, Brian Price, Scott Cohen, and Michael S. Brown,
% "When color constancy goes wrong: Correcting improperly white-balanced
% images", CVPR 2019.
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%

%% input and options
in_image_dir = fullfile('..', 'example_images');
out_image_dir = fullfile('..', 'example_images_WB');
device = 'cpu'; %'cpu' or 'gpu'
gamut_mapping = 2; % use 1 for scaling, 2 for clipping (our paper's results
% reported using clipping). If the image is over-saturated, scaling is
% recommended.
upgraded_model = 0; % use 1 to load our new model that is upgraded with new
% training examples.

%%
switch lower(device)
    case 'cpu'
        if upgraded_model == 1
            load(fullfile('models','WB_model+.mat'));
        elseif upgraded_model == 0
            load(fullfile('models','WB_model.mat'));
        else
            error('Wrong upgraded_model value; please use 0 or 1');
        end
    case 'gpu'
        try
            gpuDevice();
        catch
            error('Cannot find a GPU device');
        end
        if upgraded_model == 1
            load(fullfile('models','WB_model+_gpu.mat'));
        elseif upgraded_model == 0
            load(fullfile('models','WB_model_gpu.mat'));
        else
            error('Wrong upgraded_model value; please use 0 or 1');
        end
    otherwise
        error('Wrong device; please use ''gpu'' or ''cpu''')
end
model.gamut_mapping = gamut_mapping;
if exist(out_image_dir,'dir')==0
    mkdir(out_image_dir);
end
imds = imageDatastore(in_image_dir);
files = imds.Files;
for f = 1 : length(files)
    fprintf('Processing image: %s\n',files{f});
    infileName = files{f};
    [~,name,ext] = fileparts(infileName);
    outfileName = fullfile(out_image_dir, [name, '_', 'WB', ext]);
    I_in = imread(infileName);
    I_corr = model.correctImage(I_in);
    if strcmpi(device,'gpu')
        imwrite(gather(I_corr),outfileName);
    else
        imwrite(I_corr,outfileName);
    end
    disp('Done!');
end