function F = filmStrip( I, overlap, delta, border )
% Used to display R stacks of T images as a "filmstrip".
%
% See examples below to see what is meant by "filmstrip".
%
% USAGE
%  F = filmStrip( I, overlap, delta, border )
%
% INPUTS
%  I          - MxNxTxR or MxNx1xTxR or MxNx3xTxR array
%               (of bw or color images). R can equal 1.
%  overlap    - amount of overlap between successive frames
%  delta      - amount to shift each successive frame upward
%  border     - width of black border around each frame
%
% OUTPUTS
%  F       - filmstrip
%
% EXAMPLE - one filmstrip
%  load images;
%  F1 = filmStrip( video(:,:,1:15), 10, 2, 5 );   figure(1); im(F1); % one
%  F2 = filmStrip( videos(:,:,:,1:10), 5, 2, 3 ); figure(2); im(F2); % many
%
% See also MONTAGE2
%
% Piotr's Computer Vision Matlab Toolbox      Version 2.0
% Copyright 2014 Piotr Dollar.  [pdollar-at-gmail.com]
% Licensed under the Simplified BSD License [see external/bsd.txt]

% convert I to be of type double, and have dimensions MxNxCxTxR
I = double(I); I = I/max(I(:)); sizI = size(I);
if(~any(sizI(3)==[1 3])); I=reshape(I,[sizI(1:2),1,sizI(3:end)]); end
I = padarray( I, [border border 0 0 0], 0, 'both' );
[mRows, nCols, nColor, nFrame, nStrip] = size(I);

% size of final filmstip object
sizF1 = [mRows+delta*(nFrame-1), nFrame*nCols-overlap*(nFrame-1), nColor];
sizF=sizF1;  sizF(1)=sizF1(1)*nStrip - ((nFrame-1-2)*delta)*(nStrip-1);
mRowsF1 = sizF1(1);

for i=1:nStrip

  % Create i-th film strip
  Fi = -ones( sizF1 );  row = 1;  col = sizF1(2);
  for f=nFrame:-1:1
    Fi( row:(row+mRows-1), (col-nCols+1):col, : ) = I(:,:,:,f,i);
    row = row + delta;  col = col - nCols + overlap;
  end

  % stop if creating single filmstrip
  if( nStrip==1 ); F=Fi; break; end

  % merge with the previous film strips
  if( i==1 ); F = -ones( sizF );  row2=1;  end
  Fc = F( row2:(row2+mRowsF1-1), : );
  locs=(Fc<0);  Fc(locs) = Fi(locs);
  F( row2:(row2+mRowsF1-1), :  ) = Fc;
  row2 = row2 + mRowsF1 - ((nFrame-1-2)*delta);
end
F(F<0)=1;


%% NLOPT Install for OPTI Toolbox
% Copyright (C) 2012 Jonathan Currie (I2C2)

% This file will help you compile NonLinear OPTimization (NLOPT) for use
% with MATLAB.

% My build platform:
% - Windows 8 SP1 x64
% - Visual Studio 2012

% To recompile you will need to get / do the following:

% 1) Get NLOPT
% NLOPT is available from http://ab-initio.mit.edu/wiki/index.php/NLopt.
% Download the source.

% 2) Compile NLOPT
% The easiest way to compile NLOPT is to use the Visual Studio Project
% Builder included with OPTI. Use the following commands, substituting the
% required path on your computer:
%
% %% Visual Studio Builder Commands
% path = 'full path to NLOPT here'; %e.g. 'C:\Solvers\NLOPT'
% sdir = path;
% name = 'libnlopt';
% opts.exPP = {'_CRT_SECURE_NO_WARNINGS'};
% opts.exclude = {'testfuncs.c','tst.cc','tstc.c','testros.cc','prog.cc','redblack_test.c','DIRparallel.c'};
% opts.exFolder = {'octave','swig','test'};
% VS_WriteProj(sdir,name,[],opts)
% %%
%
% Once complete, you will have a directory called NLOPT\libnlopt. Open the
% Visual Studio 2012 project file, then complete the following steps:
%   a) A default config.h has not been supplied (and I'm guessing only gets
%   made in Linux versions) so I've made one up based on the template. Copy
%   the supplied config.h from:
%       OPTI/Solvers/nlopt/Source/Include
%   To the project directory.
%   b) Build a Win32 or x64 Release to compile the code.
%   c) Copy the generated .lib file to the following folder:
%
%   OPTI/Solvers/nlopt/Source/lib/win32 or win64
%
%   You will also need to copy nlopt.h to the following folder:
%
%   OPTI/Solvers/nlopt/Source/Include
%
%   Note with Global Optimization turned on in VS it will take a long time
%   to link the MEX file.

% 3) NLOPT MEX Interface
% The NLOPT MEX Interface was written by Steven Johnson and is located in
% the octave folder (nlopt_optimize-mex.c) HOWEVER in its original form it
% was not compatible with OPTI (due to the method of adding nonlinear
% constraints cell by cell), as well as the file name caused compile
% problems (no '-' allowed for MEX). Therefore I have modified the
% MEX interface and included is nlopt_optimize_mex.c which you will need to
% use for compatibility with OPTI. You will need to however copy
% nlopt_optimize_usage.h from nlopt-xxx/octave to:
%
%   OPTI/Solvers/nlopt/Source/Include

% 4) Compile the MEX File
% The code below will automatically include all required libraries and
% directories to build the NLOPT MEX file. Once you have completed all
% the above steps, simply run this file to compile NLOPT! You MUST BE in
% the base directory of OPTI!

clear nlopt

% Get Arch Dependent Library Path
libdir = opti_GetLibPath();

fprintf('\n------------------------------------------------\n');
fprintf('NLOPT MEX FILE INSTALL\n\n');

%Get NLOPT Libraries
post = [' -IInclude -L' libdir ' -llibnlopt -llibut -output nlopt'];

%CD to Source Directory
cdir = cd;
cd 'Solvers/nlopt/Source';

%Compile & Move
pre = 'mex -v -largeArrayDims nloptmex.c';
try
    eval([pre post])
    movefile(['nlopt.' mexext],'../','f')
    fprintf('Done!\n');
catch ME
    cd(cdir);
    error('opti:nlopt','Error Compiling NLOPT!\n%s',ME.message);
end
cd(cdir);
fprintf('------------------------------------------------\n');


function this = set_from_affine_geometry(this, affineTransformation, nVoxels, TR_s)
% Sets dimInfo from affine affineTransformation, assuming 4D nifti data
%
%   Y = MrDimInfo()
%   Y.set_from_affine_geometry(affineTransformation, nVoxels, TR_s)
%
% This is a method of class MrDimInfo.
%
% IN
%
% OUT
%
% EXAMPLE
%   set_from_affine_geometry
%
%   See also MrDimInfo

% Author:   Lars Kasper
% Created:  2017-11-07
% Copyright (C) 2017 Institute for Biomedical Engineering
%                    University of Zurich and ETH Zurich
%
% This file is part of the TAPAS UniQC Toolbox, which is released
% under the terms of the GNU General Public License (GPL), version 3.
% You can redistribute it and/or modify it under the terms of the GPL
% (either version 3 or, at your option, any later version).
% For further details, see the file COPYING or
%  <http://www.gnu.org/licenses/>.

if nargin < 3
    nVoxels = [1 1 1 1];
end

if nargin < 4
    TR_s = 1;
end

% or add them...
dimLabelsGeom = {'x','y','z', 't'};
units = {'mm', 'mm', 'mm', 's'};
iDimGeom = 1:4;
% update existing geom dimensions, add new ones for
% non-existing
iValidDimLabels = this.get_dim_index(dimLabelsGeom);
iDimGeomExisting = find(iValidDimLabels);
iDimGeomAdd = setdiff(iDimGeom, iDimGeomExisting);

% need nifti to reference first sampling point as offcenter
resolutions = [affineTransformation.scaling TR_s];

% voxel position by voxel center, time starts at 0
firstSamplingPoint = [affineTransformation.scaling 0]/2;

% if dimension labels exist, just update values
this.set_dims(dimLabelsGeom(iDimGeomExisting), ...
    'resolutions', resolutions(iDimGeomExisting), ...
    'nSamples', nVoxels(iDimGeomExisting), ...
    'firstSamplingPoint', firstSamplingPoint(iDimGeomExisting), ...
    'units', units(iDimGeomExisting));

% if they do not exist, create dims
this.add_dims(dimLabelsGeom(iDimGeomAdd), ...
    'resolutions', resolutions(iDimGeomAdd), ...
    'nSamples', nVoxels(iDimGeomAdd), ...
    'firstSamplingPoint', firstSamplingPoint(iDimGeomAdd), ...
    'units', units(iDimGeomAdd));


% ijcvMultiSegScript

imdir = '../images/stanford';
outdir = '../data';
ncv = 5;

if 1
    load '../data/stanford_imsegs2.mat';
    ijcv = load('../data/ijcv06/ijcvClassifier.mat');
    %load '../data/bsdsdata.mat';
end

cv_images = [1:numel(imsegs)];
nsegments = [5 10 15 20 25 30 35 40 45 50 60 70 80 90 100];

if ~exist('spfeatures')
    spfeatures = mcmcGetAllSuperpixelData(imdir, imsegs);
    [efeatures, adjlist] = mcmcGetAllEdgeData(spfeatures, imsegs);
    for f = 1:numel(imsegs)
        fprintf(1, '.', int32(f))
        if mod(f, 25)==0, fprintf(1, '\n'); end

        [pvSP{f}, phSP{f}, pE{f}] = mcmcInitialize(spfeatures{f}, efeatures{f}, ...
            adjlist{f}, imsegs(f), ijcv.vclassifierSP, ijcv.hclassifierSP, ijcv.eclassifier, ijcv.ecal, 'none');
        smaps{f} = generateMultipleSegmentations2(pE{f}, adjlist{f}, imsegs(f).nseg, nsegments);

        im = im2double(imread([imdir '/' imsegs(f).imname]));
        imdata = mcmcComputeImageData(im, imsegs(f));

        for k = 1:numel(nsegments)
            labdata{f, k} = mcmcGetSegmentFeatures(imsegs(f), spfeatures{f}, imdata, smaps{f}(:, k), (1:max(smaps{f}(:, k))));
            [mclab{f, k}, mcprc{f, k}, allprc{f, k}, trainw{f, k}] = segmentation2labels(imsegs(f), smaps{f}(:, k));
            unilabel{f, k} = mclab{f, k}.*(mcprc{f, k}>0.95);
            seglabel{f,k} =  1*(mcprc{f, k}>0.95) + (-1)*(mcprc{f, k}<0.95);
        end
    end

    save([outdir '/indoordata.mat'], 'spfeatures', 'efeatures', 'adjlist', ...
        'pvSP', 'phSP', 'pE', 'smaps', 'labdata', ...
        'mclab', 'mcprc', 'allprc', 'seglabel', 'unilabel', ...
        'trainw');
end
%nsegments = [3 5 6 6 7 8 9 10 11 12 13 14 15 17 18 20 23 27 32 45 50 75 100];
%nsegments = [3 4 5 6 7 7 8 8 9 9 10 11 11 12 12 13 13 14 14 15 16 16 17 19 20 21 23 25 28 31 37 51];

[vacc, hacc, vcm, hcm, pg] = testMultipleSegmentationsCV2(imsegs, ...
    labdata, labdata, smaps, ijcv.vclassifier, ijcv.hclassifier, ijcv.sclassifier, ...
    pvSP, phSP, 1);
save([outdir '/indoorResults_trainOutdoor.mat'], 'vacc', 'hacc', 'vcm', 'hcm', 'pg');
disp(num2str([vacc hacc]))
disp(num2str(vcm))
disp(num2str(hcm))

if ~exist('vclassifier')
    for k = 1:ncv
        disp(['Iteration: ' num2str(k)]);
        testind{k} = (floor((k-1)*numel(cv_images)/ncv)+1):(floor(k*numel(cv_images)/ncv));
        trainind{k} = setdiff([1:numel(cv_images)], testind{k});
        sclassifier(k) = mcmcTrainSegmentationClassifier2(...
            [labdata(trainind{k}, :) ; labdata], ...
            [seglabel(trainind{k}, :) ; seglabel], ...
            [trainw(trainind{k}, :) ; trainw]);
        [vclassifier(k), hclassifier(k)] = ...
            mcmcTrainSegmentClassifier2(...
            [labdata(trainind{k}, :) ; labdata], ...
            [unilabel(trainind{k}, :) ; unilabel], ...
            [trainw(trainind{k}, :) ; trainw], 100000);
    end
end
[vacc, hacc, vcm, hcm, pg] = testMultipleSegmentationsCV2(imsegs, ...
    labdata, labdata, smaps, vclassifier, hclassifier, sclassifier, [], [], ncv);
disp(num2str([vacc hacc]))
disp(num2str(vcm))
disp(num2str(hcm))

save([outdir '/indoorResults.mat'], 'vclassifier', 'hclassifier', 'sclassifier', 'vacc', 'hacc', 'vcm', 'hcm', 'pg');


