classdef Predict

  methods (Static) % fastPredict

    function fastPredict = fastPredict(nClassifier)
      % fastPredict constructor
      %
      % fastPredict(ibeh).classifier % struct array, reduced/indexed classifier
      % fastPredict(ibeh).windowfeaturescellparams % struct, pffnames -> cell
      % fastPredict(ibeh).wfs % window features
      % fastPredict(ibeh).pffs % all distinct pffs for wfs; fieldnames of windowfeaturescellparams;
      % fastPredict(ibeh).ts % classifier TS for this classifier
      % fastPredict(ibeh).wfidx % indices into 'external'/supplied featurename list corresponding to .wfs
      % fastPredict(ibeh).wfidx_valid = false;

      fastPredict = struct(...
        'classifier',cell(nClassifier,1),...
        'windowfeaturescellparams',[],...
        'wfs',{cell(1,0)},...
        'pffs',{cell(1,0)}, ...
        'ts',[], ...
        'wfidx',[], ...
        'wfidx_valid',false);
    end

    function fastPredict = fastPredictInit(fastPredict,classifiers,classifierTS,feature_names) %#ok<INUSL>
      % classifiers: cell array of classifiers
      % classifierTS: classifier timestamps
      % feature_names: cellstr

        nClassifier = numel(classifiers);
        assert(isequal(nClassifier,numel(classifierTS),numel(feature_names)));

        fastPredict = Predict.fastPredict(nClassifier); % No part of fastPredict is reused

        for iCls = 1:nClassifier

          if isempty(classifiers{iCls})
            continue;
          end

          % which features are actually used
          dims = [classifiers{iCls}.dim];
          feature_names_cls = feature_names{iCls}(dims);

          % put these in with the rest of the classifiers' window features
          wfs = {};
          for j = 1:numel(feature_names_cls),
            wfidxcurr = find(WindowFeatureNameCompare(feature_names_cls{j},wfs),1);
            if isempty(wfidxcurr),
              wfidxcurr = numel(wfs)+1;
              wfs{wfidxcurr} = feature_names_cls{j}; %#ok<AGROW>
            end
          end

          wf2pff = cellfun(@(x)x{1},wfs,'UniformOutput',false);
          [pffs,~,wf2pffidx] = unique(wf2pff);

          windowfeaturescellparams = struct;
          for pfi = 1:numel(pffs),
            pf = pffs{pfi};
            wfidx_cur = wf2pffidx==pfi;
            windowfeaturescellparams.(pf) = WindowFeatureName2Params(wfs(wfidx_cur));
          end

          classifiers_indexed = classifiers{iCls};
          for j = 1:numel(classifiers_indexed),
            classifiers_indexed(j).dim = j;
          end

          fastPredict(iCls).classifier = classifiers_indexed;
          fastPredict(iCls).windowfeaturescellparams = windowfeaturescellparams;
          fastPredict(iCls).wfs = feature_names_cls;
          fastPredict(iCls).pffs = pffs;
          fastPredict(iCls).ts = classifierTS(iCls);
          fastPredict(iCls).wfidx = [];
          fastPredict(iCls).wfidx_valid = false;
        end
    end

    function fastPredict = fastPredictFindWfidx(fastPredict,feature_names)
      feature_names_char = cellfun(@cell2str,feature_names,'UniformOutput',false);
      for i = 1:numel(fastPredict)
        wfs = fastPredict(i).wfs;

        wfs_char = cellfun(@cell2str,wfs,'UniformOutput',false);
        [ism,wfidx] = ismember(wfs_char,feature_names_char);
        if ~all(ism),
          error('Error matching wfs for classifier with window features computed');
        end

  %       wfidx = nan(1,numel(wfs));
  %       for j = 1:numel(wfs),
  %         idxcurr = find(WindowFeatureNameCompare(wfs{j},feature_names));
  %         if numel(idxcurr) ~= 1,
  %           error('Error matching wfs for classifier with window features computed');
  %         end
  %         wfidx(j) = idxcurr;
  %       end

        fastPredict(i).wfidx = wfidx;
        fastPredict(i).wfidx_valid = true;
      end
    end

  end

  methods (Static) % predictblocks

    function pb = predictblocks(nCls)
      pb = struct('t0',cell(nCls,1),'t1',[],'expi',[],'flies',[]);
    end

  end

end


function tagS = patient_module_tags
%"patient_module_tags"
%   Return the tags used to represent a patient as specified by C.7.1.1 in
%   PS3.3 of 2006 DICOM specification. Tags are returned in a struct array
%   with 3 fields:
%
%   Tag: String containing hex DICOM tag of a field.
%  Type: String describing type of field, with 5 options:
%         '1' Field must exist, data must exist and be valid.
%         '2' Field must exist, data can exist and be valid, or be NULL.
%         '3' Field is optional, if the field exists data can exist and be
%             valid or be NULL.
%         '1C' Field must exist under certain conditions and contain valid
%             data.
%         '2C' Field must exist under certain conditions and can contain
%             valid data or be NULL.
%Children: For sequences, a tagS with the same format as this struct array.
%
%JRA 06/06/06
%YWU Modified 03/01/08
%
%Usage:
%   tagS = patient_module_tags
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR 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.
%
% CERR 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 CERR.  If not, see <http://www.gnu.org/licenses/>.

% {SudyInstanceUID, Patient's Name, PatientID, Issuer of Patient ID, Patient's Birth Date,
% PatientSex, Patient's Birth Time, Other Patient IDs, Other Patient Names,
% Ethnic Group, Patient Comments}

tagC = {'0020000D','00100010','00100020','00100021','00100030',...
'00100040','00100032','00101000','00101001','00102160','00104000'};

tagdecC = {2097165, 1048592, 1048608, 1048609, 1048624, 1048640, 1048626,...
    1052672, 1052673, 1057120, 1064960};

fieldNameC = {'StudyInstanceUID','PatientName','PatientID',...
    'IssuerOfPatientID','PatientBirthDate','PatientSex',...
    'PatientBirthTime','OtherPatientIDs','OtherPatientNames',...
    'EthnicGroup','PatientComments'};

tagTypeC = {'1','2','2','3','2','2','3','3','3','3','3'};

childC = {[],[],[],[],[],[],[],[],[],[],[]};

tagS = struct('tag', tagC, 'tagdec', tagdecC, 'fieldname', fieldNameC, 'type', tagTypeC, 'children', childC);

%
% %Initialize the tagS structure.
% tagS = struct('tag', {}, 'type', {}, 'children', {});
%
% %Create an empty tagS template for sequence creation.
% template = tagS;
%
% %Add tags based on PS3.3 attribute lists.
%
% %Study Instance UID
% tagS(end+1) = struct('tag', ['0020000D'], 'type', ['1'], 'children', []);
%
% %Patient's Name
% tagS(end+1) = struct('tag', ['00100010'], 'type', ['2'], 'children', []);
%
% %Patient ID
% tagS(end+1) = struct('tag', ['00100020'], 'type', ['2'], 'children', []);
%
% %Issuer of Patient ID
% tagS(end+1) = struct('tag', ['00100021'], 'type', ['3'], 'children', []);
%
% %Patient's Birth Date
% tagS(end+1) = struct('tag', ['00100030'], 'type', ['2'], 'children', []);
%
% %Patient's Sex
% tagS(end+1) = struct('tag', ['00100040'], 'type', ['2'], 'children', []);
%
% % %Referenced Patient Sequence
% % tagS(end+1) = struct('tag', ['00081140'], 'type', ['3'], 'children', []);
% %     child_1        = template;
% %
% %     %Referenced SOP Class UID
% %     child_1(end+1) = struct('tag', ['00081150'], 'type', ['1'], 'children', []);
% %
% %     %Referenced SOP Instance UID
% %     child_1(end+1) = struct('tag', ['00081155'], 'type', ['1'], 'children', []);
% %     tagS(end).children = child_1;
%
% %Patient's Birth Time
% tagS(end+1) = struct('tag', ['00100032'], 'type', ['3'], 'children', []);
%
% %Other Patient IDs
% tagS(end+1) = struct('tag', ['00101000'], 'type', ['3'], 'children', []);
%
% %Other Patient Names
% tagS(end+1) = struct('tag', ['00101001'], 'type', ['3'], 'children', []);
%
% %Ethnic Group
% tagS(end+1) = struct('tag', ['00102160'], 'type', ['3'], 'children', []);
%
% %Patient Comments
% tagS(end+1) = struct('tag', ['00104000'], 'type', ['3'], 'children', []);
%
% %wy %Patient Identity Removed
% % tagS(end+1) = struct('tag', ['00120062'], 'type', ['3'], 'children', []);
% %
% % %De-identification Method
% % tagS(end+1) = struct('tag', ['00120063'], 'type', ['1C'], 'children', []);
% %
% % %De-identification Method Code Sequence
% % tagS(end+1) = struct('tag', ['00120064'], 'type', ['1C'], 'children', []);
% %
% % %Include "Code Sequence Macro"
% % tagS(end).children = code_sequence_macro_tags;


