function rez = fitTemplates(rez, DATA, uproj)

nt0             = rez.ops.nt0;
rez.ops.nt0min  = ceil(20 * nt0/61);

ops = rez.ops;

rng('default');
rng(1);

Nbatch      = rez.temp.Nbatch;
Nbatch_buff = rez.temp.Nbatch_buff;

Nfilt 	= ops.Nfilt; %256+128;

ntbuff  = ops.ntbuff;
NT  	= ops.NT;

Nrank   = ops.Nrank;
Th 		= ops.Th;
maxFR 	= ops.maxFR;

Nchan 	= ops.Nchan;

batchstart = 0:NT:NT*(Nbatch-Nbatch_buff);

delta = NaN * ones(Nbatch, 1);
iperm = randperm(Nbatch);

switch ops.initialize
    case 'fromData'
        WUinit = optimizePeaks(ops,uproj);%does a scaled kmeans
        dWU    = WUinit(:,:,1:Nfilt);
        %             dWU = alignWU(dWU);
    otherwise
        if ~isempty(getOr(ops, 'initFilePath', [])) && ~getOr(ops, 'saveInitTemps', 0)
            load(ops.initFilePath);
            dWU = WUinit(:,:,1:Nfilt);
        else
            initialize_waves0;

            ipck = randperm(size(Winit,2), Nfilt);
            W = [];
            U = [];
            for i = 1:Nrank
                W = cat(3, W, Winit(:, ipck)/Nrank);
                U = cat(3, U, Uinit(:, ipck));
            end
            W = alignW(W, ops);

            dWU = zeros(nt0, Nchan, Nfilt, 'single');
            for k = 1:Nfilt
                wu = squeeze(W(:,k,:)) * squeeze(U(:,k,:))';
                newnorm = sum(wu(:).^2).^.5;
                W(:,k,:) = W(:,k,:)/newnorm;

                dWU(:,:,k) = 10 * wu;
            end
            WUinit = dWU;
        end
end
if getOr(ops, 'saveInitTemps', 0)
    if ~isempty(getOr(ops, 'initFilePath', []))
        save(ops.initFilePath, 'WUinit')
    else
       warning('cannot save initialization templates because a savepath was not specified in ops.saveInitTemps');
    end
end

[W, U, mu, UtU, nu] = decompose_dWU(ops, dWU, Nrank, rez.ops.kcoords);
W0 = W;
W0(NT, 1) = 0;
fW = fft(W0, [], 1);
fW = conj(fW);

nspikes = zeros(Nfilt, Nbatch);
lam =  ones(Nfilt, 1, 'single');

freqUpdate = 100 * 4;
iUpdate = 1:freqUpdate:Nbatch;

dbins = zeros(100, Nfilt);
dsum = 0;
miniorder = repmat(iperm, 1, ops.nfullpasses);
%     miniorder = repmat([1:Nbatch Nbatch:-1:1], 1, ops.nfullpasses/2);

i = 1; % first iteration

epu = ops.epu;

%%
% pmi = exp(-1./exp(linspace(log(ops.momentum(1)), log(ops.momentum(2)), Nbatch*ops.nannealpasses)));
pmi = exp(-1./linspace(1/ops.momentum(1), 1/ops.momentum(2), Nbatch*ops.nannealpasses));
% pmi = exp(-linspace(ops.momentum(1), ops.momentum(2), Nbatch*ops.nannealpasses));

% pmi  = linspace(ops.momentum(1), ops.momentum(2), Nbatch*ops.nannealpasses);
Thi  = linspace(ops.Th(1),                 ops.Th(2), Nbatch*ops.nannealpasses);
if ops.lam(1)==0
    lami = linspace(ops.lam(1), ops.lam(2), Nbatch*ops.nannealpasses);
else
    lami = exp(linspace(log(ops.lam(1)), log(ops.lam(2)), Nbatch*ops.nannealpasses));
end

if Nbatch_buff<Nbatch
    fid = fopen(ops.fproc, 'r');
end

nswitch = [0];
msg = [];
fprintf('Time %3.0fs. Optimizing templates ...\n', toc)
while (i<=Nbatch * ops.nfullpasses+1)
    % set the annealing parameters
    if i<Nbatch*ops.nannealpasses
        Th      = Thi(i);
        lam(:)  = lami(i);
        pm      = pmi(i);
    end

    % some of the parameters change with iteration number
    Params = double([NT Nfilt Th maxFR 10 Nchan Nrank pm epu nt0]);

    % update the parameters every freqUpdate iterations
    if i>1 &&  ismember(rem(i,Nbatch), iUpdate) %&& i>Nbatch
        dWU = gather_try(dWU);

        % break bimodal clusters and remove low variance clusters
        if  ops.shuffle_clusters &&...
                i>Nbatch && rem(rem(i,Nbatch), 4*400)==1    % i<Nbatch*ops.nannealpasses
            [dWU, dbins, nswitch, nspikes, iswitch] = ...
                replace_clusters(dWU, dbins,  Nbatch, ops.mergeT, ops.splitT, WUinit, nspikes);
        end

        dWU = alignWU(dWU, ops);

        % restrict spikes to their peak group
        %         dWU = decompose_dWU(dWU, kcoords);

        % parameter update
        [W, U, mu, UtU, nu] = decompose_dWU(ops, dWU, Nrank, rez.ops.kcoords);

        if ops.GPU
            dWU = gpuArray(dWU);
        else
            W0 = W;
            W0(NT, 1) = 0;
            fW = fft(W0, [], 1);
            fW = conj(fW);
        end

        NSP = sum(nspikes,2);
        if ops.showfigures
%             set(0,'DefaultFigureWindowStyle','docked')
%             figure;
            subplot(2,2,1)
            for j = 1:10:Nfilt
                if j+9>Nfilt;
                    j = Nfilt -9;
                end
                plot(log(1+NSP(j + [0:1:9])), mu(j+ [0:1:9]), 'o');
                xlabel('log of number of spikes')
                ylabel('amplitude of template')
                hold all
            end
            axis tight;
            title(sprintf('%d  ', nswitch));
            subplot(2,2,2)
            plot(W(:,:,1))
            title('timecourses of top PC')

            subplot(2,2,3)
            imagesc(U(:,:,1))
            title('spatial mask of top PC')

            drawnow
        end
        % break if last iteration reached
        if i>Nbatch * ops.nfullpasses; break; end

        % record the error function for this iteration
        rez.errall(ceil(i/freqUpdate))          = nanmean(delta);

    end

    % select batch and load from RAM or disk
    ibatch = miniorder(i);
    if ibatch>Nbatch_buff
        offset = 2 * ops.Nchan*batchstart(ibatch-Nbatch_buff);
        fseek(fid, offset, 'bof');
        dat = fread(fid, [NT ops.Nchan], '*int16');
    else
        dat = DATA(:,:,ibatch);
    end

    % move data to GPU and scale it
    if ops.GPU
        dataRAW = gpuArray(dat);
    else
        dataRAW = dat;
    end
    dataRAW = single(dataRAW);
    dataRAW = dataRAW / ops.scaleproc;

    % project data in low-dim space
    data = dataRAW * U(:,:);

    if ops.GPU
        % run GPU code to get spike times and coefficients
        [dWU, ~, id, x,Cost, nsp] = ...
            mexMPregMU(Params,dataRAW,W,data,UtU,mu, lam .* (20./mu).^2, dWU, nu);
    else
        [dWU, ~, id, x,Cost, nsp] = ...
            mexMPregMUcpu(Params,dataRAW,fW,data,UtU,mu, lam .* (20./mu).^2, dWU, nu, ops);
    end

    dbins = .9975 * dbins;  % this is a hard-coded forgetting factor, needs to become an option
    if ~isempty(id)
        % compute numbers of spikes
        nsp                = gather_try(nsp(:));
        nspikes(:, ibatch) = nsp;

        % bin the amplitudes of the spikes
        xround = min(max(1, int32(x)), 100);

        dbins(xround + id * size(dbins,1)) = dbins(xround + id * size(dbins,1)) + 1;

        % estimate cost function at this time step
        delta(ibatch) = sum(Cost)/1e3;
    end

    % update status
    if ops.verbose  && rem(i,20)==1
        nsort = sort(round(sum(nspikes,2)), 'descend');
        fprintf(repmat('\b', 1, numel(msg)));
        msg = sprintf('Time %2.2f, batch %d/%d, mu %2.2f, neg-err %2.6f, NTOT %d, n100 %d, n200 %d, n300 %d, n400 %d\n', ...
            toc, i,Nbatch* ops.nfullpasses,nanmean(mu(:)), nanmean(delta), round(sum(nsort)), ...
            nsort(min(size(W,2), 100)), nsort(min(size(W,2), 200)), ...
            nsort(min(size(W,2), 300)), nsort(min(size(W,2), 400)));
        fprintf(msg);
    end

    % increase iteration counter
    i = i+1;
end

% close the data file if it has been used
if Nbatch_buff<Nbatch
    fclose(fid);
end

if ~ops.GPU
   rez.fW = fW; % save fourier space templates if on CPU
end
rez.dWU               = gather_try(dWU);
rez.nspikes               = nspikes;
% %%


function S = inv(C)
% stiffness to compliance tensor
%
% Input
%  C - @complianceTensor
%
% Output
%  S - @complianceTensor
%

S = complianceTensor(inv@tensor(C));


function [D, S] = spm_eeg_reref_eeg(S)
% Rereference EEG data to new reference channel(s)
% FORMAT [D, S] = spm_eeg_reref_eeg(S)
%
% S                    - input structure (optional)
% (optional) fields of S:
%   S.D                - MEEG object or filename of M/EEG mat-file
%   S.refchan          - New reference channel indices or labels
%                        ('average' can be used as shortcut)
%
% Output:
% D                    - MEEG object (also written on disk)
% S                    - record of parameters, including montage
%__________________________________________________________________________
%
% spm_eeg_reref_eeg re-references any EEG data within an MEEG dataset, by
% calling spm_eeg_montage with appropriate montage, excluding bad channels
%__________________________________________________________________________
% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging

% Rik Henson (updated so that bad channels included in tra but not used)
% $Id: spm_eeg_reref_eeg.m 6374 2015-03-12 10:15:18Z vladimir $

SVNrev = '$Rev: 6374 $';

%-Startup
%--------------------------------------------------------------------------
spm('FnBanner', mfilename, SVNrev);
spm('FigName','EEG reref'); spm('Pointer','Watch');

%-Get MEEG object
%--------------------------------------------------------------------------
try
    D = S.D;
catch
    [D, sts] = spm_select(1, 'mat', 'Select M/EEG mat file');
    if ~sts, D = []; return; end
    S.D = D;
end

D = spm_eeg_load(D);

% Get indices for just EEG channels and remove any bad channels
%--------------------------------------------------------------------------
eegchan  = D.indchantype('EEG');
goodchan = setdiff(eegchan, D.badchannels);

if isempty(goodchan)
    error('No EEG channels that are not marked as bad...!?')
end

%-Get reference channel indices
%--------------------------------------------------------------------------
if ~isfield(S, 'refchan')
      [selection, ok]= listdlg('ListString', D.chanlabels(goodchan), 'SelectionMode', 'multiple' ,'Name', 'Select reference channels' , 'ListSize', [400 300]);
    if ~ok
        return;
    end

    if length(selection) == length(goodchan)
        S.refchan = 'average';
    else
        S.refchan = D.chanlabels(goodchan(selection));
    end
end

if strcmpi(S.refchan,'average')
    refchan = goodchan;
