% MakeJAABAResultsMovie(expdir,classifierparamsfile,'nframesperseg',500,'nintervals',10,'aviname','JAABAResultsMovie_FCF_cantons_1500002_None_Rig1Plate15BowlA_20120519T160453.avi')

function MakeJAABAResultsMovie(expdir,classifierparamsfile,varargin)

%% parse parameters

frac_bottom = .1;
border_bottom = .05;
border_middle = .02;
trx_linewidth = 1;
target_linewidth = 2;
bar_width = .8;
trx_rad = 25;
pxborder = [];
aborder = 1;
trxcolor = [0,0,0];
figpos = [50,50,1500,1000];
barbackcolor = [.5,.5,.5];
textcolor = [0,0,0];
fontsize = 30;
aback = .25;
printnone = true;
mintrajlength = 100;
nintervals = 1;
compression = 'None';
behaviorname_dict = cell(0,2);
allowedcompressions = {'Indeo3', 'Indeo5', 'Cinepak', 'MSVC', 'RLE', 'None','Uncompressed AVI','Motion JPEG AVI'};
useVideoWriter = exist('VideoWriter','file');
aviname = '';
fps = 10;
usemencoder = true;
animaltype='fly';
headmarksize=10;
bottomtextfontsize=8;

[framestarts,nframesperseg,fracstarts,fracperseg,targets,hfig,...
  pxwidthradius,pxheightradius,awidthradius,aheightradius,frac_bottom,...
  trx_linewidth,target_linewidth,...
  behavior_colors,behavior_cm,...
  border_bottom,border_middle,bar_width,...
  trx_rad,score_norm_prctile,...
  pxborder,aborder,...
  trxcolor,...
  figpos,...
  barbackcolor,...
  behaviorweight,...
  textcolor,fontsize,...
  pxback,aback,...
  printnone,...
  mintrajlength,...
  nintervals,...
  useVideoWriter,...
  avifileTempDataFile,aviname,fps,...
  usemencoder,titlepagetext,...
  titlepausetime,...
  behaviorname_dict,...
  behaviororder,...
  textprefix,...
  textsuffix,...
  fixedaxis,...
  animaltype,...
  headmarksize,...
  bottomtextfontsize,...
  isstjaaba] = ...
  myparse(varargin,'framestarts',[],'nframesperseg',[],...
  'fracstarts',[],'fracperseg',1/60,...
  'targets',[],...
  'hfig',1,...
  'pxwidthradius',[],...
  'pxheightradius',[],...
  'awidthradius',3,...
  'aheightradius',3,...
  'frac_bottom',frac_bottom,...
  'trx_linewidth',trx_linewidth,...
  'target_linewidth',target_linewidth,...
  'behavior_colors',zeros(0,3),...
  'behavior_cm',@(x) jet(x)*.7,...
  'border_bottom',border_bottom,...
  'border_middle',border_middle,...
  'bar_width',bar_width,...
  'trx_rad',trx_rad,...
  'score_norm_prctile',80,...
  'pxborder',pxborder,...
  'aborder',aborder,...
  'trxcolor',trxcolor,...
  'figpos',figpos,...
  'barbackcolor',barbackcolor,...
  'behaviorweight',[],...
  'textcolor',textcolor,...
  'fontsize',fontsize,...
  'pxback',[],...
  'aback',aback,...
  'printnone',printnone,...
  'mintrajlength',mintrajlength,...
  'nintervals',nintervals,...
  'useVideoWriter',useVideoWriter,...
  'avifileTempDataFile','',...
  'aviname',aviname,...
  'fps',fps,...
  'usemencoder',usemencoder,...
  'titlepagetext',{},...
  'titlepausetime',2,...
  'behaviorname_dict',behaviorname_dict,...
  'behaviororder',{},...
  'textprefix','',...
  'textsuffix','',...
  'fixedaxis',[],...
  'animaltype',animaltype,...
  'headmarksize',headmarksize,...
  'bottomtextfontsize',bottomtextfontsize,...
  'isstjaaba',false);

classifierparams = ReadClassifierParamsFile(classifierparamsfile);
nbehaviors = numel(classifierparams);
behaviors = cell(1,nbehaviors);
scorefns = cell(1,nbehaviors);
labelfns = cell(1,nbehaviors);
for i = 1:nbehaviors,
  behaviors{i} = classifierparams(i).behaviors.names{1};
  j = find(strcmp(behaviors{i},behaviorname_dict(:,1)),1);
  if ~isempty(j),
    behaviors{i} = behaviorname_dict{j,2};
  else
    behaviors{i} = regexprep(behaviors{i},'(_|^)([a-z])','${upper($2)}');
  end
  scorefn = classifierparams(i).file.scorefilename;
  scorefns{i} = regexprep(scorefn,'\.mat$','');
  labelfns{i} = regexprep(scorefns{i},'score','label','preservecase','once');
end

if ~isempty(behaviororder),
  [ism,orderidx] = ismember(behaviors,behaviororder);
  orderidx(~ism) = inf;
  [~,orderidx] = sort(orderidx);
  behaviors = behaviors(orderidx);
  scorefns = scorefns(orderidx);
  labelfns = labelfns(orderidx);
  classifierparams = classifierparams(orderidx);
end

if ~ischar(compression),
  compression = '';
end
if ~ispc && ~useVideoWriter,
  compression = 'None';
end
if ~isempty(compression) && ~any(strcmpi(compression,allowedcompressions)),
  fprintf('Unknown compressor %s\n',compression);
  compression = '';
end

if isempty(aviname),
  [~,experiment_name] = fileparts(expdir);
  aviname = fullfile(expdir,sprintf('JAABAResultsMovie_%s.avi',experiment_name));
end

%% create trx structure

trx = Trx('trxfilestr',classifierparams(1).file.trxfilename,...
  'moviefilestr',classifierparams(1).file.moviefilename,...
  'perframedir',classifierparams(1).file.perframedir);
if isfield(classifierparams,'perframe') && isfield(classifierparams(1).perframe,'params'),
  trx.SetPerFrameParams(classifierparams(1).perframe.params);
end
if isfield(classifierparams,'perframe') && isfield(classifierparams(1).perframe,'landmark_params'),
  trx.SetLandmarkParams(classifierparams(1).perframe.landmark_params);
end

trx.AddExpDir(expdir,'openmovie',false);

%% for video reading

moviename = fullfile(expdir,trx.moviefilestr);
if exist(moviename)
  [readframe,~,fid,headerinfo] = get_readframe_fcn(moviename);
  im = readframe(1);
  [headerinfo.nr,headerinfo.nc,~] = size(im);
else

  maxx = 0; maxy = 0;
  for ndx = 1:trx.nflies
    maxx = max(max(trx(ndx).x+trx(ndx).a*2),maxx);
    maxy = max(max(trx(ndx).y+trx(ndx).a*2),maxy);
  end
  nr = ceil(maxy);
  nc = ceil(maxx);
  headerinfo.nr = nr;
  headerinfo.nc = nc;
  ncolors = 1;
  nframes = max([trx.endframe]);
  readframe = @(x)(uint8(256*ones(nr,nc)));
  fid = 0;

end

%% choose frames, target to show

% how many intervals
nintervals = max([nintervals,numel(targets),numel(nframesperseg),...
  numel(framestarts),numel(fracperseg),numel(fracstarts)]);

% choose frame starts evenly spaced throughout the movie
if isempty(framestarts) && isempty(fracstarts),
  framestarts = round(linspace(max(trx.firstframe),min(trx.endframe),nintervals));
end

if ~isempty(nframesperseg) && numel(nframesperseg) < nintervals,
  nframesperseg = [nframesperseg,nframesperseg(end)+zeros(1,nintervals-numel(nframesperseg))];
end

% make sizes match
if isempty(targets),
  for segi = 1:nintervals,
    if numel(nframesperseg) >= segi,
      mintrajlength_curr = max([mintrajlength,nframesperseg(segi)]);
      idx = trx.nframes>mintrajlength_curr;
      if numel(framestarts) >= segi,
        idx = idx & trx.firstframe<=framestarts(segi) & ...
          trx.endframe >= framestarts(segi);
      end
      idx = setdiff(find(idx),targets(1:segi-1));
      targets(segi) = randsample(idx,1);
    end
  end
elseif numel(targets) < nintervals,
  targets = [targets,targets(end)+zeros(1,nintervals-numel(targets))];
end
if numel(fracperseg) < nintervals,
  fracperseg = [fracperseg,fracperseg(end)+zeros(1,nintervals-numel(fracperseg))];
end
if numel(fracstarts) < nintervals && ~isempty(fracstarts),
  fracstarts = [fracstarts,fracstarts(end)+zeros(1,nintervals-numel(fracstarts))];
end

frameends = nan(1,nintervals);
for targeti = 1:nintervals,
  target = targets(targeti);
  firstframe = trx.firstframes(target);
  endframe = trx.endframes(target);
  nframes_curr = endframe - firstframe + 1;

  if numel(nframesperseg) < targeti || isnan(nframesperseg(targeti)),
    nframesperseg(targeti) = round(nframes_curr*fracperseg(targeti));
  end
  if numel(framestarts) < targeti || isnan(framestarts(targeti)),
    framestarts(targeti) = firstframe + round(fracstarts(targeti)*nframes_curr);
  end
  frameends(targeti) = max(firstframe,min(endframe,framestarts(targeti)+nframesperseg(targeti)-1));
end
%nframesperseg = frameends-framestarts+1;

%% get the score normalizations

score_norms = nan(nbehaviors,1);
for i = 1:nbehaviors,
  tmp = trx(targets).(scorefns{i});
  if iscell(tmp),
    tmp = [tmp{:}];
  end
  score_norms(i) = prctile(abs(tmp),score_norm_prctile);
end

%% choose weights for each behavior

if isempty(behaviorweight),
  behaviorweight = nan(nbehaviors,1);
  for behaviori = 1:nbehaviors,
    tmp = trx(targets).(labelfns{behaviori});
    if iscell(tmp),
      tmp = [tmp{:}];
    end
    behaviorweight(behaviori) = nnz(tmp>0);
  end
  behaviorweight = behaviorweight / sum(behaviorweight);
end
if numel(behaviorweight) == 1,
  behaviorweight = ones(nbehaviors,1);
end
behaviorweight = behaviorweight(:);

%% choose colors for each behavior

nadd = nbehaviors - size(behavior_colors,1);
if nadd > 0,
  tmp = behavior_cm(256);
  behavior_colors = cat(1,behavior_colors,tmp(round(linspace(1,256,nadd)),:));
end

%% initialize plot

if ishandle(hfig),
  set(0,'CurrentFigure',hfig);
  clf(hfig);
else
  hfig = figure(hfig);
end
set(hfig,'Units','pixels','Position',figpos);

mainpos = [0,frac_bottom+border_bottom+border_middle,1,1-frac_bottom-border_bottom-border_middle];
haxmain = axes('Position',mainpos);
haxbottom = axes('Position',[0,border_bottom,1,frac_bottom]);

t = framestarts(1);
im = readframe(t);
ncolors = size(im,3);
if ncolors == 1,
  him = imagesc(im2double(im),'Parent',haxmain,[0,1]);
  colormap(haxmain,'gray');
else
  him = image(im2double(im),'Parent',haxmain);
end
hold(haxmain,'on');
%axis(haxmain,'image');
htrx = plot(haxmain,nan,nan,'.-','LineWidth',trx_linewidth,'color',trxcolor);
htarget = plot(haxmain,nan,nan,'-','LineWidth',target_linewidth,'color','k');
if strcmp(animaltype,'larva')
    htarget_extra=plot(haxmain,nan,nan,'.','MarkerSize',headmarksize,'color','k');
end
if strcmp(animaltype,'larva_highres')
    htarget_extra(1)=plot(haxmain,nan,nan,'.-','MarkerSize',headmarksize,'color','k');
    htarget_extra(2)=plot(haxmain,nan,nan,'.','MarkerSize',headmarksize,'color','b');
end
set(haxmain,'XTick',[],'YTick',[]);
i = t+trx(target).off;

a = trx(targets).a;
if iscell(a),
  a = [a{:}];
end
meana = nanmedian(a)*4;
if isempty(pxwidthradius),
  pxwidthradius = awidthradius*meana;
end
if isempty(pxheightradius),
  pxheightradius = aheightradius*meana;
end
if isempty(pxborder),
  pxborder = aborder*meana;
end
if isempty(pxback),
  pxback = meana*aback;
end

set(haxmain,'Units','pixels');
mainpos = get(haxmain,'Position');
set(haxmain,'Units','normalized');
pxheightradius1 = min((pxwidthradius)*mainpos(4) / mainpos(3),(headerinfo.nr-1)/2);
pxwidthradius1 = min((pxheightradius)*mainpos(3) / mainpos(4),(headerinfo.nc-1)/2);
pxheightradius = max(pxheightradius1,pxheightradius);
pxwidthradius = max(pxwidthradius1,pxwidthradius);

% if we want a static axis
if ~isempty(fixedaxis),
   ax = fixedaxis;
else
   ax = ResetAxis(headerinfo.nc/2,headerinfo.nr/2,1,headerinfo.nr,headerinfo.nc,pxwidthradius,pxheightradius,pxborder);