% The mapping from 61 phones set to 48 phonse set for TIMIT. The mapping
% table is as follows:
%
% cl vcl epi --> sil
% el --> l
% en --> n
% zh --> sh
% aa --> ao
% ix --> ih
% ax --> ah
%
% Author: Xiong Xiao, NTU, Singapore
% Date: 1 Jun 2016

function phone_seq_2 = TIMIT_map39to2(phone_seq_39)

found = strcmpi(phone_seq_39, 'sil');
phone_seq_2 = phone_seq_39;
idx = find(found==0);
for j=1:length(idx)
    phone_seq_2{idx(j)} = 'speech';
end
end


%% BinghamS2


% A demo how to run a multi-face face tracker

clear;

if(isunix)
    executable = '"../../build/bin/FaceLandmarkVidMulti"';
else
    executable = '"../../x64/Release/FaceLandmarkVidMulti.exe"';
end

in_files = dir('../../samples/multi_face.avi');

model = 'model/main_ceclm_general.txt'; % Trained on in the wild, menpo and multi-pie data (a CE-CLM model)

% Uncomment the below models if you want to try them
%model = 'model/main_clnf_general.txt'; % Trained on in the wild and multi-pie data (a CLNF model)

% Uncomment the below models if you want to try them
%model = 'model/main_clnf_wild.txt'; % Trained on in-the-wild data only

%model = 'model/main_clm_general.txt'; % Trained on in the wild and multi-pie data (less accurate SVR/CLM model)
%model = 'model/main_clm_wild.txt'; % Trained on in-the-wild

% Create a command that will run the tracker on set of videos and display the output
command = sprintf('%s -mloc "%s" -verbose ', executable, model);

% add all videos to single argument list (so as not to load the model anew
% for every video)
for i=1:numel(in_files)
    inputFile = ['../../samples/', in_files(i).name];
    command = cat(2, command, [' -f "' inputFile '" ']);
end

% Call the executable
if(isunix)
    unix(command);
else
    dos(command);
end


classdef FuncRegClass < matlab.mixin.Copyable

    properties
        userfuncdir
        userfuncfiles
        entries
        type
        typeNameIdx
        config
        logger
    end

    methods

        % ----------------------------------------------------------------------------------
        function obj = FuncRegClass(varargin)
            %
            % Syntax:
            %    obj = FuncRegClass(type);
            %    obj = FuncRegClass(obj2);
            %
            % Description:
            %    Generates a list of FuncRegEntryClass objects of type
            %    'run', 'subj', or 'group' for any hmr* user files it finds
            %    in the folder <Rootdir>/FuncRegistry/UserFunctions.
            %    <Rootdir> is relative to the location of this file,
            %    FuncRegClass.m
            %
            % Example:
            %    Generate array of registry entries for all run-level users
            %    functions
            %
            %    freg = FuncRegClass('run')
            %
            %         ===> FuncRegClass with properties:
            %
            %         userfuncdir: 'c:/jdubb/workspaces/homer3/FuncRegistry/UserFunctions/'
            %       userfuncfiles: {1x10 cell}
            %             entries: [1x10 FuncRegEntryClass]
            %                type: 'run'
            %
            %    freg.entries(1)
            %
            %         ===> FuncRegEntryClass with properties:
            %
            %                name: 'hmrR_BandpassFilt'
            %              uiname: 'Bandpass_Filter'
            %        usageoptions: {'Bandpass_Filter'  'dod = hmrR_BandpassFilt( dod, t, hpf, lpf )'  'hmrR_BandpassFilt dod (dod,t hpf %0.3f 0.010 lpf %0.3f 0.500'}
            %              params: {2x2 cell}
            %                help: [1x1 FuncHelpClass]
            %
            global logger

            logger = InitLogger(logger);
            obj.logger = logger;

            obj.entries = FuncRegEntryClass().empty();

            if nargin==0
                return;
            end

            obj.typeNameIdx = 1;
            if isa(varargin{1}, 'FuncRegClass')
                obj.Copy(varargin{1});
                return;
            end

            obj.type = varargin{1};
            if nargin > 1
                obj.typeNameIdx = varargin{2};
            end

            % Get the parameter items from config file relevant to this class
            obj.userfuncdir = FindUserFuncDir(obj);
            obj.userfuncfiles = [];
            obj.Load();
        end

        % ----------------------------------------------------------------------------------
        function Load(obj)
            heading = sprintf('Registry loading %s-level user functions:', [upper(obj.type(1)), obj.type(2:end)]);
            fprintf('%s\n', heading);

            files = cell(length(obj.userfuncdir),1);
            for jj = 1:length(obj.userfuncdir)
                expr = sprintf('%shmr%s_*.m', obj.userfuncdir{jj}, upper(obj.type(obj.typeNameIdx)));
                files{jj} = dir(expr);
                for iF = 1:length(files{jj})
                    files{jj}(iF).folder = obj.userfuncdir{jj};
                end
                fprintf('User functions folder %s\n', obj.userfuncdir{jj});
            end

            h = waitbar(0, heading);

            kk = 1;
            for jj = 1:length(files)
                N = length(files{jj});
                for ii = 1:N
                    if strfind(files{jj}(ii).name, '_result.m')
                        continue;
                    end
                    progressmsg = sprintf('Parsing %s', files{jj}(ii).name);
                    obj.logger.Write('%s\n', progressmsg);
                    waitbar_improved(kk/N, h, sprintf(progressmsg));
                    obj.AddEntry(files{jj}(ii).name, jj);
                    kk = kk+1;
                end
            end

            close(h)
            fprintf('\n');
        end

        % -------------------------------------------------------------------------------
        function err = AddEntry(obj, funcname, jj)
            err = 0;
            if nargin<3
                jj = 1;
            end
            idx = obj.GetIdx(funcname);
            if ~isempty(idx)
                q = menu('Function entry already exists in registry. Do you want to reload it?', 'YES','NO');
                if q==2
                    return;
                end
            end
            tmp = FuncRegEntryClass(funcname);
            if tmp.IsValid()
                idx = length(obj.entries)+1;
                obj.entries(idx) = FuncRegEntryClass(tmp);
                obj.userfuncfiles{idx} = [obj.userfuncdir{jj}, funcname];
            else
                fprintf('  Discarding %s. Function is not valid registry entry.\n', tmp.name)
            end
        end

        % -------------------------------------------------------------------------------
        function err = ReloadEntry(obj, funcname)
            err = 0;
            idx = obj.GetIdx(funcname);
            obj.entries(idx) = FuncRegEntryClass(funcname);
        end

        % ----------------------------------------------------------------------------------
        function Copy(obj, obj2)
            obj.userfuncdir = obj2.userfuncdir;
            obj.userfuncfiles = obj2.userfuncfiles;
            obj.type = obj2.type;
            obj.config = obj2.config;
            for ii=1:length(obj2.entries)
                obj.entries(ii) = FuncRegEntryClass(obj2.entries(ii));
            end
        end

        % ----------------------------------------------------------------------------------
        function b = IsEmpty(obj)
            b = true;
            if isempty(obj)
                return
            end
            if isempty(obj.userfuncdir)
                return;
            end
            if isempty(obj.userfuncfiles)
                return;
            end
            if isempty(obj.entries)
                return;
            end
            b = false;
        end

        % ------------------------------------------------------
        function idx = GetIdx(obj, key)
            idx = [];
            if ~exist('key','var') || isempty(key)
                key = 1;
            end
            if ischar(key)
                k = [];
                for ii=1:length(obj.entries)
                    if strcmp(obj.entries(ii).GetName(), key)
                        k = ii;
                        break;
                    end
                end
                if isempty(k)
                    return
                end
                key = k(1);
            end
            if ~iswholenum(key) || key<1 || key>length(obj.entries)
                return;
            end
            idx = key;
        end

    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % Methods to retrieve all or multiple entries
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    methods

        % ----------------------------------------------------------------------------------
        function usagestrs = GetUsageStrs(obj, addnewline)
            if ~exist('addnewline','var')
                addnewline = 0;
            end
            if addnewline
                nl = sprintf('\n');
            else
                nl = '';
            end
            usagestrs = cell(2*length(obj.entries), 1);
            kk = 1;
            for ii=1:length(obj.entries)
                for jj=1:obj.entries(ii).GetOptionsNum()
                    usagestrs{kk} = sprintf('@ %s%s', obj.entries(ii).GetUsageStr(jj), nl);
                    kk=kk+1;
                end
            end
            % Remove extra empty cells
            usagestrs = usagestrs(~cellfun('isempty',usagestrs));
        end

        % ----------------------------------------------------------------------------------
        function names = GetFuncNames(obj)
            names = cell(length(obj.entries), 1);
            for ii=1:length(obj.entries)
                names{ii} = obj.entries(ii).GetName();
            end
        end

        % ----------------------------------------------------------------------------------
        function fcallsstr = GetFuncCallsEncoded(obj, procStream)
            fcallsstr = {};
            if ~isa(fcall,'FuncCallClass')
                return
            end
            if isempty(procStream)
                return;
            end
            fcalls = procStream.fcalls;
            kk=1;
            for iFcall=1:length(fcalls)
                idx = obj.GetIdx(fcall(iFcall).GetName());
                if isempty(idx)
                    continue;
                end
                str = obj.entries(idx).GetFuncCallsEncoded(fcall(iFcall));
                if ~isempty(str)
                    fcallsstr{kk} = str;
                    kk=kk+1;
                end
            end
        end

    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % Methods to retrieve individual entries
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    methods

        % ----------------------------------------------------------------------------------
        function [name, idx] = GetFuncName(obj, key)
            idx = obj.GetIdx(key);
            if isempty(idx)
                return;
            end
            name = obj.entries(idx).GetName();
        end

        % ----------------------------------------------------------------------------------
        function helpstr = GetFuncHelp(obj, key)
            helpstr = '';
            idx = obj.GetIdx(key);
            if isempty(idx)
                return;
            end
            helpstr = obj.entries(idx).GetHelpStr();
        end

        % ----------------------------------------------------------------------------------
        function fcallstr = GetFuncCallStrDecoded(obj, key, usagename)
            fcallstr = '';
            idx = obj.GetIdx(key);
            if isempty(idx)
                return;
            end
            fcallstr = obj.entries(idx).GetFuncCallStrDecoded(usagename);
        end

        % ----------------------------------------------------------------------------------
        function fcall = GetFuncCallDecoded(obj, key, usagename)
            fcall = FuncCallClass().empty();
            idx = obj.GetIdx(key);
            if isempty(idx)
                return;
            end
            fcall = obj.entries(idx).GetFuncCallDecoded(usagename);
        end

        % ----------------------------------------------------------------------------------
        function fcall = FindClosestMatch(obj, fcall0)
            fcall = FuncCallClass().empty();
            if isempty(obj)
                return;
            end
            if nargin<2
                return;
            end
            for ii=1:length(obj.entries)
                fcall = obj.entries(ii).FindClosestMatch(fcall0);
                if ~isempty(fcall)
                    break
                end
            end
        end

        % ----------------------------------------------------------------------------------
        function paramtxt = GetParamText(obj, key)
            paramtxt = '';
            idx = obj.GetIdx(key);
            if isempty(idx)
                return;
            end
            paramtxt = obj.entries(idx).GetParamText();
        end

        % ----------------------------------------------------------------------------------
        function usagename = GetUsageName(obj, fcall)
            usagename = '';
            if ~isa(fcall,'FuncCallClass')
                return
            end
            idx = obj.GetIdx(fcall.GetName());
            if isempty(idx)
                return;
            end
            usagename = obj.entries(idx).GetUsageName(fcall);
        end

        % ----------------------------------------------------------------------------------
        function usagenames = GetUsageNames(obj, funcname)
            usagenames = {};
            idx = obj.GetIdx(funcname);
            if isempty(idx)
                return;
            end
            usagenames = obj.entries(idx).GetUsageNames();
        end

        % ----------------------------------------------------------------------------------
        function n = GetNumUsages(obj, funcname)
            n = length(obj.GetUsageNames(funcname));
        end

        % ----------------------------------------------------------------------------------
        function usagestr = GetUsageStrDecorated(obj, funcname, usageparam)
            usagestr = '';
            if ~exist('funcname','var')
                return;
            end
            if ~exist('usageparam','var')
                usageparam='';
            end
            idx = obj.GetIdx(funcname);
            if isempty(idx)
                return;
            end
            usagestr = obj.entries(idx).GetUsageStrDecorated(usageparam);
        end

        % ----------------------------------------------------------------------------------
        function lastdt = DateLastModified(obj)
            try
                lastdt = datetime(1970,01,01);
                for ii = 1:length(obj.entries)
                    for jj = 1:length(obj.userfuncdir)
                        file = dir([obj.userfuncdir{jj}, '/', obj.entries(ii).GetName(), '.m']);
                        if ~isempty(file)
                            if  datetime(file.date,'locale','system') > lastdt
                                lastdt =  datetime(file.date,'locale','system');
                            end
                            break;
                        end
                    end
                end
            catch
                lastdt = -1;
            end
        end

    end