elseif iscell(S.refchan)
    refchan = D.indchannel(S.refchan);
elseif isnumeric(S.refchan)
    refchan = S.refchan;
end

refind  = find(ismember(eegchan, refchan));
goodind = find(ismember(eegchan, goodchan));
badind  = find(ismember(eegchan, D.badchannels));

if length(refind) ~= length(refchan)
    error('Not all S.refchan are valid indices for (non-bad) EEG channels')
end

tra                 = eye(length(eegchan));
tra(goodind,refind) = tra(goodind,refind) - 1/length(refchan);
tra(badind,refind)  = tra(badind,refind)  - 1/length(refchan);

S1=[];
S1.D = D;
S1.montage.labelorg = D.chanlabels(eegchan);
S1.montage.labelnew = D.chanlabels(eegchan);
S1.montage.chantypeorg = lower(D.chantype(eegchan)');
S1.montage.chantypenew = lower(S1.montage.chantypeorg);
S1.montage.chanunitorg = D.units(eegchan)';
S1.montage.chanunitnew = S1.montage.chanunitorg;
S1.montage.tra = tra;
S1.keepothers = 1;
S1.updatehistory = 0;
D = spm_eeg_montage(S1);

%-Update history (not necessary; leave as call to spm_eeg_montage?) Save new evoked M/EEG dataset
%--------------------------------------------------------------------------
D = D.history('spm_eeg_reref_eeg', S);

save(D);

%-Cleanup
%--------------------------------------------------------------------------
spm_progress_bar('Clear');
spm('FigName','EEG re-reference: done'); spm('Pointer','Arrow');


function EEG = smooth_prob(EEG, smoothlen)
if nargin<2
    smoothlen = 2;
end

llt_smooth = zeros(size(EEG.etc.amica.LLt));
smoothwnd = hann(smoothlen*EEG.srate); smoothwnd = smoothwnd/sum(smoothwnd);
for i = 1:size(llt_smooth,1)
    llt_smooth(i,:) = filtfilt(smoothwnd,1,EEG.etc.amica.LLt(i,:)')';
end
for i = 1:size(llt_smooth,1)
    EEG.etc.amica.LLt_smooth(i,:) = llt_smooth(i,:);
end

v_smooth = zeros(size(llt_smooth));
for m = 1:size(v_smooth,1)-1
    v_smooth(m,:) = 1./sum(exp(bsxfun(@minus,llt_smooth,llt_smooth(m,:))),1);
end
v_smooth(m+1,:) = ones(1,size(v_smooth,2)) - sum(v_smooth(1:m,:));
for i = 1:size(v_smooth,1)
    EEG.etc.amica.v_smooth(i,:) = v_smooth(i,:);
end

if ~isempty(EEG.etc.amica.v_cropped)
    EEG.etc.amica.v_smooth_cropped = cropvector(EEG.etc.amica.v_smooth,EEG.etc.amica.crop_start,EEG.etc.amica.crop_end);
end


% aa = dir('../3D_results_plot/results/3dLandmarks_proj_resnet50/*.mat');
clear all;
close all;
grdPath = './results/AFLW-2000-3D_grdth/';
grdDir = dir('./results/AFLW-2000-3D_grdth/*.jpg');
aa = textread('./keypoints.txt', '%s');
bb = dir('./results/data/DeFA/mesh/*.mat');

pts = zeros(204, 1);
for i = 1:204
    xx = aa(i);
    xx = xx{1};
    pts(i) = str2num(xx(1:end-1));
end
pts = reshape(pts, 3, 68) + 1;
index = pts(3,:)/3;

LL = 40;
nme_list_2DASSL = zeros(2, LL);
nme_list_3DDFA = zeros(2, LL);
for ii = 1:LL
    ii
    imgName = grdDir(ii).name;
    info = load(strcat(grdPath,strrep(imgName, 'jpg', 'mat')));
    pts1 = info.pt3d_68;
    pts1 = pts1(1:3,:);
    pts1(3,:) = pts1(3,:) -min(pts1(3,:));
    img = imread(strcat(grdPath, imgName));

    pp = textread(strcat('./results/PRNet_results/kpt/', imgName(1:end-4), '_kpt.txt'));
    pts3 = pp(:,1:3)';
    pts3(3,:) = pts3(3,:) -min(pts3(3,:));

    vertex = load(strcat('./results/2DASL_results/', strrep(imgName, 'jpg', 'mat')));
    pts5 = vertex.vertex(:,index);
    pts5 = pts5(1:3,:);
    pts5(3,:) = pts5(3,:) -min(pts5(3,:));

%     imshow(img);
%     hold on
%     for i = 1:68
%         plot(pts1(1,i), pts1(2,i), 'o');
%     end

    % NME calculation
    minx = min(pts1(1,:)); miny = min(pts1(2,:));
    maxx = max(pts1(1,:)); maxy = max(pts1(2,:));
    llen = sqrt((maxx-minx)*(maxy-miny));

    dis2 = abs(pts1 - pts3);
    dis2 = sqrt(sum(dis2.^2));
    dis2 = mean(dis2)/llen;

    dis4 = abs(pts1 - pts5);
    dis4 = sqrt(sum(dis4.^2));
    dis4 = mean(dis4)/llen;

    nme_list_PRNet(1, ii) = dis2;
    nme_list_2DASSL(1, ii) = dis4;
end

% save('nme_list', 'nme_list');
dis_PRNet = nme_list_PRNet(1,:);
dis_2DASSL = nme_list_2DASSL(1,:);

[s_dis_PRNet, index] = sort(dis_PRNet);
[s_dis_2DASSL, index] = sort(dis_2DASSL);

s_dis_PRNet = s_dis_PRNet(1:LL);
s_dis_2DASSL = s_dis_2DASSL(1:LL);

x_len = 0:1:length(s_dis_PRNet);
x_len = x_len(1:length(s_dis_PRNet));
plot(s_dis_PRNet*100, x_len, 'c', 'linewidth',2);
hold on
plot(s_dis_2DASSL*100, x_len, 'g', 'linewidth',2);

axis([0 10 0 LL])
set(gca,'XLim',[0 10]);%
set(gca,'YLim',[0 LL]);%

grid on
grid minor

ax = gca;
ax.GridColor = [0 .5 .5];
ax.GridLineStyle = '--';
ax.GridAlpha = 0.5;
ax.Layer = 'top';

h = legend('PRNet', '2DASSL', 'Location','southeast')
%set(h,'Orientation','horizon', 'Fontsize',12)
set(h,'Fontsize',12)

xlabel('NME normalized by bounding box', 'fontsize', 12)
ylabel('Number of images', 'fontsize',12)


%--- help for generic/setup_priors ---
%
%  INTERNAL FUNCTION: Format the priors for the estimation of models in RISE.
%
%  ::
%
%    obj=setup_priors(obj,MyPriors)
%    obj=setup_priors(obj,MyPriors,error_control)
%
%  Args:
%
%     obj (rise | dsge | svar | rfvar): model object. This function is not
%       meant to be called directly by the user. But it shows how to write the
%       priors when those are not written inside the model file.
%
%     MyPriors (struct): Each field of the structure is one of the
%       following alternatives
%
%       - Maximum likelihood or uniform priors::
%
%           P.pname={start_value,lower_bound,upper_bound};
%
%       - Bayesian prior using mean and standard deviation::
%
%           P.pname={start_value,prior_mean,prior_stdev,'distribution'};
%
%       - Same as above except for adding a hard lower bound::
%
%           P.pname={start_value,prior_mean,prior_stdev,'distribution',lower_bound};
%
%       - Same as above except for adding a hard upper bound::
%
%           P.pname={start_value,prior_mean,prior_stdev,'distribution',lower_bound,upper_bound};
%
%       - Bayesian prior using quantiles of the distribution::
%
%           P.pname={start_value,lower_quantile,lower_quantile,'distribution(prob)'};
%
%       - Same as above except for adding a hard lower bound::
%
%           P.pname={start_value,lower_quantile,lower_quantile,'distribution(prob)',lower_bound};
%
%       - Same as above except for adding a hard upper bound::
%
%           P.pname={start_value,lower_quantile,lower_quantile,'distribution(prob)',lower_bound,upper_bound};
%
%       - Dirichlet
%
%           Denote by dirichlet_j, the jth dirichlet distribution, j=1,2,... we
%           have P.dirichlet_j={sd_ii,pname1,m1,pname2,m2,...,pnamen,mn} where
%
%             - sd_ii: is the standard deviation of the diagonal element (a
%               parameter never listed by RISE) of the dirichlet distribution.
%             - pname1, pname2,...,pnamen: are the names of the off diagonal
%               parameters
%             - m1, m2,...,mn: are the means of each parameters
%
%     error_control (empty | cell): element constructed by RISE for
%       controling the syntax used in the model file.
%
%  Returns:
%     :
%
%     - **obj** [rise|dsge|svar|rfvar]: updated model object.
%
%  Note:
%
%     - This function is also indirectly used for svar and rfvar objects.
%     - Possible distributions include:
%
%        - beta
%        - cauchy
%        - gamma
%        - inv_gamma
%        - laplace
%        - left_triang
%        - logistic
%        - lognormal
%        - normal
%        - pareto
%        - right_triang
%        - uniform
%        - weibull
%
%  See also:
%     - rise_generic/estimate
%     - dsge/estimate
%
%


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

addpath(genpath('../libs'))
path_to_matconvnet = '/home/skong2/scratch/matconvnet-1.0-beta23_modifiedDagnn';

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

% saveFolder = 'semantic_main003_seg_v4_largeLR_l2norm_toyDigitV3';
% modelName = 'softmax_net-epoch-63.mat';

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

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

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

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

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

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

    % add max-margin loss
    obj_name = sprintf('loop%d_instSeg_MM', loopIdx);
    netbasemodel.addLayer(obj_name, ...
        InstanceSegMMLoss_randSample('loss', 'cosinesimilaritymmloss', 'marginAlpha_', 0.07, 'adaptiveMM', false, 'lastLayerName', sName), ...
        {sName, gt_name}, obj_name)
    weight_for_losses{end+1} = obj_name;
    weight_for_losses{end+1} = 1;
end
%% show learning rates for all layers
for ii = 1:numel(netbasemodel.layers)
    curLayerName = netbasemodel.layers(ii).name;
    if strfind(curLayerName, 'bn')
        fprintf('%03d, %s\n', ii, curLayerName);
%         net.layers(ii).block.bnorm_moment_type_trn = 'batch';
%         net.layers(ii).block.bnorm_moment_type_tst = 'global';
        netbasemodel.params(netbasemodel.layers(ii).paramIndexes(3)).learningRate = 0.1;
    end
end
netbasemodel.params(netbasemodel.getParamIndex('res6_conv_f')).learningRate = 1;
netbasemodel.params(netbasemodel.getParamIndex('res6_conv_b')).learningRate = 1;
%{
for i = 125:numel(netbasemodel.params)
    if ~isempty(strfind(netbasemodel.params(i).name, '_f')) && isempty(strfind(netbasemodel.params(i).name, '_bn'))
        tmp = netbasemodel.params(i).value;
        tmp = single(randn(size(tmp), 'single')*sqrt(2/size(tmp,4)));
        netbasemodel.params(i).value = tmp;
        fprintf('%s \n', netbasemodel.params(i).name);
    elseif ~isempty(strfind(netbasemodel.params(i).name, '_b')) && isempty(strfind(netbasemodel.params(i).name, '_bn'))
        tmp = netbasemodel.params(i).value;
        netbasemodel.params(i).value = zeros(1, size(tmp,2), 'single');
        fprintf('%s \n', netbasemodel.params(i).name);
    elseif ~isempty(strfind(netbasemodel.params(i).name, '_bn')) && ~isempty(strfind(netbasemodel.params(i).name, '_w'))
        tmp = netbasemodel.params(i).value;
        tmp = ones(size(tmp), 'single');
        netbasemodel.params(i).value = tmp;
        fprintf('%s \n', netbasemodel.params(i).name);
    elseif ~isempty(strfind(netbasemodel.params(i).name, '_bn')) && ~isempty(strfind(netbasemodel.params(i).name, '_b'))
        tmp = netbasemodel.params(i).value;
        tmp = zeros(size(tmp), 'single');
        netbasemodel.params(i).value = tmp;
        fprintf('%s \n', netbasemodel.params(i).name);
    elseif ~isempty(strfind(netbasemodel.params(i).name, '_bn')) && ~isempty(strfind(netbasemodel.params(i).name, '_m'))
        tmp = netbasemodel.params(i).value;
        tmp = zeros(size(tmp), 'single');
        netbasemodel.params(i).value = tmp;
        fprintf('%s \n', netbasemodel.params(i).name);
    else
        fprintf('%s ERROR!!!\n', netbasemodel.params(i).name);
    end
end
%}
for i = 1:numel(netbasemodel.params)
    fprintf('%d\t%25s, \t%.2f',i, netbasemodel.params(i).name, netbasemodel.params(i).learningRate);
    fprintf('\tsize: %dx%dx%dx%d\n', size(netbasemodel.params(i).value,1), size(netbasemodel.params(i).value,2), size(netbasemodel.params(i).value,3), size(netbasemodel.params(i).value,4));
end
%% configure training environment
batchSize = 1;
totalEpoch = 100;
learningRate = 1:totalEpoch;
learningRate = (1.0e-4) * (1-learningRate/totalEpoch).^0.9;

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

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

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

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

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

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

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

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

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

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

%% leaving blank
%{
%}


function [rec,prec,ap] = VOCevallayout_pr(VOCopts,id,draw)

% load test set
[imgids,objids]=textread(sprintf(VOCopts.layout.imgsetpath,VOCopts.testset),'%s %d');

% hash image ids
hash=VOChash_init(imgids);

% load ground truth objects

tic;
n=0;
np=zeros(VOCopts.nparts,1);
for i=1:length(imgids)
    % display progress
    if toc>1
        fprintf('layout pr: load %d/%d\n',i,length(imgids));
        drawnow;
        tic;
    end

    % read annotation
    r=PASreadrecord(sprintf(VOCopts.annopath,imgids{i}));

    % extract object
    n=n+1;
    o=r.objects(objids(i));
    gt(n)=o;

    for j=1:numel(o.part)
        c=strmatch(o.part(j).class,VOCopts.parts,'exact');
        np(c)=np(c)+1;
    end
end

% load results

fprintf('layout pr: loading results\n');
xml=VOCreadxml(sprintf(VOCopts.layout.respath,id));

% test detections by decreasing confidence

[t,si]=sort(-str2double({xml.results.layout.confidence}));
nd=numel(si);

det=false(n,1);

ptp=[];
pfp=[];
pc=[];

for di=1:nd

    % display progress
    if toc>1
        fprintf('layout pr: compute: %d/%d\n',di,nd);
        drawnow;
        tic;
    end

    % match result to ground truth object
    d=xml.results.layout(si(di));
    ii=VOChash_lookup(hash,d.image);
    oi=ii(objids(ii)==str2num(d.object));

    if isempty(oi)
        warning('unrecognized layout: image %s, object %s',d.image,d.object);
        continue
    end

    if det(oi)
        warning('duplicate layout: image %s, object %s',d.image,d.object);
        continue
    end
    det(oi)=true;
    o=gt(oi);

    % assign parts to ground truth parts

    gtd=false(numel(o.part),1);
    da=zeros(numel(d.part),1);
    dc=zeros(numel(d.part),1);
    for i=1:numel(d.part)
        dc(i)=strmatch(d.part(i).class,VOCopts.parts,'exact');
        bb=str2double({d.part(i).bndbox.xmin d.part(i).bndbox.ymin ...
                       d.part(i).bndbox.xmax d.part(i).bndbox.ymax});

        ovmax=-inf;
        for j=1:numel(o.part)
            if strcmp(d.part(i).class,o.part(j).class)
                bbgt=o.part(j).bbox;
                bi=[max(bb(1),bbgt(1))
                    max(bb(2),bbgt(2))
                    min(bb(3),bbgt(3))
                    min(bb(4),bbgt(4))];
                iw=bi(3)-bi(1)+1;
                ih=bi(4)-bi(2)+1;
                if iw>0 & ih>0
                    % compute overlap as area of intersection / area of union
                    ua=(bb(3)-bb(1)+1)*(bb(4)-bb(2)+1)+...
                       (bbgt(3)-bbgt(1)+1)*(bbgt(4)-bbgt(2)+1)-...
                       iw*ih;
                    ov=iw*ih/ua;
                    if ov>ovmax
                        ovmax=ov;
                        jmax=j;
                    end
                end
            end
        end
        if ovmax>=VOCopts.minoverlap && ~gtd(jmax)
            da(i)=jmax;
            gtd(jmax)=true;
        end
    end

    ptp=[ptp ; da~=0];
    pfp=[pfp ; da==0];
    pc=[pc ; dc];
end

% evaluate each part type

for i=1:VOCopts.nparts

    % compute precision/recall

    fpi=cumsum(pfp(pc==i));
    tpi=cumsum(ptp(pc==i));
    v=tpi+fpi>0;
    rec{i}=tpi(v)/np(i);
    prec{i}=tpi(v)./(fpi(v)+tpi(v));

    ap{i}=VOCap(rec{i},prec{i});

    if draw
        % plot precision/recall
        subplot(VOCopts.nparts,1,i);
        plot(rec{i},prec{i},'-');
        grid;
        xlabel 'recall'
        ylabel 'precision'
        title(sprintf('subset: %s, part: %s, AP = %.3f',VOCopts.testset,VOCopts.parts{i},ap{i}));
    end
end


function [rgb] = bg_rgba2rgb(bg, rgba, varargin)

% BG_RGBA2RGB overlays a transparency masked colored image on a colored background,
% and represents the result as an RGB matrix.
%
% Use as:
%   rgb = bg_rgba2rgb(bg, rgba)
%
% or
%   rgb = bg_rgba2rgb(bg, rgba, cmap, clim, alpha, amap, alim);
%
% When 2 input arguments are supplied:
%   bg   = Nx3 matrix of background rgb-coded color-values, or MxNx3
%   rgba = Nx4 matrix of rgb + alpha values, or MxNx4
%
% When 7 input arguments are supplied:
%   bg   = Nx3 matrix, Nx1 vector, 1x3 vector, MxN, or MxNx3.
%   rgba = Nx1 vector with 'functional values', or MxN.
%   cmap = Mx3 colormap, or MATLAB-supported name of colormap
%   clim = 1x2 vector denoting the color limits
%   alpha = Nx1 vector with 'alpha values', or MxN
%   amap = Mx1 alphamap, or MATLAB -supported name of alphamap ('rampup/down', 'vup/down')
%   alim = 1x2 vector denoting the opacity limits

if numel(varargin)==0
  siz1 = size(bg);
  siz2 = size(rgba);
  assert(isequal(siz1(1:end-1),siz2(1:end-1)), 'number of data points should be the same');
  assert(siz1(end)==3 && siz2(end)==4,         'inconsistent input size');

  bg   = reshape(bg,   [], 3);
  rgba = reshape(rgba, [], 4);
  rgb  = do_conversion(bg, rgba);
  rgb  = reshape(rgb, siz1);
else
  % this requires the data to be converted into rgb values irst, and
  % needs a cmap + clim, and an alpha, alphamap and alim
  if numel(varargin)~=5
    error('if a vector of color data is supplied, more input arguments are required');
  end

  % colormap handling
  cmap = varargin{1};
  if ischar(cmap)
    cmap = ft_colormap(cmap);
  elseif size(cmap,2)~=3
    error('invalid specification of colormap, should be nx3');
  end
  clim  = varargin{2}; if isempty(clim), clim(1) = min(rgba(:)); clim(2) = max(rgba(:)); end
  alpha = varargin{3};
  amap  = varargin{4};
  alim  = varargin{5}; if isempty(alim), alim(1) = min(alpha(:)); alim(2) = max(alpha(:)); end

  % deal with the color data
  dat     = rgba;
  siz     = size(dat);
  dat     = reshape(dat, [], 1);
  alpha   = reshape(alpha, [], 1); % assume to be same siza as input data!
  finvals = isfinite(dat);

  rgba              = zeros(size(dat,1),4);
  rgba(finvals,1:3) = dat2rgb(dat(finvals), cmap, clim);

  finvals          = isfinite(alpha);
  rgba(finvals,4)  = alpha2a(alpha(finvals), amap, alim);
  rgba(~finvals,4) = 0;

  % deal with the background: allow it to be 1x3, i.e. a single color per
  % pixel.
  if isequal(size(bg),[1 3])
    bg = permute(repmat(bg(:), [1 siz]), [1+(1:numel(siz)) 1]);
  else
    siz_bg = size(bg);
    if isequal(siz_bg, siz)
      % make bg rgb
      if all(bg(:)<=1) && all(bg(:)>=0)
        % don't scale
      else
        bg_min = min(bg(:));
        bg_max = max(bg(:));
        bg     = (bg-bg_min)./(bg_max-bg_min);
      end
      bg = repmat(bg, [ones(1,ndims(bg)) 3]);
    else
      %FIXME
    end
  end
  bg  = reshape(bg, [], 3);

  if numel(siz)==2 && siz(2)==1, siz = siz(1); end
  rgb = do_conversion(bg, rgba);
  rgb = reshape(rgb, [siz 3]);
end

function rgb = do_conversion(bg, rgba)

rgb = zeros(size(rgba,1),3);
a_  = 1-rgba(:,4);
a   = rgba(:,4);

for k = 1:3
    rgb(:,k) = bg(:,k).*a_  + rgba(:,k).*a;
end

function rgb = dat2rgb(dat, cmap, clim)

dat(end+1) = clim(1);
dat(end+1) = clim(2); % add the extremes to be sure that they are included

dat(dat<clim(1)) = clim(1);
dat(dat>clim(2)) = clim(2);

% scale between 0 and 1
dat = dat-min(dat);
dat = dat/max(dat);

ind = round(dat.*(size(cmap,1)-1))+1;
rgb = cmap(ind(1:end-2),:);

function a = alpha2a(alpha, amap, alim)

alpha(end+1) = alim(1);
alpha(end+1) = alim(2);

alpha(alpha<alim(1)) = alim(1);
alpha(alpha>alim(2)) = alim(2);

if ischar(amap)
    switch amap
        case 'rampup'
            a = alpha - min(alpha);
            a = a./max(a);
        case 'rampdown'
            a = alpha - min(alpha);
            a = a./max(a);
            a = 1-a;
        case 'vup'
            a = alpha - min(alpha);
            a = a./max(a);
            a = 1 - 2.*abs(a - 0.5);
        case 'vdown'
            a = alpha - min(alpha);
            a = a./max(a);
            a = 2.*abs(a - 0.5);
        otherwise
            error('unknown alphamap specified');
    end
else
    amap = amap(:);

    % assume it to be a vector that has M elements, scaled between 0 and 1
    a = alpha-min(alpha);
    a = a/max(a);
    ind = round(a.*(size(amap,1)-1))+1;
    a   = amap(ind(1:end),:);
end
a = a(1:end-2);


  function ob = Gbspline1(varargin)
%|function ob = Gbspline1(options)
%|
%| Construct Gbspline1 object for 1D B-spline interpolation
%|
%| See Gbspline1_test.m for example usage.
%|
%| in
%|
%| options
%|	'type'	char		'val2coef' convert signal values to coefficients
%|				'
%|				'coef2val' | 'synth' conv
%|	'mask'	size(image)	logical array of object support.
%|	'chat'		verbose printing of debug messages
%|	'psf'		point spread function (aka impulse response)
%|	'type'		type of bspline1:
%|				'conv,same'	usual case (default)
%|				'fft,same'	(periodic end conditions)
%|				todo: allow replicated end conditions!
%|				'imfilter,same'	(requires image toolbox)
%|				'imfilter,circ'	(requires image toolbox)
%|	'imfilter_options'	options to 'imfilter' (if used)
%|
%| out
%|	ob [nd np]	np = sum(mask(:)), so it is already "masked"
%|			nd = prod(size(mask)) for 'conv,same' type
%|
%| Copyright 2011-11-13, Jeff Fessler, University of Michigan

fail 'not done'

if nargin == 1 && streq(mask, 'test'), Gbspline1_test, return, end
if nargin < 1, ir_usage, end

% option defaults
arg.type = 'todo';
arg.mask = mask;
arg.chat = 0;

% options specified by name/value pairs
arg = vararg_pair(arg, varargin);

does_many = false;
switch arg.type
case 'todo'
	arg.odim =
	handle_forw = @(arg,x)
	handle_back = @(arg,y)
	does_many = true;

otherwise
	error 'unknown bspline1 type'
end

ob = fatrix2('idim', arg.odim, 'arg', arg, 'does_many', does_many, ...
	'forw', handle_forw, 'back', handle_back);


%TRAIN_MODEL Summary of this function goes here
%   Function: train face alignment model
%   Detailed explanation goes here
%   Input:
%       dbnames: the names of database
% Configure the parameters for training model
global params;
global Tr_Data;
config_tr;

if size(dbnames) > 1 & sum(strcmp(dbnames, 'COFW')) > 0
    disp('Sorry, COFW cannnot be combined with others')
    return;
end

if sum(strcmp(dbnames, 'COFW')) > 0
    load('../initial_shape/InitialShape_29.mat');
    params.meanshape        = S0;
else
    load('../initial_shape/InitialShape_68.mat');
    params.meanshape        = S0;
end

if params.isparallel
    disp('Attention, if error occurs, plese ensure you used the correct version of parallel initialization.');

    if isempty(gcp('nocreate'))
        parpool(4);
    else
        disp('Already initialized');
    end

    %{
    if matlabpool('size') <= 0
        matlabpool('open','local',4);
    else
        disp('Already initialized');
    end
    %}
end

% load trainning data from hardware
Tr_Data    = [];
% Tr_Bboxes  = [];
for i = 1:length(dbnames)
    % load training samples (including training images, and groundtruth shapes)
    imgpathlistfile = strcat('..\datasets\', dbnames{i}, '\Path_Images.txt');
    tr_data = loadsamples(imgpathlistfile, 2);
    Tr_Data = [Tr_Data; tr_data];
end

% Augmentate data for traing: assign multiple initial shapes to each image
Data = Tr_Data; % (1:10:end);
Param = params;

if Param.flipflag % if conduct flipping
    Data_flip = cell(size(Data, 1), 1);
    for i = 1:length(Data_flip)
        Data_flip{i}.img_gray    = fliplr(Data{i}.img_gray);
        Data_flip{i}.width_orig  = Data{i}.width_orig;
        Data_flip{i}.height_orig = Data{i}.height_orig;
        Data_flip{i}.width       = Data{i}.width;
        Data_flip{i}.height      = Data{i}.height;

        Data_flip{i}.shape_gt    = flipshape(Data{i}.shape_gt);
        Data_flip{i}.shape_gt(:, 1)  =  Data{i}.width - Data_flip{i}.shape_gt(:, 1);

        Data_flip{i}.bbox_gt        = Data{i}.bbox_gt;
        Data_flip{i}.bbox_gt(1)     = Data_flip{i}.width - Data_flip{i}.bbox_gt(1) - Data_flip{i}.bbox_gt(3);

        Data_flip{i}.bbox_facedet        = Data{i}.bbox_facedet;
        Data_flip{i}.bbox_facedet(1)     = Data_flip{i}.width - Data_flip{i}.bbox_facedet(1) - Data_flip{i}.bbox_facedet(3);
    end
    Data = [Data; Data_flip];
end

% choose corresponding points for training
for i = 1:length(Data)
    Data{i}.shape_gt = Data{i}.shape_gt(Param.ind_usedpts, :);
    Data{i}.bbox_gt = getbbox(Data{i}.shape_gt);

    % modify detection boxes
    shape_facedet = resetshape(Data{i}.bbox_facedet, Param.meanshape);
    shape_facedet = shape_facedet(Param.ind_usedpts, :);
    Data{i}.bbox_facedet = getbbox(shape_facedet);
end

Param.meanshape        = S0(Param.ind_usedpts, :);

dbsize = length(Data);

% load('Ts_bbox.mat');

augnumber = Param.augnumber;

for i = 1:dbsize
    % initializ the shape of current face image by randomly selecting multiple shapes from other face images
    % indice = ceil(dbsize*rand(1, augnumber));

    indice_rotate = ceil(dbsize*rand(1, augnumber));
    indice_shift  = ceil(dbsize*rand(1, augnumber));
    scales        = 1 + 0.2*(rand([1 augnumber]) - 0.5);

    Data{i}.intermediate_shapes = cell(1, Param.max_numstage);
    Data{i}.intermediate_bboxes = cell(1, Param.max_numstage);

    Data{i}.intermediate_shapes{1} = zeros([size(Param.meanshape), augnumber]);
    Data{i}.intermediate_bboxes{1} = zeros([augnumber, size(Data{i}.bbox_gt, 2)]);

    Data{i}.shapes_residual = zeros([size(Param.meanshape), augnumber]);
    Data{i}.tf2meanshape = cell(augnumber, 1);
    Data{i}.meanshape2tf = cell(augnumber, 1);

    % if Data{i}.isdet == 1
    %    Data{i}.bbox_facedet = Data{i}.bbox_facedet*ts_bbox;
    % end
    for sr = 1:params.augnumber
        if sr == 1
            % estimate the similarity transformation from initial shape to mean shape
            % Data{i}.intermediate_shapes{1}(:,:, sr) = resetshape(Data{i}.bbox_gt, Param.meanshape);
            % Data{i}.intermediate_bboxes{1}(sr, :) = Data{i}.bbox_gt;
            Data{i}.intermediate_shapes{1}(:,:, sr) = resetshape(Data{i}.bbox_facedet, Param.meanshape);
            Data{i}.intermediate_bboxes{1}(sr, :) = Data{i}.bbox_facedet;

            meanshape_resize = resetshape(Data{i}.intermediate_bboxes{1}(sr, :), Param.meanshape);

            Data{i}.tf2meanshape{1} = fitgeotrans(bsxfun(@minus, Data{i}.intermediate_shapes{1}(1:end,:, 1), mean(Data{i}.intermediate_shapes{1}(1:end,:, 1))), ...
                (bsxfun(@minus, meanshape_resize(1:end, :), mean(meanshape_resize(1:end, :)))), 'NonreflectiveSimilarity');
            Data{i}.meanshape2tf{1} = fitgeotrans((bsxfun(@minus, meanshape_resize(1:end, :), mean(meanshape_resize(1:end, :)))), ...
                bsxfun(@minus, Data{i}.intermediate_shapes{1}(1:end,:, 1), mean(Data{i}.intermediate_shapes{1}(1:end,:, 1))), 'NonreflectiveSimilarity');

            % calculate the residual shape from initial shape to groundtruth shape under normalization scale
            shape_residual = bsxfun(@rdivide, Data{i}.shape_gt - Data{i}.intermediate_shapes{1}(:,:, 1), [Data{i}.intermediate_bboxes{1}(1, 3) Data{i}.intermediate_bboxes{1}(1, 4)]);
            % transform the shape residual in the image coordinate to the mean shape coordinate
            [u, v] = transformPointsForward(Data{i}.tf2meanshape{1}, shape_residual(:, 1)', shape_residual(:, 2)');
            Data{i}.shapes_residual(:, 1, 1) = u';
            Data{i}.shapes_residual(:, 2, 1) = v';
        else
            % randomly rotate the shape
            % shape = resetshape(Data{i}.bbox_gt, Param.meanshape);       % Data{indice_rotate(sr)}.shape_gt
            shape = resetshape(Data{i}.bbox_facedet, Param.meanshape);       % Data{indice_rotate(sr)}.shape_gt

            if params.augnumber_scale ~= 0
                shape = scaleshape(shape, scales(sr));
            end

            if params.augnumber_rotate ~= 0
                shape = rotateshape(shape);
            end

            if params.augnumber_shift ~= 0
                shape = translateshape(shape, Data{indice_shift(sr)}.shape_gt);
            end

            Data{i}.intermediate_shapes{1}(:, :, sr) = shape;
            Data{i}.intermediate_bboxes{1}(sr, :) = getbbox(shape);

            meanshape_resize = resetshape(Data{i}.intermediate_bboxes{1}(sr, :), Param.meanshape);

            Data{i}.tf2meanshape{sr} = fitgeotrans(bsxfun(@minus, Data{i}.intermediate_shapes{1}(1:end,:, sr), mean(Data{i}.intermediate_shapes{1}(1:end,:, sr))), ...
                bsxfun(@minus, meanshape_resize(1:end, :), mean(meanshape_resize(1:end, :))), 'NonreflectiveSimilarity');
            Data{i}.meanshape2tf{sr} = fitgeotrans(bsxfun(@minus, meanshape_resize(1:end, :), mean(meanshape_resize(1:end, :))), ...
                bsxfun(@minus, Data{i}.intermediate_shapes{1}(1:end,:, sr), mean(Data{i}.intermediate_shapes{1}(1:end,:, sr))), 'NonreflectiveSimilarity');

            shape_residual = bsxfun(@rdivide, Data{i}.shape_gt - Data{i}.intermediate_shapes{1}(:,:, sr), [Data{i}.intermediate_bboxes{1}(sr, 3) Data{i}.intermediate_bboxes{1}(sr, 4)]);
            [u, v] = transformPointsForward(Data{i}.tf2meanshape{1}, shape_residual(:, 1)', shape_residual(:, 2)');
            Data{i}.shapes_residual(:, 1, sr) = u';
            Data{i}.shapes_residual(:, 2, sr) = v';
            % Data{i}.shapes_residual(:, :, sr) = tformfwd(Data{i}.tf2meanshape{sr}, shape_residual(:, 1), shape_residual(:, 2));
        end
    end
end

% train random forests for each landmark
randf = cell(Param.max_numstage, 1);
Ws    = cell(Param.max_numstage, 1);

%{
if nargin > 2
    n = size(LBFRegModel_initial.ranf, 1);
    for i = 1:n
      randf(1:n, :) = LBFRegModel_initial.ranf;
    end
end
%}

for s = 1:Param.max_numstage
    % learn random forest for s-th stage
    disp('train random forests for landmarks...');

    %{
    if isempty(randf{s})
        if exist(strcat('randfs\randf', num2str(s), '.mat'))
            load(strcat('randfs\randf', num2str(s), '.mat'));
        else
            tic;
            randf{s} = train_randomfs(Data, Param, s);
            toc;
            save(strcat('randfs\randf', num2str(s), '.mat'), 'randf', '-v7.3');
        end
    end
    %}
    if isempty(randf{s})
        tic;
        randf{s} = train_randomfs(Data, Param, s);
        toc;
    end

    % derive binary codes given learned random forest in current stage
    disp('extract local binary features...');

    tic;
    binfeatures = derivebinaryfeat(randf{s}, Data, Param, s);
    % save(strcat('LBFeats\LBFeats', num2str(s), '.mat'), 'binfeatures', '-v7.3');
    toc;

    % learn global linear regrassion given binary feature
    disp('learn global regressors...');
    tic;
    [W, Data] = globalregression(binfeatures, Data, Param, s);
    Ws{s} = W;
    % save(strcat('Ws\W', num2str(s), '.mat'), 'W', '-v7.3');
    toc;

end

LBFRegModel.ranf = randf;
LBFRegModel.Ws   = Ws;


function x0 = secondary_source_tapering(x0,conf)
%SECONDARY_SOURCE_TAPERING applies a tapering window to the secondary sources
%
%   Usage: x0 = secondary_source_tapering(x0,conf)
%
%   Input parameters:
%       x0          - secondary sources / m
%       conf        - configuration struct (see SFS_config)
%
%   Output parameters:
%       x0          - secondary sources / m, containing the applied tapering
%                     window in its weights in x0(:,7)
%
%   SECONDARY_SOURCE_TAPERING(x0,conf) applies a tapering window (depending on
%   the conf.usetapwin and conf.tapwinlen settings) to the secondary sources.
%   It is applied to the weights stored in x0(:,7).
%
%   See also: secondary_source_positions, tapering_window

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

%% ===== Checking of input  parameters ==================================
nargmin = 2;
nargmax = 2;
narginchk(nargmin,nargmax);

%% ===== Calculation ====================================================
% Apply tapering window to secondary sources
x0(:,7) = x0(:,7) .* tapering_window(x0,conf);


classdef meshgen
    %   MESHGEN: Mesh generation class
    %   Handles input parameters to create a meshgen class object that can be
    %   used to build a msh class.
    %   Copyright (C) 2018  Keith Roberts & William Pringle
    %
    %   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/>.
    %
    %   Available options:
    %         ef            % edgefx class
    %         bou           % geodata class
    %         h0            % minimum edge length (optional if bou exists)
    %         bbox          % bounding box [xmin,ymin; xmax,ymax] (manual specification, no bou)
    %         proj          % structure containing the m_map projection info
    %         plot_on       % flag to plot (def: 1) or not (0)
    %         nscreen       % how many it to plot and write temp files (def: 5)
    %         itmax         % maximum number of iterations.
    %         pfix          % fixed node positions (nfix x 2 )
    %         egfix         % edge constraints
    %         outer         % meshing boundary (manual specification, no bou)
    %         inner         % island boundaries (manual specification, no bou)
    %         mainland      % the shoreline boundary (manual specification, no bou)
    %         fixboxes      % a flag that indicates which boxes will use fixed constraints
    %         memory_gb     % memory in GB allowed to use for initial rejector
    %         cleanup       % logical flag or string to trigger cleaning of topology (default is on).
    %         direc_smooth  % logical flag to trigger direct smoothing of mesh in the cleanup
    %         dj_cutoff     % the cutoff area fraction for disjoint portions to delete
    %         qual_tol      % tolerance for the accepted negligible change in quality
    %         enforceWeirs  % whether or not to enforce weirs in meshgen
    %         enforceMin    % whether or not to enfore minimum edgelength for all edgefxs
    % delaunay_elim_on_exit % whether or not to run delaunay_elim on exit of meshgen
    % improve_with_reduced_quality % whether or not to allow mesh improvements with decreases in mesh quality
    %         big_mesh      % set to 1 to remove the bou data from memory
    %
    properties
        fd            % handle to distance function
        fh            % handle to edge function
        h0            % minimum edge length
        edgefx        % edgefx class
        bbox          % bounding box [xmin,ymin; xmax,ymax]
        pfix          % fixed node positions (nfix x 2 )
        egfix         % edge constraints
        fixboxes      % a flag that indicates which boxes will use fixed constraints
        plot_on       % flag to plot (def: 1) or not (0)
        nscreen       % how many it to plot and write temp files (def: 5)
        bou           % geodata class
        ef            % edgefx class
        itmax         % maximum number of iterations.
        outer         % meshing boundary (manual specification, no bou)
        inner         % island boundaries (manual specification, no bou)
        mainland      % the shoreline boundary (manual specification, no bou)
        boubox        % the bbox as a polygon 2-tuple
        inpoly_flip   % used to flip the inpoly test to determine the signed distance.
        memory_gb     % memory in GB allowed to use for initial rejector
        cleanup       % logical flag or string to trigger cleaning of topology (default is on).
        direc_smooth  % logical flag to trigger direct smoothing of mesh in the cleanup
        dj_cutoff     % the cutoff area fraction for disjoint portions to delete
        grd = msh();  % create empty mesh class to return p and t in.
        big_mesh      % release bou data from memory
        qual          % mean, lower 3rd sigma, and the minimum element quality.
        qual_tol      % tolerance for the accepted negligible change in quality
        proj          % structure containing the m_map projection info
        anno          % Approx. Nearest Neighbor search object.
        annData       % datat contained with KD-tree in anno
        Fb            % bathymetry data interpolant
        enforceWeirs  % whether or not to enforce weirs in meshgen
        enforceMin    % whether or not to enfore minimum edgelength for all edgefxs
        delaunay_elim_on_exit % whether or not to run delaunay_elim on exit of meshgen
        improve_with_reduced_quality % whether or not to allow mesh improvements with decreases in mesh quality
    end

    methods

        % class constructor/default grd generation options
        function obj = meshgen(varargin)
            % Check for m_map dir
            M_MAP_EXISTS=0;
            if exist('m_proj','file')==2
              M_MAP_EXISTS=1 ;
            end
            if M_MAP_EXISTS~=1
              error('Where''s m_map? Chief, you need to read the user guide')
            end

            % Check for utilties dir
            UTIL_DIR_EXISTS=0 ;
            if exist('inpoly.m','file')
              UTIL_DIR_EXISTS=1;
            end
            if UTIL_DIR_EXISTS~=1
              error('Where''s the utilities directory? Chief, you need to read the user guide')
            end

            p = inputParser;
            % unpack options and set default ones, catch errors.

            defval = 0; % placeholder value if arg is not passed.
            % add name/value pairs
            addOptional(p,'h0',defval);
            addOptional(p,'bbox',defval);
            addOptional(p,'fh',defval);
            addOptional(p,'pfix',defval);
            addOptional(p,'egfix',defval);
            addOptional(p,'fixboxes',defval);
            addOptional(p,'inner',defval);
            addOptional(p,'outer',defval);
            addOptional(p,'mainland',defval);
            addOptional(p,'bou',defval);
            addOptional(p,'ef',defval);
            addOptional(p,'plot_on',defval);
            addOptional(p,'nscreen',defval);
            addOptional(p,'itmax',defval);
            addOptional(p,'memory_gb',1);
            addOptional(p,'cleanup',1);
            addOptional(p,'direc_smooth',1);
            addOptional(p,'dj_cutoff',0.25);
            addOptional(p,'big_mesh',defval);
            addOptional(p,'proj',defval);
            addOptional(p,'qual_tol',defval);
            addOptional(p,'enforceWeirs',1);
            addOptional(p,'enforceMin',1);
            addOptional(p,'delaunay_elim_on_exit',1);
            addOptional(p,'improve_with_reduced_quality',0);

            % parse the inputs
            parse(p,varargin{:});

            %if isempty(varargin); return; end
            % store the inputs as a struct
            inp = p.Results;

            % kjr...order these argument so they are processed in a predictable
            % manner. Process the general opts first, then the OceanMesh
            % classes...then basic non-critical options.
            inp = orderfields(inp,{'h0','bbox','enforceWeirs','enforceMin',...
                                   'delaunay_elim_on_exit','improve_with_reduced_quality',...
                                   'fh',...
                                   'inner','outer','mainland',...
                                   'bou','ef',... %<--OceanMesh classes come after
                                   'egfix','pfix','fixboxes',...
                                   'plot_on','nscreen','itmax',...
                                   'memory_gb','qual_tol','cleanup',...
                                   'direc_smooth','dj_cutoff',...
                                   'big_mesh','proj'});
            % get the fieldnames of the edge functions
            fields = fieldnames(inp);
            % loop through and determine which args were passed.
            % also, assign reasonable default values if some options were
            % not assigned.
            for i = 1 : numel(fields)
                type = fields{i};
                switch type
                    % parse aux options first
                    case('h0')
                        % Provide in meters
                        obj.h0 = inp.(fields{i});
                    case('fh')
                        if isa(inp.(fields{i}),'function_handle')
                            obj.fh = inp.(fields{i});
                        end
                        % can't check for errors here yet.
                    case('bbox')
                        obj.bbox= inp.(fields{i});
                        if iscell(obj.bbox)
                            % checking bbox extents
                            ob_min = obj.bbox{1}(:,1);
                            ob_max = obj.bbox{1}(:,2);
                            for ii = 2:length(obj.bbox)
                                if any(obj.bbox{ii}(:,1) < ob_min) || ...
                                        any(obj.bbox{ii}(:,2) > ob_max)
                                    error(['Outer bbox must contain all ' ...
                                        'inner bboxes: inner box #' ...
                                        num2str(ii) ' violates this'])
                                end
                            end
                        end

                        % if user didn't pass anything explicitly for
                        % bounding box make it empty so it can be populated
                        % from ef as a cell-array
                        if obj.bbox(1)==0
                            obj.bbox = [];
                        end
                    case('pfix')
                        obj.pfix= inp.(fields{i});
                        if obj.pfix(1)~=0
                            obj.pfix(:,:) = inp.(fields{i});
                        else
                            obj.pfix = [];
                        end
                        if obj.enforceWeirs
                            for j = 1 : length(obj.bou)
                                if  ~isempty(obj.bou{j}.weirPfix)
                                    obj.pfix = [obj.pfix ; obj.bou{j}.weirPfix];
                                end
                            end
                        end
                    case('egfix')
                        obj.egfix= inp.(fields{i});
                        if ~isempty(obj.egfix) && obj.egfix(1)~=0
                            obj.egfix = inp.(fields{i});
                        else
                            obj.egfix = [];
                        end
                        if obj.enforceWeirs
                            for j = 1 : length(obj.bou)
                                if ~isempty(obj.bou{j}.weirEgfix) && ~isempty(obj.egfix)
                                    obj.egfix = [obj.egfix ; obj.bou{j}.weirEgfix+max(obj.egfix(:))];
                                else
                                    obj.egfix =  obj.bou{j}.weirEgfix;
                                end
                            end
                        end
                        obj.egfix = renumberEdges(obj.egfix);
                    case('fixboxes')
                        obj.fixboxes= inp.(fields{i});
                    case('bou')
                        % got it from user arg
                        if obj.outer~=0, continue; end

                        obj.outer = {} ;
                        obj.inner = {} ;
                        obj.mainland = {} ;

                        obj.bou = inp.(fields{i});

                        % handle when not a cell
                        if ~iscell(obj.bou)
                            boutemp = obj.bou;
                            obj.bou = cell(1);
                            obj.bou{1} = boutemp;
                        end

                        % then the geodata class was provide, unpack
                        for ee = 1:length(obj.bou)
                            try
                                arg = obj.bou{ee} ;
                            catch
                                arg = obj.bou;
                            end
                            if isa(arg,'geodata')
                                obj.outer{ee} = obj.bou{ee}.outer;
                                obj.inner{ee} = obj.bou{ee}.inner;

                                % save bathy interpolant to meshgen
                                if ~isempty(obj.bou{ee}.Fb)
                                  obj.Fb{ee} = obj.bou{ee}.Fb ;
                                end

                                if ~isempty(obj.inner{ee}) && ...
                                   obj.inner{ee}(1)~= 0
                                    obj.outer{ee} = [obj.outer{ee};
                                        obj.inner{ee}];
                                end
                                obj.mainland{ee} = obj.bou{ee}.mainland;
                                obj.boubox{ee} = obj.bou{ee}.boubox;
                                obj.inpoly_flip{ee} = obj.bou{ee}.inpoly_flip;
                                if obj.big_mesh
                                    % release gdat's
                                    obj.bou{ee}.mainland= [];
                                    obj.bou{ee}.outer= [];
                                    if ~isempty(obj.bou{ee}.inner)
                                        obj.bou{ee}.inner= [];
                                    end
                                end
                            end
                        end

                    case('ef')
                        tmp = inp.(fields{i});
                        if isa(tmp, 'function_handle')
                            error('Please specify your edge function handle through the name/value pair fh');
                        end
                        obj.ef = tmp;

                        % handle when not a cell
                        if ~iscell(obj.ef)
                            eftemp = obj.ef;
                            obj.ef = cell(1);
                            obj.ef{1} = eftemp;
                        end

                        % Gather boxes from ef class.
                        for ee = 1 : length(obj.ef)
                            if isa(obj.ef{ee},'edgefx')
                                obj.bbox{ee} = obj.ef{ee}.bbox;
                            end
                        end

                         % checking bbox extents
                        if iscell(obj.bbox)
                            ob_min = obj.bbox{1}(:,1);
                            ob_max = obj.bbox{1}(:,2);
                            for ii = 2:length(obj.bbox)
                                if any(obj.bbox{ii}(:,1) < ob_min) || ...
                                        any(obj.bbox{ii}(:,2) > ob_max)
                                    error(['Outer bbox must contain all ' ...
                                        'inner bboxes: inner box #' ...
                                        num2str(ii) ' violates this'])
                                end
                            end
                        end

                        % kjr 2018 June: get h0 from edge functions
                        for ee = 1:length(obj.ef)
                            if isa(obj.ef{ee},'edgefx')
                                obj.h0(ee) = obj.ef{ee}.h0;
                            end
                        end

                        % kjr 2018 smooth the outer automatically
                        if length(obj.ef) > 1
                            % kjr 2020, ensure the min. sizing func is
                            % used
                            if obj.enforceMin
                                obj.ef = enforce_min_ef(obj.ef);
                            end
                            obj.ef = smooth_outer(obj.ef,obj.Fb);
                        end

                        % Save the ef interpolants into the edgefx
                        for ee = 1:length(obj.ef)
                            if isa(obj.ef{ee},'edgefx')
                                obj.fh{ee} = @(p)obj.ef{ee}.F(p);
                            end
                        end

                    case('plot_on')
                        obj.plot_on= inp.(fields{i});
                    case('big_mesh')
                        obj.big_mesh = inp.(fields{i});
                    case('nscreen')
                        obj.nscreen= inp.(fields{i});
                        if obj.nscreen ~=0
                            obj.nscreen = inp.(fields{i});
                            obj.plot_on = 1;
                        else
                            obj.nscreen = 5; % default
                        end
                    case('itmax')
                        obj.itmax= inp.(fields{i});
                        if obj.itmax ~=0
                            obj.itmax = inp.(fields{i});
                        else
                            obj.itmax = 100;
                            warning('No itmax specified, itmax set to 100');
                        end
                    case('qual_tol')
                        obj.qual_tol = inp.(fields{i});
                        if obj.qual_tol ~=0
                            obj.qual_tol = inp.(fields{i});
                        else
                            obj.qual_tol = 0.01;
                        end
                    case('inner')
                        if ~isa(obj.bou,'geodata')
                            obj.inner = inp.(fields{i});
                        end
                    case('outer')
                        if ~isa(obj.bou,'geodata')
                            obj.outer = inp.(fields{i});
                            if obj.inner(1)~=0
                                obj.outer = [obj.outer; obj.inner];
                            end
                        end
                    case('mainland')
                        if ~isa(obj.bou,'geodata')
                            obj.mainland = inp.(fields{i});
                        end
                    case('memory_gb')
                        if ~isa(obj.bou,'memory_gb')
                            obj.memory_gb = inp.(fields{i});
                        end
                    case('cleanup')
                        obj.cleanup = inp.(fields{i});
                        if isempty(obj.cleanup) || obj.cleanup == 0
                            obj.cleanup = 'none';
                        elseif obj.cleanup == 1
                            obj.cleanup = 'default';
                        end
                    case('dj_cutoff')
                        obj.dj_cutoff = inp.(fields{i});
                    case('direc_smooth')
                        obj.direc_smooth = inp.(fields{i});
                    case('proj')
                        obj.proj = inp.(fields{i});
                        % default CPP
                        if obj.proj == 0; obj.proj = 'equi'; end
                        if ~isempty(obj.bbox)
                            % kjr Oct 2018 use outer coarsest box for
                            % multiscale meshing
                            lon_mi = obj.bbox{1}(1,1)-obj.h0(1)/1110;
                            lon_ma = obj.bbox{1}(1,2)+obj.h0(1)/1110;
                            lat_mi = obj.bbox{1}(2,1)-obj.h0(1)/1110;
                            lat_ma = obj.bbox{1}(2,2)+obj.h0(1)/1110;
                        else
                            lon_mi = -180; lon_ma = 180;
                            lat_mi = -90; lat_ma = 90;
                        end
                        % Set up projected space
                        dmy = msh() ;
                        dmy.p(:,1) = [lon_mi; lon_ma];
                        dmy.p(:,2) = [lat_mi; lat_ma];
                        del = setProj(dmy,1,obj.proj) ;
                    case('enforceWeirs')
                        obj.enforceWeirs = inp.(fields{i});
                    case('enforceMin')
                        obj.enforceMin = inp.(fields{i});
                    case('delaunay_elim_on_exit')
                        obj.delaunay_elim_on_exit = inp.(fields{i});
                    case('improve_with_reduced_quality')
                        obj.improve_with_reduced_quality = inp.(fields{i});
                end
            end

            if isempty(varargin); return; end

            % error checking
            if isempty(obj.boubox) && ~isempty(obj.bbox)
                % Make the bounding box 5 x 2 matrix in clockwise order if
                % it isn't present. This case must be when the user is
                % manually specifying the PSLG.
                obj.boubox{1} = [obj.bbox(1,1) obj.bbox(2,1);
                                 obj.bbox(1,1) obj.bbox(2,2); ...
                                 obj.bbox(1,2) obj.bbox(2,2);
                                 obj.bbox(1,2) obj.bbox(2,1); ...
                                 obj.bbox(1,1) obj.bbox(2,1); NaN NaN];
            end
            if any(obj.h0==0), error('h0 was not correctly specified!'), end
            if isempty(obj.outer), error('no outer boundary specified!'), end
            if isempty(obj.bbox), error('no bounding box specified!'), end
            obj.fd = @dpoly;  % <-default distance fx accepts p and pv (outer polygon).
            % kjr build ANN object into meshgen
            obj = createANN(obj) ;

            global MAP_PROJECTION MAP_COORDS MAP_VAR_LIST
            obj.grd.proj    = MAP_PROJECTION ;
            obj.grd.coord   = MAP_COORDS ;
            obj.grd.mapvar  = MAP_VAR_LIST ;

        end

        % Creates Approximate nearest neighbor objects on start-up
        function  obj = createANN(obj)

            box_vec = 1:length(obj.bbox);

            for box_num = box_vec
                if ~iscell(obj.outer)
                    dataset = obj.outer;
                    dataset(isnan(obj.outer(:,1)),:) = [];
                else
                    dataset = obj.outer{box_num};
                    dataset(isnan(obj.outer{box_num}(:,1)),:) = [];
                end
                if all(abs(obj.bbox{box_num}(1,:)) == 180)
                    % This line removes the line that can appear in the
                    % center for a global mesh
                    dataset(abs(dataset(:,1)) > 180-1e-6,:) = [];
                    dataset(abs(dataset(:,1)) < 1e-6,:) = [];
                end
                [dataset(:,1),dataset(:,2)] = m_ll2xy(dataset(:,1),dataset(:,2));
                dataset(isnan(dataset(:,1)),:) = [];
                dmy = ann(dataset');
                obj.anno{box_num} = dmy;
                obj.annData{box_num}=dataset;
            end
        end

        function  obj = build(obj)
            %DISTMESH2D 2-D Mesh Generator using Distance Functions.
            % Checking existence of major inputs
            %%
            warning('off','all')
            %%
            tic
            it = 1 ;
            Re = 6378.137e3;
            geps = 1e-3*min(obj.h0)/Re;
            deps = sqrt(eps);
            ttol=0.1; Fscale = 1.2; deltat = 0.1;
            delIT = 0 ; delImp = 2;
            imp = 10; % number of iterations to do mesh improvements (delete/add)

            % unpack initial points.
            p = obj.grd.p;
            if isempty(p)
                disp('Forming initial point distribution...');
                % loop over number of boxes
                for box_num = 1:length(obj.h0)
                    disp(['    for box #' num2str(box_num)]);
                    % checking if cell or not and applying local values
                    h0_l = obj.h0(box_num);
                    max_r0 = 1/h0_l^2;
                    if ~iscell(obj.bbox)
                        bbox_l = obj.bbox'; % <--we must tranpose this!
                    else
                        bbox_l = obj.bbox{box_num}'; % <--tranpose!
                    end
                    if ~iscell(obj.fh)
                        fh_l = obj.fh;
                    else
                        fh_l = obj.fh{box_num};
                    end
                    % Lets estimate the num_points the distribution will be
                    num_points = ceil(2/sqrt(3)*prod(abs(diff(bbox_l)))...
                                      /(h0_l/111e3)^2);
                    noblks = ceil(num_points*2*8/obj.memory_gb*1e-9);
                    len = abs(bbox_l(1,1)-bbox_l(2,1));
                    blklen = len/noblks;
                    st = bbox_l(1,1) ; ed = st + blklen; ns = 1;
                    %% 1. Create initial distribution in bounding box
                    %% (equilateral triangles)
                    for blk = 1 : noblks
                        if blk == noblks
                            ed = bbox_l(2,1);
                        end
                        ys = bbox_l(1,2);
                        ny = floor(1e3*m_lldist(repmat(0.5*(st+ed),2,1),...
                                   [ys;bbox_l(2,2)])/h0_l);
                        dy = diff(bbox_l(:,2))/ny;
                        ns = 1;
                        % start at lower left and make grid going up to
                        % north latitude
                        for ii = 1:ny
                            if st*ed < 0
                                nx = floor(1e3*m_lldist([st;0],...
                                     [ys;ys])/(2/sqrt(3)*h0_l)) + ...
                                     floor(1e3*m_lldist([0;ed],...
                                     [ys;ys])/(2/sqrt(3)*h0_l));
                            else
                                nx = floor(1e3*m_lldist([st;ed],...
                                     [ys;ys])/(2/sqrt(3)*h0_l));
                            end
                            ne = ns+nx-1;
                            if mod(ii,2) == 0
                                % no offset
                                x(ns:ne) = linspace(st,ed,nx);
                            else
                                % offset
                                dx = (ed-st)/nx;
                                x(ns:ne) = linspace(st+0.5*dx,ed,nx);
                            end
                            y(ns:ne) = ys;
                            ns = ne+1; ys = ys + dy;
                        end
                        st = ed;
                        ed = st + blklen;
                        p1 = [x(:) y(:)]; clear x y

                        %% 2. Remove points outside the region, apply the rejection method
                        p1 = p1(feval(obj.fd,p1,obj,box_num) < geps,:);     % Keep only d<0 points
                        r0 = 1./feval(fh_l,p1).^2;                          % Probability to keep point
                        p1 = p1(rand(size(p1,1),1) < r0/max_r0,:);          % Rejection method
                        p  = [p; p1];                                       % Adding p1 to p
                    end
                    if box_num == 1
                        % add points along the outermost polygon to fill
                        % outer extent more quickly.
                        outer_temp = obj.outer{1};
                        Inan = find(isnan(outer_temp(:,1)),1,'first');
                        p1 = outer_temp(1:Inan-1,:);
                        p1 = p1(feval(obj.fd,p1,obj,box_num) < geps,:);     % Keep only d<0 points
                        r0 = 1./feval(fh_l, p1).^2;                         % Probability to keep point
                        p1 = p1(rand(size(p1,1),1) < r0/max_r0,:);          % Rejection method
                        p = [p; p1];                                        % Adding p1 to p
                    end
                end
            else
                disp('User-supplied initial points!');
                obj.grd.b = [];
                h0_l = obj.h0(end); % finest h0 (in case of a restart of meshgen.build).
            end

            % remove pfix/egfix outside of desired subdomain
            nfix = size(obj.pfix,1);    % Number of fixed points
            negfix = size(obj.egfix,1); % Number of edge constraints
            if negfix > 0
                if length(obj.fixboxes)==1 && obj.fixboxes(1)==0
                  obj.fixboxes(1)=1 ;
                end
                pfixkeep = setdiff([1:nfix]',unique(obj.egfix(:)));
                % remove bars if midpoint is outside domain
                egfix_mid = (obj.pfix(obj.egfix(:,1),:) + ...
                             obj.pfix(obj.egfix(:,2),:))/2;
                for jj = 1 : length(obj.fixboxes)
                    if obj.fixboxes(jj)
                        iboubox = obj.boubox{jj};
                        inbar(:,jj) = inpoly(egfix_mid,iboubox(1:end-1,:));
                    end
                end
                inbar = sum(inbar,2) ;
                obj.egfix(~inbar,:) = [];
                tmppfix = obj.pfix([unique(obj.egfix(:)); pfixkeep],:);
                obj.pfix = tmppfix;
                obj.egfix = renumberEdges(obj.egfix);
                negfix = size(obj.egfix,1); % Number of edge constraints.
            end
            if nfix > 0
                if length(obj.fixboxes)==1 && obj.fixboxes(1)==0
                  obj.fixboxes(1)=1 ;
                end
                % remove pfix if outside domain
                for jj = 1 : length(obj.fixboxes)
                    if obj.fixboxes(jj)
                        inbox(:,jj) = inpoly(obj.pfix,obj.boubox{jj}(1:end-1,:));
                    end
                end
                inbox = sum(inbox,2) ;
                inbox(unique(obj.egfix(:))) = 1;
                obj.pfix(~inbox,:) = [];
                nfix = size(obj.pfix,1);    % Number of fixed points
            end
            if nfix >= 0, disp(['Using ',num2str(nfix),' fixed points.']);end
            if negfix > 0
                if max(obj.egfix(:)) > length(obj.pfix)
                    error('FATAL: egfix does index correcty into pfix.');
                end
                disp(['Using ',num2str(negfix),' fixed edges.']);
            end

            if ~isempty(obj.pfix); p = [obj.pfix; p]; end
            N = size(p,1); % Number of points N
            disp(['Number of initial points after rejection is ',num2str(N)]);
            %% Iterate
            pold = inf;                                                    % For first iteration
            if obj.plot_on >= 1
                clf,view(2),axis equal;
            end
            toc
            fprintf(1,' ------------------------------------------------------->\n') ;
            disp('Begin iterating...');
            while 1
                tic
                if ~mod(it,obj.nscreen) && delIT == 0
                    disp(['Iteration =' num2str(it)]) ;
                end

                % 3. Retriangulation by the Delaunay algorithm
                if max(sqrt(sum((p(1:size(pold,1),:)-pold).^2,2))/h0_l*111e3) > ttol         % Any large movement?
                    p = fixmesh(p);                                        % Ensure only unique points.
                    N = size(p,1); pold = p;                               % Save current positions
                    [t,p] = delaunay_elim(p,obj.fd,geps,0);                % Delaunay with elimination

                    if isempty(t)
                      disp('Exiting')
                      return
                    end

                    % Getting element quality and check "goodness"
                    if exist('pt','var'); clear pt; end
                    [pt(:,1),pt(:,2)] = m_ll2xy(p(:,1),p(:,2));
                    tq = gettrimeshquan( pt, t);
                    mq_m = mean(tq.qm);
                    mq_l = min(tq.qm);
                    mq_s = std(tq.qm);
                    mq_l3sig = mq_m - 3*mq_s;
                    obj.qual(it,:) = [mq_m,mq_l3sig,mq_l];

                    % If mesh quality went down "significantly" since last iteration
                    % ..or..
                    % If not allowing improvements with reduction in quality
                    % then if the number of points significantly decreased
                    % due to a mesh improvement iteration, then rewind.
                    if ~mod(it,imp+1) && ((obj.qual(it,1) - obj.qual(it-1,1) < -0.10)  || ...
                        (~obj.improve_with_reduced_quality && ...
                        (N - length(p_before_improve))/length(p_before_improve) < -0.10))
                        disp('Mesh improvement was unsuccessful...rewinding...');
                        p = p_before_improve;
                        N = size(p,1);                                     % Number of points changed
                        pold = inf;
                        it = it + 1;
                        continue
                    else
                        N = size(p,1); pold = p;                           % Assign number of points and save current positions
                    end
                    % 4. Describe each bar by a unique pair of nodes.
                    bars = [t(:,[1,2]); t(:,[1,3]); t(:,[2,3])];           % Interior bars duplicated
                    bars = unique(sort(bars,2),'rows');                    % Bars as node pairs

                    % 5. Graphical output of the current mesh
                    if obj.plot_on >= 1 && (mod(it,obj.nscreen)==0 || it == 1)
                        cla,m_triplot(p(:,1),p(:,2),t)
                        m_grid
                        title(['Iteration = ',num2str(it)]);
                        if negfix > 0
                            m_plot(reshape(obj.pfix(obj.egfix,1),[],2)',...
                                 reshape(obj.pfix(obj.egfix,2),[],2)','r-')
                        end
                        if nfix > 0
                            m_plot(obj.pfix(:,1),obj.pfix(:,2),'b.')
                        end
                        plt = cell2mat(obj.boubox');
                        % reduce point spacing for asecthics
                        [plt2(:,2),plt2(:,1)] = my_interpm(plt(:,2),plt(:,1),0.1) ;
                        hold on ; axis manual
                        m_plot(plt2(:,1),plt2(:,2),'g','linewi',2)
                        drawnow
                    end
                end

                % Getting element quality and check goodness
                if exist('pt','var'); clear pt; end
                [pt(:,1),pt(:,2)] = m_ll2xy(p(:,1),p(:,2));
                tq = gettrimeshquan( pt, t);
                mq_m = mean(tq.qm);
                mq_l = min(tq.qm);
                mq_s = std(tq.qm);
                mq_l3sig = mq_m - 3*mq_s;
                obj.qual(it,:) = [mq_m,mq_l3sig,mq_l];

                % Improve the quality of triangles next to fixed edges by
                % deleting the point part of thin triangles without the fixed
                % point in it. Thin triangles have poor geometric quality <
                % 10%.
                if ~isempty(obj.egfix) && ~mod(it,delImp)
                    del = heal_fixed_edges(p,t,obj.egfix) ;
                    if ~isempty(del)
                        delIT = delIT + 1 ;
                        if delIT < 5
                            p(del,:)= [];
                            pold = inf;
                            disp(['Deleting ',num2str(length(del)),...
                                  ' points close to fixed edges']);
                            continue;
                        else
                            % Abandon strategy..if it will not terminate
                            disp('Moving to next iteration');
                        end
                    end
                    delIT = 0 ;
                 end

                % Termination quality, mesh quality reached is copacetic.
                qual_diff = mq_l3sig - obj.qual(max(1,it-imp),2);
                if ~mod(it,imp)
                    if abs(qual_diff) < obj.qual_tol
                        % Do the final elimination of small connectivity
                        if obj.delaunay_elim_on_exit
                            [t,p] = delaunay_elim(p,obj.fd,geps,1);
                        end
                        disp('Quality of mesh is good enough, exit')
                        close all;
                        break;
                    end
                end

                % Saving a temp mesh
                if ~mod(it,obj.nscreen) && delIT == 0
                    disp(['Number of nodes is ' num2str(length(p))])
                    disp(['Mean mesh quality is ' num2str(mq_m)])
                    disp(['Min mesh quality is ' num2str(mq_l)])
                    disp(['3rd sigma lower mesh quality is ' num2str(mq_l3sig)])
                    tempp = p; tempt = t;
                    save('Temp_grid.mat','it','tempp','tempt');
                    clearvars tempp tempt
                end

                % 6. Move mesh points based on bar lengths L and forces F
                barvec = pt(bars(:,1),:)- pt(bars(:,2),:);                 % List of bar vectors
                if strcmp(obj.grd.proj.name,'UTM')
                    % UTM is already in meters (useful for small domains)
                    L = sqrt(sum(barvec.^2,2))*Re;
                else
                    % Get spherical earth distances
                    long   = zeros(length(bars)*2,1);
                    lat    = zeros(length(bars)*2,1);
                    long(1:2:end) = p(bars(:,1),1);
                    long(2:2:end) = p(bars(:,2),1);
                    lat(1:2:end)  = p(bars(:,1),2);
                    lat(2:2:end)  = p(bars(:,2),2);
                    L = m_lldist(long,lat); L = L(1:2:end)*1e3;            % L = Bar lengths in meters
                end
                ideal_bars = 0.5*(pt(bars(:,1),:) + pt(bars(:,2),:));      % Used to determine what bars are in bbox
                [ideal_bars(:,1),ideal_bars(:,2)] = ...                    % needs to be in non-projected
                    m_xy2ll(ideal_bars(:,1),ideal_bars(:,2));              % coordinates
                hbars = 0*ideal_bars(:,1);

                for box_num = 1:length(obj.h0)                             % For each bbox, find the bars that are in and calculate
                    if ~iscell(obj.fh)                                     % their ideal lengths.
                        fh_l = obj.fh;
                    else
                        fh_l = obj.fh{box_num};
                    end
                    h0_l = obj.h0(box_num);
                    if box_num > 1
                        h0_l = h0_l/111e3;                                 % create buffer to evalulate fh between nests
                        iboubox = obj.boubox{box_num}(1:end-1,:) ;
                        inside = inpoly(ideal_bars,iboubox) ;
                    else
                        inside = true(size(hbars));
                    end
                    hbars(inside) = feval(fh_l,ideal_bars(inside,:));      % Ideal lengths
                end

                L0 = hbars*Fscale*median(L)/median(hbars);                 % L0 = Desired lengths using ratio of medians scale factor
                LN = L./L0;                                                % LN = Normalized bar lengths

                % Mesh improvements (deleting and addition)
                if ~mod(it,imp)
                    p_before_improve = p;
                    nn = []; pst = [];
                    if abs(qual_diff) < imp*obj.qual_tol && ...
                        (obj.improve_with_reduced_quality || qual_diff > 0)
                        % Remove elements with small connectivity
                        nn = get_small_connectivity(p,t);
                        disp(['Deleting ' num2str(length(nn)) ' due to small connectivity'])

                        % Remove points that are too close (< LN = 0.5)
                        if any(LN < 0.5)
                            % Do not delete pfix too close.
                            nn1 = setdiff(reshape(bars(LN < 0.5,:),[],1),[(1:nfix)']);
                            disp(['Deleting ' num2str(length(nn1)) ' points too close together'])
                            nn = unique([nn; nn1]);
                        end

                        % Split long edges however many times to
                        % better lead to LN of 1
                        if any(LN > 2)
                            nsplit = floor(LN);
                            nsplit(nsplit < 1) = 1;
                            adding = 0;
                            % Probably we can just split once?
                            for jj = 2:2
                                il = find(nsplit >= jj);
                                xadd = zeros(length(il),jj-1);
                                yadd = zeros(length(il),jj-1);
                                for jjj = 1 : length(il)
                                    deltax = (p(bars(il(jjj),2),1)- p(bars(il(jjj),1),1))/jj;
                                    deltay = (p(bars(il(jjj),2),2)- p(bars(il(jjj),1),2))/jj;
                                    xadd(jjj,:) = p(bars(il(jjj),1),1) + (1:jj-1)*deltax;
                                    yadd(jjj,:) = p(bars(il(jjj),1),2) + (1:jj-1)*deltay;
                                end
                                pst = [pst; xadd(:) yadd(:)];
                                adding = numel(xadd) + adding;
                            end
                            disp(['Adding ',num2str(adding) ,' points.'])
                        end
                    end
                    if ~isempty(nn) || ~isempty(pst)
                        % Doing the actual subtracting and add
                        p(nn,:)= [];
                        p = [p; pst];
                        pold = inf;
                        it = it + 1;
                        continue;
                    end
                end

                F    = (1-LN.^4).*exp(-LN.^4)./LN;                         % Bessens-Heckbert edge force
                Fvec = F*[1,1].*barvec;

                Ftot = full(sparse(bars(:,[1,1,2,2]),ones(size(F))*[1,2,1,2],[Fvec,-Fvec],N,2));
                Ftot(1:nfix,:) = 0;                                        % Force = 0 at fixed points
                pt = pt + deltat*Ftot;                                     % Update node positions

                [p(:,1),p(:,2)] = m_xy2ll(pt(:,1),pt(:,2));

                %7. Bring outside points back to the boundary
                d = feval(obj.fd,p,obj,[],1); ix = d > 0;                  % Find points outside (d>0)
                ix(1:nfix) = 0;
                if sum(ix) > 0
                    pn = p(ix,:) + deps;
                    dgradx = (feval(obj.fd,[pn(:,1),p(ix,2)],obj,[])...%,1)...
                              -d(ix))/deps; % Numerical
                    dgrady = (feval(obj.fd,[p(ix,1),pn(:,2)],obj,[])...%,1)...
                              -d(ix))/deps; % gradient
                    dgrad2 = dgradx.^+2 + dgrady.^+2;
                    p(ix,:) = p(ix,:) - [d(ix).*dgradx./dgrad2,...
                                         d(ix).*dgrady./dgrad2];
                end

                % 8. Termination criterion: Exceed itmax
                it = it + 1 ;

                if ( it > obj.itmax )
                    % Do the final deletion of small connectivity
                    if obj.delaunay_elim_on_exit
                        [t,p] = delaunay_elim(p,obj.fd,geps,1);
                    end
                    disp('too many iterations, exit')
                    close all;
                    break ;
                end
                toc
            end
            %%
            warning('on','all')
            %%
            disp('Finished iterating...');
            fprintf(1,' ------------------------------------------------------->\n') ;

            %% Doing the final cleaning and fixing to the mesh...
            % Clean up the mesh if specified
            if ~strcmp(obj.cleanup,'none')
                % Put the mesh class into the grd part of meshgen and clean
                obj.grd.p = p; obj.grd.t = t;
                [obj.grd,qout] = clean(obj.grd,obj.cleanup,...
                                       'nscreen',obj.nscreen,'djc',obj.dj_cutoff,...
									    'pfix',obj.pfix);
                obj.grd.pfix = obj.pfix ;
				obj.grd.egfix= obj.egfix ;
                obj.qual(end+1,:) = qout;
            else
                % Fix mesh on the projected space
                [p(:,1),p(:,2)] = m_ll2xy(p(:,1),p(:,2));
                [p,t] = fixmesh(p,t);
                [p(:,1),p(:,2)] = m_xy2ll(p(:,1),p(:,2));
                % Put the mesh class into the grd part of meshgen
                obj.grd.p = p; obj.grd.t = t;
                obj.grd.pfix = obj.pfix ;
                obj.grd.egfix= obj.egfix ;
            end

            % Check element order, important for the global meshes crossing
            % -180/180 boundary
            obj.grd = CheckElementOrder(obj.grd);

            if obj.plot_on
                figure; plot(obj.qual,'linewi',2);
                hold on
                % plot the line dividing cleanup and distmesh
                plot([it it],[0 1],'--k')
                xticks(1:5:obj.itmax);
                xlabel('Iterations'); ylabel('Geometric element quality');
                title('Geometric element quality with iterations');
                set(gca,'FontSize',14);
                legend('q_{mean}','q_{mean}-q_{3\sigma}', 'q_{min}','Location','best');
                grid minor
            end
            return;
            %%%%%%%%%%%%%%%%%%%%%%%%%%
            % Auxiliary subfunctions %
            %%%%%%%%%%%%%%%%%%%%%%%%%%

            function [t,p] = delaunay_elim(p,fd,geps,final)
                % Removing mean to reduce the magnitude of the points to
                % help the convex calc
                if exist('pt1','var'); clear pt1; end
                [pt1(:,1),pt1(:,2)] = m_ll2xy(p(:,1),p(:,2));
                if isempty(obj.egfix)
                    p_s  = pt1 - repmat(mean(pt1),[N,1]);
                    TR   = delaunayTriangulation(p_s);
                else
                    TR   = delaunayTriangulation(pt1(:,1),pt1(:,2),obj.egfix);
                    pt1  = TR.Points;
                end
                for kk = 1:final+1
                    if kk > 1
                        % Perform the following below upon exit from the mesh
                        % generation algorithm
                        nn = get_small_connectivity(pt1,t);
                        nn1 = heal_fixed_edges(pt1,t,obj.egfix) ;
                        nn = unique([nn; nn1]) ;
                        TR.Points(nn,:) = [];
                        pt1(nn,:) = [];
                    end
                    t = TR.ConnectivityList;
                    pmid = squeeze(mean(reshape(pt1(t,:),[],3,2),2));      % Compute centroids
                    [pmid(:,1),pmid(:,2)] = m_xy2ll(pmid(:,1),pmid(:,2));  % Change back to lat lon
                    t    = t(feval(fd,pmid,obj,[]) < -geps,:);             % Keep interior triangles
                    if kk == 1
                        % Deleting very straight triangles
                        tq_n = gettrimeshquan( pt1, t);
                        bad_ele = any(tq_n.vang < 1*pi/180 | ...
                                      tq_n.vang > 179*pi/180,2);
                        t(bad_ele,:) = [];
                    end
                end
                if length(pt1) ~= length(p)
                    clear p
                    [p(:,1),p(:,2)] = m_xy2ll(pt1(:,1),pt1(:,2));
                end
            end

            function nn = get_small_connectivity(p,t)
                % Get node connectivity (look for 4)
                [~, enum] = VertToEle(t);
                % Make sure they are not boundary nodes
                bdbars = extdom_edges2(t, p);
                bdnodes = unique(bdbars(:));
                I = find(enum <= 4);
                nn = setdiff(I',[(1:nfix)';bdnodes]);                      % and don't destroy pfix or egfix!
                return;
            end

            function del = heal_fixed_edges(p,t,egfix)
                % kjr april2019
                % if there's a triangle with a low geometric quality that
                % contains a fixed edge, remove the non-fixed vertex
                % perform this on every other iteration to allow non-fixed
                % points to create equilateral triangles nearby the locked
                % edge.
                % returns points IDs that should be deleted.
                TR = triangulation(t,p) ;
                elock = edgeAttachments(TR,egfix) ;
                tq = gettrimeshquan(p,t);
                elock = unique(cell2mat(elock'));
                dmy = elock(tq.qm(elock) < 0.25);
                badtria = t(dmy,:);
                del     = badtria(badtria > nfix) ;
            end

        end % end distmesh2d_plus

    end % end methods

end % end class


classdef InstanceSegMMLoss_randSample < dagnn.Loss
    properties
        marginAlpha_
        adaptiveMM = false;
    end
    properties (Transient)
        curSimMat
        weightMat
        marginMat
        mass_
        size_
	lastLayerName
    end

    methods
        function outputs = forward(obj, inputs, params)
            [h, w, ch, bs] = size(inputs{1});
            sz = [h, w, ch, bs];
            mass = sz(1) * sz(2) + 1;

            obj.size_ = sz;
            obj.mass_ = mass;

            layerName = obj.lastLayerName; %'res7_cosSim';
            if isnan(obj.net.getLayerIndex(layerName))
                layerName = 'obj_instSeg_reg';
            end
            obj.curSimMat = obj.net.layers(obj.net.getLayerIndex(layerName)).block.curSimMat;
            obj.weightMat = obj.net.layers(obj.net.getLayerIndex(layerName)).block.weightMat;

            if obj.adaptiveMM
                obj.marginMat = obj.net.layers(obj.net.getLayerIndex(layerName)).block.marginMat;
                obj.marginMat = max(obj.marginMat*obj.marginAlpha_, obj.marginAlpha_*0.2);
                outputs{1} = vl_nnloss_modified(inputs{1}, obj.curSimMat, 'marginAlpha_', obj.marginAlpha_, ...
                'loss', obj.loss, ...
                'instanceWeights', obj.weightMat, 'marginMat', obj.marginMat ) ; % 1./mass
            else
                outputs{1} = vl_nnloss_modified(inputs{1}, obj.curSimMat, 'marginAlpha_', obj.marginAlpha_, ...
                    'loss', obj.loss, ...
                    'instanceWeights', obj.weightMat ) ; % 1./mass
            end
            n = obj.numAveraged ;
            m = n + size(inputs{1},4) ;
            obj.average = (n * obj.average + double(gather(outputs{1}))) / m ;
            obj.numAveraged = m ;
        end

        function [derInputs, derParams] = backward(obj, inputs, params, derOutputs)
            derInputs{1} = vl_nnloss_modified(inputs{1}, obj.curSimMat, derOutputs{1}, 'marginAlpha_', obj.marginAlpha_, ...
                'loss', obj.loss, ...
                'instanceWeights', obj.weightMat ) ; % 1./mass
            derInputs{2} = [] ;
            derParams = {} ;
        end

        function obj = InstanceSegMMLoss_randSample(varargin)
            obj.load(varargin) ;
        end
    end
end