end

hinfo = text(ax(1),ax(4),'','Parent',haxmain,'Color',textcolor,...
  'FontUnits','pixels','FontSize',fontsize,...
  'HorizontalAlignment','left',...
  'VerticalAlignment','bottom');

axis(haxmain,'image');
axis(haxmain,ax);

hbar = nan(1,nbehaviors);
hbarback = nan(1,nbehaviors);
for i = 1:nbehaviors,
  hbarback(i) = patch(i+bar_width/2*[-1,1,1,-1,-1],zeros(1,5)+.1,barbackcolor,'EdgeColor','none','Parent',haxbottom);
  if i == 1,
    hold(haxbottom,'on');
  end
  hbar(i) = patch(i+bar_width/2*[-1,1,1,-1,-1],zeros(1,5)+.1,behavior_colors(i,:),'EdgeColor','none','Parent',haxbottom);
end
htext = text(nan,nan,'','Parent',haxmain,'Color',textcolor,...
  'FontUnits','pixels','FontSize',fontsize);
plot(haxbottom,[0,nbehaviors+1],[0,0],'--','color',[.5,.5,.5]);
set(haxbottom,'XTick',1:nbehaviors,'XTickLabel',behaviors,'YTick',[],...
  'XLim',[.5,nbehaviors+.5],'YLim',[-1,1],'XColor','w','Color','k','TickLength',[0,0],'FontSize',bottomtextfontsize);

set(hfig,'Color','k');

if isstjaaba,
  set([htrx,htarget],'Visible','off');
  textpos = [size(im,2)/2,size(im,1)];
end

%% update plots

isfirstframe = true;

if ~isempty(titlepagetext),
  titlepagetext2 = sprintf(', %s',titlepagetext{:});
end

for segi = 1:numel(framestarts),

  % data for this target
  target = targets(segi);

  nframes_curr = trx(target).nframes;
  x = trx(target).x;
  y = trx(target).y;
  a = trx(target).a;
  b = trx(target).b;
  if strcmp(animaltype,'larva')
      pos.xspine=trx(target).xspine;
      pos.yspine=trx(target).yspine;
  end
   if strcmp(animaltype,'larva_highres')
      pos.xspine=trx(target).xspine;
      pos.yspine=trx(target).yspine;
      pos.xcontour=trx.xcontour{target};
      pos.ycontour=trx.ycontour{target};
  end
  try
     sex = trx(target).sex;
  catch
     sex = repmat({'?'},1,numel(x));
  end
  theta = trx(target).theta;
  % try to make sector somewhat continuous
  fil = [0.000263865082737   0.106450771973592   0.786570725887342   0.106450771973592   0.000263865082737];
  fil = fil / sum(fil(:));
  smooththeta = imfilter(theta,fil,'same','replicate');
  sector = floor(modrange((smooththeta-pi/8),0,2*pi)/pi*4)+1;
  % close holes
  if numel(sector) > 2,
    for i = 1:numel(sector),
      if ((i==1) || (i==numel(sector)) || (sector(i-1)==sector(i+1))) && ...
          (i==1 || sector(i)~=sector(i-1)) && ...
          (i==numel(sector) || sector(i)~=sector(i+1)) && ...
          (i==1 || abs(modrange(theta(i)-theta(i-1),-pi,pi))<pi/12) && ...
          (i==numel(sector) || abs(modrange(theta(i)-theta(i+1),-pi,pi))<pi/12),
        if i == 1,
          sector(i) = sector(i+1);
        else
          sector(i) = sector(i-1);
        end
      end
    end
  end

  scores = nan(nbehaviors,nframes_curr);
  labels = nan(nbehaviors,nframes_curr);
  for behaviori = 1:nbehaviors,
    scores(behaviori,:) = min(1,max(-1,trx(target).(scorefns{behaviori})/score_norms(behaviori)));
    labels(behaviori,:) = double(trx(target).(labelfns{behaviori}))*2-1;
  end

  for t = framestarts(segi):frameends(segi),
      im = readframe(t);
      set(him,'CData',im2double(im));
      i = t+trx(target).off;

      i0 = max(1,min(nframes_curr,i-trx_rad(1)));
      i1 = max(1,min(nframes_curr,i+trx_rad(1)));

      set(htrx,'XData',x(i0:i1),...
          'YData',y(i0:i1));
      switch animaltype
          case 'fly'
              updatefly(htarget,x(i),y(i),...
                  theta(i),a(i),b(i));
          case 'larva'
              pos2=struct('xspine',pos.xspine(:,i),'yspine',pos.yspine(:,i));
              updatelarvaspecies(htarget,htarget_extra,pos2)
          case 'larva_highres'
              pos2=struct('xspine',pos.xspine(:,i),'yspine',pos.yspine(:,i),'xcontour',pos.xcontour{i},'ycontour',pos.ycontour{i});
              updatelarvacontour(htarget,htarget_extra,pos2);
          otherwise
              error('Unknown animal type %s',animaltype);
      end

    for behaviori = 1:nbehaviors,
      set(hbar(behaviori),'YData',[0,0,1,1,0]*scores(behaviori,i));
      set(hbarback(behaviori),'YData',[0,0,1,1,0]*labels(behaviori,i));
    end
    w = max(0,scores(:,i)).*behaviorweight;
    z = sum(w);
    if z == 0,
      colortmp = [0,0,0];
    else
      colortmp = sum(bsxfun(@times,w,behavior_colors),1)/z*max(scores(:,i));
    end
    set(htarget,'Color',colortmp);

    if isstjaaba,
      tailpos = textpos;
      halign = 'center';
      valign = 'bottom';
    else

      tailpos = [x(i)-(pxback+2*a(i))*cos(theta(i)),y(i)-(pxback+2*a(i))*sin(theta(i))];
      sectori = sector(i);
      % floor(modrange((smooththeta(i)-pi/8),0,2*pi)/pi*4)+1;

      if ismember(sectori,[1,7,8]),
        halign = 'right';
      elseif ismember(sectori,[2,6]),
        halign = 'center';
      elseif ismember(sectori,[3,4,5]),
        halign = 'left';
      else
        error('Sanity check: sectori > 8 or < 1');
      end
      if ismember(sectori,[1,2,3]),
        valign = 'bottom';
      elseif ismember(sectori,[4,8]),
        valign = 'middle';
      elseif ismember(sectori,[5,6,7]),
        valign = 'top';
      else
        error('Sanity check: sectori > 8 or < 1');
      end
    end

    labelis = find(scores(:,i) > 0);
    s = cell(1,numel(labelis));
    for iii = 1:numel(labelis),
      s{iii} = sprintf('\\color[rgb]{%f %f %f}%s',behavior_colors(labelis(iii),:),behaviors{labelis(iii)});
    end
    if printnone && isempty(s),
      s = {'None'};
    end

    set(htext,'String',s,...
      'Position',tailpos,...
      'HorizontalAlignment',halign,...
      'VerticalAlignment',valign);

    if isstjaaba,
      ss = sprintf('Frame %d',t);
    else
      ss = sprintf('Target %d (%s), Frame %d',target,sex{i},t);
    end
    if ~isempty(textprefix),
      ss = sprintf('%s, %s',textprefix,ss);
    end
    if ~isempty(textsuffix),
      ss = sprintf('%s, %s',ss,textsuffix);
    end
    if ~isempty(titlepagetext),
      ss = [ss,titlepagetext2];
    end

    set(hinfo,'String',ss)

    if isempty(fixedaxis),
       outofbounds = x(i)-pxborder < ax(1) || ...
          x(i)+pxborder > ax(2) || ...
          y(i)-pxborder < ax(3) || ...
          y(i)+pxborder > ax(4);
    else
       outofbounds = false;
    end
    if isempty(fixedaxis) && (t == framestarts(segi) || outofbounds),
      ax = ResetAxis(x,y,i,headerinfo.nr,headerinfo.nc,pxwidthradius,pxheightradius,pxborder);
      axis(haxmain,ax);
      set(hinfo,'Position',ax([1,4]));
    end
    drawnow;

    if isfirstframe,

      if useVideoWriter,
        if strcmpi(compression,'None') || strcmpi(compression,'Uncompressed AVI'),
          profile = 'Uncompressed AVI';
        else
          profile = 'Motion JPEG AVI';
        end
        aviobj = VideoWriter(aviname,profile); %#ok<TNMLP>
        set(aviobj,'FrameRate',fps);
        if ~strcmpi(profile,'Uncompressed AVI'),
          set(aviobj,'Quality',100);
        end
        open(aviobj);
      else
        if isempty(avifileTempDataFile),
          aviobj = avifile(aviname,'fps',fps,'quality',100,'compression',compression);  %#ok<REMFF1>
        else
          aviobj = myavifile(aviname,'fps',fps,'quality',100,'compression',compression,...
            'TempDataFile',avifileTempDataFile);
          fprintf('Temporary data file for avi writing: %s\n',aviobj.TempDataFile);
        end
      end

      if ~isempty(titlepagetext),
        set(htext,'Visible','off');
        htitle = text(mean(ax(1:2)),mean(ax(3:4)),...
          titlepagetext,'HorizontalAlignment','center',...
          'VerticalAlignment','middle',...
          'Color',[.5,0,0],'Parent',haxmain,...
          'FontSize',40,'FontWeight','bold');
        fr = getframe(hfig);
        for pausei = 1:fps*titlepausetime,
          if useVideoWriter,
            writeVideo(aviobj,fr);
          else
            aviobj = addframe(aviobj,fr);
          end
        end
        set(htext,'Visible','on');
        delete(htitle);
      end

      isfirstframe = false;
    end

    % pause for a few frames at the start of a new segment
    if t == framestarts(segi) && numel(framestarts) > 1,
      set(hinfo,'Color',[.5,0,0],'FontWeight','bold');
      fr = getframe(hfig);
      for pausei = 1:fps,
        if useVideoWriter,
          writeVideo(aviobj,fr);
        else
          aviobj = addframe(aviobj,fr);
        end
      end
      set(hinfo,'Color',textcolor,'FontWeight','normal');
    end

    fr = getframe(hfig);
    height = size(fr.cdata,1);
    width = size(fr.cdata,2);
    if useVideoWriter,
      writeVideo(aviobj,fr);
    else
      aviobj = addframe(aviobj,fr);
    end

    set(hfig,'Position',figpos);

  end
end

%% clean up

if useVideoWriter,
  close(aviobj);
else
  aviobj = close(aviobj); %#ok<NASGU>
end

if fid>0,
  fclose(fid);
end
%% compress using mencoder

if isunix && usemencoder,
  [path,base,ext] = fileparts(aviname);
  tmpfile = fullfile(path,[base,'_tmp',ext]);
  newheight = 4*ceil(height/4);
  newwidth = 4*ceil(width/4);
  % subtitles are upside down, so encode with subtitles and flip, then flip
  % again
  cmd = sprintf('mencoder %s -o %s -ovc xvid -xvidencopts fixed_quant=4 -vf scale=%d:%d',...
    aviname,tmpfile,newwidth,newheight);
  status = system(cmd);
  if status ~= 0,
    error('Failed to compress avi to %s',tmpfile);
  end
  unix(sprintf('mv %s %s',tmpfile,aviname));
end

function ax = ResetAxis(x,y,i,nr,nc,pxwidthradius,pxheightradius,pxborder)

n = numel(x);

minx = x(i);
maxx = x(i);
miny = y(i);
maxy = y(i);
w = pxwidthradius-2*pxborder;
h = pxheightradius-2*pxborder;
for j = i+1:n,
  minx = min(minx,x(j));
  maxx = max(maxx,x(j));
  dx = ceil(maxx-minx)+1;
  if dx > w,
    break;
  end
  miny = min(miny,y(j));
  maxy = max(maxy,y(j));
  dy = ceil(maxy-miny)+1;
  if dy > h,
    break;
  end
end
mux = (minx+maxx)/2;
muy = (miny+maxy)/2;
ax = [mux+pxwidthradius*[-1,1],muy+pxheightradius*[-1,1]];

if ax(2)>nc && ax(1)<1,
  ax(1:2) = (nc+1)/2+pxwidthradius*[-1,1];
elseif ax(2)>nc,
  ax(2) = nc;
  ax(1) = nc-(2*pxwidthradius+1)+1;
elseif ax(1)<1,
  ax(1) = 1;
  ax(2) = 2*pxwidthradius+1;
end
if ax(2)>nc || ax(1)<1,
  ax(1:2) = (nc+1)/2+pxwidthradius*[-1,1];
end
if ax(4)>nr && ax(3)<1,
  ax(3:4) = (nr+1)/2+pxwidthradius*[-1,1];
elseif ax(4)>nr,
  ax(4) = nr;
  ax(3) = nr-(2*pxheightradius+1)+1;
elseif ax(3)<1,
  ax(3) = 1;
  ax(4) = 2*pxheightradius+1;
end
if ax(4)>nr || ax(3)<1,
  ax(3:4) = (nr+1)/2+pxheightradius*[-1,1];
end


function featS = PyradWrapper(scan3M, mask3M, voxelSizeV, paramFilePath)

% This is a wrapper function to create a .nrrd file from CERR and call
% pyradiomics.
% Requires specifying path to Pyradiomics
%
% RKP, 03/22/2018
% AI, 06/10/2020

