function testModel(downsample,model,trainNum,warpsize,cdim,ndim,seed,RoIRoD)

switch model
    case 'vgg16'
        Epoches = 40;
    case 'resnet50'
        Epoches = 20;
end

modelDir = fullfile('data','GAIC',model,RoIRoD,[num2str(trainNum) '_down' num2str(downsample)...
           '_warp' num2str(warpsize) 'cdim' num2str(cdim) 'ndim' num2str(ndim) 'seed' num2str(seed)]);

load(['imdb_GAIC',num2str(trainNum),'.mat'],'imdb');
gt_scores = cat(2,imdb.bbox.gt_scores{imdb.images.set==1});
gt_scores_means = mean(gt_scores);
gt_scores_stds = std(gt_scores);

for epoch = 1:Epoches
    acc5 = zeros(200,4);
    acc10 = zeros(200,4);
    fprintf('processing epoch %d\n',epoch);
    netStruct = load(fullfile(modelDir,['net-epoch-',num2str(epoch) '.mat']),'net');
    net = dagnn.DagNN.loadobj(netStruct.net) ;
    net.mode = 'test' ;
    net.move('gpu') ;
    probVarI = net.getVarIndex('predcls');
    net.vars(probVarI).precious = 1;
    minScale = net.meta.minScale;

    testID = imdb.images.testSet;
    tic;
    for i = 1:numel(testID)
        img = imread(imdb.meta.img_path{testID(i)});
        [x1,x2,x3] = size(img);
        boxes = imdb.bbox.boxes{testID(i)};
        ss = (boxes(3,:)-boxes(1,:)).*(boxes(4,:)-boxes(2,:));

        imre = single(imresize(img,minScale/min([x1,x2]),'bilinear'));
        imre = bsxfun(@minus,imre,net.meta.normalization.averageImage);

        [r1,r2,r3] = size(imre);
        r1 = 32*round(r1/32);
        r2 = 32*round(r2/32);
        imre = imresize(imre,[r1,r2],'bilinear');

        scale1 = r1/x1;
        scale2 = r2/x2;
        boxes_s = [];
        boxes_s(1,:) = max(floor(boxes(1,:) * scale1),1);
        boxes_s(2,:) = max(floor(boxes(2,:) * scale2),1);
        boxes_s(3,:) = min(ceil(boxes(3,:) * scale1),r1);
        boxes_s(4,:) = min(ceil(boxes(4,:) * scale2),r2);

        inputs = {'input', gpuArray(imre), 'rois', gpuArray(single([ones(1,size(boxes_s,2));boxes_s]))} ;

        net.eval(inputs) ;
        preds{i} = squeeze(gather(net.vars(probVarI).value)) ;
        preds{i} = preds{i} * gt_scores_stds + gt_scores_means;
        gts = imdb.bbox.gt_scores{testID(i)};

        srcc(i) = corr(preds{i}, gts', 'type', 'Spearman');

        [gts_sorted,id_gts] = sort(gts,'descend');
        [~,id_preds] = sort(preds{i},'descend');
        [~,id_baseline] = sort(ss,'descend');

        for k = 1:4
            for j = 1:k
                if gts(id_preds(j)) >= gts_sorted(5)
                    acc5(i,k) = acc5(i,k) + 1;
                end
            end
            acc5(i,k) = acc5(i,k) / k;
        end

        for k = 1:4
            for j = 1:k
                if gts(id_preds(j)) >= gts_sorted(10)
                    acc10(i,k) = acc10(i,k) + 1;
                end
            end
            acc10(i,k) = acc10(i,k) / k;
        end
    end
    toc;
    Acc5(epoch,:) = sum(acc5)/numel(testID);
    Acc10(epoch,:) = sum(acc10)/numel(testID);
    SRCC(epoch) = mean(srcc);
end
AllRes = cat(2,Acc5,mean(Acc5,2),Acc10,mean(Acc10,2),SRCC');
save(fullfile(modelDir,'Result.mat'),'Acc5','Acc10','SRCC','AllRes');

end


function [P, J] = regionGrowing(cIM, initPos, thresVal, maxDist, tfMean, tfFillHoles, tfSimplify)
% REGIONGROWING Region growing algorithm for 2D/3D grayscale images
%
% Syntax:
%   P = regionGrowing();
%   P = regionGrowing(cIM);
%   P = regionGrowing(cIM, initPos)
%   P = regionGrowing(..., thresVal, maxDist, tfMean, tfFillHoles, tfSimpl)
%   [P, J] = regionGrowing(...);
%
% Inputs:
%          cIM: 2D/3D grayscale matrix                      {current image}
%      initPos: Coordinates for initial seed position     {ginput position}
%     thresVal: Absolute threshold level to be included     {5% of max-min}
%      maxDist: Maximum distance to the initial position in [px]      {Inf}
%       tfMean: Updates the initial value to the region mean (slow) {false}
%  tfFillHoles: Fills enclosed holes in the binary mask              {true}
%   tfSimplify: Reduces the number of vertices {true, if dpsimplify exists}
%
% Outputs:
%   P: VxN array (with V number of vertices, N number of dimensions)
%      P is the enclosing polygon for all associated pixel/voxel
%   J: Binary mask (with the same size as the input image) indicating
%      1 (true) for associated pixel/voxel and 0 (false) for outside
%
% Examples:
%   % 2D Example
%   load example
%   figure, imshow(cIM, [0 1500]), hold all
%   poly = regionGrowing(cIM, [], 300); % click somewhere inside the lungs
%   plot(poly(:,1), poly(:,2), 'LineWidth', 2)
%
%   % 3D Example
%   load mri
%   poly = regionGrowing(squeeze(D), [66,55,13], 60, Inf, [], true, false);
%   plot3(poly(:,1), poly(:,2), poly(:,3), 'x', 'LineWidth', 2)
%
% Requirements:
%   TheMathWorks Image Processing Toolbox for bwboundaries() and axes2pix()
%   Optional: Line Simplification by Wolfgang Schwanghart to reduce the
%             number of polygon vertices (see the MATLAB FileExchange)
%
% Remarks:
%   The queue is not preallocated and the region mean computation is slow.
%   I haven't implemented a preallocation nor a queue counter yet for the
%   sake of clarity, however this would be of course more efficient.
%
% Author:
%   Daniel Kellner, 2011, braggpeaks{}googlemail.com
%   History: v1.00: 2011/08/14

% error checking on input arguments
if nargin > 7
    error('Wrong number of input arguments!')
end

if ~exist('cIM', 'var')
    himage = findobj('Type', 'image');
    if isempty(himage) || length(himage) > 1
        error('Please define one of the current images!')
    end

    cIM = get(himage, 'CData');
end

if ~exist('initPos', 'var') || isempty(initPos)
    himage = findobj('Type', 'image');
    if isempty(himage)
        himage = imshow(cIM, []);
    end

    % graphical user input for the initial position
    p = ginput(1);

    % get the pixel position concerning to the current axes coordinates
    initPos(1) = round(axes2pix(size(cIM, 2), get(himage, 'XData'), p(2)));
    initPos(2) = round(axes2pix(size(cIM, 1), get(himage, 'YData'), p(1)));
end

if ~exist('thresVal', 'var') || isempty(thresVal)
    thresVal = double((max(cIM(:)) - min(cIM(:)))) * 0.05;
end

if ~exist('maxDist', 'var') || isempty(maxDist)
    maxDist = Inf;
end

if ~exist('tfMean', 'var') || isempty(tfMean)
    tfMean = false;
end

if ~exist('tfFillHoles', 'var')
    tfFillHoles = true;
end

if isequal(ndims(cIM), 2)
    initPos(3) = 1;
elseif isequal(ndims(cIM),1) || ndims(cIM) > 3
    error('There are only 2D images and 3D image sets allowed!')
end

[nRow, nCol, nSli] = size(cIM);

if initPos(1) < 1 || initPos(2) < 1 ||...
   initPos(1) > nRow || initPos(2) > nCol
    error('Initial position out of bounds, please try again!')
end

if thresVal < 0 || maxDist < 0
    error('Threshold and maximum distance values must be positive!')
end

if ~isempty(which('dpsimplify.m'))
    if ~exist('tfSimplify', 'var')
        tfSimplify = true;
    end
    simplifyTolerance = 1;
else
    tfSimplify = false;
end

% initial pixel value
regVal = double(cIM(initPos(1), initPos(2), initPos(3)));

% text output with initial parameters
disp(['RegionGrowing Opening: Initial position (' num2str(initPos(1))...
      '|' num2str(initPos(2)) '|' num2str(initPos(3)) ') with '...
      num2str(regVal) ' as initial pixel value!'])

% preallocate array
J = false(nRow, nCol, nSli);

% add the initial pixel to the queue
queue = [initPos(1), initPos(2), initPos(3)];

%%% START OF REGION GROWING ALGORITHM
while size(queue, 1)
  % the first queue position determines the new values
  xv = queue(1,1);
  yv = queue(1,2);
  zv = queue(1,3);

  % .. and delete the first queue position
  queue(1,:) = [];

  % check the neighbors for the current position
  for i = -1:1
    for j = -1:1
      for k = -1:1

        if xv+i > 0  &&  xv+i <= nRow &&...          % within the x-bounds?
           yv+j > 0  &&  yv+j <= nCol &&...          % within the y-bounds?
           zv+k > 0  &&  zv+k <= nSli &&...          % within the z-bounds?
           any([i, j, k])       &&...      % i/j/k of (0/0/0) is redundant!
           ~J(xv+i, yv+j, zv+k) &&...          % pixelposition already set?
           sqrt( (xv+i-initPos(1))^2 +...
                 (yv+j-initPos(2))^2 +...
                 (zv+k-initPos(3))^2 ) < maxDist &&...   % within distance?
           cIM(xv+i, yv+j, zv+k) <= (regVal + thresVal) &&...% within range
           cIM(xv+i, yv+j, zv+k) >= (regVal - thresVal) % of the threshold?

           % current pixel is true, if all properties are fullfilled
           J(xv+i, yv+j, zv+k) = true;

           % add the current pixel to the computation queue (recursive)
           queue(end+1,:) = [xv+i, yv+j, zv+k];

           if tfMean
               regVal = mean(mean(cIM(J > 0))); % --> slow!
           end

        end
      end
    end
  end
end
%%% END OF REGION GROWING ALGORITHM

% loop through each slice, fill holes and extract the polygon vertices
P = [];
for cSli = 1:nSli
    if ~any(J(:,:,cSli))
        continue
    end

	% use bwboundaries() to extract the enclosing polygon
    if tfFillHoles
        % fill the holes inside the mask
        J(:,:,cSli) = imfill(J(:,:,cSli), 'holes');
        B = bwboundaries(J(:,:,cSli), 8, 'noholes');
    else
        B = bwboundaries(J(:,:,cSli));
    end

	newVertices = [B{1}(:,2), B{1}(:,1)];

    % simplify the polygon via Line Simplification
    if tfSimplify
        newVertices = dpsimplify(newVertices, simplifyTolerance);
    end

    % number of new vertices to be added
    nNew = size(newVertices, 1);

    % append the new vertices to the existing polygon matrix
    if isequal(nSli, 1) % 2D
        P(end+1:end+nNew, :) = newVertices;
    else                % 3D
        P(end+1:end+nNew, :) = [newVertices, repmat(cSli, nNew, 1)];
    end
end

% text output with final number of vertices
disp(['RegionGrowing Ending: Found ' num2str(length(find(J)))...
      ' pixels within the threshold range (' num2str(size(P, 1))...
      ' polygon vertices)!'])


% -*- INTERNAL UNDOCUMENTED FUNCTION -*-
function [spv, spp] = sp_bspline_fluid_3d (element_name, ...
                   knots, nsub_p, degree_p, regularity_p, msh)

warning ('geopdes:obsolete', 'Function SP_BSPLINE_FLUID_3D is obsolete. Using SP_BSPLINE_FLUID instead')

[spv, spp] = sp_bspline_fluid (element_name, knots, nsub_p, degree_p, regularity_p, msh);


function [sFile, ChannelMat] = in_fopen_eeglab(DataFile, ImportOptions)
% IN_FOPEN_EEGLAB: Open an EEGLAB .set file (continuous recordings).
%
% FORMAT:
%     EEGLAB datasets can have two forms : one file (.SET) or two files (.SET/.DAT).
%     In both cases, .SET file is a Matlab matrix with the dataset header in 'EEG' field.
%         1) .SET      : Recordings are stored in the 'EEGDATA' field of the .SET matrix
%         2) .SET/.DAT : Recordings are stored in binary mode in a separate .DAT file,
%                        whose file name is stored in field 'EEG.datfile' of the .SET matrix.
%                        Format : [nbChan, nbTime*nbTrials] float32 binary matrix (Little-Endian)
%     Channel locations may be stored in the .SET file, in field 'EEG.chanlocs'

