function chunk = onl_peek(streamname,samples_to_get,unit,channels_to_get)
% Peek into an online stream (generates an EEG-set like view into it).
% Chunk = onl_peek(StreamName,DesiredLength,LengthUnit,DesiredChannels)
%
% This function returns an EEGLAB dataset struct that hold the last k seconds of an online stream.
% An online stream is a data structure in the workspace that can be created with onl_newstream;
% data can be appended to it by onl_append or onl_read_background.
%
% In:
%   StreamName : Name of the online stream data structure in the MATLAB workspace to read from.
%                Must have previously been created with onl_newstream.
%
%   DesiredLength : length of the view that should be generated; should not be longer than the
%                   buffer capacity (default: 10)
%
%   LengthUnit : Can be one of the following options:
%                 * 'seconds': return the last PeekLength seconds (default)
%                 * 'samples': return the last PeekLength samples
%                 * 'index': return all samples newer than PeekLength (as a sample index)
%
%   DesiredChannels : range of channels to return ([] = all channels); (default: [])
%
% Out:
%   Chunk : An EEGLAB data set that contains the most recent data of a given stream
%
% Example:
%   % get the last 5 seconds of the stream
%   EEG = onl_peek('mystream',5)
%
%   % get the last 128 samples of the stream
%   EEG = onl_peek('mystream',128,'samples')
%
%   % get all samples past the 5000'th sample in the stream
%   EEG = onl_peek('mystream',5000,'index')
%
%   % get the default amount of data, for channels 1:10
%   EEG = onl_peek('mystream',[],[],1:10)
%
% See also:
%   onl_newstream, onl_append, onl_filtered
%
%                                Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
%                                2010-04-03

% try to get the stream from the base workspace
try
    chunk = evalin('base',streamname);
catch e
    % diagnose the error
    if nargin < 1
        error('You need to pass at least the name of a previously created stream.'); end
    if ~ischar(streamname) || isempty(streamname) || ~isvarname(streamname)
        error('The given StreamName argument must be the name of a variable name in the MATLAB workspace, but was: %s',hlp_tostring(streamname,10000)); end
    if strcmp(e.identifier,'MATLAB:badsubscript')
        error('BCILAB:onl_peek:improper_resolve','The raw data required by the predictor does not list the name of the needed source stream; this is likely a problem in onl_newstream/onl_newpredictor.');
    else
        error('BCILAB:onl_peek:stream_not_found','The stream named %s was not found in the base workspace.',hlp_tostring(streamname));
    end
end