%Get path to pyradiomics wrapper
CERRPath = getCERRPath;
CERRPathSlashes = strfind(getCERRPath,filesep);
topLevelCERRDir = CERRPath(1:CERRPathSlashes(end-1));
pyradiomicsWrapperPath = fullfile(topLevelCERRDir,'Unit_Testing','tests_for_cerr', 'pyFeatureExtraction.py');

%Add python module to system path & iImport
pyModule = 'pyFeatureExtraction';

P = py.sys.path;
currentPath = pwd;
cd(fullfile(topLevelCERRDir,'Unit_Testing','tests_for_cerr'));

try
    if count(P,pyradiomicsWrapperPath) == 0
        %Insert paths to scipy and pyradiomics.
        insert(P,int64(0),'C:\Miniconda3\Lib\site-packages\radiomics'); %Replace with path to pyradiomic pkg
        P = py.sys.path;
        insert(P,int64(0),'C:\Miniconda3\Lib\site-packages\scipy'); %Replace with path to scipy pkg
        P = py.sys.path;
        insert(P,int64(0),pyradiomicsWrapperPath);
    end
    py.importlib.import_module(pyModule);
catch e
    error('Python module %s could not be imported. %s',pyModule,e.message);
end

%Write scan & mask to NRRD format
fprintf('\nWriting scan and mask to NRRD format...\n');
cd(currentPath);

originV = [0,0,0];
encoding = 'raw';

mask3M = uint16(mask3M);
mask3M = permute(mask3M, [2 1 3]);
%mask3M = flip(mask3M,3);

scan3M = permute(scan3M, [2 1 3]);
%scan3M = flip(scan3M,3);

scanFilename = strcat(tempdir,'scan.nrrd');
scanRes = nrrdWriter(scanFilename, scan3M, voxelSizeV, originV, encoding);

maskFilename = strcat(tempdir, 'mask.nrrd');
maskRes = nrrdWriter(maskFilename, mask3M, voxelSizeV, originV, encoding);

%Call feature extractor
try

    pyFeatDict = py.pyFeatureExtraction.extract(scanFilename, maskFilename,...
        paramFilePath, tempdir);

    %Convert python dictionary to matlab struct
    featS = struct(pyFeatDict);

catch e
    error('Feature extraction failed with message %s',e.message)
end


%kkbitnand 'Output = NOT(Input 1) OR NOT(Input 2 or Constant)'
% This MatLab function was automatically generated by a converter (KhorosToMatLab) from the Khoros kbitnand.pane file
%
% Parameters:
% InputFile: i1 'Input 1', required: 'First input data object'
% OutputFile: o 'Output', required: 'Resulting output data object'
% InputFile: i2 'Input 2', optional: 'Second input data object'
% Integer: real 'Constant', default: 1: 'Constant value'
%
% Example: o = kkbitnand({i1, i2}, {'i1','';'o','';'i2','';'real',1})
%
% Khoros helpfile follows below:
%
%  PROGRAM
% kbitnand - Output = NOT(Input 1)  OR  NOT(Input 2 or Constant)
%
%  DESCRIPTION
% The bitwise NAND operator performs a bitwise NAND between each point in
% \fBInput 1" and either \fBInput 2\fP or the \fBConstant\fP value, which
% ever is specified by the user.
%
%  "Data Type - Single Input" 5
% .cI $DATAMANIP/repos/shared/man/sections/value_type_1input
%
%  "Data Type - Two Input Objects" 5
% .cI $DATAMANIP/repos/shared/man/sections/value_type_2input
% The bitwise operators will not operate on float or complex data types.
%
%  "Map Data" 5
% The bitwise operations have not been written to be fully polymorphic yet.
% They does not
% check for map data, and will therefore always operate on the value data,
% even if a map exists.  This will most likely corrupt indexing into the map.
% In the case of a single input, it is recommended to use the "Copy to
% Value" (kcptoval) segment operator to temporarily move the map data into
% the value segment, run the bitwise operation on the data, then move it
% back to the map with the "Copy from Value" (kcpfromval) operator.
% When operating with two input objects, where at least one of the objects
% contains map data, data should be mapped prior to running the bitwise
% operator.  The "Map Data" operator (kmapdata) can be used to perform
% the mapping operation.
%
%  "Validity Mask - Single Input" 5
% .cI $DATAMANIP/repos/shared/man/sections/mask_1input
%
%  "Validity Mask - Two Input Objects" 5
% Masking has not been implemented yet for the bitwise operators.  Therefore,
% only the mask from the first input object will be transferred to the output.
%
%  "Input Objects of Different Sizes" 5
% .cI $DATAMANIP/repos/shared/man/sections/resize_2input
% The value used to pad the data when the input files are not the same size is
% zero.
%
%  "Explicit Location and Time Data - Single Input" 5
% .cI $DATAMANIP/repos/shared/man/sections/loc_and_time_1input
%
%  "Explicit Location and Time Data - Two Input Objects" 5
% The bitwise operations have not been extended to understand location and
% time data.  Therefore, only location and time data present in the first input
% object will be transferred to the output.
%
% Executing \fBNAND" runs the program "kbitwise\fP with the -nand flag.
%
%
%
%  EXAMPLES
%
%  "SEE ALSO"
% DATAMANIP::kbitwise
%
%  RESTRICTIONS
%
% THIS ROUTINE is still under development, and has not been modified to fully
% support the polymorphic data model.  See paragraphs above for discussions
% concerning the polymorphic data segments.
%
% All processing is currently performed as UNSIGNED long, so the operations
% may not work properly on negative values.
%
% The bitwise operators will not operate on float or complex data types.
%
% In the future, the padding value used for this operation when the input
% images are of different sizes, will be modified from binary 0 to binary 1.
%
%  REFERENCES
%
%  COPYRIGHT
% Copyright (C) 1993 - 1997, Khoral Research, Inc. ("KRI")  All rights reserved.
%

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

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


function hf = symmetrize_filter(hf)

% Ensure hermetian symmetry.

for k = 1:length(hf)
    dc_ind = (size(hf{k},1) + 1) / 2;
    hf{k}(dc_ind+1:end,end,:) = conj(flipud(hf{k}(1:dc_ind-1,end,:)));
end


function value = s_eqi ( s1, s2 )

%*****************************************************************************80
%
%% S_EQI is a case insensitive comparison of two strings for equality.
%
%  Example:
%
%    S_EQI ( 'Anjana', 'ANJANA' ) is TRUE.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    30 April 2004
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string S1, S2, the strings to compare.
%
%    Output, logical VALUE, is TRUE if the strings are equal.
%
  FALSE = 0;
  TRUE = 1;

  len1 = length ( s1 );
  len2 = length ( s2 );
  lenc = min ( len1, len2 );

  value = FALSE;

  for i = 1 : lenc

    c1 = ch_cap ( s1(i) );
    c2 = ch_cap ( s2(i) );

    if ( c1 ~= c2 )
      value = FALSE;
      return
    end

  end

  for i = lenc + 1 : len1
    if ( s1(i) ~= ' ' )
      value = FALSE;
      return
    end
  end

  for i = lenc + 1 : len2
    if ( s2(i) ~= ' ' )
      value = FALSE;
      return
    end
  end

  value = TRUE;

  return
end


function issingle = getSinglePersonImages(annolist_all)

load('/BS/leonid-pose/work/data/new_dataset/dataset_release_candidate1/test_annolist_all_annotated','annolist');
load('/home/andriluk/IMAGES/human_pose_dataset/dataset/dataset_release_candidate1/dataset_info_v12');
imgidxs = find(DATASET.img_train == 0);

names = cell(length(annolist),1);
idxs = [];
for imgidx = 1:length(annolist)
    if (isfield(annolist(imgidx),'annorect') && length(annolist(imgidx).annorect) == 1)
        names{imgidx} = ['im' padZeros(num2str(imgidxs(imgidx)),5)];
        idxs = [idxs; imgidx];
    end
end

names = names(idxs);

issingle = zeros(length(annolist_all),1);
for imgidx = 1:length(annolist_all)
    [~,n] = fileparts(annolist_all(imgidx).image.name);
    issingle(imgidx) = ~isempty(find(strcmp(names,n(1:7))));
end

% assert(length(names) == sum(idxs));

end


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

%%
clear; close all; clc;

%% Syntax
% |[DI]=TriScatteredInterp_ND(DT,D,XI,InterpMethod);|

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


%% PTplotSpec - script that computes and plots spectrograms

% ----------------------------------------------------------------------------------
% "THE BEER-WARE LICENSE" (Revision 42):
% <brian.white@queensu.ca> wrote this file. As long as you retain this notice you
% can do whatever you want with this stuff. If we meet some day, and you think
% this stuff is worth it, you can buy me a beer in return. -Brian White
% ----------------------------------------------------------------------------------

if ~isempty(fnameMaster)

prop_max_screen=(max([PTspecfig.Position(3) PTspecfig.Position(4)]));
fontsz=(screensz_multiplier*prop_max_screen);
%% update fonts

f = fields(guiHandlesSpec);
for i = 1 : size(f,1)
    try
        eval(['guiHandlesSpec.' f{i} '.FontSize=fontsz;']);
    catch
        guiHandlesSpec.SpecSelect{1}.FontSize=fontsz;
        guiHandlesSpec.SpecSelect{2}.FontSize=fontsz;
        guiHandlesSpec.SpecSelect{3}.FontSize=fontsz;
        guiHandlesSpec.SpecSelect{4}.FontSize=fontsz;
        guiHandlesSpec.FileSelect{1}.FontSize=fontsz;
        guiHandlesSpec.FileSelect{2}.FontSize=fontsz;
        guiHandlesSpec.FileSelect{3}.FontSize=fontsz;
        guiHandlesSpec.FileSelect{4}.FontSize=fontsz;
        guiHandlesSpec.Sub100HzCheck{1}.FontSize=fontsz;
        guiHandlesSpec.Sub100HzCheck{2}.FontSize=fontsz;
        guiHandlesSpec.Sub100HzCheck{3}.FontSize=fontsz;
        guiHandlesSpec.Sub100HzCheck{4}.FontSize=fontsz;
    end
end

guiHandlesSpec.climMax_input = uicontrol(PTspecfig,'style','edit','string',[num2str(climScale(guiHandlesSpec.checkboxPSD.Value+1, 1))],'fontsize',fontsz,'TooltipString',[TooltipString_scale],'units','normalized','outerposition',[posInfo.climMax_input],...
     'callback','@textinput_call2; climScale(guiHandlesSpec.checkboxPSD.Value+1, 1)=str2num(guiHandlesSpec.climMax_input.String);updateSpec=1;PTplotSpec;');

guiHandlesSpec.climMax_input2 = uicontrol(PTspecfig,'style','edit','string',[num2str(climScale(guiHandlesSpec.checkboxPSD.Value+1, 2))],'fontsize',fontsz,'TooltipString',[TooltipString_scale],'units','normalized','outerposition',[posInfo.climMax_input2],...
     'callback','@textinput_call2; climScale(guiHandlesSpec.checkboxPSD.Value+1, 2)=str2num(guiHandlesSpec.climMax_input2.String);updateSpec=1;PTplotSpec;');

guiHandlesSpec.climMax_input3 = uicontrol(PTspecfig,'style','edit','string',[num2str(climScale(guiHandlesSpec.checkboxPSD.Value+1, 3))],'fontsize',fontsz,'TooltipString',[TooltipString_scale],'units','normalized','outerposition',[posInfo.climMax_input3],...
     'callback','@textinput_call2; climScale(guiHandlesSpec.checkboxPSD.Value+1, 3)=str2num(guiHandlesSpec.climMax_input3.String);updateSpec=1;PTplotSpec;');

guiHandlesSpec.climMax_input4 = uicontrol(PTspecfig,'style','edit','string',[num2str(climScale(guiHandlesSpec.checkboxPSD.Value+1, 4))],'fontsize',fontsz,'TooltipString',[TooltipString_scale],'units','normalized','outerposition',[posInfo.climMax_input4],...
     'callback','@textinput_call2; climScale(guiHandlesSpec.checkboxPSD.Value+1, 4)=str2num(guiHandlesSpec.climMax_input4.String);updateSpec=1;PTplotSpec;');

%%

s1={'';'gyroADC';'debug';'piderr';'setpoint';'axisP';'axisD';'axisDpf';'pidsum'};

datSelectionString=[s1];

