function [ttot, info, infoTransfdiff, imout] = transfdiffreg(transform, im, optReg, optDiff)
% TRANSFDIFFREG Transform diffusion registration of a sequence of images.
%
% TRANSFDIFFREG implements a registration algorithm for a sequence of
% images I = 1, 2, ..., N, such that each image I is aligned to its two
% neighbours I-1 and I+1. This algorithm is based on diffusion of the
% transforms between pairs of images.
%
%     |         |         |         |         |         |         |
%     |  <--->  |  <--->  |  <--->  |  <--->  |  <--->  |  <--->  |
%     |         |         |         |         |         |         |
%
% Note: Currently, only implemented for matched filter rigid registration
% (regmatchedfilt) and elastix B-spline transforms (BSplineTransform).
%
% [TTOT, INFO, INFO_TRANSFDIFF, IMOUT] = TRANSFDIFFREG(TRANSFORM, IM, OPTREG, OPTDIFF)
%
%   TRANSFORM is a string with the type of transform that will be computed
%   by the registration algorithm and diffused (more details below):
%
%     'regmatchedfilt': matched filter (i.e. cross-correlation) rigid
%        transforms.
%
%     'BSplineTransform': B-spline transforms.
%
%   IM is a sequence of 2D images, grayscale or colour. The images can be
%   in one of three formats:
%
%     * A plain array IM(R, C, I, CH), R=row, C=column, I=image index,
%       CH=colour channel.
%
%     * A vector of scimat structs.  See "help scimat" for details. IM(I)
%       has the I-th image.
%
%     * A cell vector. IM{I} has the I-th image.
%
%   OPTREG and OPTDIFF are structs with parameters for the registration and
%   diffusion parts of the algorithm, respectively (see below for details).
%
%   TTOT is a vector of N structs with the transforms (in elastix format)
%   that applied to each image forms an aligned volume.
%
%   INFO is a struct (see below for details).
%
%   INFO_TRANSFDIFF is a vector of structs. INFO_TRANSFDIFF(I) contains the
%   output information from the diffusion step in registration iteration I.
%
%   IMOUT is result of transforming each IM with the corresponding TTOT.
%   Its format (array or scimat struct) is the same as IM's.
%
% =========================================================================
%
% [TTOT, IMOUT, INFO] = TRANSFDIFFREG('regmatchedfilt', IM, OPTREG, OPTDIFF)
%
%   Diffusion of rigid transforms computed by matched filter or
%   cross-correlation registration. Global optimum.
%
%   OPTREG:
%
%     'verbose': (def false) Verbose output.
%
%     'Angle': (def (-45:45)/180*pi) Vector of angle values that the
%         matched filter will try to align adjacent slices.
%
%     'tp': (def {}) User provided intra-slice registrations. tp(I) is the
%           result of registering image I to image I+1. This is by default
%           computed internally, but with this option the user can skip the
%           slow registration process and go directly into diffusion. You
%           can obtain tp from a previous run of the algorithm in output
%           INFO.tp.
%
%     'outdir': (def tempname) If IM are provided as filenames, path to the
%        output directory. For other IM formats, it is ignored.
%
%   OPTDIFF:
%
%     'verbose': (def false) Verbose output.
%
%     'MaxIter': (def 100) Stopping criterion. The algorithm stops after
%                MaxIter diffusion iterations.
%
%     'Alpha':   (def 0.45) Diffusion coefficient. Values >0.45 may not
%                smooth high frequencies very well. Values >=0.5 cause
%                oscillations.
%
%   INFO is a struct with the output information from the top-level
%   registration algorithm.
%
%     'StopCondition': Reason why the algorithm stopped.
%
%     'tp', 'tm': Are vectors with the I -> I+1 and I -> I-1 registration
%                 results from the last registration iteration.
%
% =========================================================================
%
% [TTOT, IMOUT, INFO] = TRANSFDIFFREG('BSplineTransform', IM, OPTREG, OPTDIFF)
%
%   Diffusion of B-spline transforms. Because of B-spline non-linearity,
%   the algorithm needs to run several iterations of registration followed
%   by diffusion.
%
%   This method uses Choi and Lee (2000)'s injectivity conditions to
%   guarantee the diffused B-splines are injective and have no fold-overs.
%   Note that these conditions are conservative, so it may be possible to
%   diffuse a bit more without fold-overs.
%
%   OPTREG:
%
%     'verbose': (def false) Verbose output.
%
%     'SpatialSamplesRatio': (def 1.0) Scalar value in [0.0, 1.0], with the
%                            percentage of pixels sampled by the
%                            registration algorithm.
%
%     'MaxIter': (def 5) Stopping criterion. Maximum number of registration
%                iterations.
%
%     'MaxVal': (def 0) Stopping criterion. After a registration sweep and
%               diffusion, if the update of all B-spline coefficients is <=
%               MaxVal, then no more registration sweeps are computed and
%               the algorithm stops.
%
%     'RegParam': (def {}) A string with the path to a file, or a struct in
%                 elastix format with the registration parameters.
%
%     'mask': (def {}) An array of binary masks with the same dimensions as
%                      the images. Anything outside the binary masks will
%                      be ignored for registration. This can be used to
%                      avoid artifacts and to speed up the algorithm.
%                      Furthermore, note that masks don't need to be very
%                      precise, so performance can be improved a lot if
%                      they are provided as low resolution images (with the
%                      correct metainformation so that they overlap in real
%                      world coordinates).
%
%     't0': (def {}) A list of structs with initial transforms in elastix
%           format. Each t0(I) is applied to image I before registration.
%           This can be used to provide a pre-alignment of the images,
%           typically rigid.
%
%     'tp': See 'regmatchedfilt' above.
%
%     'tm': Like tp, but tm(I) is the result of registering image I to
%           image I-1. Note that for B-splines we cannot compute tm(I) from
%           tp(I).
%
%     'CacheFile': (def '') Name of a .mat file to save/read  internal
%                  variables to and from after each full registration
%                  sweep. This allows to kill the algorithm, and restart it
%                  without having to repeat previous registration sweeps.
%
%   OPTDIFF:
%
%     'verbose': (def false) Verbose output.
%
%     'MaxIter': (def 100) Stopping criterion. Maximum number of diffusion
%                iterations.
%
%     'Epsilon': (def 0.0) Stopping criterion. It stops when all
%                coefficient displacement components are small:
%                for all t, abs(t)<=Epsilon.
%
%     'Alpha':   (def 0.45) Diffusion coefficient. Values >0.45 may not
%                smooth high frequencies very well. Values >=0.5 cause
%                oscillations.
%
%   INFO is a struct with the output information from the top-level
%   registration algorithm.
%
%     'StopCondition': Reason why the algorithm stopped.
%
%     'tp', 'tm': Are vectors with the I -> I+1 and I -> I-1 registration
%                 results from the last registration iteration.
%
%     'MaxVal': MaxVal(I, J) is the maximum absolute value of the
%               coefficients of the I-th transform at registration
%               iteration J.
%
%     'MedVal': MedVal(I, J) is the median absolute value of the
%               coefficients of the I-th transform at registration
%               iteration J.
%
% =========================================================================

% Author: Ramon Casero <rcasero@gmail.com>
% Copyright © 2015-2016 University of Oxford
% Version: 0.7.1
%
% University of Oxford means the Chancellor, Masters and Scholars of
% the University of Oxford, having an administrative office at
% Wellington Square, Oxford OX1 2JD, UK.
%
% This file is part of Gerardus.
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details. The offer of this
% program under the terms of the License is subject to the License
% being interpreted in accordance with English Law and subject to any
% action against the University of Oxford being under the jurisdiction
% of the English Courts.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.

%% Process input arguments

% check number of arguments
switch (transform)
    case {'regmatchedfilt', 'BSplineTransform'}
        narginchk(2, 4);
        nargoutchk(0, 4);
    otherwise
        error('Not implemented for this transform')
end

% defaults
if (nargin < 3 || isempty(optReg))
    optReg = struct;
end
if (nargin < 4 || isempty(optDiff))
    optDiff = struct;
end

% common diffusion parameters
if (~isfield(optReg, 'verbose'))
    optReg.verbose = false;
end

% list of transforms, the options they admit and their default values
optRegList = {
    'regmatchedfilt',   'verbose',             false
    'regmatchedfilt',   'Angle',               (-45:45)/180*pi
    'regmatchedfilt',   'tp',                  {}
    'regmatchedfilt',   'outdir',              tempname
    'BSplineTransform', 'verbose',             false
    'BSplineTransform', 'SpatialSamplesRatio', 1.0
    'BSplineTransform', 'MaxIter',             5
    'BSplineTransform', 'MaxVal',              0
    'BSplineTransform', 'RegParam',            {}
    'BSplineTransform', 'mask',                {}
    'BSplineTransform', 't0',                  {}
    'BSplineTransform', 'tp',                  {}
    'BSplineTransform', 'tm',                  {}
    'BSplineTransform', 'outdir',              tempname
    'BSplineTransform', 'CacheFile',           ''
    };
optDiffList = {
    'regmatchedfilt',   'Alpha',               0.45
    'regmatchedfilt',   'MaxIter',             100
    'BSplineTransform', 'Alpha',               0.45
    'BSplineTransform', 'MaxIter',             100
    'BSplineTransform', 'Epsilon',             0.0
    };
optReg = check_user_options(optRegList, transform, optReg);
optDiff = check_user_options(optDiffList, transform, optDiff);

% number of input images
if (iscell(im) || isstruct(im)) % filenames or scimats
    N = length(im);
elseif (ischar(im))
    error('IM cannot be a single filename')
else % array
    N = size(im, 3);
end

% transform-specific checks
switch (transform)

    case 'regmatchedfilt'

        % with affine transforms, registrations between images need to be
        % computed only once
        optReg.MaxIter = 1;

        % transform any input image into scimat format
        [im, imOutType] = imToScimat(im);

    case 'BSplineTransform'

        if (optReg.SpatialSamplesRatio < 0.0 ...
                || optReg.SpatialSamplesRatio > 1.0)
            error('optReg.SpatialSamplesRatio must be in [0, 1]')
        end

        % if registration parameters provided as file name, read the file
        % (we may need to change some of the parameters, e.g.
        % SpatialSamplesRatio, and it also makes it easier to check the
        % values of the parameters)
        if (isempty(optReg.RegParam))
            error('No registration parameters provided in optReg.RegParam')
        elseif (ischar(optReg.RegParam))
            optReg.RegParam = elastix_read_file2param(optReg.RegParam);
        end

        % hard-coded options for registration between two images
        optElastix.verbose = false;

        % if images are in memory instead of as files, we have to
        % create temp files for them
        [im, deleteImFiles, imOutType] = imToFiles(im);
        N = length(im);

        % if the user has provided masks, and they are in memory, they need to be
        % saved as temp files
        [optReg.mask, deleteMaskFiles, maskType, nVoxMask] = imToFiles(optReg.mask);
        Nmask = length(optReg.mask);
        if (Nmask > 0 && N ~= Nmask)
            error('There must be either no masks or the same number of masks (OPTREG.mask) as images (IM)')
        end

end

%% Main algorithm

% init number of registration iterations
RegIter = 0; % number of registration iterations

switch (transform)

    case 'BSplineTransform'

    % initialize total transforms applied to each image as empty
    if (isempty(optReg.t0))
        ttot = cell(1, N);
    else
        ttot = optReg.t0;
    end

    % if the user is running in cached mode, we set the internal state of
    % the algorithm to the latest full registration sweep
    if (exist(optReg.CacheFile, 'file') == 2)

        if (optReg.verbose)
            disp('***************************************************')
            disp('** READING CACHED INFO ****************************')
            disp('***************************************************')
        end

        % load intermediate results, except for optReg, im because the temp
        % files will have different random names
        load(optReg.CacheFile, ...
            'ttot', 'info', 'infoTransfdiff', 'transform', 'optDiff', ...
            'optRegList', 'optDiffList', ...
            'N', 'imOutType', 'optElastix', 'deleteImFiles', ...
            'deleteMaskFiles', 'nVoxMask', ...
            'maskType', 'Nmask', 'RegIter', 'J', 'imoutaux', ...
            'iterinfo', 'tp', 'tpMat',  't', 'tref')

    end