end


function whitehKernDisplay(kern, spacing)

% WHITEHKERNDISPLAY Display parameters of the WHITEH kernel.
% FORMAT
% DESC displays the parameters of the white noise
% kernel and the kernel type to the console.
% ARG kern : the kernel to display.
%
% FORMAT does the same as above, but indents the display according
% to the amount specified.
% ARG kern : the kernel to display.
% ARG spacing : how many spaces to indent the display of the kernel by.
%
% SEEALSO : whiteKernParamInit, modelDisplay, kernDisplay
%
% COPYRIGHT : Neil D. Lawrence, 2004, 2005, 2006

% KERN

if nargin > 1
  spacing = repmat(32, 1, spacing);
else
  spacing = [];
end
spacing = char(spacing);
fprintf(spacing);
fprintf('White Noise Variance: %2.4f\n', kern.variance)


function m = grammar2simple(gm)
% m = grammar2simple(gm)
%
% Convert a simple grammar model (with some strong assumptions on
% its structure) into a simple format for use with the cascade code.
% This function works with models produced by voc-release5.
% This structure roughly matches the model format from voc-release3.
%
% gm  grammar model returned by project_model.m

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

%% copy scalars
m.sbin      = gm.sbin;
m.thresh    = gm.thresh;
m.maxsize   = gm.maxsize;
m.minsize   = gm.minsize;
m.interval  = gm.interval;
m.numblocks = gm.numblocks;
m.class     = gm.class;
m.numcomponents ...
            = length(gm.rules{gm.start});
m.pca_coeff = gm.pca_coeff;
m.year      = gm.year;
m.note      = gm.note;
m.features  = gm.features;
if isfield(gm, 'bboxpred')
  m.bboxpred  = gm.bboxpred;
end

% Copy root filters
%% model.rootfilter{i} =
%     size: [6 13]
%     w: [6x13x31 double]
%     blocklabel: 2

% assume the first numcomponents filters are the root filters
m.rootfilters = cell(1, m.numcomponents);
for i = 1:m.numcomponents
  bl = gm.filters(i).blocklabel;
  m.rootfilters{i}.size = gm.filters(i).size;
  if gm.filters(i).flip
    m.rootfilters{i}.w    = single(gm.blocks(bl).w_flipped);
    m.rootfilters{i}.wpca = single(gm.blocks(bl).w_pca_flipped);
  else
    m.rootfilters{i}.w    = single(gm.blocks(bl).w);
    m.rootfilters{i}.wpca = single(gm.blocks(bl).w_pca);
  end
  m.rootfilters{i}.blocklabel = gm.filters(i).blocklabel;
end

% Copy component offsets
%% model.offsets{i} =
%     w: -4.2323
%     blocklabel: 1

m.offsets = cell(1, m.numcomponents);
for i = 1:length(gm.rules{gm.start})
  m.offsets{i}   = gm.rules{gm.start}(i).offset;
  m.offsets{i}.w = model_get_block(gm, gm.rules{gm.start}(i).offset) ...
                   * gm.features.bias;
end

m.loc_w = cell(1, m.numcomponents);
for i = 1:length(gm.rules{gm.start})
  m.loc{i}   = gm.rules{gm.start}(i).loc;
  m.loc{i}.w = model_get_block(gm, gm.rules{gm.start}(i).loc);
end

% Set up component structure
%% model.components{i} =
% x   rootindex: 1
% x   offsetindex: 1
% x   parts: {[1x1 struct]  [1x1 struct]  [1x1 struct]  [1x1 struct]  [1x1 struct]  [1x1 struct]}

numparts = 0;
m.components = cell(1, m.numcomponents);
for i = 1:m.numcomponents
  m.components{i}.rootindex = i;
  m.components{i}.offsetindex = i;
  n = length(gm.rules{gm.start}(i).rhs)-1;
  m.components{i}.parts = cell(1, n);
  numparts = numparts + n;
end

% Copy part filters, deformation models, and set up
% components{i}.parts{j}
%% model.partfilters{i} =
%    w: [9x6x31 double]
%    blocklabel: 5
%% model.defs{i} =
%    anchor: [1 4]
%    w: [0.0148 -4.1908e-04 0.0106 -0.0034]
%    blocklabel: 6
%% model.components{i}.parts{j} =
%    partindex: 1
%    defindex: 1

m.partfilters = cell(1, numparts);
m.defs = cell(1, numparts);
n = 0;
for i = 1:m.numcomponents
  m.components{i}.parts = cell(1, numparts/m.numcomponents);
  % assume first symbol is the root filter
  for j = 2:length(gm.rules{gm.start}(i).rhs)
    defsym = gm.rules{gm.start}(i).rhs(j);
    partsym = gm.rules{defsym}.rhs;
    filterid = gm.symbols(partsym).filter;
    bl = gm.filters(filterid).blocklabel;
    n = n + 1;
    if gm.filters(filterid).flip
      m.partfilters{n}.w    = single(gm.blocks(bl).w_flipped);
      m.partfilters{n}.wpca = single(gm.blocks(bl).w_pca_flipped);
    else
      m.partfilters{n}.w    = single(gm.blocks(bl).w);
      m.partfilters{n}.wpca = single(gm.blocks(bl).w_pca);
    end
    m.partfilters{n}.blocklabel = gm.filters(filterid).blocklabel;
    m.defs{n}.w = model_get_block(gm, gm.rules{defsym}.def);
    m.defs{n}.blocklabel = gm.rules{defsym}.def.blocklabel;
    m.defs{n}.anchor = gm.rules{gm.start}(i).anchor{j}(1:2);
    m.components{i}.parts{j-1}.partindex = n;
    m.components{i}.parts{j-1}.defindex = n;
  end
