function [data] = ft_megrealign(cfg, data)

% FT_MEGREALIGN interpolates MEG data towards standard gradiometer locations by
% projecting the individual timelocked data towards a coarse source reconstructed
% representation and computing the magnetic field on the standard gradiometer
% locations.
%
% Use as
%   [interp] = ft_megrealign(cfg, data)
% where the input data corresponds to the output from FT_PREPROCESSING.
%
% Required configuration options are
%   cfg.template
%   cfg.inwardshift
%
% The new gradiometer definition is obtained from a template dataset,
% or can be constructed by averaging the gradiometer positions over
% multiple datasets.
%   cfg.template       = single dataset that serves as template
%   cfg.template(1..N) = datasets that are averaged into the standard
%
% The realignment is done by computing a minumum norm estimate using a
% large number of dipoles that are placed in the upper layer of the brain
% surface, followed by a forward computation towards the template
% gradiometer array. This requires the specification of a volume conduction
% model of the head and of a source model.
%
% A volume conduction model of the head should be specified with
%   cfg.headmodel   = structure, see FT_PREPARE_HEADMODEL
%
% A source model (i.e. a superficial layer with distributed sources) can be
% constructed from a headshape file, or from inner surface of the volume conduction
% model using FT_PREPARE_SOURCEMODEL using the following options
%   cfg.spheremesh  = number of dipoles in the source layer (default = 642)
%   cfg.inwardshift = depth of the source layer relative to the headshape
%                     surface or volume conduction model (no default
%                     supplied, see below)
%   cfg.headshape   = a filename containing headshape, a structure containing a
%                     single triangulated boundary, or a Nx3 matrix with surface
%                     points
%
% If you specify a headshape and it describes the skin surface, you should specify an
% inward shift of 2.5 cm.
%
% For a single-sphere or a local-spheres volume conduction model based on the skin
% surface, an inward shift of 2.5 cm is reasonable.
%
% For a single-sphere or a local-spheres volume conduction model based on the brain
% surface, you should probably use an inward shift of about 1 cm.
%
% For a realistic single-shell volume conduction model based on the brain surface, you
% should probably use an inward shift of about 1 cm.
%
% Other configuration options are
%   cfg.tolerance  = tolerance ratio for leadfield matrix inverse based on a truncated svd,
%                    reflects the relative magnitude of the largest singular value
%                    to retain (default =s 1e-3)
%   cfg.verify     = 'yes' or 'no', show the percentage difference (default = 'yes')
%   cfg.feedback   = 'yes' or 'no' (default = 'no')
%   cfg.channel    =  Nx1 cell-array with selection of channels (default = 'MEG'),
%                     see FT_CHANNELSELECTION for details
%   cfg.trials     = 'all' or a selection given as a 1xN vector (default = 'all')
%
% This implements the method described by T.R. Knosche, Transformation
% of whole-head MEG recordings between different sensor positions.
% Biomed Tech (Berl). 2002 Mar;47(3):59-62. For more information and
% related methods, see Stolk et al., Online and offline tools for head
% movement compensation in MEG. NeuroImage, 2012.
%
% To facilitate data-handling and distributed computing you can use
%   cfg.inputfile   =  ...
%   cfg.outputfile  =  ...
% If you specify one of these (or both) the input data will be read from a *.mat
% file on disk and/or the output data will be written to a *.mat file. These mat
% files should contain only a single variable, corresponding with the
% input/output structure.
%
% See also FT_PREPARE_LOCALSPHERES, FT_PREPARE_SINGLESHELL

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

% these are used by the ft_preamble/ft_postamble function and scripts
ft_revision = '$Id$';
ft_nargin   = nargin;
ft_nargout  = nargout;

% do the general setup of the function
ft_defaults
ft_preamble init
ft_preamble debug
ft_preamble loadvar data
ft_preamble provenance data

% the ft_abort variable is set to true or false in ft_preamble_init
if ft_abort
  return
end

% store the original datatype
dtype = ft_datatype(data);

% check if the input data is valid for this function
data = ft_checkdata(data, 'datatype', 'raw', 'feedback', 'yes', 'hassampleinfo', 'yes', 'ismeg', 'yes');

% check if the input cfg is valid for this function
cfg = ft_checkconfig(cfg, 'forbidden',  {'channels', 'trial'}); % prevent accidental typos, see issue 1729
cfg = ft_checkconfig(cfg, 'renamed',    {'plot3d',      'feedback'});
cfg = ft_checkconfig(cfg, 'renamedval', {'headshape',   'headmodel', []});
cfg = ft_checkconfig(cfg, 'required',   {'inwardshift', 'template'});
cfg = ft_checkconfig(cfg, 'renamed',    {'hdmfile',     'headmodel'});
cfg = ft_checkconfig(cfg, 'renamed',    {'vol',         'headmodel'});
cfg = ft_checkconfig(cfg, 'renamed',    {'grid',        'sourcemodel'});
cfg = ft_checkconfig(cfg, 'renamed',    {'pruneratio',  'tolerance'});

% set the default configuration
cfg.headshape  = ft_getopt(cfg, 'headshape',  []);
cfg.pruneratio = ft_getopt(cfg, 'tolerance',  1e-3);
cfg.spheremesh = ft_getopt(cfg, 'spheremesh', 642);
cfg.verify     = ft_getopt(cfg, 'verify',     'yes');
cfg.feedback   = ft_getopt(cfg, 'feedback',   'yes');
cfg.trials     = ft_getopt(cfg, 'trials',     'all', 1);
cfg.channel    = ft_getopt(cfg, 'channel',    'MEG');
cfg.topoparam  = ft_getopt(cfg, 'topoparam',  'rms');

% do realignment per trial
pertrial = all(ismember({'nasX';'nasY';'nasZ';'lpaX';'lpaY';'lpaZ';'rpaX';'rpaY';'rpaZ'}, data.label));

% put the low-level options pertaining to the dipole grid in their own field
cfg = ft_checkconfig(cfg, 'renamed', {'tightgrid', 'tight'}); % this is moved to cfg.sourcemodel.tight by the subsequent createsubcfg
cfg = ft_checkconfig(cfg, 'renamed', {'sourceunits', 'unit'}); % this is moved to cfg.sourcemodel.unit by the subsequent createsubcfg

% put the low-level options pertaining to the sourcemodel in their own field
cfg = ft_checkconfig(cfg, 'createsubcfg', {'sourcemodel'});
% move some fields from cfg.sourcemodel back to the top-level configuration
cfg = ft_checkconfig(cfg, 'createtopcfg', {'sourcemodel'});

if isstruct(cfg.template)
  % this should be a cell-array
  cfg.template = {cfg.template};
end

% retain only the MEG channels and temporarily store the rest of the channels
% elsewhere, these will be added back to the transformed data later.

% select trials and channels of interest, first of the non-MEG channels, then of the MEG channels
tmpcfg = keepfields(cfg, {'trials', 'showcallinfo', 'trackcallinfo', 'trackusage', 'trackdatainfo', 'trackmeminfo', 'tracktimeinfo', 'checksize'}); % don't keep tolerance, it is used differently here
tmpcfg.channel = setdiff(data.label, ft_channelselection(cfg.channel, data.label), 'stable');
rest = ft_selectdata(tmpcfg, data);
tmpcfg.channel = ft_channelselection(cfg.channel, data.label);
data = ft_selectdata(tmpcfg, data);

% restore the provenance information
[cfg, data] = rollback_provenance(cfg, data);

Ntrials = length(data.trial);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% construct the average template gradiometer array
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
template = struct([]); % initialize as 0x0 empty struct array with no fields
for i=1:length(cfg.template)
  if ischar(cfg.template{i})
    ft_info('reading template sensor position from %s\n', cfg.template{i});
    tmp = ft_read_sens(cfg.template{i}, 'senstype', 'meg');
  elseif isstruct(cfg.template{i}) && isfield(cfg.template{i}, 'coilpos') && isfield(cfg.template{i}, 'coilori') && isfield(cfg.template{i}, 'tra')
    tmp = cfg.template{i};
  elseif isstruct(cfg.template{i}) && isfield(cfg.template{i}, 'pnt') && isfield(cfg.template{i}, 'ori') && isfield(cfg.template{i}, 'tra')
    % it seems to be a pre-2011v1 type gradiometer structure, update it
    tmp = ft_datatype_sens(cfg.template{i});
  else
    ft_error('unrecognized template input');
  end
  % prevent "Subscripted assignment between dissimilar structures" error
  template = appendstruct(template, tmp); clear tmp
end

grad = ft_average_sens(template);

% construct the final template gradiometer definition
template = [];
template.grad = grad;

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% PREPARE_HEADMODEL will match the data labels, the gradiometer labels and the
% volume model labels (in case of a localspheres model) and result in a gradiometer
% definition that only contains the gradiometers that are present in the data.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

volcfg = [];
volcfg.headmodel = cfg.headmodel;
volcfg.grad      = data.grad;
volcfg.channel   = data.label; % this might be a subset of the MEG channels

% FIXME As of yet the next steps might not entirely correct, because it does not keep
% track of the balancing of the gradiometer array. This may require some thought
% because the leadfields are computed with low level functions and do not easily
% accommodate for matching the correct channels with each other (in order to compute
% the projection matrix).

% PREPARE_HEADMODEL will match the data labels, the gradiometer labels and the
% volume model labels (in case of a localspheres model) and result in a gradiometer
% definition that only contains the gradiometers that are present in the data.
[volold, data.grad] = prepare_headmodel(volcfg, []);

% Note that it is necessary to keep the two volume conduction models separate, since
% the single-shell Nolte model contains gradiometer specific precomputed parameters.
% Also note that this is not guaranteed to result in a good projection for local
% sphere models.
volcfg.grad    = template.grad;
volcfg.channel = 'MEG'; % include all MEG channels
[volnew, template.grad] = prepare_headmodel(volcfg, []);

% construct the low-level options for the leadfield computation as key-value pairs, these are passed to FT_COMPUTE_LEADFIELD
leadfieldopt = {};
leadfieldopt = ft_setopt(leadfieldopt, 'reducerank',     ft_getopt(cfg, 'reducerank'));
leadfieldopt = ft_setopt(leadfieldopt, 'backproject',    ft_getopt(cfg, 'backproject'));
leadfieldopt = ft_setopt(leadfieldopt, 'normalize',      ft_getopt(cfg, 'normalize'));
leadfieldopt = ft_setopt(leadfieldopt, 'normalizeparam', ft_getopt(cfg, 'normalizeparam'));
leadfieldopt = ft_setopt(leadfieldopt, 'weight',         ft_getopt(cfg, 'weight'));

if strcmp(ft_senstype(data.grad), ft_senstype(template.grad))
  [id, it] = match_str(data.grad.label, template.grad.label);
  ft_info('mean distance towards template gradiometers is %.2f %s\n', mean(sum((data.grad.chanpos(id,:)-template.grad.chanpos(it,:)).^2, 2).^0.5), template.grad.unit);
else
  % the projection is from one MEG system to another MEG system, which makes a comparison of the data difficult
  cfg.feedback = 'no';
  cfg.verify = 'no';
end

% copy all options that are potentially used in ft_prepare_sourcemodel
tmpcfg           = keepfields(cfg, {'sourcemodel', 'mri', 'headshape', 'symmetry', 'smooth', 'threshold', 'spheremesh', 'inwardshift', 'xgrid' 'ygrid', 'zgrid', 'resolution', 'tight', 'warpmni', 'template', 'showcallinfo', 'trackcallinfo', 'trackusage', 'trackdatainfo', 'trackmeminfo', 'tracktimeinfo', 'checksize'});
tmpcfg.headmodel = volold;
tmpcfg.grad      = data.grad;
% create the source positions on which the data will be projected
sourcemodel = ft_prepare_sourcemodel(tmpcfg);

% compute the forward model for the new gradiometer positions
ft_info('computing forward model for %d dipoles\n', size(sourcemodel.pos,1));
lfnew = ft_compute_leadfield(sourcemodel.pos, template.grad, volnew, leadfieldopt{:});
if ~pertrial
  % this needs to be done only once
  lfold = ft_compute_leadfield(sourcemodel.pos, data.grad, volold, leadfieldopt{:});
  [realign, noalign, bkalign] = computeprojection(lfold, lfnew, cfg.pruneratio, cfg.verify);
end

% interpolate the data towards the template gradiometers
for i=1:Ntrials
  ft_info('realigning trial %d\n', i);
  if pertrial
    %warp the gradiometer array according to the motiontracking data
    sel   = match_str(rest.label, {'nasX';'nasY';'nasZ';'lpaX';'lpaY';'lpaZ';'rpaX';'rpaY';'rpaZ'});
    hmdat = rest.trial{i}(sel,:);
    if ~all(hmdat==repmat(hmdat(:,1),[1 size(hmdat,2)]))
      ft_error('only one position per trial is at present allowed');
    else
      M    = ft_headcoordinates(hmdat(1:3,1),hmdat(4:6,1),hmdat(7:9,1));
      grad = ft_transform_geometry(M, data.grad);
    end

    volcfg.grad = grad;
    % compute volume conductor
    [volold, grad] = prepare_headmodel(volcfg, []);
    % compute forward model
    lfold = ft_compute_leadfield(sourcemodel.pos, grad, volold, leadfieldopt{:});
    % compute projection matrix
    [realign, noalign, bkalign] = computeprojection(lfold, lfnew, cfg.pruneratio, cfg.verify);
  end
  data.realign{i} = realign * data.trial{i};
  if strcmp(cfg.verify, 'yes')
    % also compute the residual variance when interpolating
    [id,it]   = match_str(data.grad.label, template.grad.label);
    rvrealign = rv(data.trial{i}(id,:), data.realign{i}(it,:));
    ft_info('original -> template             RV %.2f %%\n', 100 * mean(rvrealign));
    datnoalign = noalign * data.trial{i};
    datbkalign = bkalign * data.trial{i};
    rvnoalign = rv(data.trial{i}, datnoalign);
    rvbkalign = rv(data.trial{i}, datbkalign);
    ft_info('original             -> original RV %.2f %%\n', 100 * mean(rvnoalign));
    ft_info('original -> template -> original RV %.2f %%\n', 100 * mean(rvbkalign));
  end