% @=============================================================================
% This function is part of the Brainstorm software:
% https://neuroimage.usc.edu/brainstorm
%
% Copyright (c) University of Southern California & McGill University
% This software is distributed under the terms of the GNU General Public License
% as published by the Free Software Foundation. Further details on the GPLv3
% license can be found at http://www.gnu.org/copyleft/gpl.html.
%
% FOR RESEARCH PURPOSES ONLY. THE SOFTWARE IS PROVIDED "AS IS," AND THE
% UNIVERSITY OF SOUTHERN CALIFORNIA AND ITS COLLABORATORS DO NOT MAKE ANY
% WARRANTY, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
% MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, NOR DO THEY ASSUME ANY
% LIABILITY OR RESPONSIBILITY FOR THE USE OF THIS SOFTWARE.
%
% For more information type "brainstorm license" at command prompt.
% =============================================================================@
%
% Authors: Francois Tadel, 2008-2023

% ===== PARSE INPUTS =====
if (nargin < 2) || isempty(ImportOptions)
    ImportOptions = db_template('ImportOptions');
end

%% ===== READER HEADER =====
% Load .set file
if isstruct(DataFile)
    hdr = DataFile;
    DataFile = hdr.filename;
else
    hdr = load(DataFile, '-mat');
end
% If there is no EEG field
if ~isfield(hdr, 'EEG')
    if isfield(hdr, 'nbchan') && isfield(hdr, 'pnts') && isfield(hdr, 'trials')
        hdr = struct('EEG', hdr);
    else
        error('Invalid EEGLAB .set file: missing EEG structure.');
    end
end
% Add some information
hdr.isRaw = (~isfield(hdr.EEG, 'epoch') || isempty(hdr.EEG.epoch)) && (isfield(hdr.EEG, 'data') && ~isempty(hdr.EEG.data));
nChannels = hdr.EEG.nbchan;
nTime     = hdr.EEG.pnts;
nEpochs   = hdr.EEG.trials;

% === GET TIME ===
% if isfield(hdr.EEG, 'times') && ~isempty(hdr.EEG.times)   % Disabled the use of "times" because it was not always in milliseconds (2-Nov-2022)
% hdr.Time = hdr.EEG.times ./ 1000;
if isfield(hdr.EEG, 'srate') && ~isempty(hdr.EEG.srate)
    hdr.Time = hdr.EEG.xmin + (0:nTime-1) ./ hdr.EEG.srate;
else
    hdr.Time = linspace(hdr.EEG.xmin, hdr.EEG.xmax, nTime);
    if (nTime > 1)
        hdr.EEG.srate = 1 ./ (hdr.Time(2) - hdr.Time(1));
    else
        hdr.EEG.srate = 1000;
    end
end

% ===== LIST BAD TRIALS =====
if ~hdr.isRaw
    % Get accepted/rejected trials (with methods ICA, SIG, ELEC)
    iRejectedTrials = [];
    if isfield(hdr.EEG, 'reject')
        % Epochs rejected by ICA criteria
        if isfield(hdr.EEG.reject, 'icareject')
            iRejectedTrials = [iRejectedTrials, hdr.EEG.reject.icareject];
        end
        % Epochs rejected by single-channel criteria
        if isfield(hdr.EEG.reject, 'sigreject')
            iRejectedTrials = [iRejectedTrials, hdr.EEG.reject.sigreject];
        end
        % Epochs rejected by single-channel criteria
        if isfield(hdr.EEG.reject, 'elecreject')
            iRejectedTrials = [iRejectedTrials, hdr.EEG.reject.elecreject];
        end
    end
    iGoodTrials = setdiff(1:nEpochs, iRejectedTrials);

    % Remove trials that have at least one 'bad' value in its events list
    iBadTrials = [];
    if isfield(hdr.EEG.epoch(1), 'eventbad')
        for iTrial = 1:length(hdr.EEG.epoch)
            if (iscell(hdr.EEG.epoch(iTrial).eventbad) && any([hdr.EEG.epoch(iTrial).eventbad{:}])) || ...
               (~iscell(hdr.EEG.epoch(iTrial).eventbad) && hdr.EEG.epoch(iTrial).eventbad)
                iBadTrials(end+1) = iTrial;
            end
        end
        iGoodTrials = setdiff(iGoodTrials, iBadTrials);
    end
else
    iGoodTrials = 1;
end

% ===== GET RELEVANT CONDITIONS =====
epochNames = [];
isAllEmpty = 1;
if ~hdr.isRaw && isfield(hdr.EEG, 'event') && ~isempty(hdr.EEG.event)
    % Each trial is classified with many criteria
    % Need to ask the user along which creteria the trials should be classified
    % Get all fields of the 'event' structure
    listParam = fieldnames(hdr.EEG.event(1));
    % Remove entries that are not conditions of the events
    %listParam = setdiff(listParam, {'type','latency','urevent','obs','bad','badChan','epoch'});
    listParam = setdiff(listParam, {'urevent','obs','bad','badChan','epoch'});
    % Convert all the events to strings
    for iParam = 1:length(listParam)
        hdr.EEG.event = ConvertEventToStr(hdr.EEG.event, listParam{iParam});
        hdr.EEG.epoch = ConvertEventToStr(hdr.EEG.epoch, ['event' listParam{iParam}]);
    end
    % Keep only parameters for which the value varies in the valid trials
    iParam = 1;
    paramValues = {};
    while (iParam <= length(listParam))
        % Get all the unique values
        tmpValues = {hdr.EEG.event.(listParam{iParam})};
        % If char and not all the values are the same
%         if ~iscell(tmpValues{1}) && ~all(cellfun(@(c)isequal(c,tmpValues{1}), tmpValues))
        if ischar(tmpValues{1}) && ~all(cellfun(@(c)isequal(c,tmpValues{1}), tmpValues))
            % Latency: keep the native order
            if isequal(listParam{iParam}, 'latency')
                [tmp,I,J] = unique(tmpValues);
                paramValues{end + 1} = tmpValues(sort(I));
            % Else: sort in alphabetical/numerical order
            else
                paramValues{end + 1} = unique(tmpValues);
            end
            iParam = iParam + 1;
        % Else remove parameter
        else
            listParam(iParam) = [];
        end
    end

    % If there are different types of events that can be used to classify the epochs
    if (length(listParam) >= 1) && (ImportOptions.DisplayMessages || ~isempty(ImportOptions.EventsTypes))
        % Ask the user to select the parameters to be compared
        if ImportOptions.DisplayMessages
            ParamSelected = gui_show_dialog('Conditions selection', @panel_eeglab_cond, 1, [], listParam, paramValues);
        % Use the list of event types passed in input
        else
            % Get the list of parameters in input
            evtSplit = strtrim(str_split(ImportOptions.EventsTypes, ','));
            % Check that the types are available in the file
            iNotFound = find(~ismember(evtSplit, listParam));
            if ~isempty(iNotFound)
                disp(['BST> Error: Some of the event types in input where not found in the file: ', sprintf('%s ', evtSplit{iNotFound})]);
                evtSplit(iNotFound) = [];
            end
            % Get the condition name for each epoch
            ParamSelected = [];
            if ~isempty(evtSplit)
                % Find the selected event types
                [tmp, I, J] = intersect(evtSplit, listParam);
                % Get the possible combinations of values
                ParamSelected = panel_eeglab_cond('GetConditionCombinations', listParam(J), paramValues(J));
                params = listParam(J);
                % Build the condition name for each epoch (see panel_eeglab_cond.m)
                for iCond = 1:length(ParamSelected)
                    strCondName = '';
                    for iParam = 1:length(params)
                        if (iParam ~= 1)
                            strCondName = [strCondName '_'];
                        end
                        if ischar(ParamSelected(iCond).(params{iParam}))
                            tmpStr = strrep(file_standardize(ParamSelected(iCond).(params{iParam})), '_', '-');
                            strCondName  = [strCondName, params{iParam}, tmpStr];
                        else
                            strCondName  = [strCondName, params{iParam}, sprintf('%d', ParamSelected(iCond).(params{iParam}))];
                        end
                    end
                    ParamSelected(iCond).Name = file_standardize(strCondName);
                end
            end
        end
        % Build the epochs names
        epochNames = cell(1, nEpochs);
        % Process parameters selection
        if ~isempty(ParamSelected)
            paramsList = setdiff(fieldnames(ParamSelected), 'Name');
            % Create FileName in which this file should be saved
            for iTrial = 1:nEpochs
                epoch = hdr.EEG.epoch(iTrial);
                % Find in which condition should be classified this epoch
                isCondFound = 0;
                iCond = 1;
                while ~isCondFound && (iCond <= length(ParamSelected))
                    isOk = 1;
                    iParam = 1;
                    while (isOk && (iParam <= length(paramsList)))
                        % If value for targ parameter does not match
                        if isequal(paramsList{iParam}, 'latency')
                            isMatch = isequal(hdr.EEG.event(epoch.event(1)).latency, ParamSelected(iCond).(paramsList{iParam}));
                        elseif iscell(epoch.(['event', paramsList{iParam}]))
                            isMatch = any(cellfun(@(c)isequal(c,ParamSelected(iCond).(paramsList{iParam})), epoch.(['event', paramsList{iParam}])));
                        else
                            isMatch = isequal(epoch.(['event', paramsList{iParam}]), ParamSelected(iCond).(paramsList{iParam}));
                        end
                        if isMatch
                            iParam = iParam + 1;
                        else
                            isOk = 0;
                        end
                    end
                    if isOk
                        isCondFound = 1;
                    else
                        iCond = iCond + 1;
                    end
                end
                % Build new filename with the found condition
                if isCondFound
                    epochNames{iTrial} = ParamSelected(iCond).Name;
                else
                    epochNames{iTrial} = '';
                end
            end
            isAllEmpty = all(cellfun(@isempty, epochNames));
        % If no parameters were selected
        else
            epochNames = repmat({''}, 1, nEpochs);
            isAllEmpty = 1;
        end
    end
