function model = person_init_grammar()
% Initialize the structure and filters of the person grammar model.
%   model = person_init_grammar()

% AUTORIGHTS
% -------------------------------------------------------
% Copyright (C) 2011-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.
% -------------------------------------------------------

model = train_full_person_2x_res();
model = make_person_grammar_occ_def(model);

% --------------------------------------------------------------------
function model = train_full_person_2x_res()
% Train a root filter for (roughly) fully visible people.
%   model = train_full_person_2x_res()
%
%   This root filter is at about 2x the resolution of the root filter
%   used for the mixture models because it will be divided up into
%   high resolution parts. Fully visible people are estimated to be
%   the top third most vertically elongated examples.

conf = voc_config();
cachedir = conf.paths.model_dir;

try
  load([cachedir 'person_full_person_2x']);
catch
  seed_rand();
  cls = 'person';
  note = 'full person trained at 2x resolution';
  max_num_examples = conf.training.cache_example_limit;
  fg_overlap = conf.training.fg_overlap;

  [pos, neg, impos] = pascal_data(cls, conf.pascal.year);
  % Split data by aspect ratio into n groups
  spos = split(pos, 3);
  spos = spos{3};

  try
    load([cachedir 'person_model_full_person_2x_1_1_1']);
    load([cachedir 'person_model_full_person_2x_pos_inds']);
  catch
    model = root_model(cls, spos, note, 8, [23 8]);
    % Allow root detections in the first pyramid octave
    bl = model.rules{model.start}(1).loc.blocklabel;
    model.blocks(bl).w(:) = 0;

    inds = lrsplit(model, spos);
    % Train with warpped positives and random negatives
    model = train(model, spos(inds), neg, true, true, 1, 1, ...
                  max_num_examples, fg_overlap, 0, false, 'full_person_2x_1');
    save([cachedir 'person_model_full_person_2x_pos_inds'], 'inds');
  end
  % Train with latent postives and hard negatives
  model = train(model, spos(inds), neg(1:200), false, false, 1, 20, ...
                max_num_examples, fg_overlap, 0, false, 'full_person_2x_2');

  save([cachedir cls '_full_person_2x'], 'model');
end

%-------------------------------------------------------------------------
function M = make_person_grammar_occ_def(model)
% Construct a person grammar model with 6 levels of occlusion, deformable
% parts, and an 'occluder' part.
%   M = make_person_grammar_occ_def(model)
%
% Return value
%   M         The person grammar model
%
% Arugment
%   model     The root only full person model output by
%             train_full_person_2x_res()

conf = voc_config();
cachedir = conf.paths.model_dir;

cls = 'person';
note = 'NIPS 2011 person grammar model';

%{{{ Initialize a fresh model
  M = model_create(cls, note);
  M.type = model_types.Grammar;

  % Start non-terminal
  [M, Q] = model_add_nonterminal(M);
  M.start = Q;
%}}}

%{{{ Filters
  % Get the root filter for fully visible people
  w = model_get_block(model, model.filters(1));
  % Divide it into 6 pieces
  X_f = w(1:8, :, :);     % Head+shoulders
  Y1_f = w(9:11, :, :);   % and so on ...
  Y2_f = w(12:14, :, :);
  Y3_f = w(15:17, :, :);
  Y4_f = w(18:20, :, :);
  Y5_f = w(21:23, :, :);  % ... feet
  % Occluder filter (initially all zeros)
  O_f = zeros(4, 8, size(w, 3));

  % Create the set of terminals: one for each filter
  % and its left-right mirrored counterpart
  % Notation: S_{l,r}f  =>  terminal symbol S, l = left, r = right
  [M, X_lf]  = model_add_terminal(M, 'w', X_f);
  [M, X_rf]  = model_add_terminal(M, 'mirror_terminal', X_lf);
  [M, Y1_lf] = model_add_terminal(M, 'w', Y1_f);
  [M, Y1_rf] = model_add_terminal(M, 'mirror_terminal', Y1_lf);
  [M, Y2_lf] = model_add_terminal(M, 'w', Y2_f);
  [M, Y2_rf] = model_add_terminal(M, 'mirror_terminal', Y2_lf);
  [M, Y3_lf] = model_add_terminal(M, 'w', Y3_f);
  [M, Y3_rf] = model_add_terminal(M, 'mirror_terminal', Y3_lf);
  [M, Y4_lf] = model_add_terminal(M, 'w', Y4_f);
  [M, Y4_rf] = model_add_terminal(M, 'mirror_terminal', Y4_lf);
  [M, Y5_lf] = model_add_terminal(M, 'w', Y5_f);
  [M, Y5_rf] = model_add_terminal(M, 'mirror_terminal', Y5_lf);
  [M, O_lf]  = model_add_terminal(M, 'w', O_f);
  [M, O_rf]  = model_add_terminal(M, 'mirror_terminal', O_lf);
%}}}

