function HierarchicalClusteringBiox2(FiberGroupFile, numNodesToResample, NumFibersPerPartition, maxDistanceOfInterest, NClusters)

%Make all parameters numerical;
if ischar(numNodesToResample)
    numNodesToResample=str2num(numNodesToResample);
end

%Make all parameters numerical;
if ischar(NumFibersPerPartition)
    NumFibersPerPartition=str2num(NumFibersPerPartition);
end

%Make all parameters numerical;
if ischar(maxDistanceOfInterest)
    maxDistanceOfInterest=str2num(maxDistanceOfInterest);
end

%Make all parameters numerical;
if ischar(NClusters)
    NClusters=str2num(NClusters);
end

warning off;

[pathstr, FiberGroupFileName, ext, versn] = fileparts(FiberGroupFile);
if isempty(pathstr)
    pathstr=['.' filesep ];
end

%0 Reorder fibers by distance among seeds
load(FiberGroupFile);
%fibersReorderedByDistance=dtiReorderFibersByDistance(fg)
fg=dtiReorderFibersByDistance(fg);
ReorderedFiberGroupFileName=[FiberGroupFileName 'ReorderedByDistance'];
save(ReorderedFiberGroupFileName, 'fg', 'versionNum', 'coordinateSpace');
 display('Reordered by Distance');

%1. Resample originalFibersFile ->ResampledFibers
%Make all parameters numerical;
if ischar(numNodesToResample)
    numNodesToResample=str2num(numNodesToResample);
end

fg=resampleFiberGroup(fg, numNodesToResample);

ResampledFolder=['FibersResampledTo' num2str(numNodesToResample)];
mkdir(pathstr, ResampledFolder);
ResampledFiberGroupFile=fullfile(pathstr, ResampledFolder, [ReorderedFiberGroupFileName 'Resampled']);
save(ResampledFiberGroupFile, 'fg', 'coordinateSpace', 'versionNum');
clear fg coordinateSpace versionNum; %Saving memoryl
display('Resampled');

%2. Partition ResampledFiberGroupFile by chunks of
%size=NumFibersPerPartition
cd(ResampledFolder)
dtiFractionizeFiberDataSet([ReorderedFiberGroupFileName 'Resampled'], NumFibersPerPartition);

cd('fgFile_Parts')

%3. Go through FIBER partitions and cluster them, creating master clusterLabels
clusterlabelsAll=[];
allPartitions=dir([ReorderedFiberGroupFileName 'Resampled' '*.mat']);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%THIS PART IS SPECIFIC TO BIOX2CLUSTER
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
for PartitionId=1:size(allPartitions, 1)

%[clusterlabels]=ClusterFibers(allPartitions(PartitionId).name, maxDistanceOfInterest, NClusters);
%the two lines below perform clustering by submitting a job ^^
unix(['/home/elenary/project_scripts/full_brain_clustering/cluster_fibers.batch.job ' allPartitions(PartitionId).name ' ' num2str(maxDistanceOfInterest) ' ' num2str(NClusters)]);
display(['Fibers clustered for partition ' allPartitions(PartitionId).name]);
end

%Check if the jobs are completed, then load computed cluster labels
a=[];
while size(a, 1)<size(allPartitions, 1)
    a=dir('*clusterlabels.mat');
    pause(2);
end
        unix(['ls *clusterlabels.mat > ' ReorderedFiberGroupFileName 'Resampled.txt']);

for PartitionId=1:size(allPartitions, 1)

[pathstrCL, NameCL, extCL, versnCL] = fileparts(allPartitions(PartitionId).name);
load([NameCL 'clusterlabels']);
clusterlabelsAll=[clusterlabelsAll; clusterlabels+NClusters*(PartitionId-1)];
end

%cleanup temp files
unix(['rm *clusterlabels*']);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%END OF CLUSTER SPECIFIC PART%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%RECURSIVE PART START
%3. Create COMPUTE SUPERFIBERREPRESENTATION for the full original FiberSet
%DECIDE ON: levels; NumSuperFibersPerPartition, NClusters on every level
level=0;
    NumSuperFibersPerPartition=NumFibersPerPartition;  NSFClusters=NClusters; %maxDistanceOfInterest=50; %5cm
display('Start recursion');

while size(allPartitions, 1)>1
    level=level+1;

    cd('../../'); %To go back up from fg_FileParts -up, ResampledFolder -->up
    load(ResampledFiberGroupFile);
    display(['Computing superfiber representations at level ' num2str(level)]);
    tic; SuperFibersGroup = dtiComputeSuperFiberRepresentation(fg, clusterlabelsAll); toc;
    display('Superfibers Created');

    fg=SuperFibersGroup;
    ParentFiberGroupFile=[ResampledFiberGroupFile '.mat'];
    SuperFiberGroupFileName=['SuperFibersOf_' ReorderedFiberGroupFileName 'Resampled_Level' num2str(level)];
    SuperFiberGroup=fullfile(pathstr, ResampledFolder, SuperFiberGroupFileName);
    save(SuperFiberGroup, 'ParentFiberGroupFile', 'clusterlabelsAll', 'maxDistanceOfInterest', 'fg', 'coordinateSpace', 'versionNum');

    %Cluster the SuperFibers and update master clusterlabelsAll
    cd(ResampledFolder)
    FractionizeFiberDataSet(SuperFiberGroupFileName, NumSuperFibersPerPartition); %Partitions will be saved un fgFile_Parts dir
    cd('fgFile_Parts')
    %Go through FIBER partitions and cluster them, creating master clusterLabels
    %We have here clusterlabelsALL
    allPartitions=dir([SuperFiberGroupFileName '*mat']);
    clusterlabelsCurrAll=clusterlabelsAll; %Labels from the current level

    %%%%%%%%%%%THIS PART IS SPECIFIC TO BIOX2 CLUSTER
    %%%HERE WE ARE SUBMITTING BJOBS

    for SFPartitionId=1:size(allPartitions, 1)

       % [clusterlabels]=ClusterFibers(allPartitions(SFPartitionId).name, maxDistanceOfInterest, NSFClusters);
       unix(['/home/elenary/project_scripts/full_brain_clustering/cluster_fibers.batch.job ' allPartitions(SFPartitionId).name ' ' num2str(maxDistanceOfInterest) ' ' num2str(NSFClusters)]);
       display(['SuperFibers clustered for partition ' allPartitions(SFPartitionId).name]);
        %Udate the portion of clusterlabelsAll pertaining to current partition
        %of superfiberset only

    end

    %%%%%%%%%%%THIS PART IS SPECIFIC TO BIOX2 CLUSTER
    %%%HERE WE ARE READING RESULS OF SUBMITTED JOBS

        %Check if the jobs are completed, then load computed cluster labels
        a=[];
        while size(a, 1)<size(allPartitions, 1)
            a=dir('*clusterlabels.mat');
            pause(2);
        end
        unix(['ls *clusterlabels.mat > SuperFibersOf_' ReorderedFiberGroupFileName 'Resampled_Level' num2str(level) '.txt']);

    for SFPartitionId=1:size(allPartitions, 1)

        %Udate the portion of clusterlabelsAll pertaining to current partition
        %of superfiberset only
        [pathstrCLSF, NameCLSF, extCLSF, versnCLSF] = fileparts(allPartitions(SFPartitionId).name);
        load([NameCLSF 'clusterlabels']);

        for SuperFiberId=1:min(NumSuperFibersPerPartition, size(clusterlabels, 1))
            clusterlabelsAll(find(clusterlabelsCurrAll==SuperFiberId+(SFPartitionId-1)*NumSuperFibersPerPartition), 1)=clusterlabels(SuperFiberId)+NSFClusters*(SFPartitionId-1);
        end

    end

        %cleanup temp files
        unix(['rm *clusterlabels*']);

end %while size(allPartitions, 1)>1

%So we have a single partition of SuperFibers for which clustering has just
%been performed. Save the results.

    level=level+1;

    cd('../../'); %To go back up from fg_FileParts -up, ResampledFolder -->up
    load(ResampledFiberGroupFile);
    SuperFibersGroup = dtiComputeSuperFiberRepresentation(fg, clusterlabelsAll);
    display('Superfibers Created');

    fg=SuperFibersGroup;
    ParentFiberGroupFile=[ResampledFiberGroupFile '.mat'];
    SuperFiberGroupFileName=['SuperFibersOf_' ReorderedFiberGroupFileName 'Resampled_Level' num2str(level)];
    SuperFiberGroup=fullfile(pathstr, ResampledFolder, SuperFiberGroupFileName);
    save(SuperFiberGroup, 'ParentFiberGroupFile', 'clusterlabelsAll', 'maxDistanceOfInterest', 'fg', 'coordinateSpace', 'versionNum');
%Note: we are creating superfiber representation for
%ResampledFiberGroupFileName, not ReorderedFiberGroupFileName, because for
%computing SF representation the fibers need to be equisampled.

%However, to create a good visualization of clustering results, we are
%encouraged to use
%fg from ReorderedFiberGroupFileName
%and clusterlabelsAll from     SuperFiberGroupFileName

%fibergroupsvector=1:50;
%load(ReorderedFiberGroupFileName);
%dtiSaveFiberClusters(clusterlabelsAll, fibergroupsvector, fg, versionNum, coordinateSpace)


function [ox, otheta, ollh, olpp, v] = tapas_ti_step_mh(y, u, ox, otheta, ...
    ollh, olpp, ptheta, htheta, pars)
%% Estimates the posterior probability of the parameters using MCMC combined
% with path sampling.
%
% Input
%   y       Saccade data.
%   u       Experimental input.
%   ptheta  Priors of the model
%   htheta  Kernel transformation.
%   pars    Parameters for the MCMC.
%           pars.verbose   True or false. Defs. False.
%           pars.mc3it      Number of iterations for MC3. Defs. 0.
%           pars.kup        Number of steps for the kernel update.
%                           Defs. 500.
%           pars.seed       Seed for the random generation. If zero use
%                           rng('shuffle'). Defaults to zero.
%           pars.samples    If true, stores the samples from the chain at
%                           lowest temperature. Defaults to zero.
%
% Output
%   ps      Samples from the posterior distribuion
%   fe      Free energy estimate.
%
% Uses a standard MCMC on a population and applies an exchange operator
% to improve the mixing.
%
%
% Real parameter evolutionary Monte Carlo with applications to Bayes Mixture
% Models, Journal of American Statistica Association, Liang & Wong 2001.

%
% aponteeduardo@gmail.com
% copyright (C) 2014
%

if isfield(ptheta, 'mh_propose_sample')
    propose_sample = htheta.mh_propose_sample;
else
    propose_sample = @tapas_ti_propose_gaussian;
end

[ntheta, ratio] = propose_sample(otheta, ptheta, htheta);
[nllh, nx] = ptheta.llh(y, [], u, ntheta, ptheta);

T = ptheta.T;

nllh = sum(nllh, 1);
nlpp = sum(ptheta.lpp(y, nx, u, ntheta, ptheta), 1);

v = nllh .* T + nlpp - (ollh .* T + olpp) + ratio;
nansv = isnan(v);
v(nansv) = -inf;

v = rand(size(v)) < exp(bsxfun(@min, v, 0));

ollh(v) = nllh(v);
olpp(v) = nlpp(v);
otheta(:, v) = ntheta(:, v);
ox(:, v) = nx(:, v);

assert(all(-inf < ollh), 'mpdcm:ti:mh', '-inf value in the new samples');

end


function [x0,LB,UB,PLB,PUB] = boundscheck_vbmc(x0,LB,UB,PLB,PUB,prnt)
%BOUNDSCHECK Initial check of bounds.

[N0,nvars] = size(x0);

% Expand scalar inputs to full vectors
if isscalar(LB); LB = LB*ones(1,nvars); end
if isscalar(UB); UB = UB*ones(1,nvars); end
if isscalar(PLB); PLB = PLB*ones(1,nvars); end
if isscalar(PUB); PUB = PUB*ones(1,nvars); end

if isempty(PLB) || isempty(PUB)
    if N0 > 1
        if prnt > 0
            fprintf('PLB and/or PUB not specified. Estimating plausible bounds from starting set X0...\n');
        end
        width = max(x0) - min(x0);
        if isempty(PLB)
            PLB = min(x0) - width/N0;
            PLB = max(PLB,LB);
        end
        if isempty(PUB)
            PUB = max(x0) + width/N0;
            PUB = min(PUB,UB);
        end

        idx = any(PLB == PUB);
        if any(idx)
            PLB(idx) = LB(idx);
            PUB(idx) = UB(idx);
            warning('vbmc:pbInitFailed', ...
                'Some plausible bounds could not be determined from starting set. Using hard upper/lower bounds for those instead.')
        end
    else
        warning('vbmc:pbUnspecified', ...
            'Plausible lower/upper bounds PLB and/or PUB not specified and X0 is not a valid starting set. Using hard upper/lower bounds instead.');
        if isempty(PLB); PLB = LB; end
        if isempty(PUB); PUB = UB; end
    end
end

% Test that all vectors have the same length
if any([numel(LB),numel(UB),numel(PLB),numel(PUB)] ~= nvars)
    error('All input vectors (X0, LB, UB, PLB, PUB), if specified, need to have the same size.');
end

% Test that all vectors are row vectors
if ~isvector(LB) || ~isvector(UB) || ~isvector(PLB) || ~isvector(PUB) ...
        || size(LB,1) ~= 1 || size(UB,1) ~= 1 || size(PLB,1) ~= 1 || size(PUB,1) ~= 1
    error('All input vectors LB, UB, PLB, PUB, if specified, should be row vectors.');
end

% Test that plausible bounds are finite
if ~all(isfinite(([PLB, PUB])))
    error('Plausible interval bounds PLB and PUB need to be finite.');
end