end

%% ===== GET DATA SOURCE =====
% EEG.data
if isfield(hdr.EEG, 'data') && ~isempty(hdr.EEG.data)
    if isfield(hdr, hdr.EEG.data)
        EEGDATA = hdr.(hdr.EEG.data);
    else
        EEGDATA = hdr.EEG.data;
    end
% EEGDATA
elseif isfield(hdr, 'EEGDATA') && ~isempty(hdr.EEGDATA)
    EEGDATA = hdr.EEGDATA;
% EEGDATA.datfile
elseif isfield(hdr.EEG, 'datfile') && ~isempty(hdr.EEG.datfile)
    EEGDATA = hdr.EEG.datfile;
% Default data file: use the same file name than for .SET file, with .DAT/.FDT extension
else
    [fPath, fBase, fExt] = bst_fileparts(DataFile);
    EEGDATA = bst_fullfile(fPath, [fBase, '.dat']);
    if ~file_exist(EEGDATA)
        EEGDATA = bst_fullfile(fPath, [fBase, '.fdt']);
    end
end
% In case of attached binary file
if ischar(EEGDATA)
    % Check binary file existence
    if ~file_exist(EEGDATA)
        % Try with the same name as the .set file
        if file_exist(strrep(DataFile, '.set', '.fdt'))
            EEGDATA = strrep(DataFile, '.set', '.fdt');
        else
            % Try with adding a full path
            [fPath, fBase, fExt] = bst_fileparts(EEGDATA);
            EEGDATA = bst_fullfile(bst_fileparts(DataFile), [fBase, fExt]);
            % File is not accessible
            if ~file_exist(EEGDATA)
                error(['EEGLAB binary file does not exist: ', EEGDATA]);
            end
        end
    end
    % Save correct filename
    hdr.EEG.data = EEGDATA;
    % Remove EEGDATA field
    if isfield(hdr, 'EEGDATA')
        hdr = rmfield(hdr, 'EEGDATA');
    end
else
    hdr.EEGDATA = EEGDATA;
end

%% ===== FILL STRUCTURE =====
% Initialize returned file structure
sFile = db_template('sfile');
% Add information read from header
sFile.filename   = DataFile;
sFile.fid        = [];
sFile.format     = 'EEG-EEGLAB';
sFile.device     = 'EEGLAB';
sFile.byteorder  = 'l';
% Properties of the recordings
if (nTime == 1)
    sFile.prop.times = hdr.Time(1) + [0, 1./hdr.EEG.srate];
else
    sFile.prop.times = [hdr.Time(1), hdr.Time(end)];
end
sFile.prop.sfreq = hdr.EEG.srate;
sFile.prop.nAvg  = 1;
sFile.header = hdr;
% Channel file
if ImportOptions.DisplayMessages
    isFixUnits = [];
elseif (ImportOptions.ChannelAlign >= 1)
    isFixUnits = 1;
else
    isFixUnits = 0;
end
ChannelMat = in_channel_eeglab_set(hdr, isFixUnits);

% === EPOCHS ===
for i = 1:nEpochs
    if ~isempty(epochNames) && ~isempty(epochNames{i})
        sFile.epochs(i).label = epochNames{i};
        sFile.epochs(i).select  = 1;
    elseif isAllEmpty
        sFile.epochs(i).label = sprintf('%s (#%d)', hdr.EEG.setname, i);
        sFile.epochs(i).select  = 1;
    else
        sFile.epochs(i).select  = 0;
    end
    sFile.epochs(i).times   = sFile.prop.times;
    sFile.epochs(i).nAvg    = 1;
    sFile.epochs(i).bad     = ~ismember(i, iGoodTrials);
    % Bad channels
    sFile.epochs(i).channelflag = ones(nChannels, 1);
    if ~hdr.isRaw && isfield(hdr.EEG, 'epoch') && ~isempty(hdr.EEG.epoch) && isfield(hdr.EEG.epoch(1), 'eventbadChan')
        % Get all the bad channels for that epoch
        eventbadChan = hdr.EEG.epoch(iTrial).eventbadChan;
        if iscell(eventbadChan)
            iBadChan = [];
            for j = 1:length(eventbadChan)
                if ~isempty(eventbadChan{j})
                    iBadChan = [iBadChan, str2num(eventbadChan{j})];
                end
            end
        else
            iBadChan = str2num(eventbadChan);
        end
        % Report the bad channels in the ChannelFlag array
        sFile.epochs(i).channelflag(iBadChan) = -1;
    end
end
% Global channel flag
if (nEpochs == 1)
    sFile.channelflag = sFile.epochs(1).channelflag;
    sFile.epochs = [];
else
    sFile.channelflag = ones(nChannels, 1);
end

% === EVENTS ====
if isfield(hdr.EEG, 'event') && ~isempty(hdr.EEG.event) && isfield(hdr.EEG.event, 'type') % && hdr.isRaw
    % Get event types
    intTypes = [];
    if ischar(hdr.EEG.event(1).type)
        listTypes = unique({hdr.EEG.event.type});
    elseif isnumeric(hdr.EEG.event(1).type)
        intTypes = unique([hdr.EEG.event.type]);
        listTypes = cell(1, length(intTypes));
        for iType = 1:length(intTypes)
            listTypes{iType} = num2str(intTypes(iType));
        end
    else
        return;
    end
    % Initialize structure
    events = repmat(db_template('event'), [1, length(listTypes)]);
    % Process each event type
    for iEvt = 1:length(listTypes)
        % Get all the event occurrences
        if ~isempty(intTypes)
            listOcc = find([hdr.EEG.event.type] == intTypes(iEvt));
        else
            listOcc = find(strcmpi({hdr.EEG.event.type}, listTypes{iEvt}));
        end
        % Get event label
        events(iEvt).label = listTypes{iEvt};
        % If no occurrences: skip
        if isempty(listOcc)
            continue;
        end
        % Get epochs indices
        if ~hdr.isRaw && (isfield(hdr.EEG.event(listOcc(1)), 'epoch') && ~isempty(hdr.EEG.event(listOcc(1)).epoch))
            events(iEvt).epochs = [hdr.EEG.event(listOcc).epoch];
        else
            events(iEvt).epochs = ones(1, length(listOcc));
        end
        % Get samples
        if isfield(hdr.EEG.event(listOcc(1)), 'latency')
            allSmp = {hdr.EEG.event(listOcc).latency};
        elseif isfield(hdr.EEG.event(listOcc(1)), 'sample')
            allSmp = {hdr.EEG.event(listOcc).sample};
        else
            disp(['EEGLAB> Missing fields "latency" or "sample" in event "', hdr.EEG.event(listOcc(1)).type, '".']);
        end
        % Convert to values if available as strings
        iChar = find(cellfun(@ischar, allSmp));
        if ~isempty(iChar)
            allSmp(iChar) = cellfun(@str2num, allSmp(iChar), 'UniformOutput', 0);
        end
        % Remove empty latencies
        iEmpty = find(cellfun(@isempty, allSmp));
        if ~isempty(iEmpty)
            [allSmp{iEmpty}] = deal(1);
        end
        samples = round([allSmp{:}]);
        % Add durations if there are more than one sample
        if isfield(hdr.EEG.event(listOcc), 'duration') && ~ischar(hdr.EEG.event(listOcc(1)).duration)
            allDur = [hdr.EEG.event(listOcc).duration];
            if any(allDur > 1) && (length(samples) == length(allDur))
                samples(2,:) = samples + allDur;
            end
        end
        % For epoched files: convert events to samples local to each epoch
        if ~hdr.isRaw
            nSamples = round((sFile.prop.times(2) - sFile.prop.times(1)) .* sFile.prop.sfreq) + 1;
            samples = samples - (events(iEvt).epochs - 1) * nSamples + sFile.prop.times(1) * sFile.prop.sfreq - 1;
        end
        % Compute times
        events(iEvt).times = samples ./ sFile.prop.sfreq;
        % Additional fields
        events(iEvt).channels = [];
        events(iEvt).notes    = [];
    end
    % Save structure
    sFile.events = events;
end

end

%% ===== CONVERT EVENTS TO STRING =====
function s = ConvertEventToStr(s, param)
    % For each event
    for i = 1:length(s)
        % Double values: convert to strings
        if isnumeric(s(i).(param))
            s(i).(param) = val2str(s(i).(param));
        % Cell array of doubles
        elseif iscell(s(i).(param))
            for iCell = 1:length(s(i).(param))
                if isnumeric(s(i).(param){iCell})
                    s(i).(param){iCell} = val2str(s(i).(param){iCell});
                end
            end
        end
    end
end

function str = val2str(val)
    str = '';
    for iVal = 1:length(val)
        if (str > 1)
            str = [str ' '];
        end
        str = [str, num2str(val(iVal))];
    end
end


function feature_map = get_cnn_layers(im, fparams, gparams)

% Get layers from a cnn.

if size(im,3) == 1
    im = repmat(im, [1 1 3]);
end

im_sample_size = size(im);

%preprocess the image
if ~isequal(im_sample_size(1:2), fparams.net.meta.normalization.imageSize(1:2))
    im = imresize(single(im), fparams.net.meta.normalization.imageSize(1:2));
else
    im = single(im);
end

% Normalize with average image
im = bsxfun(@minus, im, fparams.net.meta.normalization.averageImage);

if fparams.use_gpu
    cnn_feat = vl_simplenn(fparams.net,im,[],[],'CuDNN',true, 'Mode', 'test');
else
    cnn_feat = vl_simplenn(fparams.net, im, [], [], 'Mode', 'test');
end

feature_map = cell(1,1,length(fparams.output_layer));

for k = 1:length(fparams.output_layer)
    % Move data to CPU if using GPU