%{{{ Nonterminals and deformation rules for placing the terminals
  % Initial deformation model
  def_w = [0.01 0 0.01 0];

  % Symbols for the lhs of the deformation rules
  % Parts 2-6 and the occluder can move, part 1 (head) is fixed
  [M, Y1_l] = model_add_nonterminal(M);
  [M, Y2_l] = model_add_nonterminal(M);
  [M, Y3_l] = model_add_nonterminal(M);
  [M, Y4_l] = model_add_nonterminal(M);
  [M, Y5_l] = model_add_nonterminal(M);
  [M, O_l]  = model_add_nonterminal(M);
  [M, Y1_r] = model_add_nonterminal(M);
  [M, Y2_r] = model_add_nonterminal(M);
  [M, Y3_r] = model_add_nonterminal(M);
  [M, Y4_r] = model_add_nonterminal(M);
  [M, Y5_r] = model_add_nonterminal(M);
  [M, O_r]  = model_add_nonterminal(M);

  [M, rule] = model_add_def_rule(M, Y1_l, Y1_lf, 'def_w', def_w);
  [M, rule] = model_add_def_rule(M, Y1_r, Y1_rf, 'mirror_rule', rule);

  [M, rule] = model_add_def_rule(M, Y2_l, Y2_lf, 'def_w', def_w);
  [M, rule] = model_add_def_rule(M, Y2_r, Y2_rf, 'mirror_rule', rule);

  [M, rule] = model_add_def_rule(M, Y3_l, Y3_lf, 'def_w', def_w);
  [M, rule] = model_add_def_rule(M, Y3_r, Y3_rf, 'mirror_rule', rule);

  [M, rule] = model_add_def_rule(M, Y4_l, Y4_lf, 'def_w', def_w);
  [M, rule] = model_add_def_rule(M, Y4_r, Y4_rf, 'mirror_rule', rule);

  [M, rule] = model_add_def_rule(M, Y5_l, Y5_lf, 'def_w', def_w);
  [M, rule] = model_add_def_rule(M, Y5_r, Y5_rf, 'mirror_rule', rule);

  [M, rule] = model_add_def_rule(M, O_l, O_lf, 'def_w', def_w);
  [M, rule] = model_add_def_rule(M, O_r, O_rf, 'mirror_rule', rule);
%}}}

