function [M, movie] = tSeriesMovie(vw, scans, anatFlag, varargin)
% [M movie] = tSeriesMovie(vw, scans, anatFlag, [options]):
%
% Make a movie of the selected tSeries, using the current view's
% settings, and play using mplay viewer. Intended as a replacement
% for makeTSeriesMovie.
%
% NOTE: The code auto-resizes the movie to fit the screen nicely. Although
% this is a benefit, it usually causes the lower line of the mplay tool to
% turn black; this line contains some useful information like the current
% frame and image size. To get this info back, press the second button
% from the left ("truesize") in the movie window's toolbar.
%
% vw: the view struct. Currently supports inplane and flat level views.
%
% scans: set of scans to view from current data type.
%
% anatFlag: if 1, will threshold the tSeries and superimpose
% the parts above threshold on top of the view's anat image.
% Defaults to 0.
%
% Returns M, a matrix of the tSeries, and movie, an object-
% oriented struct generated by mplay used for programmatic
% control of the movie. If anatFlag is 0, M is size
% x by y by nFrames, where x and y are the view's slice dims.
% If anatFlag is 1, M is a 4-D array of size x by y by 3 by nFrames,
% where each 3-D subvolume is a truecolor image.
%
% Options:  [case-insensitive]
%   slices, [val]:          set slice numbers (defaults to current slice).
%                           WARNING: >1 slice can be very memory-hungry
%   zoom, [val]:            set zoom area (as with axis command)
%   detrend, [val]:         set detrend flag (see detrendTSeries for vals)
%   convertToPct,[flag]:    if 1, will convert to % signal change.
%   applyHisto,[flag]:      flag to apply standard histogram criterion (on
%                           by default) in which the tails of the intensity
%                           histogram are clipped.
%   compareFrames,[frame]:  if nonzero, will make a truecolor move in which
%                           the red channel is the tSeries, and the
%                           blue-green channel is a reference frame --
%                           e.g., if 1, the first frame of the tSeries
%   meanThresh,[val]:       set threshold for raw functionals -- any parts
%                           of the tSeries below this value (normalized
%                           from 0 to 1 --> min to max of tSeries) will
%                           be zeroed.
%   funcClip,[val]:         set clip values for functionals, if
%                           superimposing tSeries on anatomicals
%                           (i.e., anatFlag = 1). Values outside
%                           this range won't be overlaid. Default [0.2 1]
%   fps,[val]:              Set default frames per second of playback
%                           in the movie player.
%   saveAvi,[path]:         export the tSeries movie to an .AVI file
%                           in the specified path. If path is set to
%                           'dialog', will pop up a dialog to interactively
%                           set the target file. Note that because of
%                           codecs, exporting on Windows produces smaller
%                           files than on linux (at least in my
%                           experience). Uses movie2avi.
%
%
% More info on the mplay movie player for matlab:
%
% http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=3250&objectType=file
%
% Thanks Don Orofino!
%
% ras 12/04: wrote it.
% ras 03/05: added avi export.
% ras 11/06: added multiple slices option.
if notDefined('scans'),    scans = viewGet(vw, 'curScan'); end
if notDefined('anatFlag'), anatFlag = 0;                   end

%%%%% default params
slices = viewGet(vw, 'curSlice');
sDims  = viewGet(vw, 'sliceDims',scans(1));
if checkfields(vw,'ui','zoom')
    zoom = vw.ui.zoom;
    rsFactor = upSampleFactor(vw,scans(1));
    zoom(3:4) = zoom(3:4) ./ rsFactor(1);
    zoom(1:2) = zoom(1:2) ./ rsFactor(2);
    zoom = round(zoom);
    zoom(zoom==0) = 1;
else
    zoom = [1 sDims(2) 1 sDims(1)];
end
detrend         = 0;
convertToPct    = 0;
applyHisto      = 0; % use histogram-based thresholding like makeTSeriesMovie
compareFrame    = 0; % compare to a reference frame (if 0, don't compare)
meanThresh      = 0; % normalized raw intensity threshold
funcClip        = [0.2 1]; % clipping values for overlaying functionals, if anatFlag==1
autoCmap        = 1; % flag to use the full cmap range (if anatFlag==1)
movieFlag       = 1; % call the mplay GUI
fps             = 15;
saveAvi         = '';
viewType        = viewGet(vw,'viewType');
frames       = true; % true = use all frames from each scan; false = use 1st / last frame only

%%%%% parse the option flags
opts = unNestCell(varargin);  % to enable recursive passing of options
for i = 1:length(opts)
    if ischar(opts{i})
        switch lower(opts{i})
            case 'slices',          slices      = opts{i+1};
            case 'zoom',            zoom        = opts{i+1};
            case 'detrend',         detrend     = opts{i+1};
            case 'converttopct',    convertToPct= opts{i+1};
            case 'applyhisto',      applyHisto  = opts{i+1};
            case 'compareframes',   compareFrame= opts{i+1};
            case 'meanthresh',      meanThresh  = opts{i+1};
            case 'funcclip',        funcClip    = opts{i+1};
            case 'fps',             fps         = opts{i+1};
            case 'saveavi',         saveAvi     = opts{i+1};
            case 'frames',          frames      = opts{i+1};
            case 'autocmap',        autoCmap    = opts{i+1};
            case 'nomovie',         movieFlag   = 0;
        end
    end
end

%%%%% check view type
if isequal(viewType,'Flat')
    [M, movie] = flatLevelMovie(vw,scans,saveAvi);
    return
elseif isequal(viewType,'Volume') || isequal(viewType,'Gray')
    error('Volume/Gray support not provided yet.')
end

%%%%% init M intensity array
M = [];

%%%%% if >1 slices, run recursively
if length(slices) > 1
    [M, movie] = tSeriesMovieSlices(vw, scans, slices, anatFlag, opts);
    return
end

%%%%% get tSeries from selected scans
nFrames = 0;
hwait = mrvWaitbar(0,'Loading tSeries for movie...');
for scan = scans
    tSeries = loadtSeries(vw,scan,slices(1));
    if frames,  % get just the requested frames, throw out the rest
        % make sure we don't try to get more frames than exist (could
        % happen if the number of frames differs across scans)
        validframes = frames <= viewGet(vw, 'nframes', scan);
        % now restrict the tseries
        tSeries = tSeries(frames(validframes), :);
    end

    % update frame count
    nFrames = nFrames + size(tSeries,1);

    % detrend if selected
    if detrend ~= 0
        tSeries = detrendTSeries(tSeries, detrend, 20);
    end

    % append to M
    M = [M; tSeries];

    mrvWaitbar(find(scans==scan)/length(scans),hwait);
end
close(hwait);

%%%%% apply histogram criterion
if applyHisto==1
    M = histoThresh(M);
end

% if a mean intensity threshold is set, mask out stuff
% that doesn't have strong mean signal
if meanThresh > 0
    meanImg = mean(M,1);
    meanImg = rescale2(meanImg,[],[0 1000]);
    mask = (meanImg > 1000*meanThresh);
end

% convert to pct if selected
if convertToPct==1
    M = M ./ repmat(mean(M,1),[nFrames 1]);
end

% now apply mask if selected
if meanThresh > 0
    mask = repmat(mask,[nFrames 1]);
    M(mask==0) = min(M(:))-1;
end