end

% plot the topography before and after the realignment
if strcmp(cfg.feedback, 'yes')

  ft_warning('showing MEG topography (RMS value over time) in the first trial only');
  Nchan = length(data.grad.label);
  [id,it]   = match_str(data.grad.label, template.grad.label);
  pos1 = data.grad.chanpos(id,:);
  pos2 = template.grad.chanpos(it,:);
  prj1 = elproj(pos1); tri1 = delaunay(prj1(:,1), prj1(:,2));
  prj2 = elproj(pos2); tri2 = delaunay(prj2(:,1), prj2(:,2));

  switch cfg.topoparam
    case 'rms'
      p1 = sqrt(mean(data.trial{1}(id,:).^2, 2));
      p2 = sqrt(mean(data.realign{1}(it,:).^2, 2));
    case 'svd'
      [u, s, v] = svd(data.trial{1}(id,:)); p1 = u(:,1);
      [u, s, v] = svd(data.realign{1}(it,:)); p2 = u(:,1);
    otherwise
      ft_error('unsupported cfg.topoparam');
  end

  X = [pos1(:,1) pos2(:,1)]';
  Y = [pos1(:,2) pos2(:,2)]';
  Z = [pos1(:,3) pos2(:,3)]';

  % show figure with old an new helmets, volume model and source positions
  figure
  hold on
  ft_plot_headmodel(volold);
  plot3(sourcemodel.pos(:,1),sourcemodel.pos(:,2),sourcemodel.pos(:,3),'b.');
  plot3(pos1(:,1), pos1(:,2), pos1(:,3), 'r.') % original positions
  plot3(pos2(:,1), pos2(:,2), pos2(:,3), 'g.') % template positions
  line(X,Y,Z, 'color', 'black');
  view(-90, 90);

  % show figure with data on old helmet location
  figure
  hold on
  plot3(pos1(:,1), pos1(:,2), pos1(:,3), 'r.') % original positions
  plot3(pos2(:,1), pos2(:,2), pos2(:,3), 'g.') % template positions
  line(X,Y,Z, 'color', 'black');
  axis equal; axis vis3d
  bnd1 = [];
  bnd1.pos = pos1;
  bnd1.tri = tri1;
  ft_plot_mesh(bnd1,'vertexcolor',p1,'edgecolor','none')
  title('RMS, before realignment')
  view(-90, 90)

  % show figure with data on new helmet location
  figure
  hold on
  plot3(pos1(:,1), pos1(:,2), pos1(:,3), 'r.') % original positions
  plot3(pos2(:,1), pos2(:,2), pos2(:,3), 'g.') % template positions
  line(X,Y,Z, 'color', 'black');
  axis equal; axis vis3d
  bnd2 = [];
  bnd2.pos = pos2;
  bnd2.tri = tri2;
  ft_plot_mesh(bnd2,'vertexcolor',p2,'edgecolor','none')
  title('RMS, after realignment')
  view(-90, 90)
end

% store the realigned data in a new structure
interp.label   = template.grad.label;
interp.grad    = template.grad;   % replace with the template gradiometer array
interp.trial   = data.realign;    % remember the processed data
interp.fsample = data.fsample;
interp.time    = data.time;

% add the rest channels back to the data, these were not interpolated
if ~isempty(rest.label)
  ft_info('adding %d non-MEG channels back to the data (', length(rest.label));
  ft_info('%s, ', rest.label{1:end-1});
  ft_info('%s)\n', rest.label{end});
  for trial=1:length(rest.trial)
    interp.trial{trial} = [interp.trial{trial}; rest.trial{trial}];
  end
  interp.label = [interp.label; rest.label];
end

% copy the trial specific information into the output
if isfield(data, 'trialinfo')
  interp.trialinfo = data.trialinfo;
end

% copy the sampleinfo field as well
if isfield(data, 'sampleinfo')
  interp.sampleinfo = data.sampleinfo;
end

% convert back to input type if necessary
switch dtype
  case 'timelock'
    interp = ft_checkdata(interp, 'datatype', 'timelock');
  otherwise
    % keep the output as it is
end

% do the general cleanup and bookkeeping at the end of the function
ft_postamble debug
ft_postamble previous data

% rename the output variable to accomodate the savevar postamble
data = interp;

ft_postamble provenance data
ft_postamble history    data
ft_postamble savevar    data

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% subfunction that computes the projection matrix(ces)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function [realign, noalign, bkalign] = computeprojection(lfold, lfnew, tolerance, verify)

% compute this inverse only once, although it is used twice
tmp = ft_inv(lfold, 'method', 'tsvd', 'tolerance', tolerance);
% compute the three interpolation matrices
ft_info('computing interpolation matrix #1\n');
realign = lfnew * tmp;
if strcmp(verify, 'yes')
  ft_info('computing interpolation matrix #2\n');
  noalign = lfold * tmp;
  ft_info('computing interpolation matrix #3\n');
  bkalign = (lfold * ft_inv(lfnew, 'method', 'tsvd', 'tolerance', tolerance)) * realign;
else
  noalign = [];
  bkalign = [];
end


function [output,cost,psave,timing] = solvelower(p,options,lowersolver,xmin,upper,timing)

psave = p;
removeThese = find(p.InequalityConstraintState==inf);
p.F_struc(p.K.f + removeThese,:) = [];
p.K.l = p.K.l - length(removeThese);

removeThese = find(p.EqualityConstraintState==inf);
p.F_struc(removeThese,:) = [];
p.K.f = p.K.f - length(removeThese);

if p.options.bmibnb.cut.bilinear
    p_cut = addBilinearVariableCuts(p);
end
if p.options.bmibnb.cut.evalvariable
    p_cut = addEvalVariableCuts(p_cut);
    psave.evalMap = p_cut.evalMap;
end
if p.options.bmibnb.cut.monomial
    p_cut = addMonomialCuts(p_cut);
end
if p.options.bmibnb.cut.multipliedequality
    p_cut = addMultipliedEqualityCuts(p_cut);
end
if p.options.bmibnb.cut.convexity
    p_cut = addConvexityCuts(p_cut);
end
if p.options.bmibnb.cut.complementarity
    p_cut = addComplementarityCuts(p_cut);
end
% **************************************
% SOLVE NODE PROBLEM
% **************************************
if any(p_cut.ub+1e-8<p_cut.lb)
    output.problem=1;
    cost = inf;