%{{{ Add structural rules for deriving parts

  % The following rules derive either the (deformed) left-facing or
  % right-facing subtype.
  %  X  -> X_lf | X_rf
  %  Yi -> Yi_l | Yi_r
  %  O  -> O_l  | O_r

  [M, X]  = model_add_nonterminal(M);
  [M, Y1] = model_add_nonterminal(M);
  [M, Y2] = model_add_nonterminal(M);
  [M, Y3] = model_add_nonterminal(M);
  [M, Y4] = model_add_nonterminal(M);
  [M, Y5] = model_add_nonterminal(M);
  [M, O]  = model_add_nonterminal(M);

  [M, rule] = model_add_struct_rule(M, X, X_lf, {[0 0 0]});
  [M, rule] = model_add_struct_rule(M, X, X_rf, {[0 0 0]}, 'mirror_rule', rule);
  M.blocks(rule.offset.blocklabel).learn = 0;

  [M, rule] = model_add_struct_rule(M, Y1, Y1_l, {[0 0 0]});
  [M, rule] = model_add_struct_rule(M, Y1, Y1_r, {[0 0 0]}, 'mirror_rule', rule);
  M.blocks(rule.offset.blocklabel).learn = 0;

  [M, rule] = model_add_struct_rule(M, Y2, Y2_l, {[0 0 0]});
  [M, rule] = model_add_struct_rule(M, Y2, Y2_r, {[0 0 0]}, 'mirror_rule', rule);
  M.blocks(rule.offset.blocklabel).learn = 0;

  [M, rule] = model_add_struct_rule(M, Y3, Y3_l, {[0 0 0]});
  [M, rule] = model_add_struct_rule(M, Y3, Y3_r, {[0 0 0]}, 'mirror_rule', rule);
  M.blocks(rule.offset.blocklabel).learn = 0;

  [M, rule] = model_add_struct_rule(M, Y4, Y4_l, {[0 0 0]});
  [M, rule] = model_add_struct_rule(M, Y4, Y4_r, {[0 0 0]}, 'mirror_rule', rule);
  M.blocks(rule.offset.blocklabel).learn = 0;

  [M, rule] = model_add_struct_rule(M, Y5, Y5_l, {[0 0 0]});
  [M, rule] = model_add_struct_rule(M, Y5, Y5_r, {[0 0 0]}, 'mirror_rule', rule);
  M.blocks(rule.offset.blocklabel).learn = 0;

  [M, rule] = model_add_struct_rule(M, O, O_l, {[0 0 0]});
  [M, rule] = model_add_struct_rule(M, O, O_r, {[0 0 0]}, 'mirror_rule', rule);
  M.blocks(rule.offset.blocklabel).learn = 0;
%}}}

%{{{ Add top-level structural rules

  % The following rules derive people of varying degrees of visibility
  % from head to toe.
  %  Q -> X O | X Y1 O | X Y1 Y2 O | ... | X Y1 Y2 Y3 Y4 O | X Y1 Y2 Y3 Y4 Y5

  [M, rule] = model_add_struct_rule(M, Q, [X O], ...
                                    {[0 0 0], [0 8 0]}, ...
                                    'detection_window', [8 8]);
  M.blocks(rule.loc.blocklabel).learn = 1;
  M.blocks(rule.loc.blocklabel).reg_mult = 1;

  [M, rule] = model_add_struct_rule(M, Q, [X Y1 O], ...
                                    {[0 0 0], [0 8 0], [0 11 0]}, ...
                                    'detection_window', [11 8]);
  M.blocks(rule.loc.blocklabel).learn = 1;
  M.blocks(rule.loc.blocklabel).reg_mult = 1;

  [M, rule] = model_add_struct_rule(M, Q, [X Y1 Y2 O], ...
                                    {[0 0 0], [0 8 0], [0 11 0], [0 14 0]}, ...
                                    'detection_window', [14 8]);
  M.blocks(rule.loc.blocklabel).learn = 1;
  M.blocks(rule.loc.blocklabel).reg_mult = 1;

  [M, rule] = model_add_struct_rule(M, Q, [X Y1 Y2 Y3 O], ...
                                    {[0 0 0], [0 8 0], [0 11 0], [0 14 0], [0 17 0]}, ...
                                    'detection_window', [17 8]);
  M.blocks(rule.loc.blocklabel).learn = 1;
  M.blocks(rule.loc.blocklabel).reg_mult = 1;

  [M, rule] = model_add_struct_rule(M, Q, [X Y1 Y2 Y3 Y4 O], ...
                                    {[0 0 0], [0 8 0], [0 11 0], [0 14 0], [0 17 0], [0 20 0]}, ...
                                    'detection_window', [20 8]);
  M.blocks(rule.loc.blocklabel).learn = 1;
  M.blocks(rule.loc.blocklabel).reg_mult = 1;

  [M, rule] = model_add_struct_rule(M, Q, [X Y1 Y2 Y3 Y4 Y5], ...
                                    {[0 0 0], [0 8 0], [0 11 0], [0 14 0], [0 17 0], [0 20 0]}, ...
                                    'detection_window', [23 8]);
  M.blocks(rule.loc.blocklabel).learn = 1;
  M.blocks(rule.loc.blocklabel).reg_mult = 1;
%}}}