clear vars
for i=1:4
    vars(i)=guiHandlesSpec.SpecSelect{i}.Value;
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%% compute fft %%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if guiHandlesSpec.SpecSelect{1}.Value>1 | guiHandlesSpec.SpecSelect{2}.Value>1 | guiHandlesSpec.SpecSelect{3}.Value>1 | guiHandlesSpec.SpecSelect{4}.Value>1
    set(PTspecfig, 'pointer', 'watch')
    if updateSpec==0
        clear s dat ampmat amp2d freq a RC smat amp2d freq2d Throt
        p=0;
         hw = waitbar(0,['please wait... ' ]);

        for k=1:length(vars)
            s=char(datSelectionString(vars(k)));
            for a=1:3,
                if  ( ( ~isempty(strfind(s,'axisD'))) & a==3) | isempty(s)
                    p=p+1;
                    smat{p}=[];%string
                    ampmat{p}=[];%spec matrix
                    freq{p}=[];% freq matrix
                    amp2d{p}=[];%spec 2d
                    freq2d{p}=[];% freq2d
                else
                    eval(['dat{k}(a,:) = T{guiHandlesSpec.FileSelect{k}.Value}.' char(datSelectionString(vars(k))) '_' int2str(a-1) '_(tIND{guiHandlesSpec.FileSelect{k}.Value});';])
                    Throt=T{guiHandlesSpec.FileSelect{k}.Value}.setpoint_3_(tIND{guiHandlesSpec.FileSelect{k}.Value}) / 10;% throttle
                    lograte = A_lograte(guiHandlesSpec.FileSelect{k}.Value);%in kHz
                    p=p+1;
                    waitbar(p/12, hw, ['processing spectrogram... '  int2str(p) ]);
                    smat{p}=s;
                    [freq{p} ampmat{p}]=PTthrSpec(Throt, dat{k}(a,:), lograte, guiHandlesSpec.checkboxPSD.Value); % compute matrices
                    [freq2d{p} amp2d{p}]=PTSpec2d(dat{k}(a,:),lograte, guiHandlesSpec.checkboxPSD.Value); %compute 2d amp spec at same time
               end
            end
        end
        close(hw)
    end
else
    hwarn=warndlg({'Dropdowns set to ''NONE''.'; 'Please select a preset or specific variables to analyze.'});
    pause(3);
    try
        close(hwarn);
    catch
    end
end