else
    % We are solving relaxed problem (penbmi might be local solver)
    p_cut.monomtable = eye(length(p_cut.c));

    if p.solver.lowersolver.objective.quadratic.convex
        % Setup quadratic
        [p_cut.Q,p_cut.c] = compileQuadratic(p.c,p);

        if nonconvexQuadratic(p_cut.Q);
            p_cut.Q = p.Q;
            p_cut.c = p.c;
        end
    end

    fixed = p_cut.lb >= p_cut.ub;
    if nnz(fixed) == length(p.c)
        % All variables are fixed to a bound
        output.Primal = p.lb;
        res = constraint_residuals(p,output.Primal);
        eq_ok = all(res(1:p.K.f)>=-p.options.bmibnb.eqtol);
        iq_ok = all(res(1+p.K.f:end)>=p.options.bmibnb.pdtol);
        feasible = eq_ok & iq_ok;
        if feasible
            output.problem = 0;
        else
            output.problem = 1;
        end
        cost = output.Primal'*p.Q*output.Primal + p.c'*output.Primal + p.f;
    else

        if nnz(fixed)==0

            if ~isempty(p_cut.bilinears) & 0
                top = size(p_cut.F_struc,1);
                if length(p_cut.K.s)==1 & p_cut.K.s(1)==0
                    p_cut.K.s = [];
                end
                usedterms = zeros(size(p_cut.bilinears,1),1);
                for i = 1:size(p_cut.bilinears,1)
                    if ~usedterms(i)
                        windex = p_cut.bilinears(i,1);
                        xindex = p_cut.bilinears(i,2);
                        yindex = p_cut.bilinears(i,3);
                        if xindex ~=yindex
                            % OK, we have a bilinear term
                            xsquaredindex = find(p_cut.bilinears(:,2)==xindex & p_cut.bilinears(:,3)==xindex);
                            ysquaredindex = find(p_cut.bilinears(:,2)==yindex & p_cut.bilinears(:,3)==yindex);
                            if ~isempty(xsquaredindex) & ~isempty(ysquaredindex)
                                usedterms(i) = 1;
                                usedterms(xsquaredindex) = 1;
                                usedterms(ysquaredindex) = 1;
                                xsquaredindex =  p_cut.bilinears(xsquaredindex,1);
                                ysquaredindex =  p_cut.bilinears(ysquaredindex,1);
                                if 0
                                    Z = zeros(9,size(p_cut.F_struc,2));
                                    Z(1,xsquaredindex+1) = 1;
                                    Z(2,windex+1) = 1;
                                    Z(4,windex+1) = 1;
                                    Z(5,ysquaredindex+1) = 1;
                                    Z(3,xindex+1) = 1;
                                    Z(7,xindex+1) = 1;
                                    Z(6,yindex+1) = 1;
                                    Z(8,yindex+1) = 1;
                                    Z(9,1)=1;
                                else
                                    xL = p.lb(xindex);
                                    yL = p.lb(yindex);

                                    Z = zeros(9,size(p_cut.F_struc,2));
                                    Z(1,xsquaredindex+1) = 1;
                                    Z(2,windex+1) = 1;
                                    Z(4,windex+1) = 1;
                                    Z(5,ysquaredindex+1) = 1;
                                    Z(3,xindex+1) = 1;
                                    Z(7,xindex+1) = 1;
                                    Z(6,yindex+1) = 1;
                                    Z(8,yindex+1) = 1;
                                    Z(9,1)=1;
                                    Z(3,1) = -xL;
                                    Z(7,1) = -xL;
                                    Z(6,1) = -yL;
                                    Z(8,1) = -yL;

                                    Z(1,xindex+1) = -2*xL;
                                    Z(5,yindex+1) = -2*yL;

                                    Z(1,1) = xL^2;
                                    Z(5,1) = yL^2;

                                    Z(4,xindex+1) = -yL;
                                    Z(4,yindex+1) = -xL;
                                    Z(4,1) = xL*yL;

                                    Z(2,xindex+1) = -yL;
                                    Z(2,yindex+1) = -xL;
                                    Z(2,1) = xL*yL;

                                end
                                p_cut.F_struc = [p_cut.F_struc;Z];
                                p_cut.K.s = [p_cut.K.s 3];
                            end
                        end
                    end
                end
            end

            p_cut.linearindicies = 1:length(p.c);
            p_cut.nonlinearindicies = [];
            p_cut.variabletype = zeros(1,length(p.c));
            p_cut.deppattern = eye(length(p.c));
            p_cut.linears = 1:length(p.c);
            p_cut.bilinears = [];
            p_cut.nonlinears = [];
            p_cut.monomials = [];
            p_cut.evaluation_scheme = [];

            tstart = tic;
            output = feval(lowersolver,removenonlinearity(p_cut));
            psave.counter.lowersolved = psave.counter.lowersolved + 1;
            timing.lowersolve = timing.lowersolve + toc(tstart);
            cost = output.Primal'*p_cut.Q*output.Primal + p_cut.c'*output.Primal + p.f;
            % Minor clean-up
            pp=p;
            output.Primal(output.Primal<p.lb) = p.lb(output.Primal<p.lb);
            output.Primal(output.Primal>p.ub) = p.ub(output.Primal>p.ub);
            x=output.Primal;
            return
        else
            pp = p_cut;
            removethese = fixed;
            if ~isempty(p_cut.F_struc)
                p_cut.F_struc(:,1)=p_cut.F_struc(:,1)+p_cut.F_struc(:,1+find(fixed))*p_cut.lb(fixed);
                p_cut.F_struc(:,1+find(fixed))=[];

                rf = find(~any(p_cut.F_struc,2));
                rf = rf(rf<=(p_cut.K.f + p_cut.K.l));
                p_cut.F_struc(rf,:) = [];
                p_cut.K.l = p_cut.K.l - nnz(rf>p_cut.K.f);
                p_cut.K.f = p_cut.K.f - nnz(rf<=p_cut.K.f);
            end
            p_cut.c(removethese)=[];
            if nnz(p_cut.Q)>0
                p_cut.c = p_cut.c + 2*p_cut.Q(find(~removethese),find(removethese))*p_cut.lb(removethese);
                p_cut.Q(:,find(removethese))=[];
                p_cut.Q(find(removethese),:)=[];
            else
                p_cut.Q = spalloc(length(p_cut.c),length(p_cut.c),0);
            end

            if ~isempty(p_cut.binary_variables)
                new_bin = [];
                new_var = find(~fixed);
                for i = 1:length(p_cut.binary_variables)
                    temp = find(p_cut.binary_variables(i) == new_var);
                    new_bin =  [new_bin temp(:)'];
                end
                p_cut.binary_variables = new_bin;
            end
            if ~isempty(p_cut.integer_variables)
                new_bin = [];
                new_var = find(~fixed);
                for i = 1:length(p_cut.integer_variables)
                    temp = find(p_cut.integer_variables(i) == new_var);
                    new_bin =  [new_bin temp(:)'];
                end
                p_cut.integer_variables = new_bin;
            end

            p_cut.lb(removethese)=[];
            p_cut.ub(removethese)=[];
            p_cut.x0(removethese)=[];
            p_cut.monomtable(:,find(removethese))=[];
            p_cut.monomtable(find(removethese),:)=[];

            % The model can become absolutely trivial in some case
            % For instance in ex9_2_2 everything is presolved
            if nnz(p_cut.c)==0 & nnz(p_cut.Q)==0 & size(p_cut.F_struc,1)==0
                % No objective and no constraints
                if all(p_cut.lb <= p_cut.ub)
                    output.Primal = (p_cut.lb + p_cut.ub)/2;
                    output.Primal(isinf(p_cut.lb) & isinf(p_cut.ub))=0;
                    output.problem = 0;
                else
                    output.Primal = zeros(length(p_cut.lb),1);
                    output.problem = 1;
                end
            else
                try
                    tstart = tic;
                    output = feval(lowersolver,removenonlinearity(p_cut));
                    psave.counter.lowersolved = psave.counter.lowersolved + 1;
                    timing.lowersolve = timing.lowersolve + toc(tstart);
                catch
                    1
                end
            end
            x=full(p.c*0);
            x(removethese)=p.lb(removethese);
            x(~removethese)=output.Primal;
            output.Primal = x;
            cost = output.Primal'*pp.Q*output.Primal + pp.c'*output.Primal + p.f;
        end
    end
end


%kigeowarp 'Perform Direct Bilinear Geometric Warping'
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros igeowarp.pane file
%
% Parameters:
% InputFile: i 'Input ', required: 'Input data object'
% Toggle: planes 'Process by planes', default: 0: 'Process data by full planes instead of small prisms'
% OutputFile: o 'Output', required: 'Output data object'
% InputFile: wcoeffs 'W warp coeffs object', optional: 'W warp function coeffs object'
% InputFile: hcoeffs 'H warp coeffs object', optional: 'H warp function coeffs object'
%
% Example: o = kigeowarp({i, wcoeffs, hcoeffs}, {'i','';'planes',0;'o','';'wcoeffs','';'hcoeffs',''})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% igeowarp - Perform Direct Bilinear Geometric Warping
%
%  DESCRIPTION
% .I igeowarp
% is used to apply a direct bilinear warping to an image. In this mode, the
% bilinear mapping equations give the mapping of output data coordinates to
% input data coordinates. A given output data coordinate is operated on by the
% mapping functions to give the input data coordinate. The value of the input
% data coordinate is interpolated using bilinear interpolation, and the resulting
% value is assigned to the output data coordinate.
%
% All warping is assumed to occur only in the WxH plane.
% If warping in a different plane is desired, it is necessary to use the kaxis
% program to reorient the data such that the desired plane is the WxH plane before
% applying igeowarp. This limitation is due to computational complexity and
% performance issues, particularly when dealing with large data sets.
%
% The mapping functions used by igeowarp are:
%
%
%             f(w,h) = output image
%
%             g(w',h') = input image coordinate
%
%             w' = a00 + a01*w + a10*h +a11*w*h
%             h' = b00 + b01*w + b10*h +b11*w*h
%
%
% Note that in general, w' and h' are not integers for a given integer pair
% w and h. The
% data value at g(w',h') is obtained by bilinear interpolation of the four
% nearest neighbors to coordinate (w',h').
%
% The mapping is called direct because it directly gives the location in the
% input data that corresponds to a given location in the output data. The
% opposite case gives the location in the output data that corresponds to a
% given input data location; this requires much trickier and computationally
% expensive interpolation methods to obtain good results.
%
% The input parameters a00, a01, a10, a11, b00, b01, b10, b11 are exactly as
% specified in the mapping functions above. These parameters can also be supplied
% in the form of object value data where the data is assumed to be organized with
% the parameters a00, a01, a10, and a11 present in that storage order for the
% W coefficients and likewise for the H coefficients.
%
% If the input object has a mask, a new mask will be computed for the output
% object indicating which data points contain reliable data.
%
% If the input object has a map, the data is pulled through the map prior to
% rotation, and the output object will have no map.
%
% The -planes flag is used to change the way the data is accessed for processing.
% If the data set is small enough that individual planes of data (including the
% E data) parallel to the WxH plane will fit in memory, then use of the -planes
% flag will permit processing to happen in a plane-by-plane basis; this can
% occur with efficiency and speed. Otherwise,
% processing will occur in prisms down the D axis, which is much slower but will
% work on data sets of any size. If you have lots of memory, you may be able to
% get away with using -planes even for quite large data sets, say around 2Kx2K
% or more points per plane.
%
%
%
%  EXAMPLES
%
%  "SEE ALSO"
% irotate(1)
%
%  RESTRICTIONS
%
% For this initial release, the E dimension data is "not\fR handled
% correctly. Data in WxHxD "is\fR handled correctly. This deficiency
% will be corrected in the next version of
% .I igeowarp.
%
%  REFERENCES
%
%  COPYRIGHT
% Copyright (C) 1993 - 1997, Khoral Research, Inc. ("KRI")  All rights reserved.
%

function varargout = kigeowarp(varargin)
if nargin ==0
  Inputs={};arglist={'',''};
elseif nargin ==1
  Inputs=varargin{1};arglist={'',''};
elseif nargin ==2
  Inputs=varargin{1}; arglist=varargin{2};
else error('Usage: [out1,..] = kigeowarp(Inputs,arglist).');
end
if size(arglist,2)~=2
  error('arglist must be of form {''ParameterTag1'',value1;''ParameterTag2'',value2}')
 end
narglist={'i', '__input';'planes', 0;'o', '__output';'wcoeffs', '__input';'hcoeffs', '__input'};
maxval={0,0,0,1,1};
minval={0,0,0,1,1};
istoggle=[0,1,0,1,1];
was_set=istoggle * 0;
paramtype={'InputFile','Toggle','OutputFile','InputFile','InputFile'};
% identify the input arrays and assign them to the arguments as stated by the user
if ~iscell(Inputs)
Inputs = {Inputs};
end
NumReqOutputs=1; nextinput=1; nextoutput=1;
  for ii=1:size(arglist,1)
  wasmatched=0;
  for jj=1:size(narglist,1)
   if strcmp(arglist{ii,1},narglist{jj,1})  % a given argument was matched to the possible arguments
     wasmatched = 1;
     was_set(jj) = 1;
     if strcmp(narglist{jj,2}, '__input')
      if (nextinput > length(Inputs))
        error(['Input ' narglist{jj,1} ' has no corresponding input!']);
      end
      narglist{jj,2} = 'OK_in';
      nextinput = nextinput + 1;
     elseif strcmp(narglist{jj,2}, '__output')
      if (nextoutput > nargout)
        error(['Output nr. ' narglist{jj,1} ' is not present in the assignment list of outputs !']);
      end
      if (isempty(arglist{ii,2}))
        narglist{jj,2} = 'OK_out';
      else
        narglist{jj,2} = arglist{ii,2};
      end

      nextoutput = nextoutput + 1;
      if (minval{jj} == 0)
         NumReqOutputs = NumReqOutputs - 1;
      end
     elseif isstr(arglist{ii,2})
      narglist{jj,2} = arglist{ii,2};
     else
        if strcmp(paramtype{jj}, 'Integer') & (round(arglist{ii,2}) ~= arglist{ii,2})
            error(['Argument ' arglist{ii,1} ' is of integer type but non-integer number ' arglist{ii,2} ' was supplied']);
        end
        if (minval{jj} ~= 0 | maxval{jj} ~= 0)
          if (minval{jj} == 1 & maxval{jj} == 1 & arglist{ii,2} < 0)
            error(['Argument ' arglist{ii,1} ' must be bigger or equal to zero!']);
          elseif (minval{jj} == -1 & maxval{jj} == -1 & arglist{ii,2} > 0)
            error(['Argument ' arglist{ii,1} ' must be smaller or equal to zero!']);
          elseif (minval{jj} == 2 & maxval{jj} == 2 & arglist{ii,2} <= 0)
            error(['Argument ' arglist{ii,1} ' must be bigger than zero!']);
          elseif (minval{jj} == -2 & maxval{jj} == -2 & arglist{ii,2} >= 0)
            error(['Argument ' arglist{ii,1} ' must be smaller than zero!']);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} < minval{jj})
            error(['Argument ' arglist{ii,1} ' must be bigger than ' num2str(minval{jj})]);
          elseif (minval{jj} ~= maxval{jj} & arglist{ii,2} > maxval{jj})
            error(['Argument ' arglist{ii,1} ' must be smaller than ' num2str(maxval{jj})]);
          end
        end
     end
     if ~strcmp(narglist{jj,2},'OK_out') &  ~strcmp(narglist{jj,2},'OK_in')
       narglist{jj,2} = arglist{ii,2};
     end
   end
   end
   if (wasmatched == 0 & ~strcmp(arglist{ii,1},''))
        error(['Argument ' arglist{ii,1} ' is not a valid argument for this function']);
   end
end
% match the remaining inputs/outputs to the unused arguments and test for missing required inputs
 for jj=1:size(narglist,1)
     if  strcmp(paramtype{jj}, 'Toggle')
        if (narglist{jj,2} ==0)
          narglist{jj,1} = '';
        end;
        narglist{jj,2} = '';
     end;
     if  ~strcmp(narglist{jj,2},'__input') && ~strcmp(narglist{jj,2},'__output') && istoggle(jj) && ~ was_set(jj)
          narglist{jj,1} = '';
          narglist{jj,2} = '';
     end;
     if strcmp(narglist{jj,2}, '__input')
      if (minval{jj} == 0)  % meaning this input is required
        if (nextinput > size(Inputs))
           error(['Required input ' narglist{jj,1} ' has no corresponding input in the list!']);
        else
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        end
      else  % this is an optional input
        if (nextinput <= length(Inputs))
          narglist{jj,2} = 'OK_in';
          nextinput = nextinput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end;
     else
     if strcmp(narglist{jj,2}, '__output')
      if (minval{jj} == 0) % this is a required output
        if (nextoutput > nargout & nargout > 1)
           error(['Required output ' narglist{jj,1} ' is not stated in the assignment list!']);
        else
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
          NumReqOutputs = NumReqOutputs-1;
        end
      else % this is an optional output
        if (nargout - nextoutput >= NumReqOutputs)
          narglist{jj,2} = 'OK_out';
          nextoutput = nextoutput + 1;
        else
          narglist{jj,1} = '';
          narglist{jj,2} = '';
        end;
      end
     end
  end
end
if nargout
   varargout = cell(1,nargout);
else
  varargout = cell(1,1);
end
global KhorosRoot
if exist('KhorosRoot') && ~isempty(KhorosRoot)
w=['"' KhorosRoot];
else
if ispc
  w='"C:\Program Files\dip\khorosBin\';
else
[s,w] = system('which cantata');
w=['"' w(1:end-8)];
end
end
[varargout{:}]=callKhoros([w 'igeowarp"  '],Inputs,narglist);


%{
nohup matlab -nodesktop -nodisplay < main002_instSeg_v1_ftAbsEucMM_epoch83.m 1>main002_instSeg_v1_ftAbsEucMM_epoch83.log &
%}
clear
% close all
clc;

addpath './fun4MeanShift';
addpath('./local_functions_demo1');
addpath '../libs/exportFig/';
addpath '../libs/layerExt/';
addpath '../libs/myFunctions/';

path_to_matconvnet = '../libs/matconvnet-1.0-beta23_modifiedDagnn';

run(fullfile(path_to_matconvnet, 'matlab', 'vl_setupnn'));
addpath(genpath(fullfile('dependencies', 'matconvnet','examples')));
%% read matconvnet model
load('imdb_toydata_v3_from_mnist.mat');
imdb.path = './toydata_v3';
imdb.path_to_dataset = './toydata_v3';
% set GPU
gpuId = 1;
gpuDevice(gpuId);
flagSaveFig = true; % {true false} whether to store the result

saveFolder = 'main007_instSeg_v1_absEucMM';
modelName = 'softmax_net-epoch-83.mat';

netbasemodel = load( fullfile('./exp', saveFolder, modelName) );
netbasemodel = netbasemodel.net;

netbasemodel.layers(136).block = rmfield(netbasemodel.layers(136).block, 'ignoreAverage');
netbasemodel.layers(135).block = rmfield(netbasemodel.layers(135).block, 'ignoreAverage');

netbasemodel = dagnn.DagNN.loadobj(netbasemodel);
%% 1st mean-shift grouping loop
keepLayerName = sprintf('obj_instSeg_reg');
netbasemodel.layers(netbasemodel.getLayerIndex(keepLayerName)).block.lastLayerName = 'res7_cosSim';
keepLayerName = sprintf('obj_instSeg_MM');
netbasemodel.layers(netbasemodel.getLayerIndex(keepLayerName)).block.lastLayerName = 'res7_cosSim';