% Test that all vectors are real-valued
if ~isreal([x0(:)', LB, UB, PLB, PUB])
    error('All input vectors should be real-valued.');
end

% Fixed variables (all bounds equal) are not supported
fixidx = (LB == UB) & (UB == PLB) & (PLB == PUB);
if any(fixidx)
    error('vbmc:FixedVariables', ...
        'VBMC does not support fixed variables. Lower and upper bounds should be different.');
end

% Test that plausible bounds are different
if any(PLB == PUB)
    error('vbmc:MatchingPB', ...
        'For all variables, plausible lower and upper bounds need to be distinct.')
end

% Check that all X0 are inside the bounds
if any(any(bsxfun(@lt,x0,LB))) || any(any(bsxfun(@gt,x0,UB)))
    error('vbmc:InitialPointsNotInsideBounds', ...
        'The starting points X0 are not inside the provided hard bounds LB and UB.');
end

% Compute "effective" bounds (slightly inside provided hard bounds)
bounds_range = UB - LB;
bounds_range(isinf(bounds_range)) = 1e3;
scale_factor = 1e-3;
LB_eff = LB + scale_factor*bounds_range;
LB_eff(abs(LB) <= realmin) = scale_factor*bounds_range(abs(LB) <= realmin);
UB_eff = UB - scale_factor*bounds_range;
UB_eff(abs(UB) <= realmin) = -scale_factor*bounds_range(abs(UB) <= realmin);
LB_eff(isinf(LB)) = LB(isinf(LB));  % Infinities stay the same
UB_eff(isinf(UB)) = UB(isinf(UB));

if any(LB_eff >= UB_eff)
    error('vbmc:StrictBoundsTooClose', ...
        'Hard bounds LB and UB are numerically too close. Make them more separate.');
end

% Fix when provided X0 are almost on the bounds -- move them inside
if any(any(bsxfun(@le,x0,LB_eff))) || any(any(bsxfun(@ge,x0,UB_eff)))
    warning('vbmc:InitialPointsTooClosePB', ...
        'The starting points X0 are on or numerically too close to the hard bounds LB and UB. Moving the initial points more inside...');
    x0 = bsxfun(@max, bsxfun(@min,x0,UB_eff), LB_eff);
end

% Test order of bounds (permissive)
ordidx = LB <= PLB & PLB < PUB & PUB <= UB;
if any(~ordidx)
    error('vbmc:StrictBounds', ...
        'For each variable, hard and plausible bounds should respect the ordering LB < PLB < PUB < UB.');
end

% Test that plausible bounds are reasonably separated from hard bounds
ordidx = LB_eff < PLB & PUB < UB_eff;
if any(~ordidx)
    warning('vbmc:TooCloseBounds', ...
        'For each variable, hard and plausible bounds should not be too close. Moving plausible bounds.');
    PLB = max(PLB,LB_eff);
    PUB = min(PUB,UB_eff);
end

% Check that all X0 are inside the plausible bounds, move bounds otherwise
if any(any(bsxfun(@le,x0,PLB))) || any(any(bsxfun(@ge,x0,PUB)))
    warning('vbmc:InitialPointsOutsidePB', ...
        'The starting points X0 are not inside the provided plausible bounds PLB and PUB. Expanding the plausible bounds...');
    PLB = min(PLB,min(x0,[],1));
    PUB = max(PUB,max(x0,[],1));
end

% Test order of bounds
ordidx = LB < PLB & PLB < PUB & PUB < UB;
if any(~ordidx)
    error('vbmc:StrictBounds', ...
        'For each variable, hard and plausible bounds should respect the ordering LB < PLB < PUB < UB.');
end

% Test that variables are either bounded or unbounded (not half-bounded)
halfbnd = (isinf(LB) & isfinite(UB)) | (isfinite(LB) & isinf(UB));
if any(halfbnd)
    error('vbmc:HalfBounds', ...
        'Each variable needs to be unbounded or bounded. Variables bounded only below/above are not supported.');
end


function deposit(varargin)
%*************************************************
%DATE: 11/8/2007 (created); 	23/8/2007 (modified)
%FUNCTION:
%INPUTS:
%OUTPUTS:
%*************************************************
materialName 	= varargin{1};
thickness		= varargin{2};

shiftFrom	= 0;

%=======================material color table=========================
global Layerprofile;
global layerMatrix;

switch materialName
	case 'Si'
		materialNrm = 1;
	case 'SiO2'
		materialNrm = 2;
	case 'metal'
		materialNrm = 3;
	case 'SiNi'
		materialNrm = 4;
	case 'PoSi'
		materialNrm = 5;
	case 'PR'
		materialNrm = 9;
end

newLayerMatrixHeight = round (thickness);
newLayerMatrix = zeros(newLayerMatrixHeight, 96);

if sum(sum(layerMatrix)) == 0 % if it is empty
layerMatrix =  newLayerMatrix + materialNrm;
else
layerMatrix = vertcat(newLayerMatrix, layerMatrix); % unite
	for pointIndex = 1:96
		depositFrom = find(layerMatrix(:, pointIndex), 1,'last');
      	layerMatrix (((depositFrom+1 ) : (depositFrom+1+newLayerMatrixHeight)), pointIndex) = materialNrm;
	end
end

layerMatrix = rounddeposit(layerMatrix, materialNrm);

function layerMatrix = rounddeposit(layerMatrix, materialNrm)
%*************************************************
%DATE: 23/8/2007 (created); 	23/8/2007 (modified)
%FUNCTION:
%INPUTS:
%OUTPUTS:
%*************************************************

	for pointIndex = 1:96
		depositFromArray(pointIndex) = find(layerMatrix(:, pointIndex), 1,'last');
	end

	oldDepositFromArray = depositFromArray;
save('old', 'oldDepositFromArray');
	for pointIndex = 1:95
		diff = oldDepositFromArray(pointIndex) - oldDepositFromArray(pointIndex+1);
		if (diff>0)
		addDepositArray = calculateroundarray(diff);

		index = pointIndex + 1;
		nrm   = 1;
			while (index <= 96 & nrm <= size(addDepositArray, 2))
			depositFromArray(index) = depositFromArray(index) + addDepositArray(nrm);
			index = index + 1;
			nrm   = nrm + 1;
			end
		end
	end

	for pointIndexI = 0:94
		pointIndex = 96 - pointIndexI;
		diff = oldDepositFromArray(pointIndex) - oldDepositFromArray(pointIndex-1);
		if (diff>0)
		addDepositArray = calculateroundarray(diff);

			index = pointIndex - 1;
			nrm   = 1;
			while (index > 0 & nrm <= size(addDepositArray, 2))
			depositFromArray(index) = depositFromArray(index) + addDepositArray(nrm);
			index = index - 1;
			nrm   = nrm + 1;
			end
		end
	end

	for pointIndex = 1:96
		layerMatrix(oldDepositFromArray(pointIndex):depositFromArray(pointIndex), pointIndex) = materialNrm;
	end

function roundArray = calculateroundarray(diff)
%*************************************************
%FUNCTION:
%INPUTS:
%OUTPUTS:
%*************************************************
roundArray(1) = 0;
		a = -4/diff;
		b = diff;
		for pointDiff=1:round(diff/2)
		temp = floor (a * pointDiff^2 + b);
			if (temp < 0)
				return;
			end
			roundArray(pointDiff) = temp;
		end


function [N,cN,cF] = per_corner_normals(V,F,varargin)
  % N = per_corner_normals(V,F,varargin)
  %
  % Inputs:
  %   V  #V by 3 list of mesh vertex positions
  %   F  #F by 3 list of mesh triangle indices into V
  %   Optional:
  %     'Angle'  followed by dihedral angle considered sharp. {pi*0.11}
  % Outputs:
  %   N  3*#F by 3 list of per-corner normals
  %   cN  #cN by 3 list of per-corner normals before expanding to corners
  %   cF  #F by 3 list of indices into cN
  %
  % Example:
  %   [V,F] = cylinder_mesh(1,20,'Caps',true);
  %   N = per_corner_normals(V,F);
  %   tsurf(F,V,'FaceColor','w',falpha(1,0.5));
  %   hold on;
  %   qvr(V(F,:),N);
  %   hold off;
  %   axis equal;
  %
  % Example:
  %   [V,F] = cylinder_mesh(1,20,'Caps',true);
  %   N = per_corner_normals(V,F);
  %   % zip into gltf style mesh (shared F, per-vertex attributes)
  %   [uVN,~,I] = unique([V(F,:) N],'rows');
  %   uV = uVN(:,1:3);
  %   uN = uVN(:,3+(1:3));
  %   uF = reshape(1:size(F,1)*3,size(F));
  %   uF = I(uF);
  %
  % Example:
  %   [V,F] = cylinder_mesh(1,20,'Caps',true);
  %   % zip into an obj style mesh (indexed V,N with separate faces)
  %   [~,cN,cF] = per_corner_normals(V,F);
  %   writeOBJ('per_corner_normals-test.obj',V,F,[],[],cN,cF);
  %
  %

  % Map of parameter names to variable names
  params_to_variables = containers.Map( ...
    {'Angle'}, ...
    {'angle'});
  v = 1;
  while v <= numel(varargin)
    param_name = varargin{v};
    if isKey(params_to_variables,param_name)
      assert(v+1<=numel(varargin));
      v = v+1;
      % Trick: use feval on anonymous function to use assignin to this workspace
      feval(@()assignin('caller',params_to_variables(param_name),varargin{v}));
    else
      error('Unsupported parameter: %s',varargin{v});
    end
    v=v+1;
  end

  E = sharp_edges(V,F);
  [cF,I] = cut_edges(F,E);
  cV = V(I,:);
  cN = per_vertex_normals(cV,cF);
  N = cN(cF,:);
end


function img = rendervol(imgpath, volpath, texture)
% Renders a volume which has been generated by the VRN lua script.

vol = readvol(volpath, [192 192 200]);

im = imread(imgpath);

Q = isosurface(vol);
Q.vertices(:,3) = Q.vertices(:,3) * 0.5;
Q.EdgeColor = 'none';
Q.FaceColor = [120 154 194]/255;

if texture
    red = double(im(:,:,1));
    green = double(im(:,:,2));
    blue = double(im(:,:,3));
    [vcx,vcy] = meshgrid(1:192,1:192);
    vc = [vcx(:) vcy(:) red(:) green(:) blue(:)];
    cols = knnsearch(vc(:,1:2), Q.vertices(:,1:2));
    Q.FaceVertexCData = uint8(vc(cols,3:5));
    Q.FaceColor = 'interp';
end

figure
imshow(im, 'Border', 'tight');
hold on
patch(Q);
hold on
h = camlight;
lightangle(h, -10, 80)
lighting gouraud
material dull
view(0,90)
cap = getframe;
img = cap.cdata;


function calcFeatureImportanceRF_Old(pathRF,nPerms,seed)

startpath = pwd;

cd(pathRF), load('training'), load('testing')
nameOutcomes = fieldnames(training.outcomes); nOutcomes = numel(nameOutcomes);
fSetNames = fieldnames(training.textures.(nameOutcomes{1})); nFset = numel(fSetNames);

nInst = numel(testing.outcomes.(nameOutcomes{1})); % Number of testing patients
perms = zeros(nInst,nPerms);
rng(seed)
for p = 1:nPerms
    perms(:,p) = datasample(1:nInst,nInst,'Replace',false)';
end

for o = 1:nOutcomes
    outcome = testing.outcomes.(nameOutcomes{o});
    for f = 1:nFset
        results = load(['testResults_',fSetNames{f},'_',nameOutcomes{o}]); results = struct2cell(results); results = results{1};
        AUC_baseline = results.AUC;
        RF = load(['RF_',fSetNames{f},'_',nameOutcomes{o}]); RF = struct2cell(RF); RF = RF{1};
        indClinic = training.clinical.bestAdd.(nameOutcomes{o}).(fSetNames{f});
        text = testing.textures.(nameOutcomes{o}).(fSetNames{f}); nText = size(text,2);
        tableTest = [text,testing.clinical.table(:,indClinic)];
        varNames = tableTest.Properties.VariableNames'; nVar = numel(varNames);
        percentAUCdecrease = zeros(nVar,1);
        for v = 1:nVar
            tableTemp = tableTest;
            percentAUCdecrease_temp  = 0;
            for p = 1:nPerms
                tableTemp(:,v) = tableTest(perms(:,p),v);
                [prob] = predictRF(tableTemp,RF);
                [AUC,~,~,~] = calcPerformMetrics(prob,outcome,0.5);
                percentAUCdecrease_temp = percentAUCdecrease_temp + (AUC - AUC_baseline)/AUC_baseline;
            end
            percentAUCdecrease_temp = percentAUCdecrease_temp/nPerms;
            percentAUCdecrease(v) = percentAUCdecrease_temp;
        end
        [percentAUCdecrease,ind] = sort(percentAUCdecrease,'descend');
        variableImportance.(nameOutcomes{o}).(fSetNames{f}).percentAUCdecrease = percentAUCdecrease;
        varNames = varNames(ind);
        variableImportance.(nameOutcomes{o}).(fSetNames{f}).varNames = varNames;
    end
end
save('testingVariableImportance','variableImportance')

cd(startpath)
end


function [prob,p] = coinRead(filename,type,print)
%coinRead  Read a Mathematical File and convert to Matlab Values
%
% prob = coinRead(filename,type) reads the file specified by filename
% and problem type specified by type and converts it to an optiprob. If you
% do not specify a file extension it will default to the type specified.
% The returned structure is solver independent so the user can manually
% extract matrices as required or supply it directly to opti().
%
% prob = coinRead(filename,type,print) specifies to display information
% and error messages in MATLAB when print > 0. The default is 0.
%
%   Available File Types:
%       - MPS  [.mps]
%       - QPS  [.qps]
%       - LP   [.lp]  (Appears to be CPLEX LP version - Simplified)
%       - GMPL [.mod] (Subset of AMPL, implemented via GLPK)
%       - GAMS [.gms] (Only first COIN version... Will probably not work)
%
% You may specify a full path to the file, or if you specify a filename
% only, it must exist on the MATLAB path.
%
% The routines underneath use COIN-OR & GLPK utilities for File IO. See
% attached EPL License for COIN-OR and GPL for GLPK.

%   Copyright (C) 2011 Jonathan Currie (I2C2)

%Quick Checks
if(~exist('type','var') || isempty(type))
    %See if contained within file name
    dots = strfind(filename,'.');
    if(isempty(dots))
        error('You must supply the file type to this function');
    else %hopefully got a good ext, will check below
        type = filename(dots(end)+1:end);
    end
end
if(~exist('print','var') || isempty(print))
    print = 0;
end
if(~ischar(filename))
    error('Filename must be a char array!');
end

%Determine file type
switch(lower(type))
    case 'mps'
        filetype = 'mps';
    case 'qps'
        filetype = 'qps';
    case 'lp'
        filetype = 'lp';
    case {'gmpl','mod'}
        filetype = 'mod';
    case {'gams','gms'}
        filetype = 'gms';
    otherwise
        error('Unknown file type %s',type);
end

%Check if extension is specified
if(isempty(strfind(filename,'.')))
    filename = [filename '.' filetype];
end

% If filename includes absolute path, we can skip which
if(~isempty(strfind(filename,':')))
    p = filename;
    %Check it exists
    if(~exist(filename,'file'))
        error('Cannot locate file %s!',filename);
    end
else %Locate the full path to the file (must be on matlab path)
    p = which(filename);
    %Check it exists
    if(isempty(p))
        error('Cannot locate file %s!',filename);
    end
end

%Call coin-or mex
[f,A,rl,ru,lb,ub,ivars,H,name,sostype,sosind,soswt,objc] = coinR(p,filetype,print);

%No guarantee matrices have been created with correct order (T.Kelman, 6/9/12), double transpose to ensure order)
H = H'';
A = A'';

%Remove H if all zero
if(~nnz(H))
    H = [];
%Assume we need to make symmetric if e.g. [8 0; 2 10]
elseif(nnz(triu(H,1)) == 0 && ~(nnz(tril(H,-1)) == 0))
    optiwarn('OPTI:COINR_QUADOBJ','Assumed QOBJ has been specified as lower triangular only, forcing to symmetric');
    H = H + tril(H,-1)';
%Check for upper tri only
elseif(nnz(tril(H,-1)) == 0 && ~(nnz(triu(H,1)) == 0))
    optiwarn('OPTI:COINR_QUADOBJ','Assumed QOBJ has been specified as upper triangular only, forcing to symmetric');
    H = H + triu(H,1)';
end

%Build Integer String
int = repmat('C',size(f')); ivars = logical(ivars);
int(ivars) = 'I'; %assume just integer variables (no binary - even with MPS)
%Build SOS
if(~isempty(sostype))
    sostype = num2str(sostype);
    if(length(sostype) == 1)
        sosind = sosind{1}; %remove cells
        soswt = soswt{1};
    end
else
    sostype = []; sosind = []; soswt = [];
end

%Build return problem
prob = optiprob('H',H,'f',f,'objbias',objc,'lin',A,rl,ru,'bounds',lb,ub,'int',int,'name',name,'sos',sostype,sosind,soswt);


function [hAx]=axesArea(varargin)
%function [hAx]=axesArea(hAx,varargin)
%
% Set the margine of the axis by specifying a vector of distance to the figure edges.
%
% Input:
%  [varargin=hAx] = axis handle
%    [varargin=p] = position spec: 1, 2 or 4 element vector that specify the distance
%                   from the edges of the figure by a percentage number between (0-49).
%                   If 1 element it is used for all margins.
%                   If 2 elements, p=[x-margins, y-margins].
%                   If 4 elements, p=[left, lower, right, upper] margins.
% Output:
%  [hAx] = axis handle of the axes.
%
% See also: axis, axes, ishandle, set
%
% By: Michael Wu  --  michael.wu@lithium.com (Mar 2009)
%
%====================

% Check if 1st input is axis handle
%--------------------
if ishghandle(varargin{1},'axes')
	hAx=varargin{1};
	p=varargin{2};
else
	hAx=gca;
	p=varargin{1};
end

% Process input arguments
%--------------------
p(p<0)=0;
p(p>49)=49;
p=p/100;

% Compute position property to be set
%--------------------
switch length(p)
	case 1
		xmin=p;
		ymin=xmin;
		xlen=1-2*p;
		ylen=xlen;
	case 2
		xmin=p(1);
		ymin=p(2);
		xlen=1-2*p(1);
		ylen=1-2*p(2);
	case 4
		xmin=p(1);
		ymin=p(2);
		xlen=1-p(1)-p(3);
		ylen=1-p(2)-p(4);
	otherwise
		% Default Matlab position setting
		%--------------------
		xmin=0.13;
		ymin=0.11;
		xlen=0.775;
		ylen=0.815;
end

% Set new position property of the axes
%--------------------
set(hAx,'position',[xmin ymin xlen ylen]);


%[ANR,SNR,STR]	=  ASORT(INP,'OPT',...);
% S		=  ASORT(INP,'OPT',...);
%		   to sort alphanumeric strings numerically if
%		   they contain one properly formatted number
%		   otherwise, ascii dictionary sorting is applied
%
% INP	unsorted input:
%	- a char array
%	- a cell array of strings
% OPT	options
%  -s	- sorting option
%	  '-s','ascend'					[def]
%	  '-s','descend'
%  -st	- force output form S				[def: nargout dependent]
%  -t	- replace matching template(s) with one space
%	  prior to sorting
%	  '-t','template'
%	  '-t',{'template1','template2',...}
%  -w	- remove space(s) prior to sorting
%
%	  NOTE	-t/-w options are processed in the
%		      order that they appear in
%		      the command line
%
%  -v	- verbose output				[def: quiet]
%  -d	- debug mode
%	  save additional output in S
%	  .c:	lex parser input
%	  .t:	lex parser table
%	  .n:	lex parser output
%	  .d:	numbers read from .n
%
% ANR	numerically sorted alphanumeric strings		[eg, 'f.-1.5e+2x.x']
%	- contain one number that can be read by
%	  <strread> | <sscanf>
% SNR	ascii dict  sorted alphanumeric strings
% http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=7212#
%
%	- contain more than one number			[eg, 'f.-1.5e +2.x']
%	- contain incomplete|ambiguous numbers		[eg, 'f.-1.5e+2.x']
% STR	ascii dict  sorted strings
%	- contain no numbers				[eg, 'a test']
%
% S	structure with fields
%	.anr
%	.srn
%	.str

% created:
%	us	03-Mar-2002
% modified:
%	us	30-Mar-2005 11:57:07 	/ TMW R14.sp2

%--------------------------------------------------------------------------------
function	varargout=asort(inp,varargin)

varargout(1:nargout)={[]};
if	~nargin
	help(mfilename);
	return;
end

% - common parameters/options
n=[];
ds=[];
anr={};
snr={};
str={};
smod='ascend';	% sorting option
tmpl={};	% template(s)
sflg=false;	% output  mode: structure
tflg=false;	% remove  template(s)
dflg=false;	% debug   mode
vflg=false;	% verbose output
wflg=false;	% remove  spaces

if	nargin > 1
	ix=find(strcmp('-s',varargin));
	if	~isempty(ix) && nargin > ix(end)+1
		smod=varargin{ix(end)+1};
	end
	ix=find(strcmp('-t',varargin));
	if	~isempty(ix) && nargin > ix(end)+1
		tflg=ix(end);
		tmpl=varargin{ix(end)+1};
	end
	if	find(strcmp('-d',varargin));
		dflg=true;
	end
	if	find(strcmp('-st',varargin));
		sflg=true;
	end
	if	find(strcmp('-v',varargin));
		vflg=true;
	end
	ix=find(strcmp('-w',varargin));
	if	~isempty(ix)
		wflg=ix(end);
	end
end
%   spec numbers
ntmpl={
	' inf '
	'+inf '
	'-inf '
	' nan '
	'+nan '
	'-nan '
	};
%   spec chars
ctmpl={
	'.'	% decimal point
	'd'	% exponent
	'e'	% exponent
	};

if	nargout <= 3
	varargout{1}=inp;
else
	disp(sprintf('ASORT> too many output args [%-1d/%-1d]\n',nargout,3));
	help(mfilename);
	return;
end
if	isempty(inp)
	disp(sprintf('ASORT> input is empty'));
	return;
end

ti=clock;
winp=whos('inp');
switch	winp.class
	case	'cell'
		if	~iscellstr(inp)
			disp(sprintf('ASORT> cell is not an array of strings'));
			return;
		end
		inp=inp(:);
		[ins,inx]=sort(inp);
	case	'char'
		%		[ins,inx]=sortrows(inp);
		inp=cstr(inp);
	otherwise
		disp(sprintf('ASORT> does not sort input of class <%s>',winp.class));
		return;
end

inp=inp(:);
inp=setinp(inp,tmpl,[tflg wflg]);
[ins,inx]=sort(inp);
if	strcmp(smod,'descend')
	ins=ins(end:-1:1,:);
	inx=inx(end:-1:1);
end
ins=inp(inx);
c=lower(char(ins));
wins=whos('c');
[cr,cc]=size(c);

% - LEXICAL PARSER
%--------------------------------------------------------------------------------
% - extend input on either side for search
c=[' '*ones(cr,2) c ' '*ones(cr,2)];

% - search for valid alphanumeric items in strings
%   numbers/signs
t=(c>='0'&c<='9');
t=t|c=='-';
t=t|c=='+';
[tr,tc]=size(t);
%   decimal points
%   note: valid numbers with dec points must follow these templates
%         nr.nr
%	  sign.nr
%         nr.<SPACE>
%         <SPACE>.nr
ix1=	 t(:,1:end-2) & ...
	~isletter(c(:,1:end-2)) & ...
	c(:,2:end-1)=='.';
t(:,2:end-1)=t(:,2:end-1)|ix1;
ix1=	(t(:,3:end) & ...
	(~isletter(c(:,3:end)) & ...
	~isletter(c(:,1:end-2))) | ...
	(c(:,3:end)=='e' | ...
	c(:,3:end)=='d')) & ...
	c(:,2:end-1)=='.';
t(:,2:end-1)=t(:,2:end-1)|ix1;
%		t(:,3:end)=t(:,3:end)|ix1;
%   signs
t(c=='-')=false;
t(c=='+')=false;
ix1=	 t(:,3:end) & ...
	(c(:,2:end-1)=='-' | ...
	c(:,2:end-1)=='+');
t(:,2:end-1)=t(:,2:end-1)|ix1;
%   exponents
ix1=	 t(:,1:end-2) & ...
	(c(:,2:end-1)=='e' | ...
	c(:,2:end-1)=='d');
t(:,2:end-1)=t(:,2:end-1)|ix1;
%   spec numbers
c=reshape(c.',1,[]);
t=t';
ic=[];
for	j=1:numel(ntmpl)
	ic=[ic,strfind(c,ntmpl{j})];
end
ic=sort(ic);
for	i=1:numel(ic)
	ix=ic(i)+0:ic(i)+4;
	t(ix)=true;
end
t=t';
c=reshape(c.',[tc,tr]).';
t(c==' ')=false;
%--------------------------------------------------------------------------------

% - only allow one number per string
il=~any(t,2);
ib=strfind(reshape(t.',1,[]),[0 1]);
if	~isempty(ib)
	ixe=cell(3,1);
	n=reshape(char(t.*c).',1,[]);
	for	i=1:numel(ctmpl)
		id=strfind(n,ctmpl{i});
		if	~isempty(id)
			[dum,dum,ixu{i},ixe{i}]=dupinx(id,tc);
		end
	end
	in=false(tr,1);
	im=in;
	%   must check for anomalous cases like <'.d'>
	id=sort(...
		[find(n>='0' & n<='9'),...
		strfind(n,'inf'),...
		strfind(n,'nan')]);
	%		[ibu,ibd,ixbu,ixe{i+1}]=dupinx(id,tc);
	[ibu,ibd,ixbu,ixbd]=dupinx(id,tc);
	in(ixbu)=true;
	in(ixbd)=true;
	[ibu,ibd,ixbu,ixbd]=dupinx(ib,tc);
	im(ixbu)=true;
	in=in&im;
	in([ixe{:}])=false;
	il=~any(t,2);
	ia=~(in|il);

	% - read valid strings
	n=t(in,:).*c(in,:);
	n(n==0)=' ';
	n=char(n);
	dn=strread(n.','%n');
	if	numel(dn) ~= numel(find(in))
		%disp(sprintf('ASORT> unexpected fatal error reading input!'));
		if	nargout
			s.c=c;
			s.t=t;
			s.n=n;
			s.d=dn;
			varargout{1}=s;
		end
		return;
	end

	% - sort numbers
	[ds,dx]=sort(dn,1,smod);
	in=find(in);
	anr=ins(in(dx));
	snr=ins(ia);
end
str=ins(il);
to=clock;

% - prepare output
if	nargout < 3 || sflg
	s.magic='ASORT';
	s.ver='30-Mar-2005 11:57:07';
	s.time=datestr(clock);
	s.runtime=etime(to,ti);
	s.input_class=winp.class;
	s.input_msize=winp.size;
	s.input_bytes=winp.bytes;
	s.strng_class=wins.class;
	s.strng_msize=wins.size;
	s.strng_bytes=wins.bytes;
	s.anr=anr;
	s.snr=snr;
	s.str=str;
	if	dflg
		s.c=c;
		s.t=t;
		s.n=n;
		s.d=ds;
	end
	varargout{1}=s;
else
	s={anr,snr,str};
	for	i=1:nargout
		varargout{i}=s{i};
	end
end

if	vflg
	inp=cstr(inp);
	an=[{'--- NUMERICAL'};		anr];
	as=[{'--- ASCII NUMBERS'};	snr];
	at=[{'--- ASCII STRINGS'};	str];
	nn=[{'--- NUMBERS'};		num2cell(ds)];
	ag={' ';' ';' '};
	u=[{'INPUT'};			inp;ag];
	v=[{'ASCII SORT'};		ins;ag];
	w=[{'NUM SORT'};		an;as;at];
	x=[{'NUM READ'};		nn;as;at];
	w=[u,v,w,x];
	disp(w);
end

return;
%--------------------------------------------------------------------------------
function	c=cstr(s)
% - bottleneck waiting for a good <cellstr> replacement
%   it consumes ~75% of <asort>'s processing time!

c=s;
if	ischar(s)
	sr=size(s,1);
	c=cell(sr,1);
	for	i=1:sr
		c{i}=s(i,:);	% no deblanking!
	end
end
return;
%--------------------------------------------------------------------------------
function	[idu,idd,ixu,ixd]=dupinx(ix,nc)
% - check for more than one entry/row in a matrix of column size <nc>
%   unique    indices:	idu / ixu
%   duplicate indices:	idd / ixd

if	isempty(ix)
	idu=[];
	idd=[];
	ixu=[];
	ixd=[];
	return;
end
id=fix(ix/nc)+1;
idi=diff(id)~=0;
ide=[true idi];
idb=[idi true];
idu=idb & ide;
idd=idb==1 & ide==0;
ixu=id(idu);
ixd=id(idd);
return;
%--------------------------------------------------------------------------------
function	inp=setinp(inp,tmpl,flg)
% - remove space(s) and/or templates

if	isempty(inp) || ~any(flg)
	return;
end

for	i=sort(flg)
	switch	i
		case	flg(1)
			if	ischar(tmpl)
				tmpl={tmpl};
			end
			for	i=1:numel(tmpl)
				inp=strrep(inp,tmpl{i},' ');
			end
		case	flg(2)
			inp=strrep(inp,' ','');
	end
end
return;
%--------------------------------------------------------------------------------


function[b]=minmin(x)
%MINMIN  MINMIN(X)=MIN(X(ISFINITE(X)))
%   _________________________________________________________________
%   This is part of JLAB --- type 'help jlab' for more information
%   (C) 2004--2012 J.M. Lilly --- type 'help jlab_license' for details
b=min(x(isfinite(x(:))));


%% Copyright 2011-2012 The MathWorks, Inc.
function ReplaceCard()
% This is a simple demo to show a match & replace of the video
% data.
%
% To try this,
% 1. Get a deck of playing cards with "MATLAB & Simulink" logo or print
% the file "MWqueen_crop_small.bmp" on a color printer.
% 2. Run this script in MATLAB
% 3. Show printed queen of diamond in front of the usb web camera.
% 4. Press either 1 or 2 of your keyboard to see the image changing
% 5. Press 0 of your keyboard to reset it back to queen of diamonds
%
% Original version created by Takuya Otani
% Senior Application Engineer, MathWorks, Japan
%

clear all; close all; clc; imaqreset;
global process_flag;
process_flag = false;

%% Define Geometric Transformation Objects
gte = vision.GeometricTransformEstimator;
gte.Method = 'Random Sample Consensus (RANSAC)';
gte.Transform = 'Nonreflective similarity';
gte.NumRandomSamplingsMethod = 'Desired confidence';
gte.MaximumRandomSamples = 500;
gte.DesiredConfidence = 99.8;

agt = vision.GeometricTransformer;
agt.OutputImagePositionSource = 'Property';
agt.OutputImagePosition = [1 1 480 640];

halphablender = vision.AlphaBlender( ...
    'Operation', 'Binary mask', 'MaskSource', 'Input port');

%% Setup Image Acquisition
hCamera = videoinput('winvideo', 1, 'RGB24_640x480');
hCamera.ReturnedColorspace = 'rgb';
hCamera.FramesPerTrigger =  1;

% Initialization of the camera
triggerconfig(hCamera, 'manual');
start(hCamera);

%% Load reference image data (queen of diamond) Pad the image data
%% to match 480x640 dimension
ref_img = imread('MWqueen_crop_small.bmp');
ref_img_pad = padarray(ref_img, [140 0], 'pre'); %340x240
ref_img_pad = padarray(ref_img_pad, [0 400], 'pre');
ref_img_gray = rgb2gray(ref_img_pad);
ref_keys = detectSURFFeatures(ref_img_gray, 'MetricThreshold', 1000);
% Extract SURF Features
[ref_features  ref_validPts] = extractFeatures(ref_img_gray,  ref_keys, 'SURFSize', 64);

% Open a Figure window which accepts keyboard inputs
figure('KeyPressFcn', @keycontrol);
himg = imshow(zeros(480, 640, 3, 'uint8'));

%% image data to replace
global rep_img_pad;

while ishandle(himg)

  %% Capture data from the camera, and extrace SURF features, and descriptors
  vid_img = getsnapshot(hCamera);
  vid_img = im2single(vid_img);  vid_img_gray = rgb2gray(vid_img);
  vid_keys = detectSURFFeatures(vid_img_gray, 'MetricThreshold', 1000);
  [vid_features vid_validPts] = extractFeatures(vid_img_gray, vid_keys, 'SURFSize', 64);

  %Here, I am using a MATLAB Coder compiled version of matchFeatures
  %To speed things up.
  %index_pairs = matchFeatures(ref_features, vid_features, 'MatchThreshold', 2);
  index_pairs = matching_fcn_mex(ref_features, vid_features, 2);

  if process_flag == true & size(index_pairs,1) > 60

    ref_matched_points = ref_validPts(index_pairs(:,1));
    vid_matched_points = vid_validPts(index_pairs(:,2));

    % Estimate the transformation matrix
    [tform_matrix inlierIdx] = step(gte, ...
                                    ref_matched_points.Location, ...
                                    vid_matched_points.Location);

    % peform a geometric transformation
    rep_img_trans = step(agt, rep_img_pad, tform_matrix);

    % Replace the card
    mask = rep_img_trans(:,:,1) > 0;
    mosaic = step(halphablender, vid_img, rep_img_trans, mask);

    set(himg, 'CData', mosaic);
  else
    set(himg, 'CData', vid_img);
  end

  drawnow;
end

% When keyboard is pressed, replace rep_img data.
function keycontrol(src,evnt)
global rep_img_pad;
global process_flag;

if evnt.Character == '0'
    process_flag = false;
elseif evnt.Character == '1'
    process_flag = true;
    rep_img = imread('MWJoker_crop_small.bmp');
    rep_img = im2single(rep_img);
    rep_img_pad = padarray(rep_img, [140 0], 'pre');
    rep_img_pad = padarray(rep_img_pad, [0 400], 'pre');
elseif evnt.Character == '2'
    process_flag = true;
    rep_img = imread('MWspade_crop_small.bmp');
    rep_img = im2single(rep_img);
    rep_img_pad = padarray(rep_img, [140 0], 'pre');
    rep_img_pad = padarray(rep_img_pad, [0 400], 'pre');
end


function [xVi, mask] = spm_est_non_sphericity(SPM)
% Non-sphericity estimation using ReML
% FORMAT [xVi, mask] = spm_est_non_sphericity(SPM)
%
% Required fields of SPM structure (see spm_spm):
% SPM.xY.VY  - nScan x 1 struct array of file handles
% SPM.xX     - structure containing design matrix information
% SPM.xX.W   - optional whitening/weighting matrix
% SPM.xVi    - structure describing intrinsic non-sphericity
% SPM.xM     - structure containing masking information
%
% Return xVi from SPM.xVi with extra fields:
% xVi.V      - estimated non-sphericity, trace(V) = rank(V)
% xVi.h      - hyperparameters  xVi.V = xVi.h(1)*xVi.Vi{1} + ...
% xVi.Cy     - spatially whitened <Y*Y'> (used by ReML to estimate h)
%
% mask       - logical array of voxels within analysis mask
%__________________________________________________________________________
%
% In a first pass, voxels over which non-sphericity will be estimated are
% selected using an 'effects of interest' F-contrast (can be specified in
% SPM.xVi.Fcontrast) and critical threshold taken from SPM defaults
% stats.<modality>.UFp.
% The sample covariance matrix (xVi.Cy) is then estimated by pooling over
% these voxels, assuming V is constant over them.
% Finally, SPM will invoke ReML to estimate hyperparameters (xVi.h) of an
% array of non-sphericity components (xVi.Vi), providing a high precise
% estimate of the non-sphericity matrix (xVi.V).
%__________________________________________________________________________
% Copyright (C) 1994-2019 Wellcome Trust Centre for Neuroimaging

% Karl Friston & Guillaume Flandin
% $Id: spm_est_non_sphericity.m 7577 2019-04-24 08:59:56Z guillaume $

SVNid = '$Rev: 7577 $';

%-Say hello
%--------------------------------------------------------------------------
spm('FnBanner',mfilename,SVNid);

%-Get data, design, mask and variance components details
%--------------------------------------------------------------------------
VY           = SPM.xY.VY;
M            = VY(1).mat;
DIM          = VY(1).dim;
YNaNrep      = spm_type(VY(1).dt(1),'nanrep');

xX           = SPM.xX;
nScan        = size(xX.X,1);
if ~isfield(xX,'W')
    xX.W     = speye(nScan,nScan);
end

xM           = SPM.xM;

xVi          = SPM.xVi;

%-Compute Hsqr and F-threshold under i.i.d.
%--------------------------------------------------------------------------
xX.xKXs      = spm_sp('Set',spm_filter(xX.K,xX.W*xX.X));
xX.xKXs.X    = full(xX.xKXs.X);
xX.pKX       = spm_sp('x-',xX.xKXs);

if isfield(xVi,'Fcontrast')
    Fcname   = 'User-specified contrast';
    xCon     = spm_FcUtil('Set',Fcname,'F','c',xVi.Fcontrast,xX.xKXs);
else
    Fcname   = 'effects of interest';
    iX0      = [xX.iB xX.iG];
    xCon     = spm_FcUtil('Set',Fcname,'F','iX0',iX0,xX.xKXs);
end

if ~isempty(xCon(1).c)
    X1o      = spm_FcUtil('X1o', xCon(1),xX.xKXs);
    Hsqr     = spm_FcUtil('Hsqr',xCon(1),xX.xKXs);
    trMV     = spm_SpUtil('trMV',X1o);
else
    % Force all voxels to enter non-sphericity
    trMV     = 1;
    Hsqr     = Inf;
end
trRV         = spm_SpUtil('trRV',xX.xKXs);

%-Threshold for voxels entering non-sphericity estimates
%--------------------------------------------------------------------------
try
    modality = lower(spm_get_defaults('modality'));
    UFp      = spm_get_defaults(['stats.' modality '.ufp']);
catch
    UFp      = 0.001;
end
xVi.UFp      = UFp;
UF           = spm_invFcdf(1 - UFp,[trMV,trRV]);

%==========================================================================
%-         P O O L E D   V A R I A N C E   E S T I M A T I O N
%==========================================================================

%-Get explicit mask(s)
%==========================================================================
mask = true(DIM);
for i = 1:numel(xM.VM)
    if ~(isfield(SPM,'xVol') && isfield(SPM.xVol,'G'))
        %-Assume it fits entirely in memory
        C = spm_bsplinc(xM.VM(i), [0 0 0 0 0 0]');
        v = true(DIM);
        [x1,x2] = ndgrid(1:DIM(1),1:DIM(2));
        for x3 = 1:DIM(3)
            M2  = inv(M\xM.VM(i).mat);
            y1 = M2(1,1)*x1+M2(1,2)*x2+(M2(1,3)*x3+M2(1,4));
            y2 = M2(2,1)*x1+M2(2,2)*x2+(M2(2,3)*x3+M2(2,4));
            y3 = M2(3,1)*x1+M2(3,2)*x2+(M2(3,3)*x3+M2(3,4));
            v(:,:,x3) = spm_bsplins(C, y1,y2,y3, [0 0 0 0 0 0]') > 0;
        end
        mask = mask & v;
        clear C v x1 x2 x3 M2 y1 y2 y3
    else
        if spm_mesh_detect(xM.VM(i))
            v = full(xM.VM(i).private.cdata) > 0;
        else
            v = spm_mesh_project(gifti(SPM.xVol.G), xM.VM(i)) > 0;
        end
        mask = mask & v(:);
        clear v
    end
end

Cy        = 0;                                  %-<Y*Y'> spatially whitened
Cm        = mask;                               %-mask of selected voxels

%-Split data into chunks
%==========================================================================
chunksize = floor(spm_get_defaults('stats.maxmem') / 8 / nScan);
nbchunks  = ceil(prod(DIM) / chunksize);
chunks    = min(cumsum([1 repmat(chunksize,1,nbchunks)]),prod(DIM)+1);

spm_progress_bar('Init',nbchunks,'Hyperparameter estimation','Chunks');

for i=1:nbchunks
    chunk = chunks(i):chunks(i+1)-1;

    %-Report progress
    %======================================================================
    if i > 1, fprintf(repmat(sprintf('\b'),1,72)); end                  %-#
    fprintf('%-40s: %30s', sprintf('Chunk %3d/%-3d',i,nbchunks),...
                           '...processing');                            %-#

    %-Get data & construct analysis mask
    %----------------------------------------------------------------------
    Y       = zeros(nScan,numel(chunk));
    cmask   = mask(chunk);
    for j=1:nScan
        if ~any(cmask), break, end                 %-Break if empty mask

        Y(j,cmask) = spm_data_read(VY(j),chunk(cmask));%-Read chunk of data

        cmask(cmask) = Y(j,cmask) > xM.TH(j);      %-Threshold (& NaN) mask
        if xM.I && ~YNaNrep && xM.TH(j) < 0        %-Use implicit mask
            cmask(cmask) = abs(Y(j,cmask)) > eps;
        end
    end
    cmask(cmask) = any(diff(Y(:,cmask),1));        %-Mask constant data
    mask(chunk)  = cmask;
    Cm(chunk)    = cmask;
    if ~any(cmask), continue, end
    Y       = Y(:,cmask);                          %-Data within mask

    %-Remove filter confounds
    %----------------------------------------------------------------------
    KWY      = spm_filter(xX.K,xX.W*Y);

    %-Ordinary Least Squares estimation
    %----------------------------------------------------------------------
    beta    = xX.pKX*KWY;                          %-Parameter estimates
    if any(cmask)
        res = spm_sp('r',xX.xKXs,KWY);             %-Residuals
    else
        res = zeros(nScan,0);
    end
    ResSS   = sum(res.^2);                         %-Residual SSQ
    clear res

    %-F-threshold & accumulate spatially whitened Y*Y'
    %----------------------------------------------------------------------
    j       = sum((Hsqr*beta).^2,1)/trMV > UF*ResSS/trRV;
    Cm(chunk(cmask)) = j;
    q       = nnz(j);
    if q
        q   = spdiags(sqrt(trRV./ResSS(j)'),0,q,q);
        Y   = Y(:,j)*q;
        Cy  = Cy + Y*Y';
    end

    %-Report progress
    %======================================================================
    fprintf('%s%30s',repmat(sprintf('\b'),1,30),'...done');             %-#
    spm_progress_bar('Set',i);
end

fprintf('\n');                                                          %-#
spm_progress_bar('Clear');

s = nnz(Cm);                                    %-Number of selected voxels
if ~s
    error('Please check your data: There are no significant voxels.');
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%-Estimate sample covariance matrix Y*Y' from voxels in Cm
% Cy        = 0;
% nbchunks  = ceil(s / chunksize);
% chunks    = min(cumsum([1 repmat(chunksize,1,nbchunks)]),s+1);
% vx        = find(Cm);
% for i=1:nbchunks
%     chunk = chunks(i):chunks(i+1)-1;
%     n     = numel(chunk);
%     Y     = zeros(nScan,n);
%     for j=1:nScan
%         Y(j,:) = spm_data_read(VY(j),vx(chunk));
%     end
%     % store ResSS above to prevent recomputing it here:
%     %--------------------------------------------------
%     KY    = spm_filter(xX.K,Y);
%     ResSS = spm_sp('r',xX.xKXs,KY);
%     ResSS = sum(ResSS.^2);
%     %--------------------------------------------------
%     q     = spdiags(sqrt(trRV./ResSS'),0,n,n);
%     Y     = Y*q;
%     Cy    = Cy + Y*Y';
% end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Cy = Cy / s;                                    %-Sample covariance matrix

%==========================================================================
%-                     R e M L   E S T I M A T I O N
%==========================================================================

%-ReML estimate of residual correlations through hyperparameters (h)
%--------------------------------------------------------------------------
str    = sprintf('Temporal non-sphericity (%d voxels)',s);
fprintf('%-40s: %30s\n',str,'...ReML estimation');                      %-#

%-ReML for separable designs and covariance components
%--------------------------------------------------------------------------
if isstruct(xX.K)
    m     = length(xVi.Vi);
    h     = zeros(m,1);
    V     = sparse(nScan,nScan);
    for i = 1:length(xX.K)

        % extract blocks from bases
        %------------------------------------------------------------------
        q     = xX.K(i).row;
        p     = [];
        Qp    = {};
        for j = 1:m
            if nnz(xVi.Vi{j}(q,q))
                Qp{end + 1} = xVi.Vi{j}(q,q);
                p           = [p j];
            end
        end

        % design space for ReML (with confounds in filter)
        %------------------------------------------------------------------
        Xp     = xX.X(q,:);
        try
            Xp = [Xp xX.K(i).X0];
        end

        % ReML
        %------------------------------------------------------------------
        fprintf('%-30s\n',sprintf('  ReML Block %i',i));
        [Vp,hp] = spm_reml(Cy(q,q),Xp,Qp);
        V(q,q)  = V(q,q) + Vp;
        h(p)    = hp;
    end
else
    [V,h] = spm_reml(Cy,xX.X,xVi.Vi);
end

%-Normalize non-sphericity and save hyperparameters
%--------------------------------------------------------------------------
V      = V*nScan/trace(V);
xVi.h  = h;
xVi.V  = V;                                     % Save non-sphericity xVi.V
xVi.Cy = Cy;                                    % spatially whitened <Y*Y'>


function sirfsdmap = doSIRFS_newIm(state,depthIm)
sigma = 3; % Controls the "bandwidth" of the input depth (the standard deviation of a Gaussian at which point the signal becomes reliable)
    mult = 5; % Controls the importance of the input depth (the multiplier on the loss)
    niters = 500;
    depthIm(isinf(depthIm)) = nan;
    depthIm = -depthIm;
    input_image = im2double(state.im);
    input_image(input_image<1/255) = 1/255;
    sirfsdmap = SIRFS(input_image, (state.mask), depthIm, ...
        ['params.DO_DISPLAY = 0; params.N_ITERS_OPTIMIZE = ' num2str(niters) ';params.USE_INIT_Z = true; params.INIT_Z_SIGMA = ',...
        num2str(sigma), ';params.multipliers.height.init = { ', num2str(mult), ' };']);
    sirfsdmap.mask = state.mask;
    sirfsdmap.im = input_image;
end


classdef NMPSO < ALGORITHM
% <multi/many> <real/integer>
% Novel multi-objective particle swarm optimization

%------------------------------- Reference --------------------------------
% Q. Lin, S. Liu, Q. Zhu, C. Tang, R. Song, J. Chen, C. A. Coello Coello,
% K. Wong, and J. Zhang, Particle swarm optimization with a balanceable
% fitness estimation for many-objective optimization problems, IEEE
% Transactions on Evolutionary Computation, 2018, 22(1): 32-46.
%------------------------------- Copyright --------------------------------
% Copyright (c) 2023 BIMK Group. You are free to use the PlatEMO for
% research purposes. All publications which use this platform or any code
% in the platform should acknowledge the use of "PlatEMO" and reference "Ye
% Tian, Ran Cheng, Xingyi Zhang, and Yaochu Jin, PlatEMO: A MATLAB platform
% for evolutionary multi-objective optimization [educational forum], IEEE
% Computational Intelligence Magazine, 2017, 12(4): 73-87".
%--------------------------------------------------------------------------

    methods
        function main(Algorithm,Problem)
            %% Generate random population
            Population = Problem.Initialization();
            Pbest      = Population;
            Archive    = UpdateArchive(Population(NDSort(Population.objs,1)==1),[],Problem.N);

            %% Optimization
            while Algorithm.NotTerminated(Archive)
                Population = Operator(Problem,Population,Pbest,Archive(randi(ceil(length(Archive)/10),1,Problem.N)));
                Pbest      = UpdatePbest(Pbest,Population);
                Archive    = UpdateArchive(Archive,Population,Problem.N);
                S          = OperatorGAhalf(Problem,Archive([1:length(Archive),randi(ceil(length(Archive)/2),1,length(Archive))]));
                Archive    = UpdateArchive(Archive,S,Problem.N);
            end
        end
    end
end


% Build and initialize the computational graph for regression based speech
% enhancement/dereverberation
%
function [layer, para] = Build_EnhanceNet_Gaussian(Data_tr, para, stage)
para.output = 'tmp';

layer = genNetworkDereverb_Gaussian(para.topology, stage);     % generate the network graph
para.preprocessing{1} = {};                     % optional preprocessing for each data stream
para.preprocessing{2} = {};
para.cost_func.layer_idx = length(layer);       % specify which layers are cost function layers
para.cost_func.layer_weight = [1];              % set the weights of each cost function layer
para.IO.inputFeature = [1 1];
para.IO.isTensor = [1 1];
para = ParseOptions2(para);

% generating the scaling factor for the input, as we will need to use a
% small constant in the logarithm. We need to make sure that the power of
% speech are larger than this constant most of the time.
scale = 1e4;        % we hard code the scale to be a constant so that all network will use the same number
scale = scale/2^16; % note that we are using int16 to store waveform samples, so need to scale down
if para.topology.useWav
    layer = InitWavScaleLayer(layer, scale);
end

if stage>1 && isfield(para.topology, 'initModel')
    modelfiles = findFiles(['nnet/' para.topology.initModel], 'mat');
    modelfiles = sort_nnet_by_dev(modelfiles);
    fprintf('Use initial model %s\n', modelfiles{1});
    dnnInit = load(modelfiles{1});

    % find the shared, mean, and var subnets
    [sharedLayers, meanLayers, varLayers] = findSubnets(layer);
    if stage == 3.3     % initialize with regression model, use the initial model only for mean prediction
        [sharedLayersInit, meanLayersInit] = findSubnetsRegression(dnnInit.layer);
    elseif stage == 3.2     % initialize with regression model. share the LSTM between mean and variance prediction
        [sharedLayersInit, meanLayersInit] = findSubnetsRegression(dnnInit.layer);
    else            % initialize with stage 1 or 2 model
        [sharedLayersInit, meanLayersInit, varLayersInit] = findSubnets(dnnInit.layer);
    end
    layer = CopyNetWeightsByLayerIdx(dnnInit.layer, [sharedLayersInit meanLayersInit], layer, [sharedLayers meanLayers]);
    if floor(stage) ==3 && stage~=3.1 && stage~=3.2 && stage~=3.3   % for stage 2, we don't initilize var subnet
        layer = CopyNetWeightsByLayerIdx(dnnInit.layer, varLayersInit, layer, varLayers);
    end
else
    if strcmpi(para.topology.RegressionNetType, 'DNN')    % if use DNN, splice the frames
        idx = ReturnLayerIdxByName(layer, 'splice');
    else
        idx = ReturnLayerIdxByName(layer, 'delta'); % if use LSTM, use dynamic features
    end
    fft_net_length = idx(1);
    fft_net = layer(1:fft_net_length);
    paraTmp = para;
    paraTmp.out_layer_idx = fft_net_length;
    paraTmp.IO = RemoveIOStream(paraTmp.IO, [2]);
    paraTmp.IO.nStream = 1;
    paraTmp.IO.inputFeature = paraTmp.IO.DynamicDistortion.inputFeature(1);
    if isfield(paraTmp.IO, 'fileReader'); paraTmp.IO = rmfield(paraTmp.IO, 'fileReader'); end
    paraTmp.IO.fileReader(1)= paraTmp.IO.DynamicDistortion.fileReader(1);
    fprintf('Generate global MVN weights for mask subnet - %s\n', datestr(now));
    [layer{fft_net_length+1}.W, layer{fft_net_length+1}.b] = computeGlobalCMVN(Data_tr(1), 100, paraTmp, fft_net);
    VerifyPreprocessingTree(layer(1:fft_net_length+1), Data_tr(1), paraTmp, 100);
end
end

function [sharedLayers, meanLayers, varLayers] = findSubnets(layer)

delta_idx = ReturnLayerIdxByName(layer, 'delta');
input_affine_idx = delta_idx(1)+1;
tmp = length(layer) + layer{end}.prev;
mean_idx = tmp(1);
var_idx = tmp(2);
var_start = mean_idx+1;
sharedOut_idx = layer{var_start}.prev + var_start;
sharedLayers = (input_affine_idx:sharedOut_idx);
meanLayers = (sharedOut_idx+1:var_start-1);
varLayers = (var_start:var_idx);

end

function [sharedLayers, meanLayers] = findSubnetsRegression(layer)

delta_idx = ReturnLayerIdxByName(layer, 'delta');
input_affine_idx = delta_idx(1)+1;
tmp = length(layer) + layer{end}.prev;
mean_idx = delta_idx(2);

sharedOut_idx = mean_idx-2;
sharedLayers = (input_affine_idx:sharedOut_idx);
meanLayers = (sharedOut_idx+1:mean_idx);

end


% dbn - training a DBN with up-down algorithm
% Copyright (C) 2011 KyungHyun Cho, Tapani Raiko, Alexander Ilin
%
% This program 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 2
% of the License, or (at your option) any later version.
%
% This program 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 program; if not, write to the Free Software
% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
%
function [D] = dbn(D, patches);

actual_lrate = D.learning.lrate;

n_samples = size(patches, 1);

layers = D.structure.layers;
n_layers = length(layers);

if layers(1) ~= size(patches, 2)
    error('Data is not properly aligned');
end

minibatch_sz = D.learning.minibatch_sz;
n_minibatches = ceil(n_samples / minibatch_sz);

n_epochs = D.iteration.n_epochs;

momentum = D.learning.momentum;
weight_decay = D.learning.weight_decay;

rec.biases_grad = cell(n_layers-1, 1);
rec.W_grad = cell(n_layers-1, 1);
rec.biases_grad_old = cell(n_layers-1, 1);
rec.W_grad_old = cell(n_layers-1, 1);
for l = 1:n_layers-1
    rec.biases_grad{l} = zeros(size(D.rec.biases{l}))';
    if l < n_layers
        rec.W_grad{l} = zeros(size(D.rec.W{l}));
    end
    rec.biases_grad_old{l} = zeros(size(D.rec.biases{l}))';
    if l < n_layers
        rec.W_grad_old{l} = zeros(size(D.rec.W{l}));
    end
end

gen.biases_grad = cell(n_layers-1, 1);
gen.W_grad = cell(n_layers-1, 1);
gen.biases_grad_old = cell(n_layers-1, 1);
gen.W_grad_old = cell(n_layers-1, 1);
for l = 1:n_layers-1
    gen.biases_grad{l} = zeros(size(D.gen.biases{l}))';
    if l < n_layers
        gen.W_grad{l} = zeros(size(D.gen.W{l}));
    end
    gen.biases_grad_old{l} = zeros(size(D.gen.biases{l}))';
    if l < n_layers
        gen.W_grad_old{l} = zeros(size(D.gen.W{l}));
    end
end

top.W_grad = zeros(size(D.top.W));
top.vbias_grad = zeros(size(D.top.vbias));
top.hbias_grad = zeros(size(D.top.hbias));

top.W_grad_old = zeros(size(D.top.W));
top.vbias_grad_old = zeros(size(D.top.vbias));
top.hbias_grad_old = zeros(size(D.top.hbias));

min_recon_error = Inf;
min_recon_error_update_idx = 0;
stopping = 0;

anneal_counter = 0;
actual_lrate0 = actual_lrate;

if D.debug.do_display == 1
    figure(D.debug.display_fid);
end

try
    use_gpu = gpuDeviceCount;
catch errgpu
    use_gpu = false;
    disp(['Could not use CUDA. Error: ' errgpu.identifier])
end

for step=1:n_epochs
    if D.verbose
        fprintf(2, 'Epoch %d/%d: ', step, n_epochs)
    end
    if use_gpu
        % push
        for l = 1:n_layers-1
            if l < n_layers-1
                D.rec.W{l} = gpuArray(single(D.rec.W{l}));
                D.gen.W{l} = gpuArray(single(D.gen.W{l}));
            end
            D.rec.biases{l} = gpuArray(single(D.rec.biases{l}));
            D.gen.biases{l} = gpuArray(single(D.gen.biases{l}));
        end

        D.top.W = gpuArray(single(D.top.W));
        D.top.vbias = gpuArray(single(D.top.vbias));
        D.top.hbias = gpuArray(single(D.top.hbias));
    end

    for mb=1:n_minibatches
        D.iteration.n_updates = D.iteration.n_updates + 1;

        v0 = patches((mb-1) * minibatch_sz + 1:min(mb * minibatch_sz, n_samples), :);
        mb_sz = size(v0,1);

        if use_gpu > 0
            v0 = gpuArray(single(v0));
        end

        % learning rate
        if D.learning.lrate_anneal > 0 && (step >= D.learning.lrate_anneal * n_epochs)
            anneal_counter = anneal_counter + 1;
            actual_lrate = actual_lrate0 / anneal_counter;
        else
            if D.learning.lrate0 > 0
                actual_lrate = D.learning.lrate / (1 + D.iteration.n_updates / D.learning.lrate0);
            else
                actual_lrate = D.learning.lrate;
            end
            actual_lrate0 = actual_lrate;
        end

        D.signals.lrates = [D.signals.lrates actual_lrate];

        % recognition (wake)
        h0r = cell(n_layers-1, 1);
        h0r{1} = v0;
        if D.learning.ffactored == 0
            h0r{1} = binornd(1, h0r{1});
        end

        for l = 2:(n_layers-1)
            h0r{l} = sigmoid(bsxfun(@plus, h0r{l-1} * D.rec.W{l-1}, D.rec.biases{l}'));
            if D.learning.ffactored == 0
                h0r{l} = binornd(1, h0r{l});
            end
        end

        r0 = cell(n_layers-1, 1);
        for l = 1:(n_layers-2)
            r0{l} = sigmoid(bsxfun(@plus, h0r{l+1} * D.gen.W{l}', D.gen.biases{l}'));
        end

        % update generative weight
        for l = 1:(n_layers-2)
            gen.W_grad{l} = ((h0r{l} - r0{l})' * h0r{l+1}) / size(v0, 1);
            gen.W_grad_old{l} = (1 - momentum) * gen.W_grad{l} + momentum * gen.W_grad_old{l};
            D.gen.W{l} = D.gen.W{l} + actual_lrate * (gen.W_grad_old{l} - D.learning.weight_decay * D.gen.W{l});

            gen.biases_grad{l} = mean(h0r{l} - r0{l}, 1);
            gen.biases_grad_old{l} = (1 - momentum) * gen.biases_grad{l} + momentum * gen.biases_grad_old{l};
            D.gen.biases{l} = D.gen.biases{l} + actual_lrate * (gen.biases_grad_old{l}' - D.learning.weight_decay * D.gen.biases{l});
        end

        % contrastive steps
        if D.learning.persistent_cd == 0 || D.iteration.n_updates == 1
            vtop = h0r{end};
        end
        for cs = 1:D.learning.contrastive_step
            htop = sigmoid(bsxfun(@plus, vtop * D.top.W, D.top.hbias'));
            htop = binornd(1, htop);
            vtop = sigmoid(bsxfun(@plus, htop * D.top.W', D.top.vbias'));
            vtop = binornd(1, vtop);
        end

        tv0 = h0r{end};
        th0 = sigmoid(bsxfun(@plus, tv0 * D.top.W, D.top.hbias'));
        tv1 = vtop;
        th1 = sigmoid(bsxfun(@plus, tv1 * D.top.W, D.top.hbias'));

        top.W_grad = (tv0' * th0)/size(tv0,1) - (tv1' * th1)/size(tv1,1);
        top.vbias_grad = mean(tv0, 1) - mean(tv1, 1);
        top.hbias_grad = mean(th0, 1) - mean(th1, 1);

        top.W_grad_old = (1 - momentum) * top.W_grad + momentum * top.W_grad_old;
        top.vbias_grad_old = (1 - momentum) * top.vbias_grad' + momentum * top.vbias_grad_old;
        top.hbias_grad_old = (1 - momentum) * top.hbias_grad' + momentum * top.hbias_grad_old;

        D.top.W = D.top.W + actual_lrate * (top.W_grad_old - D.learning.weight_decay * D.top.W);
        D.top.vbias = D.top.vbias + actual_lrate * (top.vbias_grad_old - D.learning.weight_decay * D.top.vbias);
        D.top.hbias = D.top.hbias + actual_lrate * (top.hbias_grad_old - D.learning.weight_decay * D.top.hbias);

        % generation (sleep)
        h0r{n_layers-1} = tv1;
        for l = (n_layers-2):-1:1
            h0r{l} = sigmoid(bsxfun(@plus, h0r{l+1} * D.gen.W{l}', D.gen.biases{l}'));
            if D.learning.ffactored == 0
                h0r{l} = binornd(1, h0r{l});
            end
        end

        for l = 2:(n_layers - 1)
            r0{l} = sigmoid(bsxfun(@plus, h0r{l-1} * D.rec.W{l-1}, D.rec.biases{l}'));
        end

        % update recognition weight
        for l = 1:(n_layers-1)
            if l < n_layers - 1
                rec.W_grad{l} = (h0r{l}' * (h0r{l+1} - r0{l+1})) / size(h0r{1}, 1);
                rec.W_grad_old{l} = (1 - momentum) * rec.W_grad{l} + momentum * rec.W_grad_old{l};
                D.rec.W{l} = D.rec.W{l} + actual_lrate * (rec.W_grad_old{l} - D.learning.weight_decay * D.rec.W{l});
            end

            if l > 1
                rec.biases_grad{l} = mean(h0r{l} - r0{l}, 1);
                rec.biases_grad_old{l} = (1 - momentum) * rec.biases_grad{l} + momentum * rec.biases_grad_old{l};
                D.rec.biases{l} = D.rec.biases{l} + actual_lrate * (rec.biases_grad_old{l}' - D.learning.weight_decay * D.rec.biases{l});
            end
        end

        clear h0r r0 htop tv0 th0 tv1 th1;
        if D.learning.persistent_cd  == 0
            clear vtop;
        end

        % compute reconstruction error
        hr = v0;
        for l = 2:n_layers-1
            hr = sigmoid(bsxfun(@plus, hr * D.rec.W{l-1}, D.rec.biases{l}'));
        end
        hr = sigmoid(bsxfun(@plus, hr * D.top.W, D.top.hbias'));
        hr = sigmoid(bsxfun(@plus, hr * D.top.W', D.top.vbias'));
        for l = n_layers-1:-1:2
            hr = sigmoid(bsxfun(@plus, hr * D.gen.W{l-1}', D.gen.biases{l-1}'));
        end

        rerr = mean(sum((v0 - hr).^2,2));
        if use_gpu > 0
            rerr = gather(rerr);
        end
        D.signals.recon_errors = [D.signals.recon_errors rerr];

        if D.verbose == 1
            fprintf(2, '.');
        end

        if use_gpu > 0
            clear v0 h0d h0e v0_clean vr hr deltae deltad
        end

        if D.stop.criterion > 0
            if D.stop.criterion == 1
                if min_recon_error > D.signals.recon_errors(end)
                    min_recon_error = D.signals.recon_errors(end);
                    min_recon_error_update_idx = D.iteration.n_updates;
                else
                    if D.iteration.n_updates > min_recon_error_update_idx + D.stop.recon_error.tolerate_count
                        fprintf(2, '\nStopping criterion reached (recon error) %f > %f\n', ...
                            D.signals.recon_errors(end), min_recon_error);
                        stopping = 1;
                        break;
                    end
                end
            else
                error ('Unknown stopping criterion %d', D.stop.criterion);
            end
        end

        if length(D.hook.per_update) > 1
            err = D.hook.per_update{1}(D, D.hook.per_update{2});

            if err == -1
                stopping = 1;
                break;
            end
        end

        if D.debug.do_display == 1 && mod(D.iteration.n_updates, D.debug.display_interval) == 0
            D.debug.display_function (D.debug.display_fid, D, v0, v1, h0, h1, W_grad, vbias_grad, hbias_grad);
            drawnow;
        end
    end

    if use_gpu > 0
        % pull
        for l = 1:n_layers-1
            if l < n_layers-1
                D.rec.W{l} = gather(D.rec.W{l});
                D.gen.W{l} = gather(D.gen.W{l});
            end
            D.rec.biases{l} = gather(D.rec.biases{l});
            D.gen.biases{l} = gather(D.gen.biases{l});
        end

        D.top.W = gather(D.top.W);
        D.top.vbias = gather(D.top.vbias);
        D.top.hbias = gather(D.top.hbias);
    end

    if length(D.hook.per_epoch) > 1
        err = D.hook.per_epoch{1}(D, D.hook.per_epoch{2});

        if err == -1
            stopping = 1;
        end
    end

    if stopping == 1
        break;
    end

    if D.verbose == 1
        fprintf(2, '\n');
    end

    fprintf(2, 'Epoch %d/%d - recon_error: %f\n', step, n_epochs, ...
        D.signals.recon_errors(end));
end

if use_gpu > 0
    % pull
    for l = 1:n_layers-1
        if l < n_layers-1
            D.rec.W{l} = gather(D.rec.W{l});
            D.gen.W{l} = gather(D.gen.W{l});
        end
        D.rec.biases{l} = gather(D.rec.biases{l});
        D.gen.biases{l} = gather(D.gen.biases{l});
    end

    D.top.W = gather(D.top.W);
    D.top.vbias = gather(D.top.vbias);
    D.top.hbias = gather(D.top.hbias);
end


function [I, T_ini,T_ref] = lime(L,para,post)
% A wrapper of LIME
% LIME: A Method for Low-light IMage Enhancement
%
% INPUTS
% para: parameter
% post: true if do post-processing (denoise)
% OUTPUTS
% I - result image
% -------------------------------------------------------------------------
% @article{guo2016lime,
%   title={LIME: A Method for Low-light IMage Enhancement},
%   author={Guo, Xiaojie},
%   journal={arXiv preprint arXiv:1605.05034},
%   year={2016}
% }
% -------------------------------------------------------------------------
%
% Zhenqiang Ying
% 2016-11-02

if nargin == 0
   I = imload(uigetimagefile);
   [J, T_ini,T_ref] = lime(I);
   T = repmat(T_ref, [1 1 3]);
   ezFig I J I./(T.^0.6) I./(T.^0.8) T_ini T_ref
   clear I;
   return;
end

Require LIME

if nargin < 2
    para.lambda = 0.15;
    para.sigma = 2;
    para.gamma = 0.8;
    if nargin < 3
       post = false;
    end
end

if ~isfloat(L), L = im2double(L); end

tic
[I, T_ini,T_ref] = LIME(L,para);
toc

% figure(1);imshow(L);title('Input');
% figure(2);imshow(I);title('LIME');

% figure(3);imshow(T_ini,[]);colormap hot;title('Initial T');
% figure(4);imshow(T_ref,[]);colormap hot;title('Refined T');
%% Post Processing
if post
    %addpath(genpath('+existingMethods\+LIME\BM3D'));
    %import filter.BM3D.*

    YUV = rgb2ycbcr(I);
    Y = YUV(:,:,1);

    Y_d = BM3D(Y);

    I_d = ycbcr2rgb(cat(3,Y_d,YUV(:,:,2:3)));
    I_f = (I).*repmat(T_ref,[1,1,3])+I_d.*repmat(1-T_ref,[1,1,3]);

%     figure(5);imshow(I_d);title('Denoised ');
%     figure(6);imshow(I_f);title('Recomposed');
    I = I_f;
end

end

%% DEMO
% clc;close all;clear all;addpath(genpath('./'));
% %%
% filename = '1.bmp';
% L = (im2double(imread(filename)));
%
% post = true;
%
% para.lambda = 0.15;
% para.sigma = 2;
% para.gamma = 0.8;
% tic
% [I, T_ini,T_ref] = LIME(L,para);
% toc
%
% figure(1);imshow(L);title('Input');
% figure(2);imshow(I);title('LIME');
% figure(3);imshow(T_ini,[]);colormap hot;title('Initial T');
% figure(4);imshow(T_ref,[]);colormap hot;title('Refined T');
% %% Post Processing
% if post
% YUV = rgb2ycbcr(I);
% Y = YUV(:,:,1);
%
% sigma_BM3D = 50;
% [~, Y_d] = BM3D(Y,Y,sigma_BM3D,'lc',0);
%
% I_d = ycbcr2rgb(cat(3,Y_d,YUV(:,:,2:3)));
% I_f = (I).*repmat(T_ref,[1,1,3])+I_d.*repmat(1-T_ref,[1,1,3]);
%
% figure(5);imshow(I_d);title('Denoised ');
% figure(6);imshow(I_f);title('Recomposed');
% end


function [montage, cfg] = ft_nirs_prepare_ODtransformation(cfg, data)

% FT_NIRS_PREPARE_ODTRANSFORMATION returns the transformation matrix from
% optical densities (OD) to chromophore concentrations such as (de-)
% oxygenated hemoglobin.
%
% Use as
%   [montage] = ft_prepare_ODtransformation(cfg, data)
%
% It is necessary to input the data on which you want to perform the
% inverse computations, since that data generally contain the optode
% information and information about the channels that should be included in
% the transformation. The data structure can be either obtained
% from FT_PREPROCESSING, FT_FREQANALYSIS, FT_TIMELOCKANALYSIS or
% FT_COMPONENTANALYSIS. If the data is empty, all channels will be included
% in transformation.
%
% The configuration should contain
%   cfg.channel            = Nx1 cell-array with selection of channels
%                            (default = 'nirs'), see FT_CHANNELSELECTION for
%                            more details
%
% Optional configuration settings are
%   cfg.age                = scalar, age of the subject (necessary to
%                            automatically select the appropriate DPF, or
%   cfg.dpf                = scalar, differential path length factor
%   cfg.dpffile            = string, location to a lookup table for the
%                            relation between participant age and DPF
%
% Note that the DPF might be different across channels, and is usually
% contained in the optode structure contained in the data.
%
% To facilitate data-handling and distributed computing you can use
%   cfg.inputfile   =  ...
% If you specify this option the input data will be read from a *.mat
% file on disk. This mat files should contain only a single variable named 'data',
% corresponding to the input structure.
%
% See also FT_NIRS_TRANSFORM_ODS, FT_APPLY_MONTAGE

% options to be implemented:
%
% The NIRS positions can be present in the data or can be specified as
% cfg.opto      = structure with optode positions, see FT_DATATYPE_SENS
% cfg.siunits   = ft_getopt(cfg, 'siunits', 'no');   % yes/no, ensure that SI units are used consistently
% cfg.logarithm = string, can be 'natural' or 'base10' (default =

% You are using the FieldTrip NIRS toolbox developed and maintained by
% Artinis Medical Systems (http://www.artinis.com). For more information
% on FieldTrip, see http://www.fieldtriptoolbox.org
%
% This work is licensed under a Creative Commons Attribution-ShareAlike 4.0
% International License. To view a copy of this license, visit
% http://creativecommons.org/licenses/by-sa/4.0/ or send a letter to
% Creative Commons, PO Box 1866, Mountain View, CA 94042, USA.
%
% Creative Commons Attribution-ShareAlike 4.0 International License:
% -----------------------------------
% You are free to:
%
%     Share - copy and redistribute the material in any medium or format
%     Adapt - remix, transform, and build upon the material
%     for any purpose, even commercially.
%
%     The licensor cannot revoke these freedoms as long as you follow the
%     license terms.
%
% Under the following terms:
%
%     Attribution - You must give appropriate credit, provide a link to
%                    the license, and indicate if changes were made. You
%                    may do so in any reasonable manner, but not in any way
%                    that suggests the licensor endorses you or your use.
%
%     ShareAlike - If you remix, transform, or build upon the material,
%                   you must distribute your contributions under the same
%                   license as the original.
%
%     No additional restrictions - You may not apply legal terms or
%                                   technological measures that legally
%                                   restrict others from doing anything the
%                                   license permits.
%
% -----------------------------------
%
% This toolbox is not to be used for medical or clinical purposes.
%
% Copyright (c) 2015-2016 by Artinis Medical Systems.
% Contact: askforinfo@artinis.com
%
% Main programmer: Jörn M. Horschig
% $Id$

revision = '$Id$';
ft_nargin   = nargin;
ft_nargout  = nargout;

% do the general setup of the function

% the ft_preamble function works by calling a number of scripts from
% fieldtrip/utility/private that are able to modify the local workspace

ft_defaults                   % this ensures that the path is correct and that the ft_defaults global variable is available
ft_preamble init              % this will reset ft_warning and show the function help if nargin==0 and return an error
ft_preamble debug             % this allows for displaying or saving the function name and input arguments upon an error
ft_preamble loadvar    data   % this reads the input data in case the user specified the cfg.inputfile option
ft_preamble provenance data   % this records the time and memory usage at the beginning of the function

% the ft_abort variable is set to true or false in ft_preamble_init
if ft_abort
  % do not continue function execution in case the outputfile is present and the user indicated to keep it
  return
end

% check if the input data is valid for this function
data = ft_checkdata(data);

% set the defaults
cfg.channel = ft_getopt(cfg, 'channel', 'nirs');
cfg.age     = ft_getopt(cfg, 'age', []);
cfg.dpf     = ft_getopt(cfg, 'dpf', []);

% get the optode definition
opto = ft_fetch_sens(cfg, data);

% select the appropriate channels
if isfield(data, 'topolabel')
  % the data reflects a componentanalysis, where the topographic and the
  % timecourse labels are different
  cfg.channel = ft_channelselection(cfg.channel, data.topolabel);
elseif isfield(data, 'label')
  % In the subsequent code, the matching channels in the sensor array and
  % in the configuration will be selected. To ensure that these channels
  % are also present in the data, update the configuration to match the data.
  cfg.channel = ft_channelselection(cfg.channel, data.label);
else
  % update the selected channels based on the optode definition
  cfg.channel = ft_channelselection(cfg.channel, opto.label);
end
cfg.channel = ft_channelselection('nirs', cfg.channel); % only NIRS valid

% check current data type by looking what's inside of []
typeidx = regexp(cfg.channel, sprintf('%s%s', regexptranslate('wildcard', '[*]'), '$'));

% remove channels that do not obey to labelling guidelines
illegalidx = cellfun(@isempty, typeidx);
cfg.channel(illegalidx) = [];

if isempty(cfg.channel)
  ft_error('no valid NIRS channels found')
end

% channel indices wrt optode structure
chanidx     = match_str(opto.label, cfg.channel);

% check on DPF values
if ~isempty(cfg.age) && ~isempty(cfg.dpf)
  error('unsure if age or dpf in configuration should take precedence');
elseif ~isempty(cfg.age)
  error('the use of cfg.age is not implemented, yet');
elseif ~isempty(cfg.dpf)
  % this is where they should be
  dpfs = repmat(cfg.dpf, size(cfg.channel));
elseif isfield(opto, 'DPF')
  % this is for backward compatibility
  dpfs = opto.DPF(chanidx);
elseif isfield(data, 'opto') && isfield(data.opto, 'DPF')
  % this is for backward compatibility
  dpfs = data.opto.DPF(chanidx);
elseif isfield(data, 'hdr') && isfield(data.hdr, 'opto') && isfield(data.hdr.opto, 'DPF')
  % this is for backward compatibility
  dpfs = data.hdr.opto.DPF(chanidx);
end

% which chromophores are desired
chromophoreIdx = [3 2];
chromophoreName = {'O2Hb' 'HHb'};

%% transform to concentrations or to OD
% read in the coefficient table
% FIXME put this into an own function to read this out
fid = fopen(fullfile(fileparts(mfilename('fullpath')), 'private', 'Cope_ext_coeff_table.txt'));
coefs = cell2mat(textscan(fid, '%f %f %f %f %f'));

% extract all optode combinations that are relevant here
tratra         = opto.tra(chanidx, :)';
transmitteridx = tratra>0;
receiveridx    = tratra<0;
optodeidx      = (transmitteridx | receiveridx)'; % transpose to get back to tra order

% extract the wavelengths
wavelengths = opto.wavelength(tratra(transmitteridx));
wlidx = bsxfun(@minus, coefs(:, 1), wavelengths);

% find the relevant channel combinations
chancmb = logical([]); % we need to loop here to get size of chancmb
chanUsed = zeros(numel(chanidx), 1);
for c=1:numel(chanidx)
  % skip channel if it already was in another channel combination
  if chanUsed(c)
    continue;
  end
  % compute the channel combinations
  tupletidx = sum(bsxfun(@minus, optodeidx, optodeidx(c, :))~=0, 2)==0;
  chanUsed = chanUsed|tupletidx;
  chancmb(:, end+1) = tupletidx;
end

% do the transformation
tra      = zeros(size(chancmb, 2)*numel(chromophoreIdx), size(cfg.channel, 1));
labelnew = cell(size(chancmb, 2)*numel(chromophoreIdx), 1);

% transformation has to be done per channel combination
chanidx = []; % we will use this variable here for indexing channels (Rx-Tx cmbs)
optoidx = []; % we will use this variable here for indexing optodes (individual Rx and Tx)
for c=1:size(chancmb, 2)

  % find all other channels of the exact same transmitter / receiver pair
  chanidx = chancmb(:, c);

  % extract coefficient idx of these channels
  [coefidx, colidx] = find(wlidx(:, chanidx)==0);

  % compute the transmitter/receiver distance in cm
  optoidx = find(chanidx, 1, 'first'); % we can take 'first' because the transmitter-optodes have to be physically in the exact same spot to form "one" channel
  dist = sqrt(sum(diff(opto.optopos(optodeidx(optoidx, :), :)).^2));

  % select dpf
  dpf = mean(dpfs(chanidx));

  % compute distance factor for correct scaling
  distfactor = dist/10*dpf/10; % in mm

  % select the respective coefficients
  eps=coefs(coefidx,chromophoreIdx)/10; % in mm

  % add to transformation matrix
  traIdx = 1+(c-1)*numel(chromophoreIdx):(c)*numel(chromophoreIdx);
  tra(traIdx, chanidx) = pinv(eps)/(distfactor);

  % create the new labels for the channels
  chanorg = cfg.channel(find(chanidx, 1, 'first'));
  for n=1:numel(chromophoreIdx)
    labelnew(traIdx(n), 1) = regexprep(chanorg, sprintf('%s%s', regexptranslate('wildcard', '[*]'), '$'), sprintf('[%s]', chromophoreName{n}));
  end

end

%% create output
montage = [];
montage.labelorg = cfg.channel(:)';
montage.labelnew = labelnew;
montage.tra      = tra;


%% AKAZE and ORB planar tracking
%
% In this demo, we will compare AKAZE and ORB local features by using them to
% find matches between video frames and track object movements.
%
% The algorithm is as follows: First, we detect and describe keypoints on the
% first frame inside a manually set object boundaries. For every next frame:
%
% * Detect and describe keypoints
% * Match them using bruteforce matcher
% * Estimate homography transformation using RANSAC
% * Filter inliers from all the matches
% * Apply homography transformation to the bounding box to find the object
% * Draw bounding box and inliers, compute inlier ratio as evaluation metric
%
% To do the tracking we need a video and object position on the first frame.
% You can download an example video and data from
% <https://docs.google.com/file/d/0B72G7D4snftJandBb0taLVJHMFk here>.
%
% To run the sample, you specify an input (video file or camera id), then
% select a bounding box with the mouse, to start tracking.
%
% Example video: <http://www.youtube.com/watch?v=pzVbhxx6aog>.
%
% Sources:
%
% * <https://docs.opencv.org/3.3.1/dc/d16/tutorial_akaze_tracking.html>
% * <https://github.com/opencv/opencv/blob/3.3.1/samples/cpp/tutorial_code/features2D/AKAZE_tracking/planar_tracking.cpp>
% * <https://github.com/opencv/opencv/blob/3.3.1/samples/python/plane_tracker.py>
%

function varargout = planar_tracker_demo(fname)
    % initialize app state and options
    app = defaultOptions();

    % load video
    if nargin < 1
        fname = fullfile(mexopencv.root(), 'test', 'blais.mp4');
        win = [136 0 366 433];     % book
        %win = [135 165 285 175];  % face
        if exist(fname, 'file') ~= 2
            error('Missing "blais.mp4" video, download it from here: %s', ...
                'https://docs.google.com/file/d/0B72G7D4snftJandBb0taLVJHMFk')
        end
    else
        win = [];
    end
    vid = cv.VideoCapture(fname);
    assert(vid.isOpened(), 'Failed to open video');

    % get first frame
    frame = vid.read();
    assert(~isempty(frame), 'Failed to read frames');

    % prompt user for an object ROI
    if isempty(win)
        win = selectROI(frame)
        assert(~isempty(win), 'No object specified');
    end

    % store first frame and bounding box points
    app.sz = size(frame);
    app.frame0 = frame;
    app.bb0 = bsxfun(@plus, win(1:2), [0 0; win(3) 0; win(3:4); 0 win(4)]);

    % create feature detectors and descriptor matchers objects
    %names = {'AKAZE', 'KAZE', 'ORB', 'BRISK', 'SIFT', 'SURF'};
    names = {'AKAZE', 'ORB'};
    f = createTrackers(app, names);

    % detect keypoints and compute descriptors in first frame (ROI region)
    for i=1:numel(f)
        if true && i>1 && strcmp(f(i).name, 'ORB')
            % to ensure detectors locate roughly same number of keypoints
            f(i).detector.MaxFeatures = f(1).stats(1);
        end
        [f(i).kp0, f(i).desc0, f(i).stats] = processFirstFrame(f(i), app);
    end
    stats = cat(1, f.stats);

    % prepare plot
    h = createUI(f, app);
    if nargout > 0, varargout{1} = h; end

    % main loop
    counter = 0;
    while ishghandle(h(1).img)
        % get next frame
        counter = counter + 1;
        frame = vid.read();
        if isempty(frame), break; end
        if rem(counter, app.show_every_frame) > 0, continue; end

        for i=1:numel(f)
            % feature matching + homography to track object
            [out, bb, f(i).stats] = processFrame(f(i), app, frame);

            % check quality of match
            if all(f(i).stats(3:4) >= [app.min_inliers app.min_inliers_ratio])
                lin_opts = {'LineWidth',2, 'LineStyle','-'};
            else
                % uncertain: not enough matches and/or too many outliers
                lin_opts = {'LineWidth',0.5, 'LineStyle',':'};
            end

            % show results
            set(h(i).txt, 'String',printStats(f(i).stats));
            set(h(i).lin, 'XData',bb([1:end 1],1) + app.sz(2), ...
                'YData',bb([1:end 1],2), lin_opts{:});
            set(h(i).img, 'CData',out);
        end
        stats = stats + cat(1, f.stats);
        drawnow;
    end
    vid.release();

    % show average stats
    stats = stats ./ (counter / app.show_every_frame);
    stats(:,1:3) = round(stats(:,1:3));
    for i=1:numel(f)
        disp(['-- ' f(i).name ' --']);
        cellfun(@disp, printStats(stats(i,:)));
    end
end

%% Helper functions

function app = defaultOptions()
    %DEFAULTOPTIONS  Create default options structure

    app = struct();
    app.akaze_thresh = 3e-4;     % AKAZE threshold set to locate ~1000 keypoints
    app.ransac_thresh = 2.5;     % RANSAC inlier threshold
    app.match_ratio = 0.8;       % nearest-neighbour matching ratio
    app.min_inliers = 100;       % minimal number of inliers to draw bounding box
    app.min_inliers_ratio = 0.2; % minimal inliers ratio to draw bounding box
    app.draw_top_matches = 100;  % draw the top 50 matches only
    app.show_every_frame = 3;    % display every 3 frames to maintain high fps
end

function win = selectROI(img)
    %SELECTROI  Interactively select region with mouse

    hImg = imshow(img);
    axis on
    hRect = imrect();
    win = wait(hRect);
    close(ancestor(hImg, 'Figure'));
    if ~isempty(win)
        win(1:2) = win(1:2) - 1;
    end
end

function f = createTrackers(app, names)
    %CREATETRACKERS  Create detectors and matchers objects

    f = struct();
    for i=1:numel(names)
        % create detector object
        f(i).name = names{i};
        switch names{i}
            case 'AKAZE'
                f(i).detector = cv.AKAZE('Threshold',app.akaze_thresh);
            case 'KAZE'
                f(i).detector = cv.KAZE('Threshold',app.akaze_thresh);
            case 'ORB'
                f(i).detector = cv.ORB('MaxFeatures',1000);
            case 'BRISK'
                f(i).detector = cv.BRISK();
            case 'SIFT'
                f(i).detector = cv.SIFT();
            case 'SURF'
                f(i).detector = cv.SURF();
            otherwise
                error('Unexpected detector type')
        end

        % create matcher object
        if true
            if false
                opts = {'CrossCheck',true};
            else
                opts = {};
            end
            f(i).matcher = cv.DescriptorMatcher('BFMatcher', ...
                'NormType',f(i).detector.defaultNorm(), opts{:});
        else
            f(i).matcher = cv.DescriptorMatcher('FlannBasedMatcher', ...
                'Index',...
                {'LSH', 'TableNumber',6, 'KeySize',12, 'MultiProbeLevel',1});
        end
    end
end

function h = createUI(f, app)
    %CREATEUI  Creates the UI

    x = app.bb0([1:end 1], 1);
    y = app.bb0([1:end 1], 2);
    lin_opts = {'Color','b', 'LineWidth',2, 'LineStyle','-'};

    % plot images, objects, and stats
    h = struct();
    figure('Position',get(0, 'DefaultFigurePosition') .* [0.4 0.2 1.5 1.7])
    for i=1:numel(f)
        subplot(numel(f),1,i)
        h(i).img = imshow([app.frame0, app.frame0]);
        line(x, y, lin_opts{:})
        h(i).lin = line(x + app.sz(2), y, lin_opts{:});
        h(i).txt = text(2*app.sz(2), app.sz(1), printStats(f(i).stats), ...
            'Color','y', 'HorizontalAlignment','right', 'VerticalAlignment','bottom');
        title(sprintf('%s, %d keypoints', f(i).name, f(i).stats(1)))
    end
end

function str = printStats(stats)
    %PRINTSTATS  Pretty print matching statistics

    str = {
        sprintf('Keypoints: %d', stats(1))
        sprintf('Matches: %d', stats(2))
        sprintf('Inliers: %d', stats(3))
        sprintf('Inliers Ratio: %.0f%%', stats(4)*100)
        sprintf('FPS: %.2f', stats(5))
    };
end

function [kp, desc, stats] = processFirstFrame(f, app)
    %PROCESSFIRSTFRAME Process first frame

    % init stats
    stats = [0, 0, 0, 0, 0];

    % create ROI mask
    mask = zeros(app.sz(1:2), 'uint8');
    mask = cv.fillPoly(mask, app.bb0, 'Color',255);

    tm = cv.TickMeter();
    tm.start();

    % detect and compute features of first frame in ROI region
    [kp, desc] = f.detector.detectAndCompute(app.frame0, 'Mask',mask);
    assert(~isempty(kp), 'No keypoints detected in first frame');
    stats(1) = numel(kp);

    tm.stop();
    stats(5) = 1 / tm.TimeSec;
end

function [out, bb1, stats] = processFrame(f, app, frame1)
    %PROCESSFRAME  Process subsequent frames

    % initialize output
    out = [app.frame0, frame1];
    bb1 = nan(size(app.bb0));
    stats = [0 0 0 0 0];

    tm = cv.TickMeter();
    tm.start();

    % detect and compute features in current frame
    [kp1, desc1] = f.detector.detectAndCompute(frame1);
    stats(1) = numel(kp1);
    if numel(kp1) < 2, return; end

    % match against first frame features
    if true
        % 2-NN matching with ratio test (if closest match is MATCH_RATIO
        % closer than the second closest one, then its a good match)
        matches = f.matcher.knnMatch(f.desc0, desc1, 2);
        idx = cellfun(@(m) numel(m) == 2 && ...
            (m(1).distance < app.match_ratio * m(2).distance), matches);
        matches = cellfun(@(m) m(1), matches(idx));
    elseif false
        matches = f.matcher.knnMatch(f.desc0, desc1, 1);
        matches = [matches{:}];
    else
        matches = f.matcher.match(f.desc0, desc1);
    end
    stats(2) = numel(matches);
    if numel(matches) < 4, return; end

    % estimate homography using RANSAC
    pt0 = cat(1, f.kp0([matches.queryIdx] + 1).pt);
    pt1 = cat(1, kp1([matches.trainIdx] + 1).pt);
    [H, inliers] = cv.findHomography(pt0, pt1, ...
        'Method','Ransac', 'RansacReprojThreshold',app.ransac_thresh);
    inliers = logical(inliers);
    stats(3) = nnz(inliers);
    stats(4) = stats(3) / stats(2);
    if isempty(H), return; end

    tm.stop();
    stats(5) = 1 / tm.TimeSec;

    % project object bounding box using homography to locate it in new frame
    bb1 = cv.perspectiveTransform(app.bb0, H);

    % draw matches
    if app.draw_top_matches > 0
        % only draw top inlier matches (sorted by distance) to reduce clutter
        [~,idx] = sort([matches.distance]);
        idx(~inliers(idx)) = [];
        inliers(idx(app.draw_top_matches+1:end)) = false;
    end
    out = cv.drawMatches(app.frame0, f.kp0, frame1, kp1, matches, ...
        'MatchesMask',inliers, ...
        'MatchColor',[255 0 0], 'SinglePointColor',[255 0 255]);
end


function gammaM = gammaScan3d(doseArray1, doseArray2, deltaXYZv, doseAgreement, distAgreement, maxDistance)
% function gammaM = gammaScan3d(doseArray1, doseArray2, deltaXYZv, doseAgreement, distAgreement, maxDistance)
%
% APA, 04/27/2012

deltaX = deltaXYZv(1);
deltaY = deltaXYZv(1);
deltaZ = deltaXYZv(1);
incrementRadius = min([deltaX deltaY deltaZ]);

% Initial gamma
gammaM = ((doseArray1-doseArray2).^2).^0.5/doseAgreement;
convergedM = false(size(gammaM));

% Calculate until 4 times the permissible distance to agreement.
if ~exist('maxDistance', 'var')
    maxDistance = distAgreement*1.5;
end
outerRadiusV = incrementRadius:incrementRadius:maxDistance;
numIters = length(outerRadiusV);

siz = size(gammaM);
minDoseDiffM = zeros(siz,'single');
maxDoseDiffM = minDoseDiffM;

% convergenceCountM = zeros(siz,'uint8');

% Update waitbar on gamma GUI
gammaGUIFig = findobj('tag','CERRgammaInputGUI');
if ~isempty(gammaGUIFig)
    ud = get(gammaGUIFig,'userdata');
    set(ud.wb.patch,'xData',[0 0 0 0])
end

for radNum = 1:numIters

    disp(['--- Gamma Calculation Iteraion ', num2str(radNum), ' ----'])

    % Create an ellipsoid ring neighborhood
    outerRadius = outerRadiusV(radNum);

    rowOutV = floor(outerRadius/deltaY);
    colOutV = floor(outerRadius/deltaX);
    slcOutV = floor(outerRadius/deltaZ);

    NHOOD_outer = createEllipsoidNHOOD(1:rowOutV,1:colOutV,1:slcOutV);

    % Compute Min and Max for the ellipsoid neighborhood
    [minLocalM, maxLocalM] = getMinMaxIM(doseArray2,NHOOD_outer);

    % Compute difference between dose1 and (min,max) for dose2
    minDoseDiffM(~convergedM) = doseArray1(~convergedM) - minLocalM(~convergedM) + doseAgreement; % D1-(min(D2)-5)
    maxDoseDiffM(~convergedM) = maxLocalM(~convergedM) + doseAgreement - doseArray1(~convergedM); % (max(D2)+5)-D1

    % If dose1 is contained within (min,max) for dose2, then it converged!
    newConvergedM = ~convergedM & minDoseDiffM >= 0 & maxDoseDiffM >= 0;

    % Compute gamma for voxels that converged
    %gammaM(newConvergedM) =  min(gammaM(newConvergedM), outerRadius/distAgreement);
    gammaM(newConvergedM) =  outerRadius;

    % Add newly converged voxels to the list
    convergedM = convergedM | newConvergedM;

    % Compute gamma for voxels that have not yet converged
    %gammaForNotConvergedM =  (min((minDoseDiffM(~convergedM)-doseAgreement).^2, (maxDoseDiffM(~convergedM)-doseAgreement).^2)./doseAgreement^2 + (outerRadius/distAgreement)^2).^0.5;
    %gammaM(~convergedM) = min(gammaM(~convergedM), gammaForNotConvergedM);

    % If gamma is less or equal to outerRadius/distAgreement, then the voxel conveged!
    %convergedM(~convergedM) = gammaM(~convergedM) <= outerRadius/distAgreement;

    if ~isempty(gammaGUIFig)
        set(ud.wb.patch,'xData',[0 0 radNum/numIters radNum/numIters])
        drawnow
    end

    if all(convergedM(:))
        disp('All converged!!!')
        if ~isempty(gammaGUIFig)
            set(ud.wb.patch,'xData',[0 0 1 1])
        end
        break
    end

end

gammaM(~convergedM) = NaN;


function [N,rho,T,P]=standardAtmosParam(dayOfYear,secondOfTheDay,latLonAlt,rhow)
%%STANDARDATMOSPARAM  Get basic parameters for atmospheric refractivity,
%                     density, pressure and temperature from the
%                     NRLMSISE-00 atmospheric model. The model is best
%                     suited for altitudes below 90km as the anomalous
%                     oxygen parameter of the NRLMSISE-00 model is not
%                     used by this function.
%
%INPUTS: dayOfYear The integer day of the year in the Gregorian calendar in
%                  universal coordinated time (UTC). Counting starts at 1.
%                  The resolution of the model is not sufficient for it to
%                  matter whether 365 or 366 is given as the day if it
%                  isn't/is a leap year.
%   secondOfTheDay The second of the day. This starts at zero. The
%                  resolution of the model is not high enough for leap
%                  seconds to matter, so values above 86400.0 are just
%                  clipped to 86400.0.
%        latLonAlt The location under consideration given in WGS-84
%                  ellipsoidal coordinates of latitude and longitude in
%                  radians and ellipsoidal height in meters.
%             rhow An optional parameter specifying the mass density of
%                  water vapor at the point in question in units of
%                  kilograms per cubic meter. If omitted, the air is
%                  assumed to be dry (rhow=0). The total density of the air
%                  is assumed to be the sum of the dry air density and
%                  rhow.
%
%OUTPUTS: N The refractivity of the atmosphere. In this model, N is always
%           real. N=10^6*(n-1) where n is the index of refraction. This is
%           generally valid for frequencies from L-band (1GHz) to 10 GHz
%           (the middle of X-band).
%       rho The atmospheric density at the point in question in units of
%           kilograms per cubic meter.
%         T The temperature at the point in question with units of degrees
%           Kelvin.
%         P The atmospheric pressure at the point in question in units of
%           Newtons per square meter (Pascals). It assumes that the gasses
%           can be treated as ideal gasses.
%
%The dry air density and temperature is obtained from the NRLMSISE-00
%atmospheric model using the default parameters for magnetic and solar
%parameters, which are generally valid below 90km, using the function
%NRLMSISE00GasTemp. The reslting standard atmospheric parameters are then
%passed to the atmosParam4GasTemp function.
%
%The atmospheric pressure is obtained using the Ideal Gas Law as described
%in [1]. The properties of atmospheric constituents used in
%atmosParam4GasTemp are more recent than those used in the NRLMSISE-00
%standard. Consequently, pressure obtained for the associated altitude is
%not completely numerically consistent with the function
%NRLMSISE00Alt4Pres.
%
%REFERENCES:
%[1] D. P. Drob, THE NRLMSISE-00 AND HWM-93 USERS GUIDE: Version 1.50,
%    Nov. 2003.
%
%December 2013 David F. Crouse, Naval Research Laboratory, Washington D.C.
%(UNCLASSIFIED) DISTRIBUTION STATEMENT A. Approved for public release.

if(nargin<4)
   rhow=0;
end

[gasTable,t]=NRLMSISE00GasTemp(dayOfYear,secondOfTheDay,latLonAlt);
T=t(2);%The temperature in Kelving at the point.

[N,rho,P]=atmosParam4GasTemp(gasTable,T,rhow);
end

%LICENSE:
%
%The source code is in the public domain and not licensed or under
%copyright. The information and software may be used freely by the public.
%As required by 17 U.S.C. 403, third parties producing copyrighted works
%consisting predominantly of the material produced by U.S. government
%agencies must provide notice with such work(s) identifying the U.S.
%Government material incorporated and stating that such material is not
%subject to copyright protection.
%
%Derived works shall not identify themselves in a manner that implies an
%endorsement by or an affiliation with the Naval Research Laboratory.
%
%RECIPIENT BEARS ALL RISK RELATING TO QUALITY AND PERFORMANCE OF THE
%SOFTWARE AND ANY RELATED MATERIALS, AND AGREES TO INDEMNIFY THE NAVAL
%RESEARCH LABORATORY FOR ALL THIRD-PARTY CLAIMS RESULTING FROM THE ACTIONS
%OF RECIPIENT IN THE USE OF THE SOFTWARE.