%     if fparams.use_gpu
%         cnn_feat(fparams.output_layer(k) + 1).x = gather(cnn_feat(fparams.output_layer(k) + 1).x);
%     end

    if fparams.use_gpu
        if fparams.downsample_factor(k) == 1
            temp_feat = cnn_feat(fparams.output_layer(k) + 1).x(fparams.start_ind(k,1):fparams.end_ind(k,1), fparams.start_ind(k,2):fparams.end_ind(k,2), :, :);
            if fparams.return_gpu
                feature_map{k} = temp_feat;
            else
                feature_map{k} = gather(temp_feat);
            end
        else
            temp_feat = average_feature_region(cnn_feat(fparams.output_layer(k) + 1).x(fparams.start_ind(k,1):fparams.end_ind(k,1), fparams.start_ind(k,2):fparams.end_ind(k,2), :, :), fparams.downsample_factor(k));
            if fparams.return_gpu
                feature_map{k} = temp_feat;
            else
                feature_map{k} = gather(temp_feat);
            end
        end
    else
        if fparams.downsample_factor(k) == 1
            feature_map{k} = cnn_feat(fparams.output_layer(k) + 1).x(fparams.start_ind(k,1):fparams.end_ind(k,1), fparams.start_ind(k,2):fparams.end_ind(k,2), :, :);
        else
            feature_map{k} = average_feature_region(cnn_feat(fparams.output_layer(k) + 1).x(fparams.start_ind(k,1):fparams.end_ind(k,1), fparams.start_ind(k,2):fparams.end_ind(k,2), :, :), fparams.downsample_factor(k));
        end
    end
end


function [E, wE] = nfgCompareVolumeTrueError(fgGS, fgSS, g_radius,vR,phantomDir)
%Compare volume of two fiber groups using trueError program
%
%   [E, wE] = nfgCompareVolumeTrueError(fgGS, fgSS, g_radius,vR,phantomDir)
%
% Assumes that fgGS is the gold standard group and fgSS is the selected
% fibers that match the gold standard from the test group.  g_radius is the
% radius of the gold standard fibers and vR is a vector of radii that might
% be the thickness of the test fibers.
%
%
% NOTES:

% Let's see how trueError compares these fibers
tempFiberDir = nfgGetName('tempFiberDir',phantomDir);
ctrparamsFile = nfgGetName('ctrparamsFile',phantomDir);
noisyImg = nfgGetName('noisyImg',phantomDir);
bvalsFile = nfgGetName('bvalsFile',phantomDir);
bvecsFile = nfgGetName('bvecsFile',phantomDir);
b0File = nfgGetName('b0File',phantomDir);
tensorsFile = nfgGetName('tensorsFile',phantomDir);
wmROIFile = nfgGetName('wmROIFile',phantomDir);
gmROIFile = nfgGetName('gmROIFile',phantomDir);

% XXX special check to find fibers less than 5 points in gold standard
fiberLen = cellfun('size',fgGS.fibers,2);
lt5 = fiberLen<5;
if sum(lt5)>0
    disp(['Warning there are ' num2str(sum(lt5)) ' fibers with less than 5 pts.']);
    fgGS.fibers = fgGS.fibers(~lt5);
end

mkdir(tempFiberDir);
mtrExportFibers(fgSS,fullfile(tempFiberDir,'ss.pdb'));
mtrExportFibers(fgGS,fullfile(tempFiberDir,'gs.pdb'));

% Convert to SBfloat
argParams = [' -i ' ctrparamsFile];
argD_SS = [' -p ' fullfile(tempFiberDir,'ss_0.SBfloat')];
argInput = [' ' fullfile(tempFiberDir,'ss.pdb')];
argThresh = [' --thresh ' num2str(length(fgSS.fibers)) ' --seq '];
cmd = ['contrack_score' argParams argD_SS argThresh argInput];
%disp(cmd);
[s,r] = system(cmd);
argD_GS = [' -p ' fullfile(tempFiberDir,'gs_0.SBfloat')];
argInput = [' ' fullfile(tempFiberDir,'gs.pdb')];
argThresh = [' --thresh ' num2str(length(fgGS.fibers)) ' --seq '];
cmd = ['contrack_score' argParams argD_GS argThresh argInput];
%disp(cmd);
[s,r] = system(cmd);
% Run true error on gold
argD_GS = [' -d ' fullfile(tempFiberDir,'gs_0.SBfloat')];
argR = [' -r ' noisyImg];
argVal = [' --val ' bvalsFile];
argVec = [' --vec ' bvecsFile];
arg0 = [' -0 ' b0File];
argG = [' -g ' gmROIFile];
argM = [' -m ' wmROIFile];
argT = [' --ten ' tensorsFile];
argGroupSize = ' -v 2';
b0 = niftiRead(b0File);
argSubSize = [' -s 0,' num2str(b0.dim(1)-1) ',0,' num2str(b0.dim(2)-1) ',0,' num2str(b0.dim(3)-1)];
argW = ' -w 0';
argDiameter = [' --diameter ' num2str(max(g_radius)*2)];
fracGoldFile = fullfile(tempFiberDir,'fgs.nii.gz');
argFractionFile = [' --fraction ' fracGoldFile];
cmd = ['trueError' argR argD_GS argVal argVec arg0 argG argM argT argGroupSize argSubSize argW argDiameter argFractionFile];
%disp(cmd);
[s,r] = system(cmd);
fracG = niftiRead(fracGoldFile);

% Search for the right radius to compare the selection to the gold
argD_SS = [' -d ' fullfile(tempFiberDir,'ss_0.SBfloat')];
fracImgs = zeros(size(repmat(b0.data,[1 1 1 length(vR)])));
E = zeros(size(vR));
for rr=1:length(vR)
    strD = num2str(vR(rr)*2);
    disp(['Comparing trueError volume using test diameter ' strD ' ...']);
    argDiameter = [' --diameter ' strD];
    fracFile = fullfile(tempFiberDir,['fss' strD '.nii.gz']);
    argFractionFile = [' --fraction ' fracFile];
    cmd = ['trueError' argR argD_SS argVal argVec arg0 argG argM argT argGroupSize argSubSize argW argDiameter argFractionFile];
    %disp(cmd);
    [s,r] = system(cmd);
    ni = niftiRead(fracFile);
    fracImgs(:,:,:,rr) = ni.data;
    %E(rr) = sum(abs(ni.data(:)-fracG.data(:)))/sum(fracG.data(:)) * 100;
    E(rr) = abs((sum(ni.data(:))-sum(fracG.data(:)))/sum(fracG.data(:)) * 100);
end
wE = sum(fracG.data(:));
% Cleanup the temp space
rmdir(tempFiberDir,'s');

return;


function brs = ssr_brs(X,phi,x0,d,irs,conf)
%SSR_BRS binaural room scanning (BRS) set for use with the SoundScape Renderer
%
%   Usage: brs = ssr_brs(X,phi,x0,d,irs,conf)
%
%   Input parameters:
%       X       - listener position / m
%       phi     - azimuthal head orientation / rad
%       x0      - secondary sources
%       d       - corresponding driving signals
%       irs     - impulse response data set for the second sources
%       conf    - configuration struct (see SFS_config)
%
%   Output parameters:
%       brs     - conf.N x 2*nangles matrix containing all impulse responses
%                 (2 channels) for every angle of the BRS set
%
%   SSR_BRS(X,phi,x0,d,irs,conf) prepares a BRS set for the given secondary
%   sources and its driving signals for the given listener position.
%   One way to use this BRS set is using the SoundScapeRenderer (SSR), see
%   http://spatialaudio.net/ssr/
%
%   See also: ssr_brs_wfs, ssr_brs_nfchoa, ir_generic

%*****************************************************************************
% The MIT License (MIT)                                                      *
%                                                                            *
% Copyright (c) 2010-2019 SFS Toolbox Developers                             *
%                                                                            *
% Permission is hereby granted,  free of charge,  to any person  obtaining a *
% copy of this software and associated documentation files (the "Software"), *
% to deal in the Software without  restriction, including without limitation *
% the rights  to use, copy, modify, merge,  publish, distribute, sublicense, *
% and/or  sell copies of  the Software,  and to permit  persons to whom  the *
% Software is furnished to do so, subject to the following conditions:       *
%                                                                            *
% The above copyright notice and this permission notice shall be included in *
% all copies or substantial portions of the Software.                        *
%                                                                            *
% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
% IMPLIED, INCLUDING BUT  NOT LIMITED TO THE  WARRANTIES OF MERCHANTABILITY, *
% FITNESS  FOR A PARTICULAR  PURPOSE AND  NONINFRINGEMENT. IN NO EVENT SHALL *
% THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER *
% LIABILITY, WHETHER  IN AN  ACTION OF CONTRACT, TORT  OR OTHERWISE, ARISING *
% FROM,  OUT OF  OR IN  CONNECTION  WITH THE  SOFTWARE OR  THE USE  OR OTHER *
% DEALINGS IN THE SOFTWARE.                                                  *
%                                                                            *
% The SFS Toolbox  allows to simulate and  investigate sound field synthesis *
% methods like wave field synthesis or higher order ambisonics.              *
%                                                                            *
% https://sfs.readthedocs.io                            sfstoolbox@gmail.com *
%*****************************************************************************

%% ===== Checking of input  parameters ==================================
nargmin = 6;
nargmax = 6;
narginchk(nargmin,nargmax);
isargposition(X);
isargsecondarysource(x0);
isargmatrix(d);
isargscalar(phi);
isargstruct(conf);

%% ===== Configuration ===================================================
N = conf.N;                       % Target length of BRIR impulse responses
angles = rad(conf.ir.brsangles);  % Angles for the BRIRs
showprogress = conf.showprogress; % Progress bar

%% ===== Computation =====================================================
nangles = length(angles);
% Initial values
brs = zeros(N,2*nangles);
% Generate a BRS set for all given angles
for ii = 1:nangles
    % Progress bar
    if showprogress, progress_bar(ii,nangles); end
    % Compute BRIR for the desired driving signals
    brs(:,(ii-1)*2+1:ii*2) = ...
        ir_generic(X,angles(ii)+phi,x0,d,irs,conf);
end


% LFCalDispEstPoses - Visualize pose estimates associated with a calibration info file
%
% Usage:
%     LFCalDispEstPoses( InputPath, CalOptions, DrawFrameSizeMult, BaseColour )
%
% Draws a set of frames, one for each camera pose, in the colour defined by BaseColour. All inputs
% except InputPath are optional. Pass an empty array "[]" to omit a parameter. See
% LFUtilCalLensletCam for example usage.
%
% Inputs:
%
%     InputPath : Path to folder containing CalInfo file.
%
%     [optional] CalOptions : struct controlling calibration parameters
%                 .CalInfoFname : Name of the file containing calibration estimate; note that this
%                                 parameter is automatically set in the CalOptions struct returned
%                                 by LFCalInit
%
%     [optional] DrawFrameSizeMult : Multiplier on the displayed frame sizes
%
%     [optional] BaseColour : RGB triplet defining a base drawing colour, RGB values are in the
%                             range 0-1
%
%
% User guide: LFToolbox.pdf
% See also:  LFUtilCalLensletCam

% Copyright (c) 2013-2020 Donald G. Dansereau

function LFCalDispEstPoses( InputPath, CalOptions, DrawFrameSizeMult, BaseColour )

%---Defaults---
CalOptions = LFDefaultField( 'CalOptions', 'CalInfoFname', 'CalInfo.json' );
DrawFrameSizeMult = LFDefaultVal( 'DrawFrameSizeMult', 1 );
BaseColour = LFDefaultVal( 'BaseColour', [1,1,0] );