weight_for_losses = {'obj_instSeg_reg', 1, 'obj_instSeg_MM', 1};
sName_l2norm = 'res7_l2norm';
gt_name =  sprintf('gt_ins');
GaussianBandwidth = 0.1;
randSampleRatio = 0.2;
for loopIdx = 1:5
    [netbasemodel, sName, sName_l2norm] = addOneLoop_forMeanShiftGrouping(...
        netbasemodel, sName_l2norm, loopIdx,...
        GaussianBandwidth, randSampleRatio);

    % add regression loss
    obj_name = sprintf('loop%d_instSeg_reg', loopIdx);
    netbasemodel.addLayer(obj_name, ...
        InstanceSegRegLoss_randSample('loss', 'cosinesimilarityabsregloss', 'lastLayerName', sName), ... softmaxlog logistic
        {sName, gt_name}, obj_name);
    weight_for_losses{end+1} = obj_name;
    weight_for_losses{end+1} = 1;

    % add max-margin loss
    obj_name = sprintf('loop%d_instSeg_MM', loopIdx);
    netbasemodel.addLayer(obj_name, ...
        InstanceSegMMLoss_randSample('loss', 'cosinesimilaritymmloss', 'marginAlpha_', 0.07, 'adaptiveMM', false, 'lastLayerName', sName), ...
        {sName, gt_name}, obj_name)
    weight_for_losses{end+1} = obj_name;
    weight_for_losses{end+1} = 1;
end
%% show learning rates for all layers
for ii = 1:numel(netbasemodel.layers)
    curLayerName = netbasemodel.layers(ii).name;
    if strfind(curLayerName, 'bn')
        fprintf('%03d, %s\n', ii, curLayerName);
        netbasemodel.params(netbasemodel.layers(ii).paramIndexes(3)).learningRate = 0.1;
    end
end
netbasemodel.params(netbasemodel.getParamIndex('res6_conv_f')).learningRate = 1;
netbasemodel.params(netbasemodel.getParamIndex('res6_conv_b')).learningRate = 1;

for i = 1:numel(netbasemodel.params)
    fprintf('%d\t%25s, \t%.2f',i, netbasemodel.params(i).name, netbasemodel.params(i).learningRate);
    fprintf('\tsize: %dx%dx%dx%d\n', size(netbasemodel.params(i).value,1), size(netbasemodel.params(i).value,2), size(netbasemodel.params(i).value,3), size(netbasemodel.params(i).value,4));
end
%% configure training environment
batchSize = 1;
totalEpoch = 100;
learningRate = 1:totalEpoch;
learningRate = (1.0e-4) * (1-learningRate/totalEpoch).^0.9;

weightDecay=0.0005; % weightDecay: usually use the default value

opts.batchSize = batchSize;
opts.learningRate = learningRate;
opts.weightDecay = weightDecay;
opts.momentum = 0.9 ;

opts.expDir = fullfile('./exp', 'main002_instSeg_v1_ftAbsEucMM_epoch83');
if ~isdir(opts.expDir)
    mkdir(opts.expDir);
end

opts.withSemanticSeg = false ;
opts.withInstanceSeg = true ;
opts.withWeights = false ;

opts.numSubBatches = 1 ;
opts.continue = true ;
opts.gpus = gpuId ;
%gpuDevice(opts.train.gpus); % don't want clear the memory
opts.prefetch = false ;
opts.sync = false ; % for speed
opts.cudnn = true ; % for speed
opts.numEpochs = numel(opts.learningRate) ;
opts.learningRate = learningRate;

for i = 1:2
    curSetName = imdb.sets.name{i};
    idxList = find(imdb.set==i);
    curList = imdb.imgList(idxList);
    opts.(curSetName) = curList;
end

opts.checkpointFn = [];
mopts.classifyType = 'softmax';

rng(777);
bopts = netbasemodel.meta.normalization;
bopts.numThreads = 12;
bopts.imdb = imdb;
%% train
fn = getBatchWrapper4toyDigitV2(bopts);

opts.backPropDepth = inf; % could limit the backprop
prefixStr = [mopts.classifyType, '_'];
opts.backPropAboveLayerName = 'conv1_conv';

trainfn = @cnnTrain;
[netbasemodel, info] = trainfn(netbasemodel, prefixStr, imdb, fn, 'derOutputs', ...
    weight_for_losses, ...
    opts);

%% leaving blank
%{
%}


%% t_mrdViewFibers
%
% Load and view a set of fibers stored in a PDB (v3) file using mrMesh.
%
% (c) Stanford VISTA Team

%% Make sure that vistadata is on your path
% vistaDataPath;

%% dtiStart for scripts

dataDir = fullfile(mrvDataRootPath,'diffusion','sampleData');
dt6Name = fullfile(dataDir,'dti40','dt6.mat');
% Initialize mrDiffusion
[dtiFig, dtiH]= mrDiffusion('on',dt6Name);
% At this point you can use dtiSet/Get on dtiH

if isunix, disp('Start mrMeshSrv.exe')
else       mrmStart
end

%% Load fiber group structure
difDir = fullfile(mrvDataRootPath,'diffusion','sampleData','dti40');
chdir(difDir)

fgName = fullfile(mrvDataRootPath,'diffusion','sampleData','fibers','leftArcuate.pdb');
% The mtr<> function name is old from metrotrac days.  It will probably change some
% day.
fg = mtrImportFibers(fgName);

%% Show fibers in mrMesh

% Attach the fiber group to the handles
dtiH = dtiSet(dtiH,'add fiber group',fg);

set(dtiH.cbUseMrMesh, 'Value',1);     % Use mrMesh for update
set(dtiH.cbShowFibers,'Value',1);     % Show loaded fibers
set(dtiH.cbShowMatlab3d,'Value',0);   % No 3d Matlab window
set(dtiH.popupBackground,'Value',2);  % Mean diffusivity

guidata(dtiFig,dtiH);  % Refresh the Matlab window handles.

mrmCloseWindow(dtiH.mrMesh.id,dtiH.mrMesh.host);

showMeshWindow = 1;
dtiH = dtiRefreshFigure(dtiH,showMeshWindow);

%% The code below pulls out key routines from dtiRefreshFigure

% Closing the window and then running this is much faster than removing the
% previous actors.
mrmCloseWindow(dtiH.mrMesh.id,dtiH.mrMesh.host);

% This code, which is not normally used, permits a faster reload of the
% mrMesh window. The code below here could be placed in a routine like
%
%   id = dtiMeshView(dtiH,varargin);
%
set(dtiH.popupBackground,'Value',1);  % Choose type of background (1-4)
set(dtiH.rbSagittal,'Value',1);       % Choose which planes
set(dtiH.rbCoronal, 'Value',0);
set(dtiH.rbAxial,   'Value',1);

% With mrMesh - This started with the code from dtiRefreshFogure/dtiFiberUI
% Needs anat, anatXform
[xSliceRgb,ySliceRgb,zSliceRgb,anat,anatXform, ...
    mmPerVoxel,xform,xSliceAxes,ySliceAxes,zSliceAxes] = ...
    dtiGetCurSlices(dtiH);

curPosition = dtiGet(dtiH,'curpos');

% Should be:
% anatXform = dtiGet(dtiH,'anatXform');

[zIm] = dtiGetSlice(anatXform, anat, 3, curPosition(3), [], dtiH.interpType);
[yIm] = dtiGetSlice(anatXform, anat, 2, curPosition(2), [], dtiH.interpType);
[xIm] = dtiGetSlice(anatXform, anat, 1, curPosition(1), [], dtiH.interpType);
% figure; imagesc(zIm); axis image; colormap(gray)

% This is a little slow.
[xIm,yIm,zIm] = dtiMrMeshSelectImages(dtiH,xIm,yIm,zIm);
origin = dtiMrMeshOrigin(dtiH);
dtiH = dtiMrMesh3AxisImage(dtiH, origin, xIm, yIm, zIm);

%%  Manipulating the mesh view

% Need to permute the rotation matrix, I think, for various cases.
% I haven't worked that out.

msh = dtiGet(dtiH,'mesh'); % If you have a mrDiffusion guidata in dtiH
mrmRotateCamera(msh.id,'front',1); pause(1)
mrmRotateCamera(msh.id,'back',1);  pause(1)
mrmRotateCamera(msh.id,'top',1);   pause(1)
mrmRotateCamera(msh.id,'bottom',1);pause(1)
mrmRotateCamera(msh.id,'right',1); pause(1)
mrmRotateCamera(msh.id,'left',1);

%% End after here


function Projector = in_projector_fif( SspFiles, ChannelNames )
% IN_PROJECTOR_FIF: Read a FIF file, and return a brainstorm Channel structure.
%
% USAGE:  Projector = in_projector_fif( SspFile, ChannelNames );
%         Projector = in_projector_fif( projs, ChannelNames );

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

global FIFF;

%% ===== PARSE INPUTS =====
if ischar(SspFiles)
    SspFiles = {SspFiles};
end
% Remove spaces in all the channel names
tmp = ChannelNames;
ChannelNames = cellfun(@(c)upper(c(c~=' ')), ChannelNames, 'UniformOutput', 0);

%% ===== READ PROJECTORS =====
projs = [];
% Read files
if iscell(SspFiles)
    % Loop on all files
    for i = 1:length(SspFiles)
        % Open SSP file
        [ fid, tree ]  = fiff_open(SspFiles{i});
        if (fid < 0)
            error(['Cannot open FIFF file : "' SspFiles{i} '"']);
        end
        % Read projectors
        node = fiff_dir_tree_find(tree, FIFF.FIFFB_PROJ);
        newProjs = fiff_read_proj(fid, node);
        % Add projectors to list of all projectors to combine
        if isempty(projs)
            projs = newProjs;
        else
            projs = [projs, newProjs];
        end
        % Close SSP file
        fclose(fid);
    end
% Projectors already loaded
elseif isstruct(SspFiles)
    projs = SspFiles;
else
    error('Invalid input.');
end

%% ====== BUILD PROJECTION MATRIX =====
Projector = repmat(db_template('projector'), 0);
nChannels = length(ChannelNames);
% Collect all the projections from the FIF file
for i = 1:length(projs)
    % Check type of the projector
    if (projs(i).kind ~= FIFF.FIFFV_PROJ_ITEM_FIELD)
        fprintf(1, 'SSP> Unsupported type of projector #%d: "%s". Skipping...\n', projs(i).kind, projs(i).desc);
        continue;
    end
    % Copy basic information
    iNew = length(Projector) + 1;
    Projector(iNew).Comment = projs(i).desc;
    if projs(i).active
        Projector(iNew).Status = 2;  % Recordings in the file saved this way
    else
        Projector(iNew).Status = 1;  % Projector is selected and applied dynamically to the recordings
    end
    % Get the projectors values
    data = projs(i).data;
    % Remove the spaces in the channel names
    data.col_names = cellfun(@(c)upper(c(c~=' ')), data.col_names, 'UniformOutput', 0);
    % Copy the information of each channel
    U = zeros(nChannels, data.nrow);
    for iCol = 1:data.ncol
        iChan = find(strcmpi(data.col_names{iCol}, ChannelNames));
        if isempty(iChan)
            % Channel not found
            continue;
        elseif (length(iChan) > 1)
            disp('IN> Warning: Several channels have the same name, the result might be random...');
            iChan = iChan(1);
        end
        U(iChan,:) = data.data(:,iCol)';
    end
    % Finish filling the entry
    Projector(iNew).Components = U;
    Projector(iNew).CompMask   = ones(1,data.nrow);
end


function display_obj(obj,texture)
%
% function display_obj(obj,texture)
%
% displays an obj structure with texture
%
% INPUTS:  obj:     object data
%                   - obj.v:    vertices
%                   - obj.vt:   texture coordinates
%                   - obj.f.v:  face definition vertices
%                   - obj.f.vt: face definition texture
%
%       : texture -  texture image full path
%
% Author: Bernard Abayowa
% University of Dayton
% 6/16/08

texture = imread(texture);
texture_img = flipdim(texture,1);
[sy sx sz] = size(texture_img);
texture_img =  reshape(texture_img,sy*sx,sz);

% make image 3D if grayscale
if sz == 1
    texture_img = repmat(texture_img,1,3);
end

% select what texture correspond to each vertex according to face
% definition
[vertex_idx fv_idx] = unique(obj.f.v);
texture_idx = obj.f.vt(fv_idx);

x = abs(round(obj.vt(:,1)*(sx-1)))+1;
y = abs(round(obj.vt(:,2)*(sy-1)))+1;
xy = sub2ind([sy sx],y,x);
texture_pts = xy(texture_idx);
tval = double(texture_img(texture_pts,:))/255;

% display object
figure; patch('vertices',obj.v,'faces',obj.f.v,'FaceVertexCData', tval);
shading interp
colormap gray(256);
lighting phong;
camproj('perspective');
axis square;
axis off;
axis equal
axis tight;
cameramenu


function output = ...
  SynthesizeByEventBasedMethod(event_index, event_time, ...
                               harmonics_level_at_event, ...
                               harmonics_deviation_at_event, fs, f0i, ...
                               unvoiced_mask)
% Synthesize speech from event based information made from lean
% representation
% output = ...
%  SynthesizeByEventBasedMethod(event_index, event_time, ...
%                               harmonics_level_at_event, ...
%                               harmonics_deviation_at_event, fs, f0i, ...
%                               unvoiced_mask)
%
% output = ...
%  SynthesizeByEventBasedMethod(event_index, event_time, ...
%                               harmonics_level_at_event, ...
%                               harmonics_deviation_at_event, fs, f0i, ...
%                               unvoiced_mask, interp_method)
%
% Input argument
%
% event_index: event location in sample index
% event_time: actual event location (s)
% harmonics_level_at_event: power information (dB)
% harmonics_deviation_at_event: rondom relative power information (dB)
% fs: sampling frequency (Hz)
% f0i: fundamental frequency in sample time (Hz)
% unvoiced_mask: silent indicator (for each event)
%
% Return value
%
% output: synthesized signal