function printModelStats(model, printModelIssues, printDetails)
% printModelStats
%   prints some statistics about a model to the screen
%
%   model               a model structure
%   printModelIssues    true if information about unconnected
%                       reactions/metabolites and elemental balancing
%                       should be printed (opt, default false)
%   printDetails        true if detailed information should be printed
%                       about model issues. Only used if printModelIssues
%                       is true (opt, default true)
%
%   Usage: printModelStats(model,printModelIssues, printDetails)

if nargin<2
    printModelIssues=false;
end
if nargin<3
    printDetails=true;
end

fprintf(['Network statistics for ' model.id ': ' model.name '\n']);

%Get which reactions are present in each compartment
rxnComps=sparse(numel(model.rxns),numel(model.comps));

%For each compartment, find the metabolites that are present in that
%compartment and then the reactions they are involved in
for i=1:numel(model.comps)
    [~, I]=find(model.S(model.metComps==i,:));
    rxnComps(I,i)=1;
end

if isfield(model,'eccodes')
    fprintf(['EC-numbers\t\t\t' num2str(numel(unique(model.eccodes))) '\n']);
end

%Print information about genes
if isfield(model,'genes')
    fprintf(['Genes*\t\t\t\t' num2str(numel(model.genes)) '\n']);
    %Find the genes in each compartment
    for i=1:numel(model.comps)
        [~, I]=find(model.rxnGeneMat(rxnComps(:,i)==1,:));
        fprintf(['\t' model.compNames{i} '\t' num2str(numel(unique(I))) '\n']);
    end
end

%Print information about reactions
fprintf(['\nReactions*\t\t\t' num2str(numel(model.rxns)) '\n']);
for i=1:numel(model.comps)
    fprintf(['\t' model.compNames{i} '\t' num2str(sum(rxnComps(:,i))) '\n']);
end

%Removes the effect of compartments and removes duplicate reactions
temp=model;
temp.comps(:)={'s'}; %Set all compartments to be the same
equ=constructEquations(sortModel(temp,true,true),temp.rxns,false);

fprintf(['Unique reactions**\t' num2str(numel(unique(equ))) '\n']);

%Print information about metabolites
fprintf(['\nMetabolites\t\t\t' num2str(numel(model.mets)) '\n']);
for i=1:numel(model.comps)
    fprintf(['\t' model.compNames{i} '\t' num2str(sum(model.metComps==i)) '\n']);
end

fprintf(['Unique metabolites\t' num2str(numel(unique(model.metNames))) '\n']);

fprintf('\n* Genes and reactions are counted for each compartment if any of the corresponding metabolites are in that compartment. The sum may therefore not add up to the total number.\n');
fprintf('** Unique reactions are defined as being biochemically unique (no compartmentalization)\n');

%Also print some potential problems if there are any
if printModelIssues==true
    fprintf(['\nShort model quality summary for ' model.id ': ' model.name '\n']);

    %Check that all the metabolites are being used
    involvedMat=model.S;
    involvedMat(involvedMat~=0)=1;
    usedMets=sum(involvedMat,2);
    notPresent=find(usedMets==0);
    if ~isempty(notPresent)
        errorText=['Non-used metabolites\t' num2str(numel(notPresent)) '\n'];
        if printDetails==true
            for i=1:numel(notPresent)
                errorText=[errorText '\t(' model.mets{notPresent(i)} ') ' model.metNames{notPresent(i)} '\n'];
            end
            errorText=[errorText '\n'];
        end
        fprintf(errorText);
    end

    %Check if there are empty reactions
    usedRxns=sum(involvedMat,1);
    notUsed=find(usedRxns==0);
    if ~isempty(notUsed)
        errorText=['Empty reactions\t' num2str(numel(notUsed)) '\n'];
        if printDetails==true
            for i=1:numel(notUsed)
                errorText=[errorText '\t' model.rxns{notUsed(i)} '\n'];
            end
            errorText=[errorText '\n'];
        end
        fprintf(errorText);
    end

    %Check if there are dead-end reactions/metabolites
    [~, deletedReactions, deletedMetabolites]=simplifyModel(model,true,false,false,true);

    if ~isempty(deletedReactions)
        errorText=['Dead-end reactions\t' num2str(numel(deletedReactions)) '\n'];
        if printDetails==true
            for i=1:numel(deletedReactions)
                errorText=[errorText '\t' deletedReactions{i} '\n'];
            end
            errorText=[errorText '\n'];
        end
        fprintf(errorText);
    end

    %Ignore non-used metabolites
    deletedMetabolites=setdiff(deletedMetabolites,model.mets(notPresent));
    %Must map to indexes in order to print names
    deletedMetabolites=find(ismember(model.mets,deletedMetabolites));
    if ~isempty(deletedMetabolites)
        errorText=['Dead-end metabolites\t' num2str(numel(deletedMetabolites)) '\n'];
        if printDetails==true
            for i=1:numel(deletedMetabolites)
                errorText=[errorText '\t(' model.mets{deletedMetabolites(i)} ') ' model.metNames{deletedMetabolites(i)} '\n'];
            end
            errorText=[errorText '\n'];
        end
        fprintf(errorText);
    end

    balanceStructure=getElementalBalance(model);

    notParsed=find(balanceStructure.balanceStatus<0);
    notBalanced=find(balanceStructure.balanceStatus==0);

    if ~isempty(notParsed)
        errorText=['Reactions which could not be elementally balanced\t' num2str(numel(notParsed)) '\n'];
        if printDetails==true
            for i=1:numel(notParsed)
                errorText=[errorText '\t' model.rxns{notParsed(i)} '\n'];
            end
            errorText=[errorText '\n'];
        end
        fprintf(errorText);
    end
    if ~isempty(notBalanced)
        errorText=['Reactions which are elementally unbalanced\t' num2str(numel(notBalanced)) '\n'];
        if printDetails==true
            names=strcat(balanceStructure.elements.names,{', '});
            for i=1:numel(notBalanced)
                badOnes=sprintf('%s', names{abs(balanceStructure.leftComp(notBalanced(i),:)-balanceStructure.rightComp(notBalanced(i),:))>10^-7});
                errorText=[errorText '\t' model.rxns{notBalanced(i)} '\t' badOnes(1:end-2) '\n'];
            end
            errorText=[errorText '\n'];
        end
        fprintf(errorText);
    end
end
end


function [d_out] = qpsk_srrc(d_in)

    persistent buf

    OS_RATE = 8;
    f = SRRC;

    if isempty(buf)
        buf = complex(zeros(1,OS_RATE*2+1),zeros(1,OS_RATE*2+1));
    end

    buf = [buf(2:end) d_in];

    d_out = buf*f;

end


function cls = spm_mars_newSeg(res,tc,bf,df,mode,tcm)
% FORMAT cls = spm_mars_newSeg(res,tc,bf,df,mode,tcm)
%
% Write out the results from toolbox 'New Segment'. This function is only for
% the purpose of making toolbox 'MARS' backwards compatible with 'New Segment'.
%
% Adapted from spm_preproc_write8 by John Ashburner
% $Id: spm_preproc_write8.m 4337 2011-05-31 16:59:44Z john $
% Copyright (C) 2008 Wellcome Department of Imaging Neuroscience
%
% Modified by Yu (Andy) Huang
% $Id: spm_mars_newSeg.m 2015-07-27 andy$
% Neural Engineering Lab, Dept. of Biomedical Engineering, City College of New York
% yhuang16@citymail.cuny.edu

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

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

N   = numel(res.image);
if nargin<2, tc = true(Kb,4); end % native, import, warped, warped-mod
if nargin<3, bf = true(N,2);  end % field, corrected
if nargin<4, df = true(1,2);  end % inverse, forward
% if nargin<5, mrf= 2;          end % MRF parameter  % andy 2013-05-03

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

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

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

    [pth1,nam1,ext1] = fileparts(res.image(n).fname);
    chan(n).ind      = res.image(n).n;

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

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

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

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

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

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

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

for z=1:length(x3),

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

    if do_defs,
        [t1,t2,t3] = defs(Coef,z,res.MT,prm,x1,x2,x3,M);
        if exist('Ndef','var'),
            tmp = M1(1,1)*t1 + M1(1,2)*t2 + M1(1,3)*t3 + M1(1,4);
            Ndef.dat(:,:,z,1,1) = tmp;
            tmp = M1(2,1)*t1 + M1(2,2)*t2 + M1(2,3)*t3 + M1(2,4);
            Ndef.dat(:,:,z,1,2) = tmp;
            tmp = M1(3,1)*t1 + M1(3,2)*t2 + M1(3,3)*t3 + M1(3,4);
            Ndef.dat(:,:,z,1,3) = tmp;
        end

        if exist('y','var'),
            y(:,:,z,1) = t1;
            y(:,:,z,2) = t2;
            y(:,:,z,3) = t3;
        end

        if do_cls,
            msk = (f==0) | ~isfinite(f);

            if isfield(res,'mg'),
                q   = zeros([d(1:2) Kb]);
                q1  = likelihoods(cr,[],res.mg,res.mn,res.vr);
                q1  = reshape(q1,[d(1:2),numel(res.mg)]);
                b   = spm_sample_priors8(tpm,t1,t2,t3);
                for k1=1:Kb,
                    q(:,:,k1) = sum(q1(:,:,lkp==k1),3).*b{k1};
                end
            else
                q   = spm_sample_priors8(tpm,t1,t2,t3);
                q   = cat(3,q{:});
                for n=1:N,
                    tmp = round(cr{n}*res.intensity(n).interscal(2) + res.intensity(n).interscal(1));
                    tmp = min(max(tmp,1),size(res.intensity(n).lik,1));
                    for k1=1:Kb,
                        likelihood = res.intensity(n).lik(:,k1);
                        q(:,:,k1)  = q(:,:,k1).*likelihood(tmp);
                    end
                end
            end
            Q(:,:,z,:) = reshape(q,[d(1:2),1,Kb]); % final probabilities not normalized

        end
    end
    spm_progress_bar('set',z);
end
spm_progress_bar('clear');

cls   = cell(1,Kb);
if do_cls
    P = zeros([d(1:3),Kb],'uint8');
    for z=1:length(x3),
        sq = sum(Q(:,:,z,:),4) + eps^2;
        for k1=1:Kb
            P(:,:,z,k1) = uint8(round(255 * Q(:,:,z,k1)./sq)); % nomalized probabilities
        end
    end
%     if mrf~=0, nmrf_its = 10; else nmrf_its = 1; end % andy 2013-05-03

%     if mrf==0, % Done this way as spm_mrf is not compiled for all platforms yet
    if mode == 0  % andy 2013-05-03
        sQ = (sum(Q,4)+eps)/255;
        for k1=1:size(Q,4)
            P(:,:,:,k1) = uint8(round(Q(:,:,:,k1)./sQ));
        end
        clear sQ
    else
        nmrf_its = 10; % andy 2013-05-03
        spm_progress_bar('init',nmrf_its,['MRF: Working on ' nam],'Iterations completed');
        vx2 = single(sum(res.image(1).mat(1:3,1:3).^2));
        if iscellstr(tcm) % if user specifies TCM, load it  % andy 2013-05-03
            load(tcm{1},'C');
            if ndims(C)~=2
                warning('Warn:convert',...
                    'Local or regional TCM detected. For MRF-based clean-up provided by SPM8, user-provided TCM must be a global TCM!\nSPM8 default config is used instead...\n');
                G   = ones([Kb,1],'single')*2;
            else
            G = single(C);  % andy 2013-04-22
            end
        else
            G   = ones([Kb,1],'single')*2; % andy 2013-05-03
            %save PQG P Q G tiss Kb x3 ind
        end

        for iter=1:nmrf_its,
            spm_mrf(P,Q,G,vx2);
            spm_progress_bar('set',iter);
        end
    end

    clear Q

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

    for k1=1:Kb,
        if tc(k1,4) || any(tc(:,3)) || tc(k1,2) || nargout>=1,
            cls{k1} = P(:,:,:,k1);
        end
    end
    clear P
end

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