if guiHandlesSpec.checkbox2d.Value==0 && ~isempty(ampmat)
    figure(PTspecfig);
    %%%%% plot spec mattrices
    c1=[1 1 1 2 2 2 3 3 3 4 4 4];
    c2=[1 2 3 1 2 3 1 2 3 1 2 3];
    baselineY = [0 -40];
    ftr = fspecial('gaussian',[guiHandlesSpec.smoothFactor_select.Value*5 guiHandlesSpec.smoothFactor_select.Value],4);
    for p=1:size(ampmat,2)
        delete(subplot('position',posInfo.SpecPos(p,:)));
        if ~isempty(ampmat{p})
            delete(subplot('position',posInfo.SpecPos(p,:)));
            h1=subplot('position',posInfo.SpecPos(p,:)); cla
            img = flipud((filter2(ftr, ampmat{p} ))') + baselineY(guiHandlesSpec.checkboxPSD.Value+1);
            imagesc(img);

            lograte=A_lograte(guiHandlesSpec.FileSelect{c1(p)}.Value);

             axLabel={'roll';'pitch';'yaw'};

            if guiHandlesSpec.Sub100HzCheck{c1(p)}.Value==1
                hold on;h=plot([0 100],[size(ampmat{p},2)-round(Flim1/3.33) size(ampmat{p},2)-round(Flim1/3.33)],'y--');set(h,'linewidth',2)
                hold on;h=plot([0 100],[size(ampmat{p},2)-round(Flim2/3.33) size(ampmat{p},2)-round(Flim2/3.33)],'y--');set(h,'linewidth',2)
                % sub100Hz scaling
                xticks=[1 size(ampmat{p},1)/5:size(ampmat{p},1)/5:size(ampmat{p},1)];
                yticks=[(size(ampmat{p},2)-30):6:size(ampmat{p},2)];
                set(h1,'PlotBoxAspectRatioMode','auto','ylim',[size(ampmat{p},2)-30 size(ampmat{p},2)])
                set(h1,'fontsize',fontsz,'CLim',[baselineY(guiHandlesSpec.checkboxPSD.Value+1) climScale(guiHandlesSpec.checkboxPSD.Value+1, c1(p))],'YTick',[yticks],'yticklabels',[{100} {80} {60} {40} {20} {0}],'XTick',[xticks],'xticklabels',{'0';'20';'40';'60';'80';'100'},'tickdir','out','xminortick','on','yminortick','on');
                a=[];a2=[];a=filter2(ftr, ampmat{p}) + baselineY(guiHandlesSpec.checkboxPSD.Value+1);
                a2 = a(:,(round(Flim1/3.33))+1:(round(Flim2/3.33)));
                meanspec=nanmean(a2(:));
                peakspec=max(max(a(:,(round(Flim1/3.33))+1:(round(Flim2/3.33)))));
                if guiHandlesSpec.ColormapSelect.Value==9 | guiHandlesSpec.ColormapSelect.Value==10
                    h=text(64,(size(ampmat{p},2)-30)+3,['mean=' num2str(meanspec,3)]);
                    set(h,'Color','k','fontsize',fontsz,'fontweight','bold');
                    h=text(64,(size(ampmat{p},2)-30)+1,['peak=' num2str(peakspec,3)]);
                    set(h,'Color','k','fontsize',fontsz,'fontweight','bold');
                else
                    h=text(64,(size(ampmat{p},2)-30)+3,['mean=' num2str(meanspec,3)]);
                    set(h,'Color','w','fontsize',fontsz,'fontweight','bold');
                    h=text(64,(size(ampmat{p},2)-30)+1,['peak=' num2str(peakspec,3)]);
                    set(h,'Color','w','fontsize',fontsz,'fontweight','bold');
                end
                h=text(xticks(1)+1,(size(ampmat{p},2)-30)+1,axLabel{c2(p)});
                set(h,'Color',[1 1 1],'fontsize',fontsz,'fontweight','bold')
            else % full scaling
                xticks=[1 size(ampmat{p},1)/5:size(ampmat{p},1)/5:size(ampmat{p},1)];
                yticks=[1:(size(ampmat{p},2))/10:size(ampmat{p},2) size(ampmat{p},2)];
                set(h1,'fontsize',fontsz,'CLim',[baselineY(guiHandlesSpec.checkboxPSD.Value+1) climScale(guiHandlesSpec.checkboxPSD.Value+1, c1(p))],'YTick',[yticks],'yticklabels',[{(max(round(yticks * 3.333))/1)-(max(round(yticks * 3.333))/5)*0} {''} {(max(round(yticks * 3.333))/1)-(max(round(yticks * 3.333))/5)*1} {''} {(max(round(yticks * 3.333))/1)-(max(round(yticks * 3.333))/5)*2} {''} {(max(round(yticks * 3.333))/1)-(max(round(yticks * 3.333))/5)*3} {''} {(max(round(yticks * 3.333))/1)-(max(round(yticks * 3.333))/5)*4} {''} {0}],'XTick',[xticks],'xticklabels',{'0';'20';'40';'60';'80';'100'},'tickdir','out','xminortick','on','yminortick','on');
                set(h1,'PlotBoxAspectRatioMode','auto','ylim',[1 size(ampmat{p},2)])
                a=[];a2=[];a=filter2(ftr, ampmat{p}) + baselineY(guiHandlesSpec.checkboxPSD.Value+1);
                a2 = a(:,(size(ampmat{p},2)/10):size(ampmat{p},2));
                meanspec=nanmean(a2(:));
                peakspec=max(max(a(:,(size(ampmat{p},2)/10):size(ampmat{p},2))));
                if guiHandlesSpec.ColormapSelect.Value==9 | guiHandlesSpec.ColormapSelect.Value==10
                    h=text(64,size(ampmat{p},2)*.04,['mean=' num2str(meanspec,3)]);
                    set(h,'Color','k','fontsize',fontsz,'fontweight','bold');
                    h=text(64,size(ampmat{p},2)*.13,['peak=' num2str(peakspec,3)]);
                    set(h,'Color','k','fontsize',fontsz,'fontweight','bold');
                else
                    h=text(64,size(ampmat{p},2)*.04,['mean=' num2str(meanspec,3)]);
                    set(h,'Color','w','fontsize',fontsz,'fontweight','bold');
                    h=text(64,size(ampmat{p},2)*.13,['peak=' num2str(peakspec,3)]);
                    set(h,'Color','w','fontsize',fontsz,'fontweight','bold');
                end
                h=text(xticks(1)+1,size(ampmat{p},2)*.04,axLabel{c2(p)});
                set(h,'Color',[1 1 1],'fontsize',fontsz,'fontweight','bold')
            end

            grid on
            ax = gca;
            ax.GridColor = [1 1 1];
            if guiHandlesSpec.ColormapSelect.Value==9 | guiHandlesSpec.ColormapSelect.Value==10
                ax.GridColor = [0 0 0]; % black on white background
                set(h,'Color',[0 0 0],'fontsize',fontsz,'fontweight','bold')
            end
             ylabel('Frequency (Hz)','fontweight','bold')
             xlabel('% Throttle','fontweight','bold')
        end
    end

    % color bar2 at the top
    try
    delete(hCbar1);delete(hCbar2);delete(hCbar3);delete(hCbar4)
    catch
    end
    if vars(1)>1 % 1=none
        subplot('position',posInfo.SpecPos(1,:));
        hCbar1= colorbar('NorthOutside');
        set(hCbar1,'Position', [posInfo.hCbar1pos]);
    end
    if vars(2)>1 % 1=none
        subplot('position',posInfo.SpecPos(4,:));
        hCbar2= colorbar('NorthOutside');
        set(hCbar2,'Position', [posInfo.hCbar2pos])
    end
    if vars(3)>1 % 1=none
        subplot('position',posInfo.SpecPos(7,:));
        hCbar3= colorbar('NorthOutside');
        set(hCbar3,'Position', [posInfo.hCbar3pos])
    end
    if vars(4)>1 % 1=none
        subplot('position',posInfo.SpecPos(10,:));
        hCbar4= colorbar('NorthOutside');
        set(hCbar4,'Position', [posInfo.hCbar4pos])
    end

    % color maps
    % standard set
    if guiHandlesSpec.ColormapSelect.Value<8,
        colormap(char(guiHandlesSpec.ColormapSelect.String(guiHandlesSpec.ColormapSelect.Value)));
    end
    % new
    if guiHandlesSpec.ColormapSelect.Value==8, colormap(viridis); end
    if guiHandlesSpec.ColormapSelect.Value==9, colormap(linearREDcmap); end
    if guiHandlesSpec.ColormapSelect.Value==10, colormap(linearGREYcmap); end

end

if guiHandlesSpec.checkbox2d.Value==1 && ~isempty(amp2d)
    figure(PTspecfig);
    try
    delete(hCbar1);delete(hCbar2);delete(hCbar3);delete(hCbar4)
    catch
    end
    baselineYlines = [0 -50];
    c1=[1 1 1 2 2 2 3 3 3 4 4 4];
    c2=[1 2 3 1 2 3 1 2 3 1 2 3];
    %%%%% plot 2d amp spec
    for p=1:size(amp2d,2)
         axLabel={'roll';'pitch';'yaw'};

        delete(subplot('position',posInfo.SpecPos(p,:)));
        if ~isempty(amp2d{p})
            h2=subplot('position',posInfo.SpecPos(p,:)); cla
            h=plot(freq2d{p}, smooth(amp2d{p}, log10(size(amp2d{p},1)) * (guiHandlesSpec.smoothFactor_select.Value^2), 'lowess'));hold on
            set(h, 'linewidth', guiHandles.linewidth.Value/2)
            set(h2,'fontsize',fontsz,'fontweight','bold')
            if guiHandlesSpec.specPresets.Value <= 3
                set(h,'Color',[SpecLineCols(c1(p),:,1)])
            end
            if guiHandlesSpec.specPresets.Value > 4 && guiHandlesSpec.specPresets.Value <= 6
                set(h,'Color',[SpecLineCols(c1(p),:,2)])
            end
            if guiHandlesSpec.specPresets.Value > 6
                set(h,'Color',[SpecLineCols(c1(p),:,3)])
            end

            if guiHandlesSpec.Sub100HzCheck{c1(p)}.Value==1
                set(h2,'xtick',[0 20 40 60 80 100], 'yminortick','on')
                axis([0 100 baselineYlines(guiHandlesSpec.checkboxPSD.Value+1) climScale(guiHandlesSpec.checkboxPSD.Value+1, c1(p))])
                h=plot([round(Flim1) round(Flim1)],[baselineYlines(guiHandlesSpec.checkboxPSD.Value+1) climScale(guiHandlesSpec.checkboxPSD.Value+1, c1(p))],'k--');
                set(h,'linewidth',1)
                h=plot([round(Flim2) round(Flim2)],[baselineYlines(guiHandlesSpec.checkboxPSD.Value+1) climScale(guiHandlesSpec.checkboxPSD.Value+1, c1(p))],'k--');
                set(h,'linewidth',1)
            else
                set(h2,'xtick',[0 : ((A_lograte(guiHandlesSpec.FileSelect{k}.Value) / 2) * 1000 / 5) : (A_lograte(guiHandlesSpec.FileSelect{k}.Value) / 2) * 1000],'yminortick','on')
                axis([0 (A_lograte(guiHandlesSpec.FileSelect{k}.Value) / 2) * 1000 baselineYlines(guiHandlesSpec.checkboxPSD.Value+1) climScale(guiHandlesSpec.checkboxPSD.Value+1, c1(p))])
            end

            xlabel('Frequency (Hz)')
            if guiHandlesSpec.checkboxPSD.Value
                ylabel(['PSD (dB)'])
            else
                ylabel(['Amplitude'])
            end

            h=text(2,climScale(guiHandlesSpec.checkboxPSD.Value+1, c1(p))*.95,axLabel{c2(p)});
            set(h,'Color',[.2 .2 .2],'fontsize',fontsz,'fontweight','bold')

            grid on
        end
    end
end
set(PTspecfig, 'pointer', 'arrow')
updateSpec=0;

end


function outputImage = flip(this, iDim)
% Flips all elements of a given dimension and updates dimInfo.samplingPoints
%
%   Y = MrDataNd()
%   Y.flip(inputs)
%
% This is a method of class MrDataNd.
%
% IN
%   iDim    index of dimension to be flipped; default: 1
%
% OUT
%
% EXAMPLE
%   flip
%
%   See also MrDataNd perform_unary_operation MrDataNd.fliplr
%   See also MrDataNd.flipud

% Author:   Saskia Bollmann & Lars Kasper
% Created:  2016-06-15
% Copyright (C) 2016 Institute for Biomedical Engineering
%                    University of Zurich and ETH Zurich
%
% This file is part of the TAPAS UniQC Toolbox, which is released
% under the terms of the GNU General Public License (GPL), version 3.
% You can redistribute it and/or modify it under the terms of the GPL
% (either version 3 or, at your option, any later version).
% For further details, see the file COPYING or
%  <http://www.gnu.org/licenses/>.

if nargin < 2
    iDim = 1;
end

if ~isnumeric(iDim)
    iDim = this.dimInfo.get_dim_index(iDim);
end

if exist('flip', 'builtin')
    outputImage = this.perform_unary_operation(@(x) flip(x, iDim));
else
    outputImage = this.perform_unary_operation(@(x) flipdim(x, iDim));
end
outputImage.dimInfo.samplingPoints{iDim} = flip(outputImage.dimInfo.samplingPoints{iDim});
end


function export_3D_model(model,FileName,psz,should_diffuse)

if(isempty(model))
    disp('Error: export_3D_model: Can not write empty data to a file. Skipping.');
    return;
end

if(~exist('psz','var'))
    psz = [];
end

if(~exist('should_diffuse','var'))
    should_diffuse = 0;
end

data.vertex.x = model.x;
data.vertex.y = model.y;
data.vertex.z = model.z;

data.vertex.nx = model.nx;
data.vertex.ny = model.ny;
data.vertex.nz = model.nz;

if(model_has_colors(model))
    if(should_diffuse)
        data.vertex.diffuse_red = model.r*255;
        data.vertex.diffuse_green = model.g*255;
        data.vertex.diffuse_blue = model.b*255;
    else
        data.vertex.red = model.r*255;
        data.vertex.green = model.g*255;
        data.vertex.blue = model.b*255;
    end
end

if(~isempty(psz))
    data.vertex.psz = 0.1*ones(length(model.x),1);
end

if(isfield(model,'face'))
    data.face = model.face;
end

[pathstr, name, ext] = fileparts(FileName);

if(strcmpi(ext,'.ply'))
    nplyWrite(FileName,data);
elseif(strcmpi(ext,'.mat'))
    save(FileName,'data');
else
    error('export_3D_model: Invalid file extension');
end


classdef(SharedTestFixtures = {
        DownloadBERTFixture}) tWordPieceTokenizer < matlab.unittest.TestCase
    % tWordPieceTokenizer   Unit tests for WordPieceTokenizer

    % Copyright 2021 The MathWorks, Inc.

    properties(TestParameter)
        ModelAndExpectedVocabSize = struct(...
            'uncased',struct('Model','base','VocabSize',30522),...
            'multilingualCased',struct('Model','multilingual-cased','VocabSize',119547))
    end

    methods(Test)
        function canConstruct(test)
            enc = wordEncoding("foo");
            tok = bert.tokenizer.internal.WordPieceTokenizer(enc);
            test.verifyClass(tok,'bert.tokenizer.internal.WordPieceTokenizer');
            test.verifyInstanceOf(tok,'bert.tokenizer.internal.Tokenizer');
        end

        function canConstructWithFile(test)
            words = ["foo","bar"];
            enc = wordEncoding(words);
            fixture = matlab.unittest.fixtures.TemporaryFolderFixture();
            test.applyFixture(fixture);
            folder = fixture.Folder;
            testVocab = fullfile(folder,"testvocab.txt");
            fid = fopen(testVocab,'w','n','utf-8');
            fprintf(fid,join(words,newline));
            fclose(fid);
            tokFromEncoding = bert.tokenizer.internal.WordPieceTokenizer(enc);
            tokFromFile = bert.tokenizer.internal.WordPieceTokenizer(testVocab);
            test.verifyEqual(tokFromEncoding.Vocab,tokFromFile.Vocab);
        end

        function canSetUnknownToken(test)
            enc = wordEncoding("foo");
            unk = "bar";
            tok = bert.tokenizer.internal.WordPieceTokenizer(enc,'UnknownToken',unk);
            test.verifyEqual(tok.Unk,unk)
            str = "blah";
            ustr = textanalytics.unicode.UTF32(str);
            act_out = tok.tokenize(ustr);
            exp_out = unk;
            test.verifyEqual(act_out,exp_out);
        end

        function canSetMaxTokenLength(test)
            enc = wordEncoding("foo");
            maxLen = 2;
            tok = bert.tokenizer.internal.WordPieceTokenizer(enc,'MaxTokenLength',maxLen);
            test.verifyEqual(tok.MaxChar,maxLen);
            str = "foo";
            ustr = textanalytics.unicode.UTF32(str);
            act_out = tok.tokenize(ustr);
            exp_out = tok.Unk;
            test.verifyEqual(act_out,exp_out);
        end

        function canTokenize(test)
            enc = wordEncoding(["foo","bar","##foo"]);
            tok = bert.tokenizer.internal.WordPieceTokenizer(enc);
            str = "foo bar foobar barba bafoobar barfoo";
            wsTok = bert.tokenizer.internal.WhitespaceTokenizer;
            ustr = textanalytics.unicode.UTF32(wsTok.tokenize(str));
            act_out = tok.tokenize(ustr);
            exp_out = ["foo","bar",tok.Unk,tok.Unk,tok.Unk,"bar","##foo"];
            test.verifyEqual(act_out,exp_out);
        end

        function hasExpectedVocabSize(test,ModelAndExpectedVocabSize)
            vocab = bert.internal.getSupportFilePath(ModelAndExpectedVocabSize.Model,"vocab.txt");
            tok = bert.tokenizer.internal.WordPieceTokenizer(vocab);
            test.verifyEqual(tok.Vocab.NumWords,ModelAndExpectedVocabSize.VocabSize);
        end
    end
end


function test_suite=test_assert_less_than()
    try % assignment of 'localfunctions' is necessary in Matlab >= 2016
        test_functions=localfunctions();
    catch % no problem; early Matlab versions can use initTestSuite fine
    end
    initTestSuite;

function test_assert_less_than_exceptions
    assertExceptionThrown(@()assertLessThan([1 2],[1,2,3]),...
                          'assertLessThan:sizeNotEqual');
    assertExceptionThrown(@()assertLessThan([0,0],[1;1]),...
                          'assertLessThan:sizeNotEqual');
    assertExceptionThrown(@()assertLessThan([1],'a'), ...
                          'assertLessThan:classNotEqual');
    assertExceptionThrown(@()assertLessThan(1,1), ...
                          'assertLessThan:notLessThan');
    assertExceptionThrown(@()assertLessThan([1,2,3],[2,3,3]), ...
                          'assertLessThan:notLessThan');
    assertExceptionThrown(@()assertLessThan(nan,nan), ...
                          'assertLessThan:notLessThan');
    assertExceptionThrown(@()assertLessThan(nan,1), ...
                          'assertLessThan:notLessThan');
    assertExceptionThrown(@()assertLessThan(1,nan), ...
                          'assertLessThan:notLessThan');
    assertExceptionThrown(@()assertLessThan(ones(2,2,2,2),ones(2,2,2,2)*nan), ...
                          'assertLessThan:notLessThan');

    a = zeros(2,2,2,2);
    assertExceptionThrown(@()assertLessThan(ones(2,2,2,2),a), ...
                          'assertLessThan:notLessThan');
    a(1,1,1,1) = 1;
    assertExceptionThrown(@()assertLessThan(ones(2,2,2,2),a), ...
                          'assertLessThan:notLessThan');

function test_assert_less_than_passes
    assertLessThan(0,1);
    assertLessThan([0,0],[1,1]);
    assertLessThan(zeros(2,2,2,2),ones(2,2,2,2));


function move_topo_layers_to_bottom(container)
    % move the topo layers to the bottom of an axes
    h=findobj(container,'Type','surface','-and','-regexp','Tag','topographic_map_.*');
    p=unique([h.Parent]);
    for i=1:numel(p)
        idx=startsWith(get(p(i).Children,'Tag'),'topographic_map_');
        h=p(i).Children(idx);
        others=p(i).Children(~idx);
        p(i).Children=[others;h];
    end

end


% std_getindvar - get independent variables of a STUDY
%
% Usage:
%   [indvar indvarvals] = std_getindvar(STUDY);
%   [indvar indvarvals] = std_getindvar(STUDY, mode, scandesign);
%
% Input:
%   STUDY - EEGLAB STUDY structure
%   mode  - ['datinfo'|'trialinfo'|'both'] get independent variables
%           linked to STUDY.datasetinfo, STUDY.datasetinfo.trialinfo or
%           both. Default is 'both'.
%   scandesign - [0|1] scan STUDY design for additional combinations of
%           independent variable values. Default is 0.
%
% Output:
%   indvar     - [cell array] cell array of independent variable names
%   indvarvals - [cell array] cell array of independent variable values
%
% Authors: A. Delorme, CERCO/CNRS and SCCN/UCSD, 2010-

% Copyright (C) Arnaud Delorme, SCCN, INC, UCSD, 2010, arno@sccn.ucsd.edu
%
% This file is part of EEGLAB, see http://www.eeglab.org
% for the documentation and details.
%
% Redistribution and use in source and binary forms, with or without
% modification, are permitted provided that the following conditions are met:
%
% 1. Redistributions of source code must retain the above copyright notice,
% this list of conditions and the following disclaimer.
%
% 2. Redistributions in binary form must reproduce the above copyright notice,
% this list of conditions and the following disclaimer in the documentation
% and/or other materials provided with the distribution.
%
% THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
% AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
% IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
% ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
% LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
% CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
% SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
% INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
% CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
% ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
% THE POSSIBILITY OF SUCH DAMAGE.

function [ factor, factorvals, subjects, paired ] = std_getindvar(STUDY, mode, scandesign)

if nargin < 1
    help std_getindvar;
    return;
end

factor     = {};
factorvals = {};
paired     = {};
subjects   = {};
if nargin < 2, mode = 'both'; end
if nargin < 3, scandesign = 0; end
countfact = 1;
setinfo = STUDY.datasetinfo;

if strcmpi(mode, 'datinfo') || strcmpi(mode, 'both')
    % get trial info
    ff = fieldnames(setinfo);
    ff = setdiff_bc(ff, { 'filepath' 'filename' 'subject' 'index' 'ncomps' 'comps' 'trialinfo' });
    for index = 1:length(ff)
        % check we have the same data type
        allSetinfoVals = { setinfo.(ff{index}) };
        if any(cellfun(@ischar, allSetinfoVals))
            if ~all(cellfun(@ischar, allSetinfoVals))
                allSetinfoVals = cellfun(@num2str, allSetinfoVals, 'uniformoutput', false);
            end
        end

        if ischar(allSetinfoVals{1})
            tmpvals = unique_bc(allSetinfoVals);
            if length(tmpvals) > 1
                factor{    countfact} = ff{index};
                factorvals{countfact} = tmpvals;

                % get subject for each factor value
                intersectSubject = { setinfo(:).subject };
                for c = 1:length(tmpvals)
                    datind = strmatch(tmpvals{c}, allSetinfoVals, 'exact');
                    subjects{  countfact}{c} = unique_bc( { setinfo(datind).subject } );
                    intersectSubject = intersect(intersectSubject, subjects{  countfact}{c});
                end

                numValues = cellfun(@length, subjects{  countfact});
                if length(intersectSubject) == numValues(1) && length(unique(numValues)) == 1
                    paired{countfact} = 'on'; % trial data always paired
                else
                    paired{countfact} = 'off';
                end
                countfact = countfact + 1;
            end
        else
            tmpvals = unique_bc([ allSetinfoVals{:} ] );
            if length(tmpvals) > 1
                factor{    countfact} = ff{index};
                factorvals{countfact} = mattocell(tmpvals);

                % get subject for each factor value
                intersectSubject = { setinfo(:).subject };
                for c = 1:length(tmpvals)
                    eval( [ 'datind = find(tmpvals(c) == [ setinfo.' ff{index} ']);' ] );
                    subjects{  countfact}{c} = unique_bc( { setinfo(datind).subject } );
                    intersectSubject = intersect(intersectSubject, subjects{  countfact}{c});
                end

                numValues = cellfun(@length, subjects{  countfact});
                if length(intersectSubject) == numValues(1) && length(unique(numValues)) == 1
                    paired{countfact} = 'on'; % trial data always paired
                else
                    paired{countfact} = 'off';
                end
                countfact = countfact + 1;
            end
        end
    end
end

% add ind. variables for trials
% -----------------------------
if strcmpi(mode, 'trialinfo') || strcmpi(mode, 'both')
    % add trial info
    if isfield(setinfo, 'trialinfo')
        ff = fieldnames(setinfo(1).trialinfo);
        for index = 1:length(ff)
            % check if any of the datasets are using string for event type
            allFieldsPresent = cellfun(@(x)(isfield(x, ff{index})), { setinfo.trialinfo });
            allFirstVal = cellfun(@(x)(getfield(x, ff{index})), { setinfo(allFieldsPresent).trialinfo }, 'uniformoutput', false);

            if any(cellfun(@isstr, allFirstVal))
                alltmpvals = {};
                for ind = 1:length(setinfo)
                    if isfield(setinfo(ind).trialinfo, ff{index})
                        eval( [ 'tmpTrialVals = { setinfo(ind).trialinfo.' ff{index} ' };' ] );
                        if isnumeric(tmpTrialVals{1})
                            % convert to string if necessary
                            tmpTrialVals = cellfun(@num2str, tmpTrialVals, 'uniformoutput', false);
                        end
                        tmpvals = unique_bc(tmpTrialVals);
                    else tmpvals = {};
                    end
                    if isempty(alltmpvals)
                        alltmpvals = tmpvals;
                    else
                        alltmpvals = { alltmpvals{:} tmpvals{:} };
                    end
                end
                alltmpvals = unique_bc(alltmpvals);
                if length(alltmpvals) > 1
                    factor{    countfact} = ff{index};
                    factorvals{countfact} = alltmpvals;
                    subjects{  countfact} = {};
                    paired{countfact}     = 'on'; % trial data always paired
                    countfact = countfact + 1;
                end
            else
                alltmpvals = [];
                for ind = 1:length(setinfo)
                    if isfield(setinfo(ind).trialinfo, ff{index}) && ~iscell( setinfo(ind).trialinfo(1).(ff{index}))
                        eval( [ 'tmpvals = unique_bc([ setinfo(ind).trialinfo.' ff{index} ' ]);' ] );
                    else tmpvals = [];
                    end
                    alltmpvals = [ alltmpvals tmpvals ];
                end
                alltmpvals = unique_bc(alltmpvals);
                if length(alltmpvals) > 1
                    factor{    countfact} = ff{index};
                    factorvals{countfact} = mattocell(alltmpvals);
                    subjects{  countfact} = {};
                    paired{countfact}     = 'off'; % pairing is irrelevant for continuous var
                    countfact = countfact + 1;
                end
            end
        end
    end
end

% scan existing design for additional combinations
% ------------------------------------------------
if scandesign
    for desind = 1:length(STUDY.design)
        for iVar = 1:length(STUDY.design(desind).variable)
            if strcmpi(STUDY.design(desind).variable(iVar).vartype, 'categorical')
                pos1 = strmatch(STUDY.design(desind).variable(iVar).label, factor, 'exact');
                if ~isempty(pos1), add1 = mysetdiff(STUDY.design(desind).variable(iVar).value, factorvals{pos1}); else add1 = []; end
                for addInd = 1:length(add1)
                    duplicate = 0;
                    for iVarVal = 1:length(factorvals{pos1})
                        if isequal(factorvals{pos1}{iVarVal}, add1{addInd})
                            duplicate = 1;
                        end
                    end
                    if ~duplicate
                        factorvals{pos1} = { factorvals{pos1}{:} add1{addInd} };
                    end
                end
            end
        end
    end
end

function cellout = mysetdiff(cell1, cell2);

    if ischar(cell2{1})
         indcell = cellfun(@iscell, cell1);
    else indcell = cellfun(@(x)(length(x)>1), cell1);
    end
    cellout = cell1(indcell);

%     if ischar(cell1{1}) && ischar(cell2{1})
%          cellout = setdiff_bc(cell1, cell2);
%     elseif ~ischar(cell1{1}) && ~ischar(cell2{1})
%         cellout = mattocell(setdiff( [ cell1{:} ], [ cell2{:} ]));
%     elseif ischar(cell1{1}) && ~ischar(cell2{1})
%          cellout = setdiff_bc(cell1, cellfun(@(x)(num2str(x)),cell2, 'uniformoutput', false));
%     elseif ~ischar(cell1{1}) && ischar(cell2{1})
%          cellout = setdiff_bc(cellfun(@(x)(num2str(x)),cell1, 'uniformoutput', false), cell2);
%     end


function S2G = delete(S2G,points)
% elilinates points from grid
%
% Input
%  S2G    - @S2Grid
%  points - logical | double | @vector3d
%
% Output
%  S2G    - @S2Grid
%
% See also
% S2Grid/copy

if isa(points,'vector3d'), points = find(S2G,points); end

if isnumeric(points),
  inds = false(length(S2G),1);
  inds(points) = true;
  points = inds;
end

S2G.x(points) = [];
S2G.y(points) = [];
S2G.z(points) = [];

cs = [0 cumsum(GridLength(S2G.rhoGrid))];
% update rho indexing
for ith = 1:GridLength(S2G.thetaGrid)
  irh = points(cs(ith)+1:cs(ith+1));
  S2G.rhoGrid(ith) = delete(S2G.rhoGrid(ith),irh);
end

% update theta indexing
S2G.thetaGrid = delete(S2G.thetaGrid,GridLength(S2G.rhoGrid)==0);
S2G.rhoGrid(GridLength(S2G.rhoGrid) == 0) = [];


function test_issue840

% WALLTIME 00:10:00
% MEM 2gb
% DEPENDENCY ft_megplanar ft_combineplanar ft_appenddata

%%

if false
  % let's make three small data structures
  % the data is from https://openneuro.org/datasets/ds000117/versions/1.0.3

  filename = {
    'sub-01_ses-meg_task-facerecognition_run-01_meg.fif'
    'sub-01_ses-meg_task-facerecognition_run-02_meg.fif'
    'sub-01_ses-meg_task-facerecognition_run-03_meg.fif'
    };

  for i=1:numel(filename)
    cfg = [];
    cfg.trialfun = 'ft_trialfun_general';
    cfg.trialdef.triallength = 1;
    cfg.trialdef.ntrials = 10;
    cfg.dataset = filename{i};
    cfg = ft_definetrial(cfg);
    data{i} = ft_preprocessing(cfg);
  end

  data1 = data{1};
  data2 = data{2};
  data3 = data{3};
  clear data
  save data1 data1
  save data2 data2
  save data3 data3

else
  % load it from the mat files
  load(dccnpath('/home/common/matlab/fieldtrip/data/test/issue840/data1.mat'));
  load(dccnpath('/home/common/matlab/fieldtrip/data/test/issue840/data2.mat'));
  load(dccnpath('/home/common/matlab/fieldtrip/data/test/issue840/data3.mat'));

end

%%

cfg = [];
data1_combined = ft_combineplanar(cfg, data1);
data2_combined = ft_combineplanar(cfg, data2);
data3_combined = ft_combineplanar(cfg, data3);

cfg = [];
data_combined_append = ft_appenddata(cfg, data1_combined, data2_combined, data3_combined);
assert( isfield(data_combined_append, 'elec')); % this one is consistent
assert(~isfield(data_combined_append, 'grad')); % this one is not consistent

%%

cfg = [];
data_append = ft_appenddata(cfg, data1, data2, data3);
assert( isfield(data_append, 'elec')); % this one is consistent
assert(~isfield(data_append, 'grad')); % this one is not consistent

%%

cfg = [];
cfg.channel = 'MEG*3';
cfg.method = 'distance';
cfg.grad = ft_convert_units(data1.grad, 'mm');
cfg.neighbourdist = 40;
neighbours = ft_prepare_neighbours(cfg);

%%

cfg = [];
cfg.channel = 'MEG*3';
cfg.neighbours = neighbours;

% the FT_MEGPLANAR function refuses to work on neuromag306 data. Here we carefully
% selected only the magnetometers (both as cfg.channel and in cfg.neighbours) so in
% principle it should work fine. The hack I am using is to make FT_MEGPLANAR think it
% is an itab153 sensor array
data1.grad.type = 'itab153';
data2.grad.type = 'itab153';
data3.grad.type = 'itab153';

data1_planar = ft_megplanar(cfg, data1);
data2_planar = ft_megplanar(cfg, data2);
data3_planar = ft_megplanar(cfg, data3);

%%

cfg = [];
data_planar_append = ft_appenddata(cfg, data1_planar, data2_planar, data3_planar);
assert( isfield(data_planar_append, 'elec')); % this one is consistent
assert(~isfield(data_planar_append, 'grad')); % this one is not consistent

%%

cfg = [];
cfg.channel = {'*_dV', '*_dV'};
data1_planar_combined = ft_combineplanar(cfg, data1_planar);
data2_planar_combined = ft_combineplanar(cfg, data2_planar);
data3_planar_combined = ft_combineplanar(cfg, data3_planar);

%%

cfg = [];
data_planar_combined_append = ft_appenddata(cfg, data1_planar_combined, data2_planar_combined, data3_planar_combined);
assert( isfield(data_planar_combined_append, 'elec')); % this one is consistent
assert(~isfield(data_planar_combined_append, 'grad')); % this one is not consistent

%%

% this fails because it cannot determine the sensor type: the data contains elec, but no grad

% However, if it were to contain grad, it would probably also fail since I modified
% the grad.type above from neuromag306 into itab153. Overall, this is a pretty crappy
% test script. I'll keep it nevertheless, since there are some usefiel parts in it.

if false
  cfg = [];
  cfg.channel = {'*_dV', '*_dV'};
  data_planar_append_combined = ft_combineplanar(cfg, data_planar_append);
end

% Error using ft_checkdata (line 573)
% This function requires data with an 'ctf151_planar', 'ctf275_planar', 'neuromag122',
% 'neuromag306', 'bti248_planar', 'bti148_planar', 'itab153_planar', 'yokogawa160_planar',
% 'yokogawa64_planar' or 'yokogawa440_planar' sensor array.


function test_all(test_dir)
% @=============================================================================
% 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.
% =============================================================================@

%% ===== START =====
% Check inputs
if (nargin < 1) || isempty(test_dir)
    error('You must specify an empty directory in input.');
end
% Create test folder
if ~isdir(test_dir)
    if ~mkdir(test_dir)
        error(['Could not create folder: ' test_dir]);
    end
end
% Start profiling
% profile on
% Reset brainstorm
bst_reset();
% Prepare test folders
BrainstormHomeDir = fileparts(fileparts(fileparts(which(mfilename))));
BrainstormDbDir = fullfile(test_dir, 'brainstorm_db');
if isdir(BrainstormDbDir)
    error(['You should delete the test database folder first: ' BrainstormDbDir]);
end
mkdir(BrainstormDbDir);
% Start Brainstorm with GUI
bst_startup(BrainstormHomeDir, 0, BrainstormDbDir);

% Initialize random generator
if exist('rng', 'file')
    rng('default');
else
    sRnd = RandStream.getDefaultStream;
    sRnd.reset();
end

% The protocol name has to be a valid folder name (no spaces, no weird characters...)
ProtocolName = 'ProtocolTest';
% Delete existing protocol
gui_brainstorm('DeleteProtocol', ProtocolName);
% Create new protocol
gui_brainstorm('CreateProtocol', ProtocolName, 0, 0, BrainstormDbDir);
% Start a new report
bst_report('Start');

%% ===== CREATE SUBJECT =====
% Create subject
SubjectName = 'Subject01';
[sSubject, iSubject] = db_add_subject(SubjectName);
% Set anatomy
sTemplates = bst_get('AnatomyDefaults');
iTemplate = find(strcmpi('ICBM152_2019', {sTemplates.Name}));
db_set_template(iSubject, sTemplates(iTemplate), 0);
% Re-compute the MNI transformation
bst_process('CallProcess', 'process_mni_affine', [], [], ...
    'subjectname', SubjectName);
% Create downsampled cortex
CortexFile = 'Subject01\tess_cortex_pial_low.mat';
CortexLowFile = tess_downsize(CortexFile, 500, 'reducepatch');
% Create a folder
ConditionName = 'simulate';
iStudySim = db_add_condition(SubjectName, ConditionName);

%% ===== DISPLAY ANATOMY =====
% Get the updated subject structure
sSubject = bst_get('Subject', SubjectName);
% Get MRI file and surface files
MriFile    = sSubject.Anatomy(sSubject.iAnatomy).FileName;
CortexFile = sSubject.Surface(sSubject.iCortex).FileName;
HeadFile   = sSubject.Surface(sSubject.iScalp).FileName;
% Display MRI
hFigMri1 = view_mri(MriFile);
hFigMri2 = view_mri_3d(MriFile, [], [], 'NewFigure');
hFigMri3 = view_mri_slices(MriFile, 'x', 20);
% Close figures
bst_report('Snapshot', hFigMri1, [], 'view_mri');
bst_report('Snapshot', hFigMri2, [], 'view_mri_3d');
bst_report('Snapshot', hFigMri3, [], 'view_mri_slices');
close([hFigMri1 hFigMri2 hFigMri3]);
% Display scalp and cortex
hFigSurf = view_surface(HeadFile);
hFigSurf = view_surface(CortexFile, [], [], hFigSurf);
hFigMriSurf = view_mri(MriFile, CortexFile);
% Figure configuration
iTess = 2;
panel_surface('SetShowSulci',     hFigSurf, iTess, 1);
panel_surface('SetSurfaceColor',  hFigSurf, iTess, [1 0 0]);
panel_surface('SetSurfaceSmooth', hFigSurf, iTess, 0.5, 0);
panel_surface('SetSurfaceTransparency', hFigSurf, iTess, 0.8);
figure_3d('SetStandardView', hFigSurf, 'left');
pause(0.5);
% Close figures
bst_report('Snapshot', hFigSurf, [], 'view_surface');
bst_report('Snapshot', hFigMriSurf, [], 'view_mri + surface');
close([hFigSurf hFigMriSurf]);

%% ===== DATA SIMULATION =====
% Process: Simulate generic signals
nfiles = 10;
sfreq = 1000;
sSimScout = bst_process('CallProcess', 'process_simulate_matrix', [], [], ...
    'subjectname', SubjectName, ...
    'condition',   ConditionName, ...
    'samples',     nfiles * sfreq, ...
    'srate',       sfreq, ...
    'matlab',      ['ferp = 1; fnoise = 60;' 10 ...
                    'Data(1,:) = sin(pi + ferp*2*pi*t);' 10 ...
                    'Data(Data < 0) = 0;' 10 ...
                    'Data(1,:) = Data(1,:) + sin(fnoise*2*pi*t + pi*rand(1)) + 4 * (0.5 - rand(1,length(t)));' ...
                    'Data(2,:) =             sin(fnoise*2*pi*t + pi*rand(1)) + 4 * (0.5 - rand(1,length(t)));']);
% Process: Set channel file
bst_process('CallProcess', 'process_import_channel', sSimScout, [], ...
    'usedefault',   'Colin27: 10-20 19');  % Colin27: 10-20 19
% Process: Compute head model
bst_process('CallProcess', 'process_headmodel', sSimScout, [], ...
    'sourcespace', 1, ...  % Cortex surface
    'eeg',         3, ...  % OpenMEEG BEM
    'openmeeg',    struct(...
         'BemSelect',    [0, 0, 1], ...
         'BemCond',      [1, 0.0125, 1], ...
         'BemNames',     {{'Scalp', 'Skull', 'Brain'}}, ...
         'BemFiles',     {{}}, ...
         'isAdjoint',    0, ...
         'isAdaptative', 1, ...
         'isSplit',      0, ...
         'SplitLength',  4000));
% Process: Simulate recordings from scouts
sSimData = bst_process('CallProcess', 'process_simulate_recordings', sSimScout, [], ...
    'scouts',      {'Desikan-Killiany', {'lateraloccipital L', 'medialorbitofrontal R'}}, ...
    'savesources', 0);
% Add time markers
DataMat.Events = db_template('event');
DataMat.Events.label   = 'sin+';
DataMat.Events.color   = [0 1 0];
DataMat.Events.times   = round((.5:1:nfiles) * sfreq) ./ sfreq;
DataMat.Events.epochs  = ones(size(DataMat.Events.times));
DataMat.Events.channels = [];
DataMat.Events.notes    = [];
bst_save(file_fullpath(sSimData.FileName), DataMat, 'v6', 1);
% Save as an EGI raw file
RawFile = bst_fullfile(test_dir, 'run01.raw');
export_data(sSimData.FileName, [], RawFile, 'EEG-EGI-RAW');

%% ===== DISPLAY SENSORS =====
% View sensors
hFig = view_surface(HeadFile);
hFig = view_channels(sSimData.ChannelFile, 'EEG', 1, 1, hFig);
% Hide sensors
hFig = view_channels(sSimData.ChannelFile, 'EEG', 0, 0, hFig);
% Edit good/bad channel for current file
gui_edit_channel(sSimData.ChannelFile);
% Unload everything
bst_memory('UnloadAll', 'Forced');

%% ===== ACCESS THE RECORDINGS =====
% Process: Create link to raw file
sFilesRaw = bst_process('CallProcess', 'process_import_data_raw', [], [], ...
    'subjectname',    SubjectName, ...
    'datafile',       {RawFile, 'EEG-EGI-RAW'}, ...
    'channelreplace', 0, ...
    'channelalign',   0, ...
    'evtmode',        'value');
% Process: Set channel file
bst_process('CallProcess', 'process_import_channel', sFilesRaw, [], ...
    'usedefault',   'Colin27: 10-20 19');  % Colin27: 10-20 19
% Process: Refine registration
bst_process('CallProcess', 'process_headpoints_refine', sFilesRaw, []);
% Process: Project electrodes on scalp
bst_process('CallProcess', 'process_channel_project', sFilesRaw, []);
% Process: Snapshot: Sensors/MRI registration
bst_process('CallProcess', 'process_snapshot', sFilesRaw, [], ...
    'target',   1, ...  % Sensors/MRI registration
    'modality', 4, ...  % EEG
    'orient',   1, ...  % left
    'Comment',  'MEG/MRI Registration');

%% ===== PRE-PROCESSING =====
% Process: Power spectrum density (Welch)
sFilesPsdBefore = bst_process('CallProcess', 'process_psd', sFilesRaw, [], ...
    'timewindow',  [], ...
    'win_length',  1, ...
    'win_overlap', 50, ...
    'sensortypes', 'EEG', ...
    'edit', struct(...
         'Comment',         'Power', ...
         'TimeBands',       [], ...
         'Freqs',           [], ...
         'ClusterFuncTime', 'none', ...
         'Measure',         'power', ...
         'Output',          'all', ...
         'SaveKernel',      0));
% Process: Sinusoid removal: 60Hz 120Hz 180Hz 300Hz
sFilesClean = bst_process('CallProcess', 'process_notch', sFilesRaw, [], ...
    'freqlist',    60, ...
    'sensortypes', 'EEG', ...
    'read_all',    0);
% Process: Band-pass:0.5-40Hz
sFilesClean = bst_process('CallProcess', 'process_bandpass', sFilesClean, [], ...
    'sensortypes', 'EEG', ...
    'highpass',    0.5, ...
    'lowpass',     80, ...
    'attenuation', 'strict', ...  % 60dB
    'mirror',      0);

% Process: Set bad channels
bst_process('CallProcess', 'process_channel_setbad', sFilesClean, [], ...
    'sensortypes', 'Fp2');
% Process: Re-reference EEG
bst_process('CallProcess', 'process_eegref', sFilesClean, [], ...
    'eegref',      'AVERAGE', ...
    'sensortypes', 'EEG');

% Process: Power spectrum density (Welch)
sFilesPsdAfter = bst_process('CallProcess', 'process_psd', sFilesClean, [], ...
    'timewindow',  [], ...
    'win_length',  1, ...
    'win_overlap', 50, ...
    'sensortypes', 'EEG', ...
    'edit', struct(...
         'Comment',         'Power', ...
         'TimeBands',       [], ...
         'Freqs',           [], ...
         'ClusterFuncTime', 'none', ...
         'Measure',         'power', ...
         'Output',          'all', ...
         'SaveKernel',      0));
% Process: Snapshot: Frequency spectrum
bst_process('CallProcess', 'process_snapshot', [sFilesPsdBefore, sFilesPsdAfter], [], ...
    'target',   10, ...  % Frequency spectrum
    'Comment',  'Power spectrum density');

%% ===== ARTIFACT CLEANING =====
% TODO: SSP, ICA, bad segments, other detections, add ECG/EOG to signal

%% ===== IMPORT RECORDINGS =====
% Process: Import MEG/EEG: Events
sFilesEpochs = bst_process('CallProcess', 'process_import_data_event', sFilesClean, [], ...
    'subjectname', SubjectName, ...
    'condition',   '', ...
    'eventname',   'sin+', ...
    'timewindow',  [], ...
    'epochtime',   [-0.100, 0.500], ...
    'createcond',  0, ...
    'ignoreshort', 1, ...
    'usectfcomp',  1, ...
    'usessp',      1, ...
    'freq',        [], ...
    'baseline',    [-0.1, -0.0017]);
% Display raster plot
hFigRaster = view_erpimage({sFilesEpochs.FileName}, 'erpimage', 'EEG');
bst_report('Snapshot', hFigRaster, sFilesEpochs(1).FileName, 'ERP image');
close(hFigRaster);

%% ===== AVERAGE =====
% Process: Average: By trial group (folder average)
sFilesAvg = bst_process('CallProcess', 'process_average', sFilesEpochs, [], ...
    'avgtype',    5, ...  % By trial groups (folder average)
    'avg_func',   7, ...  % Arithmetic average + Standard error
    'weighted',   0, ...
    'keepevents', 1);
% Process: Snapshot: Recordings time series
bst_process('CallProcess', 'process_snapshot', sFilesAvg, [], ...
    'target',     5, ...  % Recordings time series
    'modality',   4, ...  % EEG
    'Comment',    'Evoked response');
% Process: Snapshot: Recordings topography (contact sheet)
bst_process('CallProcess', 'process_snapshot', sFilesAvg, [], ...
    'target',         7, ...  % Recordings topography (contact sheet)
    'modality',       4, ...  % EEG
    'contact_time',   [0, 0.500], ...
    'contact_nimage', 15, ...
    'Comment',        'Evoked response');

%% ===== DISPLAY ERP =====
% View averages
hFigEeg = view_timeseries(sFilesAvg.FileName, 'EEG');
hFigTp1 = view_topography(sFilesAvg.FileName, 'EEG', '2DSensorCap');
hFigTp2 = view_topography(sFilesAvg.FileName, 'EEG', '3DSensorCap');
hFigTp3 = view_topography(sFilesAvg.FileName, 'EEG', '2DDisc');
hFigTp4 = view_topography(sFilesAvg.FileName, 'EEG', '2DLayout');
hFigTp5 = view_topography(sFilesAvg.FileName, 'EEG', '3DElectrodes');
figure_3d('SetStandardView', hFigTp5, 'left');
% Set time: 90ms
panel_time('SetCurrentTime', 0.250);
% Set filters: 40Hz low-pass, no high-pass
panel_filter('SetFilters', 1, 40, 0, [], 0, [], 0, 0);
% View selected sensors
SelectedChannels = {'O1', 'T5'};
bst_figures('SetSelectedRows', SelectedChannels);
hFigSel = view_timeseries(sFilesAvg.FileName, [], SelectedChannels);
% Select time window
figure_timeseries('SetTimeSelectionManual', hFigSel, [0.070, 0.130]);
% Show sensors on 2DSensorCap topography
figure_3d('ViewSensors', hFigTp1, 1, 1);
% Display time contact sheet for a figure
hContactFig = view_contactsheet( hFigTp2, 'time', 'fig', [], 12, [0 0.120] );
% Screen captures
bst_report('Snapshot', hFigSel, sFilesEpochs(1).FileName, 'Selected channels');
bst_report('Snapshot', hFigTp1, sFilesEpochs(1).FileName, '2DSensorCap');
bst_report('Snapshot', hFigTp2, sFilesEpochs(1).FileName, '3DSensorCap');
bst_report('Snapshot', hFigTp3, sFilesEpochs(1).FileName, '2DDisc');
bst_report('Snapshot', hFigTp4, sFilesEpochs(1).FileName, '2DLayout');
bst_report('Snapshot', hFigTp5, sFilesEpochs(1).FileName, '3DElectrodes');
bst_report('Snapshot', hContactFig, sFilesEpochs(1).FileName, 'Contact sheet');
% Colormap tests
bst_colormaps('SetColormapName', 'eeg', 'jet');
bst_colormaps('SetColormapName', 'eeg', 'cmap_rbw');
bst_colormaps('SetColormapAbsolute', 'eeg', 1);
bst_colormaps('SetMaxMode', 'eeg', 'local');
bst_colormaps('SetDisplayColorbar', 'eeg', 0);
bst_colormaps('RestoreDefaults', 'eeg');
gui_edit_channelflag(sFilesAvg.FileName);
bst_memory('UnloadAll', 'Forced');
close(hContactFig);

%% ===== SOURCE ANALYSIS =====
% Copy headmodel from simulation folder to data folder
sHeadmodel = bst_get('HeadModelForStudy', iStudySim);
db_add(sFilesAvg.iStudy, sHeadmodel.FileName);
% No noise model
bst_process('CallProcess', 'process_noisecov', sFilesAvg, [], ...
    'sensortypes',    'EEG', ...
    'target',         1, ...  % Noise covariance
    'identity',       1);     % Identity matrix
% Process: Compute sources [2018]
sAvgSrc = bst_process('CallProcess', 'process_inverse_2018', sFilesAvg, [], ...
    'output',  1, ...  % Kernel only: shared
    'inverse', struct(...
         'Comment',        'sLORETA: EEG', ...
         'InverseMethod',  'minnorm', ...
         'InverseMeasure', 'sloreta', ...
         'SourceOrient',   {{'free'}}, ...
         'Loose',          0.2, ...
         'UseDepth',       0, ...
         'WeightExp',      0.5, ...
         'WeightLimit',    10, ...
         'NoiseMethod',    'reg', ...
         'NoiseReg',       0.1, ...
         'SnrMethod',      'fixed', ...
         'SnrRms',         1e-06, ...
         'SnrFixed',       3, ...
         'ComputeKernel',  1, ...
         'DataTypes',      {{'EEG'}}));
% Process: Snapshot: Sources (one time)
bst_process('CallProcess', 'process_snapshot', sAvgSrc, [], ...
    'target',    8, ...  % Sources (one time)
    'modality',  4, ...  % EEG
    'orient',    3, ...  % top
    'time',      0.230, ...
    'threshold', 60, ...
    'Comment',   'Average sources');

%% ===== SCOUTS =====
% Process: Scouts time series: V1 L
sScout = bst_process('CallProcess', 'process_extract_scout', sAvgSrc, [], ...
    'timewindow',     [], ...
    'scouts',         {'Brodmann', {'V1 L', 'V1 R'}}, ...
    'scoutfunc',      1, ...  % Mean
    'isflip',         1, ...
    'isnorm',         1, ...
    'concatenate',    1, ...
    'save',           1, ...
    'addrowcomment',  1, ...
    'addfilecomment', 1);
% Process: Snapshot: Recordings time series
bst_process('CallProcess', 'process_snapshot', sScout, [], ...
    'target',     5, ...  % Recordings time series
    'modality',   4, ...  % EEG
    'Comment',    'Scouts');

% ===== SOURCE ANALYSIS: VOLUME =====
% Process: Compute head model
bst_process('CallProcess', 'process_headmodel', sFilesAvg, [], ...
    'sourcespace', 2, ...  % MRI volume
    'volumegrid',  struct(...
         'Method',        'adaptive', ...
         'nLayers',       17, ...
         'Reduction',     3, ...
         'nVerticesInit', 4000, ...
         'Resolution',    0.005, ...
         'FileName',      []), ...
    'eeg',         2);     % 3-shell sphere
% Process: Compute sources [2018]
sAvgSrcVol = bst_process('CallProcess', 'process_inverse_2018', sFilesAvg, [], ...
    'output',  1, ...  % Kernel only: shared
    'inverse', struct(...
         'Comment',        'Dipoles: EEG', ...
         'InverseMethod',  'gls', ...
         'InverseMeasure', 'performance', ...
         'SourceOrient',   {{'free'}}, ...
         'Loose',          0.2, ...
         'UseDepth',       1, ...
         'WeightExp',      0.5, ...
         'WeightLimit',    10, ...
         'NoiseMethod',    'reg', ...
         'NoiseReg',       0.1, ...
         'SnrMethod',      'rms', ...
         'SnrRms',         1e-06, ...
         'SnrFixed',       3, ...
         'ComputeKernel',  1, ...
         'DataTypes',      {{'EEG'}}));
% Process: Snapshot: Sources (one time)
bst_process('CallProcess', 'process_snapshot', sAvgSrcVol, [], ...
    'target',    8, ...  % Sources (one time)
    'modality',  1, ...  % MEG (All)
    'orient',    3, ...  % top
    'time',      0, ...
    'threshold', 0, ...
    'Comment',   'Dipole modeling');
% Process: Dipole scanning
sDipScan = bst_process('CallProcess', 'process_dipole_scanning', sAvgSrcVol, [], ...
    'timewindow', [-0.040, 0.100], ...
    'scouts',     {});
% Process: Snapshot: Dipoles
bst_process('CallProcess', 'process_snapshot', sDipScan, [], ...
    'target',    13, ...  % Dipoles
    'orient',    3, ...   % top
    'threshold', 90, ...
    'Comment',   'Dipole scanning');
% Process: Snapshot: Dipoles
bst_process('CallProcess', 'process_snapshot', sDipScan, [], ...
    'target',    13, ...  % Dipoles
    'orient',    1, ...   % left
    'threshold', 90, ...
    'Comment',   'Dipole scanning');

%% ===== TIME-FREQUENCY =====
% Process: Time-frequency (Morlet wavelets)
sFilesTf = bst_process('CallProcess', 'process_timefreq', sFilesEpochs, [], ...
    'sensortypes', 'EEG', ...
    'edit',        struct(...
         'Comment',         'Avg,Power,1-80Hz', ...
         'TimeBands',       [], ...
         'Freqs',           [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80], ...
         'MorletFc',        1, ...
         'MorletFwhmTc',    3, ...
         'ClusterFuncTime', 'none', ...
         'Measure',         'power', ...
         'Output',          'average', ...
         'RemoveEvoked',    0, ...
         'SaveKernel',      0), ...
    'normalize',   'none');  % None: Save non-standardized time-frequency maps
% Process: Event-related perturbation (ERS/ERD): [-100ms,-1ms]
sFilesTfNorm = bst_process('CallProcess', 'process_baseline_norm', sFilesTf, [], ...
    'baseline',  [-0.1, -0.001], ...
    'method',    'ersd', ...  % Event-related perturbation (ERS/ERD):    x_std = (x - μ) / μ * 100
    'overwrite', 0);
% Process: Snapshot: Time-frequency maps
bst_process('CallProcess', 'process_snapshot', sFilesTfNorm, [], ...
    'target',   14, ...  % Time-frequency maps
    'Comment',  'Not normalized');

%% ===== STOP AND REPORTING =====
ReportFile = bst_report('Save', []);
HtmlFile = bst_report('Export', ReportFile, test_dir);
web(HtmlFile);

% Start Brainstorm with no GUI
% brainstorm nogui
% brainstorm stop

% Stop profiling
% profile viewer
% profiview
% coveragerpt


clc
clear all

source_dir = 'D:\Graduate\Comparison\Comparison_IAFusion\Dataset\RoadScene_VI';
save_dir = 'D:\Graduate\Comparison\Comparison_IAFusion\Dataset\RoadScene_vi1';
if exist(save_dir,'dir')==0.
	 mkdir(save_dir);
end
fileFolder=fullfile(source_dir);
dirOutput=dir(fullfile(source_dir,'*.bmp'));
fileNames = {dirOutput.name};
[m, num] = size(fileNames);
for i = 1:num
    fileName_source = fullfile(source_dir, fileNames{i})
    save_name = fullfile(save_dir, fileNames{i});
    source_image = imread(fileName_source);
    [h, w, c]=size(source_image)
    if c > 1
        source_image = rgb2gray(source_image);
    end
    save_image = source_image(1:floor(h/4) *4, 1:floor(w/4)*4);
    [h1, w1]=size(save_image)
    imwrite(save_image, save_name)
end


function v = cvx_value( x )
v = x;

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


%FIX_LINES  Improves the line style of eps files generated by print
%
% Examples:
%   fix_lines fname
%   fix_lines fname fname2
%   fstrm_out = fixlines(fstrm_in)
%
% This function improves the style of lines in eps files generated by
% MATLAB's print function, making them more similar to those seen on
% screen. Grid lines are also changed from a dashed style to a dotted
% style, for greater differentiation from dashed lines.
%
% The function also places embedded fonts after the postscript header, in
% versions of MATLAB which place the fonts first (R2006b and earlier), in
% order to allow programs such as Ghostscript to find the bounding box
% information.
%
%IN:
%   fname - Name or path of source eps file.
%   fname2 - Name or path of destination eps file. Default: same as fname.
%   fstrm_in - File contents of a MATLAB-generated eps file.
%
%OUT:
%   fstrm_out - Contents of the eps file with line styles fixed.

% Copyright: (C) Oliver Woodford, 2008-2014

% The idea of editing the EPS file to change line styles comes from Jiro
% Doke's FIXPSLINESTYLE (fex id: 17928)
% The idea of changing dash length with line width came from comments on
% fex id: 5743, but the implementation is mine :)