end


function cfg = topoplot_common(cfg, varargin)

% TOPOPLOT_COMMON is shared by FT_TOPOPLOTTFR, FT_TOPOPLOTER and FT_TOPOPLOTIC, which
% serve as placeholder for the documentation and for the pre/postamble.

% Copyright (C) 2005-2011, F.C. Donders Centre
%
% 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$

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% DEVELOPERS NOTE: This code is organized in a similar fashion for multiplot/singleplot/topoplot
% and for ER/TFR and should remain consistent over those 6 functions.
% Section 1: general cfg handling that is independent from the data
% Section 2: data handling, this also includes converting bivariate (chan_chan and chancmb) into univariate data
% Section 3: select the data to be plotted and determine min/max range
% Section 4: do the actual plotting
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Section 1: general cfg handling that is independent from the data

% check if the input cfg is valid for this function
cfg = ft_checkconfig(cfg, 'forbidden',  {'channels', 'trial'}); % prevent accidental typos
cfg = ft_checkconfig(cfg, 'unused',     {'cohtargetchannel'});
cfg = ft_checkconfig(cfg, 'renamed',    {'cohrefchannel' 'refchannel'});
cfg = ft_checkconfig(cfg, 'renamed',    {'zparam', 'parameter'});

% check for option-values to be renamed
cfg = ft_checkconfig(cfg, 'renamedval', {'electrodes',     'dotnum',      'numbers'});
cfg = ft_checkconfig(cfg, 'renamedval', {'zlim',           'absmax',      'maxabs'});
cfg = ft_checkconfig(cfg, 'renamedval', {'directionality', 'feedforward', 'outflow'});
cfg = ft_checkconfig(cfg, 'renamedval', {'directionality', 'feedback',    'inflow'});
cfg = ft_checkconfig(cfg, 'renamedval', {'highlight',      'yes',         'on'});

% check for renamed options
cfg = ft_checkconfig(cfg, 'renamed',     {'matrixside',    'directionality'});
cfg = ft_checkconfig(cfg, 'renamed',     {'electrodes',    'marker'});
cfg = ft_checkconfig(cfg, 'renamed',     {'emarker',       'markersymbol'});
cfg = ft_checkconfig(cfg, 'renamed',     {'ecolor',        'markercolor'});
cfg = ft_checkconfig(cfg, 'renamed',     {'emarkersize',   'markersize'});
cfg = ft_checkconfig(cfg, 'renamed',     {'efontsize',     'markerfontsize'});
cfg = ft_checkconfig(cfg, 'renamed',     {'hlmarker',      'highlightsymbol'});
cfg = ft_checkconfig(cfg, 'renamed',     {'hlcolor',       'highlightcolor'});
cfg = ft_checkconfig(cfg, 'renamed',     {'hlmarkersize',  'highlightsize'});
cfg = ft_checkconfig(cfg, 'renamed',     {'maplimits',     'zlim'});

% old ft_checkconfig adapted partially from topoplot.m (backwards backwards compatability)
cfg = ft_checkconfig(cfg, 'renamed',     {'grid_scale',    'gridscale'});
cfg = ft_checkconfig(cfg, 'renamed',     {'interpolate',   'interpolation'});
cfg = ft_checkconfig(cfg, 'renamed',     {'numcontour',    'contournum'});
cfg = ft_checkconfig(cfg, 'renamed',     {'electrod',      'marker'});
cfg = ft_checkconfig(cfg, 'renamed',     {'electcolor',    'markercolor'});
cfg = ft_checkconfig(cfg, 'renamed',     {'emsize',        'markersize'});
cfg = ft_checkconfig(cfg, 'renamed',     {'efsize',        'markerfontsize'});
cfg = ft_checkconfig(cfg, 'renamed',     {'headlimits',    'interplimits'});

% check for forbidden options
cfg = ft_checkconfig(cfg, 'forbidden',  {'hllinewidth', ...
  'headcolor', ...
  'hcolor', ...
  'hlinewidth', ...
  'contcolor', ...
  'outline', ...
  'highlightfacecolor', ...
  'showlabels'});

if ft_platform_supports('griddata-v4')
  default_interpmethod = 'v4';
else
  % Octave does not support 'v4', and 'cubic' is not yet implemented
  default_interpmethod = 'linear';
end

% set other config defaults
cfg.xlim              = ft_getopt(cfg, 'xlim',             'maxmin');
cfg.ylim              = ft_getopt(cfg, 'ylim',             'maxmin');
cfg.zlim              = ft_getopt(cfg, 'zlim',             'maxmin');
cfg.style             = ft_getopt(cfg, 'style',            'both');
cfg.gridscale         = ft_getopt(cfg, 'gridscale',         67);
cfg.interplimits      = ft_getopt(cfg, 'interplimits',     'head');
cfg.interpolation     = ft_getopt(cfg, 'interpolation',     default_interpmethod);
cfg.contournum        = ft_getopt(cfg, 'contournum',        6);
cfg.colormap          = ft_getopt(cfg, 'colormap',         'default');
cfg.colorbar          = ft_getopt(cfg, 'colorbar',         'no');
cfg.colorbartext      = ft_getopt(cfg, 'colorbartext',    '');
cfg.shading           = ft_getopt(cfg, 'shading',          'flat');
cfg.comment           = ft_getopt(cfg, 'comment',          'auto');
cfg.fontsize          = ft_getopt(cfg, 'fontsize',          8);
cfg.fontweight        = ft_getopt(cfg, 'fontweight',       'normal');
cfg.baseline          = ft_getopt(cfg, 'baseline',         'no'); % to avoid warning in timelock/freqbaseline
cfg.trials            = ft_getopt(cfg, 'trials',           'all', 1);
cfg.interactive       = ft_getopt(cfg, 'interactive',      'yes');
cfg.hotkeys           = ft_getopt(cfg, 'hotkeys',          'yes');
cfg.renderer          = ft_getopt(cfg, 'renderer',          []); % let MATLAB decide on the default
cfg.marker            = ft_getopt(cfg, 'marker',           'on');
cfg.markersymbol      = ft_getopt(cfg, 'markersymbol',     'o');
cfg.markercolor       = ft_getopt(cfg, 'markercolor',       [0 0 0]);
cfg.markersize        = ft_getopt(cfg, 'markersize',        2);
cfg.markerfontsize    = ft_getopt(cfg, 'markerfontsize',    8);
cfg.highlight         = ft_getopt(cfg, 'highlight',        'off');
cfg.highlightchannel  = ft_getopt(cfg, 'highlightchannel', 'all', 1); % highlight may be 'on', making highlightchannel {} meaningful
cfg.highlightsymbol   = ft_getopt(cfg, 'highlightsymbol',  '*');
cfg.highlightcolor    = ft_getopt(cfg, 'highlightcolor',    [0 0 0]);
cfg.highlightsize     = ft_getopt(cfg, 'highlightsize',     6);
cfg.highlightfontsize = ft_getopt(cfg, 'highlightfontsize', 8);
cfg.labeloffset       = ft_getopt(cfg, 'labeloffset',       0.005);
cfg.maskparameter     = ft_getopt(cfg, 'maskparameter',     []);
cfg.component         = ft_getopt(cfg, 'component',         []);
cfg.directionality    = ft_getopt(cfg, 'directionality',    []);
cfg.channel           = ft_getopt(cfg, 'channel',          'all');
cfg.refchannel        = ft_getopt(cfg, 'refchannel',        []);
cfg.figurename        = ft_getopt(cfg, 'figurename',        []);
cfg.interpolatenan    = ft_getopt(cfg, 'interpolatenan',   'yes');
cfg.commentpos        = ft_getopt(cfg, 'commentpos',       'layout');
cfg.scalepos          = ft_getopt(cfg, 'scalepos',         'layout');
cfg.figure            = ft_getopt(cfg, 'figure',           'yes');

% the user can either specify a single group of channels for highlighting
% which are all to be plotted in the same style, or multiple groups with a
% different style for each group. The latter is used by ft_clusterplot.
if iscell(cfg.highlightchannel) && ~isempty(cfg.highlightchannel) && ischar(cfg.highlightchannel{1})
  % it is a single cell-array with channels names, e.g. {'C1', 'Cz', 'C2'}
  cfg.highlightchannel = {cfg.highlightchannel};
elseif isnumeric(cfg.highlightchannel)
  % it is a numeric selection of channels, e.g. [1 2 3 4]
  cfg.highlightchannel = {cfg.highlightchannel};