model = M;
save([cachedir cls '_simple_grammar_occ_def'], 'model');


function s=get_name(a)
s=[get_name(a.algorithm)];
eval_name

n=a.func; if length(n)>6 n=[n(1:6) '..']; end;
s=[s  '(' n ')'];

if ~isempty(a.p1) s=[s ' p1=' dis(a.p1)]; end
if ~isempty(a.p2) s=[s ' p2=' dis(a.p2)]; end
if ~isempty(a.p3) s=[s ' p3=' dis(a.p3)]; end
if ~isempty(a.p4) s=[s ' p4=' dis(a.p4)]; end

function s=dis(s)

  if isa(s,'double')
    s=num2str(s);
  else
    if isa(s,'algorithm')
      s=get_name(s);
    end
  end


function h_cb= plotutil_colorbarAside(varargin)
%PLOT_COLORBARASIDE - Add a colorbar without shrinking the axis
%
%Synopsis:
% H_CB= plotutil_colorbarAside(<OPT>)
% H_CB= plotutil_colorbarAside(ORIENTATION, <OPT>)
%
%Input:
% ORIENTATION: see OPT.Orientation
% OPT: struct or property/value list of optional properties
%  .orientation - orientation resp. location of the colorbar relative
%                 to the axis: 'vert','horiz', 'NorthOutside',
%                 'EastOutside','SouthOutside', 'WestOutside'
%
%Output:
% HCB: handle of the colorbar
%
%Note:
% So far, plotutil_colorbarAside is compatible with Matlab 6, but it allows
% the new orientation modes of Matlab 7.

% blanker@cs.tu-berlin.de

props = {'Orientation'      'vert'      '!CHAR(vert horiz NorthOutside EastOutside SouthOutside WestOutside)';
         'Gap'              0.02        '!DOUBLE';
         };

if mod(nargin, 2)==1,
  varargin = {'Orientation'  varargin{:}};
end

opt= opt_proplistToStruct(varargin{:});
[opt,isdefault] = opt_setDefaults(opt, props);
opt_checkProplist(opt, props);

ax= gca;
pos= get(ax, 'position');
% this has an influence on the behavious when resizing the figure:
epos= axis_getEffectivePosition(ax);
% this would require Matlab 7:
% h_cb= colorbar(opt.Orientation);
if ismember(lower(opt.Orientation), {'horiz','northoutside','southoutside'},'legacy'),
  h_cb= colorbar('horiz');
%  h_cb= colorbar('SouthOutside');
else
  h_cb= colorbar('EastOutside');
end
set(ax, 'position',pos);
drawnow;
cb_pos= get(h_cb, 'position');
%ii= strmatch(opt.Orientation, {'vert','horiz'}, 'exact');
%cb_pos(ii)= pos(ii)+pos(ii+2)+0.02;
%cb_pos(5-ii)= min(cb_pos(5-ii), epos(5-ii));
%cb_pos(3-ii)= pos(3-ii) + (pos(5-ii)-cb_pos(5-ii))/2;
switch(lower(opt.Orientation)),
 case {'vert','eastoutside'},
  cb_pos(1)= pos(1) + pos(3) + opt.Gap;
  cb_pos(4)= min(cb_pos(4), epos(4));
  cb_pos(2)= pos(2) + (pos(4)-cb_pos(4))/2;
 case 'westoutside',
  cb_pos(1)= pos(1) - cb_pos(3) - opt.Gap;
  cb_pos(4)= min(cb_pos(4), epos(4));
  cb_pos(2)= pos(2) + (pos(4)-cb_pos(4))/2;
  set(h_cb, 'YAxisLocation','left');
 case {'horiz','southoutside'},
  cb_pos(2)= pos(2) - cb_pos(4) - opt.Gap;
  cb_pos(3)= min(cb_pos(3), epos(3));
  cb_pos(1)= pos(1) + (pos(3)-cb_pos(3))/2;
 case 'northoutside',
  cb_pos(2)= pos(2) + pos(4) + opt.Gap;
  cb_pos(3)= min(cb_pos(3), epos(3));
  cb_pos(1)= pos(1) + (pos(3)-cb_pos(3))/2;
  set(h_cb, 'XAxisLocation','top');
 otherwise,
  error('unknown orientation/location');