%---
Fname = fullfile(CalOptions.CalInfoFname);
EstCamPosesV = LFStruct2Var( LFReadMetadata(fullfile(InputPath, Fname)), 'EstCamPosesV' );

% Establish an appropriate scale for the drawing
AutoDrawScale = EstCamPosesV(:,1:3);
AutoDrawScale = AutoDrawScale(:);
AutoDrawScale = (max(AutoDrawScale) - min(AutoDrawScale))/10;

%---Draw cameras---
BaseFrame = ([0 1 0 0 0 0; 0 0 0 1 0 0; 0 0 0 0 0 1]);
DrawFrameMed = [AutoDrawScale * DrawFrameSizeMult * BaseFrame; ones(1,6)];
DrawFrameLong = [AutoDrawScale*3/2 * DrawFrameSizeMult * BaseFrame; ones(1,6)];

for( PoseIdx = 1:size(EstCamPosesV, 1) )
    CurH = eye(4);
    CurH(1:3,1:3) = rodrigues( EstCamPosesV(PoseIdx,4:6) );
    CurH(1:3,4) = EstCamPosesV(PoseIdx,1:3);

    CurH = CurH^-1;

    CamFrame = CurH * DrawFrameMed(:,1:4);
    plot3( CamFrame(1,:), CamFrame(2,:), CamFrame(3,:), '-', 'color', BaseColour.*0.5, 'LineWidth',2 );
    hold on

    CamFrame = CurH * DrawFrameLong(:,5:6);
    plot3( CamFrame(1,:), CamFrame(2,:), CamFrame(3,:), '-', 'color', BaseColour, 'LineWidth',2 );
end

axis equal
grid on
xlabel('x [m]');
ylabel('y [m]');
zlabel('z [m]');
title('Estimated camera poses');


function [keypointsAll,bboxAll] = backprojectKeypoints(expidx)

fprintf('backprojectKeypoints()\n');

% load annolist with full size images
[annolistOrig, imgidxs] = getAnnolist(expidx);
for imgidx = 1:length(annolistOrig)
    annolistOrig(imgidx).annorect = [];
end

p = rcnn_exp_params(expidx);
load(p.testGT);
if (~exist('annolist','var'))
    annolist = single_person_annolist;
end
annolist_crops = annolist;

[~,parts] = util_get_parts24();

% load bboxes
bboxAll = getBBoxAll(expidx,parts,length(annolist_crops));
imgidxs_orig = zeros(length(bboxAll),1);
jidxs = [1:6 9:length(bboxAll(1).det)];