end

% loop registration steps
while (true)

    if (optReg.verbose)
        disp('***************************************************')
        disp('** REGISTRATION BLOCK *****************************')
        disp('***************************************************')
    end

    % increase iteration counter
    RegIter = RegIter + 1;
    if (optReg.verbose)
        disp(['** IterReg = ' num2str(RegIter) ...
            '/' num2str(optReg.MaxIter) ...
            '                                 **'])
    end

    %% registration block
    if (optReg.verbose)
        disp('** Register image i to image i+1                 **')
    end

    switch (transform)

        case 'regmatchedfilt'

            % register image to next neighbour
            if (isempty(optReg.tp))

                % register each image i to image i+1
                % images loop
                parfor J = 1:N-1

                    if (optReg.verbose)
                        fprintf('** ... %d/%d', J, N-1)
                        tic
                    end

                    % register each image J to its next neighbour J+1
                    aux(J) = regmatchedfilt(im(J+1), im(J), ...
                        optReg.Angle);

                    if (optReg.verbose)
                        fprintf(' (%0.2f sec)\n', toc)
                    end

                end % images loop
                info.tp = aux;

            % if the user has provided tp already, we are going to skip the
            % registration step. This allows for fast testing of diffusion
            % without having to recompute the registration again and again
            else % if (isempty(optReg.tp))

                if (optReg.verbose)
                    disp('** TP provided by user, skipping registration block')
                    info.tp = optReg.tp;
                end

            end % if (isempty(optReg.tp))

        case 'BSplineTransform'

            % register each image i to image i+1
            % images loop
            for J = 1:N-1

                if (optReg.verbose)
                    fprintf('** ... %d/%d', J, N-1)
                end

                % in case we only want to use a subset of the voxels within
                % the mask. We use the fixed image to set the number of
                % samped voxels for both images
                if (strcmp(optReg.RegParam.ImageSampler, 'RandomCoordinate'))

                    % number of samples we are going to take for
                    % registration
                    numSamples = round(nVoxMask(J) ...
                        * optReg.SpatialSamplesRatio);

                    % make sure that the number of pixels is not too low,
                    % or that we are taking more than what's in the mask
                    numSamples = max([numSamples 2000]);
                    numSamples = min([numSamples nVoxMask(J)]);

                    optReg.RegParam.NumberOfSpatialSamples = numSamples;
                end

                % if the user has provided tp already, we are going to skip
                % the registration step. This allows for fast testing of
                % diffusion without having to recompute the registration
                % again and again
                if (isempty(optReg.tp))

                    % register each image J to its next neighbour J+1
                    tic
                    [info.tp(J), imoutaux, iterinfo] = register_2_images(...
                        optReg.RegParam, ...         % registration param
                        im{J+1}, ...                 % fixed
                        im{J}, ...                   % moving
                        ttot(J+1), ...               % fixedT0
                        ttot(J), ...                 % movingT0
                        optReg.mask{J+1}, ...        % fixedMask
                        optReg.mask{J}, ...          % movingMask
                        optElastix);                 % elastix options

                    % DEBUG
                    if (optReg.verbose)
                        fprintf(' (tp %0.2f sec)', toc)
                    end

                else

                    info.tp(J) = optReg.tp(J);
                    fprintf(' (tp provided)')

                end

                % same as above but for tm
                if (isempty(optReg.tm))

                    % register each image J to its previous neighbour J-1
                    tic
                    [info.tm(J), imoutaux, iterinfo] = register_2_images(...
                        optReg.RegParam, ...         % registration param
                        im{J}, ...                   % fixed
                        im{J+1}, ...                 % moving
                        ttot(J), ...                 % fixedT0
                        ttot(J+1), ...               % movingT0
                        optReg.mask{J}, ...          % fixedMask
                        optReg.mask{J+1}, ...        % movingMask
                        optElastix);                 % elastix options

                    % DEBUG
                    if (optReg.verbose)
                        fprintf(' (tm %0.2f sec)\n', toc)
                    end

                else

                    info.tm(J) = optReg.tm(J);
                    fprintf(' (tm provided)\n')

                end

            end % images loop

            % average transforms so that the transform J->J+1 is the
            % inverse of J+1->J. This is important because otherwise
            % diffusion doesn't converge
            %
            % we only need to correct tp, because tm will be easily
            % computed as -tp inside of transfdiff()
            for J = 1:N-1

                tp(J) = elastix_colon(info.tp(J), 1);
                tm(J) = elastix_colon(info.tm(J), 1);

                tp(J).TransformParameters ...
                    = (tp(J).TransformParameters ...
                    - tm(J).TransformParameters) * .5;

            end
            clear tm

            % if the user provided tp and tm at the input, they were valid
            % only for the first registration iteration, and need to be
            % removed for subsequent iterations
            if (~isempty(optReg.tp))
                optReg.tp = [];
            end
            if (~isempty(optReg.tm))
                optReg.tm = [];
            end

    end % switch transform

    %% diffusion block

    if (optReg.verbose)
        disp('***************************************************')
        disp('** DIFFUSION BLOCK ********************************')
        disp('***************************************************')
    end

    switch (transform)

        case 'regmatchedfilt'

            % convert elastix parameter vectors to affine matrices
            tpMat = elastix_affine_struct2matrix(info.tp);

            % transform diffusion of affine parameters
            optDiff.Transform = 'EulerTransform';
            [t, infoTransfdiff] = transfdiff(optDiff, tpMat);

            % transfer diffused parameters to elastix structs
            ttot = elastix_affine_matrix2struct(t, info.tp(1));

        case 'BSplineTransform'

            % extract transform vector from each TP and concatenate into a
            % matrix
            tpMat = cat(1, tp(:).TransformParameters);

            % transform diffusion of B-spline parameters
            optDiff.Transform = transform; % transform = 'BSplineTransform'
            optDiff.tbsp = tp(1); % used to validate B-spline injectivity
            [t, infoTransfdiff(RegIter)] = transfdiff(optDiff, tpMat);

            % template for the current B-spline transforms to apply to each
            % image
            tref(1:N) = tp(1);

            % transfer diffused parameters to elastix structs
            for J = 1:N
                tref(J).TransformParameters = t(J, :);
            end

            % add the new level of B-splines to the series of transforms to
            % apply to each image
            ttot = elastix_cat(tref, ttot);

            % stopping criterion: all control point translations are
            % very small
            info.MaxVal(:, RegIter) = max(abs(t), [], 2);
            info.MedVal(:, RegIter) = median(abs(t), 2);

            % save intermediate results to cache file
            if (~isempty(optReg.CacheFile))
                save(optReg.CacheFile)
            end

            if (optReg.verbose)
                disp(['** max(info.MaxVal) = ' num2str(max(info.MaxVal(:, RegIter)))])
            end
            if (max(info.MaxVal(:, RegIter)) <= optReg.MaxVal)
                info.StopCondition = 'MaxVal';
                break
            end

    end

    % stop criterion
    if (RegIter == optReg.MaxIter)
        info.StopCondition = 'MaxIter';
        break
    end

end % end registration step loop

% apply transform results to the input images
if (nargout > 3)

    if (optReg.verbose)
        disp('***************************************************')
        disp('** OUTPUT IMAGE TRANSFORM BLOCK *******************')
        disp('***************************************************')
    end

    % create output directory if it doesn't exist already and the output is
    % going to be filenames
    if (iscell(im) && ~exist(optReg.outdir))
        mkdir(optReg.outdir);
    end

    % if the output is not filenames, then we will delete the files, as the
    % output will be provided in memory
    deleteImOutFiles = ~strcmp(imOutType, 'char') ...
        && ~strcmp(imOutType, 'filename');

    for I = 1:N

        if (optReg.verbose)
            fprintf('** ... %d/%d\n', I, N)
            tic
        end

        % if the output is going to be given as filenames, then we want
        % control over where those files are saved to
        if (strcmp(imOutType, 'filename') || strcmp(imOutType, 'char'))

            % filename we are going to provide to the user
            [~, filename, ext] = fileparts(im{I});
            optTransformix.outfile = [optReg.outdir filesep filename ext];

        else

            optTransformix.outfile = '';

        end

        % at this point, "im" can be:
        % * vector of scimat (regmatchedfilt)
        % * cell vector of filenames (BSplineTransform)
        if (isstruct(im))

            % transform input image
            aux = transformix(ttot(I), im(I));

        elseif (iscell(im))

            % transform input image
            aux = transformix(ttot(I), im{I}, optTransformix);

        else

            error('Internal check fail: IM should not have this type')

        end

        % make the output image match the format of the input image
        if (isstruct(im) && strcmp(imOutType, 'char'))

            error('TODO: Not implemented')

        elseif (iscell(im) && strcmp(imOutType, 'char'))

            imout = aux;

        elseif (isstruct(im) && strcmp(imOutType, 'filename'))

            imout{I} = optTransformix.outfile;
            scimat_save(imout{I}, aux);

        elseif (iscell(im) && strcmp(imOutType, 'filename'))

            imout{I} = aux;

        elseif (isstruct(im) && strcmp(imOutType, 'scimat'))

            imout(I) = aux;

        elseif (iscell(im) && strcmp(imOutType, 'scimat'))

            imout(I) = scimat_load(aux);
            delete(aux);

        elseif (isstruct(im) && strcmp(imOutType, 'array'))

            imout(:, :, I, :) = aux.data;

        elseif (iscell(im) && strcmp(imOutType, 'array'))

            error('TODO: Not implemented')

        end

    end

    % delete output files if the output is provided in memory, not files
    if (exist(optReg.outdir) && deleteImOutFiles)
        rmdir(optReg.outdir);
    end

end

% delete temp files
switch (transform)

    case 'BSplineTransform'

        % if temp files had to be created to pass images to elastix, we can
        % delete them now
        if (deleteImFiles)
            for I = 1:N
                delete(im{I})
            end
        end
        if (deleteMaskFiles && ~isempty(optReg.mask))
            for I = 1:N
                delete(optReg.mask{I})
            end
        end

end

end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% nested functions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% check_user_options
%
% check invalid options and set defaults
function opt = check_user_options(optList, transform, opt)

% list of options allowed for this transform
idx = cellfun(@(s) strcmp(s, transform), optList(:, 1));
optList = optList(idx, :);

% list of options in the user input
names = fieldnames(opt);

% check whether any option provided by the user is not used for this
% transform
for I = 1:length(names)

    if (~ismember(names{I}, optList(:, 2)))
        warning([transform ' does not use option ' inputname(3) ...
            '.' names{I}])
    end

end

% set options not provided by the user to their defaults
isProvided = ismember(optList(:, 2), names);
for I = find(~isProvided)'
    opt = setfield(opt, optList{I, 2}, optList{I, 3});
end

end

%% imToScimat()
%
% convert any input image format (filenames, arrays or scimat) to scimat
function [im, imType] = imToScimat(im)

% check the type and number of the input images
if (isempty(im)) % empty input

    % nothing to do
    return

elseif (ischar(im)) % single filename

    imType = 'char';
    im = scimat_load(im);

elseif (iscell(im)) % list of filenames

    imType = 'filename';
    for I = 1:length(im)
        aux(I) = scimat_load(im{I});
    end
    im = aux;

elseif (isstruct(im))

    imType = 'scimat';

else % plain array

    imType = 'array';

    for I = 1:size(im, 3)
        aux(I) = scimat_im2scimat(im(:, :, I, :));
    end
    im = aux;

end

end

%% imToFiles()
%
% convert any input format to filenames, creating temp files if necessary
%
% * Inputs
%
%    im: input images (filenames, scimats or arrays)
%
% * Outputs:
%
%    im: cell vector with paths and filenames to images
%    deleteFiles: whether we need to delete the image files when we finish
%                 registering them (i.e. whether they are temp files)
%    nVox: number of voxels ~= 0 (only useful for binary masks)
function [im, deleteFiles, imType, nVox] = imToFiles(im)