% Copyright 2016 Google Inc. All Rights Reserved
% Author: hidekik@google.com (Hideki Kawahara)

%% convert to FFT-based array
narginchk(7, 7);
[response_for_periodic_trim, response_for_random_trim, ola_fftl] = ...
  GenerateResponses(f0i, fs, harmonics_level_at_event, ...
                    harmonics_deviation_at_event, event_time, event_index);
%%
out_buffer = f0i*0;
noise_out_buffer = out_buffer;
synth_out_buffer = out_buffer;
synth_noise_buffer = out_buffer;
nData = length(out_buffer);
half_width_of_BLIT = 2 * fs / (0.55 * fs);
blit_base = -ceil(half_width_of_BLIT):ceil(half_width_of_BLIT);
initial_index = 1;
noise_spread = max(1,min(1,unvoiced_mask(:))); % 0.2 to 1
base_noise = randn(length(out_buffer),1);
periodic_working_buffer = zeros(ola_fftl, 1);
zero_vector = periodic_working_buffer;
ola_zero = round(fs / min(f0i)) + 1;
ola_base = (1:ola_fftl)' - ola_zero;
sample_index = (1:nData)';
for ii = 1:length(event_time)
  base_index = event_index(ii) - blit_base; % minus (-) is the proper sign
  [synth_out_buffer, out_buffer] = ...
    UpdatePeriodicComponent(response_for_periodic_trim, synth_out_buffer, ...
                            base_index, ii, zero_vector, event_time, ...
                            event_index, fs, ola_zero, ola_base, ola_fftl, ...
                            sample_index, unvoiced_mask, out_buffer);
  [synth_noise_buffer, noise_out_buffer, initial_index] = ...
    UpdateRandomComponent(response_for_random_trim, synth_noise_buffer, ...
                          initial_index, ii, noise_spread, event_time, ...
                          event_index, ola_zero, ola_base, ola_fftl, ...
                          sample_index, base_noise, noise_out_buffer);
end;
raw_blit = GenerateBlitImpulse(blit_base, half_width_of_BLIT);
blit_equalizer_response = DesignBlitEqualizer(raw_blit);
half_width_of_EQBLIT = round((length(blit_equalizer_response) - 1) / 2);
tmp_period = fftfilt(blit_equalizer_response, ...
                     [synth_out_buffer; zeros(2 * half_width_of_EQBLIT, 1)]);
synth_noise_out = synth_noise_buffer ./ sqrt(f0i);
synth_periodic_out = ...
  tmp_period(half_width_of_EQBLIT + (1:nData)) ./ sqrt(f0i);
output = synth_periodic_out + synth_noise_out;
end

function [response_for_periodic_trim, response_for_random_trim, ola_fftl] ...
  = GenerateResponses(f0i, fs, harmonics_level_at_event, ...
                      harmonics_deviation_at_event, event_time, event_index)
kResidualToSNR = 11; % calibration constant from residual to SNR (dB)
spectrum_out.used_f0 = f0i(event_index);
spectrum_out.sampling_frequency = fs;
spectrum_out.temporal_positions = event_time;
spectrum_out.harmonic_power_dB = harmonics_level_at_event;
spectral_envelope = CalculateSpectrumEnvelope(spectrum_out);
noise_out = spectrum_out;
noise_out.refined_f0 = f0i(event_index);
noise_out.frame_time = event_time;
noise_out.aperiodicity_matrix = harmonics_deviation_at_event;
aperiodicity_dB = ...
  CalculateAperiodicitySgram(noise_out) + kResidualToSNR;
aperiodicity_ratio = ConvertDecibelToPower(min(0, aperiodicity_dB));
maximum_response_length = 0.03; % default 30 ms
response_lengh = round(maximum_response_length * fs); % samples
maximum_noise_length = 2 / min(f0i);
noise_length = round(maximum_noise_length * fs); % samples
ola_fftl = 2 ^ ceil(log2(noise_length + response_lengh + 1));
fftl = (size(spectral_envelope ,1) - 1) * 2;

data_frequency_axis = (0:fftl - 1) / fftl * fs;
data_frequency_axis(data_frequency_axis > fs / 2) = ...
  data_frequency_axis(data_frequency_axis > fs / 2) - fs;
% TODO(hidekik) need check: originally 50 maybe 150 better? (hz)
% 1000 is the corner frequenccy (Hz)
low_noise_masker = ...
  GetSigmoidNoiseShaper(data_frequency_axis, 1000, 150);
spectral_envelope_DFTBIN = ...
  [spectral_envelope;spectral_envelope(end-1:-1:2,:)];
random_envelope_DFTBIN = ...
  [aperiodicity_ratio;aperiodicity_ratio(end-1:-1:2,:)];
random_envelope_SHAPED = ...
  diag(low_noise_masker) * random_envelope_DFTBIN;
periodic_part = max(0.0001, (1 - random_envelope_SHAPED));
periodic_spectrum = sqrt(periodic_part .* spectral_envelope_DFTBIN);
random_spectrum = ...
  sqrt(random_envelope_SHAPED .* spectral_envelope_DFTBIN);
%%
time_axis = (0:fftl -1)' / fs;
time_shaper = time_axis * 0 + 1;
time_shaper(time_axis > maximum_response_length) = 0;
time_segment = ...
  (time_axis(time_axis > 0.8 * maximum_response_length & ...
  time_axis <= maximum_response_length) ...
  -0.8 * maximum_response_length) / (0.2 * maximum_response_length);
time_shaper(time_axis > 0.8 * maximum_response_length & ...
  time_axis <= maximum_response_length) ...
  = 0.5 + 0.5 * cos(pi * time_segment);
cepstrum_for_periodic = ifft(log(periodic_spectrum));
complex_cepstrum_for_periodic = cepstrum_for_periodic;
complex_cepstrum_for_periodic(fftl / 2 + 1:end,:) = 0;
complex_cepstrum_for_periodic(2:fftl / 2,:) = ...
  complex_cepstrum_for_periodic(2:fftl / 2,:) * 2;
response_for_periodic = diag(time_shaper) * ...
  real(ifft(exp(fft(complex_cepstrum_for_periodic))));
response_for_periodic_trim = response_for_periodic(1:response_lengh, :);
cepstrum_for_random = ifft(log(random_spectrum));
complex_cepstrum_for_random = cepstrum_for_random;
complex_cepstrum_for_random(fftl / 2 + 1:end,:) = 0;
complex_cepstrum_for_random(2:fftl / 2,:) = ...
  complex_cepstrum_for_random(2:fftl / 2,:) * 2;
response_for_random = diag(time_shaper) * ...
  real(ifft(exp(fft(complex_cepstrum_for_random))));
response_for_random_trim = response_for_random(1:response_lengh, :);
end

function [synth_out_buffer, out_buffer] = ...
  UpdatePeriodicComponent(response_for_periodic_trim, synth_out_buffer, ...
                          base_index, ii, zero_vector, event_time, ...
                          event_index, fs, ola_zero, ola_base, ola_fftl, ...
                          sample_index, unvoiced_mask, out_buffer)
%
nData = length(out_buffer);
half_width_of_BLIT = 2 * fs / (0.55 * fs);
blit_base = -ceil(half_width_of_BLIT):ceil(half_width_of_BLIT);
periodic_working_buffer = zero_vector;
fractional_index = event_time(ii) * fs - event_index(ii) + 1 + blit_base;
tmp_blit = GenerateBlitImpulse(fractional_index, half_width_of_BLIT);
periodic_working_buffer(ola_zero - blit_base) = tmp_blit / sum(tmp_blit);
periodic_working_out_buffer = ...
  real(ifft(fft(periodic_working_buffer) .* ...
            fft(response_for_periodic_trim(:, ii), ola_fftl)));
out_buffer(max(1,min(nData,base_index))) = tmp_blit / sum(tmp_blit);
past_index_condition = event_index(ii) + ola_base > 0 & ...
  event_index(ii) + ola_base < nData;
synth_out_buffer(sample_index(event_index(ii) + ...
  ola_base(past_index_condition))) = ...
  periodic_working_out_buffer(past_index_condition) * ...
  (1 - unvoiced_mask(ii)) + ...
  synth_out_buffer(sample_index(event_index(ii) + ...
  ola_base(past_index_condition)));
end

function [synth_noise_buffer, noise_out_buffer, initial_index] = ...
  UpdateRandomComponent(response_for_random_trim, synth_noise_buffer, ...
                        initial_index, ii, noise_spread, event_time, ...
                        event_index, ola_zero, ola_base, ola_fftl, ...
                        sample_index, base_noise, noise_out_buffer)
%----
nData = length(noise_out_buffer);
prev_index = initial_index:event_index(ii);
if ii == length(event_time)
  post_index = event_index(ii):nData;
else
  post_index = event_index(ii):event_index(ii + 1);
end;
prev_time = (prev_index(:) - event_index(ii)) / length(prev_index);
post_time = (post_index(:) - event_index(ii)) / length(post_index);
noise_shape = 0.5 * [(1 + cos(pi*prev_time / noise_spread(ii))) .* ...
  (abs(prev_time / noise_spread(ii)) <= 1); ...
  (1 + cos(pi * post_time(2:end) / noise_spread(ii))) .* ...
  (abs(post_time(2:end) / noise_spread(ii)) <= 1)];
noise_out_buffer(prev_index(1):post_index(end)) = ...
  noise_out_buffer(prev_index(1):post_index(end)) + noise_shape .* ...
  base_noise(prev_index(1):post_index(end)) / sqrt(sum(noise_shape .^ 2));
initial_index = event_index(ii);
noise_base_index = (prev_index(1):post_index(end))' - event_index(ii);
noise_working_buffer(max(1,ola_zero + noise_base_index)) = ...
  noise_shape .* base_noise(prev_index(1):post_index(end)) / ...
  sqrt(sum(noise_shape .^ 2));
noise_working_out_buffer = ...
  real(ifft(fft(noise_working_buffer(:), ola_fftl) .* ...
            fft(response_for_random_trim(:, ii), ola_fftl)));
past_index_condition = event_index(ii) + ola_base > 0 & ...
  event_index(ii) + ola_base < nData;
synth_noise_buffer(sample_index(event_index(ii) + ...
  ola_base(past_index_condition))) = ...
  noise_working_out_buffer(past_index_condition) + ...
  synth_noise_buffer(sample_index(event_index(ii) + ...
  ola_base(past_index_condition)));
end


% This function dynamically generates distorted speech signals. It is
% usually used for speech enhancement or robust ASR tasks.
%
% Authors: Xiong Xiao, NTU, Singapore
% Last Modified: 28 Feb 2017
%
function [data, paraDD, data_log] = GenDynamicDistortion(base_data, paraDD)

clean_data = base_data(1).data;
rir_data = base_data(2).data;
noise_data = base_data(3).data;

% get settings
useSoftMask = ReturnFieldWithDefaultValue(paraDD, 'useSoftMask', 0);
randomizeFileOrder = ReturnFieldWithDefaultValue(paraDD, 'randomizeFileOrder', 1);
gainNorm = ReturnFieldWithDefaultValue(paraDD, 'gainNorm', 1);
vadStream = ReturnFieldWithDefaultValue(paraDD, 'vadStream', []);
if ~isempty(vadStream)
    vadData = base_data(vadStream);
end

[clean_data_loaded, clean_idx] = LoadOriginalWave(clean_data, paraDD, randomizeFileOrder,1);
if ~isempty(rir_data)
    [rir_data_loaded, rir_idx] = LoadOriginalWave(rir_data, paraDD, randomizeFileOrder,2);
end
if ~isempty(noise_data)
    [noise_data_loaded, noise_idx] = LoadOriginalWave(noise_data, paraDD, randomizeFileOrder,3);
end

if length(base_data)>3  % sometimes, we have other data, such as frame phone label. We just copy that to the output data.
    hasExtraData = 0; extraData = [];
    for i=4:length(base_data)
        if ~isempty(vadStream) && i == vadStream; continue; end
        extraData(end+1).data = base_data(i).data(clean_idx);  % note that the extra data streams are binded with the clean data.
        hasExtraData = 1;
    end
end

switch lower(paraDD.SNR_PDF)
    case 'uniform'
        SNR = rand(1,paraDD.nUtt4Iteration) * (paraDD.SNR_para(2) - paraDD.SNR_para(1)) + paraDD.SNR_para(1);
    case 'normal'
        SNR = randn(1,paraDD.nUtt4Iteration) * paraDD.SNR_para(2) + paraDD.SNR_para(1);
        SNR = max(-50, min(50, SNR));   % do not allow too low or high SNRs
end

if isfield(paraDD, 'seglen') && paraDD.seglen>0
    doSegmentation = 1;
    seglen = paraDD.seglen;
    segshift = paraDD.segshift;
    frame_len = paraDD.frame_len;
    frame_shift = paraDD.frame_shift;
else
    doSegmentation = 0;
end
if isfield(paraDD, 'outputDataIdxMask') && ~isempty(paraDD.outputDataIdxMask) && paraDD.outputDataIdxMask>0
    genMask = 1;
else
    genMask = 0;
end

if ~isfield(paraDD, 'outputDataIdxClean')   % by default, the clean speech should be in stream 2
    cleanStreamIdx = 2;
elseif ~isempty(paraDD.outputDataIdxClean) && paraDD.outputDataIdxClean>0   % otherwise, put it in the defined index
    cleanStreamIdx = paraDD.outputDataIdxClean;