elseif ischar(cfg.highlightchannel)
  % it is a single channel or single channel group, e.g. 'all'
  cfg.highlightchannel = {{cfg.highlightchannel}};
end
if ~iscell(cfg.highlight)
  cfg.highlight = {cfg.highlight};
end
if ~iscell(cfg.highlightsymbol)
  cfg.highlightsymbol = {cfg.highlightsymbol};
end
if ~iscell(cfg.highlightcolor)
  cfg.highlightcolor = {cfg.highlightcolor};
end
if ~iscell(cfg.highlightsize)
  cfg.highlightsize = {cfg.highlightsize};
end
if ~iscell(cfg.highlightfontsize)
  cfg.highlightfontsize = {cfg.highlightfontsize};
end
% make sure all cell-arrays for options are sufficiently long
ncellhigh = length(cfg.highlightchannel);
if length(cfg.highlight)          < ncellhigh,   cfg.highlight{ncellhigh}          = [];  end
if length(cfg.highlightsymbol)    < ncellhigh,   cfg.highlightsymbol{ncellhigh}    = [];  end
if length(cfg.highlightcolor)     < ncellhigh,   cfg.highlightcolor{ncellhigh}     = [];  end
if length(cfg.highlightsize)      < ncellhigh,   cfg.highlightsize{ncellhigh}      = [];  end
if length(cfg.highlightfontsize)  < ncellhigh,   cfg.highlightfontsize{ncellhigh}  = [];  end
% make sure all cell-arrays for options are not too long
cfg.highlight         (ncellhigh+1:end) = [];
cfg.highlightsymbol   (ncellhigh+1:end) = [];
cfg.highlightcolor    (ncellhigh+1:end) = [];
cfg.highlightsize     (ncellhigh+1:end) = [];
cfg.highlightfontsize (ncellhigh+1:end) = [];
% then default all empty cells
for icell = 1:ncellhigh
  if isempty(cfg.highlight{icell}),          cfg.highlight{icell} = 'on';          end
  if isempty(cfg.highlightsymbol{icell}),    cfg.highlightsymbol{icell} = 'o';     end
  if isempty(cfg.highlightcolor{icell}),     cfg.highlightcolor{icell} = [0 0 0];  end
  if isempty(cfg.highlightsize{icell}),      cfg.highlightsize{icell} = 6;         end
  if isempty(cfg.highlightfontsize{icell}),  cfg.highlightfontsize{icell} = 8;     end
end

% for backwards compatability
if strcmp(cfg.marker, 'highlights')
  ft_warning('cfg.marker=''highlights'' is no longer supported, please use cfg.highlight')
  cfg.marker = 'off';
end

% check if the colormap is in the proper format
if ~isequal(cfg.colormap, 'default')
  if ischar(cfg.colormap)
    cfg.colormap = ft_colormap(cfg.colormap);
  elseif iscell(cfg.colormap)
    cfg.colormap = ft_colormap(cfg.colormap{:});
  elseif isnumeric(cfg.colormap) && size(cfg.colormap,2)~=3
    ft_error('cfg.colormap must be Nx3');
  end
  % the actual colormap will be set below
end