if any(tc(:,2)),

    bb = nan(2,3);
    vx = 1.5;
    % Sort out bounding box etc
    bb(~isfinite(bb)) = bb1(~isfinite(bb));
    if ~isfinite(vx), vx = abs(prod(vx1))^(1/3); end;
    bb(1,:) = vx*round(bb(1,:)/vx);
    bb(2,:) = vx*round(bb(2,:)/vx);

    % Figure out the mapping from the volumes to create to the original
    mm = [[
        bb(1,1) bb(1,2) bb(1,3)
        bb(2,1) bb(1,2) bb(1,3)
        bb(1,1) bb(2,2) bb(1,3)
        bb(2,1) bb(2,2) bb(1,3)
        bb(1,1) bb(1,2) bb(2,3)
        bb(2,1) bb(1,2) bb(2,3)
        bb(1,1) bb(2,2) bb(2,3)
        bb(2,1) bb(2,2) bb(2,3)]'; ones(1,8)];

    vx2  = M1\mm;
    odim = abs(round((bb(2,1:3)-bb(1,1:3))/vx))+1;
    vx3  = [[
        1       1       1
        odim(1) 1       1
        1       odim(2) 1
        odim(1) odim(2) 1
        1       1       odim(3)
        odim(1) 1       odim(3)
        1       odim(2) odim(3)
        odim(1) odim(2) odim(3)]'; ones(1,8)];

    x      = affind(rgrid(d),M0);
    y1     = affind(y,M1);
    ind    = find(tc(:,2));
    [M,R]  = spm_get_closest_affine(x,y1,single(cls{ind(1)})/255);
    clear x y1

    M      = M0\inv(R)*M1*vx2/vx3;
    mat0   =         R\M1*vx2/vx3;
    mat    = mm/vx3;

    fwhm = max(vx./sqrt(sum(res.image(1).mat(1:3,1:3).^2))-1,0.01);
    for k1=1:size(tc,1),
        if tc(k1,2),
            tmp1     = decimate(single(cls{k1}),fwhm);
            [pth,nam,ext1]=fileparts(res.image(1).fname);
            VT      = struct('fname',fullfile(pth,['rc', num2str(k1), nam, '.nii']),...
                'dim',  odim,...
                'dt',   [spm_type('float32') spm_platform('bigend')],...
                'pinfo',[1.0 0]',...
                'mat',mat);
            VT = spm_create_vol(VT);

            Ni             = nifti(VT.fname);
            Ni.mat0        = mat0;
            Ni.mat_intent  = 'Aligned';
            Ni.mat0_intent = 'Aligned';
            create(Ni);

            for i=1:odim(3),
                tmp = spm_slice_vol(tmp1,M*spm_matrix([0 0 i]),odim(1:2),[1,NaN])/255;
                VT  = spm_write_plane(VT,tmp,i);
            end
            clear tmp1
        end
    end
end

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

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

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

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

return;
%=======================================================================

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

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

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

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

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

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


classdef Server < handle
  properties(SetAccess=private, GetAccess=private)
    heightmaps=containers.Map('KeyType', 'uint32', 'ValueType', 'any');
    map_id_last_used=[];
    map_id_last_added=[];
    max_stored_maps=5;
  end

  methods
    function obj = Server()
    end

    function region = getCSpaceRegionAtIndex(obj, i0, yaw, collision_model, varargin)
      p = inputParser();
      p.addRequired('i0', @isnumeric);
      p.addRequired('yaw', @isnumeric);
      p.addRequired('collision_model', @(x) isa(x, 'iris.terrain_grid.CollisionModel'));
      p.addParamValue('map_id', -1, @isnumeric);
      p.addParamValue('xy_bounds', iris.Polyhedron(zeros(0,2),zeros(0,1)), @(x) isa(x, 'iris.Polyhedron'));
      p.addParamValue('plane_distance_tolerance', 0.025, @isnumeric);
      p.addParamValue('plane_angle_tolerance', 10 * pi/180, @isnumeric);
      p.addParamValue('excluded_grid', []);
      p.addParamValue('debug', false);
      p.addParamValue('error_on_infeasible_start', true);
      p.parse(i0, yaw, collision_model, varargin{:});
      options = p.Results;

      if options.map_id == -1
        heightmap = obj.getHeightmap(obj.map_id_last_added(end));
      else
        heightmap = obj.getHeightmap(options.map_id);
      end

      x0 = heightmap.X(i0);
      y0 = heightmap.Y(i0);
      z0 = heightmap.Z(i0);
      p0 = [x0;y0;z0];
      n0 = heightmap.normals(:,i0);

      sz = size(heightmap.X);

      dist_to_plane = abs(n0'*[reshape(heightmap.X, 1, []); reshape(heightmap.Y, 1, []); reshape(heightmap.Z, 1, [])] - n0'*p0);
      dist_to_plane = reshape(dist_to_plane, sz);
      dist_mask = abs(dist_to_plane < options.plane_distance_tolerance);

      normal_product = n0' * heightmap.normals;
      normal_product = reshape(normal_product, sz);
      normal_angle_mask = normal_product > cos(options.plane_angle_tolerance);

      plane_mask = dist_mask & normal_angle_mask;
      if ~isempty(options.excluded_grid)
        if ~options.excluded_grid(i0)
          error('IRIS:TerrainSegmentation:BadSeed', 'the seed point is marked as infeasible in the excluded_grid option');
        end
        plane_mask = plane_mask & options.excluded_grid;
      end

      if ~plane_mask(i0)
        error('IRIS:TerrainSegmentation:SelectedPointInfeasible', 'cannot create a region around this point.');
      end

      boundary_mask = logical(iris.terrain_grid.component_boundary(plane_mask, i0));

      if options.debug
        figure(4)
        clf
        subplot(321)
        imshow(dist_mask, 'InitialMagnification', 'fit');
        title('dist')
        subplot(323)
        imshow(normal_angle_mask, 'InitialMagnification', 'fit');
        title('normal');
        subplot(325)
        imshow(options.excluded_grid, 'InitialMagnification', 'fit');
        title('existing');
        subplot(322)
        imshow(plane_mask, 'InitialMagnification', 'fit');
        title('combined');
        subplot(324)
        imshow(~boundary_mask, 'InitialMagnification', 'fit');
        title('boundary');
      end

      obs_x = heightmap.X(boundary_mask);
      obs_y = heightmap.Y(boundary_mask);
      obstacle_pts = reshape([reshape(obs_x, 1, []); reshape(obs_y, 1, [])], 2, 1, []);
      theta_steps = yaw + (-pi:pi/4:pi);

      %% Get obstacles for the foot shape
      % Reorient the foot onto the terrain plane
      c = cross([0;0;1], n0);
      if norm(c) >= 1e-6
        ax = c / norm(c);
        angle = asin(norm(c));
        Rplane = axis2rotmat([ax; angle]);
      else
        Rplane = eye(3);
      end

      c_obs = iris.cspace.cspace3(obstacle_pts, collision_model.foot_shape, theta_steps, Rplane(1:2,1:2));

      %% Get obstacles for the upper body collision model
      % compute distance from the heightmap Z to the current plane
      % n' * [x;y;z] = n' * point
      % z = (n'*point - n(1:2)'*[x;y])/n(3)
      dZ = heightmap.Z - reshape((n0'*p0 - n0(1:2)'*[reshape(heightmap.X,1,[]);reshape(heightmap.Y,1,[])])/n0(3), sz);
      for j = 1:length(collision_model.body_slices.z)
        zmin = collision_model.body_slices.z(j);
        if j < length(collision_model.body_slices.z)
          zmax = collision_model.body_slices.z(j+1);
        else
          zmax = inf;
        end
        z_range_mask = dZ >= zmin & dZ <= zmax;
        boundary_mask = logical(iris.terrain_grid.component_boundary(~z_range_mask, i0));
        obs_x = heightmap.X(boundary_mask);
        obs_y = heightmap.Y(boundary_mask);
        if ~isempty(obs_x)
          obstacle_pts = reshape([reshape(obs_x, 1, []); reshape(obs_y, 1, [])], 2, 1, []);
          c_obs = cat(3, c_obs, iris.cspace.cspace3(obstacle_pts, collision_model.body_slices.xy(:,:,j), theta_steps ));
        end
      end

      %% Add a bounding polyhedron
      bounds = iris.Polyhedron.from2DVertices([heightmap.X(end,1), heightmap.X(end,end), heightmap.X(1,end), heightmap.X(1,1);
                                             heightmap.Y(end,1), heightmap.Y(end,end), heightmap.Y(1,end), heightmap.Y(1,1)]);
      % Add bounds on yaw angle
      bounds.A = [bounds.A, zeros(size(bounds.A, 1), 1);
                  zeros(2, size(bounds.A, 2)), [-1; 1]];
      bounds.b = [bounds.b; -theta_steps(1); theta_steps(end)];
      if size(options.xy_bounds.A, 2) == 2
        options.xy_bounds.A(:,end+1:3) = 0;
      end
      bounds.A = [bounds.A; options.xy_bounds.A];
      bounds.b = [bounds.b; options.xy_bounds.b];
      [A, b, C, d] = iris.inflate_region(c_obs, bounds.A, bounds.b, [x0; y0; yaw], 'require_containment', true, 'error_on_infeasible_start', options.error_on_infeasible_start);

      [A, iA] = unique(A, 'rows');
      b = b(iA);
      region = iris.TerrainRegion(A, b, C, d, p0, n0);

      if options.debug
        figure(12)
        clf
        iris.drawing.drawPolyFromVertices(iris.thirdParty.polytopes.lcon2vert(A, b)', 'r');
      end

    end

    function regions = findSafeTerrainRegions(obj, map_id, collision_model, varargin)
      p = inputParser();
      p.KeepUnmatched = true;
      p.addRequired('map_id', @isnumeric);
      p.addRequired('collision_model', @(x) isa(x, 'iris.terrain_grid.CollisionModel'));
      p.addParamValue('seeds', []);
      p.addParamValue('default_yaw', 0, @isnumeric);
      p.addParamValue('max_slope_angle', 40 * pi/180, @isnumeric);
      p.addParamValue('max_height_variation', 0.05, @isnumeric);
      p.addParamValue('xy_bounds', iris.Polyhedron(zeros(0,2),zeros(0,1)), @(x) isa(x, 'iris.Polyhedron'));
      p.addParamValue('max_num_regions', inf, @(x) x > 0);
      p.addParamValue('debug', false);
      p.parse(map_id, collision_model, varargin{:});
      options = p.Results;

      region_options = p.Unmatched;
      region_options.map_id = map_id;
      region_options.xy_bounds = options.xy_bounds;
      region_options.debug = options.debug;

      regions = iris.TerrainRegion.empty();

      foot_length = max(collision_model.foot_shape(1,:)) - min(collision_model.foot_shape(1,:));

      heightmap = obj.getHeightmap(map_id);
      slope_angles = heightmap.getSlopeAngles();
      potential_safe_grid = slope_angles < options.max_slope_angle;
      sz = size(potential_safe_grid);

      within_bounds = all(bsxfun(@le, options.xy_bounds.A * [reshape(heightmap.X, 1, []); reshape(heightmap.Y, 1, [])], options.xy_bounds.b), 1);
      potential_safe_grid = potential_safe_grid & reshape(within_bounds,sz);
      for dx = -1:1
        for dy = -1:1
          dZ = heightmap.Z(2:end-1,2:end-1) - heightmap.Z((2+dx):(end-1+dx),(2+dy):(end-1+dy));
          potential_safe_grid(2:end-1,2:end-1) = potential_safe_grid(2:end-1,2:end-1) & abs(dZ) < options.max_height_variation;
        end
      end

      excluded_grid = true(sz);
      seed_ind = 1;

      while length(regions) < options.max_num_regions

        if options.debug
          figure(3)
          clf
          imshow(potential_safe_grid, 'InitialMagnification', 'fit');
        end

        if seed_ind <= size(options.seeds, 2)
          seed = options.seeds([1,2,6], seed_ind);
          yaw = seed(3);
          seed_ind = seed_ind + 1;
          i0 = obj.xy2ind(map_id, seed(1:2));
          % seed
          % heightmap.X(i0)
          % heightmap.Y(i0)
          % disp('here')
        else
          obs_dists = iris.terrain_grid.obs_dist(potential_safe_grid);
          [max_dist, i0] = max(obs_dists(:));
          yaw = options.default_yaw;
          if max_dist < 0.5 * foot_length / mean(heightmap.resolution);
            break;
          end
        end

        try
          region = obj.getCSpaceRegionAtIndex(i0, yaw, collision_model, region_options, 'excluded_grid', excluded_grid, 'error_on_infeasible_start', true);
        catch e
          if strcmp(e.identifier, 'IRIS:InfeasibleStart')
            excluded_grid(i0) = false;
            potential_safe_grid(i0) = false;
            continue
          else
            rethrow(e);
          end
        end
        regions(end+1) = region;
        % excluded_grid(i0) = false;
        potential_safe_grid(i0) = false;

        inpoly = all(bsxfun(@minus, region.A * [reshape(heightmap.X, 1, []); reshape(heightmap.Y, 1, []); yaw + zeros(1, numel(heightmap.X))], region.b) <= foot_length / 2, 1);
        inpoly = reshape(inpoly, sz);
        potential_safe_grid = potential_safe_grid & ~inpoly;
      end
    end

    function i0 = xy2ind(obj, map_id, xy)
      heightmap = obj.getHeightmap(map_id);
      sz = size(heightmap.X);
      [~, m0] = min(abs(heightmap.Y(:,1) - xy(2)));
      [~, n0] = min(abs(heightmap.X(1,:) - xy(1)));
      i0 = sub2ind(sz, m0, n0);
    end

    function obj = addHeightmap(obj, id, heightmap)
      if isa(heightmap, 'RigidBodyHeightMapTerrain')
        [X, Y] = meshgrid(heightmap.x, heightmap.y);
        [Z, normals] = heightmap.getHeight([reshape(X, 1, []); reshape(Y, 1, [])]);
        heightmap = iris.terrain_grid.Heightmap(X, Y, reshape(Z, size(X)), normals);
      elseif isa(heightmap, 'iris.terrain_grid.Heightmap')
      else
        error('IRIS:TerrainSegmentation:BadHeightmap', 'unrecognized heightmap class: %s', class(heightmap));
      end
      obj.heightmaps(id) = heightmap;
      obj.map_id_last_added(end+1) = id;
      obj.cleanup();
    end

    function b = hasHeightmap(obj, id)
       b = obj.heightmaps.isKey(id);
     end

    function heightmap = getHeightmap(obj, id)
      heightmap = obj.heightmaps(id);
      obj.map_id_last_used(obj.map_id_last_used == id) = [];
      obj.map_id_last_used(end+1) = id;
      obj.cleanup();
    end

    function cleanup(obj)
      if length(obj.map_id_last_added) >= 2 * obj.max_stored_maps || length(obj.map_id_last_used) >= 2 * obj.max_stored_maps
        last_used = obj.map_id_last_used(max(1, end-(obj.max_stored_maps-1)):end);
        last_added = obj.map_id_last_added(max(1, end-(obj.max_stored_maps-1)):end);
        ids = num2cell(union(last_added, last_used));
        obj.heightmaps = containers.Map(ids, obj.heightmaps.values(ids));
      end
    end
  end
end


function [signalPeaks, signalPeaksArray, signalSigmas, signalStruct] = computeSignalPeaks(signalMatrix, varargin)
	% [signalPeaks, signalPeaksArray, signalSigmas] = computeSignalPeaks(signalMatrix, varargin)
	%
	% Binarize [0,1] input analog signals based on peaks in the signal.
	%
	% Biafra Ahanonu
	% started: 2013.10.28
	%
	% inputs
	% 	signalMatrix: [nSignals frame] matrix containing analog input signals.
	% outputs
	% 	signalPeaks: [nSignals frame] matrix. Binary matrix with 1 = peaks, 0 = non-peaks.
	% 	signalPeaksArray: {1 nSignals} cell array. Each cell contains [1 nPeaks] vector that stores the frame locations of each peak.
	% 	signalSigmas: [nSignals 1] - std of each signal.
	% 	signalStruct: structure containing signalPeaks and signalPeaksArray if multiple thresholds requested.
	% options
	% 	See below.
	% 	% make a plot?
	% 	options.makePlots = 0;
	% 	% show waitbar?
	% 	options.waitbarOn = 1;
	% 	% make summary plots of spike information
	% 	options.makeSummaryPlots = 0;
	% 	% number of standard deviations above the threshold to count as spike
	% 	options.numStdsForThresh = 3;
	% 	% minimum number of time units between events
	% 	options.minTimeBtEvents = 8;
	% 	% shift peak detection
	% 	options.nFramesShift = 0;
	% 	% should diff and fast oopsi be done?
	% 	options.addedAnalysis = 0;
	% 	% use simulated oopsi data
	% 	options.oopsiSimulated = 0;
	% changelog
		% 2015.10.06 [00:14:09] Changed computePeakForSignal to shift the signal to the actual nearby peak since findpeak is sometimes off by a frame or two, should also improve the S-ratio.
		% 2016.07.05 [14:52:43] Made changes to computePeakForSignal to improve diff based peak detection.
		% 2021.08.08 [19:30:20] - Updated to handle CIAtah v4.0 switch to all functions inside ciapkg package.
		% 2022.04.20 [21:29:09] - Better comments for options.
	% TODO:
		% Add option to obtain multiple signal peak outputs from different thresholds in the same run, would save time.
		% allow input of options file (e.g. for different GCaMP variants, brain regions, etc.)
		% integrate nearest neighbor into analysis if there is a lot of cross-talk
		% possibly integrate into identifySpikes?
		% TODO: convert main loop to parfor, convert signalMatrix to cell array to allow this. Normally fast enough that this won't provide a speed-up.

	% add controller directory and subdirectories to path
	% addpath(genpath(pwd));
	import ciapkg.api.* % import CIAtah functions in ciapkg package API.

	%========================
	% Binary: 1 = show plots with found events and other information for each signal. 0 = do not show signal plot GUI.
	options.makePlots = 0;
	% Binary: 1 = show wait bar, 0 = do not show wait bar.
	options.waitbarOn = 1;
	% Binary: 1 = make summary plots of spike information, 0 = no summary plots.
	options.makeSummaryPlots = 0;
	% ===
	% Int: number of standard deviations above the threshold to count as spike
	options.numStdsForThresh = 3; % 0.5
	% DEPRECIATED - Int: alternative to options.numStdsForThresh for display purposes
	options.numStdsForThreshTwo = 2;
	% Int: minimum number of time units between events.
	options.minTimeBtEvents = 8;
	% Str: detect on differential ('diff') or raw ('raw') trace
	options.detectMethod = 'diff'; % 'raw'
	% Int: the size of the window to use to ignore smaller peaks near larger peaks.
	options.movAvgReqSize = 2;
	% Int: the size of the moving average to use on the input signals, to smooth out noise.
	options.movAvgFiltSize = 3;
	% Binary: 1 = use moving average as specified in options.movAvgFiltSize.
	options.doMovAvg = 1;
	% Binary: 1 = subtract median calculated over a filter of some range.
	options.doMedianFilter = 1;
	% Int: number of frames to calculate rolling median filter.
	options.medianFilterLength = 201;
	% Binary: 1 = leave report peaks as determined by findpeaks (e.g. normally midpoint of the peak rise if using 'diff'). 0 = adjust peak location to the maximum value found within a options.peakMaxLook window.
	options.reportMidpoint = 0;
	% Int vector: frames before and after region around each peak to look for a maximum to adjust the test peak by.
	options.peakMaxLook = -6:6;
	% Int: number of frames to shift detected peaks.
	options.nFramesShift = 0;
	% ===
	% Binary: 1 = perform diff and fast oopsi.
	options.addedAnalysis = 0;
	% Binary: 1 = use simulated oopsi data.
	options.oopsiSimulated = 0;
	% Binary: 1 = open workers, 0 = do not open workers
	options.parallel = 1;
	% Binary: 1 = display output information. 0 = suppress most output (e.g. when running in batch for certain applications).
	options.outputInfo = 1;
	% Binary: 1 = convert input inputSignals matrix to cell array
	options.convertSignalsToCell = 1;
	% get user inputs
	options = getOptions(options,varargin);
	% unpack options into current workspace
	% fn=fieldnames(options);
	% for i=1:length(fn)
	%     eval([fn{i} '=options.' fn{i} ';']);
	% end
	%========================
	try
		% make sure matrix is double
		% if ~strcmp(class(signalMatrix),'double')
		if ~isa(signalMatrix,'double')
			if options.outputInfo==1
				disp('converting signalMatrix to double');
			end
			signalMatrix = double(signalMatrix);
		end
		if options.outputInfo==1
			disp('calculating signal peaks...')
		end
		nSignals = size(signalMatrix,1);
		nFrames = size(signalMatrix,2);

		% Remove any NaN signals, set to the mean
		if sum(isnan(signalMatrix(:)))>0
			if options.outputInfo==1
				disp('Removing NaNs from matrix');
			end
			for signalNo = 1:nSignals
				repIdx = isnan(signalMatrix(signalNo,:));
				signalMatrix(signalNo,repIdx) = nanmean(signalMatrix(signalNo,:));
			end
		end

		signalStruct = struct;

		% this matrix will contain binarized version of signalMatrix
		signalPeaks = zeros(size(signalMatrix));
		% contains a list for each signal of locations of peaks
		signalPeaksArray = cell([1 nSignals]);
		% open waitbar
		% if options.waitbarOn==1 waitbarHandle = waitbar(0, 'detecting traces...'); end
		% loop over all signals.
		% reverseStr = '';
		manageParallelWorkers('parallel',options.parallel);
		signalPeaksArrayTmp = cell([1 nSignals]);
		signalSigmas = repmat([],[1 nSignals]);
		% try;[percent progress] = parfor_progress(nSignals);catch;end; dispStepSize = round(nSignals/20); dispstat('','init');

		% Convert to cell array to reduce memory transfer during parallelization
		if options.convertSignalsToCell==1
			% signalMatrix = squeeze(mat2cell(signalMatrix,ones(1,size(signalMatrix,1)),size(signalMatrix,2)));
		end

		[optionsOut] = computePeakForSignalOptions('options', options);

		% Only implement in Matlab 2017a and above
		if ~verLessThan('matlab', '9.2')
			D = parallel.pool.DataQueue;
			afterEach(D, @nUpdateParforProgress);
			p = 1;
			N = nSignals;
			nInterval = round(nSignals/30);%25
			options_waitbarOn = options.waitbarOn;
		end

		% signalIdxAll = {};
		% if options.convertSignalsToCell==1
		%     parfor signalNum3 = 1:nSignals
		%         signalIdxAll{signalNum3} = signalNum3;
		%     end
		% else
		%     parfor signalNum3 = 1:nSignals
		%         signalIdxAll{signalNum3} = sub2ind(size(signalMatrix), repmat(signalNum3,[nFrames 1]), [1:nFrames]');
		%     end
		% end

		% startState = ticBytes(gcp);
		if isempty(gcp)
			optsConstant.Value = optionsOut;
		else
			optsConstant = parallel.pool.Constant(optionsOut);
		end
		optionsCopy_addedAnalysis = options.addedAnalysis;
		optionsCopy_convertSignalsToCell = options.convertSignalsToCell;
		parfor signalNum = 1:nSignals
			optionsOutCopy = optsConstant.Value;
			% [percent progress] = parfor_progress;if mod(progress,dispStepSize) == 0;dispstat(sprintf('progress %0.1f %',percent));else;end
			% get the current signal and find its peaks
			% if options.convertSignalsToCell==1
			%     thisSignal = signalMatrix{signalNum2};
			%     signalNum = signalNum2;
			%     signalNum3 = signalNum2;
			%     signalSigmas(signalNum2) = std(thisSignal);
			%     signalPeaksArray{signalNum2} = computePeakForSignal(thisSignal,optionsOutCopy);
			% else
			%     signalNum = find(cellfun(@(x) sum(signalNum2==x),signalIdxAll));
			%     % signalIdx = sub2ind(size(signalMatrix), repmat(signalNum,[nFrames 1]), [1:nFrames]');
			%     % signalIdx = signalIdxAll{signalNum};
			%     thisSignal = signalMatrix(signalNum2);
			%     signalSigmas(signalNum) = std(thisSignal);
			%     signalPeaksArray{signalNum} = computePeakForSignal(thisSignal,optionsOutCopy);
			% end

			thisSignal = signalMatrix(signalNum,:);

			% if iscell(signalMatrix)==1
			%     thisSignal = signalMatrix{signalNum};
			% else
			%     thisSignal = signalMatrix(signalNum,:);
			%     % signalSigmas(signalNum) = std(thisSignal);
			%     % signalPeaksArray{signalNum} = computePeakForSignal(thisSignal,optionsOutCopy);
			% end

			%
			signalSigmas(signalNum) = std(thisSignal);
			signalPeaksArray{signalNum} = computePeakForSignal(thisSignal,optionsOutCopy);

			% [~] = viewComputePeaksPlot(thisSignal,signalPeaksArray{signalNum},[0 0 0],options.makePlots,50,2,'on')
			% ===
			if optionsCopy_addedAnalysis==1
				% using diff
				detectOld = optionsOutCopy.detectMethod;
				optionsOutCopy.detectMethod = 'diff';
				optionsOutCopy.detectMethod = detectOld;

				signalPeaksArrayTmp{signalNum} = computePeakForSignal(thisSignal,optionsOutCopy);
				% plot the resulting peaks overlayed
				[~] = viewComputePeaksPlot(thisSignal,signalPeaksArrayTmp{signalNum},[0 0 1],options.makePlots,20,0.1,'off',options)
				legend('signal','raw','signal','diff')
				title(['raw: ' num2str(length(signalPeaksArray{signalNum})) ' | diff: ' num2str(length(signalPeaksArrayTmp{signalNum}))]);
				[~,~,~]=ginput(1);
				% fast oopsi
				[Nhat] = computePeakForSignalOopsi(thisSignal,signalPeaksArray{signalNum},options);
				[~,~,~]=ginput(1);
				% if options.convertSignalsToCell==1
				% else
				% end
				% ===
			end
			% create matrix of peaks
			% signalPeaks(signalNum,signalPeaksArray{signalNum})=1;

			% waitbar access
			% reverseStr = cmdWaitbar(signalNum,nSignals,reverseStr,'inputStr','detecting peaks','waitbarOn',options.waitbarOn,'displayEvery',50);

			if ~verLessThan('matlab', '9.2')
				% Update
				send(D, signalNum);
			end
		end
		% tocBytes(gcp,startState)
		if options.makePlots==1
			for signalNum=1:nSignals
				if options.convertSignalsToCell==1
					% thisSignal = signalMatrix{signalNum};
					thisSignal = signalMatrix(signalNum,:);
				else
					thisSignal = signalMatrix(signalNum,:);
				end
					[~] = viewComputePeaksPlot(thisSignal,signalPeaksArray{signalNum},[0 0 0],options.makePlots,50,2,'on',options,signalNum,nSignals,options.numStdsForThresh);
					pause
					keyIn = get(gcf,'CurrentCharacter');
					if strcmp(keyIn,'e')
						return
					end
				% pause
				% clf
			end
		end
		for signalNum=1:nSignals
			% create matrix of peaks
			signalPeaks(signalNum,signalPeaksArray{signalNum})=1;
		end
	catch err
		signalPeaks = [];
		signalPeaksArray = {};
		disp(repmat('@',1,7))
		disp(getReport(err,'extended','hyperlinks','on'));
		disp(repmat('@',1,7))
	end
	% summary of general statistics for this set of IC data
	try
		if options.makeSummaryPlots==1
			viewSpikeSummary(signalMatrix,signalPeaks);
		end
	catch err
		disp(repmat('@',1,7))
		disp(getReport(err,'extended','hyperlinks','on'));
		disp(repmat('@',1,7))
	end
	function nUpdateParforProgress(~)
		if ~verLessThan('matlab', '9.2')
			p = p + 1;
			if (mod(p,nInterval)==0||p==2||p==nSignals)&&options_waitbarOn==1
				if p==nSignals
					fprintf('%d\n',round(p/nSignals*100))
				else
					fprintf('%d|',round(p/nSignals*100))
				end
				% cmdWaitbar(p,nSignals,'','inputStr','','waitbarOn',1);
			end
			% [p mod(p,nInterval)==0 (mod(p,nInterval)==0||p==nSignals)&&options_waitbarOn==1]
		end
	end
end
function [inputSignal] = viewComputePeaksPlot(inputSignal,testpeaks,dotColor,makePlots,markersize,linewidth,holdVal,options,signalNum,nSignals,numStdsForThresh)
	import ciapkg.api.* % import CIAtah functions in ciapkg package API.

	% decide whether to plot the peaks with points indicating location of
	% chosen peaks
	if makePlots==1
		% setFigureDefaults()
		fig1 = figure(422); clf;
		subplot(4,3,1)
			% hist(inputSignal(testpeaks),20);
			hist((inputSignal(testpeaks) - nanmean(inputSignal(:)))/nanstd(inputSignal(:)),20);
			hold(holdVal);
			title(num2str(nanstd(inputSignal(:))))
			xlabel('Peak amplitude (Z-score)');ylabel('Peak count')
			set(gca,'XMinorTick','on','TickDir','out');box off;
			% plot(histBins,histCounts);
			% set(gca,'yscale','log');

		subplot(4,3,2)
			peakROI = [-20:20];
			extractMatrix = bsxfun(@plus,testpeaks',peakROI);
			extractMatrix(extractMatrix<=0)=1;
			extractMatrix(extractMatrix>=size(inputSignal,2))=size(inputSignal,2);
			spikeCenterTrace = reshape(inputSignal(extractMatrix),size(extractMatrix));
			plot(repmat(peakROI, [size(spikeCenterTrace,1) 1])', spikeCenterTrace','Color',[4 4 4]/8)
			set(gca,'TickDir','out');box off;
			xlabel('Time (frames)');ylabel('Signal amplitude')
			title('All peaks')

		subplot(4,3,3)
			peakSignalAmplitude = inputSignal(testpeaks(:));
			[peakSignalAmplitude, peakIdx] = sort(spikeCenterTrace(:,round(end/2)+1),'descend');
			spikeCenterTrace = spikeCenterTrace(peakIdx,:);
			if size(spikeCenterTrace,1)>20
				spikeCenterTrace = spikeCenterTrace(1:20,:);
			end
			% xlabel('Time (frames)')
			plot(repmat(peakROI, [size(spikeCenterTrace,1) 1])', spikeCenterTrace','Color',[4 4 4]/8)
			xlabel('Time (frames)');ylabel('Signal amplitude')
			title('Top 20 peaks')
			set(gca,'TickDir','out');box off;

		subplot(4,3,[4:12])
			set(gcf,'color','w');
			% scnsize = get(0,'ScreenSize');
			% position = get(fig1,'Position');
			% outerpos = get(fig1,'OuterPosition');
			% borders = outerpos - position;
			% edge = -borders(1)/2;
			% %pos1 = [scnsize(3)/2 + edge, 0, scnsize(3)/2 - edge, scnsize(4)];
			% pos1 = [0, 0, scnsize(3), scnsize(4)];
			% set(fig1,'OuterPosition',pos1);

			plot(inputSignal, 'r');

			inputSignalMedian=medfilt1(inputSignal,options.medianFilterLength,'omitnan','truncate');
			inputSignal = inputSignal - inputSignalMedian;
			inputSignal = filtfilt(ones(1,options.movAvgFiltSize)/options.movAvgFiltSize,1,inputSignal);
			hold on;
			plot(inputSignal, 'b');

			set(gca,'Color','none'); box off;
			set(gca,'TickDir','out');box off;
			hold on;
			% axis([0 length(inputSignal) -0.1 0.5]);
			scatter(testpeaks, inputSignal(testpeaks),markersize, 'LineWidth',linewidth,'MarkerFaceColor',dotColor, 'MarkerEdgeColor',dotColor)
			legend({'Raw signal','Raw signal minus rolling median filter'},'Location','northoutside')
			axis tight;
			xlabel('Time (frames)');ylabel('Signal amplitude')
			% options.numStdsForThresh = options.numStdsForThreshTwo;
			% signalPeaksArray{signalNum} = computePeakForSignal(thisSignal, 'options', options);

			% [x,y,reply]=ginput(1);
			% close(fig1);
			hold(holdVal);
			title(sprintf('%d peaks | Zoom is enabled for closer look at peaks',length(testpeaks)))
		zoom on
		ciapkg.overloaded.suptitle(sprintf('Right arrow key to move to next signal | press ''e'' to exit | threshold = %0.2f | signal %d/%d',numStdsForThresh,signalNum,nSignals))
	end
end
function [Nhat] = computePeakForSignalOopsi(inputSignal,testpeaks, options, varargin)
	% clear, clc,
	import ciapkg.api.* % import CIAtah functions in ciapkg package API.

	switch options.oopsiSimulated
		case 1
			% set simulation metadata
			T       = 1000; % # of time steps
			V.dt    = 1/8;  % time step size

			% initialize params
			P.a     = 1;    % observation scale
			P.b     = 0;    % observation bias
			tau     = 1.5;    % decay time constant
			P.gam   = 1-V.dt/tau; % C(t) = gam*C(t-1)
			P.lam   = 0.1;  % firing rate = lam*dt
			P.sig   = 0.1;  % standard deviation of observation noise

			% simulate data
			N = poissrnd(P.lam*V.dt*ones(T,1)); % simulate spike train
			N(100) = 10;
			P.sig   = std(N);  % standard deviation of observation noise
		case 0
			% our data
			% inputSignal = inputSignal(1:1000);
			% testpeaks = testpeaks(testpeaks<1000);
			T = length(inputSignal);
			V.dt    = 1/5;  % time step size
			P.k = 30;
			%
			P.a     = 1;    % observation scale
			P.b     = 0;    % observation bias
			tau     = 1.5;    % decay time constant
			P.gam   = 1-V.dt/tau; % C(t) = gam*C(t-1)
			P.lam   = 0.01;  % firing rate = lam*dt

			inputSignalMetric = nanmean(inputSignal(testpeaks));
			% inputSignalMetric = nanmax(inputSignal(testpeaks));
			N = inputSignal(:)/inputSignalMetric;
			% inputSignal = normalizeVector(inputSignal,'normRange','zeroToOne');
			% N = inputSignal(:);
			P.sig = std(inputSignal(:)); % standard deviation of observation noise
			P.sig
			F = N;
			% N = zeros([length(inputSignal) 1]);
			% N(testpeaks) = 1;
			% normalizeVector(inputSignal(:),'normRange','zero')

		otherwise
			% body
	end
	P.sig = 0.1; % standard deviation of observation noise
	C = filter(1,[1 -P.gam],N);         % calcium concentration
	F = P.a*C+P.b + P.sig*randn(T,1);   % observations
	% fast oopsi
	[Nhat Phat] = fast_oopsi(F,V,P);

	% smc-oopsi
	V.smc_iter_max = 1;
	% [M P V] = smc_oopsi(F,V,P);

	%% plot results
	figure(1), clf
	tvec=0:V.dt:(T-1)*V.dt;
	tvec = 1:T;
	h(1)=subplot(411); plot(tvec,F); axis('tight'), ylabel('F (au)')
	h(2)=subplot(412); plot(tvec,C); axis('tight'), ylabel('C (au)')
	switch options.oopsiSimulated
		case 1
			h(3)=subplot(4,1,[3 4]); stem(tvec,N,'.'); hold on, plot(tvec,Nhat,'r','linewidth',1), axis('tight'), ylabel('fast')
		case 0
			h(3)=subplot(4,1,3); stem(tvec,N,'.'); hold on, plot(tvec,Nhat,'r','linewidth',1), axis('tight'), ylabel('fast')
			h(4)=subplot(414);plot(tvec,inputSignal, 'r'); box off; hold on;
			scatter(testpeaks, inputSignal(testpeaks),50, 'LineWidth',2,'MarkerFaceColor',[0 0 1], 'MarkerEdgeColor',[0 0 1]); axis('tight')
		otherwise
	end
	% Nsmc = M.nbar/max(M.nbar);
	% Nsmc(Nsmc<0.1)=0;
	% h(4)=subplot(414); stem(tvec,N); hold on, plot(tvec,Nsmc,'k','linewidth',2); axis('tight'), ylabel('smc')
	% xlabel('time (sec)')
	linkaxes(h,'x')
end
function [optionsOut] = computePeakForSignalOptions(varargin)
	import ciapkg.api.* % import CIAtah functions in ciapkg package API.

	%========================
	% number of standard deviations above the threshold to count as spike
	options.numStdsForThresh = 3;
	% minimum number of time units between events
	options.minTimeBtEvents = 10;
	% make a plot?
	options.makePlots = 0;
	% the size of the moving average
	options.movAvgReqSize = 2;
	options.movAvgFiltSize = 3;
	% subtract median calculated over a filter of some range.
	options.doMedianFilter = 1;
	% number of frames to calculate median filter
	options.medianFilterLength = 201;
	% decide whether to have a moving average
	options.doMovAvg = 1;
	% report the midpoint of the rise
	options.reportMidpoint=0;
	% shift peak detection
	options.nFramesShift = 0;
	% detect on differential ('diff') or raw ('raw') trace
	options.detectMethod = 'raw';
	% region around each peak to look for a maximum to adjust the test peak by
	options.peakMaxLook = [-6:6];
	% get options
	optionsOut = getOptions(options,varargin,'showWarnings',0);
end
function [testpeaks] = computePeakForSignal(inputSignal, options)
	import ciapkg.api.* % import CIAtah functions in ciapkg package API.

	% identifies peaks in an input signal given a particular threshold and other parameters.
	% biafra ahanonu
	% started: 2013.10.28
	% adapted from Lacey Kitch's and Laurie Burns' code
	% inputs
		%
	% outputs
		%
	% changelog
		% 2013.11.18 [20:06:00]
	% TODO
		%

	%========================
	% unpack options into current workspace
	fn=fieldnames(options);
	for i=1:length(fn)
		eval([fn{i} '=options.' fn{i} ';']);
	end;
	%========================

	% histBins = logspace(1,max(inputSignal));
	% [histCounts histBins] = hist(inputSignal(:),100);
	% plot(histBins,histCounts);
	% set(gca,'yscale','log');

	% median filter subtract
	if doMedianFilter==1
		inputSignalMedian=medfilt1(inputSignal,options.medianFilterLength,'omitnan','truncate');
		inputSignal = inputSignal - inputSignalMedian;
	end
	% moving average of input signal
	if doMovAvg==1
		% class(movAvgFiltSize)
		% movAvgFiltSize
		% class(inputSignal)
		% inputSignal(1:10)
		inputSignal = filtfilt(ones(1,movAvgFiltSize)/movAvgFiltSize,1,inputSignal);
	end

	switch options.detectMethod
		case 'diff'
			rawInputSignalStd = std(inputSignal(:));
			rawInputSignal = inputSignal;
			% get the differential
			inputSignal = [0 diff(inputSignal)];
			inputSignal(inputSignal<0) = 0;
			% options.nFramesShift = 0;
		case 'raw'
			%
		otherwise
			% body
	end

	% get standard deviation of current signal
	% inputSignalStd = std(inputSignal(:));
	inputSignalStd = nanstd(inputSignal(:));
	thisStdThreshold = inputSignalStd*numStdsForThresh;

	% =======
	% peakIdx = bsxfun(@plus,options.timeSeq',testpeaks);
	% tmpTestPeak = testpeaks;
	% % remove peaks outside range of signal
	% peakIdx(peakIdx>length(loopSignal))=[];
	% peakIdx(peakIdx<=0)=[];

	% % remove signal then add back in noise based on signal statistics
	% noiseSignal = loopSignal;
	% noiseSignal(peakIdx) = NaN;
	% =======

	% run findpeaks (part of signal), returns maxima above thisStdThreshold
	% and ignores smaller peaks around larger maxima within minTimeBtEvents
	warning off
	[~,testpeaks] = findpeaks(inputSignal,'minpeakheight',thisStdThreshold,'minpeakdistance',minTimeBtEvents);
	warning on

	% ignores smaller peaks around larger maxima within minTimeBtEvents
	%[~,testpeaks2] = findpeaks(inputSignal,'minpeakdistance',minTimeBtEvents);
	%testpeaks = intersect(testpeaks,testpeaks2);
	% extra check
	testpeaks = intersect(testpeaks,...
		find(filtfilt(ones(1,movAvgReqSize)/movAvgReqSize,1,inputSignal)>thisStdThreshold)...
		);

	% remove non-peaks within some set criteria
	switch options.detectMethod
		case 'diff'
			% get the differential
			testpeaks = testpeaks(rawInputSignal(testpeaks)>(numStdsForThresh*rawInputSignalStd));
			% options.nFramesShift = 0;

			% switch back for later analysis
			inputSignal = rawInputSignal;
		case 'raw'
			%
		otherwise
			% body
	end

	% check that maximum is at peak, else shift it
	if ~isempty(testpeaks)&options.reportMidpoint==0
		peakMaxLook = options.peakMaxLook;
		for peakNo = 1:length(testpeaks)
				testNewPeakIdx = testpeaks(peakNo)+peakMaxLook;
				if min(testNewPeakIdx)<=0|max(testNewPeakIdx)>length(inputSignal)
					continue;
				end
				testSignal = inputSignal(testNewPeakIdx);
				[~,maxSignal] = max(testSignal);
				% [maxSignal testpeaks(peakNo) testpeaks(peakNo)+(maxSignal-round(length(testNewPeakIdx)/2))]
				testpeaks(peakNo) = testpeaks(peakNo)+(maxSignal(1)-round(length(testNewPeakIdx)/2));
		end
	elseif options.reportMidpoint==1
		% peakMaxLook = options.peakMaxLook;
		% for peakNo = 1:length(testpeaks)
		%         testNewPeakIdx = testpeaks(peakNo)+peakMaxLook;
		%         if min(testNewPeakIdx)<=0|max(testNewPeakIdx)>length(inputSignal)
		%             continue;
		%         end
		%         testSignal = inputSignal(testNewPeakIdx);
		%         [~,maxSignal] = max(testSignal);
		%         % [maxSignal testpeaks(peakNo) testpeaks(peakNo)+(maxSignal-round(length(testNewPeakIdx)/2))]
		%         testpeaks(peakNo) = testpeaks(peakNo)+(maxSignal(1)-round(length(testNewPeakIdx)/2));
		% end
	end

	% shift peaks
	testpeaks = testpeaks + options.nFramesShift;

end

function [peakIdx] = subfxnCalcSignalNew(noiseSigmaThreshold,noiseSignal,loopSignal,tmpTestPeak)
	import ciapkg.api.* % import CIAtah functions in ciapkg package API.

	noiseStd = nanstd(noiseSignal(:));
	% noiseStd
	nPeaks = length(tmpTestPeak);
	peakIdxTmp1 = cell([1 nPeaks]);
	peakIdxTmp2 = cell([1 nPeaks]);
	% noiseSigmaThreshold = options.noiseSigmaThreshold;
	loopSignalThresholded = loopSignal>noiseSigmaThreshold*noiseStd;
	loopSignalThresholdedDiff = [0 diff(loopSignalThresholded)];
	for peakNo = 1:nPeaks
		try
			peakFrame = tmpTestPeak(peakNo);

			% currFrame = peakFrame;
			signalCriteria = loopSignalThresholdedDiff(1:peakFrame);
			currFrame = find(signalCriteria,1,'last');
			if isempty(currFrame)
				if sum(loopSignalThresholded(1:peakFrame))==length(loopSignalThresholded(1:peakFrame))
					currFrame = 1;
				end
			end
			% currFrame
			% aboveNoise = 1;
			% while aboveNoise==1
			%   aboveNoise = loopSignal(currFrame)>options.noiseSigmaThreshold*noiseStd;
			%   currFrame = currFrame - 1;
			%   if currFrame<1
			%       break
			%   end
			% end
			peakIdxTmp1{peakNo} = currFrame:peakFrame;

			% currFrame = peakFrame;
			% loopSignalThresholded = loopSignal>noiseSigmaThreshold*noiseStd;
			signalCriteria = abs(loopSignalThresholdedDiff(peakFrame:end));
			currFrame = find(signalCriteria,1,'first')+peakFrame;
			if isempty(currFrame)
				if sum(loopSignalThresholded(peakFrame:end))==length(loopSignalThresholded(peakFrame:end))
					currFrame = length(loopSignal);
				end
			end
			% currFrame
			% aboveNoise = 1;
			% currFrame = peakFrame;
			% while aboveNoise==1
			%   aboveNoise = loopSignal(currFrame)>options.noiseSigmaThreshold*noiseStd;
			%   currFrame = currFrame + 1;
			%   if currFrame>length(loopSignal)
			%       break
			%   end
			% end
			peakIdxTmp2{peakNo} = peakFrame:currFrame;
		catch err
			fprintf('peakFrame = %d\n',peakFrame);
			display(repmat('@',1,7))
			disp(getReport(err,'extended','hyperlinks','on'));
			display(repmat('@',1,7))
		end
	end
	peakIdx = [peakIdxTmp1{:} peakIdxTmp2{:}];
	peakIdx = unique(peakIdx(:));
end


function b = reduceboxes(model, bs)
% Eliminate columns for filters that are not used.
%   b = reduceboxes(model, bs)
%
%   E.g., [0 0 0 0 10 20 110 120] -> [10 20 110 120]
%   Index end-1 is the component label and index end is the
%   detection score.
%
%   This function assumes that model is a mixture model where
%   each component always places exactly the same number of filters.
%
% Return value
%   b       Filter bounding boxes with unused filter columns removed
% Arguments
%   model   Object model
%   bs      Filter bounding boxes

% AUTORIGHTS
% -------------------------------------------------------
% Copyright (C) 2009-2012 Ross Girshick
%
% This file is part of the voc-releaseX code
% (http://people.cs.uchicago.edu/~rbg/latent/)
% and is available under the terms of an MIT-like license
% provided in COPYING. Please retain this notice and
% COPYING if you use this file (or a portion of it) in
% your project.
% -------------------------------------------------------

% Only reduce boxes for mixtures of star models
if model.type ~= model_types.MixStar
  b = bs;
  return;
end

% n = #filters per component (assuming all components have
% the same number of parts)
n = length(model.rules{model.start}(1).rhs);
% n*4+2 := 4 coordinates per boxes plus the component index
% and score
b = zeros(size(bs, 1), n*4+2);
maxc = max(bs(:,end-1));
for i = 1:maxc
  % process boxes for component i
  I = find(bs(:,end-1) == i);
  tmp = bs(I,:);
  del = [];
  % find unused filters
  for j = 1:4:size(bs, 2)-2
    % count # of non-zero coordinates
    s = sum(sum(tmp(:,j:j+3)~=0));
    % the filter was not used if all coordinates are zero
    if s == 0
      del = [del j:j+3];
    end
  end
  % remove all unused filters
  tmp(:,del) = [];
  b(I,:) = tmp;
end


%% Copyright 2013 The MathWorks, Inc.
% Read input image
I = imread('visionteam.jpg');
figure,imshow(I);

%% Detect upright people
peopleDetector = vision.PeopleDetector;
[bboxes, scores] = step(peopleDetector,I);
I_people = insertObjectAnnotation(I,'rectangle',bboxes,scores);
figure, imshow(I_people);

%% Try the other model
peopleDetector = vision.PeopleDetector('ClassificationModel','UprightPeople_128x64');
peopleDetector.WindowStride = [4 4];
peopleDetector.MinSize = [256 128];
[bboxes, scores] = step(peopleDetector,I);
I_people = insertObjectAnnotation(I,'rectangle',bboxes,scores);
figure, imshow(I_people);

%% Use PeopleDetector with video
peopleDetector = vision.PeopleDetector;
video = vision.VideoFileReader('viptrain.avi');
viewer = vision.VideoPlayer;
while ~isDone(video)
    image = step(video);
    [bboxes, scores] = step(peopleDetector,image);
    I_people = insertObjectAnnotation(image,'rectangle',bboxes,scores);
    step(viewer,I_people);
end

%% Detect Faces in the image
% Create a detector object
faceDetector = vision.CascadeObjectDetector('FrontalFaceCART');

% Detect faces
bbox = step(faceDetector, I);

% Draw boxes around detected faces and display results
shapeInserter = vision.ShapeInserter('BorderColor','Custom','CustomBorderColor',[255 255 0]);
I_faces = step(shapeInserter, I, int32(bbox));
imshow(I_faces);

%% Use new object annotation function
I_faces = insertObjectAnnotation(I,'rectangle',bbox,[1:size(bbox,1)]);
imshow(I_faces);

%% Detect Upper Bodies in the image
% Create a detector object
bodyDetector = vision.CascadeObjectDetector('UpperBody');
bodyDetector.MinSize = [60 60];
bodyDetector.ScaleFactor = 1.05;

bbox_body = step(bodyDetector, I);

% Draw bounding boxes
I_body = insertObjectAnnotation(I,'rectangle',bbox_body,1:size(bbox_body,1));
figure, imshow(I_body);

%% Remove false detections by looking for face
bbox_face = zeros(size(bbox_body));
for i=1:length(bbox_body)
    Icrop = imcrop(I,bbox_body(i,:));
    bbox = step(faceDetector,Icrop);
    if ~isempty(bbox)
        bbox_face(i,:) = bbox + [bbox_body(i,1:2)-1 0 0];
    end
end

I_faces2 = insertObjectAnnotation(I,'rectangle',bbox_face,1:size(bbox_face,1));
figure, imshow(I_faces2);

%% Extract one face to use
Icrop = imcrop(I,bbox_body(1,:));
figure;imshow(Icrop);
bbox = step(faceDetector,Icrop);
hold on;rectangle('Position',bbox,'EdgeColor','y');

%% See how much we can rotate the image with the face still being detected
x = 10;
Irotate = imrotate(Icrop,x);
imshow(Irotate);
bbox = step(faceDetector, Irotate);
if bbox > 0
    hold on;rectangle('Position',bbox,'EdgeColor','y'); hold off;
end


%% ASCII Art
%  The ASCII art is an interesting art of representing images using
% characters. In the past a great number of powerful libs and tools are
% develped, the most famous is aalib that allow to paint in real time a
% stream of images (a video) containing only ASCII characters (on a TTY).
% So for example the using the mplayer under Linux it is possible to see a
% DVD film completely in ASCII art, or to play Quake with it's aalib version
% (tty-quake). Here a simple ASCII art converter function is given using
% multiple palettes depending on the gradient of the image, something
% different with respecto to the other AA tools.

%% Reading an image
%  Reading and preparing an image for the conversion: an image must be
% gayscale to be used with gabAscii.

% Getting a standard image:
img = imread('peppers.png');

% Converting to grayscale:
img = rgb2gray(img);

%% Generating the ASCII-Art version of the image
%  Here the ASCII-Art version of the image is generated using the default
% parameters, in this tool the size of the axels and the palettes can be
% defined as parameters.

% Generation of the output:
asciiImg = gabAscii(img),


% Copyright 2018 Marc René Schädler
%
% This file is part of the mobile hearing aid prototype project
% The the mobile hearing aid prototype project is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
%
% The mobile hearing aid prototype project 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 the mobile hearing aid prototype project. If not, see http://www.gnu.org/licenses/.

function thresholds = measure_thresholds(freqs, ear)
  thresholds = nan(size(freqs));
  for i=1:length(freqs)
    threshold = measure_sweep(freqs(i), ear);
    if ~isempty(threshold)
      thresholds(i) = threshold;
    end
  end
end


function computeAllModelChoice_batchVASARI_LGG(pathExperiments,maxOrder,nBoot,imbalance,nBatch,matlabPATH,seed)
% -------------------------------------------------------------------------
% function computeAllModelChoice_batchVASARI_LGG(pathExperiments,maxOrder,nBoot,imbalance,nBatch,matlabPATH,seed)
% -------------------------------------------------------------------------
% DESCRIPTION:
% This function computes feature set selection for all feature set types
% and for all experiments with different degrees of freedom. See ref. [1]
% for more details.
% -------------------------------------------------------------------------
% REFERENCES:
% [1] Vallieres, M. et al. (2015). A radiomics model from joint FDG-PET and
%     MRI texture features for the prediction of lung metastases in soft-tissue
%     sarcomas of the extremities. Physics in Medicine and Biology, 60(14),
%     5471-5496. doi:10.1088/0031-9155/60/14/5471
% -------------------------------------------------------------------------
% INPUTS:
% 1. pathExperiments: Full path to where all experiments need to be
%                     performed.
%                     --> Ex: /myProject/WORKSPACE/VASARI
% 2. maxOrder: Integer specifying the maximal model order to construct.
%              --> Ex: 10
% 3. nBoot: Number of bootstrap samples to use.
%           --> Ex: 100
% 4. imbalance: String specifying the type of imbalance-adjustement strategy
%               employed. Either 'IABR' for imbalance-adjusted bootstrap
%               resampling (see ref.[1]), or 'IALR' for imbalance-adjusted
%               logistic regression (formal reference to come).
%               --> Ex: 'IALR'
% 5. nBatch: Number of parallel batch.
%            --> Ex: 8
% 6. matlabPATH: Full path to the MATLAB executable on the system.
%                --> 'matlab' if a symbolic link to the matlab executable
%                     was previously created.
% 7. seed: Numerical number to use as seed for bootstrapping experiment
%          --> Ex: 54288
% -------------------------------------------------------------------------
% OUTPUTS: Mutivariable models are saved in a folder named 'MODELS' in the
% corresponding folder of 'pathExperiments'.
% -------------------------------------------------------------------------
% AUTHOR(S): Martin Vallieres <mart.vallieres@gmail.com>
% -------------------------------------------------------------------------
% HISTORY:
% - Creation: January 2017
%--------------------------------------------------------------------------
% STATEMENT:
% This file is part of <https://github.com/mvallieres/radiomics/>,
% a package providing MATLAB programming tools for radiomics analysis.
% --> Copyright (C) 2015-2017  Martin Vallieres
%
%    This package is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    This package is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with this package.  If not, see <http://www.gnu.org/licenses/>.
% -------------------------------------------------------------------------

startpath = pwd;

% INITIALIZATON
time = 60; % Number of seconds to wait before checking if parallel computations are done
cd(pathExperiments), load('outcomes')
pathSet = fullfile(pwd,'FSET'); mkdir('MODELS'), cd('MODELS'), pathModels = pwd;
mkdir('batchLog_Models'), cd('batchLog_Models'), pathBatch = pwd;
setNames = {'VASARI'};
[param] = batchExperiments(setNames,outcomes,nBatch); nBatch = length(param);

% PRODUCE BATCH COMPUTATIONS
save('workspace','pathSet','pathModels','outcomes','param','maxOrder','nBoot','imbalance','seed'), pause(5);
for i = 1:nBatch
    nameScript = ['batch',num2str(i),'_script.m'];
    fid = fopen(nameScript,'w');
    fprintf(fid,'load(''workspace'')\n');
    for j = 1:numel(param{i})
        fprintf(fid,['computeAllModelChoice_VASARI_LGG(pathSet,pathModels,outcomes,param{',num2str(i),'}{',num2str(j),'},maxOrder,nBoot,imbalance,seed)\n']);
    end
    fprintf(fid,['system(''touch batch',num2str(i),'_end'');\n']);
    fprintf(fid,'clear all');
    fclose(fid);
    system([matlabPATH,' -nojvm -nodisplay -nodesktop -nosplash < ',nameScript,' >& ',nameScript(1:end-1),'log &']);
end

% WAITING LOOP
waitBatch(pathBatch,time,nBatch)
delete('workspace.mat')

cd(startpath)
end


function y = gpReversibleDynamicsSamp(model, X);

% GPREVERSIBLEDYNAMICSSAMP Sample from the dynamics for a given input.

% FGPLVM

persistent oldX
if isempty(oldX)
  Xp = [X zeros(size(X))];
else
  Xp = [X X-oldX];
end
oldX = X;
[mu, var] = gpPosteriorMeanVar(model, Xp);
y = gsamp(mu, diag(var), 1);
y = X + y;


clear all;
close all;
clc;
spx.cluster.ssc.util.simulate_subspace_preservation(@ssc_omp, 'ssc_omp');
spx.cluster.ssc.util.print_subspace_preservation_results('ssc_omp');


function [niiC, scanFileNameC] = scan2nii(planC,scanNumV,tmpDirPath,reorientFlag)

if ~exist('tmpDirPath','var') || isempty(tmpDirPath) || ~exist(tmpDirPath,'dir')
    tmpDirPath = fullfile(getCERRPath, 'ImageRegistration', 'tmpFiles');
end

if ~exist('reorientFlag','var') || isempty(reorientFlag)
    reorientFlag = 1;
end

for i = 1:numel(scanNumV)
    scanNum = scanNumV(i);
    [scanUniqName, ~] = genScanUniqName(planC,scanNum);
    [affineMat,scan3M_RAS,voxel_size] = getPlanCAffineMat(planC, scanNum, 1);
    qOffset = affineMat(1:3,end)';
    scanFileName = fullfile(tmpDirPath, ['scan_' num2str(scanNumV(i)) '_' scanUniqName '.nii']);
    niiC{i} = vol2nii(scan3M_RAS,affineMat,qOffset,voxel_size,scanFileName);
    scanFileNameC{i} = scanFileName;
end


function gradx = singletrack_gradx_f(in1,in2,in3)
%SINGLETRACK_GRADX_F
%    GRADX = SINGLETRACK_GRADX_F(IN1,IN2,IN3)

%    This function was generated by the Symbolic Math Toolbox version 8.3.
%    09-Jan-2020 11:52:33

I_z = in3(2,:);
M = in3(1,:);
T = in2(2,:);
V_vx = in1(4,:);
V_vy = in1(5,:);
c_f = in3(8,:);
c_r = in3(9,:);
delta = in2(1,:);
deltamax = in3(5,:);
l_f = in3(3,:);
l_r = in3(4,:);
maxbrakeWForce = in3(6,:);
psi_dot = in1(6,:);
vpsi = in1(3,:);
t2 = cos(vpsi);
t3 = sin(vpsi);
t4 = l_f.*psi_dot;
t5 = l_r.*psi_dot;
t6 = V_vx.^2;
t7 = 1.0./I_z;
t8 = 1.0./M;
t9 = T.*5.0e+1;
t10 = delta.*5.0e+1;
t11 = deltamax.*5.0e+1;
t14 = V_vx.*1.0e+2;
t12 = -t5;
t13 = -t9;
t15 = -t10;
t16 = -t11;
t17 = V_vy+t4;
t18 = tanh(t14);
t19 = t17.^2;
t20 = V_vy+t12;
t21 = t13+5.0e+1;
t22 = t18.^2;
t23 = t13-5.0e+1;
t27 = t11+t15;
t31 = t15+t16;
t24 = t20.^2;
t25 = exp(t21);
t26 = exp(t23);
t28 = exp(t27);
t29 = t6+t19;
t32 = exp(t31);
t34 = t22.*1.0e+2;
t30 = t25+1.0;
t33 = t26+1.0;
t35 = t6+t24;
t36 = t28+1.0;
t38 = t32+1.0;
t39 = 1.0./t29;
t44 = t34-1.0e+2;
t37 = 1.0./t30;
t40 = 1.0./t33;
t41 = 1.0./t36;
t42 = 1.0./t35;
t43 = 1.0./t38;
t45 = t37.*5.0e+1;
t46 = t37-1.0;
t47 = t40.*5.0e+1;
t49 = deltamax.*t41;
t50 = t41-1.0;
t52 = V_vx.*c_r.*l_r.*t42;
t53 = t43-1.0;
t48 = -t45;
t51 = -t47;
t54 = deltamax.*t53;
t55 = T.*t40.*t46;
t57 = t9.*t40.*t46;
t58 = delta.*t43.*t50;
t56 = -t55;
t59 = -t58;
t61 = t48+t51+t57+5.0e+1;
t60 = t40+t46+t56;
t62 = exp(t61);
t64 = t49+t54+t59;
t63 = t62+1.0;
t65 = sin(t64);
t67 = cos(t64);
t66 = 1.0./t63;
t69 = V_vx.*c_f.*l_f.*t39.*t67;
t68 = t66-1.0;
gradx = reshape([0.0,0.0,-V_vx.*t3-V_vy.*t2,t2,-t3,0.0,0.0,0.0,0.0,V_vx.*t2-V_vy.*t3,t3,t2,0.0,0.0,0.0,0.0,0.0,0.0,0.0,1.0,0.0,0.0,0.0,0.0,t8.*(-c_f.*t17.*t39.*t65+(maxbrakeWForce.*t44.*t60.*t68)./2.0+(maxbrakeWForce.*t44.*t60.*t67.*t68)./2.0),t8.*(psi_dot+V_vx.*c_f.*t39.*t65),t8.*(V_vy+V_vx.*c_f.*l_f.*t39.*t65),0.0,0.0,0.0,0.0,t8.*(c_r.*t20.*t42+c_f.*t17.*t39.*t67+(maxbrakeWForce.*t44.*t60.*t65.*t68)./2.0),-t8.*(psi_dot+V_vx.*c_r.*t42+V_vx.*c_f.*t39.*t67),-t8.*(V_vy-t52+t69),0.0,0.0,0.0,0.0,t7.*(-c_r.*l_r.*t20.*t42+c_f.*l_f.*t17.*t39.*t67+(l_f.*maxbrakeWForce.*t44.*t60.*t65.*t68)./2.0),t7.*(t52-t69),-t7.*(l_f.*t69+l_r.*t52),0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0],[7,7]);


% this code is revised based on ILSVRC 2013 (http://www.image-net.org/challenges/LSVRC/2013/)

function  zeroShot = zeroShot_top_recall_Phrase(Nre, tuple_confs_cell, tuple_labels_cell, sub_bboxes_cell, obj_bboxes_cell)

load('gt.mat','gt_tuple_label','gt_obj_bboxes','gt_sub_bboxes');
load('zeroShot.mat','zeroShot');

for ii = 1 : 1000
    det = find(zeroShot{ii} == 0);
    gt_tuple_label{ii}(det,:) = [];
    gt_obj_bboxes{ii}(det,:) = [];
    gt_sub_bboxes{ii}(det,:) = [];
end

%num_imgs = length(gt_tuple_label);
num_imgs = 1000;
for i=1:num_imgs
    [tuple_confs_cell{i}, ind] = sort(tuple_confs_cell{i},'descend');
    if length(ind) >= Nre
        tuple_confs_cell{i} = tuple_confs_cell{i}(1:Nre);
        tuple_labels_cell{i} = tuple_labels_cell{i}(ind(1:Nre),:);
        obj_bboxes_cell{i} = obj_bboxes_cell{i}(ind(1:Nre),:);
        sub_bboxes_cell{i} = sub_bboxes_cell{i}(ind(1:Nre),:);
    else
        tuple_labels_cell{i} = tuple_labels_cell{i}(ind,:);
        obj_bboxes_cell{i} = obj_bboxes_cell{i}(ind,:);
        sub_bboxes_cell{i} = sub_bboxes_cell{i}(ind,:);
    end
end

num_pos_tuple = 0;
for ii = 1 : num_imgs
    num_pos_tuple = num_pos_tuple + size(gt_tuple_label{ii},1);
end

tp_cell = cell(1,num_imgs);
fp_cell = cell(1,num_imgs);

gt_thr = 0.5;
% iterate over images
for i=1:num_imgs

    gt_tupLabel = gt_tuple_label{i};
    if ~isempty(gt_obj_bboxes{i})
        gt_box_entity = [min(gt_obj_bboxes{i}(:,1:2),gt_sub_bboxes{i}(:,1:2)),max(gt_obj_bboxes{i}(:,3:4),gt_sub_bboxes{i}(:,3:4))];
    else
        gt_box_entity = [];
    end

    num_gt_tuple = size(gt_tupLabel,1);
    gt_detected = zeros(1,num_gt_tuple);

    labels = tuple_labels_cell{i};
    boxObj = obj_bboxes_cell{i};
    boxSub = sub_bboxes_cell{i};
    if ~isempty(boxObj)
        box_entity_our  = [min(boxObj(:,1:2), boxSub(:,1:2)), max(boxObj(:,3:4), boxSub(:,3:4))];
    else
        box_entity_our  = [];
    end

    num_obj = size(labels,1);
    tp = zeros(1,num_obj);
    fp = zeros(1,num_obj);
    for j=1:num_obj

        bbO = box_entity_our(j,:);
        ovmax = -inf;
        kmax = -1;

        for k=1:num_gt_tuple
            if norm(labels(j,:) - gt_tupLabel(k,:),2) ~= 0
                continue;
            end
            if gt_detected(k) > 0
                continue;
            end

            bbgtO = gt_box_entity(k,:);

            biO=[max(bbO(1),bbgtO(1)) ; max(bbO(2),bbgtO(2)) ; min(bbO(3),bbgtO(3)) ; min(bbO(4),bbgtO(4))];
            iwO=biO(3)-biO(1)+1;
            ihO=biO(4)-biO(2)+1;

            if iwO>0 & ihO>0
                % compute overlap as area of intersection / area of union
                uaO=(bbO(3)-bbO(1)+1)*(bbO(4)-bbO(2)+1)+...
                   (bbgtO(3)-bbgtO(1)+1)*(bbgtO(4)-bbgtO(2)+1)-...
                   iwO*ihO;
                ov =iwO*ihO/uaO;

                % makes sure that this object is detected according
                % to its individual threshold
                if ov >= gt_thr && ov > ovmax
                    ovmax=ov;
                    kmax=k;
                end
            end
        end

        if kmax > 0
            tp(j) = 1;
            gt_detected(kmax) = 1;
        else
            fp(j) = 1;
        end
    end

    % put back into global vector
    tp_cell{i} = tp;
    fp_cell{i} = fp;

end

t = tic;
tp_all = [];
fp_all = [];
confs = [];
for ii = 1 : num_imgs
tp_all = [tp_all; tp_cell{ii}(:) ];
fp_all = [fp_all; fp_cell{ii}(:) ];
confs = [confs; tuple_confs_cell{ii}(:)];
end

[confs, ind] = sort(confs,'descend');
tp_all = tp_all(ind);
fp_all = fp_all(ind);

tp = cumsum(tp_all );
fp = cumsum(fp_all );
recall =(tp/num_pos_tuple);
zeroShot = recall(end);

end


function out=declus_wintec(mCatalog, eMethod)
    % DECLUS_WINTEC execute declustering by windowing technique
    %
    % DECLUS_WINTEC(catalog, nMethod)
    %
    % Incoming variables:
    % mCatalog : EQ catalog in ZMAP format
    % eMethod  : a choice from DeclusterWindowingMethods
    %
    %
    % out : struct of declustered details [output from CALC_DECLUSTER]
    % J. Woessner, woessner@seismo.ifg.ethz.ch
    % updated: 14.08.02
    %
    % see DECLUS_INP, CALC_DECLUSTER

    report_this_filefun();

    %%% Decluster catalog using window technique
    out = struct;
    % [out.mCatDecluster, out.mCatAfter, out.vCluster, out.vCl, out.vMain] = calc_decluster(mCatalog,eMethod);
    [out.declusteredCatalog, out.aftershockCatalog, out.aftershockClusterIdx, out.allClusterIdx, out.mainshockClusterIdx] = calc_decluster(mCatalog,eMethod);
    %vSel = (out.vMain(:,1) > 0); % Selects mainshocks of clusters
    %mCluster = mCatalog.subset(vSel);
    out.description = ["Decluster results have been written to gk_decluster_output with the following fields";...
    	"  declusteredCatalog: Declustered earthquake catalog (with mainshocks, no fore/aftershocks)";...
        "  aftershockCatalog: Catalog of aftershocks (and foreshocks)";...
        "  aftershockClusterIdx : Vector indicating only aftershocks/foreshocks in cluster using a cluster number (background seismicity == 0)";...
        "  allClusterIdx: Vector indicating all events in clusters using a cluster number (background seismicity == 0)";...
        "  mainshockClusterIdx: index into original catalog, indicating mainshocks, where any non-zero value is the cluster number"];

    %%% Plot comparison to window length
    %[vMags, vClusTime, vDist]= plot_cluscomp(vMain, vCluster, mCatalog, eMethod);
    % plot_cluscomp(out.mainshockClusterIdx, out.aftershockClusterIdx, mCatalog, eMethod); % output arguments weren't being used
    return % CGR - Sep 1 2020

    %%% Plot seismicity map, clusters and mainshocks
    replaceMainCatalog(out.mCatDecluster);
    zmap_update_displays();
    plot(mCluster.Longitude, mCluster.Latitude,'m+');

    describe_clusters(mCatalog, out)
    plot_mag_histogram(catalog, mCatAfter)

end

function describe_clusters(mCatalog, out)
    %%% Calculate moment release [local variables], only used for description
    [fMomentCluster, vMomentCluster] = calc_moment(out.mCatAfter);

    [fMomentorg, vMomentorg] = calc_moment(out.mCatalog);

    fMomentpercentage = 100*fMomentCluster/fMomentorg;

    fEventpercentage = 100*out.mCatAfter.Count/mCatalog.Count; % Percentage of events in clusters

    %% Setup message box
    sInfost1 = sprintf(...
        [' The declustering found %d clusters of earthquakes, a total of %d (%g\%) events out of %d. ',...
        ' The map window now displays the declustered catalog containing %d events as blue dots.', ....
        ' The individual clusters are displayed as magenta pluses. The seismic moment released by the clusters'...
        ' is %g Nm which is about %g\% of the total seismic moment (%g Nm) of the catalog.'],...
        max(out.vMain) , out.mCatAfter.Count, fEventpercentage, mCatalog.Count,...
        out.mCatDecluster.Count,...
        fMomentCluster, fMomentpercentage, fMomentorg);

    msgbox(sInfost1,'Declustering Information')
end

function plot_mag_histogram(origCatalog, declusteredCatalog)
    %%% Plotting magnitude histogram
    if exist('hd1_declus_wintec','var') && ishandle(hd1_declus_wintec)
        set(0,'Currentfigure',hd1_declus_wintec);
        disp('Figure exists');
    else
        hd1_win_fig=figure('tag','fig_declus_wintec','Name','Histogram',...
            'Units','normalized','Nextplot','add',...
            'Numbertitle','off','Position',[0.4 0.2 .4 .6],'Menubar','none');
    end

    set(gca,'tag','ax_declus_wintec_mag','Nextplot','replace','box','on','Xticklabel', [0 10 100]);
    axs1=findobj('tag','ax_declus_wintec_mag');
    axes(axs1(1));
    maxMagLimit=max(origCatalog.Magnitude);
    magEdges = 0:0.1:maxMagLimit;
    histogram(declusteredCatalog.Magnitude, magEdges);
    xlabel('Magnitude (events of all clusters)');
end


function vot_wrapper(tracker_name, runfile_name, do_cleanup)

if nargin < 3
    do_cleanup = true;
end

% *************************************************************
% VOT: Always call exit command at the end to terminate Matlab!
% *************************************************************
if do_cleanup
    cleanup = onCleanup(@() exit() );
else
    [pathstr, ~, ~] = fileparts(mfilename('fullpath'));
    cd_ind = strfind(pathstr, filesep());
    pathstr = pathstr(1:cd_ind(end)-1);
    cleanup = onCleanup(@() cd(pathstr));
end

try

% *************************************************************
% VOT: Set random seed to a different value every time.
% *************************************************************
RandStream.setGlobalStream(RandStream('mt19937ar', 'Seed', sum(clock)));

% **********************************
% VOT: Get initialization data
% **********************************
[images, region] = vot_initialize();

results = cell(length(images), 1);

bb_scale = 1;

% If the provided region is a polygon ...
if numel(region) > 4
    % Init with an axis aligned bounding box with correct area and center
    % coordinate
    cx = mean(region(1:2:end));
    cy = mean(region(2:2:end));
    x1 = min(region(1:2:end));
    x2 = max(region(1:2:end));
    y1 = min(region(2:2:end));
    y2 = max(region(2:2:end));
    A1 = norm(region(1:2) - region(3:4)) * norm(region(3:4) - region(5:6));
    A2 = (x2 - x1) * (y2 - y1);
    s = sqrt(A1/A2);
    w = s * (x2 - x1) + 1;
    h = s * (y2 - y1) + 1;
else
    cx = region(1) + (region(3) - 1)/2;
    cy = region(2) + (region(4) - 1)/2;
    w = region(3);
    h = region(4);
end

init_c = [cx cy];
init_sz = bb_scale * [w h];

im_size = size(imread(images{1}));
im_size = im_size([2 1]);

init_pos = min(max(round(init_c - (init_sz - 1)/2), [1 1]), im_size);
init_sz = min(max(round(init_sz), [1 1]), im_size - init_pos + 1);

seq.s_frames = images;
seq.init_rect = [init_pos, init_sz];

[file_path, file_name_start, file_ext] = fileparts(seq.s_frames{1});
[~, file_name_end, ~] = fileparts(seq.s_frames{end});
seq.path = file_path;
seq.name = 'vot_seq';
seq.ext = file_ext(2:end);
seq.len = length(seq.s_frames);
seq.nz = length(file_name_start);
seq.startFrame = str2num(file_name_start);
seq.endFrame = str2num(file_name_end);

% setup_tracker_paths(tracker_name);

otb_res = eval([runfile_name '(seq, [], []);']);
%convert the results to rectangle format
% otb_res = convert_to_rect(otb_res);

num_frames = numel(images);

for frame = 1:num_frames
    bb = otb_res.res(frame,:);
    sz = bb(3:4);
    c = bb(1:2) + (sz - 1)/2;
    new_sz = sz / bb_scale;
    new_tl = c - (new_sz - 1)/2;
    results{frame} = round([new_tl, new_sz]);
end

% **********************************
% VOT: Output the results
% **********************************
vot_quit(results);

catch err
    [wrapper_pathstr, ~, ~] = fileparts(mfilename('fullpath'));
    cd_ind = strfind(wrapper_pathstr, filesep());
    VOT_path = wrapper_pathstr(1:cd_ind(end));

    error_report_path = [VOT_path 'error_reports\'];
    if ~exist(error_report_path, 'dir')
        mkdir(error_report_path);
    end

    report_file_name = [error_report_path tracker_name '_' runfile_name datestr(now,'_yymmdd_HHMM') '.mat'];

    save(report_file_name, 'err')

    rethrow(err);
end


function event = read_micromed_event(trcfile)

% reads the events of the Micromed TRC format files

fid=fopen_or_error(trcfile,'r');

%------------------reading patient & recording info----------
fseek(fid,64,-1);
surname=char(fread(fid,22,'char'))';
name=char(fread(fid,20,'char'))';

fseek(fid,128,-1);
day=fread(fid,1,'char');
if length(num2str(day))<2
    day=['0' num2str(day)];
else
    day=num2str(day);
end
month=fread(fid,1,'char');
switch month
case 1
    month='JAN';
case 2
    month='FEB';
case 3
    month='MAR';
case 4
    month='APR';
case 5
    month='MAY';
case 6
    month='JUN';
case 7
    month='JUL';
case 8
    month='AUG';
case 9
    month='SEP';
case 10
    month='OCT';
case 11
    month='NOV';
case 12
    month='DEC';
end
year=num2str(fread(fid,1,'char')+1900);

%------------------ Reading Header Info ---------

fseek(fid,175,-1);
Header_Type=fread(fid,1,'char');
if Header_Type ~= 4
    ft_error('*.trc file is not Micromed System98 Header type 4')
end

fseek(fid,138,-1);
Data_Start_Offset=fread(fid,1,'uint32');
Num_Chan=fread(fid,1,'uint16');
Multiplexer=fread(fid,1,'uint16');
Rate_Min=fread(fid,1,'uint16');
Bytes=fread(fid,1,'uint16');
fseek(fid,176+8,-1);
Code_Area=fread(fid,1,'uint32');
Code_Area_Length=fread(fid,1,'uint32');
fseek(fid,192+8,-1);
Electrode_Area=fread(fid,1,'uint32');
Electrode_Area_Length=fread(fid,1,'uint32');

fseek(fid,400+8,-1);
Trigger_Area=fread(fid,1,'uint32');
Tigger_Area_Length=fread(fid,1,'uint32');

%----------------- Read Trace Data ----------

fseek(fid,Data_Start_Offset,-1);
switch Bytes
case 1
    trace=fread(fid,'uint8');
case 2
    trace=fread(fid,'uint16');
case 4
    trace=fread(fid,'uint32');
end
m=length(trace);
if rem(m,Num_Chan)~=0
    roundata=floor(m/Num_Chan);
    trace=trace(1:roundata*Num_Chan);
    m=length(trace);
end
trace=reshape(trace,Num_Chan,m/Num_Chan);

%---------------- Reading Trigger Data ----------
fseek(fid,Trigger_Area,-1);
for l=1:Tigger_Area_Length/6
    trigger(1,l)=fread(fid,1,'uint32');
    trigger(2,l)=fread(fid,1,'uint16');
end

first_trigger=trigger(1,1);
m=length(trace);
tl=length(trigger);
NoTrig=0;
for tr=1:tl
    if ((trigger(1,tr) <= m) && (trigger(1,tr) >= first_trigger))
        NoTrig=NoTrig+1;
    end
end
if NoTrig > 0
   	trigger=trigger(:,1:NoTrig);
else
	trigger=[];
	first_trigger=[];
end

fclose(fid);

event = [];

if ~isempty(trigger)
  for E=1:length(trigger)
    event(E).type    = 'MARKER';
    event(E).sample  = trigger(1,E)+1;
    event(E).value   = trigger(2,E);
  end
end