% check the type and number of the input images
if (isempty(im)) % empty input (typically, no masks provided)

    imType = '';
    deleteFiles = false;
    nVox = 0;
    return

elseif (ischar(im)) % single filename

    imType = 'char';
    deleteFiles = false;
    im = {im};

    % get metainformation
    if (nargout > 3) % user wants to know number of voxels ~= 0
        scimat0 = scimat_load(im{1});
        nVox = nnz(scimat0.data);
    end

elseif (iscell(im)) % list of filenames

    imType = 'filename';
    deleteFiles = false;
    N = length(im);

    % get metainformation (we assume that all slices have the same)
    if (nargout > 3) % user wants to know number of voxels ~= 0
        for I = 1:N
            scimat0 = scimat_load(im{I});
            nVox(I) = nnz(scimat0.data);
        end
    end

elseif (isstruct(im)) % list of scimat structs

    imType = 'scimat';

    % number of images
    N = length(im);

    % if user wants to know number of voxels ~= 0
    if (nargout > 3)
        for I = 1:N
            nVox(I) = nnz(im(I).data);
        end
    end

    % create a temp file for each input image
    deleteFiles = true;
    filename = cell(1, N);
    for I = 1:N
        filename{I} = [tempname '.mha'];
        scimat_save(filename{I}, im(I));
    end
    im = filename;

else % plain array

    imType = 'array';

    % number of images
    N = size(im, 3);

    % if user wants to know number of voxels ~= 0
    if (nargout > 3)
        if (size(im, 4) ~= 1)
            warning('User wants to count voxels~=0, so we assume binary mask, but it has more than one channel')
        end
        for I = 1:N
            nVox(I) = nnz(im(:, :, I, :));
        end
    end

    % create a temp file for each input image
    deleteFiles = true;
    filename = cell(1, N);
    for I = 1:N
        filename{I} = [tempname '.mha'];
        scimat_save(filename{I}, scimat_im2scimat(im(:, :, I, :)));
    end
    im = filename;

end

end

%% register_2_images: local function to register two images
function [t, imout, iterinfo] = register_2_images(regParam, fixed, moving, fixedT0, movingT0, fixedMask, movingMask, optElastix)
tic
if (iscell(fixedT0)) % no initial transform

    % the only way to pass empty initial transorms is as a cell with an
    % empty, but if we pass them to elastix, we need to remove the cell
    fixedT0 = fixedT0{1};
    optElastix.fMask = fixedMask;

else % initial transform

    % apply initial transform
    fixed = transformix(fixedT0, fixed);
    optElastix.fMask = transformix(fixedT0, fixedMask);

end
if (iscell(movingT0)) % no initial transform

    % the only way to pass empty initial transorms is as a cell with an
    % empty, but if we pass them to elastix, we need to remove the cell
    optElastix.mMask = movingMask;

else % initial transform

    % we apply the initial transform explictly to the moving image
    % and mask, because it's substantially faster than providing a list of
    % transforms in optElastix.t0
    optElastix.t0 = '';
    optElastix.mMask = transformix(movingT0, movingMask);
    moving = transformix(movingT0, moving);

end

% register both images
[t, imout, iterinfo] ...
    = elastix(regParam, fixed, moving, optElastix);

% concatenate new level of B-splines and previous initial transforms.
% Note: because of elastix internals, if we apply t0 explicitly before
% registration, then t has to be concatenated BEFORE t0, not after
t = elastix_cat(t, movingT0);

end

%% dbg_plot_reg_results
% debug function to plot the registration result
function dbg_plot_reg_results(imf, imm, fT0, mT0, mT)

% plot input images
subplot(2, 1, 1)
imagesc(imfuse(imf, imm))

% transform fixed image with its initial transform
imf = transformix(fT0, imf);

% apply the registration output transform to the moving image
immreg = transformix(mT, imm);

% transform moving image with its initial transform
imm = transformix(mT0, imm);

% plot registration results
subplot(2, 1, 2)
imagesc(imfuse(imf, immreg))

end


function im = crop_qim(im, bbx, imgsz)
% CROP_QIM crops query image with defined bounding box.
%
%   IM = crop_qim(IM, BBX)  Crop IM with BBX.
%
%   IM = crop_qim(IM, BBX, IMG_SIZE) Resize image so that longer edge is IMG_SIZE.
%     After that crop IM with BBX.

	allow_increase = 0;

	if size(im, 1) == 1, im = imread(im); end
	if ~exist('imgsz'),
		im = im(bbx(2):min(bbx(4),size(im,1)), bbx(1):min(bbx(3),size(im,2)), :);
		return;
	end

	bbx = uint32(max(imgsz * (bbx + 1) / max(size(im,1), size(im,2)), 1));
	im = imresizemaxd(im, imgsz, allow_increase);
	im = im(bbx(2):min(bbx(4),size(im,1)), bbx(1):min(bbx(3),size(im,2)), :);


function [dat, ref] = ft_preproc_rereference(dat, refchan, method, handlenan)

% FT_PREPROC_REREFERENCE computes the average reference over all EEG channels
% or rereferences the data to the selected channels
%
% Use as
%   [dat] = ft_preproc_rereference(dat, refchan, method, handlenan)
% where
%   dat        data matrix (Nchans X Ntime)
%   refchan    vector with indices of the new reference channels, or 'all'
%   method     string, can be 'avg' or 'median'
%   handlenan  boolean, can be true or false
%
% If the new reference channel is not specified, the data will be
% rereferenced to the average of all channels.
%
% See also PREPROC

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

% determine the size of the data
[Nchans, Nsamples] = size(dat);

% determine the new reference channels
if nargin<2 || isempty(refchan) || (ischar(refchan) && strcmp(refchan, 'all'))
  refchan = 1:Nchans;
end

if nargin<3 || isempty(method)
  method = 'avg';
end

if nargin<4 || isempty(handlenan)
  handlenan = false;
end

hasnan = any(any(isnan(dat(refchan,:))));

if hasnan && handlenan
  % preprocessing works differently if channels contain NaN
  switch method
    case 'avg'
      ref = nanmean(dat(refchan,:), 1);
    case 'median'
      ref = nanmedian(dat(refchan,:), 1);
    otherwise
      ft_error('unsupported method')
  end % switch
else
  % preprocessing fails on channels that contain NaN
  if any(isnan(dat(:)))
    ft_warning('FieldTrip:dataContainsNaN', 'data contains NaN values');
  end
  % compute the average value over the reference channels
  switch method
    case 'avg'
      ref = mean(dat(refchan,:), 1);
    case 'median'
      ref = median(dat(refchan,:), 1);
    otherwise
      ft_error('unsupported method')
  end % switch
end

% subtract the new reference from the data
for chan=1:Nchans
  dat(chan,:) = dat(chan,:) - ref;
end