try
    % set further default arguments
    if nargin < 4 || isempty(channels_to_get)
        channels_to_get = 1:size(chunk.buffer,1);
        if nargin < 3 || isempty(unit)
            unit = 'seconds'; end
        if nargin < 2 || isempty(samples_to_get)
            samples_to_get = 10; end
    end

    % determine the amount of data needed
    switch unit
        case 'index'
            samples_to_get = min(chunk.buffer_len,chunk.smax-samples_to_get);
        case 'samples'
            samples_to_get = min(chunk.buffer_len,samples_to_get);
        case 'seconds'
            samples_to_get = min(chunk.buffer_len,round(chunk.srate*samples_to_get));
        otherwise
            error('Unrecognized LengthUnit specified: %s',hlp_tostring(unit));
    end

    % extract the desired interval from the .buffer field and move it to .data
    range = 1+mod(chunk.smax-samples_to_get:chunk.smax-1,chunk.buffer_len);
    chunk.data = chunk.buffer(channels_to_get,range);

    % extract the markers, if there are any
    if chunk.mmax
        [ranks,sample_indices,record_indices] = find(chunk.marker_pos(:,range));
        if any(ranks)
            chunk.event = chunk.marker_buffer(1+mod(record_indices-1,chunk.marker_buffer_len));
            [chunk.event.latency] = arraydeal(sample_indices(:) + [chunk.event.latency]');
        end
    end

    % update meta-data
    [chunk.nbchan,chunk.pnts,chunk.trials] = size(chunk.data);
    chunk.chanlocs = chunk.chanlocs(channels_to_get);
    if chunk.xmin == 0
        chunk.xmax = (chunk.smax-1)/chunk.srate;
        chunk.xmin = chunk.xmax - (chunk.pnts-1)/chunk.srate;
    else
        chunk.xmax = chunk.xmin + (chunk.pnts-1)/chunk.srate;
    end

catch e
    if ~ischar(unit) || isempty(unit)
        error('The given Unit argument must be a string, but was: %s',hlp_tostring(unit,10000)); end
    if ~isnumeric(samples_to_get) || ~isscalar(samples_to_get)
        error('The given DesiredLength argument must be a numeric scalar, but was: %s',hlp_tostring(samples_to_get,10000)); end
    if ~isnumeric(channels_to_get) || min(size(channels_to_get) ~= 1)
        error('The given DesiredChannels argument must be a numeric vector, but was: %s',hlp_tostring(channels_to_get,10000)); end
    error('Failed to read from stream %s with error: %s\nPossibly the stream variable has been overwritten or you have the wrong name.',streamname,e.message);
end


function [GAmp,GTime]=GxUser(p)

global VVar;

load(p.GxFile); % load Gx file, Gx file needs to contain GAmp, GTime
Duplicates=max(1,p.Duplicates);
DupSpacing=max(0,p.DupSpacing);

GAmp=GAmp(min(end,VVar.TRCount),:);
GTime=GTime(min(end,VVar.TRCount),:);

GAmp(1)=0;
GAmp(end)=0;

% Create Duplicates
if Duplicates~=1 & DupSpacing ~=0
    GAmp=repmat(GAmp,[1 Duplicates]);
    TimeOffset = repmat(0:DupSpacing:(Duplicates-1)*DupSpacing,[length(GTime) 1]);
    GTime=repmat(GTime,[1 Duplicates]) + (TimeOffset(:))';
end

end


%% HEATED_PLATE_FSU runs HEATED_PLATE_FUN using fsuClusterMatlab.
%
%  Discussion:
%
%    The arguments to fsuClusterMatlab mean:
%
%    [] allows us to specify an output directory;
%    [] allows us to specify switches to the MOAB queue handler, such as more time.
%    'p" indicates this is a "parfor" job;
%    'w' indicates that the MATLAB session should wait for completion;
%    4 is the number of processors requested;
%    @heated_plate_fun indicates the function we want to execute;
%    { tol, filename } contains input arguments.
%
%    The function "heated_plate_fun" must correspond to a MATLAB M-file
%    "heated_plate_fun.m" and that file must be in MATLAB's path.
%
%    The easiest way to ensure that the file is in MATLAB's path is to
%    create a subdirectory called "matlab" immediately below your home directory,
%    and place the function file there (as well as any other files needed by
%    the job).
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    25 December 2010
%
%  Author:
%
%    John Burkardt
%
  tol = 0.001;
  filename = 'solution.txt';

  results = fsuClusterMatlab ( [], [], 'p', 'w', 4, @heated_plate_fun, { tol, filename } )


function param = quenchInitParams(varargin)
%
% param = quenchInitParams(varargin)
%
% Initializes the params structure which gets attached to a quench fg. It
% contains information that Quench uses for visualization and for dynamic
% queries. Pulled out of dtiCreateQuenchStats.m
%
% PARAMS STRUCTURE:
%       name    -  Name for a fiber summary statistic. Typically this
%                  will be  something like length or "average FA". THis
%                  statistic is used to limit the range of fibers we see in
%                  the Quench window.
%       uid     -  Unique ID [=1] Subtract 1 so Quench starts from 0 (C-counting)
%       ile     -  Is luminance encoding (used by Quench)
%       icpp    -  Is computed per point [default=1]
%       ivs     -  Is viewable stat (used by Quench)
%       agg     -  Name for fiber summary aggregate statistic (defaults to
%                  param.name
%       lname   -  Local name name for the per point statistic (e.g., TF,
%                  FA, eccentricity)
%       stat    -  A (1 x nFibers) array of statistical values
%
% WEB:
%       mrvBrowseSVN('quenchInitParams');
%
% EXAMPLE USAGE:
%       param = quenchInitParams('name','averageFA');
%
% See also:
%       dtiCreateQuenchStats.m , addStatisticHeader.m
%

%% This statistics parameter field gets attached to the fg.
% It contains information that Quench uses for visualization and for
% dynamic queries. We should probably write a function for initializing
% this parameter field. (see also addStatisticHeader.m)

param       = struct;       % Initialize structure
param.name  = 'nameStat';   % Stat name
param.uid   = 1;            % Subtract 1 so Quench starts from 0 (C-counting)
param.ile   = 1;            % Is luminace encoding
param.icpp  = 1;            % Visualization per point, not per fiber
param.ivs   = 1;            % is_viewable_stat
param.agg   = param.name;   % Stat name
param.lname = 'localStat';  % Local Name
param.stat  = [];

param = mrVarargin( param, varargin );

% If the users only passes in 'name' then set param.agg = param.name since
% they are essentially the same thing. If the user passes in both, or just
% param.agg then leave it be.
if ~isempty(strmatch('name',varargin)) && isempty(strmatch('agg',varargin))
    param.agg = param.name;
end

return


function [overlaps, score, class_ids, avg_cube, avg_cube_per_class] = detector_wiggle_from_iou_score(filename)
  ld = load(filename, 'ious', 'scores', 'classes');

  avg_cube = [];
  avg_cube_per_class = [];
  score = ld.scores;
  overlaps = ld.ious;
  class_ids = ld.classes;
end


function [fg,filename] = mtrImportFibers(filename,xform,bEndOnly, coordspace)
%
% [fg,filename] = mtrImportFibers(filename, [xform], [bEndOnly], [coordspace])
%
% Imports non-mrDiffusion fiber/path file formats. Currently supports:
%
% 1. PDB (pathway data base)
% 2. Bfloat (ConTrack/Camino)
% 3. tck (MRtrix)
%
% Parameters
% ----------
% filename: Full path to the file containing the fiber group. Currently
% supports:
%   1. PDB (pathway data base)
%   2. Bfloat (ConTrack/Camino)
%   3. MRtrix (MRtrix fibers)
% xform: A transformation matrix to apply to the coordinates of all the
% fibers in the fiber group, optional.
% bEndOnly: For Bfloat files, whether to read in only the end points,
% optional, default to read the full fibers.
% coordspace: string {'acpc' | 'img' | ...}, the coordinate space in which
% the fiber coordinates are defined. optional. Defaults to an empty matrix
%
% 2011.07.21 - LMP changed the call for Bfloat file types to
% dtiLoadBfloatPaths
%
% 2009 Stanford, Vista

if notDefined('bEndOnly'), bEndOnly = 0; end
if notDefined('xform'),    xform = eye(4); end

%persistent defaultPath;
defaultPath = pwd;

if (~exist('filename','var') || isempty(filename))
    if(isempty(defaultPath)), filename = pwd;
    else                      filename = defaultPath;
    end
end

if (~exist('xform','var') || isempty(xform)), xform = eye(4); end

% Read the file name from the user
if(isdir(filename)) || ~exist(filename,'file')
    if(~isempty(defaultPath)), filename = defaultPath; end
    [f,p] = uigetfile({'*.pdb','ConTrack PDB *.pdb';'*.Bfloat','ConTrack/Camino *.Bfloat';'*.*','all files'},'Select fiber file...',filename);
    if(isnumeric(f)), disp('Cancel.'); return; end
    % defaultPath = p;
    filename = fullfile(p,f);
end

%
[p,f,e] = fileparts(filename); %#ok<ASGLU>
if(isempty(e)), e = '.pdb'; end

fg = [];
switch(e)
    case '.pdb',
        %disp('Trying to import PDB paths...');

        % Read the whole file into a string, str.  The str is decoded in
        % the function below.
        fid = fopen(filename, 'r');
        str = fread(fid, inf,'uint8');
        fclose(fid);

        % MetroTrack is the old name.  It reads the pdb file.
        mt = dtiLoadMetrotracPathsFromStr(str,xform);

        % Copy the loaded data in the mrDiffusion format
        if ~isempty(mt)
            fg = dtiNewFiberGroup;
            fg.fibers = mt.pathways;
            fg.name = f;
            fg.colorRgb = [200 200 100];
            fg.pathwayInfo = mt.pathwayInfo;
            for ss = 1:length(mt.statHeader)
                statstruct.name = mt.statHeader(ss).agg_name;
                statstruct.uid=mt.statHeader(ss).uid;
                statstruct.ile=mt.statHeader(ss).is_luminance_encoding;
                statstruct.icpp=mt.statHeader(ss).is_computed_per_point;
                statstruct.ivs=mt.statHeader(ss).is_viewable_stat;
                statstruct.agg=mt.statHeader(ss).agg_name;
                statstruct.lname=mt.statHeader(ss).local_name;
                for pp = 1:length(mt.pathwayInfo)
                    statstruct.stat(pp) = mt.pathwayInfo(pp).pathStat(ss);
                end
                fg.params{ss} =  statstruct;
            end
        end

    case '.Bfloat',
        mt = dtiLoadBfloatPaths(filename,xform,bEndOnly);
        if ~isempty(mt)
            fg = fgCreate;
            fg.fibers = mt.pathways;
            fg.name = f;
            fg.colorRgb = [200 200 100];
            for ss = 1:length(mt.statHeader)
                statstruct.name = mt.statHeader(ss).agg_name;
                for pp = 1:length(mt.pathwayInfo)
                    statstruct.stat(pp) = mt.pathwayInfo(pp).pathStat(ss);
                end
                fg.params{ss} =  statstruct;
            end
        end

    case '.tck'
        fg = dtiImportFibersMrtrix(filename);

    otherwise,
        error('unknown format: %s\n',e);
end

% These are always going to be in acpc space, so there we go:
if ~notDefined('coordspace')
    fgSet(fg, 'coordspace', coordspace);
else
    fgSet(fg, 'coordspace', []);
end

fprintf('Fibers loaded from file %s\n',f);

return;


%% interp_polar
% Below is a demonstration of the features of the |interp_polar| function

%%
clear; close all; clc;

%% Syntax
% |[Ri]=interp_polar(TZ,radiusData,TZi,interp_method);|

%% Description
% UNDOCUMENTED
%% Examples
%
%%
%
% <<gibbVerySmall.gif>>
%
% _*GIBBON*_
% <www.gibboncode.org>
%
% _Kevin Mattheus Moerman_, <gibbon.toolbox@gmail.com>
%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.


function [ report ] = codeReport( varargin )
%CODEREPORT Builds a report of the code health
%
% This function generates a Markdown report on the code health. At the moment
% this is limited to the McCabe (cyclomatic) complexity of a function and its
% subfunctions.
%
% This makes use of |checkcode| in MATLAB.
%
% Usage:
%
%   CODEREPORT('function', functionName) to determine which function is
%   analyzed. (default: matlab2tikz)
%
%   CODEREPORT('complexityThreshold', integer ) to set above which complexity, a
%   function is added to the report (default: 10)
%
%   CODEREPORT('stream', stream) to set to which stream/file to output the report
%   (default: 1, i.e. stdout). The stream is used only when no output argument
%   for `codeReport` is specified!.
%
% See also: checkcode, mlint

    SM = StreamMaker();
    %% input options
    ipp = m2tInputParser();
    ipp = ipp.addParamValue(ipp, 'function', 'matlab2tikz', @ischar);
    ipp = ipp.addParamValue(ipp, 'complexityThreshold', 10, @isnumeric);
    ipp = ipp.addParamValue(ipp, 'stream', 1, SM.isStream);
    ipp = ipp.parse(ipp, varargin{:});

    stream = SM.make(ipp.Results.stream, 'w');

    %% generate report data
    data = checkcode(ipp.Results.function,'-cyc','-struct');
    [complexityAll, mlintMessages] = splitCycloComplexity(data);

    %% analyze cyclomatic complexity
    categorizeComplexity = @(x) categoryOfComplexity(x, ...
                                 ipp.Results.complexityThreshold, ...
                                 ipp.Results.function);

    complexityAll = arrayfun(@parseCycloComplexity, complexityAll);
    complexityAll = arrayfun(categorizeComplexity, complexityAll);

    complexity = filter(complexityAll, @(x) strcmpi(x.category, 'Bad'));
    complexity = sortBy(complexity, 'line', 'ascend');
    complexity = sortBy(complexity, 'complexity', 'descend');

    [complexityStats] = complexityStatistics(complexityAll);

    %% analyze other messages
    %TODO: handle all mlint messages and/or other metrics of the code

    %% format report
    dataStr = complexity;
    dataStr = arrayfun(@(d) mapField(d, 'function',  @markdownInlineCode), dataStr);
    if ~isempty(dataStr)
        dataStr = addFooterRow(dataStr, 'complexity', @sum, {'line',0, 'function',bold('Total')});
    end
    dataStr = arrayfun(@(d) mapField(d, 'line',         @integerToString), dataStr);
    dataStr = arrayfun(@(d) mapField(d, 'complexity',   @integerToString), dataStr);

    report = makeTable(dataStr, {'function', 'complexity'}, ...
                                {'Function', 'Complexity'});

    %% command line usage
    if nargout == 0
        if ismember(stream.name, {'stdout','stderr'})
            stream.print('%s\n', codelinks(report, ipp.Results.function));
        else
            stream.print('%s\n', report);
        end

        figure('name',sprintf('Complexity statistics of %s', ipp.Results.function));
        h = statisticsPlot(complexityStats, 'Complexity', 'Number of functions');
        for hh = h
            plot(hh, [1 1]*ipp.Results.complexityThreshold, ylim(hh), ...
                 'k--','DisplayName','Threshold');
        end
        legend(h(1),'show','Location','NorthEast');

        clear report
    end

end
%% CATEGORIZATION ==============================================================
function [complexity, others] = splitCycloComplexity(list)
% splits codereport into McCabe complexity and others
    filter = @(l) ~isempty(strfind(l.message, 'McCabe complexity'));
    idxComplexity = arrayfun(filter, list);
    complexity = list( idxComplexity);
    others     = list(~idxComplexity);
end
function [data] = categoryOfComplexity(data, threshold, mainFunc)
% categorizes the complexity as "Good", "Bad" or "Accepted"
  TOKEN = '#COMPLEX'; % token to signal allowed complexity

  try %#ok
    helpStr = help(sprintf('%s>%s', mainFunc, data.function));
    if ~isempty(strfind(helpStr, TOKEN))
        data.category = 'Accepted';
        return;
    end
  end
  if data.complexity > threshold
      data.category = 'Bad';
  else
      data.category = 'Good';
  end
end

%% PARSING =====================================================================
function [out] = parseCycloComplexity(in)
% converts McCabe complexity report strings into a better format
    out = regexp(in.message, ...
                 'The McCabe complexity of ''(?<function>[A-Za-z0-9_]+)'' is (?<complexity>[0-9]+).', ...
                 'names');
    out.complexity = str2double(out.complexity);
    out.line = in.line;
end

%% DATA PROCESSING =============================================================
function selected = filter(list, filterFunc)
% filters an array according to a binary function
    idx = logical(arrayfun(filterFunc, list));
    selected = list(idx);
end
function [data] = mapField(data, field, mapping)
    data.(field) = mapping(data.(field));
end
function sorted = sortBy(list, fieldName, mode)
% sorts a struct array by a single field
% extra arguments are as for |sort|
    values = arrayfun(@(m)m.(fieldName), list);
    [dummy, idxSorted] = sort(values(:), 1, mode); %#ok
    sorted = list(idxSorted);
end

function [stat] = complexityStatistics(list)
% calculate some basic statistics of the complexities

    stat.values     = arrayfun(@(c)(c.complexity), list);
    stat.binCenter  = sort(unique(stat.values));

    categoryPerElem = {list.category};
    stat.categories = unique(categoryPerElem);
    nCategories = numel(stat.categories);

    groupedHist = zeros(numel(stat.binCenter), nCategories);
    for iCat = 1:nCategories
        category = stat.categories{iCat};
        idxCat = ismember(categoryPerElem, category);
        groupedHist(:,iCat) = hist(stat.values(idxCat), stat.binCenter);
    end

    stat.histogram  = groupedHist;
    stat.median     = median(stat.values);
end
function [data] = addFooterRow(data, column, func, otherFields)
% adds a footer row to data table based on calculations of a single column
footer = data(end);
for iField = 1:2:numel(otherFields)
    field = otherFields{iField};
    value = otherFields{iField+1};
    footer.(field) = value;
end
footer.(column) = func([data(:).(column)]);
data(end+1) = footer;
end

%% FORMATTING ==================================================================
function str = integerToString(value)
% convert integer to string
    str = sprintf('%d',value);
end
function str = markdownInlineCode(str)
% format as inline code for markdown
    str = sprintf('`%s`', str);
end
function str = makeTable(data, fields, header)
% make a markdown table from struct array
    nData = numel(data);
    str = '';
    if nData == 0
        return; % empty input
    end
    %TODO: use gfmTable from makeTravisReport instead to do the formatting

    % determine column sizes
    nFields = numel(fields);
    table = cell(nFields, nData);
    columnWidth = zeros(1,nFields);
    for iField = 1:nFields
        field = fields{iField};
        table(iField, :) = {data(:).(field)};
        columnWidth(iField) = max(cellfun(@numel, table(iField, :)));
    end
    columnWidth = max(columnWidth, cellfun(@numel, header));
    columnWidth = columnWidth + 2; % empty space left and right
    columnWidth([1,end]) = columnWidth([1,end]) - 1; % except at the edges

    % format table inside cell array
    table = [header; table'];
    for iField = 1:nFields
        FORMAT = ['%' int2str(columnWidth(iField)) 's'];

        for jData = 1:size(table, 1)
            table{jData, iField} = strjust(sprintf(FORMAT, ...
                                           table{jData, iField}), 'center');
        end
    end

    % insert separator
    table = [table(1,:)
             arrayfun(@(n) repmat('-',1,n), columnWidth, 'UniformOutput',false)
             table(2:end,:)]';

    % convert cell array to string
    FORMAT = ['%s' repmat('|%s', 1,nFields-1) '\n'];
    str = sprintf(FORMAT, table{:});

end

function str = codelinks(str, functionName)
% replaces inline functions with clickable links in MATLAB
str = regexprep(str, '`([A-Za-z0-9_]+)`', ...
                ['`<a href="matlab:edit ' functionName '>$1">$1</a>`']);
%NOTE: editing function>subfunction will focus on that particular subfunction
% in the editor (this also works for the main function)
end
function str = bold(str)
str = ['**' str '**'];
end

%% PLOTTING ====================================================================
function h = statisticsPlot(stat, xLabel, yLabel)
% plot a histogram and box plot
    nCategories = numel(stat.categories);
    colors = colorscheme;

    h(1) = subplot(5,1,1:4);
    hold all;
    hb = bar(stat.binCenter, stat.histogram, 'stacked');

    for iCat = 1:nCategories
        category = stat.categories{iCat};

        set(hb(iCat), 'DisplayName', category, 'FaceColor', colors.(category), ...
                   'LineStyle','none');
    end

    %xlabel(xLabel);
    ylabel(yLabel);

    h(2) = subplot(5,1,5);
    hold all;

    boxplot(stat.values,'orientation','horizontal',...
                        'boxstyle',   'outline', ...
                        'symbol',     'o', ...
                        'colors',  colors.All);
    xlabel(xLabel);

    xlims = [min(stat.binCenter)-1 max(stat.binCenter)+1];
    c     = 1;
    ylims = (ylim(h(2)) - c)/3 + c;

    set(h,'XTickMode','manual','XTick',stat.binCenter,'XLim',xlims);
    set(h(1),'XTickLabel','');
    set(h(2),'YTickLabel','','YLim',ylims);
    linkaxes(h, 'x');
end
function colors = colorscheme()
% recognizable color scheme for the categories
 colors.All      = [  0 113 188]/255;
 colors.Good     = [118 171  47]/255;
 colors.Bad      = [161  19  46]/255;
 colors.Accepted = [236 176  31]/255;
end


function varargout = real(varargin)
%REAL      Real part of a CHEBFUN2.
%
% See also IMAG.

% Copyright 2017 by The University of Oxford and The Chebfun Developers.
% See http://www.chebfun.org/ for Chebfun information.

[varargout{1:nargout}] = real@separableApprox(varargin{:});

end


function hdr=cosmo_map2fmri(dataset, fn, varargin)
% maps a dataset structure to a NIFTI, AFNI, or BV structure or file
%
% Usage 1: hdr=cosmo_map2fmri(dataset, '-{FMT}) returns a header structure
% Usage 2: cosmo_map2fmri(dataset, fn) saves dataset to a volumetric file.
%
% In Usage 1, {FMT} can be one of 'nii','bv_vmp',bv_vmr','bv_msk','afni'.
% In Usage 2, fn should end with '.nii.gz', '.nii', '.hdr', '.img', '.vmp',
%             '.vmr', '.msk', '+orig','+orig.HEAD','+orig.BRIK',
%             '+orig.BRIK.gz','+tlrc','+tlrc.HEAD','+tlrc.BRIK', or
%             '+tlrc.BRIK.gz'.
%
% Use cosmo_map2fmri(...,'deoblique',true) to de-oblique the dataset. For
% information on this option, see cosmo_fmri_deoblique
%
% - for NIFTI files, it requires the following toolbox:
%   http://www.mathworks.com/matlabcentral/fileexchange/8797-tools-for-nifti-and-analyze-image
%   (note that his toolbox is included in CoSMoMVPA in /externals)
% - for Brainvoyager files (.vmp and .vtc), it requires the NeuroElf
%   toolbox, available from: http://neuroelf.net
% - for AFNI files (+{orig,tlrc}.{HEAD,BRIK[.gz]}) it requires the AFNI
%   Matlab toolbox, available from: http://afni.nimh.nih.gov/afni/matlab/
%
% See also: cosmo_fmri_deoblique
%
% #   For CoSMoMVPA's copyright information and license terms,   #
% #   see the COPYING file distributed with CoSMoMVPA.           #

    defaults=struct();
    defaults.deoblique=false;
    defaults.bv_force_fit=[];
    opt=cosmo_structjoin(defaults,varargin{:});

    cosmo_check_dataset(dataset, 'fmri');

    img_formats=get_img_formats();
    sp=cosmo_strsplit(fn,'-');
    save_to_file=~isempty(sp{1});

    if save_to_file
        fmt=get_format(img_formats, fn);
    else
        if numel(sp)~=2
            error('expected -{FORMAT}');
        end
        fmt=sp{2};
    end

    if ~isfield(img_formats,fmt)
        error('Unsupported format %s', fmt);
    end

    methods=img_formats.(fmt);
    externals=methods.externals;
    cosmo_check_external(externals);

    creator=methods.creator;

    % preprocess the data (de-oblique if necessary)
    dataset=preprocess(dataset,opt);

    % build header structure
    hdr=creator(dataset);

    if nargout==0
        cleaner=onCleanup(get_cleaner(methods,hdr));
    end

    if save_to_file
        writer=methods.writer;
        writer(fn, hdr);
    end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% general helper functions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function f=get_cleaner(methods,hdr)
    if isfield(methods,'cleaner')
        f=@()methods.cleaner(hdr);
    else
        f=@do_nothing;
    end

function do_nothing
    % do nothing

function ds=preprocess(ds,opt)
    if opt.deoblique
        ds=cosmo_fmri_deoblique(ds);
    end

    if opt.bv_force_fit
        ds=bv_force_fit(ds);
    end

function ds=bv_force_fit(ds)

    mat=ds.a.vol.mat;

    dim=[ds.a.vol.dim(:);1];
    rot=mat(1:3,1:3);
    vox_size=sqrt(sum(rot.^2,1)); %.*sign(sum(rot,1));
    resolution=prod(vox_size).^(1/3);

    % put the center defined by the original matrix close to the center
    % defined by the new matrix
    old_center=mat*(dim+1)/2;
    mat(1:3,1:3)=bsxfun(@times,mat(1:3,1:3),resolution./vox_size);

    new_center=mat*(dim+1)/2;
    mat(:,4)=round(mat(:,4)+old_center-new_center)+.5;

    ds.a.vol.mat=mat;

function check_plump_orientation(ds)
    rot=ds.a.vol.mat(1:3,1:3);
    nonzero=rot~=0;
    is_oblique=any(sum(nonzero,1)~=1) || any(sum(nonzero,2)~=1);
    if is_oblique
        error(['Dataset has oblique orientation, and the specified '...
                'output format does not support this. Options are:\n\n'...
                '(1) use %s(...,''deoblique'',true) to deoblique the '...
                'dataset automatically when writing it\n'...
                '(2) use cosmo_fmri_deoblique to deoblique the dataset '...
                'manually\n'...
                '(3) store the dataset in another file format (such as '...
                'NIFTI)\n\n'...
                'For options (1) and (2), the spatial location of the '...
                'voxels will change. It is recommended to inspect the '...
                'results visually when using one of these options.'],...
                mfilename());
    end

function check_isotropic_voxels(ds)
    rot=ds.a.vol.mat(1:3,1:3);
    vox_size=sqrt(sum(rot.^2,1));

    max_delta=1e-5;
    delta=max(vox_size)-min(vox_size);

    if delta>max_delta
        error(['Dataset has non-isotropic voxels, and the specified '...
                'output format does not support this. Options are:\n\n'...
                '(1) use %s(...,''bv_force_fit'',true) to set the '...
                'voxel size to be isotropic\n'...
                '(2) store the dataset in another file format (such as '...
                'NIFTI)\n\n'...
                'For option (1), the spatial location of the '...
                'voxels will change. It is recommended to inspect the '...
                'results visually when using one of these options.'],...
                mfilename());
    end

function unfl_ds_timelast=unflatten(ds)
    % puts the time dimension last, instead of first
    unfl_ds=cosmo_unflatten(ds);

    % make time dimension last instead of first
    unfl_ds_timelast=shiftdim(unfl_ds,1);

    % deal with singleton dimension
    sz=size(unfl_ds);
    dim_size=ones(1,4);
    dim_size(4)=sz(1);
    dim_size(1:(numel(sz)-1))=sz(2:end);

    nsamples=size(ds.samples,1);
    assert(isequal([ds.a.vol.dim nsamples],dim_size));

    if ~isequal(size(unfl_ds_timelast),dim_size)
        unfl_ds_timelast=reshape(unfl_ds_timelast,dim_size);
    end

function b=ends_with(end_str, str)
    if iscell(end_str)
        b=any(cellfun(@(x) ends_with(x,str),end_str));
    else
        b=isempty(cosmo_strsplit(str, end_str,-1));
    end

function fmt=get_format(img_formats, file_name)
    fns=fieldnames(img_formats);
    fmt=[];
    for k=1:numel(fns)
        fn=fns{k};
        exts=img_formats.(fn).exts;
        if ends_with(exts, file_name)
            fmt=fn;
        end
    end
    if isempty(fmt)
        error('Not found: format for %s', file_name);
    end

function img_formats=get_img_formats()
    img_formats=struct();

    img_formats.nii.creator=@new_nii;
    img_formats.nii.writer=@write_nii;
    img_formats.nii.externals={'nifti'};
    img_formats.nii.exts={'.nii','.nii.gz','.hdr','.img'};

    img_formats.bv_vmp.creator=@new_bv_vmp;
    img_formats.bv_vmp.writer=@write_bv;
    img_formats.bv_vmp.cleaner=@clean_bv;
    img_formats.bv_vmp.externals={'neuroelf'};
    img_formats.bv_vmp.exts={'.vmp'};

    img_formats.bv_vmr.creator=@new_bv_vmr;
    img_formats.bv_vmr.writer=@write_bv;
    img_formats.bv_vmr.cleaner=@clean_bv;
    img_formats.bv_vmr.externals={'neuroelf'};
    img_formats.bv_vmr.exts={'.vmr'};

    img_formats.bv_msk.creator=@new_bv_msk;
    img_formats.bv_msk.writer=@write_bv;
    img_formats.bv_msk.cleaner=@clean_bv;
    img_formats.bv_msk.externals={'neuroelf'};
    img_formats.bv_msk.exts={'.msk'};

    img_formats.afni.creator=@new_afni;
    img_formats.afni.writer=@write_afni;
    img_formats.afni.externals={'afni'};
    img_formats.afni.exts={'+orig','+orig.HEAD','+orig.BRIK',...
                           '+orig.BRIK.gz','+tlrc','+tlrc.HEAD',...
                           '+tlrc.BRIK','+tlrc.BRIK.gz'};

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% format-specific helper functions
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% Nifti

function ni=new_nii(ds)
    a=ds.a;
    vol=a.vol;
    vol_data=unflatten(ds);
    dim_all=[4 ones(1,7)];
    vol_dim=size(vol_data);
    nvol_dim=numel(vol_dim);
    dim_all(1+(1:nvol_dim))=vol_dim;

    mat=vol.mat;
    mat(1:3,4)=mat(1:3,4)+mat(1:3,1:3)*[1 1 1]';
    pix_dim=sqrt(sum(mat(1:3,1:3).^2,1));
    hdr=struct();

    dime=struct();
    dime.datatype=16; %single
    dime.dim=dim_all;
    dime.pixdim=[0 abs(pix_dim(:))' 0 0 0 0]; % ensure positive values

    fns={'intent_p1','intent_p2','intent_p3','intent_code',...
        'slice_start','slice_duration','slice_end',...
        'scl_slope','scl_inter','slice_code','cal_max',...
        'cal_min','toffset'};

    dime=set_all(dime,fns);
    dime=cosmo_structjoin(dime, cosmo_statcode(ds,'nifti'));
    dime.xyzt_units=10;
    hdr.dime=dime;

    hk=struct();
    hk.sizeof_hdr=348;
    hk.data_type='';
    hk.db_name='';
    hk.extents=0;
    hk.session_error=0;
    hk.regular='r';
    hk.dim_info=0;
    hdr.hk=hk;

    hist=struct();

    if isfield(ds.a.vol,'xform')
        xform=ds.a.vol.xform;
    else
        xform=''; % unknown
    end
    xform_code=cosmo_fmri_convert_xform('nii',xform);

    hist.sform_code=xform_code;
    % hist.originator=[1 1 1 1 0];
    hist=set_all(hist,{'descrip','aux_file','intent_name'},'');
    hist=set_all(hist,{'qform_code','quatern_b',...
                        'quatern_d',...
                        'qoffset_x','qoffset_y','qoffset_z'});
    hist=set_all(hist,{'intent_name'},'');
    hist.srow_x=mat(1,:);
    hist.srow_y=mat(2,:);
    hist.srow_z=mat(3,:);
    hist.quatern_c=0;

    % required to avoid complaints with hdr/img
    hist.originator=round(dim_all(2:5)/2);
    hdr.hist=hist;

    ni.img=single(vol_data);
    ni.hdr=hdr;

function write_nii(fn, hdr)
    save_nii(hdr, fn);

    %% BrainVoyager helper
function mat=neuroelf_bvcoordconv_wrapper(varargin)
    % converts BV bounding box to affine transformation matrix
    % helper functions that deals with both new neuroelf (version 1.0)
    % and older versions.
    % the old version provides a 'bvcoordconv' .m file
    % the new version privides this function in the neuroelf class
    has_bvcoordconv=~isempty(which('bvcoordconv'));

    % set function handle
    if has_bvcoordconv
        f=@bvcoordconv;
    else
        n=neuroelf();
        f=@n.bvcoordconv;
    end

    mat=f(varargin{:});

function result=neuroelf_bless_wrapper(arg)
    % deals with recent neuroelf (>v1.1), where bless is deprecated
    s=warning('off','neuroelf:xff:deprecated');
    resetter=onCleanup(@()warning(s));

    result=bless(arg);

    %% Brainvoyager VMP
function [hdr,ds]=new_bv_mat_hdr(ds,bv_type)
    % ensure dataset is plump
    check_plump_orientation(ds);

    if ~strcmp(bv_type,'vmr')
        % require voxels to be isotropic
        check_isotropic_voxels(ds);
    end

    % automatically set orientation to ASR
    if ~strcmp(cosmo_fmri_orientation(ds),'ASR')
        ds=cosmo_fmri_reorient(ds,'ASR');
    end

    % helper to set matrix in header
    mat=ds.a.vol.mat;

    % ensure proper VMP/VMR orientation
    rot_asr=mat(1:3,1:3)*[0 0 -1; -1 0 0; 0 -1 0]';

    if ~isequal(rot_asr,diag(diag(rot_asr)))
        % should never get here because of the ASR orientation
        % forced earlier
        error('Unsupported orientation: need ASR');
    end

    hdr=xff(['new:' bv_type]);
    hdr=neuroelf_bless_wrapper(hdr);

    % Set {X,Y,Z}{Start,End} values based on the transformation matrix
    tal_coords=mat*[1 1 1 1; ds.a.vol.dim+1, 1]';
    bv_coords=neuroelf_bvcoordconv_wrapper(tal_coords(1:3,:), ...
                                        'tal2bvs',hdr.BoundingBox);

    switch bv_type
        case {'vmp','msk'}
            dg=diag(rot_asr);
            resolution=prod(dg)^(1/3);

            labels={'ZStart','ZEnd','XStart','XEnd','YStart','YEnd'};
            for k=1:numel(labels)
                label=labels{k};
                hdr.(label)=bv_coords(k);
            end

            hdr.Resolution=resolution;

        case 'vmr'
            % this *should* be a 256^3 volume
            % XXX check for this?
            labels={'X','Y','Z'};
            dg=diag(rot_asr);
            for k=1:3
                label=labels{k};
                hdr.(['VoXRes' label])=dg(k);
                hdr.(['Dim' label])=round(bv_coords(2,k));
            end

        otherwise
            error('Unsupported type %s', bv_type);
    end

function hdr=new_bv_vmp(ds)
    [hdr,ds]=new_bv_mat_hdr(ds,'vmp');

    % Get data
    nsamples=size(ds.samples,1);
    maps=cell(1,nsamples);

    stats=cosmo_statcode(ds,'bv');

    for k=1:nsamples
        empty_hdr=xff('new:vmp');
        map=empty_hdr.Map;
        ds_k=cosmo_slice(ds,k);
        map.VMPData=unflatten(ds_k);
        if cosmo_isfield(ds_k,'sa.labels')
            map.Name=[ds_k.sa.labels{:}];
        end
        if ~isempty(stats)
            stats_k=stats{k};

            stats_k_fieldnames=fieldnames(stats_k);
            for j=1:numel(stats_k_fieldnames)
                key=stats_k_fieldnames{j};
                map.(key)=stats_k.(key);
            end
        end
        maps{k}=map;
        empty_hdr.ClearObject();
    end

    hdr.Map=cat(2,maps{:});
    hdr.NrOfMaps=nsamples;

    neuroelf_bless_wrapper(hdr);

function write_bv(fn, hdr)
    % general storage function
    hdr.SaveAs(fn);

function clean_bv(hdr)
    hdr.ClearObject();

    %% Brainvoyager VMR
function hdr=new_bv_vmr(ds)
    nsamples=size(ds.samples,1);
    if nsamples~=1,
        error('Unsupported: more than 1 sample');
    end

    [hdr,ds]=new_bv_mat_hdr(ds,'vmr');

    % scale to 0..255
    vol_data=unflatten(ds);
    hdr.VMRData=scale_uint8(vol_data(:,:,:,1));

    %% Brainvoyager mask
function hdr=new_bv_msk(ds)
    nsamples=size(ds.samples,1);
    if nsamples~=1,
        error('Unsupported: more than 1 sample');
    end

    [hdr,ds]=new_bv_mat_hdr(ds,'msk');

    vol_data=unflatten(ds);
    hdr.Mask=scale_uint8(vol_data);

function scaled_data=scale_uint8(data)
    mn=min(data(:));
    mx=max(data(:));
    if mn<0 || mx>255 || ~isequal(round(data),data)
        cosmo_warning(['.samples does not fit integer range 0..255 ',...
                            'data will be rescaled']);
        data=255*(data-mn)/(mx-mn);
    end

    scaled_data=uint8(data);

%% AFNI
function afni_info=new_afni(ds)
    check_plump_orientation(ds);

    a=ds.a;
    mat=a.vol.mat;

    % deal with orientation
    idxs=zeros(1,3);
    m=zeros(1,3);

    % this part should be necessary
    for k=1:3
        % for each spatial dimension, find which row transforms it
        idx=find(mat(1:3,k));
        assert(numel(idx)==1); % ds is plump

        % store index and transformation matrix
        idxs(k)=idx;
        m(k)=mat(idx,k);
    end

    % set voxel size and origin
    % as the header was stored in LPI but AFNI likes RAI,
    % convert coordinates to RAI
    lpi2rai=[-1 -1 1];

    delta=m.*lpi2rai(idxs);
    origin=mat(idxs,:)*[1 1 1 1]'.*lpi2rai(idxs)';

    % set orientation code.
    % thse are neither RAI or LPI, but RPI (when ordered logically)
    % No idea why Bob Cox made that decision.
    lpi2orient=[-1 1 1];
    offset=(1-sign(m).*lpi2orient(idxs))/2;
    orient=(idxs-1)*2+offset;

    [nsamples,nfeatures]=size(ds.samples);

    vol_data=unflatten(ds);
    dim=ds.a.vol.dim(:)';
    vol_dim=[size(vol_data) 1]; % in case of singleton in last dimension
    assert(isequal(vol_dim(1:3),dim));
    assert(prod(dim)>=nfeatures);

    brik_type=1; %functional head
    brik_typestring='3DIM_HEAD_FUNC';
    brik_func=11; % FUNC_BUCK_TYPE

    if isfield(ds.a.vol,'xform')
        xform=ds.a.vol.xform;
    else
        xform=''; % unknown
    end
    xform_code=cosmo_fmri_convert_xform('afni',xform);

    brik_view=xform_code; % default to +orig, but overriden by writer

    afni_info=struct();
    afni_info.SCENE_DATA=[brik_view, brik_func, brik_type];
    afni_info.TYPESTRING=brik_typestring;
    afni_info.BRICK_TYPES=3*ones(1,nsamples); % store in float format
    afni_info.BRICK_STATS=[];                 % ... and thus need no stats
    afni_info.BRICK_FLOAT_FACS=[];            % ... or multipliers
    afni_info.DATASET_RANK=[3 nsamples];      % number of volumes
    afni_info.DATASET_DIMENSIONS=dim(1:3);
    afni_info.ORIENT_SPECIFIC=orient;
    afni_info.DELTA=delta(:)';
    afni_info.ORIGIN=origin(:)';
    afni_info.SCALE=0;
    afni_info.NOTES_COUNT=0;
    afni_info.WARP_TYPE=[0 0];
    afni_info.FileFormat='BRIK';

    [unused, unused, endian_ness] = computer();
    afni_info.BYTEORDER_STRING = sprintf('%sSB_FIRST', endian_ness);

    set_empty={'BRICK_LABS','BRICK_KEYWORDS',...
                'BRICK_STATS','BRICK_FLOAT_FACS',...
                'BRICK_STATAUX','STAT_AUX'};
    for k=1:numel(set_empty)
        fn=set_empty{k};
        afni_info.(fn)=[];
    end

    % if labels for the samples, store them in the header
    if isfield(ds.sa,'labels') && ~isempty(ds.sa.labels)
        afni_info.BRICK_LABS=cosmo_strjoin(ds.sa.labels,'~');
    end

    if isfield(ds.sa,'stats') && ~isempty(ds.sa.stats)
        afni_info=cosmo_structjoin(afni_info,cosmo_statcode(ds,'afni'));
    end

    % store data in non-afni field 'img'
    afni_info.img=vol_data;

function write_afni(fn, hdr)

    hdr.RootName=fn;
    data=hdr.img; % get the data
    hdr=rmfield(hdr,'img'); % remove the non-afni field 'img'

    afniopt=struct();
    afniopt.Prefix=fn; %the second input argument
    afniopt.OverWrite='y';
    afniopt.NoCheck=0;
    afniopt.AppendHistory=false;
    afniopt.verbose=0;
    afniopt.Scale=0;
    afniopt.AdjustHeader='no';

    if ends_with({'+orig','+orig.HEAD','+orig.BRIK',...
                           '+orig.BRIK.gz'},fn)
        hdr.SCENE_DATA(1)=0;
        afniopt.View='+orig';
    elseif ends_with({'+tlrc','+tlrc.HEAD',...
                           '+tlrc.BRIK','+tlrc.BRIK.gz'},fn)
        hdr.SCENE_DATA(1)=2;
        afniopt.View='+tlrc';
    else
        error('Unsupported scene data for %s', fn);
    end

    [err, ErrMessage]=WriteBrik(data, hdr, afniopt);
    if err
        error(ErrMessage);
    end

 function s=set_all(s, fns, v)
    % sets all fields in fns in struct s to v
    % if v is omitted it is set to 0.
    if nargin<3, v=0; end
    n=numel(fns);
    for k=1:n
        fn=fns{k};
        s.(fn)=v;
    end


% ------------------------------------------------------------------------
%  Copyright (C)
%  Torr Vision Group (TVG)
%  University of Oxford - UK
%
%  Qizhu Li <liqizhu@robots.ox.ac.uk>
%  August 2018
% ------------------------------------------------------------------------
% This file is part of the weakly-supervised training method presented in:
%    Qizhu Li*, Anurag Arnab*, Philip H.S. Torr,
%    "Weakly- and Semi-Supervised Panoptic Segmentation,"
%    European Conference on Computer Vision (ECCV) 2018.
% Please consider citing the paper if you use this code.
% ------------------------------------------------------------------------
% This script demos generation of iterative ground truths for weakly-
% supervised experiments.
%
% It post-processes network predictions to produce iterative GT for
% training in the next iteration.
%  INPUT:
%  - results/pred_flat_feat/*.mat: the softmax scores of the predicted
%  classes.
%  - results/pred_sem_raw/*.png: the prediction made by the current
%  weakly-supervised model.
%  - results/mcg_and_grabcut/<split>/*.png: the combined cues from MCG
%  and Grabcut. Optional. Set opts.run_merge_with_mcg_and_grabcut = false
%  to disable. To reproduce the results in our paper, disable after first
%  5 iterations.
% ------------------------------------------------------------------------

clearvars;
addpath scripts
addpath utils
addpath visualisation

% Extract detections from Cityscapes ground truth file and save as .mat
% this only need to be done once, as they do not change over iterative
% training stages
demo_instanceTrainId_to_dets;

clearvars;
dataset = 'cityscapes';
split = 'train';
opts = get_opts(dataset, split);
opts.list_path = 'lists/demo_id.txt';
opts.visualise_results = true;
[opts, results] = run_sub(opts);

% visualise
visualise_results_iterative_gt(opts, results);


function CPD = set_params(CPD, varargin)
% SET_PARAMS Set the parameters (fields) for a softmax_CPD object
% CPD = set_params(CPD, name/value pairs)
%
% The following optional arguments can be specified in the form of name/value pairs:
% (Let ns(i) be the size of node i, X = ns(X), Y = ns(Y), Q1=ns(dps(1)), Q2=ns(dps(2)), ...
%   where dps are the discrete parents; if there are no discrete parents, we set Q1=1.)
%
% weights - (W(:,j,a,b,...) - W(:,j',a,b,...)) is ppn to dec. boundary
%           between j,j' given Q1=a,Q2=b,... [ randn(X,Y,Q1,Q2,...) ]
% offset  - (offset(j,a,b,...) - offset(j',a,b,...)) is the offset to dec. boundary
%           between j,j' given Q1=a,Q2=b,... [ randn(Y,Q1,Q2,...) ]
% clamped     - 'yes' means don't adjust params during learning ['no']
% max_iter    - the maximum number of steps to take [10]
% verbose     - 'yes' means print the LL at each step of IRLS ['no']
% wthresh     - convergence threshold for weights [1e-2]
% llthresh    - convergence threshold for log likelihood [1e-2]
% approx_hess - 'yes' means approximate the Hessian for speed ['no']
%
% e.g., CPD = set_params(CPD,'offset', zeros(ns(i),1));

args = varargin;
nargs = length(args);
glimsz = prod(CPD.sizes(CPD.dpndx));
for i=1:2:nargs
  switch args{i},
   case 'discrete',     str='nothing to do';
   case 'clamped',      CPD = set_clamped(CPD, strcmp(args{i+1}, 'yes'));
   case 'max_iter',     CPD.max_iter = args{i+1};
   case 'verbose',      CPD.verbose = strcmp(args{i+1}, 'yes');
   case 'max_iter',     CPD.max_iter = args{i+1};
   case 'wthresh',      CPD.wthresh = args{i+1};
   case 'llthresh',     CPD.llthresh = args{i+1};
   case 'approx_hess',  CPD.approx_hess = strcmp(args{i+1}, 'yes');
   case 'weights',      for q=1:glimsz, CPD.glim{q}.w1 = args{i+1}(:,:,q); end;
   case 'offset',
    if glimsz == 1
      CPD.glim{1}.b1 = args{i+1};
    else
      for q=1:glimsz, CPD.glim{q}.b1 = args{i+1}(:,q); end;
    end
   otherwise,
    error(['invalid argument name ' args{i}]);
  end
end


% COMPUTATION TIME FIGURES
function [currentFigure,figureHandle] = GetFigure_computationTimes(SIM,objectIndex,DATA,currentFigure)
% This function gets the computation time figures for all agents using the
% objectIndex.DATA fields.
% INPUTS:
% SIM     - Local copy of the META structure
% OBJECTS - The object class objects.
% currentFigure - The current figure number
% OUTPUTS:
% currentFigure - Updated figure number
% figureHandle - Handle to the created figure

figureHandle = [];

% SANITY CHECK 1
if DATA.totalAgents == 0
    warning('No agent data available.');
    return
else
    % LOGICALLY SELECT THE AGENTS FROM THE OBJECT INDEX
    agentObjectIndex = objectIndex([SIM.OBJECTS.type] == OMAS_objectType.agent); % The agents themselves
end

iter = 0;
for ID1 = 1:SIM.totalAgents
    % TRY TO GET THE REQUIRED PROPERTIES
    try
       agentObjectIndex{ID1}.DATA.dt;
    catch
       iter = iter + 1;
    end
end

% SANITY CHECK 2
if iter == SIM.totalAgents
    warning('No agents have computational timeseries data stored in (agent)."DATA.dt".');
    return
end

% CONFIGURE THE PLOT ATTRIBUTES
figurePath = strcat(SIM.outputPath,'computation-times');
figureHandle = figure('Name','OpenMAS computation timeseries');
ax = axes(figureHandle);
set(figureHandle,'Position',DATA.figureProperties.windowSettings);         % [x y width height]
set(figureHandle,'Color',DATA.figureProperties.figureColor);               % Background colour
setappdata(figureHandle, 'SubplotDefaultAxesLocation', [0.08, 0.08, 0.90, 0.88]);
hold on;
legendEntries = cell(SIM.totalAgents,1);
for ind = 1:SIM.totalAgents
    % Get object data
    entity = objectIndex{ind};
    % Get the equivalent SIM object
    SIMobject = SIM.OBJECTS([SIM.OBJECTS.objectID] == entity.objectID);
    % Legend entry
    legendEntries{ind} = sprintf('[ID-%s] %s',num2str(SIMobject.objectID),SIMobject.name);
    % Generate the figures
    stairs(ax,DATA.timeVector(1:SIM.TIME.endStep),entity.DATA.dt*1E3,...
         'Color',SIMobject.colour,...
         'LineWidth',DATA.figureProperties.lineWidth,...
         'DisplayName',legendEntries{ind});
end

% Title
title(ax,...
    sprintf('Agent computation times over a period of %ss',num2str(SIM.TIME.endTime)),...
    'Interpreter',DATA.figureProperties.interpreter,...
    'fontname',DATA.figureProperties.fontName,...
    'Fontweight',DATA.figureProperties.fontWeight,...
    'FontSize',DATA.figureProperties.titleFontSize,...
    'FontSmoothing','on');
% X-Label
xlabel(ax,'t (s)',...
    'Interpreter',DATA.figureProperties.interpreter,...
    'fontname',DATA.figureProperties.fontName,...
    'Fontweight',DATA.figureProperties.fontWeight,...
    'FontSize',DATA.figureProperties.axisFontSize,...
    'FontSmoothing','on');
% Y-Label
ylabel(ax,'Computation time (ms)',...
    'Interpreter',DATA.figureProperties.interpreter,...
    'fontname',DATA.figureProperties.fontName,...
    'Fontweight',DATA.figureProperties.fontWeight,...
    'FontSize',DATA.figureProperties.axisFontSize,...
    'FontSmoothing','on');
% Legend
legend(...
    'Location','northeast',...
    'Interpreter',DATA.figureProperties.interpreter,...
    'fontname',DATA.figureProperties.fontName);
% Axes
set(ax,...
    'TickLabelInterpreter',DATA.figureProperties.interpreter,...
    'fontname',DATA.figureProperties.fontName,...
    'Fontweight',DATA.figureProperties.fontWeight,...
    'FontSize',DATA.figureProperties.axisFontSize,...
    'FontSmoothing','on',...
    'Color',DATA.figureProperties.axesColor,...
    'GridLineStyle','--',...
    'GridAlpha',0.25,...
    'GridColor','k');
grid on; box on; grid minor;

% Show the timestep difference in the figure
ax.XAxis.MinorTickValues = ax.XAxis.Limits(1):SIM.TIME.dt:ax.XAxis.Limits(2);

% SAVE THE OUTPUT FIGURE
savefig(figureHandle,figurePath);

% Publish as .pdf if requested
if DATA.figureProperties.publish
	GetFigurePDF(figureHandle,figurePath);
end

% INCREMENT THE FIGURE INDEX
DATA.figureProperties.alignment = DATA.figureProperties.alignment + DATA.figureProperties.spacing;
currentFigure = currentFigure + 1;
end


function [v,f] = mesh_flip_faces(arg1, arg2, arg3)

if nargin==1
    name = arg1;
    [v,f] = read_mesh(name);
else
    v = arg1;
    f = arg2
end

if size(f,1)~=3
    f = f';
end

f = [f(1,:); f(3,:); f(2,:)];

if nargin==1
    write_mesh(name, v,f);
end


function nDefined = notDefined(varString)
%
% nDefined= notDefined( varString )
%
% Determines if a variable is defined in the workspace.
% A variable is defined if (a) it exists and (b) it is not empty.
%
% nDefined: 1 (true) if the variable is not defined in the calling workspace
%           0 (false) if the variable is defined in the calling workspace
%
%  This routine replaces calls of the form:
%    if ~exist('varname','var') || isempty(xxx) with the call
%    if notDefined('varname')
%

if (~ischar(varString)), error('Varible name must be a string'); end

nDefined = 1;

str = sprintf('''%s''',varString);
cmd1 = ['~exist(' str ',''var'') == 1'];
cmd2 = ['isempty(',varString ') == 1'];

% If either of these conditions holds, Defined == true
nDefined = evalin('caller',cmd1);     % Check that the variable exists in the caller space
if nDefined, return;                  % If it does not, return with a status of 0
else
    nDefined = evalin('caller',cmd2); % Check if the variable is empty in the caller space
    if nDefined return;
    else nDefined = 0;

    end
end

return;


function [header_array, CateNames, CatLengths, preBaseline] = read_sbin_header(filename)

% READ_SBIN_HEADER reads the header information from an EGI segmented simple binary format file
%
% Use as
%   [header_array, CateNames, CatLengths, preBaseline] = read_sbin_header(filename)
% with
%   header_array     - differs between versions, read code for details
%   CateNames        - category names
%   CatLengths       - length of category names
%   preBaseline      - number of samples in the baseline prior to the baseline event
% and
%   filename    - the name of the data file
%
% Since there is no unique event code for the segmentation event, and hence the baseline period,
% the first event code in the list will be assumed to be the segmentation event.
% NetStation itself simply ignores possible baseline information when importing simple binary files.
%_______________________________________________________________________
%
%
% Modified from EGI's readEGLY.m with permission 2008-03-31 Joseph Dien
%

% 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$

fid=fopen_or_error([filename],'r');

version     = fread(fid,1,'int32');
if isempty(version)
    ft_error('ERROR:  This is not a simple binary file.  Note that NetStation does not successfully directly convert EGIS files to simple binary format.');
end;

%check byteorder
[str,maxsize,cEndian]=computer;
if version < 7
    if cEndian == 'B'
        endian = 'ieee-be';
    elseif cEndian == 'L'
        endian = 'ieee-le';
    end;
elseif (version > 6) && ~bitand(version,6)
    if cEndian == 'B'
        endian = 'ieee-le';
    elseif cEndian == 'L'
        endian = 'ieee-be';
    end;
    version = swapbytes(uint32(version));
else
    ft_error('ERROR:  This is not a simple binary file.  Note that NetStation does not successfully directly convert EGIS files to simple binary format.');
end;

if bitand(version,1) == 0
    unsegmented = 1;
else
    unsegmented = 0;
end;

precision = bitand(version,6);
if precision == 0
    ft_error('File precision is not defined.');
end;

%       read header...
year        = fread(fid,1,'int16',endian);
month       = fread(fid,1,'int16',endian);
day         = fread(fid,1,'int16',endian);
hour        = fread(fid,1,'int16',endian);
minute      = fread(fid,1,'int16',endian);
second      = fread(fid,1,'int16',endian);
millisecond = fread(fid,1,'int32',endian);
Samp_Rate   = fread(fid,1,'int16',endian);
NChan       = fread(fid,1,'int16',endian);
Gain        = fread(fid,1,'int16',endian);
Bits        = fread(fid,1,'int16',endian);
Range       = fread(fid,1,'int16',endian);

epochMarked=0;
if unsegmented,
    NumCategors = 0;
    NSegments   = 1;
    NSamples    = fread(fid,1,'int32',endian);
    NEvent      = fread(fid,1,'int16',endian);
    EventCodes='';
    for j = 1:NEvent
        EventCodes(j,1:4) = char(fread(fid,[1,4],'char',endian));
    end
    CateNames   = [];
    CatLengths  = [];
    preBaseline = 0;

    if any(strcmp('epoc',cellstr(EventCodes))) && any(strcmp('tim0',cellstr(EventCodes))) %actually epoch-marked segmented file format

        %Note that epoch-marked simple binary file format loses cell information so just the one "cell"

        switch precision
            case 2
                [temp,count]    = fread(fid,[NChan+NEvent, NSamples],'int16',endian);
            case 4
                [temp,count]    = fread(fid,[NChan+NEvent, NSamples],'single',endian);
            case 6
                [temp,count]    = fread(fid,[NChan+NEvent, NSamples],'double',endian);
        end
        eventData(:,1:NSamples)  = temp( (NChan+1):(NChan+NEvent), 1:NSamples);

        NSegments = length(find(eventData(find(strcmp('epoc',cellstr(EventCodes))),:)));
        epocSamples=find(eventData(find(strcmp('epoc',cellstr(EventCodes))),:));
        segmentLengths=diff(epocSamples);
        segmentLengths=[segmentLengths length(eventData)-epocSamples(end)+1];
        NSamples=max(segmentLengths); % samples per segment, will zero pad out to longest length
        preBaseline=min(find(eventData(find(strcmp('tim0',cellstr(EventCodes))),:)))-min(find(eventData(find(strcmp('epoc',cellstr(EventCodes))),:))); %make assumption all prestimulus durations are the same
    end;
else
    NumCategors = fread(fid,1,'int16',endian);
    for j = 1:NumCategors
        CatLengths(j)   = fread(fid,1,'int8',endian);
        for i = 1:CatLengths(j)
            CateNames{j}(i) = char(fread(fid,1,'char',endian));
        end
    end
    NSegments   = fread(fid,1,'int16',endian);
    NSamples    = fread(fid,1,'int32',endian);          % samples per segment
    NEvent      = fread(fid,1,'int16',endian);          % num events per segment
    EventCodes = '';
    for j = 1:NEvent
        EventCodes(j,1:4)   = char(fread(fid,[1,4],'char',endian));
    end

    preBaseline=0;
    if NEvent > 0

        for j = 1:NSegments
            [segHdr(j,1), count]    = fread(fid, 1,'int16',endian);    %cell
            [segHdr(j,2), count]    = fread(fid, 1,'int32',endian);    %time stamp
            switch precision
                case 2
                    [temp,count]    = fread(fid,[NChan+NEvent, NSamples],'int16',endian);
                case 4
                    [temp,count]    = fread(fid,[NChan+NEvent, NSamples],'single',endian);
                case 6
                    [temp,count]    = fread(fid,[NChan+NEvent, NSamples],'double',endian);
            end
            eventData(:,((j-1)*NSamples+1):j*NSamples)  = temp( (NChan+1):(NChan+NEvent), 1:NSamples);
        end
    end;

    if ~unsegmented
        if NEvent == 0
            theEvent=[];
        elseif NEvent == 1
            %assume this is the segmentation event
            theEvent=find(eventData(1,:)>0);
            theEvent=mod(theEvent(1)-1,NSamples)+1;
        else
            %assume the sample that always has an event is the baseline
            %if more than one, choose the earliest one
            totalEventSamples=mod(find(sum(eventData(:,:))')-1,NSamples)+1;
            baselineCandidates = unique(totalEventSamples);
            counters=hist(totalEventSamples, length(baselineCandidates));
            theEvent=min(baselineCandidates(find(ismember(counters,NSegments))));
        end;

        preBaseline=theEvent-1;
        if (preBaseline == -1)
            preBaseline =0;
        end;
        if isempty(preBaseline)
            preBaseline =0;
        end;
    end
end

fclose(fid);

header_array    = double([version year month day hour minute second millisecond Samp_Rate NChan Gain Bits Range NumCategors, NSegments, NSamples, NEvent]);


function FigFont(opt)
% =======================================================================
% Set font in a figure to desired font size and font style
% =======================================================================
% FigFont(opt)
% -----------------------------------------------------------------------
% OPTIONAL INPUT
%   - opt: output of FigFontOption(fsize)
% =======================================================================
% Ambrogio Cesa Bianchi, March 2015
% ambrogio.cesabianchi@gmail.com

%% CHECK INPUT
% =======================================================================
if ~exist('opt','var')
    opt = FigFontOption;
end

%% SET FONT STYLE
% =======================================================================
% AXES
aux = findobj(gcf,'Type','axes');
set(aux,'Fontsize',opt.axes_size,'FontWeight',opt.axes_weight,'FontName',opt.axes_name);

% TITLES
if max(size(aux))==1
    aux = get(aux, 'title');
    set(aux,'Fontsize',opt.title_size,'FontWeight',opt.title_weight,'FontName',opt.title_name);
else
    aux1 = get(aux(1), 'title');
    set(aux1, 'Fontsize', opt.title_size,'FontWeight',opt.title_weight,'FontName',opt.title_name);
    aux2 = get(aux(2), 'title');
    set(aux2, 'Fontsize',opt.title_size,'FontWeight',opt.title_weight,'FontName',opt.title_name);
end

% SUPTITLE
aux = findobj(gcf,'tag','suptitleText');
set(aux,'Fontsize',opt.suptitle_size,'FontWeight',opt.suptitle_weight,'FontName',opt.suptitle_name);

% LEGEND
aux = findobj(gcf,'Type','axes','Tag','legend');
set(aux,'Fontsize',opt.legend_size, 'FontWeight',opt.legend_weight,'FontName',opt.legend_name);

% Y LABEL
aux = get(gca,'YLabel');
if isempty(aux)~=1
    set(aux,'Fontsize',opt.ylabel_size, 'FontWeight',opt.ylabel_weight,'FontName',opt.ylabel_name);
end

% X LABEL
aux = get(gca,'XLabel');
if isempty(aux)~=1
    set(aux,'Fontsize',opt.xlabel_size, 'FontWeight',opt.xlabel_weight,'FontName',opt.xlabel_name);
end


function msh = meshDefault(host,id,mmPerVox,relaxIter,meshName,decimateReduction)
%
%    msh = meshDefault([host],[id],[mmPerVox],[relaxIter],[meshName])
%
%Author: Wandell/RFD
%Purpose:
%   Return a default mesh structure used by mrVista (and mrMesh).  The
%   various parameters were originally set by RFD.  The structures and
%   access routines were built by BW and are still in progress.
%

if ieNotDefined('host'),host = 'localhost'; end
if ieNotDefined('id'), id = 1; end
if ieNotDefined('mmPerVox'), mmPerVox = [1,1,1]; end
if ieNotDefined('relaxIter'), relaxIter = 0; end
if ieNotDefined('meshName'), meshName = 'untitled'; end
if ieNotDefined('decimateReduction'), decimateReduction = 0.1; end

% These can be passed in.
msh.name = meshName;
msh.fileName = '';
msh.relaxIterations = relaxIter;
msh.mmPerVox = mmPerVox;
msh.host = host;
msh.id = id;
msh.actor  = [];
msh.curvature = [];
% We always save the initial vertices so we can go back to the unsmoothed
% form and so that we can compute the vertex to gray map without
% distortion.
msh.initVertices = [];

% Empty specifications for data.  These are the data slots that mrMesh
% uses.
msh.data.camera_space = [];
msh.data.triangles = [];
msh.data.normals = [];
msh.data.vertices = [];
msh.data.rotation = [];
msh.data.colors = [];
msh.data.origin = [];

% These are hard-wired for now.
msh.grayLayers = 0;
msh.vertexGrayMap = [];

msh.decimate_reduction = decimateReduction;

msh.decimate_iterations = 0;
msh.decimate_subiterations = 0;
msh.decimate_preserve_edges = 0;
msh.decimate_preserve_topology = 1;
msh.decimate_boudary_vertex_deletion = 1;
msh.decimate_aspect_ratio = 25;
msh.decimate_degree = 20;
msh.smooth_sinc_method = 0;

% If you use sinc, smooth_relaxation is the passband parameter. Lower
% values will give more smoothing. Try .05 with 20-40 iterations for light
% smoothing, or .0001 with 150 iterations to fully relax a mesh. The main
% advantage of windowed sinc smoothing over the regular smoothing
% (Laplacian) is that it doesn't shrink the mesh with heavy smoothing.
msh.smooth_iterations = 50;
msh.smooth_relaxation = .25;
msh.smooth_feature_angle = 45;
msh.smooth_edge_angle = 15;
msh.smooth_boundary = 1;
msh.smooth_feature_angle_smoothing = 0;

% This smooth_pre boolean flag param is new. If true, an additional
% smoothing will be applied BEFORE decimation. Currently, it uses the same
% parameters as the post-decimation smoothing. Note that this param only
% applies to the build_mesh, open_gray and open_class commands (NOT smooth).
msh.smooth_pre = 0;
msh.curvature_mod_depth = 0.25;
msh.curvature_color = 1;

msh.conMat = [];
msh.lights = [];

return;


function dt6FileName = dtiRawPreprocessSimple(dwRawFileName, bvalsFile, bvecsFile, t1FileName, dt6BaseName, numBootStrapSamples, dwOutMm)
%
% dtiRawPreprocessSimple([dwRawFileName], [bvalFile], [bvecFile],
%                        [t1FileName], [dt6BaseName='dti'],
%                        [numBootStrapSamples=500], [dwOutMm=[2 2 2]])
%
% A function for running a simple version of the mrDiffusion pre-processing
% steps on raw DWI data. This is similar to dtiRawPreprocess, but skips
% alignment to a t1 anatomical and assumes that you already have
% appropriate bvals/bvecs files.
%
% This assumes that you've already generated the raw NIFTI data file with
% something like dinifti.
%
% HISTORY:
% 2010.11.29 RFD wrote it.
%

% 1 = always clobber existing files, -1 = never replace existing files, just
% load them and use them, 0 = ask the user what to do for each file
clobber = -1;

outSuffix = '_aligned';
excludeVols = [];
bsplineInterpFlag = false;

if(~exist('dwOutMm','var')||isempty(dwOutMm))
    % The eddy-current-corrected, aligned diffusion images will be resampled at
    % 2mm isotropic voxels by default.
    dwOutMm = [2 2 2];
end

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

if(~exist('numBootStrapSamples','var')||isempty(numBootStrapSamples))
  numBootStrapSamples = 500;
end

mrDiffusionDir = fileparts(which('mrDiffusion.m'));

if(~exist('dwRawFileName','var')||isempty(dwRawFileName))
    [f,p] = uigetfile({'*.nii.gz';'*.*'},'Select a raw NIFTI file for input...');
    if(isnumeric(f)), disp('User canceled.'); return; end
    dwRawFileName = fullfile(p,f);
end

if(~exist(dwRawFileName,'file'))
  error(['Couldn''t find diffusion data file "' dwRawFileName '".']);
end

[dataDir,inBaseName] = fileparts(dwRawFileName);
[junk,inBaseName] = fileparts(inBaseName);
if(isempty(dataDir)), dataDir = pwd; end
mnB0Name = fullfile(dataDir,[inBaseName '_b0.nii.gz']);

outBaseName = [inBaseName outSuffix];
outBaseDir = fullfile(dataDir,outBaseName);
inBaseDir = fullfile(dataDir,inBaseName);
% Default output dir is one level above the dataDir. We assume that
% this is the 'subjectDir', which contains the 'raw' data dir.
subjectDir = fileparts(dataDir);
if(isempty(subjectDir)), subjectDir = pwd; end

if(~exist('t1FileName','var')||isempty(t1FileName))
    t1FileName = fullfile(subjectDir,'t1');
    [f,p] = uigetfile('*.nii.gz','Select the t1-file for alignment...',t1FileName);
    if(isnumeric(f)), error('user canceled.'); end
    t1FileName = fullfile(p,f);
elseif(strcmpi(t1FileName,'MNI'))
    t1FileName = fullfile(mrDiffusionDir,'templates','MNI_EPI.nii.gz');
    disp('The MNI EPI template will be used for alignment.');
end
if(~exist(t1FileName,'file'))
    error(['Couldn''t find t1 data file "' t1FileName '".']);
end

%bvalsFile = [inBaseDir '.bval'];
%bvecsFile = [inBaseDir '.bvec'];
if(~exist('bvalsFile','var')||isempty(bvalsFile))
    [f,p] = uigetfile({'*.bval';'*.bvals';'*.*'},'Select the bvals file...');
    if(isnumeric(f)), disp('User canceled.'); return; end
    bvalsFile = fullfile(p,f);
end
if(~exist('bvecsFile','var')||isempty(bvecsFile))
    [f,p] = uigetfile({'*.bvec';'*.bvecs';'*.*'},'Select the bvecs file...');
    if(isnumeric(f)), disp('User canceled.'); return; end
    bvecsFile = fullfile(p,f);
end

ecFile = [inBaseDir '_ecXform.mat'];
acpcFile = [inBaseDir '_acpcXform.mat'];
alignedBvecsFile = [outBaseDir '.bvecs'];
alignedBvalsFile = [outBaseDir '.bvals'];
dwAlignedRawFile = [outBaseDir '.nii.gz'];

disp('loading raw data...');
dwRaw = niftiRead(dwRawFileName);
oldPhaseDim = dwRaw.phase_dim;
% *** FIXME: the canXform might need to be applied to bvecs.
[dwRaw,canXform] = niftiApplyCannonicalXform(dwRaw);
% Make sure there is a valid phase-encode dir, as this is crucial for
% eddy-current correction.

if notDefined('phaseEncodeDir')
    while(dwRaw.phase_dim<1 || dwRaw.phase_dim>3)
        prompt = sprintf('Phase-encode dir is currently %d, but must be 1, 2 or 3. New value:',dwRaw.phase_dim);
        resp = inputdlg(prompt,'Set phase encode direction',1,{'2'});
        if(~isempty(resp))
            dwRaw.phase_dim = round(str2double(resp{1}));
        end
    end
else
    dwRaw.phase_dim=phaseEncodeDir;
end

doResamp = false;

bvecs = dlmread(bvecsFile);
bvals = dlmread(bvalsFile);
goodVols = squeeze(max(max(max(dwRaw.data))))~=0;
% Negative bvals are used to indicate bad volumes that should be skipped
if(any(bvals<0))
    goodVols = goodVols&bvals>0;
end
if(~isempty(excludeVols))
    goodVols(excludeVols) = false;
end
if(~all(goodVols))
  fprintf('Found %d bad volumes in data- removing them from analysis...\n',sum(~goodVols));
  dwRaw.data = dwRaw.data(:,:,:,goodVols);
  bvecs = bvecs(:,goodVols);
  bvals = bvals(goodVols);
  doResamp = true;
else
  if(length(goodVols)<size(bvecs,2))
	warning('mrDiffusion:dimMismatch', 'More bvecs than vols- ignoring some bvecs...');
    bvecs = bvecs(:,goodVols);
  end
  if(length(goodVols)<size(bvals,2))
	warning('mrDiffusion:dimMismatch', 'More bvals than vols- ignoring some bvals...');
    bvals = bvals(goodVols);
  end
end

% Get a mean b0 to be used for eddy-current correction and
% alignment to a structural scan.
if(clobber==1||~exist(mnB0Name,'file'))
  dtiRawComputeMeanB0(dwRaw, bvals, mnB0Name);
else
  if(clobber==0)
    resp = questdlg([mnB0Name ' exists- would you like to overwrite it?'], 'Clobber mnB0', 'Overwrite','Use Existing File','Abort','Use Existing File');
    if(strcmpi(resp,'Abort')), error('User aborted.'); end
    if(strcmpi(resp,'Overwrite'))
      dtiRawComputeMeanB0(dwRaw, bvals, mnB0Name);
    end
  end
end

% Compute the eddy-current correction for all the DWIs (SLOW!)
if(clobber==1||~exist(ecFile,'file'))
    dtiRawRohdeEstimateEddyMotion(dwRaw, mnB0Name, bvals, ecFile);
    doResamp = true;
else
    if(clobber==0)
        resp = questdlg([ecFile ' exists- would you like to overwrite it?'], 'Clobber EddyCorrect', ...
            'Overwrite','Use Existing File','Abort','Use Existing File');
        if(strcmpi(resp,'Abort')), error('User aborted.'); end
        if(strcmpi(resp,'Overwrite'))
            dtiRawRohdeEstimateEddyMotion(dwRaw, mnB0Name, bvals, ecFile);
            doResamp = true;
        end
    end
end

% Compute the dti-structural alignment
if(clobber==1||~exist(acpcFile,'file'))
  dtiRawAlignToT1(mnB0Name, t1FileName, acpcFile);
  doResamp = true;
else
  if(clobber==0)
    resp = questdlg([acpcFile ' exists- would you like to overwrite it?'], 'Clobber AcPc', ...
		    'Overwrite','Use Existing File','Abort','Use Existing File');
    if(strcmpi(resp,'Abort')), error('User aborted.'); end
    if(strcmpi(resp,'Overwrite'))
      dtiRawAlignToT1(mnB0Name, t1FileName, acpcFile);
      doResamp = true;
    end
  end
end

% Resample all the DWIs, applying the dti-to structural xform and
% the eddy-current correction xforms.
if((doResamp&&clobber==-1)||clobber==1||~exist(dwAlignedRawFile,'file'))
  dtiRawResample(dwRaw, ecFile, acpcFile, dwAlignedRawFile, bsplineInterpFlag, dwOutMm);
else
  if(clobber==0)
    resp = questdlg([dwAlignedRawFile ' exists- would you like to overwrite it?'], 'Clobber Resampled Data', ...
		    'Overwrite','Use Existing File','Abort','Use Existing File');
    if(strcmpi(resp,'Abort')), error('User aborted.'); end
    if(strcmpi(resp,'Overwrite'))
      dtiRawResample(dwRaw, ecFile, acpcFile, dwAlignedRawFile, bsplineInterpFlag, dwOutMm);
    end
  end
end

if((doResamp&&clobber==-1)||clobber==1||~exist(alignedBvecsFile,'file')||~exist(alignedBvalsFile,'file'))
    dtiRawReorientBvecs(bvecs, ecFile, acpcFile, alignedBvecsFile);
    %copyfile(bvalsFile, alignedBvalsFile);
	dlmwrite(alignedBvalsFile,bvals,' ');
else
    if(clobber==0)
        resp = questdlg([alignedBvecsFile ' exists- would you like to overwrite it?'], 'Clobber Bvecs/bvals', ...
            'Overwrite','Use Existing File','Abort','Use Existing File');
        if(strcmpi(resp,'Abort')), error('User aborted.'); end
        if(strcmpi(resp,'Overwrite'))
            dtiRawReorientBvecs(bvecsFile, ecFile, acpcFile, alignedBvecsFile);
            dlmwrite(alignedBvalsFile,bvals,' ');
        end
    end
end

clear dwRaw;
dwRawAligned = niftiRead(dwAlignedRawFile);

bs.n = numBootStrapSamples;
% We'll use the non-realigned bvecs since we want to count bvecs that are
% only a little differnt due to motion correction as 'repeats'. Also, we
% can count a direction with just a sign-flip as a 'repeat' since it will
% contain essentially the same diffusion info.
% Note that this code is now used just to compute the number of unique
% diffusion directions. We now use a residual bootstrap, so the repetion
% pattern is no long important for the bootstrap.
[bs.permuteMatrix, nNonRepeats, nUniqueDirs] = dtiBootGetPermMatrix(dlmread(bvecsFile), dlmread(bvalsFile));
% We still need an over-determined tensor fit to do residual bootstrap.
% We'll skip the bootstrap for datasets with fewer than 14 measurements
% (7 is the minimum for tensor fitting).
if(size(dwRawAligned.data,4)<14)
    warning('mrDiffusion:bootstrap','Not enough redundancy in the data- skipping bootstrap.');
    bs.n = 0;
end
bs.showProgress = false;
if(isempty(dt6BaseName))
    dt6BaseName = fullfile(subjectDir,sprintf('dti%02d',nUniqueDirs));
    if(bsplineInterpFlag)
        dt6BaseName = [dt6BaseName 'bs'];
    end
else
    if(isempty(fileparts(dt6BaseName)))
        % Don't allow a relative path
        dt6BaseName = fullfile(subjectDir,dt6BaseName);
    end
end

dt6FileName = dtiRawFitTensorMex(dwRawAligned, alignedBvecsFile, alignedBvalsFile, dt6BaseName, bs);
% Make sure paths are correct
%dtiRawFixDt6File(dt6FileName);

% snCheckIm = dtiComputeSpatialNorm(dt6FileName);
% imwrite(makeMontage(snCheckIm),fullfile(dt6BaseName,'MNIalignmentCheck.png'),'CreationTime',now,'Author','mrDiffusion from Stanford University','Description','b0 with MNI overlay');

dt6 = load(dt6FileName,'files');
files = dt6.files;
% Make the t1 filename relative
k = strfind(t1FileName,dtiGetSubjDirInDT6(dt6FileName));
if isempty(k) || ~exist(t1FileName(k:end),'file')
  [p,t1RelFileName,e] = fileparts(t1FileName);
  t1RelFileName = [t1RelFileName e];
  if(exist(fullfile(subjectDir,'t1'),'dir'))
	t1RelFileName = fullfile('t1',t1RelFileName);
  end
else
  t1RelFileName = t1FileName(k+1+length(dtiGetSubjDirInDT6(dt6FileName)):end);
end
files.t1 = t1RelFileName;
% add the raw data file names (these will be full paths)
files.alignedDwRaw = dwAlignedRawFile;
file.alignedDwBvecs = alignedBvecsFile;
file.alignedDwBvals = alignedBvalsFile;
save(dt6FileName,'files','-APPEND');

[pddT1,xform,mm] = dtiRawCheckTensors(fullfile(dt6BaseName,'bin','tensors.nii.gz'),t1FileName);
pddT1 = flipdim(permute(pddT1,[2 1 3 4]),1);
imSlices = [1:2:size(pddT1,3)];
m = makeMontage3(pddT1,imSlices,mm(1),0,[],[],0);
%imshow(m)
imwrite(m,fullfile(dt6BaseName,'t1pdd.png'),'CreationTime',now,'Author','mrDiffusion from Stanford University','Description','T1 with PDD overlay');

% Setup conTrack directory
fiberDir = fullfile(subjectDir,'fibers');
if(~exist(fiberDir,'dir')); mkdir(fiberDir); end;
conTrackDir = fullfile(fiberDir,'conTrack');
if(~exist(conTrackDir,'dir')); mkdir(conTrackDir); end;
paramsName = fullfile(conTrackDir,'met_params.txt');

% Create conTrack options file if it doesn't exist or we need to clobber
bWriteParams = 0;
if(clobber==1||~exist(paramsName,'file'))
    bWriteParams = 1;
else
    if(clobber==0&&exist(paramsName,'file'))
        resp = questdlg([paramsName ' exists- would you like to overwrite it?'], 'Clobber conTrack params', 'Overwrite','Use Existing File','Abort','Use Existing File');
        if(strcmpi(resp,'Abort')); error('User aborted.'); end
        if(strcmpi(resp,'Overwrite'))
            bWriteParams = 1;
        end
    end
end
if( bWriteParams )
    mtr = mtrCreate();
    mtr = mtrSet(mtr, 'tensors_filename', fullfile(subjectDir,'bin','tensors.nii.gz'));
    mtr = mtrSet(mtr, 'fa_filename', fullfile(subjectDir,'bin','wmMask.nii.gz'));
    mtr = mtrSet(mtr, 'pdf_filename', fullfile(subjectDir,'bin','pddDispersion.nii.gz'));
    mtrSave(mtr,paramsName);
end

return;


function test_bug2741

% WALLTIME 00:10:00
% MEM 2gb
% DEPENDENCY ft_read_cifti ft_write_cifti

cd(dccnpath('/home/common/matlab/fieldtrip/data/test/bug2741'));

cii1 = ft_read_cifti('zstat1.dtseries.nii', 'cortexleft', '100307.L.midthickness.32k_fs_LR.surf.gii', 'cortexright', '100307.R.midthickness.32k_fs_LR.surf.gii');

ft_write_cifti('zstat2', cii1, 'parameter', 'dtseries');

cii2 = ft_read_cifti('zstat2.dtseries.nii'); % surfaces will be read automatically

cii1 = rmfield(cii1, 'hdr');
cii2 = rmfield(cii2, 'hdr');

assert(isequaln(cii1, cii2));


function roiColorRgba = dtiRoiGetColor(roi,alpha)
%
%  roiColorRgba = dtiRoiGetColor(roi,[defaultAlpha=0.75])
%
% If the roi does not specify an alpha channel (opacity), then defaultAlpha
% is appended to the rgb triplet.
%
% HISTORY:
% 2006.08.18 RFD: extracted from dtiSaveImageSlicesOverlay.
% 2006.11.06 RFD: added csome comments about alpha.

% Default transparency (alpha) channel
if ~exist('alpha','var'), alpha = 0.75; end

if(isnumeric(roi.color))
  roiColorRgba = roi.color(:)';
  if(length(roiColorRgba)==3) roiColorRgba(4) = alpha;
  elseif(length(roiColorRgba)~=4) error('ROI color must be 1x3 or 1x4!'); end
else
  switch(roi.color)
   case 'r'
    roiColorRgba = [1 0 0 alpha];
   case 'g'
    roiColorRgba = [0 1 0 alpha];
   case 'b'
    roiColorRgba = [0 0 1 alpha];
   case 'm'
    roiColorRgba = [1 0 1 alpha];
   case 'w'
    roiColorRgba = [1 1 1 alpha];
   case 'y'
    roiColorRgba = [1 1 0 alpha];
   case 'c'
    roiColorRgba = [0 1 1 alpha];
   case 'k'
    roiColorRgba = [0 0 0 alpha];
   otherwise
    error('Unknown ROI color');
  end
end

return;


function xy_io_test ( )

%*****************************************************************************80
%
%% XY_IO_TEST tests the XY_IO library.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    08 January 2009
%
%  Author:
%
%    John Burkardt
%
  timestamp ( );
  fprintf ( 1, '\n' );
  fprintf ( 1, 'XY_IO_TEST\n' );
  fprintf ( 1, '  MATLAB version\n' );
  fprintf ( 1, '  Test the XY_IO library.\n' );

  xy_io_test01 ( );
  xy_io_test02 ( );
  xy_io_test03 ( );
  xy_io_test04 ( );
  xy_io_test05 ( );
  xy_io_test06 ( );
%
%  Terminate.
%
  fprintf ( 1, '\n' );
  fprintf ( 1, 'XY_IO_TEST\n' );
  fprintf ( 1, '  Normal end of execution.\n' );
  fprintf ( 1, '\n' );
  timestamp ( );

  return
end


function fg = dtiCreateQuenchStats(fg, statFiberName, statPointName, perPoint, img, fiberSummary, pointCompute,mpRange)
%
% fg = dtiCreateQuenchStats(fg, [statFiberName='avg'], [statPointName='fa'],...
%     [perPoint=1], img, [fiberSummary='avg'],[pointCompute='none']);
%
% Add satistics to a fiber group that are used for Quench visualization and
% queries. Every path gets a number (its statistic) for that path. Some
% paths also have statistics per point along the path (e.g. in the array
% condition). They are stored in the returned fiber group struct.
%
% INPUTS:
%  fg            - Fiber Group. The statistics will be attached to this
%                  structure.
%  statFiberName - Name for a fiber summary statistic. Typically this
%                  will be  something like length or "average FA". THis
%                  statistic is used to limit the range of fibers we see
%                  in the Quench window.
%  statPointName - Name for the per point statistic (e.g., TF, FA,
%                  eccentricity)
%  perPoint      - 1 if computed per point, 0 if only per fiber
%  img           - Data for statistical overlay. The data form is either a
%                  nifti image structure or dti image structure.
%                  If no data are supplied the routine computes a default
%                  statistic determined by the statFiberName. At present,
%                  the only statFiberName that generates a statistic is
%                  'length'.
%  fiberSummary  - If data are supplied, this variable specifies
%                  which statFiber statistic to compute. We currently
%                  support can be 'avg', 'min','max'.  This fiber statistic
%
%  pointCompute  - Method for taking per point color and remapping to new
%                  per point colors.  Either 'none' or 'matchEnd' are in
%                  place.  More methods will arise.  The matchEnd is a
%                  hack.
%
% WEB:
%  mrvBrowseSVN('dtiCreateQuenchStats');
%
%  This function computes output parameters pathStat and point_stat_array.
%  The max and min of pathStat are used to set the range for colormaps etc.
%  in Quench.
%
%Delete this comment soon ---
% Statistics can be one of three types:
%   1) geometry - path length, curvature
%   2) image    - FA value, other pointwise statistic (e.g., eccentricity)
%   3) array    - An array of vectors. each the same length as a
%      corresponding path in the fiber group, and values of the vectors are
%      assigned to each position along the paths.
%
% EXAMPLES:
%
%   Image Stat:
%       fg = mtrImportFibers('fibers.pdb');
%       fa_img = niftiRead('faMap.nii.gz');
%       fg = dtiCreateQuenchStats(fg,'FA_avg','FA', 1, fa_img, 'avg');
%     OR...
%       h = guidata(gcf); fa_img = h.bg(1,2,3 or 5);
%       fg = dtiCreateQuenchStats(fg,'FA_avg','FA', 1, fa_img, 'avg');
%
%   Given Array Stat
%       fg = dtiCreateQuenchStats(fg,'Group',stat_array);
%
%   Geometric Stat
%       fg = dtiCreateQuenchStats(fg,'Length','Length', 1);
%
%
% HISTORY:
% 2009.06.17 : SA wrote it
% 2009.08.04 : AJS fixed the mixing between explicit declaration of
% perPoint and the implicit assumption in most of the code.
% Now we truly have the variable report explicit info.  Also added the
% ability to have a statistic be added by passing the array of per pathway
% stats that you would like to have.
% 2009.10.19 : AJS significantly sped up the code by using cellfun.
%

%% Check if input params are ok
if notDefined('fg') || ~isstruct(fg)
    disp('fg should be a proper fibergroup struct');
    return;
end

if notDefined('statFiberName'), statFiberName = 'avg'; end
if notDefined('statPointName'), statPointName = 'fa'; end
if notDefined('perPoint'), perPoint = 1; end
if notDefined('img')
    if ~strcmpi(statFiberName,'length')
        error('Data for overlay are required');
    end
end
if notDefined('fiberSummary'), fiberSummary = 'avg'; end
if notDefined('pointCompute'), pointCompute = 'none'; end

if ~isfield(fg,'pathwayInfo'), fg.pathwayInfo=[];  end

% Indices into the number of statistical images. Quench counts from 0.  We
% start with Matlab counting from 1, and then we subtract 1 later when we
% do the Quench parameter field.
if ~isfield(fg,'params') || isempty(fg.params),  idx = 1;
else                                             idx = length(fg.params)+1;
end

% If there are only three input variables, the third must be a data set,
% not a name.  So we copy the data set to the statistics array, we set the
% name variable to not applicable, and we do not compute per point.
% Instead, we will be computing per fiber.
% if nargin == 3
%     stat_array = statPointName;
%     statPointName = 'NA';
%     perPoint = 0;
% end

%%
% This statistics parameter field gets attached to the fg.  It contains
% information that Quench uses for visualization and for dynamic queries.
% We should probably write a function for initializing this parameter
% field.
param = struct;              % Initialize structure
param.name = statFiberName;  % Struct name?
param.uid = idx-1;           % Subtract 1 so Quench starts from 0 (C-counting)
param.ile = 1;
param.icpp= perPoint;        % Visualization per point, not per fiber
param.ivs = 1;               %
param.agg = statFiberName;   % A stats name
param.lname = statPointName; % Local Name
param.stat = [];

%dummy.  Same to you.
% c        = struct;
% c.fibers = cell(1);

if notDefined('img')
    % No overlay data, so we use the statFiberName to determine how we color
    % the fibers.

    switch lower(statFiberName)
        case 'length'
            % For each fiber, figure out its length.
            for ii=1:length(fg.fibers)
                fiber = fg.fibers{ii};      % Get the ith fiber.
                %if(size(fiber,2)<size(fiber,1)); fiber=fiber'; end

                % Find the step size - could be be moved out of the loop.  Could
                % use cellfun to find all these.  Mostly these are the same.
                mmPerStep  = sqrt(sum((fiber(:,1)-fiber(:,2)).^2));

                % Find the length for the whole fiber
                param.stat(ii) = mmPerStep*(size(fiber,2)-1);

                % Store the length for this stat image (idx) and the ith pathway.
                % Each fiber group has a pathway info that summarizes all of the
                % stats in stat images.
                fg.pathwayInfo(ii).pathStat(idx) = param.stat(ii);

                % If we are computing per point, then we compute the length to each
                % point on the fiber, so that we don't assign a single length.
                % Rather we assign a length from the end point to each point along
                % the path.  And we put that into the point_stat_array.  Up to now,
                % we had a single length assigned to all of the fibers.
                if perPoint
                    fg.pathwayInfo(ii).point_stat_array(idx,:) = mmPerStep * (0:1:size(fiber,2)-1);
                end
            end
        otherwise
            error('Unknown statFiberName %s\n',statFiberName);
    end

else

    %isNiftiImg = NaN; %mat =[]; data=[];
    minVal=0; maxVal = 1;

    % Test whether the img variable is a nifti file structure or a dti file
    % structure.
    if isNiftiStruct(img);
        % NIFTI land.  Get what you need.
        isNiftiImg = true;
        mat  = img.qto_ijk;
        data = img.data;
    else
        isNiftiImg = false;
        if isfield(img,'img') && isfield(img,'mat')
            % It has the dti fields. So we copy the
            % fields we need and set isNifti to false.
            mat  = inv(img.mat);
            data = img.img;
            if isfield(img,'minVal'),  minVal = img.minVal; end
            if isfield(img,'maxVal'),  maxVal = img.maxVal; end
        else
            error('img must be either nifti or dti');
        end
    end

    % Interpolate from the values in the statistics to the position of the
    % fiber nodes.  For speed, we use nearest neighbor interpolation
    vals = dtiGetValFromFibers(data, fg, mat,[],'nearest');

    % Apply the scaling function function to each entry of vals. This will
    % take a 0,1 range and put it with the scale specified by max and min
    % vals.
    % 'UniformOutput' insists that the return must be a cell array.
    if ~isNiftiImg
        vals = cellfun(@(x) x*(maxVal-minVal)+minVal, vals, 'UniformOutput', false);
    end

    % For each of the cell arrays, we can determine a single value that
    % will be applied to the whole fiber.  This might be the avg, min or
    % max. We compute this value and store it as a single scalar here in
    % the stat field.  The stat field should have one scalar for every
    % fiber in the fiber group.  This is for the non per-point calculation.
    %
    switch(lower(fiberSummary))
        case 'avg'
            param.stat = cellfun(@mean,vals);
        case 'min'
            param.stat = cellfun(@min,vals);
        case 'max'
            param.stat = cellfun(@max,vals);
        otherwise
            error('Unknown fiberSummary: %s\n',fiberSummary);

    end

    % In principle, we could write point_stat_array into param.statPoint
    % and the other could be param.statFiber.  Leave it for now, think
    % later.

    % Copy the statistics to the fg pathwayInfo location.  The stat field
    % is for plotting for a whole fiber, and the per point is for the per
    % point case (not always set).
    for ii=1:length(fg.fibers)
        fg.pathwayInfo(ii).pathStat(idx)=param.stat(ii);

        if perPoint
            % We can write algorithms that take the vals, smooth them, use
            % the max, whatever, to create the point coloring.  This is how
            % we will get perFiber coloring.
            switch lower(pointCompute)
                case 'none'
                    fg.pathwayInfo(ii).point_stat_array(idx,:) = vals{ii}(:);
                case 'matchend'
                    tmp = vals{ii}(:);
                    last = min(length(tmp),10);
                    foo = tmp(1:last);
                    foo = foo(foo ~= 0);
                    if ~isempty(foo)
                        tmp(:) = max(foo(:));
                    else
                        tmp(:) = 0;
                    end
                    fg.pathwayInfo(ii).point_stat_array(idx,:) = tmp;
                otherwise
                    error('Unknown pointCompute method %s\n',pointCompute);
            end
        end
    end

end
%This is a hack to edit the range of the colorbar display in Quench. This
%will only be a problem if you clean fibers with the refine selection tool
%in quench
if ~notDefined('mpRange')
    fg.pathwayInfo(1).pathStat(idx)=mpRange(1);
    fg.pathwayInfo(2).pathStat(idx)=mpRange(2);
    param.stat(1)=mpRange(1);
    param.stat(2)=mpRange(2);
end

fg.params{idx} = param;

end

%% Make this better over time
function isNifti = isNiftiStruct(img)

isNifti = true;
if ~isfield(img,'qto_ijk') || ~isfield(img,'data')
    isNifti = false;
end

end


function uvMat = sc_uvMat_from_uvMap(map, uvPixInd)

% Function: conver uvMap into matrix form

% Extract uv transform from the NNF
[imgH, imgW, nCh] = size(map);

offset = uint32((0:nCh-1)*imgH*imgW);
uvPixInd = bsxfun(@plus, uvPixInd, offset);
uvMat = map(uvPixInd);

% map(uvPixInd) = data;
%
%
%
% uvMat = zeros(nCh, numUvPixels, 'single');
%
% for i = 1: nCh
%     uvMat(i,:) = map(uvPix.ind + (i-1)*imgH*imgW);
% end

% Indices of valid uv pixels
% uvValid.ind = uvMapMask(uvPix.ind);
% uvValid.pos = find(uvValid.ind);

end


function [varargout]=patchCleanUnused(F,V)

% function [Fc,Vc,indFix2,logicValid,indUni]=patchCleanUnused(F,V)
%-------------------------------------------------------------------------
%
%
% 2010 Created
% 2019/03/04 Added basic description header to function
% 2019/04/22 Added handling of cell input
%-------------------------------------------------------------------------

%%

if isa(F,'cell')
    indUni=[];
    for q=1:1:numel(F)
        f=F{q};
        indUni=[indUni;f(:)];
    end
else
    indUni=unique(F(:));
end
indUni=unique(indUni(~isnan(indUni)));

%%

numPointsOriginal=size(V,1); %Number of original points
numPointsNew=numel(indUni); %Number of points in new set
Vc=V(indUni,:); %Select relevant points

%Fix indices in faces matrix
indFix1=1:1:numPointsNew;
indFix2=zeros(numPointsOriginal,1);
indFix2(indUni)=indFix1;

Fc=F;
if isa(F,'cell')
    for q=1:1:numel(F)
        fc=F{q};
        logicValid=~isnan(fc);
        fc(logicValid)=indFix2(fc(logicValid));
        Fc{q}=fc;
    end
else
    logicValid=~isnan(F);
    Fc(logicValid)=indFix2(F(logicValid));
end

%Output
varargout{1}=Fc;
varargout{2}=Vc;
varargout{3}=indFix2;
varargout{4}=logicValid;
varargout{5}=indUni;

%%
% _*GIBBON footer text*_
%
% License: <https://github.com/gibbonCode/GIBBON/blob/master/LICENSE>
%
% GIBBON: The Geometry and Image-based Bioengineering add-On. A toolbox for
% image segmentation, image-based modeling, meshing, and finite element
% analysis.
%
% Copyright (C) 2006-2022 Kevin Mattheus Moerman and the GIBBON contributors
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.


% GPSIMTOOLBOXES Toolboxes for the GPSIM software.

importLatest('netlab');
importLatest('minimize');
importLatest('gpmat');


function [h, class] = LMplot(varargin)
% Visualizes the polygons in an image.
%
% LMplot(annotation, img)
% or
% LMplot(database, ndx, HOMEIMAGES)
%
% Example:
%   [annotation, img] = LMread(filename, HOMEIMAGES)
%   LMplot(annotation, img)
%
%   thumbnail = size of the image
%   The plot uses only 7 colors, therefore, some times, different object.
%   Classes might have the same color assigned.
%
% If an object has the field 'confidence', the thickness of the bounding
% box will be equal to the confidence.
%
% If the object has the filed 'detection', then when the state is 'false'
% it will show the outline in green, and when the state is 'correct' it
% will show the outline in red.

switch length(varargin)
   case 1
       annotation = varargin{1};
       img = -1;
   case 2
       annotation = varargin{1};
       img = varargin{2};
   case 3
       D = varargin{1};
       ndx = varargin{2};

       if ndx>length(D)
           return
       end

       HOMEIMAGES = varargin{3};

       annotation = D(ndx).annotation;
       %img = imread(fullfile(HOMEIMAGES, annotation.folder, annotation.filename));
       img = LMimread(D, ndx, HOMEIMAGES); % Load image
   otherwise
       error('Too many input arguments.')
end

% Define colors
colors = 'rgbcmyw';
colors = hsv(15);

% Draw image
%figure;
if size(img,1)>1
    if isfloat(img)
        img = img-min(img(:));
        img = 255*img/max(img(:));
    end
    image(uint8(img)); axis('off'); axis('equal'); hold on
    if size(img,3) == 1
        colormap(gray(256))
    end
else
    axis('off'); axis('equal'); axis('ij'); hold on
end

% Draw each object (only non deleted ones)
h = []; class = [];
if isfield(annotation, 'object')
   Nobjects = length(annotation.object); n=0;
   for i = 1:Nobjects
       n = n+1;
       class{n} = annotation.object(i).name; % get object name
       col = colors(mod(sum(double(class{n})),15)+1, :);
       [X,Y] = getLMpolygon(annotation.object(i).polygon);
       X = double(X(:)); Y = double(Y(:));

       LineWidth = 4;
       if isfield(annotation.object(i), 'parts') && isfield(annotation.object(i).parts, 'ispartof')
           if ~isempty(annotation.object(i).parts.ispartof)
               class{n} = [class{n} ' (part)'];
               LineWidth = 1.5;
           end
       end

       if isfield(annotation.object(i), 'confidence')
           p = annotation.object(i).confidence;
           class{n} = [class{n} sprintf('[%2.2f]',p)];

           LineWidth = round(annotation.object(i).confidence);
           LineWidth = min(max([2 LineWidth]), 8);

           if isfield(annotation.object(i), 'detection')
               if strcmp(annotation.object(i).detection, 'false')
                   LineWidth = 1; %col = 'g';
               else
                   LineWidth = 4; %col = 'r';
               end
           end

           dim = get(gca,'position');
           if dim(3)>.5
               % If the plot occupies the entire figure, then we can plot the
               % confidence as a string.
               hold on
               xx = mean(X);
               %yy = mean(Y);
               yy = min(Y);
               ht=text(xx,yy, ...
                   sprintf('%5.2f', annotation.object(i).confidence), ...
                   'horizontalAlignment', 'center', 'verticalAlignment', 'bottom');
               %sprintf('%d %5.3f', i, annotation.object(i).confidence), ...
               %'horizontalAlignment', 'center', 'verticalAlignment', 'bottom');

               set(ht, 'color', col, 'fontsize', 10);
           else
               xx = mean(X);
               %yy = mean(Y);
               yy = min(Y);
               ht=text(xx,yy, class{n}, 'horizontalAlignment', 'center', 'verticalAlignment', 'bottom');
               set(ht, 'color', col, 'fontsize', 10);
           end
       end

       plot([X; X(1)],[Y; Y(1)], 'LineWidth', LineWidth, 'color', [0 0 0]); hold on
       h(n) = plot([X; X(1)],[Y; Y(1)], 'LineWidth', LineWidth/2, 'color', col);
       hold on
   end

   if nargout == 0
       legend(h, class);
   end
   %drawnow
end


function imgs = modcrop(imgs, modulo)

for i = 1:numel(imgs)
    sz = size(imgs{i});
    sz = sz - mod(sz, modulo);
    imgs{i} = imgs{i}(1:sz(1), 1:sz(2));
end


function [objout] = convm2java(objin)

if iscell(objin),
    objin = cell2mat(objin);
end

if isnumeric(objin)
   objout = m2j(objin);
elseif isstruct(objin)
    fields = fieldnames(objin);
    vals = struct2cell(objin);
    [x, y, k] = size(objin);
    objout = matlab.jmstruct.JMStruct;
    objout.setFields(fields);
    objout.setDimX(x);
    objout.setDimY(y);
    for I=1:numel(vals)
        tmp = convm2java(vals(I));
        vals(I) = tmp;
    end
    objout.setValues(vals);
else
    objout = cellstr(objin);
end;
return;

function [jarray] = m2j(marray)
    jarray = matlab.jmarray.JMArray;
    [x, y, k] = size(marray);
    jarray.setDimX(x);
    jarray.setDimY(y);
    jarray.setRealpart(real(marray));
    if isreal(marray),
        jarray.setReal(true);
    else
        jarray.setReal(false);
        jarray.setImagpart(imag(marray));
    end
return;


function SO3F = smooth(SO3F,varargin)
% smooth SO3FunComposition
%
% Input
%  SO3F - @SO3FunComposition
%  psi - @SO3Kernel (smoothing kernel)
%
% Options
%  halfwidth
%
% Output
%  SO3F - smoothed @SO3Fun
%
% See also
% SO3Fun/smooth

% smooth components
for i = 1:length(SO3F.components)
  SO3F.components{i} = SO3F.components{i}.smooth(varargin{:});
end

end