end
set(h_cb, 'Position',cb_pos);
%delete(h_cb);
%cb_ax= axes('position',cb_pos);
%h_cb= colorbar(cb_ax, 'peer',ax);

if nargout==0,
  clear h_cb;
end


function varargout=importsii(varargin)
%
% For calling details please see v_importsii.m
%
% This dummy routine is included for backward compatibility only
% and will be removed in a future release of voicebox. Please use
% v_importsii.m in future and/or update with v_voicebox_update.m
%
%      Copyright (C) Mike Brookes 2018
%      Version: $Id: importsii.m 10863 2018-09-21 15:39:23Z dmb $
%
if nargout
    varargout=cell(1,nargout);
    [varargout{:}]=v_importsii(varargin{:});
else
    v_importsii(varargin{:});
end


function varargout = process_snapshot( varargin )
% PROCESS_SNAPSHOT: Save snapshot.
%
% USAGE:     sProcess = process_snapshot('GetDescription')
%                       process_snapshot('Run', sProcess, sInputs)

% @=============================================================================
% 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, 2012-2022

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'Save snapshot';
    sProcess.Category    = 'Custom';
    sProcess.SubGroup    = 'File';
    sProcess.Index       = 982;
    sProcess.Description = 'https://neuroimage.usc.edu/brainstorm/Tutorials/Scripting';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'raw', 'data', 'results', 'timefreq', 'matrix', 'dipoles', 'pdata', 'presults', 'ptimefreq', 'pmatrix'};
    sProcess.OutputTypes = {'raw', 'data', 'results', 'timefreq', 'matrix', 'dipoles', 'pdata', 'presults', 'ptimefreq', 'pmatrix'};
    sProcess.nInputs     = 1;
    sProcess.nMinFiles   = 1;
    % Definition of the options
    % === TARGET
    sProcess.options.type.Comment = 'Snapshot: ';
    sProcess.options.type.Type    = 'combobox_label';
    sProcess.options.type.Value   = {1, {...
        'Sensors/MRI registration',              'registration'; ...    % 1
        'SSP projectors',                        'ssp'; ...             % 2
        'Noise covariance',                      'noiscov'; ...         % 3
        'Data covariance',                       'ndatacov'; ...        % 12
        'Headmodel spheres',                     'headmodel'; ...       % 4
        'Recordings time series',                'data'; ...            % 5
        'Recordings topography (one time)',      'topo'; ...            % 6
        'Recordings topography (contact sheet)', 'topo_contact'; ...    % 7
        'Sources (one time)',                    'sources'; ...         % 8
        'Sources (contact sheet)',               'sources_contact'; ... % 9
        'Sources (MRI viewer)',                  'mriviewer'; ...
        'Frequency spectrum',                    'spectrum'; ...        % 10
        'Time-frequency maps',                   'timefreq'; ...        % 14
        'Connectivity matrix',                   'connectimage'; ...    % 11
        'Connectivity graph',                    'connectgraph'; ...
        'Dipoles',                               'dipoles'; ...         % 13
        }'};
    % === SENSORS
    sProcess.options.modality.Comment    = 'Sensor type: ';
    sProcess.options.modality.Type       = 'combobox';
    sProcess.options.modality.Value      = {1, {'MEG (All)', 'MEG (Gradiometers)', 'MEG (Magnetometers)', 'EEG', 'ECOG', 'SEEG', 'NIRS', 'EOG', 'ECG', 'EMG'}};
    sProcess.options.modality.InputTypes = {'raw', 'data', 'timefreq', 'pdata', 'ptimefreq'};
    % === Orientation
    sProcess.options.orient.Comment    = 'Orientation: ';
    sProcess.options.orient.Type       = 'combobox';
    sProcess.options.orient.Value      = {1, {'left', 'right', 'top', 'bottom', 'front', 'back', 'left_intern', 'right_intern'}};
    sProcess.options.orient.InputTypes = {'raw', 'data', 'results', 'timefreq', 'dipoles', 'pdata', 'presults', 'ptimefreq'};
    % === TIME: Single view
    sProcess.options.time.Comment = 'Time (in seconds):';
    sProcess.options.time.Type    = 'value';
    sProcess.options.time.Value   = {0, 's', 4};
    % === TIME: Contact sheet
    sProcess.options.contact_time.Comment = 'Contact sheet (start time, stop time):';
    sProcess.options.contact_time.Type    = 'value';
    sProcess.options.contact_time.Value   = {[0,.1], 'list', 4};
    sProcess.options.contact_nimage.Comment = 'Contact sheet (number of images):';
    sProcess.options.contact_nimage.Type    = 'value';
    sProcess.options.contact_nimage.Value   = {12, '', 0};
    % === THRESHOLD
    sProcess.options.threshold.Comment = 'Amplitude threshold:';
    sProcess.options.threshold.Type    = 'value';
    sProcess.options.threshold.Value   = {30, '%', 0};
    sProcess.options.threshold.InputTypes = { 'results', 'timefreq', 'presults', 'ptimefreq'};
    % === SMOOTHING
    sProcess.options.surfsmooth.Comment = 'Surface smoothing:';
    sProcess.options.surfsmooth.Type    = 'value';
    sProcess.options.surfsmooth.Value   = {30, '%', 0};
    sProcess.options.surfsmooth.InputTypes = {'results', 'timefreq', 'presults', 'ptimefreq'};
    % === FREQUENCY
    sProcess.options.freq.Comment = 'Frequency:';
    sProcess.options.freq.Type    = 'value';
    sProcess.options.freq.Value   = {0, 'Hz', 2};
    sProcess.options.freq.InputTypes = {'timefreq', 'ptimefreq'};
    % === ROW NAMES
    sProcess.options.rowname.Comment    = 'Signal name (empty=all): ';
    sProcess.options.rowname.Type       = 'text';
    sProcess.options.rowname.Value      = '';
    sProcess.options.rowname.InputTypes = {'raw', 'data', 'timefreq', 'matrix', 'pdata', 'ptimefreq', 'pmatrix'};
    % === MNI coordinates
    sProcess.options.mni.Comment = 'MNI coordinates:';
    sProcess.options.mni.Type    = 'value';
    sProcess.options.mni.Value   = {[0,0,0], 'list', 3};
    sProcess.options.mni.InputTypes = {'results', 'timefreq', 'presults', 'ptimefreq'};
    % === COMMENT
    sProcess.options.Comment.Comment = 'Comment: ';
    sProcess.options.Comment.Type    = 'text';
    sProcess.options.Comment.Value   = '';
end

%% ===== FORMAT COMMENT =====
function Comment = FormatComment(sProcess) %#ok<DEFNU>
    iType = find(strcmpi(sProcess.options.type.Value{1}, sProcess.options.type.Value{2}(2,:)));
    Comment = ['Snapshot: ' sProcess.options.type.Value{2}{1,iType}];
end

%% ===== RUN =====
function OutputFiles = Run(sProcess, sInputs) %#ok<DEFNU>
    % Returned files: same as input
    OutputFiles = {sInputs.FileName};
    % Get options
    if isfield(sProcess.options, 'target') && isfield(sProcess.options.target, 'Value') && ~isempty(sProcess.options.target.Value)
        switch (sProcess.options.target.Value)
            case 1,  SnapType = 'registration';
            case 2,  SnapType = 'ssp';
            case 3,  SnapType = 'noiscov';
            case 4,  SnapType = 'spheres';
            case 5,  SnapType = 'data';
            case 6,  SnapType = 'topo';
            case 7,  SnapType = 'topo_contact';
            case 8,  SnapType = 'sources';
            case 9,  SnapType = 'sources_contact';
            case 10, SnapType = 'spectrum';
            case 11, SnapType = 'connectimage';
            case 12, SnapType = 'ndatacov';
            case 13, SnapType = 'dipoles';
            case 14, SnapType = 'timefreq';
        end
    else
        SnapType = sProcess.options.type.Value{1};
    end
    if isfield(sProcess.options, 'modality') && isfield(sProcess.options.modality, 'Value') && ~isempty(sProcess.options.modality.Value) && iscell(sProcess.options.modality.Value)
        switch (sProcess.options.modality.Value{1})
            case 1,  Modality = 'MEG';
            case 2,  Modality = 'MEG GRAD';
            case 3,  Modality = 'MEG MAG';
            case 4,  Modality = 'EEG';
            case 5,  Modality = 'ECOG';
            case 6,  Modality = 'SEEG';
            case 7,  Modality = 'NIRS';
            case 8,  Modality = 'EOG';
            case 9,  Modality = 'ECG';
            case 10, Modality = 'EMG';
            otherwise, Modality = [];
        end
    else
        Modality = [];
    end
    Time           = sProcess.options.time.Value{1};
    contact_time   = sProcess.options.contact_time.Value{1};
    contact_nimage = sProcess.options.contact_nimage.Value{1};
    if isfield(sProcess.options, 'orient') && isfield(sProcess.options.orient, 'Value') && ~isempty(sProcess.options.orient.Value) && iscell(sProcess.options.orient.Value)
        Orient = sProcess.options.orient.Value{2}{sProcess.options.orient.Value{1}};
    else
        Orient = [];
    end
    if isfield(sProcess.options, 'freq') && isfield(sProcess.options.freq, 'Value') && ~isempty(sProcess.options.freq.Value) && iscell(sProcess.options.freq.Value) && ~isequal(sProcess.options.freq.Value{1}, 0)
        Freq = sProcess.options.freq.Value{1};
    else
        Freq = [];
    end
    if isfield(sProcess.options, 'mni') && isfield(sProcess.options.mni, 'Value') && iscell(sProcess.options.mni.Value) && (length(sProcess.options.mni.Value{1}) == 3)
        XYZmni = sProcess.options.mni.Value{1};
    else
        XYZmni = [];
    end
    % If using "comment" instead of "Comment" (common scripting error)
    Comment = sProcess.options.Comment.Value;
    if isempty(Comment) && isfield(sProcess.options, 'comment') && isfield(sProcess.options.comment, 'Value') && ~isempty(sProcess.options.comment.Value)
        Comment = sProcess.options.comment.Value;
    end
    % Amplitude threshold
    if isfield(sProcess.options, 'threshold') && isfield(sProcess.options.threshold, 'Value') && ~isempty(sProcess.options.threshold.Value) && iscell(sProcess.options.threshold.Value)
        Threshold = sProcess.options.threshold.Value{1} / 100;
    else
        Threshold = 0.3;
    end
    % Surface smoothing
    if isfield(sProcess.options, 'surfsmooth') && isfield(sProcess.options.surfsmooth, 'Value') && ~isempty(sProcess.options.surfsmooth.Value) && iscell(sProcess.options.surfsmooth.Value)
        SurfSmooth = sProcess.options.surfsmooth.Value{1} / 100;
    else
        SurfSmooth = 0.3;
    end
    % Row names
    if isfield(sProcess.options, 'rowname') && isfield(sProcess.options.rowname, 'Value') && ~isempty(sProcess.options.rowname.Value)
        RowName = strtrim(sProcess.options.rowname.Value);
        iComa = find(RowName == ',');
        if ~isempty(iComa)
            bst_report('warning', sProcess, [], 'The option "signal name" should be empty or include only one signal.');
            RowName = strtrim(RowName(1,iComa-1));
        end
    else
        RowName = [];
    end
    % Contact sheet
    Contact = [contact_time, contact_nimage];
    % Select only one input file per channel file
    if ismember(SnapType, {'registration', 'ssp', 'noiscov', 'ndatacov', 'headmodel'})
        [AllChannelFile, iAllChan] = unique({sInputs.ChannelFile});
        sInputs = sInputs(iAllChan);
    end
    % For each file, capture view for each file
    for iFile = 1:length(sInputs)
        FileName = sInputs(iFile).FileName;
        switch (SnapType)
            case 'registration'
                bst_report('Snapshot', 'registration', FileName, Comment, Modality, Orient);
            case 'ssp'
                bst_report('Snapshot', 'ssp', FileName, Comment);
            case 'noiscov'
                bst_report('Snapshot', 'noisecov', FileName, Comment);
            case 'ndatacov'
                bst_report('Snapshot', 'ndatacov', FileName, Comment);
            case 'headmodel'
                bst_report('Snapshot', 'headmodel', FileName, Comment);
            case 'data'
                bst_report('Snapshot', 'data', FileName, Comment, Modality, Time, RowName);
            case 'topo'
                bst_report('Snapshot', 'topo', FileName, Comment, Modality, Time, Freq);
            case 'topo_contact'
                if (length(Contact) ~= 3)
                    bst_report('Error', sProcess, [], 'Invalid contact sheet time values');
                    return;
                end
                bst_report('Snapshot', 'topo', FileName, Comment, Modality, Contact, Freq);
            case 'sources'
                bst_report('Snapshot', 'sources', FileName, Comment, Time, Threshold, Orient, SurfSmooth, Freq);
            case 'sources_contact'
                if (length(Contact) ~= 3)
                    bst_report('Error', sProcess, [], 'Invalid contact sheet time values');
                    return;
                end
                bst_report('Snapshot', 'sources', FileName, Comment, Contact, Threshold, Orient, SurfSmooth, Freq);
            case 'mriviewer'
                bst_report('Snapshot', 'mriviewer', FileName, Comment, Time, Threshold, Freq, XYZmni);
            case 'spectrum'
                bst_report('Snapshot', 'spectrum', FileName, Comment, RowName, Freq);
            case 'timefreq'
                bst_report('Snapshot', 'timefreq', FileName, Comment, RowName, Time, Freq);
            case 'connectimage'
                bst_report('Snapshot', 'connectimage', FileName, Comment, Time, Freq);
            case 'connectgraph'
                bst_report('Snapshot', 'connectgraph', FileName, Comment, Threshold, Time, Freq);
            case 'dipoles'
                bst_report('Snapshot', 'dipoles', FileName, Comment, Threshold, Orient);
        end
    end
end


function b = ispathvalid_startup(p, options)
% ispathvalid can replace matlab's o.isdir, o.isfile and exist functions
% all of which have flaws (e.g., isdir not fully backwards compatible,
% exist has bugs where it'll confuse files and folders)
%
b = false;
if ~exist('options','var')
    options = '';
end
if ~ischar(options)
    return;
end
if ~isempty(options)
    if optionExists_startup(options,'file')

        % Err check 1.
        if isdir_private_startup(p)
            return;
        end

        % Err check 2.
        if optionExists_startup(options, 'checkextension')
            [~, ~, ext] = fileparts(p);
            if isempty(ext)
                return;
            end
        end

    elseif optionExists_startup(options,'dir')
        if isfile_private_startup(p)
            return;
        end
    else
        return;
    end
end
b = ~isempty(dir(p));


function ldl_make
%LDL_MAKE compile LDL
%
% Example:
%   ldl_make       % compiles ldlsparse and ldlsymbol
%
% See also ldlsparse, ldlsymbol

% Copyright 2006-2007 by Timothy A. Davis, http://www.suitesparse.com

d = '' ;
if (~isempty (strfind (computer, '64')))
    d = '-largeArrayDims' ;
end
eval (sprintf ('mex -O %s -DLDL_LONG -I../../SuiteSparse_config -I../Include -output ldlsparse ../Source/ldl.c ldlmex.c', d)) ;
eval (sprintf ('mex -O %s -DLDL_LONG -I../../SuiteSparse_config -I../Include -output ldlsymbol ../Source/ldl.c ldlsymbolmex.c', d)) ;

eval (sprintf ('mex -O %s -DLDL_LONG -I../../SuiteSparse_config -I../Include -output ldlsparse_short ../Source/ldl.c ldlmex_short.c', d)) ;
eval (sprintf ('mex -O %s -DLDL_LONG -I../../SuiteSparse_config -I../Include -output ldlsymbol_extra ../Source/ldl.c ldlsymbol_extra.c', d)) ;
eval (sprintf ('mex -O %s -DLDL_LONG -I../../SuiteSparse_config -I../Include -output ldlnumeric ../Source/ldl.c ldlnumericmex.c', d)) ;
fprintf ('LDL successfully compiled.\n') ;