else
    cleanStreamIdx = -1;
end

distorted = {}; clean_data_aligned = {}; mask = {};
for j=1:length(extraData)
    extraDataSeg(j).data = {};
end
for i=1:length(clean_idx)
    data_log{i} = [clean_data{clean_idx(i)}];
    if paraDD.singlePrecision
        curr_clean_wav = single(clean_data_loaded{clean_idx(i)})';
        if ~isempty(rir_data); curr_rir = single(rir_data_loaded{rir_idx(i)})'; else; curr_rir = []; end
        if ~isempty(noise_data); curr_noise = single(noise_data_loaded{noise_idx(i)})'; else; curr_noise = [];  end
    else
        curr_clean_wav = double(clean_data_loaded{clean_idx(i)})';
        if ~isempty(rir_data); curr_rir = double(rir_data_loaded{rir_idx(i)})'; else; curr_rir = []; end
        if ~isempty(noise_data); curr_noise = double(noise_data_loaded{noise_idx(i)})'; else; curr_noise = [];  end
    end
    if ~isempty(curr_rir)   % we want the direct sound to have the gain of 1
        curr_rir = curr_rir/max(curr_rir(:));
        data_log{i} = sprintf('%s\t%s', data_log{i}, rir_data{rir_idx(i)});
    else
        data_log{i} = sprintf('%s\tNULL', data_log{i});
    end
    if ~isempty(noise_data)
        data_log{i} = sprintf('%s\t%s', data_log{i}, noise_data{noise_idx(i)});
    else
        data_log{i} = sprintf('%s\tNULL', data_log{i});
    end
    data_log{i} = sprintf('%s\t%fdB', data_log{i}, SNR(i));

    [curr_distorted, curr_reverb, curr_direct] = ApplyConstRirNoise(curr_clean_wav, paraDD.fs, curr_rir, curr_noise, SNR(i), paraDD.useGPU);
    curr_distorted = gather(curr_distorted)';
    curr_distorted(:, length(curr_clean_wav)+1:end) = [];
    curr_direct = gather(curr_direct)';
    curr_direct(:, length(curr_clean_wav)+1:end) = [];
    %curr_reverb = gather(curr_reverb)';
    %curr_reverb(:, length(curr_clean_wav)+1:end) = [];

    if genMask
        if 0
            [curr_mask, curr_SNR] = genMaskFromParallelData(curr_clean_wav, curr_distorted', paraDD.fs, 0);
        else    % it's better to use curr_direct, which contains the early reflection up to 50ms after the direct sound, as the clean reference.
            if isempty(vadStream)
                [curr_mask] = genMaskFromParallelData(curr_clean_wav, curr_direct', curr_distorted', [], paraDD.fs, useSoftMask, 0);
            else
                [curr_mask] = genMaskFromParallelData(curr_clean_wav, curr_direct', curr_distorted', vadData.data{clean_idx(i)}, paraDD.fs, useSoftMask, 0);
            end
        end
    end
    if gainNorm
        curr_distorted = curr_distorted / max(abs(curr_distorted(:)));
    end
    switch class(gather(clean_data_loaded{clean_idx(i)}(1)))
        case 'int16'
            curr_distorted = StoreWavInt16(curr_distorted);
        case {'single', 'float'}
            curr_distorted = single(curr_distorted);
        otherwise
            curr_distorted = double(curr_distorted);
    end

    if doSegmentation
        curr_distorted_seg = DivideSent2Segments(curr_distorted, (seglen-1)*frame_shift+frame_len, segshift*frame_shift, 1);
        distorted = [distorted curr_distorted_seg'];
        curr_clean_wav_seg = DivideSent2Segments(clean_data_loaded{clean_idx(i)}, (seglen-1)*frame_shift+frame_len, segshift*frame_shift, 1);
        if cleanStreamIdx>0
            clean_data_aligned = [clean_data_aligned curr_clean_wav_seg'];
        end
        if genMask
            curr_mask_seg = DivideSent2Segments(curr_mask, seglen, segshift, 1);
            mask = [mask curr_mask_seg'];
        end
        for j=1:length(extraData)
            tmpDataSeg = DivideSent2Segments(extraData(j).data{i}, seglen, segshift, 1);    % we assume that the extra data are having 100Hz frame rate
            extraDataSeg(j).data = [extraDataSeg(j).data tmpDataSeg'];
        end
    else
        distorted{i} = curr_distorted;
        if cleanStreamIdx>0; clean_data_aligned{i} = clean_data_loaded{clean_idx(i)}; end
        if genMask; mask{i} = curr_mask; end
    end

    PrintProgress(i, length(clean_idx), max(10, round(length(clean_idx)/10)), ...
            'GenDynamicDistortion->Mixing clean speech with RIR and noise');
end

nOutputStream = 3;
if ~isfield(paraDD, 'outputDataIdxDistorted')   % by default, the distorted speech should be in stream 1
    data(1).data = distorted;
elseif ~isempty(paraDD.outputDataIdxDistorted) && paraDD.outputDataIdxDistorted>0   % otherwise, put it in the defined index
    data(paraDD.outputDataIdxDistorted).data = distorted;
else
    nOutputStream = nOutputStream -1;
    % do not output distorted speech
end
if cleanStreamIdx>0
    data(cleanStreamIdx).data = clean_data_aligned;
else
    nOutputStream = nOutputStream -1;
    % do not output clean speech
end
if genMask
    data(paraDD.outputDataIdxMask).data = mask;
else
    nOutputStream = nOutputStream -1;
end

if hasExtraData
    if doSegmentation
        data = [data extraDataSeg];
    else
        data = [data extraData];
    end
end

end

function [data, idx] = LoadOriginalWave(wavlist, paraDD, randomizeFileOrder, streamIdx)
if isempty(wavlist)
    return;
end
if randomizeFileOrder
    idx = randperm(length(wavlist));
else
    idx = 1:length(wavlist);
end
nRepeat = ceil(paraDD.nUtt4Iteration/length(idx));
idx = repmat(idx, 1, nRepeat);
idx(paraDD.nUtt4Iteration+1:end) = [];

if paraDD.inputFeature(streamIdx)
    data = wavlist;
else
    need2load = unique(idx);
    for i=1:length(need2load)
        PrintProgress(i, length(need2load), max(100, round(length(need2load)/10)), ...
            sprintf('GenDynamicDistortion->LoadOriginalWav->Stream %d', streamIdx));
        % data{need2load(i)} = InputReader(wavlist{need2load(i)}, paraDD.fileReader(streamIdx));
        data{need2load(i)} = paraDD.fileReader{streamIdx}.read(wavlist{need2load(i)});
    end
end
end


function studyreg = readLeksellStudyregFile(filename)
%"readLeksellStudyregFile"
%   Reads a Leksell studyreg file using decodeLeksellData and places the
%   fields into a datastructure with meaningful names.  This structure
%   shows the meaning of the transformation matrices in the Leksell Study
%   files so that it is clear which modality the matrices are going to and
%   from.  However, this information is not used when importing to CERR
%   because the target modality is always LGP (the Leksell coordinate
%   system).
%
%JRA 6/13/05
%
%LM: KRK, 05/29/07, added additional documentation
%
%Usage:
%   studyreg = readLeksellStudyregFile(filename)
%
% 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/>.

fid = fopen(filename, 'r', 'b');

data = decodeLeksellData(fid);

fclose(fid);

%If data does not have at least two elements it is effectively empty.
if length(data) < 2
    studyreg = [];
    return;
end

for i=1:length(data) - 1
    rawStudyreg = data{i};

	studyreg(i).original_modality       = rawStudyreg{1}; %MR/CT/etc
	studyreg(i).target_modality         = rawStudyreg{2}; %Leksell
  	studyreg(i).registration_value      = rawStudyreg{3}; %original modalities reg. value

end


function [vM, vM_]=cluster200x(fTaumin, fTaumax, fXk, fXmeff, fP1, fRfact, fMerr, fZerr,mCat)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Example: [vMain, vClus]=cluster200x(2880,14400,0.5,3.0,0.99,10,0,0,a)
% Author: van Stiphout, Thomas
% Email: vanstiphout@sed.ethz.ch
% Created: 14. Feb. 2007
% Changed: -
%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Variables:
% mCat              Catalog to be declustered
% iYr1                Starting year (read from mCat)
% iYr2                Ending year (read from mCat)
% fXmeff            Magnitude cutoff
% fRfact             rfact
% fTau0              Tau0 (is equal fTaumin)
% fTaumin          Taumin
% fTaumax         Taumax
% fP1                  P1
% fXk                  xk
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% prevent year 2000 problem / transition
fYrStart_=floor(min(mCat(:,3)));
mCatTime_=mCat(:,3);
mCat(:,3)=mCat(:,3)-fYrStart_;
% prepare variables
fTaumin=fTaumin*24*60;
fTau0=fTaumin;
fTaumax=fTaumax*24*60;
sYr1=num2str(floor(min(mCat(:,3))));
sYr2=num2str(ceil(max(mCat(:,3))));
% sYr1=sYr1(3:4);
% sYr2=sYr2(3:4);

% does catalog contain 12 columns?
if (size(mCat,2)<12)
    mTmp=nan(size(mCat,1),(12-size(mCat,2)));
    mCat=[mCat mTmp];
end
!rm tmp cluster.* input.cmn tmp v.dat CA.hypo71
% export to readalbe format for tmp
export2hypo71(mCat,'tmp');
% remove NaN from exported catalog
unix(['sed ''s/NaN/   /'' tmp > CA.hypo71']);
% write input file (input.cmn)
infile=fopen('input.cmn','w');
fprintf(infile,'CA.hypo71\n4\n%2s\n%2s\n%3.1f\n%06.3f\n%08.3f\n%010.2f\n%010.2f\n%05.2f\n%05.2f',...
    sYr1,sYr2,fXmeff,fRfact,fTau0,fTaumin,fTaumax,fP1,fXk);
fclose(infile);
% run declustering algorithm of f reasenberg cluster200x
unix('~/zmap/src/thomas/decluster/reasen/cluster200x > tmp');
% extract vector with 1 and 0's of events in declustered catalog
unix(['awk -f ~/zmap/src/thomas/decluster/reasen/clu2list.awk cluster.ano > v.dat ' ]);
% import vector
vM_=load('v.dat');
vSelN0=(vM_==0);
vSelNc=zeros(size(vSelN0));

for i=1:max(vM_)
    vPos1=find(vM_==i);
    if ~isempty(vPos1)
        vPos2=find(mCat(vPos1,6)==max(mCat(vPos1,6)));
        if (vPos2==1)
            vPos3=vPos1(vPos2);
        else
            vPos3=vPos1(1);
        end
        vSelNc(vPos3)=1;
    end
    clear vPos1 vPos2 vPos3
end

vM=(logical(vSelN0) | logical(vSelNc));
% % clean up
% !rm cluster.*


function [ bad_idx ] = prepLBPconf( config, options, type, annotation_struct, images_fname, gt_fname )
%PREPLBPCONF Prepares configuration for LBP features function call.
%   Detailed explanation goes here
%
% INPUT:
%   config       ...     structure config (see experiment02.m)
%   options      ...     structure options (bw, bw_margin, components, M, order, ...)
%   type         ...     string with one of ['TRN', 'TST', 'VAL'] value
%   datapath     ...     path to dataset (TRN, VAL, TST)
%   images_fname ...     full path filename where matrix with images will be stored
%   gt_fname     ...     full path filename where GT will be stored
%
% 09-08-10 Michal Uricar
% 12-07-11 Michal Uricar, corners dataset modification

    bad_idx = [];

    if (~strcmp(type, 'TRN') && ~strcmp(type, 'VAL') && ~strcmp(type, 'TST'))
        error('??? Invalid type of dataset. Must be one of [''TRN'', ''VAL'', ''TST'']');
    end;

    fprintf(['Prepare ' type ' images...\n']);

%     load(datapath);
    switch (config.nImages)
        case 'all'
            nImages = annotation_struct.N;
        case 'half'
            nImages = round(annotation_struct.N/2);
        otherwise
            nImages = str2num(config.nImages);
    end;

    gt = cell(nImages, 1);

    fprintf([type ': Creating array of images...\n']);
    imSize = [options.bw(2), options.bw(1)];
    Images = uint8(zeros(options.bw(1)*options.bw(2), nImages));
    for i = 1 : nImages
        [I, Annotation] = getImageFrame(options, i, annotation_struct);

        if (isempty(Annotation))
            bad_idx = [bad_idx i];
            continue;
        end;

        Points = prepareS0gt(Annotation.P, options);    % transform nose to the center of face
        Points = Points(:, options.comselect);          % extract relevant points only (name list in options.compnames)
        Points(:, options.M) = Annotation.P(:, 10);     % copy back original nose position

        fprintf([type ': %.2f%% Processing image %s.\n'], i*100/nImages, Annotation.image.filename);

        Images(:, i) = I(:);
        gt{i} = Points;
        %gt{i} = TestPoints;
    end;

    %% Save array of Images
    fprintf([ type ': Saving array of images to file %s...\n'], images_fname);
    save(images_fname, 'Images', 'imSize');
    fprintf([ type ': Saving ground truth points to file %s...\n'], gt_fname);
    save(gt_fname, 'gt');
end


function D = spm_eeg_combineplanar(S)
% Combine data from MEGPLANAR sensors
% FORMAT D = spm_eeg_combineplanar(S)
%
% S        - optional input struct
%  fields of S:
%   D        - MEEG object or filename
%   mode     -
%              'append'     - add combined channels to the origal channels
%              'replace'    - replace MEGPLANAR with combined [default]
%              'replacemeg' - replace all MEG channels with combined but
%                             keep non-MEG
%              'keep'       - only write out the combined channels
%
%   prefix   - prefix for the output file [default: 'P']
%
% Output:
% D        - MEEG object (also written on disk)
%
%__________________________________________________________________________
% Copyright (C) 2008-2017 Wellcome Trust Centre for Neuroimaging

% Vladimir Litvak
% $Id: spm_eeg_combineplanar.m 7132 2017-07-10 16:22:58Z guillaume $

SVNrev = '$Rev: 7132 $';

%-Startup
%--------------------------------------------------------------------------
spm('FnBanner', mfilename, SVNrev);
spm('FigName','MEG Combine planar'); spm('Pointer','Watch');

if ~isfield(S, 'prefix'),       S.prefix   = 'P';           end
if ~isfield(S, 'mode'),         S.mode     = 'replace';     end

D = spm_eeg_load(S.D);

isTF = strncmpi(D.transformtype,'TF',2);

chanset = spm_eeg_planarchannelset(D.chanlabels);

chanind = [];
labelnew = {};
for i = 1:size(chanset, 1)
    cind = D.indchannel(chanset(i, 1:2));
    if length(cind) == 2
        chanind  = [chanind cind];
        labelnew = [labelnew; chanset(i, end)];
    end
end

megind    = D.indchantype('MEG');
planarind = D.indchantype('MEGPLANAR');

% add a row of zeros;
chanind(3, end) = 0;
copyind = [];
switch S.mode
    case 'append'
        copyind = 1:D.nchannels;
        copyind = [copyind; copyind];
        chanind(3, :) = (D.nchannels+1):(D.nchannels+size(chanind, 2));
    case {'replace', 'replacemeg'}
        ind = 1;
        for i = 1:D.nchannels
            if any(i == planarind)
                k = find((chanind(1, :) == i) | (chanind(2, :) == i));
                if ~isempty(k) && (chanind(3, k) == 0)
                    chanind(3, k) = ind;
                    ind = ind + 1;
                end
            elseif isequal(S.mode, 'replace') || ~any(i == megind)
                copyind = [copyind [i ind]'];
                ind = ind + 1;
            end
        end
    case 'keep'
        copyind       = [];
        chanind(3, :) = 1:size(chanind, 2);
end

if isempty(copyind)
    Nchannels = max(chanind(3, :));
else
    Nchannels = max([chanind(3, :), copyind(2, :)]);
end

%-Generate new MEEG object with new files
%--------------------------------------------------------------------------
if isTF
    Dnew = clone(D, [S.prefix fname(D)], [Nchannels D.nfrequencies D.nsamples D.ntrials], 1);
else
    Dnew = clone(D, [S.prefix fname(D)], [Nchannels D.nsamples D.ntrials], 1);
end

if strcmp(D.type, 'continuous')
    %-Continuous data
    %----------------------------------------------------------------------
    blksz  = D.fsample;
    blknum = floor(D.nsamples/blksz);

    spm_progress_bar('Init', blknum, 'Data blocks processed');
    if blknum > 100, Ibar = floor(linspace(1, blknum,100));
    else Ibar = 1:blknum; end

    if isTF
        i = 1;
        while 1
            if i<=blknum
                Iblock  = ((i-1)*blksz + 1):(i*blksz);
            elseif (i == (blknum + 1)) && blknum*blksz < D.nsamples
                Iblock  = (blknum*blksz + 1):D.nsamples;
            else
                break;
            end

            planar1 = D(chanind(1, :), :, Iblock);
            planar2 = D(chanind(2, :), :, Iblock);

            Dnew(chanind(3, :), :, Iblock) = planar1 + planar2;

            if ~isempty(copyind)
                Dnew(copyind(2, :), :, Iblock) =  D(copyind(1, :), :, Iblock);
            end

            if any(Ibar == i), spm_progress_bar('Set', i); end

            i = i+1;
        end
    else
        i = 1;
        while 1
            if i<=blknum
                Iblock  = ((i-1)*blksz + 1):(i*blksz);
            elseif (i == (blknum + 1)) && blknum*blksz < D.nsamples
                Iblock  = (blknum*blksz + 1):D.nsamples;
            else
                break;
            end

            planar1 = D(chanind(1, :),  Iblock);
            planar2 = D(chanind(2, :),  Iblock);

            Dnew(chanind(3, :), Iblock) = sqrt(planar1.^2 + planar2.^2);

            if ~isempty(copyind)
                Dnew(copyind(2, :), Iblock) =  D(copyind(1, :), Iblock);
            end

            if any(Ibar == i), spm_progress_bar('Set', i); end

            i = i+1;
        end
    end
else
    %-Epoched data
    %----------------------------------------------------------------------
    spm_progress_bar('Init', D.ntrials, 'Trials processed');
    if D.ntrials > 100, Ibar = floor(linspace(1, D.ntrials, 100));
    else Ibar = 1:D.ntrials; end

    if isTF
        for i=1:D.ntrials
            planar1 = D(chanind(1, :), :, :, i);
            planar2 = D(chanind(2, :), :, :, i);

            Dnew(chanind(3, :),:, :, i) = planar1 + planar2;

            if ~isempty(copyind)
                Dnew(copyind(2, :), :, :, i) =  D(copyind(1, :), :, :, i);
            end

            if any(Ibar == i), spm_progress_bar('Set', i); end
        end
    else
        for i=1:D.ntrials
            planar1 = D(chanind(1, :), :, i);
            planar2 = D(chanind(2, :), :, i);

            Dnew(chanind(3, :), :, i) = sqrt(planar1.^2 + planar2.^2);

            if ~isempty(copyind)
                Dnew(copyind(2, :), :, i) =  D(copyind(1, :), :, i);
            end

            if any(Ibar == i), spm_progress_bar('Set', i); end
        end
    end
end

spm_progress_bar('Clear');

Dnew = chanlabels(Dnew, chanind(3, :), labelnew);
Dnew = badchannels(Dnew, chanind(3, :), badchannels(D, chanind(1, :)) | badchannels(D, chanind(2, :)));
Dnew = chantype(Dnew, chanind(3, :), 'MEGCOMB');
Dnew = units(Dnew, chanind(3, :), units(D, chanind(1, :)));
Dnew = coor2D(Dnew, chanind(3, :), 0.5*(coor2D(D, chanind(1,:))+coor2D(D, chanind(1,:))));

if ~isempty(copyind)
    Dnew = chanlabels(Dnew, copyind(2, :), D.chanlabels(copyind(1, :)));
    Dnew = badchannels(Dnew, copyind(2, :), badchannels(D, copyind(1, :)));
    Dnew = chantype(Dnew, copyind(2, :), D.chantype(copyind(1, :)));
    Dnew = units(Dnew, copyind(2, :), D.units(copyind(1, :)));
    Dnew = coor2D(Dnew, copyind(2, :),coor2D(D, copyind(1, :)));
end

%-Save the new M/EEG dataset
%--------------------------------------------------------------------------
Dnew = Dnew.history(mfilename, S);
save(Dnew);

D = Dnew;

%-Cleanup
%--------------------------------------------------------------------------
fprintf('%-40s: %30s\n','Completed',spm('time'));                       %-#
spm('FigName','MEG Combine planar: done'); spm('Pointer','Arrow');


2     % problem
5     % outflow boundary
7     % grid parameter
3     % discretisation
0.02  % viscosity parameter
1     % Picard/Newton/hybrid linearization
9     % number of Picard iterations
1.d-5 % nonlinear tolerance

%% Data file for backward facing step


function model = edgesTrainDL(dlPara, varargin )
% Train structured edge detector with deep contour features.
% The code is modified from Structured Edge Detection Toolbox

% get default parameters
dfs={'imWidth',32, 'gtWidth',16, 'nPos',5e5, 'nNeg',5e5, 'nImgs',inf, ...
  'nTrees',10, 'fracFtrs',0.25, 'minCount',1, 'minChild',8, ...
  'maxDepth',64, 'discretize','pca', 'nSamples',256, 'nClasses',2, ...
  'split','gini', 'nOrients',4, 'grdSmooth',0, 'chnSmooth',2, ...
  'simSmooth',8, 'normRad',4, 'shrink',2, 'nCells',5, 'rgbd',0, ...
  'stride',2, 'multiscale',0, 'sharpen',2, 'nTreesEval',4, ...
  'nThreads',4, 'nms',0, 'seed',1, 'useParfor',0, 'modelDir','models/', ...
  'modelFnm','model', 'bsdsDir','BSR/BSDS500/data/'};
opts = getPrmDflt(varargin,dfs,1);
if(nargin==0), model=opts; return; end
% opts.nBatch = dlPara.nBatch;
% opts.nOutputNum = dlPara.nOutputNum;
opts.patch_mean = dlPara.patch_mean;
opts.modelDir = dlPara.modelDir;
opts.selected_dims = dlPara.selected_dims;
% if forest exists load it and return
cd(fileparts(mfilename('fullpath')));
forestDir = [opts.modelDir '/forest/'];
forestFn = [forestDir opts.modelFnm];
if(exist([forestFn '.mat'], 'file'))
  load([forestFn '.mat']); return; end
if(~exist(forestDir,'dir')), mkdir(forestDir); end
% compute constants and store in opts
nTrees=opts.nTrees; nCells=opts.nCells; shrink=opts.shrink;
opts.nPos=round(opts.nPos); opts.nNeg=round(opts.nNeg);
opts.nTreesEval=min(opts.nTreesEval,nTrees);
opts.stride=max(opts.stride,shrink);
imWidth=opts.imWidth; gtWidth=opts.gtWidth;
% imWidth=round(max(gtWidth,imWidth)/shrink/2)*shrink*2;
% opts.imWidth=imWidth; opts.gtWidth=gtWidth;
% nChnsGrad=(opts.nOrients+1)*2; nChnsColor=3;
% if(opts.rgbd==1), nChnsColor=1; end
% if(opts.rgbd==2), nChnsGrad=nChnsGrad*2; nChnsColor=nChnsColor+1; end
% nChns = nChnsGrad+nChnsColor;

% opts.nChnFtrs = imWidth*imWidth*nChns/shrink/shrink;
% opts.nSimFtrs = (nCells*nCells)*(nCells*nCells-1)/2*nChns;
nChns = length(opts.selected_dims);
if(nChns == 0)
    I = zeros([1 1 3], 'single');
    deepFeat = edgesChnsDL2(I, opts);
    nChns = size(deepFeat, 3);
    opts.selected_dims = [1:nChns]';
end
opts.nChns = nChns;
% opts.nTotFtrs = imWidth*imWidth*nChns/shrink/shrink; disp(opts);
opts.nTotFtrs = round(imWidth/shrink)*round(imWidth/shrink)*nChns; disp(opts);
% generate stream for reproducibility of model
stream=RandStream('mrg32k3a','Seed',opts.seed);

% train nTrees random trees (can be trained with parfor if enough memory)
if(opts.useParfor),
    parfor i=1:nTrees,
        trainTree(opts,stream,i);
    end
else
    for i=1:nTrees,
        trainTree(opts,stream,i);
    end;
end

% merge trees and save model
model = mergeTrees( opts );
if(~exist(forestDir,'dir')), mkdir(forestDir); end
save([forestFn '.mat'], 'model', '-v7.3');

end

function model = mergeTrees( opts )
% accumulate trees and merge into final model
nTrees=opts.nTrees; gtWidth=opts.gtWidth;
treeFn = [opts.modelDir '/tree/' opts.modelFnm '_tree'];
for i=1:nTrees
  t=load([treeFn int2str2(i,3) '.mat'],'tree'); t=t.tree;
  if(i==1), trees=t(ones(1,nTrees)); else trees(i)=t; end
end
nNodes=0; for i=1:nTrees, nNodes=max(nNodes,size(trees(i).fids,1)); end
% merge all fields of all trees
model.opts=opts; Z=zeros(nNodes,nTrees,'uint32');
model.thrs=zeros(nNodes,nTrees,'single');
model.fids=Z; model.child=Z; model.count=Z; model.depth=Z;
model.segs=zeros(gtWidth,gtWidth,nNodes,nTrees,'uint8');
for i=1:nTrees, tree=trees(i); nNodes1=size(tree.fids,1);
  model.fids(1:nNodes1,i) = tree.fids;
  model.thrs(1:nNodes1,i) = tree.thrs;
  model.child(1:nNodes1,i) = tree.child;
  model.count(1:nNodes1,i) = tree.count;
  model.depth(1:nNodes1,i) = tree.depth;
  model.segs(:,:,1:nNodes1,i) = tree.hs-1;
end
% remove very small segments (<=5 pixels)
segs=model.segs; nSegs=squeeze(max(max(segs)))+1;
parfor i=1:nTrees*nNodes, m=nSegs(i);
  if(m==1), continue; end; S=segs(:,:,i); del=0;
  for j=1:m, Sj=(S==j-1); if(nnz(Sj)>5), continue; end
    S(Sj)=median(single(S(convTri(single(Sj),1)>0))); del=1; end
  if(del), [~,~,S]=unique(S); S=reshape(S-1,gtWidth,gtWidth);
    segs(:,:,i)=S; nSegs(i)=max(S(:))+1; end
end
model.segs=segs; model.nSegs=nSegs;
% store compact representations of sparse binary edge patches
nBnds=opts.sharpen+1; eBins=cell(nTrees*nNodes,nBnds);
eBnds=zeros(nNodes*nTrees,nBnds);
parfor i=1:nTrees*nNodes
  if(model.child(i) || model.nSegs(i)==1), continue; end %#ok<PFBNS>
  E=gradientMag(single(model.segs(:,:,i)))>.01; E0=0;
  for j=1:nBnds, eBins{i,j}=uint16(find(E & ~E0)'-1); E0=E;
    eBnds(i,j)=length(eBins{i,j}); E=convTri(single(E),1)>.01; end
end
eBins=eBins'; model.eBins=[eBins{:}]';
eBnds=eBnds'; model.eBnds=uint32([0; cumsum(eBnds(:))]);
end

function trainTree( opts, stream, treeInd )
% Train a single tree in forest model.

% location of ground truth
trnImgDir = [opts.bsdsDir '/images/train/'];
trnDepDir = [opts.bsdsDir '/depth/train/'];
trnGtDir = [opts.bsdsDir '/groundTruth/train/'];
trnFeatFir = [opts.modelDir '/feat/'];
if(~exist(trnFeatFir,'dir')), mkdir(trnFeatFir); end
imgIds=dir(trnImgDir); imgIds=imgIds([imgIds.bytes]>0);
imgIds={imgIds.name}; ext=imgIds{1}(end-2:end);
nImgs=length(imgIds); for i=1:nImgs, imgIds{i}=imgIds{i}(1:end-4); end

% extract commonly used options
imWidth=opts.imWidth; imRadius=floor(imWidth/2);
gtWidth=opts.gtWidth; gtRadius=gtWidth/2;
nChns=opts.nChns;
nTotFtrs=opts.nTotFtrs;
selected_dims = opts.selected_dims;
rgbd=opts.rgbd;
nPos=opts.nPos; nNeg=opts.nNeg; shrink=opts.shrink;

% finalize setup
treeDir = [opts.modelDir '/tree/'];
treeFn = [treeDir opts.modelFnm '_tree'];
if(exist([treeFn int2str2(treeInd,3) '.mat'],'file'))
  fprintf('Reusing tree %d of %d\n',treeInd,opts.nTrees); return; end
fprintf('\n-------------------------------------------\n');
fprintf('Training tree %d of %d\n',treeInd,opts.nTrees); tStart=clock;

% set global stream to stream with given substream (will undo at end)
streamOrig = RandStream.getGlobalStream();
set(stream,'Substream',treeInd);
RandStream.setGlobalStream( stream );

% collect positive and negative patches and compute features
fids=sort(randperm(nTotFtrs,round(nTotFtrs*opts.fracFtrs)));
k = nPos+nNeg; nImgs=min(nImgs,opts.nImgs);
ftrs = zeros(k,length(fids),'single');
labels = zeros(gtWidth,gtWidth,k,'uint8'); k = 0;
tid = ticStatus('Collecting data',30,1);
for i = 1:nImgs
  % get image and compute channels
  gt=load([trnGtDir imgIds{i} '.mat']); gt=gt.groundTruth;
  I=imread([trnImgDir imgIds{i} '.' ext]); siz=size(I);
  if(rgbd), D=single(imread([trnDepDir imgIds{i} '.png']))/1e4; end
  if(rgbd==1), I=D; elseif(rgbd==2), I=cat(3,single(I)/255,D); end
  p=zeros(1,4); p([2 4])=mod(4-mod(siz(1:2),4),4);
  if(any(p)), I=imPad(I,p,'symmetric'); end
%   [chnsReg,chnsSim] = edgesChns(I,opts);
%   deepFeat = chnsReg(:, :, 4);
  tic;
if exist([trnFeatFir imgIds{i} '_deepFeat.mat'], 'file');
    load([trnFeatFir imgIds{i} '_deepFeat.mat']);
else
  deepFeat = edgesChnsDL2(I, opts);
  save([trnFeatFir imgIds{i} '_deepFeat.mat'], 'deepFeat');
end
  deepFeat = deepFeat(:, :, selected_dims);
  toc;
  % sample positive and negative locations
  nGt=length(gt); xy=[]; k1=0; B=false(siz(1),siz(2));
  B(shrink:shrink:end,shrink:shrink:end)=1;
  B([1:imRadius end-imRadius:end],:)=0;
  B(:,[1:imRadius end-imRadius:end])=0;
  for j=1:nGt
    M=gt{j}.Boundaries; M(bwdist(M)<gtRadius)=1;
    [y,x]=find(M.*B); k2=min(length(y),ceil(nPos/nImgs/nGt));
    rp=randperm(length(y),k2); y=y(rp); x=x(rp);
    xy=[xy; x y ones(k2,1)*j]; k1=k1+k2; %#ok<AGROW>
    [y,x]=find(~M.*B); k2=min(length(y),ceil(nNeg/nImgs/nGt));
    rp=randperm(length(y),k2); y=y(rp); x=x(rp);
    xy=[xy; x y ones(k2,1)*j]; k1=k1+k2; %#ok<AGROW>
  end
  if(k1>size(ftrs,1)-k), k1=size(ftrs,1)-k; xy=xy(1:k1,:); end
  % crop patches and ground truth labels
  psReg=zeros(round(imWidth/shrink),round(imWidth/shrink),nChns,k1,'single');
  lbls=zeros(gtWidth,gtWidth,k1,'uint8');
%   psSim=psReg;
    ri=round(imRadius/shrink);
    rg=gtRadius;
%     ftrs1 = zeros(k1, nTotFtrs);
  for j=1:k1, xy1=xy(j,:); xy2=xy1/shrink;
    psReg(:,:,:,j)=deepFeat(round(xy2(2))-ri+1:round(xy2(2))+ri,round(xy2(1))-ri+1:round(xy2(1))+ri,:);
%     psSim(:,:,:,j)=chnsSim(xy2(2)-ri+1:xy2(2)+ri,xy2(1)-ri+1:xy2(1)+ri,:);
%     ftrs1(j, :) = deepFeat(round(xy2(2)), round(xy2(1)), :);
    t=gt{xy1(3)}.Segmentation(xy1(2)-rg+1:xy1(2)+rg,xy1(1)-rg+1:xy1(1)+rg);
    if(all(t(:)==t(1))), lbls(:,:,j)=1; else [~,~,t]=unique(t);
      lbls(:,:,j)=reshape(t,gtWidth,gtWidth); end
  end
%   if(0), figure(1); montage2(squeeze(psReg(:,:,1,:))); drawnow; end
%   if(0), figure(2); montage2(lbls(:,:,:)); drawnow; end
  % compute features and store
    ftrs1=reshape(psReg,[],k1)';
%   ftrs1=[reshape(psReg,[],k1)' stComputeSimFtrs(psSim,opts)];
%   ftrs1=reshape(ftrs1,[],k1)';

  ftrs(k+1:k+k1,:)=ftrs1(:,fids);
  labels(:,:,k+1:k+k1)=lbls;
  k=k+k1;
  if(k==size(ftrs,1)), tocStatus(tid,1); break; end
  tocStatus(tid,i/nImgs);
end
if(k<size(ftrs,1)), ftrs=ftrs(1:k,:); labels=labels(:,:,1:k); end

% train structured edge classifier (random decision tree)
pTree=struct('minCount',opts.minCount, 'minChild',opts.minChild, ...
  'maxDepth',opts.maxDepth, 'H',opts.nClasses, 'split',opts.split);
t=labels; labels=cell(k,1); for i=1:k, labels{i}=t(:,:,i); end
pTree.discretize=@(hs,H) discretize(hs,H,opts.nSamples,opts.discretize);
tree=forestTrain(ftrs,labels,pTree); tree.hs=cell2array(tree.hs);
tree.fids(tree.child>0) = fids(tree.fids(tree.child>0)+1)-1;
if(~exist(treeDir,'dir')), mkdir(treeDir); end
save([treeFn int2str2(treeInd,3) '.mat'],'tree'); e=etime(clock,tStart);
fprintf('Training of tree %d complete (time=%.1fs).\n',treeInd,e);
RandStream.setGlobalStream( streamOrig );

end

function ftrs = stComputeSimFtrs( chns, opts )
% Compute self-similarity features (order must be compatible w mex file).
w=opts.imWidth/opts.shrink; n=opts.nCells; if(n==0), ftrs=[]; return; end
nSimFtrs=opts.nSimFtrs; nChns=opts.nChns; m=size(chns,4);
inds=round(w/n/2); inds=round((1:n)*(w+2*inds-1)/(n+1)-inds+1);
chns=reshape(chns(inds,inds,:,:),n*n,nChns,m);
ftrs=zeros(nSimFtrs/nChns,nChns,m,'single');
k=0; for i=1:n*n-1, k1=n*n-i; i1=ones(1,k1)*i;
  ftrs(k+1:k+k1,:,:)=chns(i1,:,:)-chns((1:k1)+i,:,:); k=k+k1; end
ftrs = reshape(ftrs,nSimFtrs,m)';
end

function [hs,segs] = discretize( segs, nClasses, nSamples, type )
% Convert a set of segmentations into a set of labels in [1,nClasses].
persistent cache; w=size(segs{1},1); assert(size(segs{1},2)==w);
if(~isempty(cache) && cache{1}==w), [~,is1,is2]=deal(cache{:}); else
  % compute all possible lookup inds for w x w patches
  is=1:w^4; is1=floor((is-1)/w/w); is2=is-is1*w*w; is1=is1+1;
  kp=is2>is1; is1=is1(kp); is2=is2(kp); cache={w,is1,is2};
end
% compute n binary codes zs of length nSamples
nSamples=min(nSamples,length(is1)); kp=randperm(length(is1),nSamples);
n=length(segs); is1=is1(kp); is2=is2(kp); zs=false(n,nSamples);
for i=1:n, zs(i,:)=segs{i}(is1)==segs{i}(is2); end
zs=bsxfun(@minus,zs,sum(zs,1)/n); zs=zs(:,any(zs,1));
if(isempty(zs)), hs=ones(n,1,'uint32'); segs=segs{1}; return; end
% find most representative segs (closest to mean)
[~,ind]=min(sum(zs.*zs,2)); segs=segs{ind};
% apply PCA to reduce dimensionality of zs
U=pca(zs'); d=min(5,size(U,2)); zs=zs*U(:,1:d);
% discretize zs by clustering or discretizing pca dimensions
d=min(d,floor(log2(nClasses))); hs=zeros(n,1);
for i=1:d, hs=hs+(zs(:,i)<0)*2^(i-1); end
[~,~,hs]=unique(hs); hs=uint32(hs);
if(strcmpi(type,'kmeans'))
  nClasses1=max(hs); C=zs(1:nClasses1,:);
  for i=1:nClasses1, C(i,:)=mean(zs(hs==i,:),1); end
  hs=uint32(kmeans2(zs,nClasses,'C0',C,'nIter',1));
end
% optionally display different types of hs
for i=1:0, figure(i); montage2(cell2array(segs(hs==i))); end
end


function varargout = process_aec2( varargin )
% PROCESS_AEC2: Compute amplitude envelope correlation between one signal in one file, and all the signals in another file.

% @=============================================================================
% 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-2014
%          Peter Donhauser, 2017

eval(macro_method);
end

%% ===== GET DESCRIPTION =====
function sProcess = GetDescription() %#ok<DEFNU>
    % Description the process
    sProcess.Comment     = 'Amplitude Envelope Correlation AxB';
    sProcess.Category    = 'Custom';
    sProcess.SubGroup    = 'Connectivity';
    sProcess.Index       = 657;
    sProcess.Description = 'https://neuroimage.usc.edu/brainstorm/Tutorials/Connectivity';
    % Definition of the input accepted by this process
    sProcess.InputTypes  = {'data', 'results', 'matrix'};
    sProcess.OutputTypes = {'timefreq', 'timefreq', 'timefreq'};
    sProcess.nInputs     = 2;
    sProcess.nMinFiles   = 1;
    sProcess.isPaired    = 1;

    % === CONNECT INPUT
    sProcess = process_corr2('DefineConnectOptions', sProcess);
    % === FREQ BANDS
    sProcess.options.freqbands.Comment = 'Frequency bands for the Hilbert transform:';
    sProcess.options.freqbands.Type    = 'groupbands';
    sProcess.options.freqbands.Value   = bst_get('DefaultFreqBands');
    % === Orthogonalize pairs of signals
    sProcess.options.isorth.Comment = 'Orthogonalize signal pairs before envelope computation';
    sProcess.options.isorth.Type    = 'checkbox';
    sProcess.options.isorth.Value   = 0;
    % === OUTPUT MODE
    sProcess.options.outputmode.Comment = {'Save individual results (one file per input file)', 'Concatenate input files before processing (one file)', 'Save average connectivity matrix (one file)'};
    sProcess.options.outputmode.Type    = 'radio';
    sProcess.options.outputmode.Value   = 1;
    sProcess.options.outputmode.Group   = 'output';
end

%% ===== FORMAT COMMENT =====
function Comment = FormatComment(sProcess) %#ok<DEFNU>
    Comment = sProcess.Comment;
end

%% ===== RUN =====
function OutputFiles = Run(sProcess, sInputA, sInputB) %#ok<DEFNU>
    % Input options
    OPTIONS = process_corr2('GetConnectOptions', sProcess, sInputA, sInputB);
    if isempty(OPTIONS)
        OutputFiles = {};
        return
    end

    OPTIONS.Method = 'aec';
    % Hilbert and frequency bands options
    OPTIONS.Freqs = sProcess.options.freqbands.Value;
    OPTIONS.isOrth = sProcess.options.isorth.Value;

    % Compute metric
    OutputFiles = bst_connectivity({sInputA.FileName}, {sInputB.FileName}, OPTIONS);
end