Ndata = numel(varargin);
for indx=1:Ndata

  % open a new figure, or add it to the existing one
  open_figure(keepfields(cfg, {'figure', 'position', 'visible', 'renderer', 'figurename', 'title'}));

  % apply the same colormap to all figures
  if ~isempty(cfg.colormap)
    set(gcf,  'colormap', cfg.colormap);
  end

  if iscell(cfg.dataname)
    dataname = cfg.dataname{indx};
  else
    dataname = cfg.dataname;
  end

  data = varargin{indx};
  data = ft_checkdata(data, 'datatype', {'comp', 'timelock', 'freq'});

  %% Section 2: data handling, this also includes converting bivariate (chan_chan and chancmb) into univariate data

  dtype  = ft_datatype(data);
  hastime = isfield(data, 'time');

  % Set x/y/parameter defaults according to datatype and dimord
  switch dtype
    case 'timelock'
      xparam = ft_getopt(cfg, 'xparam', 'time');
      yparam = ft_getopt(cfg, 'yparam', '');
      if isfield(data, 'trial')
        cfg.parameter = ft_getopt(cfg, 'parameter', 'trial');
      elseif isfield(data, 'individual')
        cfg.parameter = ft_getopt(cfg, 'parameter', 'individual');
      elseif isfield(data, 'avg')
        cfg.parameter = ft_getopt(cfg, 'parameter', 'avg');
      end
    case 'freq'
      if hastime
        xparam = ft_getopt(cfg, 'xparam', 'time');
        yparam = ft_getopt(cfg, 'yparam', 'freq');
        cfg.parameter = ft_getopt(cfg, 'parameter', 'powspctrm');
      else
        xparam = 'freq';
        yparam = '';
        cfg.parameter = ft_getopt(cfg, 'parameter', 'powspctrm');
      end
    case 'comp'
      % Add a pseudo-axis with the component numbers
      data.comp = 1:size(data.topo,2);
      if ~isempty(cfg.component)
        % make a selection of components
        data.comp  = data.comp(cfg.component);
        data.topo  = data.topo(:,cfg.component);
        try, data.label     = data.label(cfg.component); end
        try, data.unmixing  = data.unmixing(cfg.component,:); end
      end
      % Rename the field with topographic label information
      data.label      = data.topolabel;
      data.topodimord = 'chan_comp';
      data = removefields(data, {'topolabel', 'unmixing', 'unmixingdimord'}); % not needed any more
      xparam = 'comp';
      yparam = '';
      cfg.parameter = ft_getopt(cfg, 'parameter', 'topo');
    otherwise
      % if the input data is not one of the standard data types, or if the functional
      % data is just one value per channel: in this case xparam, yparam are not defined
      % and the user should define the parameter
      if ~isfield(data, 'label'),     ft_error('the input data should at least contain a label-field');            end
      if ~isfield(cfg,  'parameter'), ft_error('the configuration should at least contain a ''parameter'' field'); end
      if ~isfield(cfg,  'xparam')
        cfg.xlim = [1 1];
        xparam   = '';
        yparam   = '';
      end
  end

  % check whether rpt/subj is present and remove if necessary
  dimord = getdimord(data, cfg.parameter);
  dimtok = tokenize(dimord, '_');
  hasrpt = any(ismember(dimtok, {'rpt' 'subj'}));

  if ~hasrpt
    assert(isequal(cfg.trials, 'all') || isequal(cfg.trials, 1), 'incorrect specification of cfg.trials for data without repetitions');
  else
    assert(~isempty(cfg.trials), 'empty specification of cfg.trials for data with repetitions');
  end

  % parse cfg.channel
  if isfield(cfg, 'channel') && isfield(data, 'label')
    cfg.channel = ft_channelselection(cfg.channel, data.label);
  elseif isfield(cfg, 'channel') && isfield(data, 'labelcmb')
    cfg.channel = ft_channelselection(cfg.channel, unique(data.labelcmb(:)));
  end

  % Apply baseline correction
  if ~strcmp(cfg.baseline, 'no')
    % keep mask-parameter if it is set
    if ~isempty(cfg.maskparameter)
      tempmask = data.(cfg.maskparameter);
    end
    tmpcfg = removefields(cfg, 'inputfile');
    if strcmp(xparam, 'time') && strcmp(yparam, 'freq')
      data = ft_freqbaseline(tmpcfg, data);
    elseif strcmp(xparam, 'time') && strcmp(yparam, '')
      data = ft_timelockbaseline(tmpcfg, data);
    end
    % put mask-parameter back if it is set
    if ~isempty(cfg.maskparameter)
      data.(cfg.maskparameter) = tempmask;
    end
  end

  % time and/or frequency should NOT be selected and averaged here, since a singleplot might follow in interactive mode
  tmpcfg = keepfields(cfg, {'channel', 'trials', 'showcallinfo', 'trackcallinfo', 'trackusage', 'trackdatainfo', 'trackmeminfo', 'tracktimeinfo', 'checksize'});
  if hasrpt
    tmpcfg.avgoverrpt = 'yes';
  else
    tmpcfg.avgoverrpt = 'no';
  end
  tmpvar = data;
  ws = ft_warning('off', 'FieldTrip:getdimord:warning_dimord_could_not_be_determined');
  [data] = ft_selectdata(tmpcfg, data);
  ft_warning(ws);
  % restore the provenance information
  [cfg, data] = rollback_provenance(cfg, data);

  if isfield(tmpvar, cfg.maskparameter) && ~isfield(data, cfg.maskparameter)
    % the mask parameter is not present after ft_selectdata, because it is
    % not included in all input arguments. Make the same selection and copy
    % it over
    tmpvar = ft_selectdata(tmpcfg, tmpvar);
    data.(cfg.maskparameter) = tmpvar.(cfg.maskparameter);
  end

  clear tmpvar tmpcfg dimord dimtok hastime hasfreq hasrpt

  % ensure that the preproc specific options are located in the cfg.preproc
  % substructure, but also ensure that the field 'refchannel' remains at the
  % highest level in the structure. This is a little hack by JM because the field
  % refchannel can relate to connectivity or to an EEG reference.

  if isfield(cfg, 'refchannel'), refchannelincfg = cfg.refchannel; cfg = rmfield(cfg, 'refchannel'); end
  cfg = ft_checkconfig(cfg, 'createsubcfg',  {'preproc'});
  if exist('refchannelincfg', 'var'), cfg.refchannel  = refchannelincfg; end

  if ~isempty(cfg.preproc)
    % preprocess the data, i.e. apply filtering, baselinecorrection, etc.
    fprintf('applying preprocessing options\n');
    if ~isfield(cfg.preproc, 'feedback')
      cfg.preproc.feedback = cfg.interactive;
    end
    data = ft_preprocessing(cfg.preproc, data);
  end

  % Handle the bivariate case
  dimord = getdimord(varargin{1}, cfg.parameter);
  if startsWith(dimord, 'chan_chan_') || startsWith(dimord, 'chancmb_')
    % convert the bivariate data to univariate and call the parent plotting function again
    s = dbstack;
    cfg.originalfunction = s(2).name;
    cfg.trials = 'all'; % trial selection has been taken care off
    bivariate_common(cfg, varargin{:});
    return
  end

  % Apply channel-type specific scaling
  tmpcfg = keepfields(cfg, {'parameter', 'chanscale', 'ecgscale', 'eegscale', 'emgscale', 'eogscale', 'gradscale', 'magscale', 'megscale', 'mychan', 'mychanscale'});
  data = chanscale_common(tmpcfg, data);

  %% Section 3: select the data to be plotted and determine min/max range

  dimord = getdimord(varargin{1}, cfg.parameter);
  dimtok = tokenize(dimord, '_');

  % Create time-series of small topoplots
  if ~ischar(cfg.xlim) && length(cfg.xlim)>2 %&& any(ismember(dimtok, 'time'))
    % Switch off interactive mode:
    cfg.interactive = 'no';
    xlims = cfg.xlim;
    % Iteratively call topoplotER with different xlim values:
    nplots = numel(xlims)-1;
    nyplot = ceil(sqrt(nplots));
    nxplot = ceil(nplots./nyplot);
    tmpcfg = removefields(cfg, 'inputfile');
    for i=1:length(xlims)-1
      tmpcfg.figure = subplot(nxplot, nyplot, i);
      tmpcfg.xlim = xlims(i:i+1);
      ft_topoplotTFR(tmpcfg, data);
    end
    return
  end

  % Get physical min/max range of x
  if ~isempty(xparam)
    if strcmp(cfg.xlim, 'maxmin')
      xmin = min(data.(xparam));
      xmax = max(data.(xparam));
    else
      xmin = cfg.xlim(1);
      xmax = cfg.xlim(2);
    end
    xminindx = nearest(data.(xparam), xmin);
    xmaxindx = nearest(data.(xparam), xmax);
    xmin = data.(xparam)(xminindx);
    xmax = data.(xparam)(xmaxindx);
    selx = xminindx:xmaxindx;
  end

  % Get physical min/max range of y
  if ~isempty(yparam)
    if strcmp(cfg.ylim, 'maxmin')
      ymin = min(data.(yparam));
      ymax = max(data.(yparam));
    else
      ymin = cfg.ylim(1);
      ymax = cfg.ylim(2);
    end
    yminindx = nearest(data.(yparam), ymin);
    ymaxindx = nearest(data.(yparam), ymax);
    ymin = data.(yparam)(yminindx);
    ymax = data.(yparam)(ymaxindx);
    sely = yminindx:ymaxindx;
  end

  % Take subselection of channels, this only works if the interactive mode is switched off
  if exist('selchannel', 'var')
    sellab = match_str(data.label, selchannel);
    label  = data.label(sellab);
  else
    sellab = 1:numel(data.label);
    label  = data.label;
  end

  % Make data vector with one scalar value for each channel
  dat = data.(cfg.parameter);
  % get dimord dimensions
  ydim = find(strcmp(yparam, dimtok));
  xdim = find(strcmp(xparam, dimtok));
  zdim = setdiff(1:ndims(dat), [ydim xdim]);
  % and permute
  dat = permute(dat, [zdim(:)' ydim xdim]);

  if ~isempty(yparam)
    % time-frequency data
    dat = dat(sellab, sely, selx);
    dat = nanmean(nanmean(dat, 3), 2);
  elseif ~isempty(cfg.component)
    % component data, nothing to do
  else
    % time or frequency data
    dat = dat(sellab, selx);
    dat = nanmean(dat, 2);
  end
  dat = dat(:);

  if isfield(data, cfg.maskparameter)
    % Make mask vector with one value for each channel
    msk = data.(cfg.maskparameter);
    % get dimord dimensions
    ydim = find(strcmp(yparam, dimtok));
    xdim = find(strcmp(xparam, dimtok));
    zdim = setdiff(1:ndims(dat), [ydim xdim]);
    % and permute
    msk = permute(msk, [zdim(:)' ydim xdim]);

    if ~isempty(yparam)
      % time-frequency data
      msk = msk(sellab, sely, selx);
    elseif ~isempty(cfg.component)
      % component data, nothing to do
    else
      % time or frequency data
      msk = msk(sellab, selx);
    end

    if size(msk,2)>1 || size(msk,3)>1
      ft_warning('no masking possible for average over multiple latencies or frequencies -> cfg.maskparameter cleared')
      msk = [];
    end

  else
    msk = [];
  end

  % Select the channels in the data that match with the layout:
  [seldat, sellay] = match_str(label, cfg.layout.label);
  if isempty(seldat)
    ft_error('labels in data and labels in layout do not match');
  end

  dat = dat(seldat);
  if ~isempty(msk)
    msk = msk(seldat);
  end

  % Select x and y coordinates and labels of the channels in the data
  chanX = cfg.layout.pos(sellay,1);
  chanY = cfg.layout.pos(sellay,2);

  % Get physical min/max range of z:
  if strcmp(cfg.zlim, 'maxmin')
    zmin = min(dat);
    zmax = max(dat);
  elseif strcmp(cfg.zlim, 'maxabs')
    zmin = -max(max(abs(dat)));
    zmax = max(max(abs(dat)));
  elseif strcmp(cfg.zlim, 'zeromax')
    zmin = 0;
    zmax = max(dat);
  elseif strcmp(cfg.zlim, 'minzero')
    zmin = min(dat);
    zmax = 0;
  else
    zmin = cfg.zlim(1);
    zmax = cfg.zlim(2);
  end

  % Construct comment
  switch cfg.comment
    case 'auto'
      comment = date;
      if ~isempty(xparam)
        comment = sprintf('%0s\n%0s=[%.3g %.3g]', comment, xparam, xmin, xmax);
      end
      if ~isempty(yparam)
        comment = sprintf('%0s\n%0s=[%.3g %.3g]', comment, yparam, ymin, ymax);
      end
      if ~isempty(cfg.parameter)
        comment = sprintf('%0s\n%0s=[%.3g %.3g]', comment, cfg.parameter, zmin, zmax);
      end
    case 'xlim'
      comment = '';
      if ~isempty(xparam)
        comment = sprintf('%0s=[%.3g %.3g]', xparam, xmin, xmax);
      end
    case 'ylim'
      comment = '';
      if ~isempty(yparam)
        comment = sprintf('%0s=[%.3g %.3g]', yparam, ymin, ymax);
      end
    case 'zlim'
      comment = sprintf('%0s=[%.3g %.3g]', cfg.parameter, zmin, zmax);
    otherwise
      comment = cfg.comment; % allow custom comments (e.g., ft_clusterplot specifies custom comments)
  end % switch comment

  if ~isempty(cfg.refchannel)
    if iscell(cfg.refchannel)
      comment = sprintf('%s\nreference=%s %s', comment, cfg.refchannel{:});
    else
      comment = sprintf('%s\nreference=%s %s', comment, cfg.refchannel);
    end
  end

  % Draw topoplot
  cla
  hold on

  % check for nans
  nanInds = isnan(dat);
  if strcmp(cfg.interpolatenan, 'yes') && any(nanInds)
    ft_warning('removing NaNs from the data');
    chanX(nanInds) = [];
    chanY(nanInds) = [];
    dat(nanInds)   = [];
    if ~isempty(msk)
      msk(nanInds) = [];
    end
  end

  % Set ft_plot_topo specific options
  if strcmp(cfg.interplimits, 'head')
    interplimits = 'mask';
  else
    interplimits = cfg.interplimits;
  end
  if strcmp(cfg.style, 'both');            style = 'surfiso';     end
  if strcmp(cfg.style, 'straight');        style = 'surf';        end
  if strcmp(cfg.style, 'contour');         style = 'iso';         end
  if strcmp(cfg.style, 'fill');            style = 'isofill';     end
  if strcmp(cfg.style, 'straight_imsat');  style = 'imsat';       end
  if strcmp(cfg.style, 'both_imsat');      style = 'imsatiso';    end

  % Draw plot
  if strcmp(cfg.style, 'blank')
    ft_plot_layout(cfg.layout, 'box', 'no', 'label', 'no', 'point', 'no')
  else
    opt = {};
    opt = ft_setopt(opt, 'interpmethod',  cfg.interpolation);
    opt = ft_setopt(opt, 'interplim',     interplimits);
    opt = ft_setopt(opt, 'gridscale',     cfg.gridscale);
    opt = ft_setopt(opt, 'outline',       cfg.layout.outline);
    opt = ft_setopt(opt, 'shading',       cfg.shading);
    opt = ft_setopt(opt, 'isolines',      cfg.contournum);
    opt = ft_setopt(opt, 'mask',          cfg.layout.mask);
    opt = ft_setopt(opt, 'style',         style);
    opt = ft_setopt(opt, 'datmask',       msk);
    if strcmp(style, 'imsat') || strcmp(style, 'imsatiso')
      opt = ft_setopt(opt, 'clim',  [zmin zmax]);
    end
    ft_plot_topo(chanX, chanY, dat, opt{:});
  end

  % For Highlight (channel-selection)
  for icell = 1:length(cfg.highlight)
    if ~strcmp(cfg.highlight{icell}, 'off')
      cfg.highlightchannel{icell} = ft_channelselection(cfg.highlightchannel{icell}, data.label);
      [dum, layoutindex] = match_str(cfg.highlightchannel{icell}, cfg.layout.label);
      templay = [];
      templay.outline = cfg.layout.outline;
      templay.mask    = cfg.layout.mask;
      templay.pos     = cfg.layout.pos(layoutindex,:);
      templay.width   = cfg.layout.width(layoutindex);
      templay.height  = cfg.layout.height(layoutindex);
      templay.label   = cfg.layout.label(layoutindex);
      if strcmp(cfg.highlight{icell}, 'labels') || strcmp(cfg.highlight{icell}, 'numbers')
        labelflg = 1;
      else
        labelflg = 0;
      end
      if strcmp(cfg.highlight{icell}, 'numbers')
        for ichan = 1:length(layoutindex)
          templay.label{ichan} = num2str(match_str(data.label, templay.label{ichan}));
        end
      end

      ft_plot_layout(templay, 'box', 'no', 'label', labelflg, 'point', ~labelflg, ...
        'pointsymbol',  cfg.highlightsymbol{icell}, ...
        'pointcolor',   cfg.highlightcolor{icell}, ...
        'pointsize',    cfg.highlightsize{icell}, ...
        'fontsize',     cfg.highlightfontsize{icell}, ...
        'labeloffset',  cfg.labeloffset, ...
        'labelalignh', 'center', ...
        'labelalignv', 'middle');
    end
  end % for icell

  % For Markers (all channels)
  switch cfg.marker
    case {'off', 'no'}
      % do not show the markers
    case {'on', 'labels', 'numbers'}
      channelsToMark = 1:length(data.label);
      channelsToHighlight = [];
      for icell = 1:length(cfg.highlight)
        if ~strcmp(cfg.highlight{icell}, 'off')
          channelsToHighlight = [channelsToHighlight; match_str(data.label, cfg.highlightchannel{icell})];
        end
      end
      if strcmp(cfg.interpolatenan, 'no')
        channelsNotMark = channelsToHighlight;
      else
        channelsNotMark = union(find(isnan(dat)), channelsToHighlight);
      end
      channelsToMark(channelsNotMark) = [];
      [dum, layoutindex] = match_str(ft_channelselection(channelsToMark, data.label), cfg.layout.label);
      templay = [];
      templay.outline = cfg.layout.outline;
      templay.mask    = cfg.layout.mask;
      templay.pos     = cfg.layout.pos(layoutindex,:);
      templay.width   = cfg.layout.width(layoutindex);
      templay.height  = cfg.layout.height(layoutindex);
      templay.label   = cfg.layout.label(layoutindex);
      if strcmp(cfg.marker, 'labels') || strcmp(cfg.marker, 'numbers')
        labelflg = 1;
      else
        labelflg = 0;
      end
      if strcmp(cfg.marker, 'numbers')
        for ichan = 1:length(layoutindex)
          templay.label{ichan} = num2str(match_str(data.label,templay.label{ichan}));
        end
      end
      ft_plot_layout(templay, 'box', 'no', 'label', labelflg, 'point', ~labelflg, ...
        'pointsymbol',  cfg.markersymbol, ...
        'pointcolor',   cfg.markercolor, ...
        'pointsize',    cfg.markersize, ...
        'fontsize',     cfg.markerfontsize, ...
        'labeloffset',  cfg.labeloffset, ...
        'labelalignh', 'center', ...
        'labelalignv', 'middle');
    otherwise
      ft_error('incorrect value for cfg.marker');
  end

  if isfield(cfg, 'vector')
    % FIXME this is not documented
    vecX = nanmean(real(data.(cfg.vector)(:,selx)), 2);
    vecY = nanmean(imag(data.(cfg.vector)(:,selx)), 2);

    % scale quiver relative to largest gradiometer sample
    k = 0.15/max([max(abs(real(data.(cfg.vector)(:)))) max(abs(imag(data.(cfg.vector)(:))))]);
    quiver(chanX, chanY, k*vecX, k*vecY, 0, 'red');
  end

  % Write comment
  if strcmp(cfg.comment, 'no')
    comment_handle = [];
  elseif strcmp(cfg.commentpos, 'title')
    comment_handle = title(comment, 'FontSize', cfg.fontsize);
  elseif ~isempty(strcmp(cfg.layout.label, 'COMNT'))
    x_comment = cfg.layout.pos(strcmp(cfg.layout.label, 'COMNT'), 1);
    y_comment = cfg.layout.pos(strcmp(cfg.layout.label, 'COMNT'), 2);
    % 'HorizontalAlignment', 'left', 'VerticalAlignment', 'bottom',
    comment_handle = ft_plot_text(x_comment, y_comment, comment, 'FontSize', cfg.fontsize, 'FontWeight', cfg.fontweight);
  else
    comment_handle = [];
  end

  % Set colour axis
  if ~strcmp(cfg.style, 'blank')
    caxis([zmin zmax]);
  end

  % Plot colorbar
  if isfield(cfg, 'colorbar')
    if strcmp(cfg.colorbar, 'yes')
      c = colorbar;
      ylabel(c, cfg.colorbartext);
    elseif ~strcmp(cfg.colorbar, 'no')
      c = colorbar('location', cfg.colorbar);
      ylabel(c, cfg.colorbartext);
    end
  end

  % set the figure window title, but only if the user has not changed it
  if isempty(get(gcf, 'Name'))
    if isfield(cfg, 'funcname')
      funcname = cfg.funcname;
    else
      funcname = mfilename;
    end
    if isempty(cfg.figurename)
      dataname_str = join_str(', ', dataname);
      set(gcf, 'Name', sprintf('%d: %s: %s', double(gcf), funcname, dataname_str));
      set(gcf, 'NumberTitle', 'off');
    else
      set(gcf, 'name', cfg.figurename);
      set(gcf, 'NumberTitle', 'off');
    end
  end

  axis off
  hold off
  axis equal

  if strcmp('yes', cfg.hotkeys)
    %  Attach data and cfg to figure and attach a key listener to the figure
    set(gcf, 'KeyPressFcn', {@key_sub, zmin, zmax})
  end

  % Make the figure interactive
  if strcmp(cfg.interactive, 'yes')
    % add the cfg/data/channel information to the figure under identifier linked to this axis
    ident                    = ['axh' num2str(round(sum(clock.*1e6)))]; % unique identifier for this axis
    set(gca, 'tag',ident);
    info                     = guidata(gcf);
    info.(ident).x           = cfg.layout.pos(:, 1);
    info.(ident).y           = cfg.layout.pos(:, 2);
    info.(ident).label       = cfg.layout.label;
    info.(ident).dataname    = dataname;
    info.(ident).cfg         = cfg;
    info.(ident).commenth    = comment_handle;
    if exist('linecolor', 'var')
      info.(ident).linecolor   = linecolor;
    end
    if ~isfield(info.(ident),'datvarargin')
      info.(ident).datvarargin = varargin(1:Ndata); % add all datasets to figure
    end
    info.(ident).datvarargin{indx} = data; % update current dataset (e.g. baselined, channel selection, etc)
    guidata(gcf, info);
    if any(strcmp(dimord, {'chan_time', 'chan_freq', 'subj_chan_time', 'rpt_chan_time', 'chan_chan_freq', 'chancmb_freq', 'rpt_chancmb_freq', 'subj_chancmb_freq'}))
      set(gcf, 'WindowButtonUpFcn',     {@ft_select_channel, 'multiple', true, 'callback', {@select_singleplotER}, 'event', 'WindowButtonUpFcn'});
      set(gcf, 'WindowButtonDownFcn',   {@ft_select_channel, 'multiple', true, 'callback', {@select_singleplotER}, 'event', 'WindowButtonDownFcn'});
      set(gcf, 'WindowButtonMotionFcn', {@ft_select_channel, 'multiple', true, 'callback', {@select_singleplotER}, 'event', 'WindowButtonMotionFcn'});
    elseif any(strcmp(dimord, {'chan_freq_time', 'subj_chan_freq_time', 'rpt_chan_freq_time', 'rpttap_chan_freq_time', 'chan_chan_freq_time', 'chancmb_freq_time', 'rpt_chancmb_freq_time', 'subj_chancmb_freq_time'}))
      set(gcf, 'WindowButtonUpFcn',     {@ft_select_channel, 'multiple', true, 'callback', {@select_singleplotTFR}, 'event', 'WindowButtonUpFcn'});
      set(gcf, 'WindowButtonDownFcn',   {@ft_select_channel, 'multiple', true, 'callback', {@select_singleplotTFR}, 'event', 'WindowButtonDownFcn'});
      set(gcf, 'WindowButtonMotionFcn', {@ft_select_channel, 'multiple', true, 'callback', {@select_singleplotTFR}, 'event', 'WindowButtonMotionFcn'});
    else
      ft_warning('unsupported dimord "%s" for interactive plotting', dimord);
    end
  end

end % for numel(varargin)

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION which is called after selecting channels in case of cfg.interactive='yes'
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function select_singleplotER(label)
ident       = get(gca, 'tag');
info        = guidata(gcf);
cfg         = info.(ident).cfg;
datvarargin = info.(ident).datvarargin;
linecolor   = ft_getopt(info.(ident), 'linecolor', lineattributes_common(cfg, datvarargin{:}));

if ~isempty(label)
  cfg = removefields(cfg, 'inputfile');       % the reading has already been done and varargin contains the data
  cfg.baseline = 'no';                        % make sure the next function does not apply a baseline correction again
  cfg.channel = label;
  cfg.dataname = info.(ident).cfg.dataname;   % put data name in here, this cannot be resolved by other means
  cfg.trials = 'all';                         % trial selection has already been taken care of
  cfg.xlim = 'maxmin';
  % if user specified a zlim, copy it over to the ylim of singleplot
  if isfield(cfg, 'zlim')
    cfg.ylim = cfg.zlim;
    cfg = rmfield(cfg, 'zlim');
  end
  fprintf('selected cfg.channel = {%s}\n', join_str(', ', cfg.channel));
  % ensure that the new figure appears at the same position
  cfg.figure = 'yes';
  cfg.position = get(gcf, 'Position');

  selchan = match_str(datvarargin{1}.label, cfg.channel);
  cfg.linecolor = linecolor(selchan, :, :); % make a subselection for the correct inheritance of the line colors
  ft_singleplotER(cfg, datvarargin{:});
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION which is called after selecting channels in case of cfg.interactive='yes'
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function select_singleplotTFR(label)
ident       = get(gca, 'tag');
info        = guidata(gcf);
cfg         = info.(ident).cfg;
datvarargin = info.(ident).datvarargin;
if ~isempty(label)
  cfg = removefields(cfg, 'inputfile');   % the reading has already been done and varargin contains the data
  cfg.baseline = 'no';                    % make sure the next function does not apply a baseline correction again
  cfg.channel = label;
  cfg.dataname = info.(ident).dataname;   % put data name in here, this cannot be resolved by other means
  cfg.trials = 'all';                     % trial selection has already been taken care of
  cfg.xlim = 'maxmin';
  cfg.ylim = 'maxmin';
  fprintf('selected cfg.channel = {%s}\n', join_str(', ', cfg.channel));
  % ensure that the new figure appears at the same position
  cfg.figure = 'yes';
  cfg.position = get(gcf, 'Position');
  ft_singleplotTFR(cfg, datvarargin{:});
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% SUBFUNCTION which handles hot keys in the current plot
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function key_sub(handle, eventdata, varargin)
ident       = get(gca, 'tag');
info        = guidata(gcf);

climits = caxis;
incr_c  = abs(climits(2) - climits(1)) /10;

newz = climits;
if length(eventdata.Modifier) == 1 && strcmp(eventdata.Modifier{:}, 'control')
  % TRANSLATE by 10%
  switch eventdata.Key
    case 'pageup'
      newz = [climits(1)+incr_c climits(2)+incr_c];
    case 'pagedown'
      newz = [climits(1)-incr_c climits(2)-incr_c];
  end % switch
else
  % ZOOM by 10%
  switch eventdata.Key
    case 'pageup'
      newz = [climits(1)-incr_c climits(2)+incr_c];
    case 'pagedown'
      newz = [climits(1)+incr_c climits(2)-incr_c];
    case 'm'
      newz = [varargin{1} varargin{2}];
  end % switch
end % if

% update the color axis
caxis(newz);

if ~isempty(ident) && isfield(info.(ident), 'commenth') && ~isempty(info.(ident).commenth)
  commentstr = get(info.(ident).commenth, 'string');
  sel        = contains(commentstr, info.(ident).cfg.parameter);
  if any(sel)
    commentstr{sel} = sprintf('%0s=[%.3g %.3g]', info.(ident).cfg.parameter, newz(1), newz(2));
    set(info.(ident).commenth, 'string', commentstr);
  end
end


method = 'manual';  % random, uniform, manual
frames = 200;       % 50 100 150 200

ms_regular_training('baseline/highway', method, frames);
ms_regular_training('baseline/office', method, frames);
ms_regular_training('baseline/pedestrians', method, frames);
ms_regular_training('baseline/PETS2006', method, frames);

ms_regular_training('badWeather/blizzard', method, frames);
ms_regular_training('badWeather/skating', method, frames);
ms_regular_training('badWeather/snowFall', method, frames);
ms_regular_training('badWeather/wetSnow', method, frames);

ms_regular_training('cameraJitter/badminton', method, frames);
ms_regular_training('cameraJitter/boulevard', method, frames);
ms_regular_training('cameraJitter/sidewalk', method, frames);
ms_regular_training('cameraJitter/traffic', method, frames);

ms_regular_training('dynamicBackground/boats', method, frames);
ms_regular_training('dynamicBackground/canoe', method, frames);
ms_regular_training('dynamicBackground/fall', method, frames);
ms_regular_training('dynamicBackground/fountain01', method, frames);
ms_regular_training('dynamicBackground/fountain02', method, frames);
ms_regular_training('dynamicBackground/overpass', method, frames);

ms_regular_training('intermittentObjectMotion/abandonedBox', method, frames);
ms_regular_training('intermittentObjectMotion/parking', method, frames);
ms_regular_training('intermittentObjectMotion/sofa', method, frames);
ms_regular_training('intermittentObjectMotion/streetLight', method, frames);
ms_regular_training('intermittentObjectMotion/tramstop', method, frames);
ms_regular_training('intermittentObjectMotion/winterDriveway', method, frames);

ms_regular_training('lowFramerate/port_0_17fps', method, frames);
ms_regular_training('lowFramerate/tramCrossroad_1fps', method, frames);
ms_regular_training('lowFramerate/tunnelExit_0_35fps', method, frames);
ms_regular_training('lowFramerate/turnpike_0_5fps', method, frames);

ms_regular_training('nightVideos/bridgeEntry', method, frames);
ms_regular_training('nightVideos/busyBoulvard', method, frames);
ms_regular_training('nightVideos/fluidHighway', method, frames);
ms_regular_training('nightVideos/streetCornerAtNight', method, frames);
ms_regular_training('nightVideos/tramStation', method, frames);
ms_regular_training('nightVideos/winterStreet', method, frames);

ms_regular_training('PTZ/continuousPan', method, frames);
ms_regular_training('PTZ/intermittentPan', method, frames);
ms_regular_training('PTZ/twoPositionPTZCam', method, frames);
ms_regular_training('PTZ/zoomInZoomOut', method, frames);

ms_regular_training('shadow/backdoor', method, frames);
ms_regular_training('shadow/bungalows', method, frames);
ms_regular_training('shadow/busStation', method, frames);
ms_regular_training('shadow/copyMachine', method, frames);
ms_regular_training('shadow/cubicle', method, frames);
ms_regular_training('shadow/peopleInShade', method, frames);

ms_regular_training('thermal/corridor', method, frames);
ms_regular_training('thermal/diningRoom', method, frames);
ms_regular_training('thermal/lakeSide', method, frames);
ms_regular_training('thermal/library', method, frames);
ms_regular_training('thermal/park', method, frames);

ms_regular_training('turbulence/turbulence0', method, frames);
ms_regular_training('turbulence/turbulence1', method, frames);
ms_regular_training('turbulence/turbulence2', method, frames);
ms_regular_training('turbulence/turbulence3', method, frames);


function model = ZF_for_Fast_RCNN_VOC0712(model)

model.solver_def_file        = fullfile(pwd, 'models', 'fast_rcnn_prototxts', 'ZF', 'solver_30k60k.prototxt');
model.test_net_def_file      = fullfile(pwd, 'models', 'fast_rcnn_prototxts', 'ZF', 'test.prototxt');

model.net_file               = fullfile(pwd, 'models', 'pre_trained_models', 'ZF', 'ZF.caffemodel');
model.mean_image             = fullfile(pwd, 'models', 'pre_trained_models', 'ZF', 'mean_image');

end