% Thank you to Sylvain Favrot for bringing the embedded font/bounding box
% interaction in older versions of MATLAB to my attention.
% Thank you to D Ko for bringing an error with eps files with tiff previews
% to my attention.
% Thank you to Laurence K for suggesting the check to see if the file was
% opened.

% 01/03/15: Issue #20: warn users if using this function in HG2 (R2014b+)
% 27/03/15: Fixed out of memory issue with enormous EPS files (generated by print() with OpenGL renderer), related to issue #39

function fstrm = fix_lines(fstrm, fname2)

% Issue #20: warn users if using this function in HG2 (R2014b+)
if using_hg2
    warning('export_fig:hg2','The fix_lines function should not be used in this Matlab version.');
end

if nargout == 0 || nargin > 1
    if nargin < 2
        % Overwrite the input file
        fname2 = fstrm;
    end
    % Read in the file
    fstrm = read_write_entire_textfile(fstrm);
end

% Move any embedded fonts after the postscript header
if strcmp(fstrm(1:15), '%!PS-AdobeFont-')
    % Find the start and end of the header
    ind = regexp(fstrm, '[\n\r]%!PS-Adobe-');
    [ind2, ind2] = regexp(fstrm, '[\n\r]%%EndComments[\n\r]+');
    % Put the header first
    if ~isempty(ind) && ~isempty(ind2) && ind(1) < ind2(1)
        fstrm = fstrm([ind(1)+1:ind2(1) 1:ind(1) ind2(1)+1:end]);
    end