fprintf('backproject bboxes()\n');
for imgidx = 1:length(bboxAll)
    fprintf('.');
    [~,name] = fileparts(annolist_crops(imgidx).image.name);
    load([fileparts(p.testGT) '/T_' name(3:end)],'T');
    imgidxs_orig(imgidx) = str2double(name(3:7));

    det = bboxAll(imgidx).det;
    for jidx = jidxs
        detNew1 = ([det{jidx}(:,1:2) ones(size(det{jidx},1),1)]*T');
        detNew2 = ([det{jidx}(:,3:4) ones(size(det{jidx},1),1)]*T');
        det{jidx}(:,1:4) = [detNew1(:,1:2) detNew2(:,1:2)];
        %             figure(101); clf; imagesc(imread(annolist(imgidx_orig).image.name)); axis equal; hold on;
        %             plot(det{jidx}(:,1),det{jidx}(:,2),'ro','MarkerFaceColor','r','MarkerEdgeColor','k','MarkerSize',5);
    end

    bboxAll(imgidx).det = det;
    bboxAll(imgidx).imgname = annolistOrig(imgidxs_orig(imgidx)).image.name;
    if (~mod(imgidx, 100))
        fprintf(' %d/%d\n',imgidx,length(bboxAll));
    end
end
fprintf(' done\n');

% merge detections
imgidxs_merged = imgidxs_orig(1);
imgidxs_merged_rel = 1;
for imgidx = 2:length(bboxAll)
    if (imgidxs_orig(imgidx) == imgidxs_merged(end))
        for jidx = jidxs
            bboxAll(imgidxs_merged_rel(end)).det{jidx} = [bboxAll(imgidxs_merged_rel(end)).det{jidx}; bboxAll(imgidx).det{jidx}];
        end
    else
        imgidxs_merged = [imgidxs_merged; imgidxs_orig(imgidx)];
        imgidxs_merged_rel = [imgidxs_merged_rel; imgidx];
    end
end
bboxAll = bboxAll(imgidxs_merged_rel);

fprintf('nms()\n');
for imgidx = 1:length(bboxAll)
    fprintf('.');
    for jidx = jidxs
        keep = nms(bboxAll(imgidx).det{jidx}, 0.5);
        bboxAll(imgidx).det{jidx} = bboxAll(imgidx).det{jidx}(keep, :);
    end
    if (~mod(imgidx, 100))
        fprintf(' %d/%d\n',imgidx,length(bboxAll));
    end
end
fprintf(' done\n');

for imgidx = 1:length(bboxAll)
    keypointsAll(imgidx).imgname = bboxAll(imgidx).imgname;
    keypointsAll(imgidx).det = cell(16,1);
end

% convert to keypoints
for imgidx = 1:length(bboxAll)
    points = nan(16,2);
    for jidx = jidxs
        det = bboxAll(imgidx).det{jidx};
        x = mean(det(:,[1 3]),2);
        y = mean(det(:,[2 4]),2);
        keypointsAll(imgidx).det{jidx,:} = [[x y] det(:,5)];
        [val,idx] = max(det(:,5));
        points(jidx,:) = [x(idx) y(idx)];
    end
%     figure(101); clf; imagesc(imread(keypointsAll(imgidx).imgname)); axis equal; hold on;
%     plot(points(:,1),points(:,2),'ro','MarkerFaceColor','r','MarkerEdgeColor','k','MarkerSize',5);
%     set(gca,'Ydir','reverse');
end

path = fileparts(p.evalTest);
fnameKeypoints = [path '/keypointsAll'];
save(fnameKeypoints, 'keypointsAll');

fnameBBox = [path '/bboxAll'];
save(fnameBBox, 'bboxAll');
end


function write_off(filename, pnt, plc)

% WRITE_OFF writes a set of geometrical planar forms (called piecewise linear complex, PLC)
% to an ascii *.off file, which is a file format created by Princeton Shape Benchmark
%
% Use as
%   write_stl(filename, pnt, tri)
%
% See also READ_OFF

% Copyright (C) 2010, Cristiano Micheli
%
% This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip
% 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: write_off.m 2212 2010-11-27 11:55:07Z roboos $

nedges = 0;
fid  = fopen(filename, 'wb');
npnt = size(pnt,1);
nplc = size(plc,1);

fprintf(fid, 'OFF\n');
fprintf(fid, '%d %d %d\n',npnt,nplc,nedges);
for i=1:npnt
  fprintf(fid, '%f %f %f\n', pnt(i,1), pnt(i,2), pnt(i,3));
end
for i=1:nplc
  str = '%d ';
  nvert = size(plc(i,:),2);
  str2 = 'nvert,';
  for j=1:nvert
    str  = [str '%d '];
    str2 = [str2 'plc(i,' num2str(j) '),'];
  end
  str = [str '\n''']; str2 = str2(1:end-1);
  eval(['fprintf(fid,''' str ',' str2 ');']);
end

fclose(fid);


% removERP.m removes the averaged ERP from every single epoch of its corresponding epoched EEG dataset
%
% Input:
%
% EEG       - epoched EEG dataset
% ERP       - averaged ERP (ERPset)
%
% Output:
%
% EEG       - epoched EEG dataset
%
% IMPORTANT:
% - epoched EEG dataset must have assigned bins (e.g. processed by ERPLAB).
% - epoched EEG dataset and averaged ERP (ERPset) must have the same amount of channels, samples and sample rate.
% - the time-locked event must be at the same sample location in both epoched EEG dataset and averaged ERP (ERPset).
%
%
%
% *** This function is part of ERPLAB Toolbox ***
% Author: Javier Lopez-Calderon & Johanna Kreither
% Center for Mind and Brain
% University of California, Davis,
% Davis, CA
% July-August 2014

function EEG = removERP(EEG, ERP)

nepoch   = EEG.trials;
nchaneeg = EEG.nbchan;
npntseeg = EEG.pnts;
srateeeg = EEG.srate;
eegzerolatpos = find(EEG.times == 0,1,'first'); % catch zero-time locked code position,

nchanerp = ERP.nchan;
npntserp = ERP.pnts;
srateerp = ERP.srate;
erpzerolatpos = find(ERP.times == 0,1,'first'); % catch zero-time locked code position,

if nchaneeg~=nchanerp
      error('ERPLAB:error', 'EEG has %g channels and ERP has %g canales!', nchaneeg, nchanerp)
end
if npntseeg~=npntserp
      error('ERPLAB:error', 'EEG has %g points and ERP has %g points!', npntseeg, npntserp)
end
if srateeeg~=srateerp
      error('ERPLAB:error', 'EEG has a srate of %g sps and ERP has a srate of %g sps!', srateeeg, srateerp)
end
if eegzerolatpos~=erpzerolatpos
      error('ERPLAB:error', 'The time-locked event is not located at the same sample for EEG and ERP.')
end
for k=1:nepoch
      latenarray = EEG.epoch(k).eventlatency;
      biniarray  = EEG.epoch(k).eventbini;

      if iscell(latenarray)
            latenarray = cell2mat(latenarray);
      end

      indxtimelock = find(latenarray == 0,1,'first'); % catch zero-time locked code position,
      bini  = biniarray(indxtimelock);

      if isempty(bini)
            error('ERPLAB:error', 'Epoch %g does not habe a bin assigned (empty).', k)
      end
      if iscell(bini)
            bini = cell2mat(bini);
      end
      if bini<1
            error('ERPLAB:error', 'Epoch %g does not habe a bin assigned (-1).', k)
      end
      if length(bini)>1
            error('ERPLAB:error', 'Epoch %g has more than one bin assigned.', k)
      end

      EEG.data(:,:,k) = EEG.data(:,:,k) - ERP.bindata(:,:, bini);
end


function gX = whiteKernGradX(kern, X, X2)

% WHITEKERNGRADX Gradient of WHITE kernel with respect to input locations.
% FORMAT
% DESC computes the gradident of the white noise
% kernel with respect to the input positions where both the row
% positions and column positions are provided separately.
% ARG kern : kernel structure for which gradients are being
% computed.
% ARG x1 : row locations against which gradients are being computed.
% ARG x2 : column locations against which gradients are being computed.
% RETURN g : the returned gradients. The gradients are returned in
% a matrix which is numData2 x numInputs x numData1. Where numData1 is
% the number of data points in X1, numData2 is the number of data
% points in X2 and numInputs is the number of input
% dimensions in X.
%
% SEEALSO whiteKernParamInit, kernGradX, whiteKernDiagGradX
%
% COPYRIGHT : Neil D. Lawrence, 2004, 2005, 2006

% KERN

if nargin<3
    X2 = X;
end

gX = zeros(size(X2, 1), size(X2, 2), size(X, 1));


function [FluxCorrelations, PValues, TaxonomyInfo] = correlateFluxWithTaxonAbundance(abundancePath, fluxPath, infoFilePath, corrMethod)
% Part of the Microbiome Modeling Toolbox. This function calculates and
% plots the correlations between fluxes for one or more reactions of
% interest in a number of microbiome samples and the relative microbe
% abundance on different taxonomical levels in the same samples.
% The function should be used after running mgPipe to identify correlations
% between the computed metabolic profiles and specific taxa in the samples.
%
% USAGE
%
%     [FluxCorrelations, PValues, TaxonomyInfo] = correlateFluxWithTaxonAbundance(abundancePath, fluxPath, taxonomy, corrMethod)
%
% INPUTS:
%    abundancePath:     Path to the .csv file with the abundance data.
%                       Needs to be in same format as example file
%                       'cobratoolbox/papers/018_microbiomeModelingToolbox/examples/normCoverage.csv'
%     fluxPath:         Path to the .csv file with the fluxes for reactions
%                       of interest with sample IDs as rows and reaction
%                       IDs in microbiome community models as columns
%
% OPTIONAL INPUTS:
%    infoFilePath:      Path to the spreadsheet with the taxonomy information
%                       on organisms (default: AGORA_infoFile.xlsx)
%     corrMethod:       Method to compute the linear correlation
%                       coefficient. Allowed inputs: 'Pearson' (default),
%                       'Kendall', 'Spearman'.
%
% OUTPUTS:
%     FluxCorrelations: Structure with correlations between fluxes for each
%                       reaction and abundances on taxon levels
%     PValues:          p-values corresponding to each calculated
%                       correlation
%     TaxonomyInfo:     Taxonomical information on each taxon level
%
% .. Author: Almut Heinken, 03/2018
%                           10/2018:  changed input to location of the csv file with the
%                                     abundance data
%                           01/2020:  adapted to be suitable for pan-models, and
%                                     changed flux input to a csv file.

% read the csv file with the abundance data
abundance = readInputTableForPipeline(abundancePath);
if isnumeric(abundance{2, 1})
    abundance(:, 1) = [];
end
% adapt IDs if neccessary
abundance(1,2:end) = strrep(abundance(1,2:end),'-','_');

fluxes = readInputTableForPipeline(fluxPath);
fluxes(1,:) = strrep(fluxes(1,:),'microbiota_model_diet_','');
fluxes(1,:) = strrep(fluxes(1,:),'microbiota_model_samp_','');
fluxes(:,1) = strrep(fluxes(:,1),'microbiota_model_diet_','');
fluxes(:,1) = strrep(fluxes(:,1),'microbiota_model_samp_','');

% check if data is from same samples
if ~isempty(setdiff(fluxes(1,2:end),abundance(1,2:end)))
    fluxes=fluxes';
    % if it still does not match
    if ~isempty(setdiff(fluxes(1,2:end),abundance(1,2:end)))
        error('Sample IDs in abundance and flux files do not agree!')
    end
end

% load database
database=loadVMHDatabase;

fluxes(:,1)=strrep(fluxes(:,1),'EX_','');
fluxes(:,1)=strrep(fluxes(:,1),'(e)','');
fluxes(:,1)=strrep(fluxes(:,1),'[fe]','');
% for i=2:size(fluxes,1)
% fluxes{i,1}=database.metabolites{find(strcmp(database.metabolites(:,1),fluxes{i,1})),2};
% end

% Get the taxonomy information
if exist('infoFilePath','var')
    taxonomy = readInputTableForPipeline(infoFilePath);
else
    taxonomy = readInputTableForPipeline('AGORA_infoFile.xlsx');
end

if ~exist('corrMethod', 'var')  % Define correlation coefficient method if not entered
    corrMethod = 'Pearson';
end

% Calculate the abundance in each sample on all taxon levels
TaxonomyLevels = {
    'Phylum'
    'Class'
    'Order'
    'Family'
    'Genus'
    'Species'
};
% extract the list of entries on each taxonomical level and prepare the
% summarized abundance table
fprintf('Calculating the relative abundances on all taxon levels. \n')
for t = 1:size(TaxonomyLevels, 1)
    % find the columns corresponding to each taxonomy level and the list of
    % unique taxa
    taxonCol = find(strcmp(taxonomy(1, :), TaxonomyLevels{t}));
    % find and save all entries
    taxa = unique(taxonomy(2:end, taxonCol));
    % exclude unclassified entries
    taxa(strncmp('unclassified', taxa, taxonCol)) = [];
    TaxonomyLevels{t, 2} = taxa;
    for i = 1:length(taxa)
        SampleAbundance.(TaxonomyLevels{t}){1, i + 1} = taxa{i};
        for j = 2:size(abundance, 2)
            SampleAbundance.(TaxonomyLevels{t}){j, 1} = abundance{1, j};
            SampleAbundance.(TaxonomyLevels{t}){j, i + 1} = 0;
        end
    end
end

% Go through the abundance data and summarize taxon abundances for each
% strain in at least one sample

% Find the right column for the input data (strains, species,..)
if sum(strncmp(abundance(:,1),'pan',3))==size(abundance,1)-1
abundance(:,1)=regexprep(abundance(:,1),'pan','','once');
end
inputTaxa={};
for i=1:size(taxonomy,2)
    if ~isnumeric(taxonomy{2,i})
        taxa=strrep(taxonomy(:,i),' ','_');
        taxa=strrep(taxa,'.','_');
        taxa=strrep(taxa,'/','_');
        taxa=strrep(taxa,'-','_');
        taxa=strrep(taxa,'-','_');
        taxa=strrep(taxa,'[','');
        taxa=strrep(taxa,']','');
        taxa=strrep(taxa,')','');
        taxa=strrep(taxa,'__','_');
        if length(intersect(abundance(2:end,1),taxa))==size(abundance,1)-1
            inputTaxa=taxa;
            inputCol=i;
            break
        end
    end
end
if isempty(inputTaxa)
    error('Some taxa in the abundance file are not found in the taxonomy file!')
end

for i = 2:size(abundance, 2)
    for j = 2:size(abundance, 1)
        for t = 1:size(TaxonomyLevels, 1)
            % find the taxon for the current strain
            taxonCol = find(strcmp(taxonomy(1, :), TaxonomyLevels{t}));
            if taxonCol >= inputCol
                findTax = taxonomy(find(strcmp(abundance{j, 1}, inputTaxa)), taxonCol);
                if isempty(strfind(findTax{1}, 'unclassified'))
                    % find the taxon for the current strain in the sample abundance
                    % variable
                    findinSampleAbun = find(strcmp(findTax{1}, SampleAbundance.(TaxonomyLevels{t})(1, :)));
                    % sum up the relative abundance
                    if contains(version,'(R202') % for Matlab R2020a and newer
                        SampleAbundance.(TaxonomyLevels{t}){i, findinSampleAbun} = SampleAbundance.(TaxonomyLevels{t}){i, findinSampleAbun} + abundance{j, i};
                    else
                        SampleAbundance.(TaxonomyLevels{t}){i, findinSampleAbun} = SampleAbundance.(TaxonomyLevels{t}){i, findinSampleAbun} + str2double(abundance{j, i});
                    end
                end
            end
        end
    end
end
% remove the taxa not present in samples or only present in small abundances
for t = 1:size(TaxonomyLevels, 1)
    delArray = [];
    cnt = 1;
    for i = 2:size(SampleAbundance.(TaxonomyLevels{t}), 2)
        for j = 2:size(SampleAbundance.(TaxonomyLevels{t}), 1)
            abun(j - 1, 1) = SampleAbundance.(TaxonomyLevels{t}){j, i};
        end
        if sum(abun) < 0.005
            delArray(cnt, 1) = i;
            cnt = cnt + 1;
        end
    end
    SampleAbundance.(TaxonomyLevels{t})(:, delArray) = [];
end
% find the flux data for each reaction
fprintf('Calculating the correlations between fluxes and abundances. \n')
for i = 2:size(fluxes, 1)
    data = [];
    for m = 2:size(fluxes, 2)
        data(m - 1, 1) = str2double(string(fluxes{i, m}));
    end
    for t = 1:size(TaxonomyLevels, 1)
        FluxCorrelations.(TaxonomyLevels{t}){1, i} = fluxes{i, 1};
        PValues.(TaxonomyLevels{t}){1, i} = fluxes{i, 1};
        % find the abundance data for each taxon
        for j = 2:size(SampleAbundance.(TaxonomyLevels{t}), 2)
            FluxCorrelations.(TaxonomyLevels{t}){j, 1} = SampleAbundance.(TaxonomyLevels{t}){1, j};
            PValues.(TaxonomyLevels{t}){j, 1} = SampleAbundance.(TaxonomyLevels{t}){1, j};
            % find the abundance data for each sample
            dataTaxa = data;
            for k = 2:size(SampleAbundance.(TaxonomyLevels{t}), 1)
                % match with correct individual in flux table
                sampleInFluxes = find(strcmp(fluxes(1, :), SampleAbundance.(TaxonomyLevels{t}){k, 1}));
                dataTaxa(sampleInFluxes - 1, 2) = SampleAbundance.(TaxonomyLevels{t}){k, j};
            end
            % exclude NaNs
            dataTaxa(find(isnan(dataTaxa(:,1))),:)=[];
            dataTaxa(find(isnan(dataTaxa(:,2))),:)=[];
            % calculate the correlation with the given correlation coefficient method
            [RHO, PVAL] = corr(dataTaxa(:, 1), dataTaxa(:, 2), 'type', corrMethod);
            if isnan(RHO)
                RHO = 0;
            end
            if abs(RHO) < 0.0000000001
                RHO = 0;
            end
            FluxCorrelations.(TaxonomyLevels{t}){j, i} = RHO;
            PValues.(TaxonomyLevels{t}){j, i} = PVAL;
        end
    end
end

% remove entries that are only weak correlations and empty taxonomy entries
for t = 1:size(TaxonomyLevels, 1)
    cnt=1;
    delArray=[];
    for j=2:size(FluxCorrelations.(TaxonomyLevels{t}),2)
        if ~any(abs(cell2mat(FluxCorrelations.(TaxonomyLevels{t})(2:end,j))) > 0.3)
            delArray(cnt,1)=j;
            cnt=cnt+1;
        end
    end
    FluxCorrelations.(TaxonomyLevels{t})(:,delArray)=[];

    cnt=1;
    delArray=[];
    for j=2:size(FluxCorrelations.(TaxonomyLevels{t}),1)
        if ~any(abs(cell2mat(FluxCorrelations.(TaxonomyLevels{t})(j,2:end))) > 0.3)
            delArray(cnt,1)=j;
            cnt=cnt+1;
        end
    end
    FluxCorrelations.(TaxonomyLevels{t})(delArray,:)=[];

    FluxCorrelations.(TaxonomyLevels{t})(find(strcmp(FluxCorrelations.(TaxonomyLevels{t})(:,1),'')),:)=[];
end

% translate to metabolite descriptions
for t = 2:size(TaxonomyLevels, 1)
    for i=2:size(FluxCorrelations.(TaxonomyLevels{t}),2)
        try
            FluxCorrelations.(TaxonomyLevels{t}){1,i}=database.metabolites{find(strcmp(database.metabolites(:,1),FluxCorrelations.(TaxonomyLevels{t}){1,i})),2};
        catch
            warning('Flux label could not be translated to metabolite name.')
        end
    end
end

% export taxonomical information
taxonCol = 'Phylum';
% remove unnecessary columns
taxonomy(:,taxonCol+1:end)=[];

for t = 2:size(TaxonomyLevels, 1)
    TaxonomyReduced=taxonomy;
    taxonCol = find(strcmp(taxonomy(1, :), TaxonomyLevels{t}));
    TaxonomyReduced(:,1:taxonCol-1)=[];
    % remove duplicate entries
    [C,IA] = unique(TaxonomyReduced(:,1),'stable');
    % remove unclassified taxa
    findUncl=find(contains(C,'unclassified'));
    IA(findUncl,:)=[];
    TaxonomyInfo.(TaxonomyLevels{t})=TaxonomyReduced(IA,:);
end

set(0, 'DefaultTextInterpreter', 'none')

% Plot the calculated correlations.
for t = 1:length(TaxonomyLevels)
    data = string(FluxCorrelations.(TaxonomyLevels{t})(2:end, 2:end));
    data = str2double(data);

    if size(FluxCorrelations.(TaxonomyLevels{t}),2) > 5 && size(FluxCorrelations.(TaxonomyLevels{t}),1) > 5
        if size(FluxCorrelations.(TaxonomyLevels{t}),2) < 50
            cgo = clustergram(data,...
                'RowLabels', FluxCorrelations.(TaxonomyLevels{t})(2:end,1),...
                'ColumnLabels', FluxCorrelations.(TaxonomyLevels{t})(1,2:end),...
                'ColumnLabelsRotate', 45, ...
                'Cluster', 'all', ...
                'ShowDendrogram','True', ...
                'symmetric','False', ...
                'colormap', 'turbo' ...
                );
        else
            cgo = clustergram(data,...
                'RowLabels', FluxCorrelations.(TaxonomyLevels{t})(2:end,1),...
                'Cluster', 'all', ...
                'ShowDendrogram','True', ...
                'symmetric','False', ...
                'colormap', 'turbo' ...
                );
        end
        h = plot(cgo);
        set(h,'TickLabelInterpreter','none');
        colorbar(h)
    end
end

end


function tests = test_spm_dcm_bmr
% Unit Tests for test_spm_dcm_bmr
%__________________________________________________________________________
% Copyright (C) 2016 Wellcome Trust Centre for Neuroimaging

% $Id: test_spm_dcm_bmr.m 7369 2018-07-09 09:58:03Z peter $

tests = functiontests(localfunctions);

% -------------------------------------------------------------------------
function test_bmr(testCase)

data_path = get_data_path();

% Load PEB of full DCM
PEB = load(fullfile(data_path,'PEB_test.mat'));
PEB = PEB.PEB;
PEB = PEB(1);

% Load DCMs
GCM = load(fullfile(data_path,'models','GCM_simulated.mat'));
GCM = GCM.GCM;

% Limit to 3 subjects for performance
GCM = GCM(1:3,:);

% Prune connections on the B-matrix only
[RCM,BMC,BMA] = spm_dcm_bmr(GCM,{'B'});

% Check outputs have expected sizes
[ns,nm] = size(GCM);
assert(all(size(RCM) == size(GCM)));
assert(all(size(BMC) == [1 ns]));
assert(all(size(BMA) == [1 ns]));

% Again with only two outputs
[RCM,BMC] = spm_dcm_bmr(GCM,{'B'});

% Check outputs have expected sizes
[ns,nm] = size(GCM);
assert(all(size(RCM) == size(GCM)));
assert(all(size(BMC) == [1 ns]));

% -------------------------------------------------------------------------
function test_bmr_single_subject(testCase)

data_path = get_data_path();

% Load PEB of full DCM
PEB = load(fullfile(data_path,'PEB_test.mat'));
PEB = PEB.PEB;
PEB = PEB(1);

% Load DCMs
GCM = load(fullfile(data_path,'models','GCM_simulated.mat'));
GCM = GCM.GCM;

% Limit to 1 subject
GCM = GCM(1,:);

% Prune connections on the B-matrix only
[RCM,BMC,BMA] = spm_dcm_bmr(GCM,{'B'});

% Check outputs have expected sizes
[ns,nm] = size(GCM);
assert(all(size(RCM) == size(GCM)));
assert(all(size(BMC) == [1 ns]));
assert(all(size(BMA) == [1 ns]));

% Again with only two outputs
[RCM,BMC] = spm_dcm_bmr(GCM,{'B'});

% Check outputs have expected sizes
[ns,nm] = size(GCM);
assert(all(size(RCM) == size(GCM)));
assert(all(size(BMC) == [1 ns]));

% -------------------------------------------------------------------------
function data_path = get_data_path()

data_path = fullfile( spm('Dir'), 'tests', ...
    'data', 'fMRI', 'simulated_2region');


function [J_x0,J_xf,J_t] = sys_Dg(neq,t,x0,xf)

global sys_params A B C D

% J_x0 and J_xf are row vectors of length n.
% J_t is not used.

F_NUM = neq(5);

J_x0 = zeros(1, length(x0));
J_xf = 0*C;


function varargout = spwelch(s,w, varargin)
% PWELCH   (* DEPRECATED*) overloaded pwelch for spectralobjects
%   spwelch(spectralobject, waveform) - plots the spectral density
%       Pxx = pwelch(spectralobject, waveform) - returns the Power Spectral
%           Density (PSD) estimate, Pxx, of a discrete-time signal
%           vector X using Welch's averaged,  modified periodogram method.
%       [Pxx, Freqs] = pwelch(spectralobject, waveform) - returns spectral
%       density and associated frequency bins.
%
%       Options, spwelch(s,w, 'DEFAULT') - plots the spectral density using
%       pwelch's defaults (8 averaged windows, 50% overlap)
%   window is length of entire waveform..
%
% THIS FUNCTION HAS BEEN REPLACED WITH PWELCH, and will be removed from
% future versions.

% AUTHOR: Celso Reyes, Geophysical Institute, Univ. of Alaska Fairbanks
% $Date$
% $Revision$

error('Spectralobject:spwelch:Depricated',...
    ['spectralobject/spwelch has been deprecated. ',...
    'Please use spectralobject/pwelch instead.  the syntax is the same']);


function [hmm,Gamma,fehist] = hmmmar_init(data,T,options)
%
% Initialise the hidden Markov chain using HMM-MAR
%
% INPUT
% data      observations, a struct with X (time series) and C (classes, optional)
% T         length of observation sequence
% options,  structure with the training options
% Sind
%
% OUTPUT
% Gamma     p(state given X)
%
% Author: Diego Vidaurre, University of Oxford
% Author: Romesh Abeysuriya, University of Oxford

if ~isfield(options,'maxorder')
    [~,order] = formorders(options.order,options.orderoffset,options.timelag,options.exptimelag);
    options.maxorder = order;
end

% Run two initializations for each K less than requested K, plus options.initrep K
if options.initTestSmallerK
    init_k = [repmat(1:(options.K-1),1,2) options.K*ones(1,options.initrep)];
    init_k = init_k(end:-1:1);
else % Standard behaviour, test specified K options.initrep times
    init_k = options.K*ones(1,options.initrep);
end

felast = zeros(length(init_k),1);
maxfo = zeros(length(init_k),1);
fehist = cell(length(init_k),1);
Gamma = cell(length(init_k),1);
hmm = cell(length(init_k),1);

if options.useParallel && length(init_k) > 1 % not very elegant
    parfor it = 1:length(init_k)
        [hmm{it},Gamma{it},fehist{it}] = run_initialization(data,T,options,init_k(it));
        felast(it) = fehist{it}(end);
        maxfo(it) = mean(getMaxFractionalOccupancy(Gamma{it},T,options));
        if options.verbose
            if options.episodic
                fprintf('Init run %2d, Free Energy = %f \n',it,felast(it));
            else
                fprintf('Init run %2d, %2d->%2d states, Free Energy = %f \n',...
                    it,init_k(it),size(Gamma{it},2),felast(it));
            end
        end
    end
else
    for it = 1:length(init_k)
        [hmm{it},Gamma{it},fehist{it}] = run_initialization(data,T,options,init_k(it));
        felast(it) = fehist{it}(end);
        maxfo(it) = mean(getMaxFractionalOccupancy(Gamma{it},T,options));
        if options.verbose
            if options.episodic
                fprintf('Init run %2d, Free Energy = %f \n',it,felast(it));
            else
                fprintf('Init run %2d, %2d->%2d states, Free Energy = %f \n',...
                    it,init_k(it),size(Gamma{it},2),felast(it));
            end
        end
    end
end

if isfield(options,'initcriterion') && strcmpi(options.initcriterion,'FreeEnergy')
    [fe,s] = min(felast);
    if options.verbose
        fprintf('%i-th was the best iteration with FE=%f \n',s,fe)
    end
else
    [fo,s] = min(maxfo);
    if options.verbose
        fprintf('%i-th was the best iteration with mean maxFO=%f \n',s,fo)
    end
end

Gamma = Gamma{s};
hmm = hmm{s};
fehist = fehist{s};

end

function [hmm,Gamma,fehist] = run_initialization(data,T,options,init_k)
% INPUTS
% - data,T,options,Sind <same as hmmmar_init>
% - init_k is the number of states to use for this initialization

% Need to adjust the worker dirichletdiags if testing smaller K values
%if ~options.episodic && init_k < options.K
Sind = options.Sind;

if init_k < options.K
    for j = 1:length(options.DirichletDiag)
        p = options.DirichletDiag(j)/(options.DirichletDiag(j) + options.K - 1); % Probability of remaining in same state
        f_prob = dirichletdiags.mean_lifetime(); % Function that returns the lifetime in steps given the probability
        expected_lifetime =  f_prob(p)/options.Fs; % Expected number of steps given the probability
        options.K = init_k;
        adjusted_DirichletDiag = dirichletdiags.get(expected_lifetime,options.Fs,options.K);
        if isfinite(adjusted_DirichletDiag) % It is NaN if there was a numerical issue
            options.DirichletDiag(j) = adjusted_DirichletDiag;
        end
    end
end

data.C = data.C(:,1:options.K);
if ~isfield(options,'ehmm_priorOFFvsON'), ehmm_priorOFFvsON = [];
else, ehmm_priorOFFvsON = options.ehmm_priorOFFvsON;
end
% Note - initGamma_random uses DD=1 so that there are lots of transition times, which
% helps the inference not get stuck in a local minimum. options.DirichletDiag is
% then used inside hmmtrain when computing the free energy
keep_trying = true; notries = 0;
while keep_trying
    Gamma = initGamma_random(T-options.maxorder,options.K,...
        min(median(double(T))/10,500),...
        options.Pstructure,options.Pistructure,...
        options.episodic,ehmm_priorOFFvsON);
    hmm = struct('train',struct());
    hmm.K = options.K;
    hmm.train = options;
    hmm.train.Sind = Sind;
    hmm.train.cyc = max(hmm.train.initcyc,2);
    hmm.train.verbose = 0;
    hmm.train.plotGamma = 0;
    hmm = hmmhsinit(hmm);
    if isfield(hmm.train,'Gamma'), hmm.train = rmfield(hmm.train,'Gamma'); end
    [hmm,residuals] = obsinit(data,T,hmm,Gamma);
    try
        [hmm,Gamma,~,fehist] = hmmtrain(data,T,hmm,Gamma,residuals);
        fehist(end) = [];
        keep_trying = false;
    catch exception
        notries = notries + 1;
        if notries > 10
            disp('Initialisation went wrong');
            throw(exception)
        end
        disp('Something strange happened in the initialisation - repeating')
    end
    hmm.train.verbose = options.verbose;
    hmm.train.cyc = options.cyc;
    hmm.train.plotGamma = options.plotGamma;
end
%fe = fehist(end);
end


addpath(genpath('D:\GitHub\KiloSort2')) % path to kilosort folder
addpath('D:\GitHub\npy-matlab')

rootZ = 'H:\DATA\Spikes\Benchmark2015';

pathToYourConfigFile = 'D:\GitHub\KiloSort2\configFiles'; % take from Github folder and put it somewhere else (together with the master_file)
run(fullfile(pathToYourConfigFile, 'configFile384.m'))

ops.chanMap = 'H:\DATA\Spikes\Benchmark2015\forPRBimecToWhisper.mat';
rootH = 'H:\DATA\Spikes\temp\';

ops.NchanTOT = 129;
ops.trange      = [0 Inf]; % TIME RANGE IN SECONDS TO PROCESS

 % these settings overwrite any settings from config
ops.lam      = 10^2;   % weighting on the amplitude penalty (like in Kilosort1, but it has to be much larger)

ops.ThS      = [8 8];  % lower bound on acceptable single spike quality
ops.momentum = [20 400]; % number of samples to average over
ops.minFR    = 1/50; % minimum spike rate (Hz)

ops.sigmaMask  = 30;

ops.Nfilt       = 768; % max number of clusters
ops.nfullpasses = 1; % how many forward backward passes to do
ops.nPCs        = 3; % how many PCs to project the spikes into

ops.useRAM = 0;
ops.nNeighPC = 3;
ops.ccsplit = .975;

ops.NT                  = 64*1024+ ops.ntbuff;

dsets = {'20141202_all_es', '20150924_1_e', '20150601_all_s',...
    '20150924_1_GT', '20150601_all_GT', '20141202_all_GT'};

addpath(rootZ)

tic

for idk = [4 3 5] %:3 %1:length(dsets)
    fname = [dsets{idk} '.dat']; %'20141202_all_es.dat';

    savePath = fullfile(rootZ,  dsets{idk});

    ops.fbinary     = fullfile(savePath,  fname);
    ops.fproc       = fullfile(rootH, 'temp_wh2.dat'); % residual from RAM of preprocessed data

    % preprocess data
    rez = preprocessDataSub(ops);

    fname = fullfile(savePath, 'rez.mat');
    if exist(fname, 'file')
        dr = load(fname);
        rez.iorig = dr.rez.iorig;
        rez.ccb = dr.rez.ccb;
        rez.ccbsort = dr.rez.ccbsort;
    else
        rez = clusterSingleBatches(rez);
        save(fname, 'rez', '-v7.3');
    end

    rez = learnAndSolve8b(rez);

    rez = splitAllClusters(rez);

    fname = [dsets{idk} '.dat']; %'20141202_all_es.dat';
    [~, fn, ~] = fileparts(fname);

    isgood = rez.st3(:,4)<Inf;

    testClu = rez.st3(isgood,2);
    testRes = rez.st3(isgood,1);

    gtClu = LoadClu(fullfile(savePath, [fn '.clu.1']));
    fid   = fopen(fullfile(savePath, [fn '.res.1']), 'r');
    gtRes = int32(fscanf(fid, '%d'));
    fclose(fid);

    [allScores, allFPrates, allMissRates, allMerges] = ...
        compareClustering2(gtClu, gtRes, testClu, testRes, 1);

    rezToPhy(rez, savePath);

end

% save(fullfile(rootZ , 'allrez52718'), 'allrez', '-v7.3')

%%

%%


function [batches, batch_padding] = rcnn_extract_regions(im, boxes, rcnn_model)
% [batches, batch_padding] = rcnn_extract_regions(im, boxes, rcnn_model)
%   Extract image regions and preprocess them for use in Caffe.
%   Output is a cell array of batches.
%   Each batch is a 4-D tensor formatted for input into Caffe:
%     - BGR channel order
%     - single precision
%     - mean subtracted
%     - dimensions from fastest to slowest: width, height, channel, batch_index
%
%   im is an image in RGB order as returned by imread
%   boxes are in [x1 y1 x2 y2] format with one box per row

% AUTORIGHTS
% ---------------------------------------------------------
% Copyright (c) 2014, Ross Girshick
%
% This file is part of the R-CNN code and is available
% under the terms of the Simplified BSD License provided in
% LICENSE. Please retain this notice and LICENSE if you use
% this file (or any portion of it) in your project.
% ---------------------------------------------------------

% convert image to BGR and single
im = single(im(:,:,[3 2 1]));
num_boxes = size(boxes, 1);
batch_size = rcnn_model.cnn.batch_size;
num_batches = ceil(num_boxes / batch_size);
batch_padding = batch_size - mod(num_boxes, batch_size);
if batch_padding == batch_size
  batch_padding = 0;
end

crop_mode = rcnn_model.detectors.crop_mode;
image_mean = rcnn_model.cnn.image_mean;
crop_size = size(image_mean,1);
crop_padding = rcnn_model.detectors.crop_padding;

batches = cell(num_batches, 1);
%for batch = 1:num_batches
for batch = 1:num_batches
  batch_start = (batch-1)*batch_size+1;
  batch_end = min(num_boxes, batch_start+batch_size-1);

  ims = zeros(crop_size, crop_size, 3, batch_size, 'single');
  for j = batch_start:batch_end
    bbox = boxes(j,:);

    crop = rcnn_im_crop(im, bbox, crop_mode, crop_size, ...
        crop_padding, image_mean);

    % swap dims 1 and 2 to make width the fastest dimension (for caffe)
    ims(:,:,:,j-batch_start+1) = permute(crop, [2 1 3]);
  end

  batches{batch} = ims;
end


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

startpath = pwd;
cd(pathDATA)
load('Patient51_CT'), sDataCT = sData;
load('Patient51_PET'), sDataPET = sData;

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

cd(startpath)
end


% STD_READSPECGRAM - returns the stored mean power spectrogram for an ICA component
%                  or a data channel in a specified dataset.  The spectrogram is
%                  assumed to have been saved in a Matlab file,
%                  "[dataset_name].datspecgram", in the same
%                  directory as the dataset file. If this file doesn't exist,
%                  use STD_SPECGRAM to create it.
% Usage:
%  >> [spec, times, freqs] = std_readspecgram(ALLEEG, setindx, component, timerange, freqrange);
%
% Inputs:
%   ALLEEG     - a vector of dataset EEG structures (may also be one dataset).
%                Must contain the dataset of interest (the 'setindx' below).
%   setindx    - [integer] an index of an EEG dataset in the ALLEEG
%                structure for which to read a component spectrum.
%   component  - [integer] index of the component in the selected EEG dataset
%                for which to return the spectrum
%   freqrange  - [min max in Hz] frequency range to return
%
%
% Outputs:
%   spec      - the log-power spectrum of the requested ICA component in the
%               specified dataset (in dB)
%   freqs     - vector of spectral frequencies (in Hz)
%
%  See also  STD_SPEC, POP_PRECLUST, STD_PRECLUST
%
% Authors:  Arnaud Delorme, SCCN, INC, UCSD, February, 2008

% Copyright (C) Hilit Serby, SCCN, INC, UCSD, October 11, 2004, hilit@sccn.ucsd.edu
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.

function [X, t, f] = std_readspecgram(ALLEEG, abset, comp, timerange, freqrange, rmsubjmean);

if nargin < 4
    timerange = [];
end
if nargin < 5
    freqrange = [];
end

X = [];

if iscell(comp)
    % find channel indices list
    % -------------------------
    chanind  = [];
    tmpchanlocs = ALLEEG(abset).chanlocs;
    chanlabs = lower({ tmpchanlocs.labels });
    for index = 1:length(comp)
        tmp = strmatch(lower(comp{index}), chanlabs, 'exact');
        if isempty(tmp)
            error([ 'Channel ''' comp{index} ''' not found in dataset ' int2str(abset)]);
        else
            chanind = [ chanind tmp ];
        end
    end
    filename = fullfile( ALLEEG(abset).filepath,[ ALLEEG(abset).filename(1:end-3) 'datspecgram']);
    prefix = 'chan';
    inds   = chanind;
elseif comp(1) < 0
    filename = fullfile( ALLEEG(abset).filepath,[ ALLEEG(abset).filename(1:end-3) 'datspecgram']);
    prefix = 'chan';
    inds   = -comp;
else
    filename = fullfile( ALLEEG(abset).filepath,[ ALLEEG(abset).filename(1:end-3) 'icaspecgram']);
    prefix = 'comp';
    inds   = comp;
end

for k=1:length(inds)
    try,
        warning backtrace off;
        erpstruct = load( '-mat', filename, [ prefix int2str(inds(k)) ], 'freqs', 'times');
        warning backtrace on;
    catch
        error( [ 'Cannot read file ''' filename '''' ]);
    end

    tmpdat    = getfield(erpstruct, [ prefix int2str(inds(k)) ]);
    if k == 1
        X = zeros([size(tmpdat) length(comp)]);
    end
    X(:,:,k) = tmpdat;
    f        = getfield(erpstruct, 'freqs');
    t        = getfield(erpstruct, 'times');
end

% select frequency range of interest
% ----------------------------------
if ~isempty(freqrange)
    maxind = max(find(f <= freqrange(end)));
    minind = min(find(f >= freqrange(1)));
else
    %if not, use whole spectrum
    maxind = length(f);
    minind = 1;
end
f = f(minind:maxind);
X = X(minind:maxind,:,:);

% select time range of interest
% -----------------------------
if ~isempty(timerange)
    maxind = max(find(t <= timerange(end)));
    minind = min(find(t >= timerange(1)));
else
    %if not, use whole spectrum
    maxind = length(t);
    minind = 1;
end
t = t(minind:maxind);
X = X(:,minind:maxind,:);

return;