%%%%% reshape and apply zoom
M = reshape(M', [sDims(1) sDims(2) nFrames]);
M = M(zoom(3):zoom(4),zoom(1):zoom(2),:);

%%%%% if anat flag set, convert M to 4D truecolor x time array
if anatFlag==1
	hwait = mrvWaitbar(0,'Superimposing over anat image...');

    % get cmap from map mode
    modeInfo    = viewGet(vw,'mapMode');
    numColors   = modeInfo.numColors;
    numGrays    = modeInfo.numGrays;
    cmap        = 255 .* modeInfo.cmap(numGrays+1:end,:);

    % make an overlay, indexing into the rows of cmap
    clipVals = min(M(:)) + funcClip .* (max(M(:))-min(M(:)));
    overlay  = uint8(rescale2(M, clipVals, [0 numColors]));
    if ~isequal(dataSize(vw,1),viewGet(vw,'Size'))

        % adjust zoom to be of view size again, not data size
        rsFactor  = upSampleFactor(vw,scans(1));
        zoom(3:4) = zoom(3:4) .* rsFactor(1);
        zoom(1:2) = zoom(1:2) .* rsFactor(2);

        % now up-sample the overlay to match the view resolution
        newSize = [zoom(4)-zoom(3) zoom(2)-zoom(1) nFrames];
        overlay = upSampleRep(overlay,newSize);

    end
    mrvWaitbar(1/4,hwait);

    % re-initialize M as a 4D array w/ the anat background
    anatImg = recomputeAnatImage(vw);
    anatImg = anatImg(zoom(3):zoom(4),zoom(1):zoom(2));
    anatImg = normalize(anatImg,0,255);
    M       = uint8(repmat(anatImg,[1 1 nFrames 3]));

    mrvWaitbar(1/2,hwait);

    % get the values for which the overlay > 0
    locs3D = find(overlay>0);
    [yy, xx, zz] = ind2sub(size(overlay),locs3D);

    % auto-scale the colormap
    if autoCmap==1
        overlay(locs3D) = rescale2(overlay(locs3D),[],[1 numColors]);
    end

    % for each color channel, plug in the appropriate
    % values from the cmap into the overlay locations
    for col = 1:3
        locs4D      = sub2ind(size(M),yy,xx,zz,repmat(col,size(yy)));
        M(locs4D)   = cmap(overlay(locs3D),col);
        mrvWaitbar(1/2+col/6,hwait);
    end
    close(hwait);

    % mplay likes the 4D truecolor array rows x cols x 3 x frames,
    % so permute to this order:
    M = permute(M,[1 2 4 3]);
elseif compareFrame > 0
    % make an R G B movie in which the red channel is the tSeries,
    % and the G,B channels are the reference frame
    ref = repmat(M(:,:,compareFrame), [1 1 nFrames 2]);
    M = cat(4, M, ref);
    M = permute(M, [1 2 4 3]);
    M = uint8(rescale2(M,[],[0 2^8-1]));
else
    % just reduce it to a uint8, for the purpose of rendering
    % (seems tricky to find a way around this bottleneck):
    M = uint8(rescale2(M,[],[0 2^8-1]));
end

%%%%% plug into mplayer
if movieFlag==1
    global mrSESSION dataTYPES;
    movie = implay(M,fps);

    ttltxt = sprintf('Movie: %s %s %s, Slice %i', ...
                        mrSESSION.sessionCode, ...
                        dataTYPES(vw.curDataType).name, ...
                        num2str(scans), slices(1));
    %set(get(movie,'hfig'),'Name',ttltxt);

    % play the movie
    %movie.play
end

%%%%% export to AVI if selected
if ~isempty(saveAvi)
    if isequal(saveAvi,'dialog')
        % put up a dialog to get path
        [f p] = uiputfile('*.avi','Save .avi movie as...');
        saveAvi = fullfile(p,f);
    end

    % check for .avi extension
    [~, p, ext] = fileparts(saveAvi);
    if ~isequal(lower(ext),'.avi')
        saveAvi = [saveAvi '.avi'];
    end

    % construct a matlab movie struct for the export
    mov = repmat(struct('cdata',[],'colormap',[]),1,nFrames);
    for frame = 1:nFrames
        if ndims(M)==4
            mov(frame).cdata = M(:,:,:,frame);
        else
            mov(frame).cdata = repmat(M(:,:,frame),[1 1 3]);
        end
    end

    % now export the movie
    if ispc
        codec = 'Indeo3';
    else
        codec = 'None';
    end
    movie2avi(mov,saveAvi,'Compression',codec,'Quality',100,'FPS',fps);
    fprintf('Exported movie to %s.\n',saveAvi);
end

return
% /-------------------------------------------------------------------/ %

% /-------------------------------------------------------------------/ %
function [M, movie] = tSeriesMovieSlices(vw, scans, slices, anatFlag, varargin)
% code to recursively call the main function (tSeriesMovie)
% for each slice, and assemble a montage across slices.
% Can be very memory-intensive.
M = []; movie = [];

%%%%% params
movieFlag = 1; % call the mplay GUI
fps = 15;
saveAvi = '';

% remove the 'slices' specification for recursion
opts = unNestCell(varargin);
% for i=1:length(opts), opts{i} = lower(opts{i}); end
ii = cellfind(opts, 'slices');
ok = setdiff(1:length(opts), [ii ii+1]);
opts = opts(ok);

% parse movie options
for i = 1:length(opts)
    if ischar(opts{i})
        switch lower(opts{i})
            case 'fps', fps = opts{i+1};
            case 'saveavi',
				% if there's a flag to save an .AVI file, we only want to
				% do that saving here, not in the recursive call to
				% tSeriesMovie below. So, mark it here, but removeit from
				% the options list
				saveAvi = opts{i+1};
				opts{i} = []; opts{i+1} = [];
            case 'nomovie', movieFlag = 0;
        end
    end
end

% get movie data for each slice
hwait = mrvWaitbar(0, 'Creating Montage Across Slices...');
set(hwait, 'Position', get(hwait, 'Position')+[0 100 0 0]);

nSlices = length(slices);
for i = 1:nSlices
    slice{i} = tSeriesMovie(vw, scans, anatFlag, 'slices', slices(i), ...
                            'nomovie', opts);
    mrvWaitbar(.8 * (i/nSlices), hwait);
end

%%%%%% make montage
nRows = ceil(sqrt(nSlices));
nCols = ceil(nSlices/nRows);
if nSlices < nRows*nCols    % pad out extra images
    for i = nSlices+1:nRows*nCols
        slice{i} = zeros(size(slice{1}));
    end
end
slice = reshape(slice, [nCols nRows])';

mrvWaitbar(.95, hwait);

for r = 1:nRows
    row = [];
    for c = 1:nCols
        row = cat(2, row, slice{1,c});
    end

    % be memory-efficient: clear the row we just grabbed
    slice = slice(2:end,:);

    M = cat(1, M, row);
end

close(hwait);

%%%%% show movie if selected
if movieFlag==1
    global mrSESSION dataTYPES;
    movie = mplay(M,fps);
    ttltxt = sprintf( 'Movie: %s %s %s, Slices %s', ...
                       mrSESSION.sessionCode, ...
                       dataTYPES(vw.curDataType).name, ...
                       num2str(scans), num2str(slices) );
    set(get(movie,'hfig'), 'Name', ttltxt);
end

%%%%% export to AVI if selected
if ~isempty(saveAvi)
    if isequal(saveAvi,'dialog')
        % put up a dialog to get path
        [f p] = uiputfile('*.avi','Save .avi movie as...');
        saveAvi = fullfile(p,f);
    end

    % check for .avi extension
    [f p ext] = fileparts(saveAvi);
    if ~isequal(lower(ext),'.avi')
        saveAvi = [saveAvi '.avi'];
    end

    % construct a matlab movie struct for the export
    nFrames = size(M, ndims(M));
    mov = repmat( struct('cdata',[], 'colormap',[]), 1, nFrames );
    for frame = 1:nFrames
        if ndims(M)==4
            mov(frame).cdata = M(:,:,:,frame);
        else
            mov(frame).cdata = repmat(M(:,:,frame),[1 1 3]);
        end
	end

	% allow the movie path to refer to directories that have not yet been
	% created (like 'Movies/')
	ensureDirExists( fileparts(fullpath(saveAvi)) );

    % now export the movie
    if ispc
        codec = 'Indeo5';
    else
        codec = 'None';
    end
    movie2avi(mov, saveAvi, 'Compression', codec, 'Quality', 100, 'FPS', fps);
    fprintf('Exported movie to %s.\n', saveAvi);
end

%% lastly, play the movie if it exists
if movieFlag==1
    movie.play
end

return


function a = shake();
global layout;
rand('twister',sum(100*clock));

a=1;
figure(1);
plot([0 1],[.25 .25],'k',[0 1],[.5 .5],'k',[0 1],[.75 .75],'k',[0 1],[1 1],'k'); hold on;
plot([.25 .25],[0 1],'k',[.5 .5],[0 1],'k',[.75 .75],[0 1],'k',[1 1],[0 1],'k');
axis([0 1 0 1],'equal','square');

Cube(1).letter = {'L','R','Y','T','T','E'};
Cube(2).letter = {'V','T','H','R','W','E'};
Cube(3).letter = {'E','G','H','W','N','E'};
Cube(4).letter = {'S','E','O','T','I','S'};
Cube(5).letter = {'A','N','A','E','E','G'};
Cube(6).letter = {'I','D','S','Y','T','T'};
Cube(7).letter = {'O','A','T','T','O','W'};
Cube(8).letter = {'M','T','O','I','C','U'};
Cube(9).letter = {'A','F','P','K','F','S'};
Cube(10).letter = {'X','L','D','E','R','I'};
Cube(11).letter = {'H','C','P','O','A','S'};
Cube(12).letter = {'E','N','S','I','E','U'};
Cube(13).letter = {'Y','L','D','E','V','R'};
Cube(14).letter = {'Z','N','R','N','H','L'};
Cube(15).letter = {'N','M','I','Qu','H','U'};
Cube(16).letter = {'O','B','B','A','O','J'};

rand(1,16);
[temp,order]=sort(rand(1,16));
Cube=Cube(order);

for i=1:4,
	for j=1:4,
		layout{i,j} = Cube(i+(j-1)*4).letter(ceil(rand*6));
		text(i/4-.15,j/4-.13,layout{i,j},'FontSize',20);
	end;
end;
set(gca,'XTick',[],'YTick',[]);


function [nodes,edges,classData] = mrgGrowGray(classData,numLayers,layer0,hemisphere)
%
%     [nodes,edges,classData] = mrgGrowGray(classInfo,[numLayers],[layer0],[hemisphere='left'])
%
% This routine calls the mex function grow_gray with the parameters
% specified by the user and loads the class file if necessary
%
% INPUTS :
%     - classInfo : a variable loaded from a class file or
%                   a path string pointing to the class file
%     - numLayers : number of layers to grow the gray matter
%     - layer0 : optional input argument. (Default 0)
%                   - 0 : no white matter included in the gray graph
%                   - 1 : the white boundary included in the gray graph.
%                         In this case, the layer number of the boundary will be 0.
%                   - 2 : All the white matter fully connected is included
%                         in the gray graph
%
% OUTPUTS :
%     - nodes : structure containing nodes of the gray matter
%     - edges : structure containing edges of the gray matter
%     - classData : matlab class file structure containing the output
%
% Example:
%  To visualize a mesh with, say, two layers of gray superimposed do this.
%   fName=fullfile(mrvDataRootPath,'anatomy','anatomyV','left','left.Class');
%   [nodes,edges,classData] = mrgGrowGray(fName,2);
%   wm = uint8( (classData.data == classData.type.white) | (classData.data == classData.type.gray));
%   msh = meshColor(meshSmooth(meshBuildFromClass(wm,[1 1 1])));
%   meshVisualize(msh,2);
%
%  To see the gray matter and white matter in a slice, along with their
%  connectivity, use this.  Change the third argument of mrGrowGray to
%  change the type of connectivity you visualize.
%
%   fName ='X:\anatomy\nakadomari\left\20050901_fixV1\left.Class';
%   [nodes,edges,classData] = mrgGrowGray(fName,2,2);
%   mrgDisplayGrayMatter(nodes,edges,80,[120 140 120 140]);
%
% NOTE:  M. Schira says that the gray nodes should be more densely
% connected to reduce the error in the distance measurements.  We should
% look into completing the connections in mrGrowGray.
%
% HISTORY:
%  GB 01/11/06 wrote it.
%  2008.07.07 RFD: fixed bug in VOI clipping.
%  2008.12.19 DY: fixed hemisphere variable setting bug
%
% (c) Stanford VISTA Team, 2006

if notDefined('numLayers'),  numLayers = 3; end
if notDefined('layer0'), layer0 = 0; end
if notDefined('hemisphere'), hemisphere = 'left'; end
if ischar(classData), classData = readClassFile(classData,0,0,hemisphere); end

voi  = classData.header.voi;
data = classData.data;
% RFD: added the "| ... 160". I think this is the type code for 'selected
% gray matter', but it's not listed in
data(data(:) == classData.type.gray) = classData.type.unknown;

fprintf('Growing %i gray layers...\n',numLayers);
if layer0,
    fprintf('White matter included in the gray graph...\n');
else
    fprintf('White matter excluded in the gray graph...\n');
end

% Growgray should ignore extra labels, but doesn't-= so we 'clean' it's
% class data.
cleanData = data;
cleanData(cleanData>=classData.type.other) = classData.type.unknown;
[nodes,edges] = grow_gray(cleanData,numLayers,voi,layer0);

grayMatter = nodes(1:3,nodes(6,:) ~= 0)+1;
outOfVoi = grayMatter(1,:)<=voi(1) | grayMatter(1,:)>voi(2) ...
         | grayMatter(2,:)<=voi(3) | grayMatter(2,:)>voi(4) ...
         | grayMatter(3,:)<=voi(5) | grayMatter(3,:)>voi(6);
grayMatter(:,outOfVoi) = [];
data(sub2ind(size(data),...
    grayMatter(1,:) - voi(1),...
    grayMatter(2,:) - voi(3),...
    grayMatter(3,:) - voi(5))) = classData.type.gray;

classData.data = data;

return;


function [EEG, choppedFront, choppedBack] = chop(EEG, frontChop, backChop)
% Deletes all but specified seconds from outside first and last event
%
% This removes bad segments from the beginning and end of the data
% set, which often correspond to bad times for continuous EEG
%
% Parameters:
%    EEG = EEGLAB structure
%    frontChop =
choppedFront = 0;
choppedBack = 0;
if size(EEG.data, 3) > 1
    warning('chop:DataNotContinuous', 'Only works on continuous data');
    return;
elseif nargin < 3
    error('chop:NotEnoughArguments', 'Requires 3 arguments');
elseif isempty(EEG.event)
    warning('chop:NoEvents', 'EEG has no events, so no chop is done');
    return;
end
srate = EEG.srate;
firstEventTime = double(EEG.event(1).latency - 1)/srate;
lastEventTime = double(EEG.event(end).latency - 1)/srate;
lastDataTime = double(EEG.pnts-1)/srate;
choppedFront = max(0, firstEventTime - frontChop);
endTime = min(lastDataTime, lastEventTime + backChop);
choppedBack = lastDataTime - endTime;
EEG = pop_select(EEG, 'time', [choppedFront, endTime]);
boundaryEvents = strcmpi({EEG.event.type}, 'boundary');
if sum(strcmpi(EEG.event(1).type, 'boundary')) > 0
    fprintf('Removing %g boundary events\n', sum(boundaryEvents));
    EEG.event(boundaryEvents) = [];
end


function calcAllFusedTextures_HN(pathData,pathText,namePT,nameCT,nameROI,featType,CTweight_mat,scale_mat,algo_cell,Ng_mat)
% -------------------------------------------------------------------------
% function calcAllFusedTextures_HN(pathData,pathText,namePT,nameCT,nameROI,featType,CTweight_mat,scale_mat,Ng_mat)
% -------------------------------------------------------------------------
% DESCRIPTION:
% This function computes FUSED texture features for all patients, for all
% different combinations of the following texture extraction parameters:
% - CT weight: Weight given to MRI wavelet low-pass sub-bands in the
%               PET/CT fusion process.
% - Scale: Resolution at which the ROI is isotropically resampled.
% - Ng: Number of gray-levels in the quantization process.
%
% Different extraction parameters are passed as arrays or cells in the
% function in order to test all possible combinations. This function is
% used for FUSED scans specifically. See Ref. [1,2] and 'prepareVolume.m'
% for more details.
%
% Texture features are computed for all head and neck (HN) DICOM  imaging
% data downloaded from The Cancer Imaging Archive (TCIA) website at:
% <http://dx.doi.org/10.7937/K9/xxxxxxxxxxxxxxxxxxx, and first organized
% in a 'DATA' directory using the function readAllDICOM_HN.m.  Results are
% then saved in a folder 'TEXTURES' in the HN WORKSPACE.
% -------------------------------------------------------------------------
% REFERENCE:
% [1] Vallieres, M. et al. (2015). FDG-PET/CT radiomics models for the
%     early prediction of different tumour outcomes in head and neck cancer.
%     The Journal of Nuclear Medicine, aa(bb), xxx-yyy.
%     doi:
% [2] Vallieres, M. et al. (2015). A radiomics model from joint FDG-PET and
%     MRI texture features for the prediction of lung metastases in soft-tissue
%     sarcomas of the extremities. Physics in Medicine and Biology, 60(14),
%     5471-5496. doi:10.1088/0031-9155/60/14/5471
% -------------------------------------------------------------------------
% INPUTS:
% 1. pathData: Full path to the HN sData files directory.
%              --> Ex: '/myProject/WORKSPACE/DATA'
% 2. pathNonText: Full path to the HN non texture features directory.
%              --> Ex: '/myProject/WORKSPACE/FEATURES/NON_TEXTURES'
% 3. namePT: Cell of strings of all PET sData files to read
%            --> Ex: {'HGJ_001_PT.PTscan.mat';'HGJ_022_PT.PTscan.mat'}
% 4. namePT: Cell of strings of all CT sData files to read
%            --> Ex: {'HGJ_001_CT.CTscan.mat';'HGJ_022_CT.CTscan.mat'}
% 5. nameROI: Cell of strings specifying the ROI names to analyze for the
%             patients defined by "namePT" and "nameCT"
%             --> Ex: {'GTV';'GTV-P'}
% 6. featType: Either 'GTVp' for primary GTV, or 'GTVtot' for primaty GTV +
%              nodal GTVs
% 7. CTweight_mat: Array vector specifying the different CT weights to test
%                  --> Ex: [1/4,1/3,1/2,2/3,3/4]
% 8. scale_mat: Array vector specifying the different 'Scale' values to test.
%               --> Ex: [1,2,3,4,5]
% 9. Ng_mat: Array vector specifying the different 'Ng' values to test.
%            --> Ex: [8,16,32,64]
% -------------------------------------------------------------------------
% AUTHOR(S): Martin Vallieres <mart.vallieres@gmail.com>
% -------------------------------------------------------------------------
% HISTORY:
% - Creation: March 2016
%--------------------------------------------------------------------------
% STATEMENT:
% This file is part of <https://github.com/mvallieres/radiomics/>,
% a package providing MATLAB programming tools for radiomics analysis.
% --> Copyright (C) 2015  Martin Vallieres
%
%    This package is free software: you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation, either version 3 of the License, or
%    (at your option) any later version.
%
%    This package is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with this package.  If not, see <http://www.gnu.org/licenses/>.
% -------------------------------------------------------------------------

startpath=pwd;

% INITIALIZATION
nPatient = numel(namePT);

% COMPUTATION
fprintf('\n')
for i = 1:nPatient
    cd(pathData)
    sDataPT = load(namePT{i}); sDataPT = struct2cell(sDataPT); sDataPT = sDataPT{1};
    sDataCT = load(nameCT{i}); sDataCT = struct2cell(sDataCT); sDataCT = sDataCT{1};
    tStart = tic;
    fprintf(['\n*********************** COMPUTING TEXTURES: FUSED %s and %s ***********************'],namePT{i},nameCT{i})
    [textures] = calcPatientFusText_HN(sDataPT,sDataCT,nameROI{i},CTweight_mat,scale_mat,algo_cell,Ng_mat);
    ind = strfind(namePT{i},'_'); namePatient = namePT{i}(1:ind(2)-1);
    cd(pathText), save([namePatient,'_PTCT_',featType,'_text'],'textures')
    time = toc(tStart);
    fprintf('TOTAL TIME: %.2f seconds\n',time)
end

cd(startpath)
end


function DVH_out = DVHwithShifts(planC,structNum,doseNum)
%function DVH_out = DVHwithShifts(planC,structNum,doseNum)
%
%APA, 11/09/2010
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
% without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% See the GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with CERR.  If not, see <http://www.gnu.org/licenses/>.

%Number of Fractions and Trials
numFractions = 30;
numTrials    = 100;

% Systematic Shifts in cm (Inter-treatment)
XdispSys_Std = 0;
YdispSys_Std = 0;
ZdispSys_Std = 0;

% Random Shifts in cm (inter-fraction)
XdispRnd_Std = 1;
YdispRnd_Std = 1;
ZdispRnd_Std = 1;

indexS = planC{end};

%Distances are in cm
XpdfMean = 0;

YpdfMean = 0;

ZpdfMean = 0;

%Get scan associated with doseNum
scanNum = getAssociatedScan(planC{indexS.dose}(doseNum).assocScanUID, planC);

if isempty(scanNum) %Assume dose is associated with this scan
    scanNum = getStructureAssociatedScan(structNum,planC);
end

%Get reference transformation matrix for doseNum
if ~isempty(scanNum) && isempty(planC{indexS.dose}(doseNum).transM)
    referenceTransM = planC{indexS.scan}(scanNum).transM;
else
    referenceTransM = planC{indexS.dose}(doseNum).transM;
end

%Store the reference doseArray
referenceDoseArray = planC{indexS.dose}(doseNum).doseArray;

%Try and get a binWidth from stateS.  If it doesnt exist, get it from
%the CERROptions file (allows this function to be called outside CERR)
global stateS;
if ~isempty(stateS) && isfield(stateS, 'optS') && isfield(stateS.optS, 'DVHBinWidth') && ~isempty(stateS.optS.DVHBinWidth)
    binWidth = stateS.optS.DVHBinWidth;
else
    optS = CERROptions;
    binWidth = optS.DVHBinWidth;
end

%Compute DVH at given dose
[dosesCurrentV, volsV] = getDVH(structNum, doseNum, planC);
[doseBinsCurrentV, volsHistCurrentV] = doseHist(dosesCurrentV, volsV, binWidth);

%Divide doseArray into fractions
planC{indexS.dose}(doseNum).doseArray = planC{indexS.dose}(doseNum).doseArray / numFractions;

%Get systematic errors
deltaX_systematic = randn(1, numTrials) * XdispSys_Std + XpdfMean;
deltaY_systematic = randn(1, numTrials) * YdispSys_Std + YpdfMean;
deltaZ_systematic = randn(1, numTrials) * ZdispSys_Std + ZpdfMean;

hWait = waitbar(0,'Computing plan robustness...');

if isempty(referenceTransM)
    referenceTransM_matrix = eye(4);
else
    referenceTransM_matrix = referenceTransM;
end

%Obtain DVH calculation points in blocks
optS    = planC{indexS.CERROptions};

ROIImageSize = [planC{indexS.scan}(scanNum).scanInfo(1).sizeOfDimension1  planC{indexS.scan}(scanNum).scanInfo(1).sizeOfDimension2];

deltaY = planC{indexS.scan}(scanNum).scanInfo(1).grid1Units;

%Get raster segments for structure.
[segmentsM, planC, isError] = getRasterSegments(structNum, planC);

if isempty(segmentsM)
    isError = 1;
end
numSegs = size(segmentsM,1);

%Relative sampling of ROI voxels in this place, compared to CT spacing.
%Set when rasterSegments are generated (usually on import).
sampleRate = optS.ROISampleRate;

%Sample the rows
indFullV =  1 : numSegs;
if sampleRate ~= 1
    rV = 1 : length(indFullV);
    rV([rem(rV+sampleRate-1,sampleRate)~=0]) = [];
    indFullV = rV;
end

%Block process to avoid swamping on large structures
if isfield(optS, 'DVHBlockSize') & ~isempty(optS.DVHBlockSize)
    DVHBlockSize = optS.DVHBlockSize;
else
    DVHBlockSize = 5000;
end

blocks = ceil(length(indFullV)/DVHBlockSize);

start = 1;

for b = 1 : blocks

  %Build the interpolation points matrix

  dummy = zeros(1,DVHBlockSize * ROIImageSize(1));
  x1V = dummy;
  y1V = dummy;
  z1V = dummy;
  volsSectionV =  dummy;

  if start+DVHBlockSize > length(indFullV)
    stop = length(indFullV);
  else
    stop = start + DVHBlockSize - 1;
  end

  indV = indFullV(start:stop);

  mark = 1;
  for i = indV

    tmpV = segmentsM(i,1:10);
    delta = tmpV(5) * sampleRate;
    xV = tmpV(3): delta : tmpV(4);
    len = length(xV);
    rangeV = ones(1,len);
    yV = tmpV(2) * rangeV;
    zV = tmpV(1) * rangeV;
    sliceThickness = tmpV(10);
    %v = delta^2 * sliceThickness;
    v = delta * (deltaY*sampleRate) * sliceThickness;
    x1V(mark : mark + len - 1) = xV;
    y1V(mark : mark + len - 1) = yV;
    z1V(mark : mark + len - 1) = zV;
    volsSectionV(mark : mark + len - 1) = v;
    mark = mark + len;

  end

  %cut unused matrix elements
  x1V = x1V(1:mark-1);
  y1V = y1V(1:mark-1);
  z1V = z1V(1:mark-1);
  volsSectionV = volsSectionV(1:mark-1);

  %Get transformation matrices for both dose and structure.
  transMDose    = getTransM('dose', doseNum, planC);
  transMStruct  = getTransM('struct', structNum, planC);

  %Forward transform the structure's coordinates.
  if ~isempty(transMStruct)
      [x1V, y1V, z1V] = applyTransM(transMStruct, x1V, y1V, z1V);
  end

  dvhCalcPtsC{b} = [x1V', y1V', z1V'];

  %Interpolate.
%   [dosesSectionV] = getDoseAt(doseNum, x1V, y1V, z1V, planC);

%   dosesV = [dosesV, dosesSectionV];
%   volsV  = [volsV, volsSectionV];

  start = stop + 1;

end

%Loop over to generate DVH
for iTrial = 1:numTrials

    deltaX = deltaX_systematic(iTrial) + randn(1, numFractions) * XdispRnd_Std + XpdfMean;
    deltaY = deltaY_systematic(iTrial) + randn(1, numFractions) * YdispRnd_Std + YpdfMean;
    deltaZ = deltaZ_systematic(iTrial) + randn(1, numFractions) * ZdispRnd_Std + ZpdfMean;

    tmpDoseV = zeros(1,length(dosesCurrentV));

    for iFraction = 1:numFractions

        waitbar(((iTrial-1)*numFractions + iFraction)/(numTrials*numFractions),hWait)

        transM = referenceTransM_matrix;
        transM(1:3,4) = transM(1:3,4) + [deltaX(iFraction); deltaY(iFraction); deltaZ(iFraction)];

        %Apply the new transM to dose
        planC{indexS.dose}(doseNum).transM = transM;

        %Get doses and volumes of points in structure.
        %[dosesV, volsV] = getDVH(structNum, doseNum, planC);

        volsV  = [];
        dosesV = [];

        for b = 1 : blocks
            x1V = dvhCalcPtsC{b}(:,1);
            y1V = dvhCalcPtsC{b}(:,2);
            z1V = dvhCalcPtsC{b}(:,3);

            %Back transform the coordinates into the doses' coordinate system.
            [x1V, y1V, z1V] = applyTransM(inv(transM), x1V, y1V, z1V);

            %Interpolate.
            [dosesSectionV] = getDoseAt(doseNum, x1V, y1V, z1V, planC);

            dosesV = [dosesV, dosesSectionV];
            volsV  = [volsV, volsSectionV];
        end

        tmpDoseV = tmpDoseV + dosesV;

    end

    DVHm(:,iTrial) = single(tmpDoseV(:));

    %Compute Mean and Std for binned dose
    %for iTrial=1:numTrials
        doseTrialV = DVHm(:,iTrial);
        [doseBinsV, volsHistV] = doseHist(doseTrialV, volsV, binWidth);
        doseBinsTmpV = doseBinsV;
        volsHistTmpV = volsHistV;
        if iTrial > 1
            AddToCurrent = ~ismember(doseBins{1},doseBinsV);
            AddToPrevious = ~ismember(doseBinsV,doseBins{1});
            doseBinsV = [doseBinsV doseBins{1}(AddToCurrent)];
            volsHistV = [volsHistV volsHist{1}(AddToCurrent)];
            for jTrial = 1:iTrial-1
                doseBins{jTrial} = [doseBins{jTrial} doseBinsTmpV(AddToPrevious)];
                volsHist{jTrial} = [volsHist{jTrial} volsHistTmpV(AddToPrevious)];
                [doseBins{jTrial},indSort] = sort(doseBins{jTrial});
                volsHist{jTrial} = volsHist{jTrial}(indSort);
            end
        end
        doseBins{iTrial} = doseBinsV;
        volsHist{iTrial} = volsHistV;
        [doseBins{iTrial},indSort] = sort(doseBins{iTrial});
        volsHist{iTrial} = volsHist{iTrial}(indSort);
    %end

end

close(hWait)

%Compute Std of binned volumes
blockSize = 100;
for iBlock = 1:ceil(length(volsHist{1})/blockSize)
    if iBlock == ceil(length(volsHist{1})/blockSize)
        indicesV = (iBlock-1)*blockSize+1:length(volsHist{1});
    else
        indicesV = (iBlock-1)*blockSize+1:iBlock*blockSize;
    end
    volsHistStdM = [];
    for iTrial = 1:length(volsHist)
        volsHistStdM(:,iTrial) = volsHist{iTrial}(indicesV);
    end
    volsHistStdV(indicesV) = std(volsHistStdM,0,2)';
end

%Reassign reference transM and doseArray to doseNum
planC{indexS.dose}(doseNum).transM = referenceTransM;
planC{indexS.dose}(doseNum).doseArray = referenceDoseArray;

%Compute Mean and Std Dev in blocks
numVoxels = length(volsV);
DVHBlockSize = 5000;
blocks = ceil(numVoxels/DVHBlockSize);
start = 1;

for b = 1 : blocks

  if start+DVHBlockSize > numVoxels
    stop = numVoxels;
  else
    stop = start + DVHBlockSize - 1;
  end

  DVH_block = DVHm(start:stop,:);

  meanDoseV(start:stop) = mean(DVH_block,2);
  stdDoseV(start:stop)  = std(DVH_block,0,2);

  start = stop + 1;

end

DVH_out = [meanDoseV(:)'; volsV(:)'];


function [header,tracks] = trk_read(filePath)
%TRK_READ - Load TrackVis .trk files
%TrackVis displays and saves .trk files in LPS orientation. After import, this
%function attempts to reorient the fibers to match the orientation of the
%original volume data.
%
% Syntax: [header,tracks] = trk_read(filePath)
%
% Inputs:
%    filePath - Full path to .trk file [char]
%
% Outputs:
%    header - Header information from .trk file [struc]
%    tracks - Track data structure array [1 x nTracks]
%      nPoints - # of points in each streamline
%      matrix  - XYZ coordinates (in mm) and associated scalars [nPoints x 3+nScalars]
%      props   - Properties of the whole tract (ex: length)
%
% Example:
%    exDir           = '/path/to/along-tract-stats/example';
%    subDir          = fullfile(exDir, 'subject1');
%    trkPath         = fullfile(subDir, 'CST_L.trk');
%    [header tracks] = trk_read(trkPath);
%
% Other m-files required: none
% Subfunctions: get_header
% MAT-files required: none
%
% See also: http://www.trackvis.org/docs/?subsect=fileformat
%           http://github.com/johncolby/along-tract-stats/wiki/orientation

% Author: John Colby (johncolby@ucla.edu)
% UCLA Developmental Cognitive Neuroimaging Group (Sowell Lab)
% Mar 2010

% Parse in header
fid    = fopen(filePath, 'r');
header = get_header(fid);

% Check for byte order
if header.hdr_size~=1000
    fclose(fid);
    fid    = fopen(filePath, 'r', 'b'); % Big endian for old PPCs
    header = get_header(fid);
end

if header.hdr_size~=1000, error('Header length is wrong'), end

% Check orientation
[tmp ix] = max(abs(header.image_orientation_patient(1:3)));
[tmp iy] = max(abs(header.image_orientation_patient(4:6)));
iz = 1:3;
iz([ix iy]) = [];

% Fix volume dimensions to match the reported orientation.
header.dim        = header.dim([ix iy iz]);
header.voxel_size = header.voxel_size([ix iy iz]);

% Parse in body
if header.n_count > 0
	max_n_trks = header.n_count;
else
	% Unknown number of tracks; we'll just have to read until we run out.
	max_n_trks = inf;
end

% It's impossible to preallocate the "tracks" variable because we don't
% know the number of points on each curve ahead of time; we find out by
% reading the file.  The line below suppresses preallocation warnings.
%#ok<*AGROW>

iTrk = 1;
while iTrk <= max_n_trks
	pts = fread(fid, 1, 'int');
	if feof(fid)
		break;
	end
    tracks(iTrk).nPoints = pts;
    tracks(iTrk).matrix  = fread(fid, [3+header.n_scalars, tracks(iTrk).nPoints], '*float')';
    if header.n_properties
        tracks(iTrk).props = fread(fid, header.n_properties, '*float');
    end

    % Modify orientation of tracks (always LPS) to match orientation of volume
    coords = tracks(iTrk).matrix(:,1:3);
    coords = coords(:,[ix iy iz]);
    if header.image_orientation_patient(ix) < 0
        coords(:,ix) = header.dim(ix)*header.voxel_size(ix) - coords(:,ix);
    end
    if header.image_orientation_patient(3+iy) < 0
        coords(:,iy) = header.dim(iy)*header.voxel_size(iy) - coords(:,iy);
    end
    tracks(iTrk).matrix(:,1:3) = coords;
	iTrk = iTrk + 1;
end

if header.n_count == 0
	header.n_count = length(tracks);
end

fclose(fid);

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function header = get_header(fid)

header.id_string                 = fread(fid, 6, '*char')';
header.dim                       = fread(fid, 3, 'short')';
header.voxel_size                = fread(fid, 3, 'float')';
header.origin                    = fread(fid, 3, 'float')';
header.n_scalars                 = fread(fid, 1, 'short')';
header.scalar_name               = fread(fid, [20,10], '*char')';
header.n_properties              = fread(fid, 1, 'short')';
header.property_name             = fread(fid, [20,10], '*char')';
header.vox_to_ras                = fread(fid, [4,4], 'float')';
header.reserved                  = fread(fid, 444, '*char');
header.voxel_order               = fread(fid, 4, '*char')';
header.pad2                      = fread(fid, 4, '*char')';
header.image_orientation_patient = fread(fid, 6, 'float')';
header.pad1                      = fread(fid, 2, '*char')';
header.invert_x                  = fread(fid, 1, 'uchar');
header.invert_y                  = fread(fid, 1, 'uchar');
header.invert_z                  = fread(fid, 1, 'uchar');
header.swap_xy                   = fread(fid, 1, 'uchar');
header.swap_yz                   = fread(fid, 1, 'uchar');
header.swap_zx                   = fread(fid, 1, 'uchar');
header.n_count                   = fread(fid, 1, 'int')';
header.version                   = fread(fid, 1, 'int')';
header.hdr_size                  = fread(fid, 1, 'int')';


clear; clc; close all;

DEVICE_ID = 0; %set gpu id starting from 0

% set paths to Caffe and DeepFool
PATH_CAFFE = '/path/to/caffe/matlab';
PATH_DEEPFOOL = '/path/to/DeepFool';
PATH_IMAGENET_TRAIN = '/path/to/ILSVRC2012/train';

addpath(PATH_CAFFE);
addpath(PATH_DEEPFOOL);

caffe.set_mode_gpu();
caffe.set_device(DEVICE_ID);

model = 'caffenet';

if (strcmp(model, 'vgg_16'))
    fprintf('Loading VGG 16\n');
    net_model_path = fullfile('data', 'deploy_vgg_16.prototxt');
    net_weights_path = fullfile('data', 'vgg_16.caffemodel');
    net_url = 'http://www.robots.ox.ac.uk/~vgg/software/very_deep/caffe/VGG_ILSVRC_16_layers.caffemodel';
elseif (strcmp(model, 'vgg_19'))
    fprintf('Loading VGG 19\n');
    net_model_path = fullfile('data', 'deploy_vgg_19.prototxt');
    net_weights_path = fullfile('data', 'vgg_19.caffemodel');
    net_url = 'http://www.robots.ox.ac.uk/~vgg/software/very_deep/caffe/VGG_ILSVRC_19_layers.caffemodel';
elseif (strcmp(model, 'googlenet'))
    fprintf('Loading GoogLeNet\n');
    net_model_path = fullfile('data', 'deploy_googlenet.prototxt');
    net_weights_path = fullfile('data', 'googlenet.caffemodel');
    net_url = 'http://dl.caffe.berkeleyvision.org/bvlc_googlenet.caffemodel';
elseif (strcmp(model, 'vgg_f'))
    fprintf('Loading VGG-F\n');
    net_model_path = fullfile('data', 'deploy_vgg_f.prototxt');
    net_weights_path = fullfile('data', 'googlenet.caffemodel');
    net_url = 'http://dl.caffe.berkeleyvision.org/bvlc_vgg_f.caffemodel';
elseif (strcmp(model, 'caffenet'))
    fprintf('Loading CaffeNet\n');
    net_model_path = fullfile('data', 'deploy_caffenet.prototxt');
    net_weights_path = fullfile('data', 'caffenet.caffemodel');
    net_url = 'http://dl.caffe.berkeleyvision.org/bvlc_reference_caffenet.caffemodel';
elseif (strcmp(model, 'resnet-152'))
    fprintf('Loading ResNet-152\n');
    net_model_path = fullfile('data', 'deploy_resnet.prototxt');
    net_weights_path = fullfile('data', 'caffenet.caffemodel');
    net_url = 'https://deepdetect.com/models/resnet/ResNet-152-model.caffemodel';
else
    error('Model is not recognized!');
end

if (~exist(net_weights_path, 'file'))
    fprintf('Downloading model...\n');
    websave(net_weights_path, net_url);
end

net = caffe.Net(net_model_path, net_weights_path, 'test'); % run with phase test (so that dropout isn't applied)
fprintf('Network is loaded\n');

% Loading the data
fprintf('Loading the data...\n');
im_array = makeImagenetData(PATH_IMAGENET_TRAIN);
fprintf('Data is loaded\n');

% set options
opts.library = 'caffe';
opts.net = net;

v = universal_perturbation(im_array, opts);

% Test perturbation on a sample image

d = load(fullfile('data', 'ilsvrc_2012_mean.mat'));
mean_data = d.mean_data;

labels_file = fullfile('data', 'synset_words.txt');
fileID = fopen(labels_file);
C = textscan(fileID, '%c %d %s', 'Delimiter', '\n');
synset_names = C{3};
synset_names_short = synset_names;
for i = 1:1000
    synset_names_short(i) = strtok(synset_names(i), ',');
end

im_test = imread(fullfile('data', 'test_img.png'));
im_test = preprocess_img(im_test, mean_data);

original_label = predict_caffe(im_test, net, 1);
perturbed_label = predict_caffe(im_test+v, net, 1);

% Show original and perturbed images
im_ = im_test + mean_data(1:224, 1:224, :);
im_ = permute(im_,[2,1,3]);
im_ = im_(:,:,[3,2,1],:);

figure; imshow(uint8(im_));
xlabel(synset_names_short(original_label));
set(gca,'xtick',[],'ytick',[])
set(gcf, 'Color', 'white');
set(gca, 'FontSize', 15);
title('Original image');

v_ = permute(v,[2,1,3]);
v_ = v_(:,:,[3,2,1],:);
figure; imshow(uint8(im_ + v_));
xlabel(synset_names_short(perturbed_label));
set(gca,'xtick',[],'ytick',[])
set(gcf, 'Color', 'white');
set(gca, 'FontSize', 15);
title('Perturbed image');


function m = moreland(n)
%MORELAND   Divergent red-white-blue color map
%
%   Usage: m = moreland([n])
%
%   Input parameters:
%       n - optional length of colormap (default uses the figure default length)
%
%   Output parameters:
%       m - colormap [n 3]
%
%   MORELAND(N) returns an N-by-3 matrix containing a divergent colormap.
%   Without a given N the same length as the current figure's colormap is used.
%   For details on the colormap have a look at:
%   http://www.kennethmoreland.com/color-advice/
%
%   To change the colormap current figure run: colormap(moreland)
%
%   Original written by Lewis Marshall.
%
%   See also: generate_colormap, yellowred, plot_sound_field

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

%% ===== Checking input parameters =======================================
nargmin = 0;
nargmax = 1;
narginchk(nargmin,nargmax);
if nargin < nargmax
    n = size(get(gcf,'colormap'),1);
end

%% ===== Computation =====================================================
table = [ 59,76,192
60,78,194
61,80,195
62,81,197
63,83,198
64,85,200
66,87,201
67,88,203
68,90,204
69,92,206
70,93,207
71,95,209
73,97,210
74,99,211
75,100,213
76,102,214
77,104,215
79,105,217
80,107,218
81,109,219
82,110,221
84,112,222
85,114,223
86,115,224
87,117,225
89,119,226
90,120,228
91,122,229
93,123,230
94,125,231
95,127,232
96,128,233
98,130,234
99,131,235
100,133,236
102,135,237
103,136,238
104,138,239
106,139,239
107,141,240
108,142,241
110,144,242
111,145,243
112,147,243
114,148,244
115,150,245
116,151,246
118,153,246
119,154,247
120,156,247
122,157,248
123,158,249
124,160,249
126,161,250
127,163,250
129,164,251
130,165,251
131,167,252
133,168,252
134,169,252
135,171,253
137,172,253
138,173,253
140,174,254
141,176,254
142,177,254
144,178,254
145,179,254
147,181,255
148,182,255
149,183,255
151,184,255
152,185,255
153,186,255
155,187,255
156,188,255
158,190,255
159,191,255
160,192,255
162,193,255
163,194,255
164,195,254
166,196,254
167,197,254
168,198,254
170,199,253
171,199,253
172,200,253
174,201,253
175,202,252
176,203,252
178,204,251
179,205,251
180,205,251
182,206,250
183,207,250
184,208,249
185,208,248
187,209,248
188,210,247
189,210,247
190,211,246
192,212,245
193,212,245
194,213,244
195,213,243
197,214,243
198,214,242
199,215,241
200,215,240
201,216,239
203,216,238
204,217,238
205,217,237
206,217,236
207,218,235
208,218,234
209,219,233
210,219,232
211,219,231
213,219,230
214,220,229
215,220,228
216,220,227
217,220,225
218,220,224
219,220,223
220,221,222
221,221,221
222,220,219
223,220,218
224,219,216
225,219,215
226,218,214
227,218,212
228,217,211
229,216,209
230,216,208
231,215,206
232,215,205
232,214,203
233,213,202
234,212,200
235,212,199
236,211,197
236,210,196
237,209,194
238,209,193
238,208,191
239,207,190
240,206,188
240,205,187
241,204,185
241,203,184
242,202,182
242,201,181
243,200,179
243,199,178
244,198,176
244,197,174
245,196,173
245,195,171
245,194,170
245,193,168
246,192,167
246,191,165
246,190,163
246,188,162
247,187,160
247,186,159
247,185,157
247,184,156
247,182,154
247,181,152
247,180,151
247,178,149
247,177,148
247,176,146
247,174,145
247,173,143
247,172,141
247,170,140
247,169,138
247,167,137
247,166,135
246,164,134
246,163,132
246,161,131
246,160,129
245,158,127
245,157,126
245,155,124
244,154,123
244,152,121
244,151,120
243,149,118
243,147,117
242,146,115
242,144,114
241,142,112
241,141,111
240,139,109
240,137,108
239,136,106
238,134,105
238,132,103
237,130,102
236,129,100
236,127,99
235,125,97
234,123,96
233,121,95
233,120,93
232,118,92
231,116,90
230,114,89
229,112,88
228,110,86
227,108,85
227,106,83
226,104,82
225,102,81
224,100,79
223,98,78
222,96,77
221,94,75
220,92,74
218,90,73
217,88,71
216,86,70
215,84,69
214,82,67
213,80,66
212,78,65
210,75,64
209,73,62
208,71,61
207,69,60
205,66,59
204,64,57
203,62,56
202,59,55
200,57,54
199,54,53
198,51,52
196,49,50
195,46,49
193,43,48
192,40,47
190,37,46
189,34,45
188,30,44
186,26,43
185,22,41
183,17,40
181,11,39
180,4,38];
m = generate_colormap(table,n);


classdef grain2d < phaseList & dynProp
  % class representing two dimensional grains
  %
  % Syntax
  %   grains = grain2d(V, poly, ori, CSList, phaseId, phaseMap)
  %
  % Input
  %  V    - n x 2 list of vertices
  %  poly - cell array of the polyhedrons
  %  ori  - array of mean orientations
  %  CSList   - cell array of symmetries
  %  phaseId  - list of phaseId for each grain
  %  phaseMap -
  %
  % Example
  %
  %   V = [0 0; 1 0; 2 0; 0 1; 1 1; 2 1];
  %   poly = {[1 2 5 4 1];[2 3 6 5 2]};
  %   rot = rotation.rand(2,1);
  %   grains = grain2d(V,poly,rot,crystalSymmetry.load('quartz'))
  %   plot(grains,grains.meanOrientation)
  %
  % Class Properties
  %  phaseId - phase identifier of each grain
  %  id            - id of each grain
  %  poly          - cell list of the vertex ids of each grain (index to V)
  %  V             - list of verticies (x,y coordinates)
  %  boundary      - @grainBoundary
  %  innerBoundary - @grainBoundary
  %  triplePoints  - @triplePoints
  %  grainSize     - number if pixels belonging to the grain
  %  GOS           - grain orientation spread
  %  meanOrientation - average grain orientation (<GrainOrientationParameters.html only single phase>)
  %
  % See also
  % GrainReconstruction GrainSpatialPlots SelectingGrains ShapeParameter

  % properties with as many rows as data
  properties
    poly={}    % cell list of polygons forming the grains
    id=[]      % id of each grain
    grainSize = [] % number of measurements per grain
  end

  properties (Hidden = true)
    inclusionId = []; % number of elements in poly that model inclusions
  end

  % general properties
  properties
    boundary = grainBoundary % boundary of the grains
    innerBoundary = grainBoundary % inner grain boundary
  end

  properties (Dependent = true)
    meanOrientation  % mean orientation
    V                % vertices with x,y coordinates
    scanUnit         % unit of the vertice coordinates
    GOS              % intragranular average misorientation angle
    x                % x coordinates of the vertices of the grains
    y                % y coordinates of the vertices of the grains
    triplePoints     % triple points
  end

  properties (Dependent = true, Access = protected)
    idV % active vertices
  end

  methods

    function grains = grain2d(V, poly, ori, CSList, phaseId,  phaseMap, varargin)
      % constructor
      %
      % Input
      %  V    - n x 2 list of vertices
      %  poly - cell array of the polyhedrons
      %  ori  - array of mean orientations
      %  CSList   - cell array of symmetries
      %  phaseId  - list of phaseId for each grain
      %  phaseMap -

      if nargin == 0, return;end

      grains.poly = poly;
      grains.inclusionId = cellfun(@(p) length(p) - find(p(2:end)==p(1),1),poly)-1;

      if nargin>=3 && ~isempty(ori)
        grains.prop.meanRotation = ori;
      else
        grains.prop.meanRotation = rotation.nan(length(poly),1);
      end

      if nargin>=4
        grains.CSList = ensurecell(CSList);
      else
        grains.CSList = {'notIndexed'};
      end

      if nargin>=5
        grains.phaseId = phaseId;
      else
        grains.phaseId = ones(length(poly),1);
      end

      if nargin>=6
        grains.phaseMap = phaseMap;
      else
        grains.phaseMap = 1:length(grains.CSList);
      end

      grains.id = (1:numel(grains.phaseId)).';
      grains.grainSize = ones(size(poly));

      if isa(V,'grainBoundary') % grain boundary already given
        grains.boundary = V;
      else % otherwise compute grain boundary

        % compute boundary segments
        F = arrayfun(@(i) poly{i}([1:end-grains.inclusionId(i)-1;2:end-grains.inclusionId(i)]),...
          1:length(poly),'uniformOutput',false);
        F = [F{:}].';

        lBnd = cellfun(@(p) length(p),poly) - grains.inclusionId -1;

        grainIds = repelem(1:length(grains),lBnd);

        [~,iF,iG] = unique(sort(F,2),'rows','stable');
        F = F(iF,:);

        % compute boundary grainIds
        grainId = zeros(size(F));
        grainId(:,1) = grainIds(iF); % first column - first apperance

        % second column - second appearance
        col2 = true(size(iG,1),1);
        col2(iF) = false;
        grainId(iG(col2),2) = grainIds(col2);

        % compute misorientation from mean orientations of the grain
        mori = rotation.nan(size(F,1),1);
        isNotBoundary = all(grainId,2);
        mori(isNotBoundary) = ...
          inv(grains.prop.meanRotation(grainId(isNotBoundary,2))) ...
          .* grains.prop.meanRotation(grainId(isNotBoundary,1));

        grains.boundary = grainBoundary(V,F,grainId,1:max(grainId),...
          grains.phaseId,mori,grains.CSList,grains.phaseMap);

      end

    end

    function V = get.V(grains)
      V = grains.boundary.V;
    end

    function x = get.x(grains)
      x = grains.boundary.x;
    end

    function y = get.y(grains)
      y = grains.boundary.y;
    end

    function grains = set.V(grains,V)

      grains.boundary.V = V;
      grains.innerBoundary.V = V;

    end

    function idV = get.idV(grains)

      isCell = cellfun('isclass',grains.poly,'cell');
      polygons = grains.poly;
      polygons(isCell) = cellfun(@(x) [x{:}] ,grains.poly(isCell),'UniformOutput',false);
      idV = unique([polygons{:}]);

    end

    function varargout = size(grains,varargin)
      [varargout{1:nargout}] = size(grains.id,varargin{:});
    end

    function ori = get.meanOrientation(grains)
      if isempty(grains)
        ori = orientation;
      else
        ori = orientation(grains.prop.meanRotation,grains.CS);

        % set not indexed orientations to nan
        if ~all(grains.isIndexed), ori(~grains.isIndexed) = NaN; end
      end
    end

    function grains = set.meanOrientation(grains,ori)

      if ~isempty(grains)

        if isnumeric(ori) && all(isnan(ori(:)))
          grains.prop.meanRotation = rotation.nan(size(grains.prop.meanRotation));
        else
          % update rotation
          grains.prop.meanRotation = rotation(ori);

          % update phase
          grains.CS = ori.CS;
        end
      end

    end

    function gos = get.GOS(grains)
      gos = grains.prop.GOS;
    end

    function unit = get.scanUnit(grains)
      unit = grains.boundary.scanUnit;
    end

    function grains = set.scanUnit(grains,unit)
      grains.boundary.scanUnit = unit;
      grains.innerBoundary.scanUnit = unit;
    end

    function tP = get.triplePoints(grains)
      tP = grains.boundary.triplePoints;
    end

    function grains = set.triplePoints(grains,tP)
      grains.boundary.triplePoints = tP;
    end

    function grains = update(grains)

      grains.boundary = grains.boundary.update(grains);
      grains.innerBoundary = grains.innerBoundary.update(grains);
      grains.triplePoints = grains.triplePoints.update(grains);

    end

  end

  methods (Static = true)

    [grain2d] = load(fname)

  end

end


function [cfg] = ft_multiplotCC(cfg, data)

% FT_MULTIPLOTCC visualises the coherence between channels by using
% multiple topoplots. The topoplot at a given channel location shows the
% coherence of that channel with all other channels.
%
% Use as
%   ft_multiplotCC(cfg, data)
%
% See also FT_PREPARE_LAYOUT, FT_TOPOPLOTCC, FT_CONNECTIVITYPLOT

% Undocumented local options:
% cfg.layout  = layout filename or a structure produced by prepare_layout
% cfg.xlim
% cfg.parameter
% This function requires input from FT_FREQSTATISTICS_SHIFTPREDICT
% This function should be rewritten, using the clean topoplot implementation

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

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

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

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

% check if the input data is valid for this function
data = ft_checkdata(data);

% check if the input cfg is valid for this function
cfg = ft_checkconfig(cfg, 'renamed', {'zparam', 'parameter'});
cfg = ft_checkconfig(cfg, 'deprecated',  {'xparam'});

% set the defaults
cfg.xlim      = ft_getopt(cfg, 'xlim', 'all');
cfg.parameter = ft_getopt(cfg, 'parameter', 'avg.icohspctrm');
cfg.renderer  = ft_getopt(cfg, 'renderer'); % let MATLAB decide on the default

if strcmp(cfg.parameter, 'avg.icohspctrm') && ~issubfield(data, 'avg.icohspctrm')
  data.avg.icohspctrm = abs(imag(data.avg.cohspctrm));
end

if strcmp(data.dimord, 'chan_chan_freq')
  % reshape input-data, such that ft_topoplotTFR will take it
  cnt = 1;
  siz = size(data.prob);
  data.labelcmb = cell(siz(1)*siz(2),2);
  data.prob = reshape(data.prob, [siz(1)*siz(2) siz(3)]);
  data.stat = reshape(data.stat, [siz(1)*siz(2) siz(3)]);
  for j = 1:length(data.label)
    for k = 1:length(data.label)
      data.labelcmb(cnt,:) = [data.label(k) data.label(j)];
      cnt = cnt + 1;
    end
  end
  tmpdata = data;
else
  dat   = getsubfield(data, cfg.parameter);
  scale = [0 max(dat(:))-0.2];
end

if isfield(cfg, 'xparam')
  xparam = getsubfield(data, cfg.xparam);
  if ~strcmp(cfg.xlim, 'all')
    fbin = [nearest(xparam, cfg.xlim(1)) nearest(xparam, cfg.xlim(2))];
  else
    fbin = [xparam(1) xparam(end)];
  end
end

% read or create the layout that will be used for plotting
tmpcfg = keepfields(cfg, {'layout', 'channel', 'rows', 'columns', 'commentpos', 'skipcomnt', 'scalepos', 'skipscale', 'projection', 'viewpoint', 'rotate', 'width', 'height', 'elec', 'grad', 'opto', 'showcallinfo', 'trackcallinfo', 'trackusage', 'trackdatainfo', 'trackmeminfo', 'tracktimeinfo', 'checksize'});
layout = ft_prepare_layout(tmpcfg);
ft_plot_layout(layout, 'box', false, 'label', 'no', 'point', 'no');

X = layout.pos(:,1);
Y = layout.pos(:,2);
Width = layout.width;
Height = layout.height;
Lbl = layout.label;
chNum = numel(layout.label);

xScaleFac = 1/(max(Width)+ max(X) - min(X));
yScaleFac = 1/(max(Height)+ max(Y) - min(Y));

Xpos = xScaleFac*(X-min(X));
Ypos = 0.9*yScaleFac*(Y-min(Y));

for k=1:length(chNum) - 2
  subplotOL('position',[Xpos(k) Ypos(k)+(Height(k)*yScaleFac) Width(k)*xScaleFac*2 Height(k)*yScaleFac*2])
  tmpcfg = keepfields(cfg, {'parameter', 'layout', 'renderer', 'visible'});
  if exist('tmpdata', 'var')
    tmpcfg.style      = 'straight';
    tmpcfg.marker     = 'off';
    try
      tmpcfg.refmarker = strmatch(Lbl(k), data.reflabel);
    catch
      tmpcfg.refmarker = strmatch(Lbl(k), data.label);
    end
    tmpcfg.interplimits = 'electrodes';
    if isfield(cfg, 'xparam')
      tmpcfg.xparam = cfg.xparam;
      tmpcfg.xlim   = xparam;
    else
      tmpcfg.xparam = 'freq';
      tmpcfg.xlim   = [k-0.5 k+0.5];
    end
    tmpcfg.refchannel = Lbl(k);
    tmpcfg.colorbar   = 'no';
    tmpcfg.zlim       = scale;
    tmpcfg.grid_scale = 30;
    ft_topoplotTFR(tmpcfg, data);
    drawnow
  end
end

% this is needed for the figure title
if isfield(cfg, 'dataname') && ~isempty(cfg.dataname)
  dataname = cfg.dataname;
elseif isfield(cfg, 'inputfile') && ~isempty(cfg.inputfile)
  dataname = cfg.inputfile;
elseif nargin>1
  dataname = arrayfun(@inputname, 2:nargin, 'UniformOutput', false);
else
  dataname = {};
end

% set the figure window title
if ~isempty(dataname)
  set(gcf, 'Name', sprintf('%d: %s: %s', double(gcf), mfilename, join_str(', ', dataname)));
else
  set(gcf, 'Name', sprintf('%d: %s', double(gcf), mfilename));
end
set(gcf, 'NumberTitle', 'off');

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

% add a menu to the figure, but only if the current figure does not have subplots
menu_fieldtrip(gcf, cfg, false);

if ~ft_nargout
  % don't return anything
  clear cfg
end


function visual_odo_main(visualDataFile,groundTruthFile)
%     NeuroSLAM System Copyright (C) 2018-2019
%     NeuroSLAM: A Brain inspired SLAM System for 3D Environments
%
%     Fangwen Yu (www.yufangwen.com), Jianga Shang, Youjian Hu, Michael Milford(www.michaelmilford.com)
%
%     The NeuroSLAM V1.0 (MATLAB) was developed based on the OpenRatSLAM (David et al. 2013).
%     The RatSLAM V0.3 (MATLAB) developed by David Ball, Michael Milford and Gordon Wyeth in 2008.
%
%     Reference:
%     Ball, David, Scott Heath, Janet Wiles, Gordon Wyeth, Peter Corke, and Michael Milford.
%     "OpenRatSLAM: an open source brain-based SLAM system." Autonomous Robots 34, no. 3 (2013): 149-176.
%
%     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/>.

    %% vo
    % for drawing images for estimating pitch and yaw
    global SUB_TRANS_IMG;
    global SUB_YAW_ROT_IMG;
    global SUB_HEIGHT_V_IMG;

    global ODO_IMG_TRANS_RESIZE_RANGE;
    global ODO_IMG_YAW_ROT_RESIZE_RANGE;
    global ODO_IMG_HEIGHT_V_RESIZE_RANGE;

    global PREV_TRANS_V_IMG_X_SUMS;
    global PREV_YAW_ROT_V_IMG_X_SUMS;
    global PREV_HEIGHT_V_IMG_Y_SUMS;

    %% read imgs as VT input

    % get the visual data info
    [subFoldersPathSet, numSubFolders] = get_images_data_info(visualDataFile);

    % load ground truth data
    [frameId, gt_x, gt_y, gt_z, gt_rx, gt_ry, gt_rz] = load_ground_truth_data(groundTruthFile);
    groundTruthData = [gt_x gt_y gt_z];

    % define temp variables of previous profiles
    preProfilesTransImg = zeros(1,ODO_IMG_TRANS_RESIZE_RANGE(2));
    preProfilesYawRotImg = zeros(1,ODO_IMG_YAW_ROT_RESIZE_RANGE(2));
    preProfilesPitchRotImg = zeros(ODO_IMG_HEIGHT_V_RESIZE_RANGE(2), 1);

    % For drawing the current rot direction
    curRotDir = [0 0 0];
    curRotDir(1,3) = 0;

    % The total rotational angle
    sumRotAngle = 0;

    % for drawing the direction theta (rotational angle)
    theta = [0 0 0 0 0 0];
    rho = [0 0 0 0 0 0];
    startPoint =[0 0];
    endPoint = [0.8 0.8];

    % For drawing the velocity
    subRotVel = [];
    transVelVector = [];
    heightVelVector = [];
    sumHeight = [];
    transVelVector(1) = 0;
    heightVelVector(1) = 0;
    subRotVel(1) = 0;
    sumHeight = 0;

     % Transform the angle from degree to radian
    global DEGREE_TO_RADIAN;
    DEGREE_TO_RADIAN = pi / 180;

    % Transform the angle from radian to degree
    global RADIAN_TO_DEGREE;
    RADIAN_TO_DEGREE = 180 / pi;

    RENDER_RATE = 2;

    IMG_TYPE = '*.png';

    global OFFSET_YAW_ROT;
    offset_yaw_rot_vector = [];
    offset_yaw_rot_vector(1) = 0;

    global OFFSET_HEIGHT_V;
    offset_height_V_vector = [];
    offset_height_V_vector(1) = 0;

    odoMapTrajectory(1,1) = 0;
    odoMapTrajectory(1,2) = 0;
    odoMapTrajectory(1,3) = 0;
    odoMapTrajectory(1,4) = 0;

    ODO_MAP_X_SCALING = 1;
    ODO_MAP_Y_SCALING = 1;
    ODO_MAP_Z_SCALING = 1;
    GT_ODO_X_SCALING = 1;
    GT_ODO_Y_SCALING = 1;
    GT_ODO_Z_SCALING = 1;

    curFrame = 0;
%     step = 1;

%     %  up
    startFrame = 1;
    endFrame = 3000;

    % down
%     startFrame = 2484;
%     endFrame = 2621;

    % level_2
%     startFrame = 925;
%     endFrame = 2483;

    %% processing visual odometry
    for iSubFolder = 1 : numSubFolders

        [curFolderPath, imgFilesPathList, numImgs] = get_cur_img_files_path_list(subFoldersPathSet, IMG_TYPE, iSubFolder);

        if numImgs > 0
            for indFrame = startFrame : endFrame
                curFrame = curFrame + 1;
                [curImg] = read_current_image(curFolderPath, imgFilesPathList, indFrame);

                % visual templates and visual odo uses intensity so convert to grayscale
                curGrayImg = rgb2gray(curImg);
                curGrayImg = im2double(curGrayImg);

                % for drawing previous intensity
                preProfilesTransImg = PREV_TRANS_V_IMG_X_SUMS;
                preProfilesYawRotImg = PREV_YAW_ROT_V_IMG_X_SUMS;
                preProfilesHeightVImg = PREV_HEIGHT_V_IMG_Y_SUMS;

                %% step2: processing vo
                % get the odometry from the video
                % computing the 3d odometry based on the current image.
                % yawRotV and pitchRotV in degree
                [transV, yawRotV, heightV] = visual_odometry(curGrayImg);

                subRotVel(curFrame) = yawRotV;
                transVelVector(curFrame) = transV;
                heightVelVector(curFrame) = heightV;

                offset_yaw_rot_vector(curFrame) = OFFSET_YAW_ROT;
                offset_height_V_vector(curFrame) = OFFSET_HEIGHT_V;

                sumHeight(curFrame + 1) = sumHeight(curFrame) + heightV;

                % For drawing the total rotational angle
                if  sumRotAngle + yawRotV >= 360
                    sumRotAngle = mod(sumRotAngle + yawRotV, 360);
                elseif sumRotAngle + yawRotV <= -360
                    sumRotAngle = sumRotAngle + yawRotV + 360;
                else
                    sumRotAngle = sumRotAngle + yawRotV;
                end

                % Transform the vrot from degree to radian
                yawRotV = yawRotV * DEGREE_TO_RADIAN;

                % Direction by raw odometry
                curRotDir(curFrame + 1, 1) = cos(curRotDir(curFrame, 3) + yawRotV );
                curRotDir(curFrame + 1, 2) = sin(curRotDir(curFrame, 3) + yawRotV );
                curRotDir(curFrame + 1, 3) = curRotDir(curFrame, 3) + yawRotV;

                odoMapTrajectory(curFrame + 1,1) = odoMapTrajectory(curFrame,1) + transV * cos(sym(curRotDir(curFrame + 1, 3)));
                odoMapTrajectory(curFrame + 1,2) = odoMapTrajectory(curFrame,2) + transV * sin(sym(curRotDir(curFrame + 1, 3)));
                odoMapTrajectory(curFrame + 1,3) = odoMapTrajectory(curFrame,3) + heightV;
                odoMapTrajectory(curFrame + 1,4) = curRotDir(curFrame + 1, 3);

                % render results information
                if (mod(curFrame, RENDER_RATE) == 0)

                    % computing the normalized profile of horizontal translational image
                    profilesTransImg = sum(SUB_TRANS_IMG);
                    avgIntensity = sum(profilesTransImg) / size(profilesTransImg, 2);
                    profilesTransImg = profilesTransImg / avgIntensity;

                    % computing the normalized profile of rotational image
                    profilesYawRotImg = sum(SUB_YAW_ROT_IMG);
                    avgIntensity = sum(profilesYawRotImg) / size(profilesYawRotImg, 2);
                    profilesYawRotImg = profilesYawRotImg / avgIntensity;

%                     diffYawRotImgs = profilesYawRotImg - preProfilesYawRotImg;
                    % computing the normalized profile of vertical transV image
                    profilesHeightVImg = sum(SUB_HEIGHT_V_IMG,2);
                    avgIntensity = sum(profilesHeightVImg) / size(profilesHeightVImg, 1);
                    profilesHeightVImg = profilesHeightVImg / avgIntensity;

                    diffHeightVImgs = profilesHeightVImg - preProfilesHeightVImg;

                    figure(1)

                    % draw odo map
                    subplot(1, 1, 1, 'replace');
                    hold on
                    plot3((odoMapTrajectory(:,2))* ODO_MAP_X_SCALING , ...
                        (odoMapTrajectory(:,1)) * ODO_MAP_Y_SCALING, ...
                        (odoMapTrajectory(:,3) ) * ODO_MAP_Z_SCALING, '.b');

%                     plot3((gt_x(1:curFrame) - gt_x(1)) * GT_ODO_X_SCALING, ...
%                         (gt_y(1:curFrame) - gt_y(1)) * GT_ODO_Y_SCALING, ...
%                         (gt_z(1:curFrame) - gt_z(1)) * GT_ODO_Z_SCALING, '.r');
                    hold off;
                    view(3);
                    grid on;
%                     view(0, 90);
                    xl = xlabel('odo-map-x');
                    yl = ylabel('odo-map-y');
                    zlabel('odo-map-z');
                    set(xl,'Rotation',15);
                    set(yl,'Rotation',-30);
%                     title('Multilayered Odometry Map');
%                     legend('Result','Truth' ,'1');
%                     axis([-5 110 -5 100 -2 5]);
                    axis on
                    rotate3d on

                    % draw odo map
%                     subplot(1, 2, 2, 'replace');
%                     hold on
%                     plot3((odoMapTrajectory(:,2))* ODO_MAP_X_SCALING , ...
%                         (odoMapTrajectory(:,1)) * ODO_MAP_Y_SCALING, ...
%                         (odoMapTrajectory(:,3) ) * ODO_MAP_Z_SCALING, '.b');
%
% %                     plot3((gt_x(1:curFrame) - gt_x(1)) * GT_ODO_X_SCALING, ...
% %                         (gt_y(1:curFrame) - gt_y(1)) * GT_ODO_Y_SCALING, ...
% %                         (gt_z(1:curFrame) - gt_z(1)) * GT_ODO_Z_SCALING, '.r');
%                     hold off;
%                     grid on;
%                     view(0, 90);
%                     xlabel('odo-map-x');
%                     ylabel('odo-map-y');
%                     zlabel('odo-map-z');
% %                     title('Multilayered Odometry Map (top view)');
% %                     legend('Result','Truth' ,'1');
% %                      axis([-5 110 -5 100]);
%                     axis on
%

                    figure(2)
                    % render the horizontal translational velocity
                    subplot(1, 1, 1, 'replace');
                    plot(subRotVel);
%                     title('Yaw rotational velocity');
                    xlabel('Time');
                    ylabel('vYawRot');
                    axis on

                    figure(3)
                     % render current rot direction
                    subplot(1, 1, 1, 'replace');
                    polar(theta,rho);
                    hold on
                    plot([startPoint(1) curRotDir(curFrame,1)],[startPoint(2) curRotDir(curFrame,2)],'linewidth',1,'color',[0.9 0 0]);
%                     title('Current rotational direction');

                    figure(4)
                    % render the sub image for estimating horizontal translational velocity
                    subplot(3, 4, 1, 'replace');
                    imshow(SUB_TRANS_IMG, []);
                    title('Sub image (vHoriTrans)');
                    xlabel('Width');
                    ylabel('Height');
                    axis on

                    % draw the the normalized profile of horizontal translational image
                    subplot(3,4,5, 'replace');
                    hold on
                    plot(1: size(profilesTransImg,2),profilesTransImg, 'r');
                    plot(1: size(preProfilesTransImg,2),preProfilesTransImg, 'g');
                    hold off
%                     legend('curr', 'g: prev');
                    title('The intensity profiles');
                    xlabel('Width');
                    ylabel('Normalized profile');
                    axis on

                    % render the horizontal translational velocity
                    subplot(3, 4, 9, 'replace');
                    plot(transVelVector);
                    title('Translational velocity');
                    xlabel('Time');
                    ylabel('vTrans');
                    axis on

                    % render the sub image for estimating rotational velocity
                    subplot(3, 4, 2, 'replace');
                    imshow(SUB_YAW_ROT_IMG, []);
                    title('Sub image (vYawRot)');
                    xlabel('Width');
                    ylabel('Height');
                    axis on

                    % draw the the normalized profile of rotational image
                    subplot(3,4,6, 'replace');
                    hold on
                    plot(1: size(profilesYawRotImg,2),profilesYawRotImg, 'r');
                    plot(1: size(preProfilesYawRotImg,2),preProfilesYawRotImg, 'g');
                    hold off
%                     legend('r: curr', 'g: prev');
                    title('The intensity profiles');
                    xlabel('Width');
                    ylabel('Normalized profile');
                    axis on

                                        % render the sub image for estimating pitch velocity
                    subplot(3, 4, 3, 'replace');
                    imshow(SUB_HEIGHT_V_IMG, []);
                    title('Sub image (vPitchRot)');
                    xlabel('Width');
                    ylabel('Height');
                    axis on

                    % draw the the normalized profile of height v image
                    subplot(3,4,7, 'replace');
                    hold on
                    plot(profilesHeightVImg, 1: size(profilesHeightVImg,1), 'r');
                    plot(preProfilesHeightVImg, 1: size(preProfilesHeightVImg,1), 'g');
                    hold off
%                     legend('r: curr', 'g: prev');
                    title('The intensity profiles');
                    xlabel('Width');
                    ylabel('Normalized profile');
                    axis on

                    % render the horizontal translational velocity
                    subplot(3, 4, 11, 'replace');
                    plot(heightVelVector);
                    title('Vertical translational velocity');
                    xlabel('Time');
                    ylabel('vVertTrans');
                    axis on

                    % render the horizontal translational velocity
                    subplot(3, 4, 8, 'replace');
                    plot(sumHeight);
                    title('Height Change');
                    xlabel('Time');
                    ylabel('Height');
                    axis on

                    % render the horizontal translational velocity
                    subplot(3, 4, 12, 'replace');
                    hold on
                    plot(offset_yaw_rot_vector, 'r');
                    plot(offset_height_V_vector, 'g');
                    hold off
                    title('Offset yaw rotation');
                    xlabel('Time');
                    ylabel('Offset');
                    axis on

%                     figure(3)
%
%                      subplot(1,1,1, 'replace');
%                     hold on
%                     plot(1: size(diffHeightVImgs,1),diffHeightVImgs, 'r');
%                     hold off
% %                     legend('r: curr', 'g: prev');
%                     title('The diff between two intensity profiles');
%                     xlabel('Width');
%                     ylabel('Diff');
%                     axis on

                    drawnow
                end
            end
        end
    end
end


function eventLog = ma_executeCoast_goto_func_value(event, initialState, eventNum, considerSoITransitions, soiSkipIds, massLoss, orbitDecay, maData, celBodyData)
%ma_executeCoast_goto_func_value Summary of this function goes here
%   Detailed explanation goes here
    global number_state_log_entries_per_coast;

    old_number_state_log_entries_per_coast = number_state_log_entries_per_coast;
    number_state_log_entries_per_coast = 5; %#ok<NASGU>

    bnds = [0, event.maxPropTime];
    funcNoAbs = @(dt) getCoastFuncValue(dt, event.funcHandle, event.coastToValue, initialState, eventNum, considerSoITransitions, soiSkipIds, massLoss, orbitDecay, maData, celBodyData, false);
    funcAbs = @(dt) getCoastFuncValue(dt, event.funcHandle, event.coastToValue, initialState, eventNum, considerSoITransitions, soiSkipIds, massLoss, orbitDecay, maData, celBodyData, true);

    odeFunc = @(t,y) odefun(t,y);
    evtFnc = @(t,y) odeEventsFun(t,y,funcNoAbs);
    options = odeset('RelTol',1E-6, 'AbsTol',1E-6, 'Events',evtFnc, 'MaxStep',abs(diff(bnds))/50);
    [~,~,te,~,ie] = ode45(odeFunc, bnds, bnds(1), options);

    if(isempty(ie)) %fall back to old way of doing things
        options = optimset('TolX',1E-6);
        [x, fval, exitflag] = fminbnd(funcAbs, bnds(1), bnds(2), options);

        if(exitflag == 0 || abs(fval) > 1E-4)
            x = event.maxPropTime;
        end
    else
        x = te(1);
    end

    number_state_log_entries_per_coast = old_number_state_log_entries_per_coast;
    eventLog = ma_executeCoast_goto_dt(x, initialState, eventNum, considerSoITransitions, soiSkipIds, massLoss, orbitDecay, celBodyData);
end

function ydot = odefun(~,~)
    ydot = 1;
end

function [value,isterminal,direction] = odeEventsFun(t,~, func)
    value = func(t);
    isterminal = 1;
    direction = 0;
end

function value = getCoastFuncValue(dt, funcHandle, desiredValue, initialState, eventNum, considerSoITransitions, soiSkipIds, massLoss, orbitDecay, maData, celBodyData, useAbs)
    eventLog = ma_executeCoast_goto_dt(dt, initialState, eventNum, considerSoITransitions, soiSkipIds, massLoss, orbitDecay, celBodyData);
    finalState = eventLog(end,:);

    [datapt, ~, ~, taskStr, refBodyInfo, otherSC, station] = funcHandle(finalState, false, maData);

    if(useAbs)
        value = abs(datapt - desiredValue);
    else
        value = datapt - desiredValue;
    end
end


function out = PlotPath(state, scalling, mapName)
% uses B-spline interpolation
    path = state.path';
    path(:,3) = 1;
    path(:,4) = 1;

    if nargin <3
        scalling = 1;
        imag = state.map;
    else
        tmp = LoadMap(  strcat(mapName, '.png'), 1);
        imag = tmp.map;
    end

    if scalling ~= 1
        path(:,1) = path(:,1)-2.5;
        path(:,2) = path(:,2)-2.5;
        pathInterpolated = BSpline(path*scalling);
    else
        pathInterpolated = path;
    end
    %%5
    for it = pathInterpolated'
        a = round(it);
        imag(a(1), a(2)) = 0.6;
    end
    figure(100)
    out.handle = imshow(imag);
    out.path = pathInterpolated;

end


function mv = multiVoxelUI(view, roi , scans, dt);
%   mv = multiVoxelUI(view, [roi , scans, dt])
%
% multiVoxelUI: user interface for displaying time course / amplitude
% data across roi  within an ROI.
%
%
%
% ARGS:
% input:
%       view: mrVista view struct
%
%       roi : coords (by the current view's conventions) of the roi
%       from which to plot data. May also be a string providing the name of
%       the ROI to plot. Default is view's currently-selected ROI.
%
%       scans: scans in the current view/data dt to plot. Default is
%       currently-selected scan. For multiple scans,  will concatenate
%       together after detrending (using the detrend options saved in the
%       dataTYPES struct in the mrSESSION file).
%
%       dt: data type to use. Defaults to currently selected data
%       type.
%
%output:
%       h: handle to time course interface.
%
%       data: array containing time course data from selected voxels /scans.
%
% More notes:
% This is currently implemented as a small set of linked analysis tools,
% all starting with the prefix 'tc_'. This particular piece of code
% decides,  based on the number of input args,  whether to open a new
% mv user interface or just update an existing one,  and then refreshes the
% current mv view according to the time course data and user prefs.
%
% Here's how it decides to open a new UI or not: if a view is passed as the
% first argument,  it opens a new one. If there are no input args,  it
% assumes the current figure has the interface and refreshes it. (This is
% called by all the callbacks on the interface).
%
% The time course data,  condition information,  user prefs,  and handles to
% important controls are all kept in a mv struct stored in the user
% interface's 'UserData' field. I currently treat each UI as a separate
% figure; though in time I may find it useful to group several interfaces
% on multiple axes in the same figure.

% 1/3/04 ras: wrote it.
% 1/04: added signal to noise ratios,  ability to switch off different cond
% displays,  shift onsets,  change colors
% 2/04: added ability to read multiple scans / concat parfiles if it uses
% them
% 2/23/04: broke up into several functions,  under mrLoadRet sub-section
% Plots/TimeCourseUI. Also made an offshoot of this,  fancyplot2,  which
% doesn't use the mrLoadRet view info,  for kalanit. Renamed multiVoxelUI
% (previously ras_tc -- saved that as an older,  stable version).
mrGlobals

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% 1ST DECISION: extract new time course data (w/ corresponding
% axes figure) or just re-render existing data? This is decided by
% the # args: if 1,  update data; otherwise,  just render.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if nargin >= 1
    openFigFlag = 1;
else
    openFigFlag = 0;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Open the figure if needed %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if openFigFlag
    if notDefined('dt')
        dt = viewGet(view, 'curDataType');
    end

    if notDefined('scans')
        scans = er_getScanGroup(view); %viewGet(view, 'curScan');
    end

    if notDefined('roi')
        r = viewGet(view, 'curRoi');
        if r==0
            myErrorDlg('Sorry, you need to load an ROI first.')
        end
        roi = view.ROIs(r);
    end

    % want roi to be a struct: parse
    % how it's passed in and make a struct
    roi = tc_roiStruct(view, roi);

    % init a voxel data struct from params
    % (this includes loading the data)
    mv = mv_init(view, roi, scans, dt);

    % put up the interface window
    mv = mv_openFig(mv);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% RENDER TIME COURSE (taking into account prefs as specified in the
% ui)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% get the data struct -- in the current axes' UserData
mv = get(gcf, 'UserData');

% clear previous axes in the figure
old = findobj('Type', 'axes', 'Parent', gcf);
old = [old; findobj('Type', 'uicontrol', 'Parent', gcf)];
delete(old);

% legend (kind of a hack -- legends are tricky)
if mv.params.legend==1
    sel = find(tc_selectedConds(mv));
    co = [];
    for i = 1:length(sel)
        % The legend text should be the specified condition names
        leg{i} = mv.condNames{sel(i)};
        % remove underscores -- the teX interpreter is weird about em
        leg{i}(findstr(leg{i}, '_')) = ' ';
        % also get appropriate colors
        co = [co; mv.condColors{sel(i)}];
    end
    set(gca, 'NextPlot', 'ReplaceChildren');
    if length(sel) > 0
        % Plot some random data,  just to get the legend put up
        set(gca, 'ColorOrder', co);
        plot(rand(2, length(sel)), 'LineWidth', 4);
        legend(gca, leg, -1);
    end
end

% select the current plot type
eval( get(mv.ui.plotHandles(mv.ui.plotType), 'Callback') )

% stash the updated data struct
set(gcf, 'UserData', mv);

return


function ChannelMat = in_channel_besa_eps(ChannelFile)
% IN_CHANNEL_BESA_EPS:  Read 3D cartesian positions for a set of electrodes from an couple .eps/ela BESA file.
%
% USAGE:  ChannelMat = in_channel_besa_eps(ChannelFile)
%
% INPUTS:
%     - ChannelFile : Full path to the file (either .ela or .eps file)
%
% FORMAT:
%     - .ELA file: contains the labels
%     - .EPS file: contains the positions of the electrodes

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

% Check file extension
[fPath, fBase, fExt] = bst_fileparts(ChannelFile);
switch lower(fExt)
    case '.eps'
        epsFile = ChannelFile;
        elaFile = bst_fullfile(fPath, [fBase, '.ela']);
    case '.ela'
        elaFile = ChannelFile;
        epsFile = bst_fullfile(fPath, [fBase, '.eps']);
end

% ELP: Get the positions
if ~isempty(epsFile) && file_exist(epsFile)
    ChannelMat = in_channel_ascii(epsFile, {'-Y','X','Z'}, 0, .01);
    ChannelMat.Comment = 'BESA channels';
else
    disp('BESA> Error: EPS file is missing');
    ChannelMat = [];
end

% ELA: Get the labels/types
if ~isempty(elaFile) && file_exist(elaFile)
    % === READ FILE ===
    % Open file
    fid = fopen(elaFile, 'r');
    if (fid == -1)
        error('Cannot open .ela file.');
    end
    % Read file
    ela = textscan(fid, '%s %s');
    % Close file
    fclose(fid);

    % === FILE TYPE ===
    nChan = length(ela{1});
    % ONE column (channel names)
    if all(cellfun(@isempty, ela{2}))
        chLabels = ela{1};
        % By default, all channels are considered as EEG
        chTypes  = repmat({'EEG'}, [nChan, 1]);
    % TWO columns (channel types+names)
    else
        chTypes  = ela{1};
        chLabels = ela{2};
    end

    % === SET THE LABELS ===
    % Create an empty Channel structure if EPS file is missing
    if isempty(ChannelMat)
        ChannelMat = db_template('channelmat');
        ChannelMat.Comment = 'BESA channels';
        ChannelMat.Channel = repmat(db_template('channeldesc'), [1, nChan]);
        [ChannelMat.Channel.Loc] = deal([0;0;0]);
    end
    % Set the labels
    for i = 1:length(chLabels)
        ChannelMat.Channel(i).Name = chLabels{i};
    end
    % Set the types
    for i = 1:length(chLabels)
        ChannelMat.Channel(i).Type = chTypes{i};
    end
else
    disp('BESA> Error: ELA file is missing');
end


% readneuronedata() -  Read NeurOne Binary files
%
% Usage:  >> data = readneuronedata(dataFiles,nChannels,chans)
%
% ===================================================================
% Inputs:
%         dataFiles      - A cell structure containing full paths for data
%                          files to be read. This is input argument is
%                          required.
%         nChannels      - Another required input argument:
%                          the total number of channels.
%         chans          - A vector containing the channel numbers to be
%                          read. They have to be arranged in an ascending
%                          order. This input argument is optional.
% ====================================================================
% Output:
%         data           - A matrix where all numerical data is stored.
%                          The data is arranged so that the data for each
%                          channel is stored in a row according
%                          to its number.
%
% ========================================================================
% NOTE:
% This file is part of the NeurOne data import plugin for EEGLAB.
% ========================================================================
%
% Current version: 1.0.3.4 (2016-06-17)
% Author: Mega Electronics

function data = readneuronedata(dataFiles,nChannels,chans)
%% Parse input arguments
p = inputParser;
p.addRequired('dataFiles', @iscellstr);
p.addRequired('nChannels', @isnumeric);
p.addOptional('chans', 0, @isnumeric);

p.parse(dataFiles, nChannels, chans);
arglist=p.Results;

%% Prepare reading data
nDataFiles = numel(arglist.dataFiles);
chans = arglist.chans;
nChannels = arglist.nChannels;

if chans==0
    chans=1:nChannels;
end

% Get all file sizes
fSize=zeros(1,nDataFiles);
tmp={};

for k=1:nDataFiles
    tmp{k,1}=dir(dataFiles{k,1});
    fSize(1,k)=tmp{k,1}.bytes;
end

fSizes=cumsum(fSize);

% Total size of the data
totalSize=sum(fSize);
% Total number of data points (per channel)
dataPntsTotal=(totalSize/4)/nChannels;
% Number of data points in each binary file (per channel)
dataPnts=(fSize./4)./nChannels;

%% Read binary data

fprintf('Allocating memory...\n')
data=zeros(numel(chans),dataPntsTotal);

% Option 1: Read all channels at once (faster method)
if numel(chans)==nChannels
    fprintf('Reading data...');
    data=data(:);
    readidx=[0 (fSizes/4)];
    for n=1:nDataFiles
        fid = fopen([dataFiles{n}], 'rb');
        data((1+readidx(n)):readidx(n+1),1)=fread(fid, ...
            fSize(n)/4,'int32');
        fclose(fid);
    end
    data=reshape(data,nChannels,dataPntsTotal);
    fprintf('%s','Done');
else
    % Option 2: Read only specific channels to save memory
    fprintf('Reading data... 0%%');
    readidx=[0 (fSizes/4)./nChannels];

    % Read channels one by one
    for k=1:numel(chans)
        for n=1:nDataFiles

            fid = fopen([dataFiles{n}], 'rb');
            fseek(fid, 4*(chans(k) - 1), 'bof');
            data(k,(1+readidx(n)):readidx(n+1)) = fread(fid, ...
                dataPnts(n),'int32',4*(nChannels-1))';
            fclose(fid);
        end

    % Print progress to the command window
    ii = floor(k/numel(chans)*100);
        if ii<10
            ii = [num2str(ii) '%'];
            fprintf(1,'\b\b%s',ii);
        elseif ii==100;
            ii = 'Done';
            fprintf(1,'\b\b\b%s',ii);
        else
            ii = [num2str(ii) '%'];
            fprintf(1,'\b\b\b%s',ii);

        end
    end
end

fprintf('\n');

%% Convert data from nanovolts to microvolts
data=data./1000;

end


function y = conj( x )

%Disciplined convex/geometric programming information for CONJ:
%   CONJ(X) imposes no convexity restrictions on its arguments. However,
%   since CONJ(X)=X when X is real, it is only useful for complex
%   affine expressions.

y = cvx( x.size_, conj( x.basis_ ) );

% Copyright 2005-2014 CVX Research, Inc.
% See the file LICENSE.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


function y = conj( x )

%Disciplined convex/geometric programming information for CONJ:
%   CONJ(X) imposes no convexity restrictions on its arguments. However,
%   since CONJ(X)=X when X is real, it is only useful for complex
%   affine expressions.

y = cvx( x.size_, conj( x.basis_ ) );

% Copyright 2005-2016 CVX Research, Inc.
% See the file LICENSE.txt for full copyright information.
% The command 'cvx_where' will show where this file is located.


% ------------------------------------------------------------------------
%      Analyze non-linear ROIs that were clicked as 'object points'
% ------------------------------------------------------------------------

%% ENTER PARAMETERS AND FILE LOCATION

% file location of object points
save_folder = 'C:\Drive\Histology\for tutorial\Richards\processed';

% directory of reference atlas files
annotation_volume_location = 'C:\Drive\Histology\annotation_volume_10um_by_index.npy'; % from the allen inst (see readme)
structure_tree_location = 'C:\Drive\Histology\structure_tree_safe_2017.csv'; % located in github repo
CCF_to_FP_location =  'C:\Drive\Histology\CCF_to_FP.csv'; % located in github repo
FP_table_location = 'C:\Drive\Histology\FP_table_Chon_2020.csv'; % located in github repo
chon_images_loc = 'C:\Drive\Histology\Suppl_File1_Labels'; % from chon et al (supplementary data 4, https://www.nature.com/articles/s41467-019-13057-w)

% name of the saved object points
object_save_name_suffix = '';

% either set to 'all' or a list of indices from the clicked objects in this file, e.g. [2,3]
objects_to_analyze = 'all';

% plane used to view when points were clicked ('coronal' -- most common, 'sagittal', 'transverse')
plane = 'coronal';

% brain figure black or white
black_brain = true;

%% LOAD THE REFERENCE ANNOTATIONS AND PROBE POINTS

% load the reference brain annotations
if ~exist('av','var') || ~exist('st','var')
    disp('loading reference atlas...')
    av = readNPY(annotation_volume_location);
    st = loadStructureTree(structure_tree_location);
end
if ~exist('CCFtoFPtable','var') || ~exist('FPtable','var')
    CCFtoFPtable = loadCCFtoFP(CCF_to_FP_location);
    FPtable = loadFPtable(FP_table_location);
end

% load object points
objectPoints = load(fullfile(save_folder, ['probe_points' object_save_name_suffix]));

% determine which objects to analyze
if strcmp(objects_to_analyze,'all')
    objects = 1:size(objectPoints.pointList.pointList,1);
else
    objects = objects_to_analyze;
end

%% BRING UP THE RELEVANT DATA FOR EACH PROBE POINTS, FOR FURTHER ANALYSIS

% initialize cell array containing info on each clicked point
if length(objects) > 1
    roi_annotation = cell(length(objects),1);
    roi_location = cell(length(objects),1);
end

% generate needed values
bregma = allenCCFbregma(); % bregma position in reference data space
atlas_resolution = 0.010; % mm

% plot brain grid
ProbeColors = [1 1 1; 1 .75 0;  .3 1 1; .4 .6 .2; 1 .35 .65; .7 .7 1; .65 .4 .25; .7 .95 .3; .7 0 0; .6 0 .7; 1 .6 0];
% order of colors: {'white','gold','turquoise','fern','bubble gum','overcast sky','rawhide', 'green apple','purple','orange','red'};
fwireframe = plotBrainGrid([], [], [], black_brain); hold on;
fwireframe.InvertHardcopy = 'off';

for object_num = objects

    selected_object = objects(object_num);

    % get the object points for the currently analyzed object
    if strcmp(plane,'coronal')
        curr_objectPoints = objectPoints.pointList.pointList{selected_object,1}(:, [3 2 1]);
    elseif strcmp(plane,'sagittal')
        curr_objectPoints = objectPoints.pointList.pointList{selected_object,1}(:, [1 2 3]);
    elseif strcmp(plane,'transverse')
        curr_objectPoints = objectPoints.pointList.pointList{selected_object,1}(:, [1 3 2]);
    end

    % plot points on the wire frame brain
    figure(fwireframe); hold on
    hp = plot3(curr_objectPoints(:,1), curr_objectPoints(:,3), curr_objectPoints(:,2), '.','linewidth',2, 'color',[ProbeColors(object_num,:) .2],'markers',10);

    % use the point's position in the atlas to get the AP, DV, and ML coordinates
    ap = -(curr_objectPoints(:,1)-bregma(1))*atlas_resolution;
    dv = (curr_objectPoints(:,2)-bregma(2))*atlas_resolution;
    ml = (curr_objectPoints(:,3)-bregma(3))*atlas_resolution;

    roi_location_curr = [ap dv ml];

    % initialize array of region annotations
    roi_annotation_curr = cell(size(curr_objectPoints,1),3);
    roi_annotation_curr_FP = cell(size(curr_objectPoints,1),3);

    % loop through every point to get ROI locations and region annotations
    for point = 1:size(curr_objectPoints,1)

        % find the annotation, name, and acronym of the current ROI pixel
        ann = av(curr_objectPoints(point,1),curr_objectPoints(point,2),curr_objectPoints(point,3));
        name = st.safe_name{ann};
        acr = st.acronym{ann};

        roi_annotation_curr{point,1} = ann;
        roi_annotation_curr{point,2} = name;
        roi_annotation_curr{point,3} = acr;

        % find the annotation, name, and acronym of the current ROI pixel
        [ann_FP, name_FP, acr_FP] = CCF_to_FP(curr_objectPoints(point,1), curr_objectPoints(point,2), curr_objectPoints(point,3), CCFtoFPtable, FPtable, chon_images_loc);

        roi_annotation_curr_FP{point,1} = ann_FP;
        roi_annotation_curr_FP{point,2} = name_FP;
        roi_annotation_curr_FP{point,3} = acr_FP;

    end

    % save results in cell array
    if length(objects) > 1
        roi_annotation{object_num} = roi_annotation_curr;
        roi_location{object_num} = roi_location_curr;
    else
        roi_annotation = roi_annotation_curr;
        roi_location = roi_location_curr;
    end

    % display results in a table
    disp(['Clicked points for object ' num2str(selected_object)])
    roi_table = table(roi_annotation_curr(:,2),roi_annotation_curr(:,3), roi_annotation_curr_FP(:,2),roi_annotation_curr_FP(:,3),...
                        roi_location_curr(:,1),roi_location_curr(:,2),roi_location_curr(:,3), roi_annotation_curr(:,1), roi_annotation_curr_FP(:,1),...
         'VariableNames', {'CCF_name', 'CCF_acronym', 'FP_name', 'FP_acronym', 'AP_location', 'DV_location', 'ML_location', 'avIndex', 'fpIndex'});
     disp(roi_table)

end

% now, use roi_location and roi_annotation for your further analyses