end

% Make sure all line width commands come before the line style definitions,
% so that dash lengths can be based on the correct widths
% Find all line style sections
ind = [regexp(fstrm, '[\n\r]SO[\n\r]'),... % This needs to be here even though it doesn't have dots/dashes!
       regexp(fstrm, '[\n\r]DO[\n\r]'),...
       regexp(fstrm, '[\n\r]DA[\n\r]'),...
       regexp(fstrm, '[\n\r]DD[\n\r]')];
ind = sort(ind);
% Find line width commands
[ind2, ind3] = regexp(fstrm, '[\n\r]\d* w[\n\r]');
% Go through each line style section and swap with any line width commands
% near by
b = 1;
m = numel(ind);
n = numel(ind2);
for a = 1:m
    % Go forwards width commands until we pass the current line style
    while b <= n && ind2(b) < ind(a)
        b = b + 1;
    end
    if b > n
        % No more width commands
        break;
    end
    % Check we haven't gone past another line style (including SO!)
    if a < m && ind2(b) > ind(a+1)
        continue;
    end
    % Are the commands close enough to be confident we can swap them?
    if (ind2(b) - ind(a)) > 8
        continue;
    end
    % Move the line style command below the line width command
    fstrm(ind(a)+1:ind3(b)) = [fstrm(ind(a)+4:ind3(b)) fstrm(ind(a)+1:ind(a)+3)];
    b = b + 1;