function savenirfast(v,f,filestub, nodeseg, nodeprop, proptype)
%
% savenirfast(nirfaststruct,filestub)
%    or
% savenirfast(v,f,filestub, nodeseg, proptype, proptype)
%
% save a tetrahedral or surface mesh and associated properties to NIRFAST format
%
% author: Qianqian Fang, <q.fang at neu.edu>
%
% input:
%      nirfaststruct: a structure storing the NIRFAST mesh data, type
%         'help readnirfast' to read more; alternatively one can use:
%      v: input, node list, the first 3 columns are the x/y/z positions,
%         the remaining columns are combined with nodeprop as node-based
%         (optical) parameters
%      f: input, tetrahedral or surface element list, dimension (ne,3)
%      filestub: output file stub, output will include multiple files
%          filestub.node: node file
%          filestub.elem: element file to store the surface or tet mesh
%          filestub.param: parameter file
%          filestub.region: node label file
%      nodeseg: optional, an integer label field to group nodes into
%         segmentations, same length as v, number starting from 0; or empty
%      nodeprop: optional, additional nodal parameters, typically defined
%         as mua (1/mm), musp (1/mm) and refractive index (n)l; row number
%         equals to that of v, column number is user-defined
%      proptype: optional, the type of the node-property. by default it is
%         'stnd' - for standard properties; one can also define multi-row
%         header using a cell-array.
%
% example:
%    [node,face,elem]=meshabox([0 0 0],[10 10 10],0.3,1);
%    savenirfast(node,elem,'test', [], ones(size(node)), 'user');
%    mymesh=readnirfast('test')
%    plotmesh([mymesh.nodes mymesh.bndvtx], mymesh.elements,'x>5')
%
% -- this function is part of iso2mesh toolbox (http://iso2mesh.sf.net)
%

if(nargin<2)
    error('you must provide at least 2 inputs');
end

if(nargin==2)
    filestub=f;
    node=v.nodes;
    f=v.elements;
    proptype=v.type;
    if(isfield(v,'region'))
        nodeseg=v.region;
    end
    if(isfield(v,'mua'))
        nodeprop=[v.mua v.mus v.ri];
    end
    if(isfield(v,'bndvtx'))
        isboundary=v.bndvtx;
    end
    v=node;
end

if(size(v,2)>3)
    if(nargin>4)
        nodeprop=[v(:,4:end) nodeprop];
    else
        nodeprop=v(:,4:end);
    end
else
    if(nargin<5)
        nodeprop=[];
    end
end

if(nargin<6)
    proptype='stnd';
end

if(nargin<4 || isempty(nodeseg))
    nodeseg=zeros(size(v,1),1);
end

if(nargin<6)
    proptype='stnd';
end

if(size(f,2)>4)
    f(:,5:end)=[];
end

if(size(v,2)<3)
    error('v must contain at least 3 columns, and f must have at least 4 columns');
end

if(~exist('isboundary','var'))
    face=surfedge(f);
    isboundary=ismember(1:size(v,1), face(:));
end

fid=fopen([filestub,'.node'],'wt');
if(fid==-1)
    error('Saving node file failed, check permission or disk space.');
end
fprintf(fid,'%d\t%.16f\t%.16f\t%.16f\n',[isboundary(:) v(:,1:3)]');
fclose(fid);

if(size(f,2)<2 || size(f,2)>4)
    error('element list f must contain 3 or 4 columns');
end

fid=fopen([filestub,'.elem'],'wt');
if(fid==-1)
    error('Saving elem file failed, check permission or disk space.');
end
fprintf(fid,'%6d\t%6d\t%6d\t%6d\t\n',f');
fclose(fid);

if(~isempty(nodeseg))
    if(numel(nodeseg)~=size(v,1))
        error('nodeseg must have the same length as v');
    end
    fid=fopen([filestub,'.region'],'wt');
    if(fid==-1)
        error('Saving regin file failed, check permission or disk space.');
    end
    fprintf(fid,'%d\n',nodeseg(:));
    fclose(fid);
end

if(~isempty(nodeprop))
    if(size(nodeprop,1)~=size(v,1))
        error('nodeprop must have the same row number as v');
    end
    fid=fopen([filestub,'.param'],'wt');
    if(fid==-1)
        error('Saving param file failed, check permission or disk space.');
    end
    if(iscell(proptype))
        proptype=strjoin(proptype,'\n');
    end
    fprintf(fid,[proptype '\n']);
    fprintf(fid,[repmat('%.16f\t', 1, size(nodeprop,2)) '\n'],nodeprop');
    fclose(fid);
end


function [params, names] = dexpKernExtractParam(kern)

% DEXPKERNEXTRACTPARAM Extract parameters from the double exponential's
% kernel structure.
%
% FORMAT
% DESC extracts parameters from the double exponential kernel's structure
% into a vector of parameters for optimisation.
% ARG kern : the kernel structure containing the parameters to be
% extracted.
% RETURN params : vector of parameters extracted from the kernel. If
% the field 'transforms' is not empty in the kernel structure, the
% parameters will be transformed before optimisation (for example
% positive only parameters could be logged before being returned).
%
% FORMAT
% DESC extracts parameters and parameter names from the double exponential
% kernel structure.
% ARG kern : the kernel structure containing the parameters to be
% extracted.
% RETURN param : vector of parameters extracted from the kernel. If
% the field 'transforms' is not empty in the kernel structure, the
% parameters will be transformed before optimisation (for example
% positive only parameters could be logged before being returned).
% RETURN names : cell array of strings containing names for each
% parameter.
%
% SEEALSO dexpKernParamInit, dexpKernExpandParam, kernExtractParam, scg, conjgrad
%
% COPYRIGHT : David Luengo, 2009

% KERN

params = [kern.decay kern.variance];
if nargout > 1
  names={'decay', 'variance'};
end


## Copyright (C) 2013 CarnĂŤ Draug <carandraug@octave.org>
##
## This program is free software; you can redistribute it and/or modify it under
## the terms of the GNU General Public License as published by the Free Software
## Foundation; either version 3 of the License, or (at your option) any later
## version.
##
## This program is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
## details.
##
## You should have received a copy of the GNU General Public License along with
## this program; if not, see <http://www.gnu.org/licenses/>.

## -*- texinfo -*-
## @deftypefn  {Function File} {@var{cmap} =} ycbcr2rgb (@var{YCbCrmap})
## @deftypefnx {Function File} {@var{RGB} =} ycbcr2rgb (@var{YCbCr})
## @deftypefnx {Function File} {@dots{} =} ycbcr2rgb (@dots{}, [@var{Kb} @var{Kr}])
## @deftypefnx {Function File} {@dots{} =} ycbcr2rgb (@dots{}, @var{standard})
## Convert YCbCr color space to RGB.
##
## The convertion changes the image @var{YCbCr} or colormap @var{YCbCrmap},
## from the YCbCr (luminance, chrominance blue, and chrominance red)
## color space to RGB values.  @var{YCbCr} must be of class double, single,
## uint8, or uint16.
##
## The formula used for the conversion is dependent on two constants, @var{Kb}
## and @var{Kr} which can be specified individually, or according to existing
## standards:
##
## @table @asis
## @item "601" (default)
## According to the ITU-R BT.601 (formerly CCIR 601) standard.  Its values
## of @var{Kb} and @var{Kr} are 0.114 and 0.299 respectively.
## @item "709" (default)
## According to the ITU-R BT.709 standard.  Its values of @var{Kb} and
## @var{Kr} are 0.0722 and 0.2116 respectively.
## @end table
##
## @seealso{hsv2rgb, ntsc2rgb, rgb2hsv, rgb2ntsc, rgb2ycbcr}
## @end deftypefn

function rgb = ycbcr2rgb (ycbcr, standard = "601")
  if (nargin < 1 || nargin > 2)
    print_usage ();
  endif
  rgb = ycbcrfunc ("ycbcr2rgb", ycbcr, standard);
endfunction

%!assert (ycbcr2rgb (rgb2ycbcr (jet (10))), jet (10), 0.00001);

## Copyright (C) 2013 CarnĂŤ Draug <carandraug+dev@gmail.com>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 3 of the License, or
## (at your option) any later version.
##
## This program is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, see <http://www.gnu.org/licenses/>.

## Private function for ycbcr2rgb and rgb2ycbcr functions which are
## very similar

function out = ycbcrfunc (func, in, standard)

  img = false; # was input an image?
  if (iscolormap (in))
    ## do nothing, it's a colormap
  elseif (isrgb (in))
    img = true;
    ## we shape it as a colormap (2D matrix) so we can use matrix multiplcation
    nRows = rows (in);
    nCols = columns (in);
    in    = reshape (in, [nRows*nCols 3]);
  else
    error ("%s: input must be a colormap (Nx3) or RGB image (NxMx3)", func);
  endif

  if (ischar (standard))
    if (strcmpi (standard, "601")) # for ITU-R BT.601
      Kb = 0.114;
      Kr = 0.299;
    elseif (strcmpi (standard, "709")) # for ITU-R BT.709
      Kb = 0.0722;
      Kr = 0.2126;
    else
      error ("%s: unknown standard `%s'", func, standard);
    endif
  elseif (isnumeric (standard) && numel (standard) == 2)
    Kb = standard(1);
    Kr = standard(2);
  else
    error ("%s: must specify a standard (string), or Kb and Kr values", func);
  endif

  ## the color matrix for the conversion. Derived from:
  ##    Y  = Kr*R + (1-Kr-Kb)*G + kb*B
  ##    Cb = (1/2) * ((B-Y)/(1-Kb))
  ##    Cr = (1/2) * ((R-Y)/(1-Kr))
  ## It expects RGB values in the range [0 1], and returns Y in the
  ## range [0 1], and Cb and Cr in the range [-0.5 0.5]
  cmat = [  Kr            (1-Kr-Kb)            Kb
          -(Kr/(2-2*Kb)) -(1-Kr-Kb)/(2-2*Kb)   0.5
            0.5          -(1-Kr-Kb)/(2-2*Kr) -(Kb/(2-2*Kr)) ];

  cls = class (in);
  in  = im2double (in);

  ## note that these blocks are the inverse of one another. Changes
  ## in one will most likely require a change on the other
  if (strcmp (func, "rgb2ycbcr"))
    ## convert to YCbCr colorspace
    out = (cmat * in')'; # transpose at the end to get back colormap shape
    ## rescale Cb and Cr to range [0 1]
    out(:, [2 3]) += 0.5;
    ## footroom and headroom will take from the range 16/255 each for Cb and Cr,
    ## and 16/255 and 20/255 for Y. So we have to compress the values of the
    ## space, and then shift forward
    out(:,1)     = (out(:,1) * 219/255) + 16/255;
    out(:,[2 3]) = (out(:,[2 3]) * 223/255) + 16/255;

  elseif (strcmp (func, "ycbcr2rgb"))
    ## just the inverse of the rgb2ycbcr conversion
    in(:,[2 3])  = (in(:,[2 3]) - 16/255) / (223/255);
    in(:,1)      = (in(:,1) - 16/255) / (219/255);
    in(:,[2 3]) -= 0.5;
    out          = (inv (cmat) * in')';
  else
    error ("internal error for YCbCr conversion. Unknown function %s", func);
  endif

  switch (cls)
    case {"single", "double"}
      ## do nothing. All is good
    case "uint8"
      out = im2uint8 (out);
    case "uint16"
      out = im2uint16 (out);
    otherwise
      error ("%s: unsupported image class %s", func, cls);
  endswitch

  if (img)
    ## put the image back together
    out = reshape (out, [nRows nCols 3]);
  endif

endfunction


function drawArrowhead(point,dir,rad,color)
%drawArrowhead Adds arrowhead to curve
%
% drawArrowhead(point,dir,rad,color)
%
%INPUTS
% point     Coordinates
% dir       Direction of arrowhead
% rad       Controls width of arrowhead
% color     Color of arrowhead
%
    global mapHandle
    global CB_MAP_OUTPUT
    angle = atan2(dir(2),dir(1));
    l = rad*.9;
    spread = pi/6*.9;
    x = [(point(1)+l*cos(angle+spread)) point(1) (point(1)+l*cos(angle-spread))];
    y = [(point(2)+l*sin(angle+spread)) point(2) (point(2)+l*sin(angle-spread))];
    if strcmp(CB_MAP_OUTPUT, 'matlab')
        if find(color>1)
            color = color/255;
        end
        fill(x,-y,color);
    elseif strcmp(CB_MAP_OUTPUT, 'java')
        %missing code
    elseif strcmp(CB_MAP_OUTPUT, 'svg')
        %determine type of color input
        if ischar(color)
            colorFill = color;
        else if isvector(color)
                colorFill = strcat('rgb(',num2str(color(1)),',',num2str(color(2)), ',',num2str(color(3)),')');
            end
        end
        colorStroke = colorFill;
        fprintf(mapHandle,'<path d="M %8.2f %8.2f L %8.2f %8.2f L %8.2f %8.2f z" fill="%s" stroke="%s"/>\n',x(1),y(1),x(2),y(2),x(3),y(3),colorFill,colorStroke);
    else
        display('no render found');
    end
end


function msm_to_mm_coordinate_real_general ( output_filename, a )

%*****************************************************************************80
%
%% MSM_TO_MM_COORDINATE_REAL_GENERAL writes a "matrix coordinate real general" Matrix Market file.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    02 November 2008
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string OUTPUT_FILENAME, the name of the file to which the information
%    should be written.
%
%    Input, sparse matrix A, the NROW by NCOL matrix, stored in MATLAB sparse
%    matrix format, which is to be written to the file.
%
  [ nrow, ncol ] = size ( a );
  nnzeros = nnz ( a );

  fid = fopen ( output_filename, 'wt+' );

  if ( fid < 0 );
    fprintf ( 1, '\n' );
    fprintf ( 1, 'MSM_TO_MM_COORDINATE_REAL_GENERAL - Fatal error!\n' );
    fprintf ( 1, '  Cannot open the output file.\n' );
    error ( 'MSM_TO_MM_COORDINATE_REAL_GENERAL - Fatal error!');
  end;

  fprintf ( fid, '%%%%MatrixMarket matrix coordinate real general\n');
  fprintf ( fid, '%%%%  Created by MSM_TO_MM_COORDINATE_REAL_GENERAL.M\n' );
  fprintf ( fid, '  %d  %d  %d\n', nrow, ncol, nnzeros );

  for j = 1 : ncol
    [ rows, temp, vals ] = find ( a(:,j) );
    sz = size ( rows );
    for k2 = 1 : sz
      fprintf ( fid, '  %d  %d  %f\n', rows(k2), j, vals(k2) );
    end

  end

  fclose ( fid );

  return
end


function [ y, j, ierror ] = yj_check_gregorian ( y, j )

%*****************************************************************************80
%
%% YJ_CHECK_GREGORIAN checks a Gregorian YJ date.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    06 March 2013
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input/output, integer Y, J, the YJ date.
%
%    Output, integer IERROR, is 0 if no serious error was found in
%    the date, and 1 otherwise.
%

%
%  Check the year.
%
  [ y, ierror ] = y_check_gregorian ( y );

  if ( ierror ~= 0 )
    return
  end
%
%  Make sure J is not too small or too big.
%
  [ y, j ] = j_borrow_gregorian ( y, j );

  [ y, j ] = j_carry_gregorian ( y, j );

  return
end


function data = LoadBinary_Down_ss(filename,varargin)

% LoadBinary - Load data from a multiplexed binary file.
%
%  Reading a subset of the data can be done in two different manners: either
%  by specifying start time and duration (more intuitive), or by indicating
%  the position and size of the subset in terms of number of samples per
%  channel (more accurate).
%
%  USAGE
%
%    data = LoadBinary(filename,<options>)
%
%    filename       file to read
%    <options>      optional list of property-value pairs (see table below)
%
%    =========================================================================
%     Properties    Values
%    -------------------------------------------------------------------------
%     'frequency'   sampling rate (in Hz, default = 20kHz)
%     'start'       position to start reading (in s, default = 0)
%     'duration'    duration to read (in s, default = Inf)
%     'offset'      position to start reading (in samples per channel,
%                   default = 0)
%     'samples'     number of samples (per channel) to read (default = Inf)
%     'nChannels'   number of data channels in the file (default = 1)
%     'channels'    channels to read (default = all)
%     'precision'   sample precision (default = 'int16')
%     'skip'        number of bytes to skip after each value is read
%                   (default = 0)
%     'downsample'  factor by which to downample by (default = 1)
%    =========================================================================

% Copyright (C) 2004-2011 by Michaël Zugaro
%Modified by DLevenstein 2016 to include downsampling
%
% This program is free software; you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation; either version 3 of the License, or
% (at your option) any later version.

% Default values
nChannels = 1;
precision = 'int16';
skip = 0;
frequency = 20000;
channels = [];
start = 0;
duration = Inf;
offset = 0;
nSamplesPerChannel = Inf;
time = false;
samples = false;
downsamplefactor = 1;

if nargin < 1 | mod(length(varargin),2) ~= 0,
  error('Incorrect number of parameters (type ''help LoadBinary'' for details).');
end

% Parse options
for i = 1:2:length(varargin),
	if ~ischar(varargin{i}),
		error(['Parameter ' num2str(i+3) ' is not a property (type ''help LoadBinary'' for details).']);
	end
	switch(lower(varargin{i})),
		case 'frequency',
			frequency = varargin{i+1};
			if ~isdscalar_ss(frequency,'>0'),
				error('Incorrect value for property ''frequency'' (type ''help LoadBinary'' for details).');
			end
		case 'start',
			start = varargin{i+1};
			if ~isdscalar_ss(start),
				error('Incorrect value for property ''start'' (type ''help LoadBinary'' for details).');
			end
			if start < 0, start = 0; end
			time = true;
		case 'duration',
			duration = varargin{i+1};
			if ~isdscalar_ss(duration,'>=0'),
				error('Incorrect value for property ''duration'' (type ''help LoadBinary'' for details).');
			end
			time = true;
		case 'offset',
			offset = varargin{i+1};
			if ~isiscalar_ss(offset),
				error('Incorrect value for property ''offset'' (type ''help LoadBinary'' for details).');
			end
			if offset < 0, offset = 0; end
			samples = true;
		case 'samples',
			nSamplesPerChannel = varargin{i+1};
			if ~isdscalar_ss(nSamplesPerChannel,'>=0'),
				error('Incorrect value for property ''samples'' (type ''help LoadBinary'' for details).');
			end
			samples = true;
		case 'nchannels',
			nChannels = varargin{i+1};
			if ~isiscalar_ss(nChannels,'>0'),
				error('Incorrect value for property ''nChannels'' (type ''help LoadBinary'' for details).');
			end
		case 'channels',
			channels = varargin{i+1};
			if ~isivector_ss(channels,'>0'),
				error('Incorrect value for property ''channels'' (type ''help LoadBinary'' for details).');
			end
		case 'precision',
			precision = varargin{i+1};
			if ~isstring_ss(precision),
				error('Incorrect value for property ''precision'' (type ''help LoadBinary'' for details).');
			end
		case 'skip',
			skip = varargin{i+1};
			if ~isiscalar_ss(skip,'>=0'),
				error('Incorrect value for property ''skip'' (type ''help LoadBinary'' for details).');
            end
		case 'downsample',
			downsamplefactor = varargin{i+1};
			if ~isiscalar_ss(downsamplefactor,'>=0'),
				error('Incorrect value for property ''downsample'' (type ''help LoadBinary'' for details).');
			end
		otherwise,
			error(['Unknown property ''' num2str(varargin{i}) ''' (type ''help LoadBinary'' for details).']);
	end
end

% Either start+duration, or offset+size
if time && samples,
	error(['Data subset can be specified either in time or in samples, but not both (type ''help LoadBinary'' for details).']);
end

% By default, load all channels
if isempty(channels),
	channels = 1:nChannels;
end

% Check consistency between channel IDs and number of channels
if any(channels>nChannels),
	error('Cannot load specified channels (listed channel IDs inconsistent with total number of channels).');
end

% Open file
if ~exist(filename),
	error(['File ''' filename ''' not found.']);
end
f = fopen(filename,'r');
if f == -1,
	error(['Cannot read ' filename ' (insufficient access rights?).']);
end

% Size of one data point (in bytes)
sampleSize = 0;
switch precision,
	case {'uchar','unsigned char','schar','signed char','int8','integer*1','uint8','integer*1'},
		sampleSize = 1;
	case {'int16','integer*2','uint16','integer*2'},
		sampleSize = 2;
	case {'int32','integer*4','uint32','integer*4','single','real*4','float32','real*4'},
		sampleSize = 4;
	case {'int64','integer*8','uint64','integer*8','double','real*8','float64','real*8'},
		sampleSize = 8;
end

% Position and number of samples (per channel) of the data subset
if time,
	dataOffset = floor(start*frequency)*nChannels*sampleSize;
	nSamplesPerChannel = floor((duration*frequency));
else
	dataOffset = offset*nChannels*sampleSize;
end

% Position file index for reading
status = fseek(f,dataOffset,'bof');
if status ~= 0,
	fclose(f);
	error('Could not start reading (possible reasons include trying to read past the end of the file).');
end

% Determine total number of samples in file
fileStart = ftell(f);
status = fseek(f,0,'eof');
if status ~= 0,
	fclose(f);
	error('Error reading the data file (possible reasons include trying to read past the end of the file).');
end
fileStop = ftell(f);
% (floor in case all channels do not have the same number of samples)
maxNSamplesPerChannel = floor(((fileStop-fileStart)/nChannels/sampleSize));
frewind(f);
status = fseek(f,dataOffset,'bof');
if status ~= 0,
	fclose(f);
	error('Could not start reading (possible reasons include trying to read past the end of the file).');
end

if isinf(nSamplesPerChannel) || nSamplesPerChannel > maxNSamplesPerChannel,
	nSamplesPerChannel = maxNSamplesPerChannel;
end

if downsamplefactor>1
    precision = [num2str(nChannels),'*',precision];
    skip = nChannels*(downsamplefactor-1)*sampleSize;
    nSamplesPerChannel = floor(nSamplesPerChannel./downsamplefactor);
end

% For large amounts of data, read chunk by chunk
maxSamplesPerChunk = 10000;
nSamples = nSamplesPerChannel*nChannels;
if nSamples <= maxSamplesPerChunk,
	data = LoadChunk(f,nChannels,channels,nSamples/nChannels,precision,skip);
else
	% Determine chunk duration and number of chunks
	nSamplesPerChunk = floor(maxSamplesPerChunk/nChannels)*nChannels;
	nChunks = floor(nSamples/nSamplesPerChunk);
	% Preallocate memory
	data = zeros(nSamplesPerChannel,length(channels));
	% Read all chunks
	i = 1;
	for j = 1:nChunks,
		d = LoadChunk(f,nChannels,channels,nSamplesPerChunk/nChannels,precision,skip);
		[m,n] = size(d);
		if m == 0, break; end
		data(i:i+m-1,:) = d;
		i = i+m;
	end
	% If the data size is not a multiple of the chunk size, read the remainder
	remainder = nSamples - nChunks*nSamplesPerChunk;
	if remainder ~= 0,
		d = LoadChunk(f,nChannels,channels,remainder/nChannels,precision,skip);
		[m,n] = size(d);
		if m ~= 0,
			data(i:i+m-1,:) = d;
		end
	end
end

fclose(f);

% ---------------------------------------------------------------------------------------------------------

function data = LoadChunk(fid,nChannels,channels,nSamples,precision,skip)

if skip ~= 0,
	data = fread(fid,[nChannels nSamples],precision,skip);
    %data = fread(fid,[nChannels nSamples],[num2str(nChannels),'*',precision],nChannels*skip);
else
	data = fread(fid,[nChannels nSamples],precision);
end
data=data';

if isempty(data),
	warning('No data read (trying to read past file end?)');
elseif ~isempty(channels),
	data = data(:,channels);
end


function varargout=abs(varargin)
%ABS (overloaded)

switch class(varargin{1})
    case 'double'
        error('Overloaded SDPVAR/ABS CALLED WITH DOUBLE. Report error')

    case 'sdpvar' % Overloaded operator for SDPVAR objects. Pass on args and save them.
        if isreal(varargin{1})
            varargout{1} = yalmip('define',mfilename,varargin{1});
            B = getbase(varargin{1});
            if all(all(fix(B)==B))
                CurrInt = yalmip('quantvariables');
                if all(ismember(getvariables(varargin{1}),CurrInt))
                    yalmip('setintvariables',[CurrInt getvariables(varargout{1})]);
                end
            end
        else
            % For complex args, abs(X) is defined [norm(X(i,j),2)] in MATLAB
            y = [];
            x = varargin{1};
            for i = 1:size(x,1)
                temp = [];
                for j = 1:size(x,2)
                    temp = [temp norm(extsubsref(x,i,j))];
                end
                y = [y;temp];
            end
            varargout{1} = y;
        end

    case 'char' % YALMIP send 'graph' when it wants the epigraph or hypograph
        switch varargin{1}
            case 'graph'
                % Description using epigraphs
                t = varargin{2};
                X = varargin{3};
                varargout{1} = [1 -1;-1 -1]*[X;t] <= [0;0];
                varargout{2} = struct('convexity','convex','monotonicity','none','definiteness','positive','model','graph');
                varargout{3} = X;

            case {'exact','integer','callback'}
                % Exact description using binary variables
                t = varargin{2};
                X = varargin{3};
                d = varargin{4};
                [M,m]=derivebounds(X);
                if m>=0
                    F = (t == X);
                elseif M<=0
                    F = (t == -X);
                else
                    maxABSX = max([abs(m) abs(M)],[],2);
                    F = [[0 1 0;
                     0 -1 0;
                     1 0 -M;
                     1 1 -2*maxABSX;
                     -1 -1 0;
                     -1 0 -m;
                     -1 1 2*maxABSX;1 -1 0]*[X;t;d] <= [maxABSX;0;0;0;0;-m;2*maxABSX;0]];
                end

                varargout{1} = F;
                varargout{2} = struct('convexity','convex','monotonicity','none','definiteness','positive','model','integer');
                varargout{3} = X;

            otherwise
                error('SDPVAR/ABS called with CHAR argument?');
        end
    otherwise
        error('Strange type on first argument in SDPVAR/ABS');
end


function [uniqueSparse, grayGraph] = Mesh2Connections(mesh)
%
%  [uniqueSparse,grayGraph] = Mesh2Connections(mesh)
%
% AUTHOR:  Maher Khoury
% DATE:    08.19.99
% PURPOSE:
%  Takes in a Mesh structure and computes a connections matrix.
%
%  The Mesh structure has data in a striplist and triangles.
%  These are OpenGL constructs.  A striplist is organized so that
%  the first three nodes defines triangle, the 2-4 define another
%  triangle, 3-5, and so forth.
%
%
% INPUT:
%   Mesh
%
% SEE ALSO:  MrReadMrM to understand more about the Mesh structure.
%
% 09.03.99  BW.
%    Rounding the mesh early caused problems.  So, I leave the
%    vertices as floats.  The rounding only happens now when the
%    data are converted to nodes in Connections2Graph.
%

%    vertices = round(mesh.vertices); -- MK rounded.  Why?  I
%    removed this rounding from here.
%  There is also an issue of when the vertices should have 1
% added to them.  This should probably happen in Connections2Graph?
%
vertices = mesh.vertices;

%% Get number of nodes
%
numNodes = size(vertices,1);

%% Initialize nodeConnection sparse matrix
%
nodeConnections = sparse(numNodes,numNodes);

%% Build unique list.  nodeIdx maps the unique node entries back
%  to the redundant node list.  uniqueIdx are the nodes in
%  vertices that are instances of unique node.
%    uniqueVertices = vertices(nodeIdx)
%    vertices    = uniqueVertices(uniqueIdx)
%
[uniqueVertices, nodeIdx, uniqueIdx] = unique(vertices,'rows');

%% Fill the sparse matrix with stripList
%
h = mrvWaitbar(0,'Reading Edges from Stripped List');
for k=1:size(mesh.stripList,1),
  % Start at the 3rd vertex on the list, and build your triangles
  % looking backwards.
  %
   vertex = mesh.stripList(k,1)+3;
   nodeConnections(vertex,vertex-1)=1;
   nodeConnections(vertex,vertex-2)=1;
   nodeConnections(vertex-2,vertex-1)=1;
   nodeConnections(vertex-1,vertex)=1;
   nodeConnections(vertex-2,vertex)=1;
   nodeConnections(vertex-1,vertex-2)=1;

   % Go on to the next vertex if you are between the current one
   % and the next strip list location.
   %
   while vertex < mesh.stripList(k,1)+mesh.stripList(k,2),
      vertex = vertex + 1;
      nodeConnections(vertex,vertex-1)=1;
      nodeConnections(vertex,vertex-2)=1;
      nodeConnections(vertex-1,vertex)=1;
      nodeConnections(vertex-2,vertex)=1;

      if ~mod(vertex,100)
	mrvWaitbar(vertex/(mesh.triangleOffset-1));
      end
   end
end

close(h);

%% Check statement
%
if ~((vertex+1)==mesh.triangleOffset)
   disp('Problem !!');
   return;
end

ptr = mesh.triangleOffset;

%% Fill the sparse matrix with triangle
%
h = mrvWaitbar(0,'Reading Edges from Triangles');
while ptr<size(vertices,1),
   nodeConnections(ptr,ptr+1)=1;nodeConnections(ptr,ptr+2)=1;nodeConnections(ptr+1,ptr+2)=1;
   nodeConnections(ptr+1,ptr)=1;nodeConnections(ptr+2,ptr)=1;nodeConnections(ptr+2,ptr+1)=1;
   ptr = ptr+3;
   if ~mod(ptr,100)
     mrvWaitbar(ptr/numNodes);
   end
end
close(h);

%% So now we have an unconnected set of strips and triangles in the nodeConnections structure
%  We go through the unique list, find all the strips and triangles the nodes in that list
%  belong to and we merge them.  We then copy the new connection lines back in the structure

%    BW.  Nothing is connected to itself up to here.  But,
%    something in here causes same nodes to be connected to
%    themselves.  Let's clear up this logic and see why.
%
tmp = nodeConnections;
h = mrvWaitbar(0,'Removing redundant vertices from connections...');
for k=1:length(uniqueVertices),

  % For each unique node, find rows of nodeConnections
  % that are really the same and refer to that unique node.
  %
  sameNodes = find(uniqueIdx==k)  ;

  % Now, we are going to collapse the larger matrix by copying
  % all of the entries from all of the same nodes into each other.
  % I think this can produce some diagonal entries. -- BW

  % This sums the nodeConnections along the first (row) dimension
  %
  mergedNodes = sum(nodeConnections(sameNodes,:),1);

  % repmat belows up the mergedNodes to the same size or
  % nodeConnections(sameNodes,:)
  % The two are or'd together to make the entries all 0s and 1s
  %
  nodeConnections(sameNodes,:) = or(nodeConnections(sameNodes,:),...
      repmat(mergedNodes,size(nodeConnections(sameNodes,:),1),1)) ;

  % And then this is done again but for the corresponding columns
  %
  nodeConnections(:,sameNodes') = or(nodeConnections(:,sameNodes'),...
      nodeConnections(sameNodes,:)');

  %
  if ~mod(k,100)
    mrvWaitbar(k/length(nodeIdx));
  end
end
close(h);

%% Compacting the connections matrix
%
uniqueSparse = nodeConnections(sort(nodeIdx),sort(nodeIdx)');

% Did the user want a gray graph, too?
%
if nargout == 1,
   return;
elseif nargout == 2
  [grayGraph.nodes, grayGraph.edges] = Connections2Graph(uniqueSparse,uniqueVertices);
  grayGraph.dimdist = mesh.parameters.structural.voxSize;
else
  error('Mesh2Connections:  Wrong number of output arguments.');
end


function a = r82vec_sort_quick_a ( n, a )

%*****************************************************************************80
%
%% R82VEC_SORT_QUICK_A ascending sorts an R82VEC using quick sort.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    06 February 2005
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer N, the number of entries in the array.
%
%    Input, real A(2,N), the array to be sorted.
%
%    Output, real A(2,N), the sorted array.
%
  level_max = 25;
  dim_num = 2;

  if ( n < 1 )
    fprintf ( 1, '\n' );
    fprintf ( 1, 'R82VEC_SORT_QUICK_A - Fatal error!\n' );
    fprintf ( 1, '  N < 1.\n' );
    error ( 'R82VEC_SORT_QUICK_A - Fatal error!' );
  elseif ( n == 1 )
    return
  end

  level = 1;
  rsave(level) = n + 1;
  base = 1;
  n_segment = n;

  while ( 1 )
%
%  Partition the segment.
%
    [ a(1:2,base:base+n_segment-1), l_segment, r_segment ] = ...
      r82vec_part_quick_a ( n_segment, a(1:2,base:base+n_segment-1) );
%
%  If the left segment has more than one element, we need to partition it.
%
    if ( 1 < l_segment )

      if ( level_max < level )
        fprintf ( 1, '\n' );
        fprintf ( 1, 'R82VEC_SORT_QUICK_A - Fatal error!\n' );
        fprintf ( 1, '  Exceeding recursion maximum of %d\n', level_max );
        error ( 'R82VEC_SORT_QUICK_A - Fatal error!' );
      end

      level = level + 1;
      n_segment = l_segment;
      rsave(level) = r_segment + base - 1;
%
%  The left segment and the middle segment are sorted.
%  Must the right segment be partitioned?
%
    elseif ( r_segment < n_segment )

      n_segment = n_segment + 1 - r_segment;
      base = base + r_segment - 1;
%
%  Otherwise, we back up a level if there is an earlier one.
%
    else

      while ( 1 )

        if ( level <= 1 )
          return
        end

        base = rsave(level);
        n_segment = rsave(level-1) - rsave(level);
        level = level - 1;

        if ( 0 < n_segment )
          break
        end

      end

    end

  end

  return
end


classdef FixedThre < ThreAlgs.ThreAlg
    properties
        thre;
    end
    methods
        function obj = FixedThre(thre)
            obj.algName = 'Segmentation using a fixed threshold';
            obj.thre = thre;
        end
        function CM = segment(obj, DI)
            if size(DI, 3) > 1
                cdMap2d = Utilities.mergeAvg(DI);
            end
            CM = (cdMap2d > obj.thre);
        end
    end
end


% TODO delete this. looks like someone's play file. has error, undocumented
report_this_filefun(mfilename('fullpath'));

ttcat = newt2;
mati = maepi(1,3);
%[p,sdp] = mypval(3,mati);
% [p,sdp,c,sdc,dk,sdk,aa,bb]=mypval2(3, mati);

tmin1 = 0.05;

p0 = 1.44;
b0 = 0.96;
c0 = 1.1;
A0 = -1.62;

save_aspar2;
do = [ ' ! '  hodi '/aspar/myaspar' ];
eval(do)

load aspar3.out
re = aspar3;

bv = re(2,3);
p =  re(1,3)
c =  re(4,3);
A =  re(3,3);

%[bv magco stan av me mer me2,  pr] =  bvalca3(newt2,1,1);
%[me, bv, si, av] = bmemag(newt2) ;

%A = log10(bv/av)
la = 0;
m0 = maepi(1,6);

m = min(newt2.Magnitude);
dt = 0.5;

%t0 = ( max(newt2.Date) - mati)*365;
t0 = tlen;

la = [];ti = [];
for t = c:dt:t0
    la = [la (10^(A + bv*(m0-m)) * (t + c)^(-p))*dt ];
    ti = [ti mati+t/365];
end

la2 = [];ti2 = [];
for t = c0:dt:t0
    la2 = [la2 (10^(A + b0*(m0-m)) * (t + c0)^(-p0))*dt ];
    ti2 = [ti2 mati+t/365];
end
pla = 0; pla2 = 0;
M = max(a.Magnitude) - 5.;
for t = t0:dt:t0+365
    pla  = pla + (10^(A + bv*(M)) * (t + c)^(-p))  *dt;
    pla2 = pla2 + (10^(A + b0*(M)) * (t + c0)^(-p0))  *dt;
end

P = 1 - exp(-pla)
P0  = 1 - exp(-pla2);
figure_w_normalized_uicontrolunits(cum)

l = newt2.Date >= mati + c/365;
tmpn = newt2(l,:);
nu = (1:length(tmpn(:,3))+1); nu(length(tmpn(:,3))+1) = length(tmpn(:,3));
try delete(plc); catch ME, error_handler(ME,@do_nothing);end
try delete(plc2); catch ME, error_handler(ME,@do_nothing);end
try delete(plc3); catch ME, error_handler(ME,@do_nothing);end

hold on
plc = plot([tmpn(:,3) ; teb],nu,'k');

plc2 = plot(ti,cumsum(la),'r');
%plc3 = plot(ti2,cumsum(la2),'g');


function computeModelCoefficients_batchLGG(pathExperiments,imbalance,nBatch,matlabPATH,seed)
% -------------------------------------------------------------------------
% function computeModelCoefficients_batchLGG(pathExperiments,imbalance,nBatch,matlabPATH,seed)
% -------------------------------------------------------------------------
% DESCRIPTION:
% This function computes the final logistic regression coefficients and
% bootstrap confidence intervals of the final models obtained for all
% outcomes analyzed in the LGG study.
% -------------------------------------------------------------------------
% INPUTS:
% 1. pathExperiments: Full path to the directory containing all experiments.
%                     --> Ex: '/myProject/WORKSPACE/LOGISTIC_REGRESSION'
% 2. 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'
% 3. nBatch: Number of parallel batch.
%            --> Ex: 8
% 4. matlabPATH: Full path to the MATLAB executable on the system.
%                --> 'matlab' if a symbolic link to the matlab executable
%                     was previously created.
% 5. seed: Numerical number to use as seed for bootstrapping experiment
%          --> Ex: 54288
% -------------------------------------------------------------------------
% OUTPUTS: Final coefficients, model response and confidence intervals
%          saved in a folder named '/myProject/WORKSPACE/LOGISTIC_REGRESSION/FINAL_MODELS/nameOutcome/fSetName'.
% -------------------------------------------------------------------------
% 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;
time = 30; % Number of seconds to wait before checking if parallel computations are done

cd(pathExperiments), load('training')
cd('FINAL_MODELS'), pathFinalModels = pwd;
mkdir('batchLog_Coeff'), cd('batchLog_Coeff'), pathBatch = pwd;
nameOutcomes = fieldnames(training); nOutcomes = numel(nameOutcomes);
for o = 1:nOutcomes
    outcomes.(nameOutcomes{o}) = training.(nameOutcomes{o}).outcome;
end
setNames = fieldnames(training.(nameOutcomes{1}).text);
[param] = batchExperiments(setNames,outcomes,nBatch); nBatch = length(param);

% PRODUCE BATCH COMPUTATIONS
save('workspace','pathFinalModels','training','param','pathBatch','imbalance','seed'), pause(3)
for i = 1:nBatch
    nameScript = ['batch',num2str(i),'_script.m'];
    fid = fopen(nameScript,'w');
    fprintf(fid,'tic\n');
    fprintf(fid,'load(''workspace'')\n');
    for j = 1:numel(param{i})
        fprintf(fid,['cd(fullfile(pathFinalModels,param{',num2str(i),'}{',num2str(j),'}{2},param{',num2str(i),'}{',num2str(j),'}{1}))\n']);
        fprintf(fid,'load(''finalModel'')\n');
        fprintf(fid,['fprintf(''COMPUTING THE LOGISTIC REGRESSION COEFFICIENTS OF THE FINAL MODEL OF "',param{i}{j}{2},'" OUTCOME, "',param{i}{j}{1},'" FEATURE SET ... '')']);
        fprintf(fid,'\n');
        fprintf(fid,['[coeff,response,modelCI] = computeModelCoefficients(finalModel.Data,training.',param{i}{j}{2},'.outcome,imbalance,seed);\n']);
        fprintf(fid,['fprintf(''DONE!\\n'')']);
        fprintf(fid,'\n');
        fprintf(fid,'save(''coeff'',''coeff''), save(''response'',''response''), save(''modelCI'',''modelCI'')\n');
    end
    fprintf(fid,'cd(pathBatch)\n');
    fprintf(fid,['system(''touch batch',num2str(i),'_end'');\n']);
    fprintf(fid,'clear all\n');
    fprintf(fid,'toc');
    fclose(fid);
    system([matlabPATH,' -nojvm -nodisplay -nodesktop -nosplash < ',nameScript,' >& ',nameScript(1:end-1),'log &']);
end
waitBatch(pathBatch,time,nBatch)
delete('workspace.mat')

cd(startpath)
end


function spm_dem_search_plot(DEM)
% plots visual search in extrinsic and intrinsic coordinates
% FORMAT spm_dem_search_plot(DEM)
%
% DEM - {DEM} structures from visual search simulations
%
% hidden causes and states
%==========================================================================
% x    - hidden states:
%   o(1) - oculomotor angle
%   o(2) - oculomotor angle
%   x(1) - relative amplitude of visual hypothesis 1
%   x(2) - relative amplitude of visual hypothesis 2
%   x(3) - ...
%
% v    - hidden causes
%
% g    - sensations:
%   g(1) - oculomotor angle (proprioception - x)
%   g(2) - oculomotor angle (proprioception - y)
%   g(3) - retinal input - channel 1
%   g(4) - retinal input - channel 2
%   g(5) - ...
%__________________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% Karl Friston
% $Id: spm_dem_search_plot.m 4851 2012-08-20 15:03:48Z karl $

% Preliminaries
%--------------------------------------------------------------------------
clf, global STIM
N  = length(DEM);
S  = spm_read_vols(STIM.U);

% Stimulus
%======================================================================
Dx = STIM.U.dim(1)/2;
Dy = STIM.U.dim(2)/2;
a  = [];
q  = [];
c  = [];

for i = 1:N

    % i-th saccade - position
    %----------------------------------------------------------------------
    pU = DEM{i}.pU.x{1}(1:2,:)*16;
    qU = DEM{i}.qU.x{1}(1:2,:)*16;
    T  = length(pU);

    % conditional confidence
    %----------------------------------------------------------------------
    qC = DEM{i}.qU.S;
    for t = 1:length(qC)
        qV(t) = qC{t}(3,3);
    end

    % accumulate responses
    %----------------------------------------------------------------------
    a  = [a DEM{i}.qU.a{2}];                % action
    q  = [q DEM{i}.qU.x{1}(3:end,:)];       % hidden perceptual states
    c  = [c qV];                            % conditional variance

    % eye movements in extrinsic coordinates
    %======================================================================
    subplot(6,N,i)

    image((S + 1)*32), axis image off, hold on
    plot(qU(2,T) + Dy,qU(1,T) + Dx,'.g','Markersize',8)
    plot(pU(2,T) + Dy,pU(1,T) + Dx,'.r','Markersize',16)
    drawnow, hold off

    % salience map
    %======================================================================
    subplot(6,N,i + N*1)
    imagesc(DEM{i}.S), axis image off

    % sensory input
    %======================================================================
    subplot(6,N,i + N*3)

    % i-th saccade - sensory samples
    %----------------------------------------------------------------------
    o   = DEM{i}.pU.x{1}(:,T);
    s   = ADEM_sample_image(STIM.U,o,STIM.R);

    imagesc(s), axis image off

    % percept
    %======================================================================
    subplot(6,N,i + N*5)

    % i-th saccade - percept
    %----------------------------------------------------------------------
    qU    = DEM{i}.qU.x{1}(3:end,:);

    % hypotheses (0 < H < 1 = normalised neg-entropy)
    %----------------------------------------------------------------------
    h     = spm_softmax(qU(:,T),2);
    H     = 1 + h'*log(h)/log(length(h));

    % retinotopic predictions
    %----------------------------------------------------------------------
    s     = 0;
    for j = 1:length(h)
        s = s + h(j)*spm_read_vols(STIM.S{j});
    end
    image(s*H*64), axis image off

end

% set ButtonDownFcn
%--------------------------------------------------------------------------
t  = (1:length(a))*12;
subplot(6,1,3)
plot(t,a')
title('Action (EOG)','FontSize',16)
xlabel('time (ms)')
axis([1 t(end) -2 2])

subplot(6,1,5)
spm_plot_ci(q(1,:),c,t); hold on
plot(t,q), hold off
axis([1 t(end) -8 8])
title('Posterior belief','FontSize',16)
xlabel('time (ms)')


function FB = FbMake( dim, flag, show )
% Various 1D/2D/3D filterbanks (hardcoded).
%
% USAGE
%  FB = FbMake( dim, flag, [show] )
%
% INPUTS
%  dim     - dimension
%  flag    - controls type of filterbank to create
%          - if d==1
%            1: gabor filter bank for spatiotemporal stuff
%          - if d==2
%            1: filter bank from Serge Belongie
%            2: 1st/2nd order DooG filters.  Similar to Gabor filterbank.
%            3: similar to Laptev&Lindberg ICPR04
%            4: decent seperable steerable? filterbank
%            5: berkeley filterbank for textons papers
%            6: symmetric DOOG filters
%          - if d==3
%            1: decent seperable steerable filterbank
%  show    - [0] figure to use for optional display
%
% OUTPUTS
%
% EXAMPLE
%  FB = FbMake( 2, 1, 1 );
%
% See also FBAPPLY2D
%
% 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]

if( nargin<3 || isempty(show) ); show=0; end

% create FB
switch dim
  case 1
    FB = FbMake1D( flag );
  case 2
    FB = FbMake2D( flag );
  case 3
    FB = FbMake3d( flag );
  otherwise
    error( 'dim must be 1 2 or 3');
end

% display
FbVisualize( FB, show );

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function FB = FbMake1D( flag )
switch flag
  case 1  %%% gabor filter bank for spatiotemporal stuff
    omegas = 1 ./ [3 4 5 7.5 11];
    sigmas =      [3 4 5 7.5 11];
    FB = FbMakegabor1D( 15, sigmas, omegas );

  otherwise
    error('none created.');
end

function FB = FbMakegabor1D( r, sigmas, omegas )
for i=1:length(omegas)
  [feven,fodd]=filterGabor1d(r,sigmas(i),omegas(i));
  if( i==1 ); FB=repmat(feven,[2*length(omegas) 1]); end
  FB(i*2-1,:)=feven; FB(i*2,:)=fodd;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function FB = FbMake2D( flag )

switch flag
  case 1  %%% filter bank from Berkeley / Serge Belongie
    r=15;
    FB = FbMakegabor( r, 6, 3, 3, sqrt(2) );
    FB2 = FbMakeDOG( r, .6, 2.8, 4);
    FB = cat(3, FB, FB2);
    %FB = FB(:,:,1:2:36); %include only even symmetric filters
    %FB = FB(:,:,2:2:36); %include only odd symmetric filters

  case 2 %%% 1st/2nd order DooG filters.  Similar to Gabor filterbank.
    FB = FbMakeDooG( 15, 6, 3, 5, .5) ;

  case 3 %%% similar to Laptev&Lindberg ICPR04
    % Wierd filterbank of Gaussian derivatives at various scales
    % Higher order filters probably not useful.
    r = 9;   dims=[2*r+1 2*r+1];
    sigs = [.5 1 1.5 3]; % sigs = [1,1.5,2];

    derivs = [];
    %derivs = [ derivs; 0 0 ]; % 0th order
    %derivs = [ derivs; 1 0; 0 1 ]; % first order
    %derivs = [ derivs; 2 0; 0 2; 1 1 ]; % 2nd order
    %derivs = [ derivs; 3 0; 0 3; 1 2; 2 1 ]; % 3rd order
    %derivs = [ derivs; 4 0; 0 4; 1 3; 3 1; 2 2 ]; % 4th order
    derivs = [ derivs; 0 1; 0 2; 0 3; 0 4; 0 5]; % 0n order
    derivs = [ derivs; 1 0; 2 0; 3 0; 4 0; 5 0]; % n0 order
    cnt=1;  nderivs = size(derivs,1);
    for s=1:length(sigs)
      for i=1:nderivs
        dG = filterDoog( dims, [sigs(s) sigs(s)], derivs(i,:), 0 );
        if(s==1 && i==1); FB=repmat(dG,[1 1 length(sigs)*nderivs]); end
        FB(:,:,cnt) = dG; cnt=cnt+1;
        %dG = filterDoog( dims, [sigs(s)*3 sigs(s)], derivs(i,:), 0 );
        %FB(:,:,cnt) = dG; cnt=cnt+1;
        %dG = filterDoog( dims, [sigs(s) sigs(s)*3], derivs(i,:), 0 );
        %FB(:,:,cnt) = dG; cnt=cnt+1;
      end
    end

  case 4 % decent seperable steerable? filterbank
    r = 9;   dims=[2*r+1 2*r+1];
    sigs = [.5 1.5 3];
    derivs = [1 0; 0 1; 2 0; 0 2];
    cnt=1;  nderivs = size(derivs,1);
    for s=1:length(sigs)
      for i=1:nderivs
        dG = filterDoog( dims, [sigs(s) sigs(s)], derivs(i,:), 0 );
        if(s==1 && i==1); FB=repmat(dG,[1 1 length(sigs)*nderivs]); end
        FB(:,:,cnt) = dG; cnt=cnt+1;
      end
    end
    FB2 = FbMakeDOG( r, .6, 2.8, 4);
    FB = cat(3, FB, FB2);

  case 5  %%% berkeley filterbank for textons papers
    FB = FbMakegabor( 7, 6, 1, 2, 2 );

  case 6  %%% symmetric DOOG filters
    FB = FbMakeDooGSym( 4, 2, [.5 1] );

  otherwise
    error('none created.');
end

function FB = FbMakegabor( r, nOrient, nScales, lambda, sigma )
% multi-scale even/odd gabor filters. Adapted from code by Serge Belongie.
cnt=1;
for m=1:nScales
  for n=1:nOrient
    [F1,F2]=filterGabor2d(r,sigma^m,lambda,180*(n-1)/nOrient);
    if(m==1 && n==1); FB=repmat(F1,[1 1 nScales*nOrient*2]); end
    FB(:,:,cnt)=F1;  cnt=cnt+1;   FB(:,:,cnt)=F2;  cnt=cnt+1;
  end
end

function FB = FbMakeDooGSym( r, nOrient, sigs )
% Adds symmetric DooG filters.  These are similar to gabor filters.
cnt=1; dims=[2*r+1 2*r+1];
for s=1:length(sigs)
  Fodd = -filterDoog( dims, [sigs(s) sigs(s)], [1 0], 0 );
  Feven = filterDoog( dims, [sigs(s) sigs(s)], [2 0], 0 );
  if(s==1); FB=repmat(Fodd,[1 1 length(sigs)*nOrient*2]); end
  for n=1:nOrient
    theta = 180*(n-1)/nOrient;
    FB(:,:,cnt) = imrotate( Feven, theta, 'bil', 'crop' );  cnt=cnt+1;
    FB(:,:,cnt) = imrotate( Fodd,  theta, 'bil', 'crop' );  cnt=cnt+1;
  end
end

function FB = FbMakeDooG( r, nOrient, nScales, lambda, sigma )
% 1st/2nd order DooG filters.  Similar to Gabor filterbank.
% Defaults: nOrient=6, nScales=3, lambda=5, sigma=.5,
cnt=1; dims=[2*r+1 2*r+1];
for m=1:nScales
  sigma = sigma * m^.7;
  Fodd = -filterDoog( dims, [sigma lambda*sigma^.6], [1,0], 0 );
  Feven = filterDoog( dims, [sigma lambda*sigma^.6], [2,0], 0 );
  if(m==1); FB=repmat(Fodd,[1 1 nScales*nOrient*2]); end
  for n=1:nOrient
    theta = 180*(n-1)/nOrient;
    FB(:,:,cnt) = imrotate( Feven, theta, 'bil', 'crop' );  cnt=cnt+1;
    FB(:,:,cnt) = imrotate( Fodd,  theta, 'bil', 'crop' );  cnt=cnt+1;
  end
end

function FB = FbMakeDOG( r, sigmaStr, sigmaEnd, n )
% adds a serires of difference of Gaussian filters.
sigs = sigmaStr:(sigmaEnd-sigmaStr)/(n-1):sigmaEnd;
for s=1:length(sigs)
  FB(:,:,s) = filterDog2d(r,sigs(s),2); %#ok<AGROW>
  if( s==1 ); FB=repmat(FB,[1 1 length(sigs)]); end
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

function FB = FbMake3d( flag )

switch flag
  case 1 % decent seperable steerable filterbank
    r = 25; dims=[2*r+1 2*r+1 2*r+1];
    sigs = [.5 1.5 3];
    derivs = [0 0 1; 0 1 0; 1 0 0; 0 0 2; 0 2 0; 2 0 0];
    cnt=1; nderivs = size(derivs,1);
    for s=1:length(sigs)
      for i=1:nderivs
        dG = filterDoog( dims, repmat(sigs(s),[1 3]), derivs(i,:), 0 );
        if(s==1 && i==1); FB=repmat(dG,[1 1 1 nderivs*length(sigs)]); end
        FB(:,:,:,cnt) = dG; cnt=cnt+1;
      end
    end

  otherwise
    error('none created.');
end


function [b, iCv, iC] = gp_looprep(gp, x, y)
%GP_LOOPREP  Calculates help terms needed in Gaussian GP LOO
%
%  Description
%    [b, iCv] = gp_looprep(gp, x, y, varargin) takes GP structure,
%    training input and output and returns following terms
%      b = C\y
%      iCv = diag(inv(C))
%    [b, iCv, iC] = gp_looprep(gp, x, y, varargin) returns additionally
%      iC = inv(C)
%    In case of CS, FIC, PIC and CS+FIC, these terms are computed
%    using appropriate efficient sparse matrix and inverse lemma
%    computations.
%

% Copyright (c) 2012 Aki Vehtari

% This software is distributed under the GNU General Public
% License (version 3 or later); please refer to the file
% License.txt, included with the software, for details.

  switch gp.type
    case 'FULL'
      % FULL GP (and compact support GP)
      [K, C] = gp_trcov(gp, x);

      if issparse(C)
        if nargout==3
          iC = full(spinv(C));        % sparse inverse
          iCv = full(diag(iC));       % the diagonal of sparse inverse
        else
          iCv = full(diag(spinv(C))); % the diagonal of sparse inverse
        end
        [LD, notpositivedefinite] = ldlchol(C);
        if notpositivedefinite
          b = NaN;
          iCv = NaN;
          iC = NaN;
          return
        end
        b = ldlsolve(LD,y);
      else
        if nargout==3
          iC = inv(C);                % full inverse
          iCv = diag(inv(C));         % the diagonal of full inverse
        else
          iCv = diag(inv(C));         % the diagonal of full inverse
        end
        b = C\y;
      end

    case 'FIC'
      % FIC
      % Use inverse lemma for FIC low rank covariance matrix approximation
      % Code adapated from gp_pred

      u = gp.X_u;
      m = size(u,1);
      % Turn the inducing vector on right direction
      if size(u,2) ~= size(x,2)
        u=u';
      end
      [Kv_ff, Cv_ff] = gp_trvar(gp, x);  % 1 x f  vector
      K_fu = gp_cov(gp, x, u);   % f x u
      K_uu = gp_trcov(gp, u);     % u x u, noiseles covariance K_uu
      [Luu, notpositivedefinite] = chol(K_uu,'lower');
      if notpositivedefinite
        b = NaN;
        iCv = NaN;
        iC = NaN;
        return
      end
      B=Luu\(K_fu');
      Qv_ff=sum(B.^2)';
      Lav = Cv_ff-Qv_ff;   % 1 x f, Vector of diagonal elements

      % iLaKfu = diag(inv(Lav))*K_fu = inv(La)*K_fu
      iLaKfu = zeros(size(K_fu));  % f x u,
      n=size(x,1);
      for i=1:n
        iLaKfu(i,:) = K_fu(i,:)./Lav(i);  % f x u
      end
      A = K_uu+K_fu'*iLaKfu;
      A = (A+A')./2;
      L = iLaKfu/chol(A);

      if nargout==3
        iC = diag(1./Lav) - L*L'; % Cv = inv(C);
        iCv = diag(iC); %           iCv = diag(inv(C));
      else
        iCv = 1./Lav - sum(L.^2,2); % iCv = diag(inv(C));
      end
      b = y./Lav - L*(L'*y);      % b = C\y;

    case {'PIC' 'PIC_BLOCK'}
      % PIC
      % Use inverse lemma for PIC low rank covariance matrix approximation
      % Code adapated from gp_pred

      u = gp.X_u;
      ind = gp.tr_index;
      if size(u,2) ~= size(x,2)
        % Turn the inducing vector on right direction
        u=u';
      end

      % Calculate some help matrices
      [Kv_ff, Cv_ff] = gp_trvar(gp, x);  % 1 x f  vector
      K_fu = gp_cov(gp, x, u);         % f x u
      K_uu = gp_trcov(gp, u);    % u x u, noiseles covariance K_uu
      Luu = chol(K_uu)';

      % Evaluate the Lambda (La) for specific model
      % Q_ff = K_fu*inv(K_uu)*K_fu'
      % Here we need only the diag(Q_ff), which is evaluated below
      B=Luu\K_fu';
      iLaKfu = zeros(size(K_fu));  % f x u
      for i=1:length(ind)
        Qbl_ff = B(:,ind{i})'*B(:,ind{i});
        [Kbl_ff, Cbl_ff] = gp_trcov(gp, x(ind{i},:));
        La{i} = Cbl_ff - Qbl_ff;
        iLaKfu(ind{i},:) = La{i}\K_fu(ind{i},:);
      end
      A = K_uu+K_fu'*iLaKfu;
      A = (A+A')./2;            % Ensure symmetry
      L = iLaKfu/chol(A);

      % From this on evaluate the prediction
      % See Snelson and Ghahramani (2007) for details
      n=size(y,1);
      iCv=zeros(n,1);
      if nargout==3
        iC=zeros(n,n);
      end
      for i=1:length(ind)
        if nargout==3
          iC(ind{i},ind{i}) = inv(La{i});
        else
          iCv(ind{i},:) = diag(inv(La{i}));
        end
        b(ind{i},:) = La{i}\y(ind{i},:);
      end
      if nargout==3
        iC = iC - L*L';          % iC  = inv(C);
        iCv = diag(iC);          % iCv = diag(inv(C));
      else
        iCv = iCv - sum(L.^2,2); % iCv = diag(inv(C));
      end
      b = b - L*(L'*y);        % b = C\y;

    case 'CS+FIC'
      % CS+FIC
      % Use inverse lemma for CS+FIC
      % Code adapated from gp_pred

      u = gp.X_u;
      if size(u,2) ~= size(x,2)
        % Turn the inducing vector on right direction
        u=u';
      end

      n = size(x,1);
      m = size(u,1);
      ncf = length(gp.cf);

      % Indexes to all non-compact support and compact support covariances.
      cf1 = [];
      cf2 = [];

      % Loop through all covariance functions
      for i1 = 1:ncf
        if ~isfield(gp.cf{i1},'cs')
          % Non-CS covariances
          cf1 = [cf1 i1];
        else
          % CS-covariances
          cf2 = [cf2 i1];
        end
      end

      % First evaluate needed covariance matrices
      % v defines that parameter is a vector
      [Kv_ff, Cv_ff] = gp_trvar(gp, x, cf1); % f x 1  vector
      K_fu = gp_cov(gp, x, u, cf1);          % f x u
      K_uu = gp_trcov(gp, u, cf1);    % u x u, noiseles covariance K_uu
      K_uu = (K_uu+K_uu')./2;         % ensure the symmetry of K_uu

      Luu  = chol(K_uu)';
      % Evaluate the Lambda (La)
      % Q_ff = K_fu*inv(K_uu)*K_fu'
      B=Luu\(K_fu');       % u x f
      Qv_ff=sum(B.^2)';
      Lav = Cv_ff-Qv_ff;   % f x 1, Vector of diagonal elements

      K_cs = gp_trcov(gp,x,cf2);
      La = sparse(1:n,1:n,Lav,n,n) + K_cs;

      iLaKfu = La\K_fu;
      A = K_uu+K_fu'*iLaKfu;
      A = (A+A')./2;     % Ensure symmetry
      L = iLaKfu/chol(A);

      if nargout==3
        iC = inv(La) - L*L'; % iCv = diag(inv(C));
        iCv = diag(iC)     ; % iCv = diag(inv(C));
      else
        iCv = diag(inv(La)) - sum(L.^2,2); % iCv = diag(inv(C));
      end
      b = La\y - L*(L'*y);         % b = C\y;

    case 'SSGP' % SSGP
      error('GP_LOOPRED is not yet implemented for SSGP and Gaussian likelihood')

    otherwise
      error('Unknown type of Gaussian process')
  end

end


% Function to derive residual peak prominence parameter
%
% Description
% Function to derive residual peak prominence parameter
%
%
% Inputs
%  rep      : [samples] [Nx1]  Resonator output of LP-residual of speech
%                              signal
%  fs       : [Hz]      [1x1]  Sampling frequency
%  winLen   : [samples] [1x1]  Window length
%  x        : [samples] [Nx1]  Speech signal
%  Es       : [dB]      [Mx1]  Energy contour
%
% Outputs
%  peak_prom: [samples] [Px1]  Peak prominence contour
%  peak_t   : [samples] [Px1]  Time locations of Peak prominence contour
%
% Example
%  Please see the HOWTO_glottalsource.m example file.
%
% References
%  [1] Drugman, T., Kane, J., Gobl, C., `Automatic Analysis of Creaky
%       Excitation Patterns', Submitted to Computer Speech and
%       Language.
%  [2] Kane, J., Drugman, T., Gobl, C., (2013) `Improved automatic
%       detection of creak', Computer Speech and Language 27(4), pp.
%       1028-1047.
%  [3] Drugman, T., Kane, J., Gobl, C., (2012) `Resonator-based creaky
%       voice detection', Interspeech 2012, Portland, Oregon, USA.
%
% Copyright (c) 2013 University of Mons, FNRS & 2013 Trinity College Dublin
%
% License
%  This code is a part of the GLOAT toolbox with the following
%  licence:
%  This program is free software: you can redistribute it and/or modify
%  it under the terms of the GNU General Public License as published by
%  the Free Software Foundation, either version 3 of the License, or
%  (at your option) any later version.
%  This program is distributed in the hope that it will be useful,
%  but WITHOUT ANY WARRANTY; without even the implied warranty of
%  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%  GNU General Public License for more details.
%
% This function is part of the Covarep project: http://covarep.github.io/covarep
%
% Authors
%  Thomas Drugman <thomas.drugman@umons.ac.be> & John Kane <kanejo@tcd.ie>

function [peak_prom,peak_t,peak_idx] = get_res_peak_prom(rep,fs,winLen,x,Es)

%% Initial settings
Es=interp1(linspace(1,length(x),length(Es)),Es,1:length(x)); % Interpolated energy contour

ener_tol = 400/1000*fs;
ener_thresh=4;

peak_prom=zeros(1,round(length(rep)/winLen));
peak_t=zeros(1,round(length(rep)/winLen));
peak_idx=zeros(1,round(length(rep)/winLen));
winLenRem=winLen*.2; % just under half so periodic signals will have space for another peak

start=1;
stop=start+winLen-1;
n=1;
shift=20/1000*fs;

%% Do processing
while stop <= length(rep)

    frame_init = rep(start:stop);
    peak_t(n)=round(mean([start stop]));
    [~,maxIdx]=max(abs(frame_init));

    if maxIdx+start-round(winLen/2) < 1 || start+maxIdx+round(winLen/2) > length(rep)
        peak_prom(n)=0;
    else
        start2=maxIdx+start-round(winLen/2);
        stop2=start+maxIdx+round(winLen/2);
        frame2=rep(start2:stop2);
        [~,maxIdx]=max(abs(frame2));
        if frame2(maxIdx)< 0
            frame2=frame2*-1;
        end

        peak_idx(n) = maxIdx+start2-1;
        midpoint = round(winLen/2);
        frame2=frame2/max(frame2); % Normalise amplitude
        frame2(round(midpoint-winLenRem):round(midpoint+winLenRem))=0;
        frame2(frame2<0)=0;

        peak_prom(n)=1-max(frame2);

        if maxIdx+start-ener_tol < 1
            start_ener=1;
        else start_ener = maxIdx+start-ener_tol;
        end
        if maxIdx+start+ener_tol > length(x)
            stop_ener = length(x);
        else stop_ener = maxIdx+start+ener_tol;
        end

        max_ener=max(Es(start_ener:stop_ener));

        if Es(maxIdx+start) < max_ener-ener_thresh
            peak_prom(n)=0;
        end
    end

    start=start+shift;
    stop=start+winLen-1;
    n=n+1;
end

% Median filtering to remove outliers
peak_prom=medfilt1(peak_prom,5);

peak_prom(peak_t==0)=[];
peak_idx(peak_t==0)=[];
peak_t(peak_t==0)=[];