end

% Find any grid line definitions and change to GR format
% Find the DO sections again as they may have moved
ind = int32(regexp(fstrm, '[\n\r]DO[\n\r]'));
if ~isempty(ind)
    % Find all occurrences of what are believed to be axes and grid lines
    ind2 = int32(regexp(fstrm, '[\n\r] *\d* *\d* *mt *\d* *\d* *L[\n\r]'));
    if ~isempty(ind2)
        % Now see which DO sections come just before axes and grid lines
        ind2 = repmat(ind2', [1 numel(ind)]) - repmat(ind, [numel(ind2) 1]);
        ind2 = any(ind2 > 0 & ind2 < 12); % 12 chars seems about right
        ind = ind(ind2);
        % Change any regions we believe to be grid lines to GR
        fstrm(ind+1) = 'G';
        fstrm(ind+2) = 'R';
    end
end

% Define the new styles, including the new GR format
% Dot and dash lengths have two parts: a constant amount plus a line width
% variable amount. The constant amount comes after dpi2point, and the
% variable amount comes after currentlinewidth. If you want to change
% dot/dash lengths for a one particular line style only, edit the numbers
% in the /DO (dotted lines), /DA (dashed lines), /DD (dot dash lines) and
% /GR (grid lines) lines for the style you want to change.
new_style = {'/dom { dpi2point 1 currentlinewidth 0.08 mul add mul mul } bdef',... % Dot length macro based on line width
             '/dam { dpi2point 2 currentlinewidth 0.04 mul add mul mul } bdef',... % Dash length macro based on line width
             '/SO { [] 0 setdash 0 setlinecap } bdef',... % Solid lines
             '/DO { [1 dom 1.2 dom] 0 setdash 0 setlinecap } bdef',... % Dotted lines
             '/DA { [4 dam 1.5 dam] 0 setdash 0 setlinecap } bdef',... % Dashed lines
             '/DD { [1 dom 1.2 dom 4 dam 1.2 dom] 0 setdash 0 setlinecap } bdef',... % Dot dash lines
             '/GR { [0 dpi2point mul 4 dpi2point mul] 0 setdash 1 setlinecap } bdef'}; % Grid lines - dot spacing remains constant

% Construct the output
% This is the original (memory-intensive) code:
%first_sec = strfind(fstrm, '% line types:'); % Isolate line style definition section
%[second_sec, remaining] = strtok(fstrm(first_sec+1:end), '/');
%[remaining, remaining] = strtok(remaining, '%');
%fstrm = [fstrm(1:first_sec) second_sec sprintf('%s\r', new_style{:}) remaining];
fstrm = regexprep(fstrm,'(% line types:.+?)/.+?%',['$1',sprintf('%s\r',new_style{:}),'%']);

% Write the output file
if nargout == 0 || nargin > 1
    read_write_entire_textfile(fname2, fstrm);
end
end