function phow_caltech101()
% PHOW_CALTECH101 Image classification in the Caltech-101 dataset
%   This program demonstrates how to use VLFeat to construct an image
%   classifier on the Caltech-101 data. The classifier uses PHOW
%   features (dense SIFT), spatial histograms of visual words, and a
%   Chi2 SVM. To speedup computation it uses VLFeat fast dense SIFT,
%   kd-trees, and homogeneous kernel map. The program also
%   demonstrates VLFeat PEGASOS SVM solver, although for this small
%   dataset other solvers such as LIBLINEAR can be more efficient.
%
%   By default 15 training images are used, which should result in
%   about 64% performance (a good performance considering that only a
%   single feature type is being used).
%
%   Call PHOW_CALTECH101 to train and test a classifier on a small
%   subset of the Caltech-101 data. Note that the program
%   automatically downloads a copy of the Caltech-101 data from the
%   Internet if it cannot find a local copy.
%
%   Edit the PHOW_CALTECH101 file to change the program configuration.
%
%   To run on the entire dataset change CONF.TINYPROBLEM to FALSE.
%
%   The Caltech-101 data is saved into CONF.CALDIR, which defaults to
%   'data/caltech-101'. Change this path to the desired location, for
%   instance to point to an existing copy of the Caltech-101 data.
%
%   The program can also be used to train a model on custom data by
%   pointing CONF.CALDIR to it. Just create a subdirectory for each
%   class and put the training images there. Make sure to adjust
%   CONF.NUMTRAIN accordingly.
%
%   Intermediate files are stored in the directory CONF.DATADIR. All
%   such files begin with the prefix CONF.PREFIX, which can be changed
%   to test different parameter settings without overriding previous
%   results.
%
%   The program saves the trained model in
%   <CONF.DATADIR>/<CONF.PREFIX>-model.mat. This model can be used to
%   test novel images independently of the Caltech data.
%
%     load('data/baseline-model.mat') ; # change to the model path
%     label = model.classify(model, im) ;
%

% Author: Andrea Vedaldi

% Copyright (C) 2011-2013 Andrea Vedaldi
% All rights reserved.
%
% This file is part of the VLFeat library and is made available under
% the terms of the BSD license (see the COPYING file).

conf.calDir = 'data/caltech-101' ;
conf.dataDir = 'data/' ;
conf.autoDownloadData = true ;
conf.numTrain = 15 ;
conf.numTest = 15 ;
conf.numClasses = 102 ;
conf.numWords = 600 ;
conf.numSpatialX = [2 4] ;
conf.numSpatialY = [2 4] ;
conf.quantizer = 'kdtree' ;
conf.svm.C = 10 ;

conf.svm.solver = 'sdca' ;
%conf.svm.solver = 'sgd' ;
%conf.svm.solver = 'liblinear' ;

conf.svm.biasMultiplier = 1 ;
conf.phowOpts = {'Step', 3} ;
conf.clobber = false ;
conf.tinyProblem = true ;
conf.prefix = 'baseline' ;
conf.randSeed = 1 ;

if conf.tinyProblem
  conf.prefix = 'tiny' ;
  conf.numClasses = 5 ;
  conf.numSpatialX = 2 ;
  conf.numSpatialY = 2 ;
  conf.numWords = 300 ;
  conf.phowOpts = {'Verbose', 2, 'Sizes', 7, 'Step', 5} ;
end

conf.vocabPath = fullfile(conf.dataDir, [conf.prefix '-vocab.mat']) ;
conf.histPath = fullfile(conf.dataDir, [conf.prefix '-hists.mat']) ;
conf.modelPath = fullfile(conf.dataDir, [conf.prefix '-model.mat']) ;
conf.resultPath = fullfile(conf.dataDir, [conf.prefix '-result']) ;

randn('state',conf.randSeed) ;
rand('state',conf.randSeed) ;
vl_twister('state',conf.randSeed) ;

% --------------------------------------------------------------------
%                                            Download Caltech-101 data
% --------------------------------------------------------------------

if ~exist(conf.calDir, 'dir') || ...
   (~exist(fullfile(conf.calDir, 'airplanes'),'dir') && ...
    ~exist(fullfile(conf.calDir, '101_ObjectCategories', 'airplanes')))
  if ~conf.autoDownloadData
    error(...
      ['Caltech-101 data not found. ' ...
       'Set conf.autoDownloadData=true to download the required data.']) ;
  end
  vl_xmkdir(conf.calDir) ;
  calUrl = ['http://www.vision.caltech.edu/Image_Datasets/' ...
    'Caltech101/101_ObjectCategories.tar.gz'] ;
  fprintf('Downloading Caltech-101 data to ''%s''. This will take a while.', conf.calDir) ;
  untar(calUrl, conf.calDir) ;
end

if ~exist(fullfile(conf.calDir, 'airplanes'),'dir')
  conf.calDir = fullfile(conf.calDir, '101_ObjectCategories') ;
end

% --------------------------------------------------------------------
%                                                           Setup data
% --------------------------------------------------------------------
classes = dir(conf.calDir) ;
classes = classes([classes.isdir]) ;
classes = {classes(3:conf.numClasses+2).name} ;

images = {} ;
imageClass = {} ;
for ci = 1:length(classes)
  ims = dir(fullfile(conf.calDir, classes{ci}, '*.jpg'))' ;
  ims = vl_colsubset(ims, conf.numTrain + conf.numTest) ;
  ims = cellfun(@(x)fullfile(classes{ci},x),{ims.name},'UniformOutput',false) ;
  images = {images{:}, ims{:}} ;
  imageClass{end+1} = ci * ones(1,length(ims)) ;
end
selTrain = find(mod(0:length(images)-1, conf.numTrain+conf.numTest) < conf.numTrain) ;
selTest = setdiff(1:length(images), selTrain) ;
imageClass = cat(2, imageClass{:}) ;

model.classes = classes ;
model.phowOpts = conf.phowOpts ;
model.numSpatialX = conf.numSpatialX ;
model.numSpatialY = conf.numSpatialY ;
model.quantizer = conf.quantizer ;
model.vocab = [] ;
model.w = [] ;
model.b = [] ;
model.classify = @classify ;

% --------------------------------------------------------------------
%                                                     Train vocabulary
% --------------------------------------------------------------------

if ~exist(conf.vocabPath) || conf.clobber

  % Get some PHOW descriptors to train the dictionary
  selTrainFeats = vl_colsubset(selTrain, 30) ;
  descrs = {} ;
  %for ii = 1:length(selTrainFeats)
  parfor ii = 1:length(selTrainFeats)
    im = imread(fullfile(conf.calDir, images{selTrainFeats(ii)})) ;
    im = standarizeImage(im) ;
    [drop, descrs{ii}] = vl_phow(im, model.phowOpts{:}) ;
  end

  descrs = vl_colsubset(cat(2, descrs{:}), 10e4) ;
  descrs = single(descrs) ;

  % Quantize the descriptors to get the visual words
  vocab = vl_kmeans(descrs, conf.numWords, 'verbose', 'algorithm', 'elkan', 'MaxNumIterations', 50) ;
  save(conf.vocabPath, 'vocab') ;
else
  load(conf.vocabPath) ;
end

model.vocab = vocab ;

if strcmp(model.quantizer, 'kdtree')
  model.kdtree = vl_kdtreebuild(vocab) ;
end

% --------------------------------------------------------------------
%                                           Compute spatial histograms
% --------------------------------------------------------------------

if ~exist(conf.histPath) || conf.clobber
  hists = {} ;
  parfor ii = 1:length(images)
  % for ii = 1:length(images)
    fprintf('Processing %s (%.2f %%)\n', images{ii}, 100 * ii / length(images)) ;
    im = imread(fullfile(conf.calDir, images{ii})) ;
    hists{ii} = getImageDescriptor(model, im);
  end

  hists = cat(2, hists{:}) ;
  save(conf.histPath, 'hists') ;
else
  load(conf.histPath) ;
end

% --------------------------------------------------------------------
%                                                  Compute feature map
% --------------------------------------------------------------------

psix = vl_homkermap(hists, 1, 'kchi2', 'gamma', .5) ;

% --------------------------------------------------------------------
%                                                            Train SVM
% --------------------------------------------------------------------

if ~exist(conf.modelPath) || conf.clobber
  switch conf.svm.solver
    case {'sgd', 'sdca'}
      lambda = 1 / (conf.svm.C *  length(selTrain)) ;
      w = [] ;
      parfor ci = 1:length(classes)
        perm = randperm(length(selTrain)) ;
        fprintf('Training model for class %s\n', classes{ci}) ;
        y = 2 * (imageClass(selTrain) == ci) - 1 ;
        [w(:,ci) b(ci) info] = vl_svmtrain(psix(:, selTrain(perm)), y(perm), lambda, ...
          'Solver', conf.svm.solver, ...
          'MaxNumIterations', 50/lambda, ...
          'BiasMultiplier', conf.svm.biasMultiplier, ...
          'Epsilon', 1e-3);
      end

    case 'liblinear'
      svm = train(imageClass(selTrain)', ...
                  sparse(double(psix(:,selTrain))),  ...
                  sprintf(' -s 3 -B %f -c %f', ...
                          conf.svm.biasMultiplier, conf.svm.C), ...
                  'col') ;
      w = svm.w(:,1:end-1)' ;
      b =  svm.w(:,end)' ;
  end

  model.b = conf.svm.biasMultiplier * b ;
  model.w = w ;

  save(conf.modelPath, 'model') ;
else
  load(conf.modelPath) ;
end

% --------------------------------------------------------------------
%                                                Test SVM and evaluate
% --------------------------------------------------------------------

% Estimate the class of the test images
scores = model.w' * psix + model.b' * ones(1,size(psix,2)) ;
[drop, imageEstClass] = max(scores, [], 1) ;

% Compute the confusion matrix
idx = sub2ind([length(classes), length(classes)], ...
              imageClass(selTest), imageEstClass(selTest)) ;
confus = zeros(length(classes)) ;
confus = vl_binsum(confus, ones(size(idx)), idx) ;

% Plots
figure(1) ; clf;
subplot(1,2,1) ;
imagesc(scores(:,[selTrain selTest])) ; title('Scores') ;
set(gca, 'ytick', 1:length(classes), 'yticklabel', classes) ;
subplot(1,2,2) ;
imagesc(confus) ;
title(sprintf('Confusion matrix (%.2f %% accuracy)', ...
              100 * mean(diag(confus)/conf.numTest) )) ;
print('-depsc2', [conf.resultPath '.ps']) ;
save([conf.resultPath '.mat'], 'confus', 'conf') ;

% -------------------------------------------------------------------------
function im = standarizeImage(im)
% -------------------------------------------------------------------------

im = im2single(im) ;
if size(im,1) > 480, im = imresize(im, [480 NaN]) ; end

% -------------------------------------------------------------------------
function hist = getImageDescriptor(model, im)
% -------------------------------------------------------------------------

im = standarizeImage(im) ;
width = size(im,2) ;
height = size(im,1) ;
numWords = size(model.vocab, 2) ;

% get PHOW features
[frames, descrs] = vl_phow(im, model.phowOpts{:}) ;

% quantize local descriptors into visual words
switch model.quantizer
  case 'vq'
    [drop, binsa] = min(vl_alldist(model.vocab, single(descrs)), [], 1) ;
  case 'kdtree'
    binsa = double(vl_kdtreequery(model.kdtree, model.vocab, ...
                                  single(descrs), ...
                                  'MaxComparisons', 50)) ;
end

for i = 1:length(model.numSpatialX)
  binsx = vl_binsearch(linspace(1,width,model.numSpatialX(i)+1), frames(1,:)) ;
  binsy = vl_binsearch(linspace(1,height,model.numSpatialY(i)+1), frames(2,:)) ;

  % combined quantization
  bins = sub2ind([model.numSpatialY(i), model.numSpatialX(i), numWords], ...
                 binsy,binsx,binsa) ;
  hist = zeros(model.numSpatialY(i) * model.numSpatialX(i) * numWords, 1) ;
  hist = vl_binsum(hist, ones(size(bins)), bins) ;
  hists{i} = single(hist / sum(hist)) ;
end
hist = cat(1,hists{:}) ;
hist = hist / sum(hist) ;

% -------------------------------------------------------------------------
function [className, score] = classify(model, im)
% -------------------------------------------------------------------------

hist = getImageDescriptor(model, im) ;
psix = vl_homkermap(hist, 1, 'kchi2', 'period', .7) ;
scores = model.w' * psix + model.b' ;
[score, best] = max(scores) ;
className = model.classes{best} ;


function demo_svg_water
% Create a new figure and remember the figure handle
fig = figure;
% Set default font and font size
set(fig, 'DefaultAxesFontName', 'Arial')
set(fig, 'DefaultAxesFontSize', 16)
% This is the data [year water_consumption]
data = [
1980	230
1982	265
1984	265
1986	266
1988	262
1990	259
1992	277
1994	247
1996	238
1998	250
2000	250
2002	235
2004	233
2006	228
];
% Let's plot the data
hold on
s = bar(data(:, 1), data(:, 2));
% We do not want to apply the filter to the edge of the bars. Therefore, we
% plot them in addition on top
h = bar(data(:, 1), data(:, 2));
set(h, 'FaceColor', 'none');
set(h, 'EdgeColor', 'black');
% Add the text inside each bar with correct rotation and alignment
t = text(data(:, 1), data(:, 2) - 10, num2str(data(:, 2)));
set(t, 'Rotation', -90);
set(t, 'FontSize', 16);
set(t, 'FontName', 'Arial');
set(t, 'VerticalAlignment', 'middle');
set(t, 'FontWeight', 'bold');
% Adding labels and tick marks
axis([1979 2008 0 400])
set(gca, 'XTick', data(:, 1));
set(gca, 'XTickLabel', num2str(data(:, 1)));
title('Water Consumption per Person and Year')
ylabel('Water consumption [l]')
grid on
box on
set(gca, 'Position', [0.13 0.17 0.80 0.72]);
% Now we add the filters
% The bounding box with extension axes makes sure that we cover the full
% axis region with the background images. Due to the shadow we have to
% define an overlap region of 12px. Otherwise, distortions at the border of
% the axis reagion may be visible.
svgBoundingBox(s, 'axes', 12, 'off');
% Now we add the image. To keep the aspect ratio we use 'xMidYMid slice'.
% This setting centers the picture for both x and y. The kewyord 'slice'
% makes sure that the picture is scaled so that the full axis region is
% covered.
svgImage(s, 'water_stones.jpg', 'xMidYMid slice', 'pic');
% Create a composite between bars and picture by union.
svgComposite(s, 'pic', 'SourceGraphic', 'atop', 'obj');
% To create the shadow we use the bars and use a blur filter
svgGaussianBlur(s, 'SourceAlpha', 5, 'blur');
% The blurred bars are now shifted by [10 10].
svgOffset(s, 'blur', [10 10], 'shade');
% Combine the shadow and 'picture bars' by applying the later on top of the
% shadow.
svgComposite(s, 'obj', 'shade', 'over', 'front');
% For the background we use the same picture but make it brighter
svgComposite(s, 'pic', 'pic', 'arithmetic', 'background', [0 0.2 0 0]);
% As last step, we combine the foreground and background
svgComposite(s, 'front', 'background', 'over', 'final');
% Beautify the tick mark labels by rotating them by 90 deg
setting.svg.XTickLabelAngle = -90;
set(gca, 'UserData', setting);
% Finally, we save the result into a SVG file
plot2svg('demo_svg_water.svg')


function stack_samples = LDRStackSubSampling(stack, stack_exposure, nSamples, sampling_strategy, outliers_percentage )
%
%       stack_samples = LDRStackSubSampling(stack, stack_exposure, nSamples, sampling_strategy, outliers_percentage )
%
%       This function subsamples a stack
%
%        Input:
%           -stack: a stack of LDR images. If the stack is a single or
%           double values are assumed to be in [0,1]
%           -nSamples: number of samples for computing the CRF
%           -sampling_strategy: how to select samples:
%               -'Grossberg': picking samples according to Grossberg and
%               Nayar algorithm (CDF based)
%               -'RandomSpatial': picking random samples in the image
%               -'RegularSpatial': picking regular samples in the image
%               -outliers_percentage:
%
%        Output:
%           -stack_samples: sub-sampled stack
%
%     Copyright (C) 2016  Francesco Banterle
%
%     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/>.
%

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

if(isempty(stack))
    error('LDRStackSubSampling: a stack cannot be empty!');
end

if(~exist('nSamples', 'var'))
    nSamples = 256;
end

if(~exist('sampling_strategy', 'var'))
    sampling_strategy = 'Grossberg';
end

[~, sort_index] = sort(stack_exposure, 'descend');
sort_index = sort_index';

%stack sub-sampling
switch sampling_strategy
    case 'Grossberg'
        stack_hist = ComputeLDRStackHistogram(stack);
        stack_samples = GrossbergSampling(stack_hist, nSamples);

    case 'RandomSpatial'
        stack_samples = SpatialSampling(stack, sort_index, nSamples, sampling_strategy);
        stack_samples = round(stack_samples * 255);

    case 'RegularSpatial'
        stack_samples = SpatialSampling(stack, sort_index, nSamples, sampling_strategy);
        stack_samples = round(stack_samples * 255);

    otherwise
        error('LDRStackSubSampling: A sampling strategy neeeds to be specified.');
end

if(outliers_percentage > 0.0)
    t_min = outliers_percentage;
    t_max = 1.0 - t_min;
    stack_samples(stack_samples < (t_min * 255.0) | stack_samples > (t_max * 255)) = -1.0;
end

end


function [y,u,f_fname,g_fname,dim,options,in]    = VBA_getDefaults()
% outputs default input to VBA_NLStateSpaceModel.m
% function [y,u,f_fname,g_fname,dim,options,in]    = VBA_getDefaults()
%
% This function provides the defaults inputs to the main
% VBA_NLStateSpaceModel.m routine. See VBA_check.m routine.

%------- Main inputs ------------%
y                   = 'pxn_t measured data matrix';
u                   = 'n_uxn_t driving input matrix (can be left empty)';
f_fname             = 'name/handle of the evolution function';
g_fname             = 'name/handle of the observation function';
dim.n               = 'dimension of the state space';
dim.n_theta         = 'dimension of the evolution parameter vector';
dim.n_phi           = 'dimension of the observation parameter vector';
in                  = 'only for inversion refinement (can be unspecified)';

%------- Options structure -------%
options.priors = 'structure containing the first two moments of all prior pdfs [see VBA_defaultPriors()]';
options.decim = 'for micro-time resolution';
options.microU = 'flag for micro-time input u';
options.inF = '(internal) parameters of the evolution function';
options.inG = '(internal) parameters of the observation function';
options.checkGrads = 'Check analytical gradients against numerical gradients';
options.updateX0 = 'Flag for initial conditions update';
options.updateHP = 'Flag for precision hyperparameters update';
options.MaxIter = 'Maximum number of iterations';
options.MinIter = 'Minimum number of iterations';
options.TolFun = 'Minimum change in the free energy ';
options.DisplayWin = 'VB display window';
options.GnMaxIter = 'Gauss-Newton inner-loops maximum number of iterations';
options.GnTolFun = 'Gauss-Newton inner-loops threshold on relative variational energy';
options.GnFigs = 'Gauss-Newton inner-loops figures';
options.verbose = 'summary messages in MATLAB main window';
options.finalEval = 'Externally-specified function evaluation: end of VB algo';
options.figName = 'Name of the display figure';
options.delays = 'dim.nX1 vector for delay embedding';
options.sources = 'Type and dimension of the data distribution(s)';


%for subplots
function p=plots(x,y,z,img)
H1=abs(img);
colormap(hot)
subplot(x,y,z);
image(100*H1/max(max(H1)));
if z==2
    title('ISAR images using Harmonic Wavelets');
end
if z==4
    ylabel('Range')
end
if z==8
    xlabel('Doppler')
end


function [x,varargout] = blasst(x,lineFrequencies,frequencyRanges,samplingRate,varargin)
% blasst(): EEGLAB helper function for OCW line noise removal.
% Takes as input an array of signals x, along with relevant parameters, and
% performs BLASST filtering at specified frequencies. For each specified
% frequency, blasst() iteratively calls blasst_internal() and then uses
% blasst_test() to test for convergence based on the distributions of
% convolution coefficients in the target and surrounding frequency bands.
%
% INPUT:
% x                 an [n,N] array of n signals of length N.
% lineFrequencies   an array of target frequencies (not normalized).
% frequenyRanges    an array of target frequency ranges. Must be the same
%                   size as lineFrequencies.
% samplingRate      the sampling rate of the signal.
% varargin          optional 'key',value pairs:
%   'key'
%           [default] purpose
%   'Scale'
%           [2^(log2(samplingRate)+2)] Manually set the scale that
%           indicates the spread of Gabor atoms.
%   'ContinuousEpochs'
%           [0] If x is epoched, but epochs are temporally adjacent,
%           setting to 1 will flatten x for processing. Otherwise,
%           blasst is run on individual epochs.
%   'Verbose'
%           [1] When on, progress is printed on command line.
%   'Resolution'
%           [2] May be an integer value >= 1, sets 'resolution' in blasst.
%           Specificies density of Gabor atoms. May also be an array of
%           integer values of size(lineFrequencies).
%   'MaxIterations'
%           [50] Maximum number of external iterations of blasst run on
%           each frequency. May be either a scalar integer or array of
%           integers of size(lineFrequencies).
%   'ManualOffset'
%           [log2(scaleBases)+1] A scalar value that offsets the
%           arrangement of Gabor atoms at each iteration of blasst.
%   'Channels'
%           [1:size(x,1)] An array of integers indexing channels to
%           be computed. Allows manually selection of channels for
%           processing.
%
% OUTPUT:
% x                 the processed, or ``cleaned'' signal.
% varargout{1}      the aggregate of the target signal feature removed, an
%                   array of size(x).
%
% DEPENDENCIES:
% blasst_internal()         primary line noise removal algorithm.
% blasst_test()        convergence test for iterative blasst algorithm.
%
% EXAMPLE:
%  Suppose we wish to remove line noise frequency at 60 and 120 Hz, and the
%  noise is mostly stationary at 120 Hz but non-stationary varying by about
%  2 Hz, around 60 Hz. Then we might call the method as:
% >> x = blasst(x,[60,120],[2,.25],<sampling rate>);
%
%  If we want to use more densely packed Gabor atoms, we could call:
% >> x = blasst(x,[60,120],[2,.25],<sampling rate>,'Resolution',4);
%
% AUTHOR: Kenneth Ball, 2015.
%
% IF YOU FIND BLASST USEFUL IN YOUR WORK, PLEASE CITE:
%
% Ball, K. R., Hairston, W. D., Franaszczuk, P. J., Robbins, K. A.,
% BLASST: Band Limited Atomic Sampling with Spectral Tuning with
% Applications to Utility Line Noise Filtering, [Under Review].
%
% Copyright 2015 Kenneth Ball
%
% Licensed under the Apache License, Version 2.0 (the "License");
% you may not use this file except in compliance with the License.
% You may obtain a copy of the License at
%
%     http://www.apache.org/licenses/LICENSE-2.0
%
% Unless required by applicable law or agreed to in writing, software
% distributed under the License is distributed on an "AS IS" BASIS,
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
% See the License for the specific language governing permissions and
% limitations under the License.

% Set defaults:
flattenData = 0;
verbose = 1;
resolution = 2;
maxIterations = 50; % Default is high so that the method generally will generally converge.
channels = 1:size(x,1);

% Adjust for optional inputs
if (nargin> 1)
    if (nargin> 4 && rem(nargin,2) == 1)
        if length(varargin) == 1
            varargin = varargin{1};
        else
            fprintf('blasst(): Optional key and value pairs do not match.')
            return
        end
    end

    for ii = 1:2:length(varargin)
        key = varargin{ii};
        val = varargin{ii+1};
        switch key
            case 'SamplingRate'
                samplingRate = val;
            case 'Scale'
                scale = val;
            case 'ContinuousEpochs'
                flattenData = val;
            case 'Verbose'
                verbose = val;
            case 'Resolution'
                resolution  = val;
            case 'MaxIterations'
                maxIterations = val;
            case 'ManualOffset'
                manualOffset = val;
            case 'Channels'
                channels = val;
        end
    end
end

if length(frequencyRanges) == 1
    frequencyRanges = ones(size(lineFrequencies))*frequencyRanges;
elseif length(frequencyRanges) ~= length(lineFrequencies)
    error('Number of specified noise frequencies does not match number of specified range values.');
end

if length(resolution) == 1
    resolution = ones(size(lineFrequencies))*resolution;
elseif length(resolution) ~= length(lineFrequencies)
    error('Number of specified noise resolutions does not match number of specified range values.');
end

if length(maxIterations) == 1
    maxIterations = ones(size(lineFrequencies))*maxIterations;
elseif length(maxIterations) ~= length(lineFrequencies)
    error('Number of specified max iterations does not match number of specified range values.');
end

if ~exist('samplingRate','var')
    error('No sampling rate specified.');
elseif isempty(samplingRate) || samplingRate == 0
    error('Null or zero sampling rate specified.');
end

if ~exist('scaleBases','var')
    scale = 2^(log2(samplingRate)+2);
end

if ~exist('manualOffset','var')
    manualOffset = log2(scale)+1;
end

if flattenData
    dataSizeTemp = size(x);
    x = reshape(x,size(x,1),size(x,2)*size(x,3));
end

% Initialize holders for fitted noise and cleaned signals.
y = zeros(size(x));
yTemp = zeros(size(x));
xTemp = zeros(size(x));

countTrack = 1;

for ii = 1:length(lineFrequencies)

    if verbose
        fprintf(1,'Frequency: %d Hz\n',lineFrequencies(ii));
    end
    for jj = channels
        if verbose
            fprintf(1,'Computing Channel:                ');
        end
        %         BDist = Inf;
        [BDist,~,~] = blasst_test(reshape(x(jj,:,:),1,size(x,2)*size(x,3)),lineFrequencies(ii),frequencyRanges(ii),samplingRate,scale);
        for mm = 1:maxIterations(ii)
            if verbose
                fprintf(1,'\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b% 3i   Pass: % 3i',jj,mm);
            end
            % Run blasst_internal for each epoch of data in the EEG struct.
            for kk = 1:size(x,3)
                [yTemp(jj,:,kk),xTemp(jj,:,kk)] = blasst_internal(x(jj,:,kk),lineFrequencies(ii),frequencyRanges(ii),samplingRate,scale,resolution(ii),(mm-1)*manualOffset);
                %                 y(jj,:,kk) = y(jj,:,kk) + tempY;
                %                 EEG.data(jj,:,kk) = tempX;
            end

            % Compute Bhatt. distance for testing convergence. Overwrite
            % BDist1, then compare to BDist (from the last pass). If BDist1
            % exceeds BDist, we presume we have passed the minimum distance
            % between the distributions, and we should halt the algorithm
            % for this channel and frequency, retaining only previous
            % passes.

            [BDist1,maxFlag,~] = blasst_test(reshape(xTemp(jj,:,:),1,size(xTemp,2)*size(xTemp,3)),lineFrequencies(ii),frequencyRanges(ii),samplingRate,scale);
            countTrack = countTrack+1;
            if BDist1 >= BDist && ~maxFlag % && mm > 1
                if verbose
                    fprintf(1,'\nBreak at pass % 2i\n',mm-1);
                end
                break
            else
                BDist = BDist1;
                y(jj,:,:) = y(jj,:,:) + yTemp(jj,:,:);
                x(jj,:,:) = xTemp(jj,:,:);
            end
        end
        if verbose
            fprintf(1,'\n');
        end
    end
    if verbose
        fprintf(1,'\n');
    end

end

varargout{1} = y; % This is the aggregate of all line noise that was removed. That is, y+EEG.data is the original dataset.

if flattenData
    x = reshape(x,dataSizeTemp(1),dataSizeTemp(2),dataSizeTemp(3));
end

end


function varargout = clusterImg(imgin,Clusters,varargin)

% Copyright 2010 The MathWorks, Inc.
% [IDX,C,sumd,D] = clusterImg(imgin,nClusters,inMask,labels)
%
% This function implements kmeans clustering on an input RGB (m x n x 3)
% image. The user inputs at least two inputs: IMGIN and
% NCLUSTERS, and this
% function will step through an interactive color segmentation using kmeans
% clustering. It is interactive in that the user will be prompted to
% click-define the target colors in the segmentation. (There will be
% NCLUSTERS prompts.) Optionally, the user can also input a third argument
% containing a binary mask of the input image, in which case the program
% will operate on a subimage defined by the BOUNDING BOX of the (true)
% inMask.
%
% NOTE 1: Following segmentation (clustering), the user can EXPORT THE CLUSTERED
% IMAGE TO THE WORKSPACE, along with its corresponding colormap. Additionally,
% the user can opt to EXPORT A BINARY MASK OF A SINGLE COLOR CLUSTER TO THE
% WORKSPACE. Both of these options are initiated by press of a uicontrol
% pushbutton. The naming convention is automatic; the clustered image and
% corresponding colormap will be named CLUSTERED1 and CLUSTEREDMAP1,
% respectively. If those variables already exist in the workspace, they
% will NOT be overwritten. Rather, the names will be modified by
% incrementing the integer value on the end (e.g., CLUSTERED2,
% CLUSTERMAP2). Similarly, the binary mask on a cluster will be named
% CLUSTERMASK, CLUSTERMASK1, CLUSTERMASK2,....
%
% NOTE 2: To reconstruct the image from workspace variables, use the command:
%       imshow(clustered,clusterMap);
%
% ***************************************
% SYNTAX:
%
% [IDX,C,sumd,D] = clusterImg(imgin,nClusters,inMask,labels);
%
% ***************************************
% INPUTS:
%
% Imgin:
%     an m x n x 3 (RGB) image
%
% Clusters:
%     Either A) an integer number of target clusters. (Note
%     that you will be prompted to click-define--using
%     IMPIXEL syntax--at least one pixel in each target
%     color cluster; you probably want this to be a
%     "reasonable" number (more than 1, less than, say,
%     20); or B) an nx3 array of predefined colors. Using
%     option B will skip the interactive selection process
%     and use the colors in Clusters as the bin targets.
%
% inMask (OPTIONAL)
%     a binary mask of size m x n. If a mask is included, this function
%     will operate on a sub-image defined by IMCROP(imgin,BoundingBox),
%     where BoundingBox is the rectangular extent of the true mask.
%
% labels (OPTIONAL)
%     a cell array of labels for the colormap display of the
%     segmented image. Note that the number of strings must
%     be equal to the number of colors.
%
% ***************************************
% OUTPUTS:
%
% IDX, C, sumd, D
%     All as returned by KMEANS. Please refer to the help for that
%     function. Note, however, that the size of the outputs may be modified
%     by the inclusion of optional inMask input argument. (See NOTE 2
%     below.)
%
% clustered
%     an indexed image of size p x q x 1 (see NOTE 4 below), segmented using kmeans
%     clustering. (See description of optional inMask input argument). If no
%     mask is provided, the CLUSTERED image is m x n x 1.
%
% clusteredMap
%     an Clusters x 3 matrix of RGB colors. These colors comprise the mean
%     values returned by each IMPIXEL selection when the user defines the
%     target cluster colors.
%
% clusterMask
%     a binary mask of size p x q x 1 (see NOTE 2 below). Once the input has been
%     clustered, it will be displayed in a figure with a pushbutton prompt
%     to create a mask on a clustered color. If the button is pressed, the
%     user will be prompted to click-select a single cluster color (using
%     IMPIXEL syntax). CLUSTER_MASK will return a binary image with ones in
%     the location of the selected color, and zeros elsewhere.
%
% NOTE 3: OUTPUT ARGUMENTS [CLUSTERED, CLUSTEREDMAP, CLUSTERMASK] ARE
% PROMTED BY UICONTROLS, and do not require the presence of output
% arguments. Nomenclature is automatic, as described in NOTE 1 above.)
%
% NOTE 4: OUTPUT IMAGE (AND MAP) SIZES are p x q, if a binary mask is provided
% as an optional input, where p and q are defined by the extent of the
% bounding box of the (true) input mask. If no input mask is provided, p=m
% and q=n.
%
%
% ***************************************
% EXAMPLES:
%
% 1)
% clusterImg(imread('peppers.png'),6);
%     segments the image in 'peppers.png' into 6 color
%     clusters.
% 2)
% a=[0.2902    0.1647    0.2824;
%    0.9137    0.7922    0.6784;
%    0.3725    0.4392    0.1412;
%    0.5961    0.1529    0.2039;
%    1.0000    0.7843    0.0431;
%    0.9686    0.5294         0];
% clusterImg(imread('peppers.png'),a,[],{'One','Two','Three','Four','Five','Six'});
%
% 3)
% [IDX, C, sumd, D] = clusterImg(x, 5, x(:,:,1) < 100);
%     masks image x with "red plane less than 100", finds the bounding box
%     of the resulting (logically true) inMask, and segments the IMCROPped
%     image x(inMask) into 5 color clusters. Returns the variables IDX, C,
%     sumd, and D, as described by the documentation for KMEANS.
%
% ***************************************

% ***************************************
% DEPENDENCIES:
% Image Processing Toolbox, Statistics Toolbox
%
% ***************************************
% Written by Brett Shoelson, PhD
% brett.shoelson@mathworks.com
%
% Ver 2.0
% Revisions:
% Ver 1.1; 11/12/06. Bounding box detection on imcrop modified.
% Ver 1.2; 1/31.07. The second input argument has been
%    renamed from nClusters to Clusters to reflect the fact
%    that one can now either A) specify the number of clusters
%    and select them interactively (as before); or B)input an
%    nx3 array of colors, skipping the interactive
%    selection. ALSO, the function now takes an additional
%    optional input argument to specify the labels on the
%    colormap of the segmented image.
% Ver 2.0; 11/29/07. Now includes a pushbutton for the auto-generation of
%    all segmented colormap. Also, included description of LABELS as input
%    argument, and fixed incorrect capitalization of "clusterMaskn"
%    indicator.
% ***************************************

%Parse and initialize input and output arguments
if ndims(imgin)~=3
	error('Input image must be m x n x 3 (RGB)');
end
if nargin == 3
	inMask = varargin{1};
	if all(inMask(:))
		%NO OP: inMask is all 1's...no masking performed
		inMask = [];
	end
else
	inMask = [];
end

if ~isempty(inMask) %at least one excluded point; operate on masked sub-image
	[r,c]=find(inMask);
	bb = [min(c) min(r) max(c)-min(c) max(r)-min(r)];
	imgin = imcrop(imgin,bb);
	%bb = regionprops(bwlabel(inMask),'BoundingBox');
	%imgin = imcrop(imgin,bb.BoundingBox);
end

%Operate on image as double, regardless of input type
if ~isa(imgin,'double')
	imgin = im2double(imgin);
end

%Get individual RGB colors included in segmentation
rc = imgin(:,:,1);
gc = imgin(:,:,2);
bc = imgin(:,:,3);
tmpimg = imgin;
segcolors = double([rc(:) gc(:) bc(:)]);

tmpfig=figure('numbertitle','off','name','Cluster RGB Image','visible','off');
imshow(tmpimg);
set(tmpfig,'units','pixels','position',[0 0 1000 800]);
centerfig(tmpfig);
set(tmpfig,'visible','on');

if numel(Clusters)==1
	% Prompt for target cluster colors
	colors = zeros(Clusters,3);
	for ii=1:Clusters
		figure(tmpfig);
		title(sprintf('Select sample pixel(s) in cluster %d of %d:',ii,Clusters));
		tmp = impixel;
		colors(ii,:) = mean(tmp,1);
	end
elseif size(Clusters,2) == 3
	% User passed in the target colors; skip interactive
	% selection
	colors = Clusters;
	Clusters = size(colors,1);
end
title('Segmenting. Please wait.......');
drawnow;
%Operate on colors (and image) as doubles()
colors = im2double(colors);

% CALL KMEANS with appropritate output arguments.
if nargout < 2
	Idx = kmeans(segcolors,Clusters,'distance','sqEuclidean','start',colors,'emptyaction','singleton');
elseif nargout == 2
	[Idx,varargout{2}] = ...
	   kmeans(segcolors,Clusters,'distance','sqEuclidean','start',colors,'emptyaction','singleton');
elseif nargout == 3
	[Idx, varargout{2}, varargout{3}] = ...
		kmeans(segcolors,Clusters,'distance','sqEuclidean','start',colors,'emptyaction','singleton');
elseif nargout == 4
		[Idx, varargout{2}, varargout{3}, varargout{4}] = ...
		kmeans(segcolors,Clusters,'distance','sqEuclidean','start',colors,'emptyaction','singleton');
else
	error('Inappropriate number of outputs requested in ClusterImg.');
end
if nargout >= 2
	varargout{1} = Idx;
end

pixel_labels = reshape(Idx,size(rc));
imshow(pixel_labels,[]);
title('Image labeled by cluster index');
set(tmpfig,'units','pixels','position',[0 0 1000 800]);
centerfig(tmpfig);

colormap(colors);
tmp = colorbar;
set(tmp,'ytick',1:Clusters);
if nargin == 4
	set(tmp,'yticklabel',varargin{2});
end
uicontrol('style','pushbutton','string','Export Clustered Image and Map to Workspace','units','normalized',...
	'pos',[0.05 0.05 0.3 0.05],'callback',@exportMain);
uicontrol('style','pushbutton','string','Export Mask on Single Color','units','normalized',...
	'pos',[0.35 0.05 0.3 0.05],'callback',@exportClusterMask);
uicontrol('style','pushbutton','string','Export All Color Masks','units','normalized',...
	'pos',[0.65 0.05 0.3 0.05],'callback',{@exportAllClusterMasks,colors});

%Subfunctions local to clustering only. Isn't MATLAB wonderful?
	function exportMain(varargin)
		n = 0; tmp = 1;
		while tmp
			n = n + 1;
			tmp = evalin('base',['exist(''clustered', num2str(n), ''')']);
		end
		fprintf('Image written to clustered%d and associated colormap written to clusteredMap%d\n',n,n);
		fprintf('Display with:    imshow(clustered%d,clusteredMap%d);\n',n,n);
		assignin('base',['clustered' num2str(n)],pixel_labels);
		assignin('base',['clusteredMap' num2str(n)],colors);
	end

	function exportClusterMask(varargin)
		title('Click on any pixel in the desired region.');
		p=impixel;
		if ~all(p(:)==p(1))
			beep;
			disp('Select a SINGLE region!!!');
		else
			n = 0; tmp = 1;
			while tmp
				n = n + 1;
				tmp = evalin('base',['exist(''clusterMask', num2str(n), ''')']);
			end
			title('Image labeled by cluster index');
			fprintf('Mask of region %d written to clusterMask%d\n',p(1),n);
			assignin('base',['clusterMask' num2str(n)],pixel_labels==p(1));
		end
    end

    function exportAllClusterMasks(varargin)
        colors = varargin{3};
        n = 0; tmp = 1;
        while tmp
            n = n + 1;
            tmp = evalin('base',['exist(''clusterMask', num2str(n), ''')']);
        end
        for ii = 1:size(colors,1)
            fprintf('Mask of region %d written to clusterMask%d\n',ii,n+ii-1);
            assignin('base',['clusterMask' num2str(n+ii-1)],pixel_labels==ii);
        end
    end

end


function param = bbox_search_cnn(frame, param, tmpl, background_threshold, frameNum, netId)

num_scale = 4;
neg_num = 16;
p = param.est;
ave_len = (p(3) + p(4)) / 2;

% test in batch to get the probability map of both the postive samples and the negative samples
feas = forward_cnn(tmpl, frame, num_scale + neg_num);

% Collect neg samples with high response on the CNN, which are used for finetune the cnn afterwards
neg_sample = [];
for i = num_scale + 1 : num_scale + neg_num
    if (sum(sum(feas(:, :, i))) > background_threshold)
        neg_sample = [neg_sample, tmpl.basis(:, i)];
    end
end

% bestConf = -1e9;
% bestCord = zeros(1,4);

% hierarchical search in the probability map of different scales to find the best bounding box, details of the searching scheme can be found in the paper
for scale = 1 : num_scale
    curfea = feas(:, :, scale);
    i = 1;
    clear x1_ y1_ x2_ y2_;
    % first get a rough estimation of the bounding box by thresholding the probability map
    for threshold = 0.1 : 0.1 : 0.7
        tmp = curfea > threshold;
        if sum(sum(tmp)) < 10
            break;
        end
        [x1_(i),y1_(i),x2_(i),y2_(i)] = find_box_in_map(tmp);
        i = i + 1;
    end
    if ~exist('x1_', 'var')
        continue;
    end
    cur_cord = mean([x1_', y1_', x2_', y2_'], 1);
    fea = feas(:, :, scale);
    x1 = cur_cord(1); x2 = cur_cord(3);
    y1 = cur_cord(2); y2 = cur_cord(4);

    % param.lastUpdate = 1;

    % save the average response of the region, which is then used to determine whether the cnn will be updated
    curconf = mean(mean(fea(x1 : x2, y1 : y2)));

    %% based on the above estimated center location, we then find a more accurate scale of the bounding box
    % calculate the location in the original frame
    scale_ratio = scale * 0.5;
    scale_width = (p(3) + scale_ratio * ave_len ) / 50;
    scale_height = (p(4) + scale_ratio * ave_len ) / 50;
    new_center = [(x1+x2)/2, (y1+y2)/2];
    center_x = (1 + 50)/2;
    center_y = (1 + 50)/2;
    new_p = p;
    new_p(1) = (p(1) + (new_center(1) - center_x ) * scale_width);
    new_p(2) = (p(2) + (new_center(2) - center_y ) * scale_height);

    % search again in the probability map for a more accurate scale.
    h = p(3) + scale_ratio * ave_len;
    w = p(4) + scale_ratio * ave_len;
    oriResizedMap = imresize(fea, [w, h]);

    total_scale = 0;
    total_ratio = 0;
    for th = 1 : 0.05 : 1.3
        resizedMap = max(oriResizedMap, 0) * 2 - th;
        % penalize boundary values, otherwise the estimated scale turns to be bigger
        resizedMap(resizedMap < -th + 0.1) = -2;
        iter_center(1) = new_center(1) * h / 50;
        iter_center(2) = new_center(2) * w / 50;
        forDecision = -1e9;
        curRes = -1e9;
        bestScale = 0;
        bestRatio = 0;
        sumMap = integral(double(resizedMap));
        for i = -0.02 : 0.001 : 0.02
            for j = 0.00 : 0.002 : 0.00
                newH = p(3) * (1 + i);
                newW = p(4) * (1 + i) * (1 + j);
                x1 = round(max(1, iter_center(2) - newW / 2));
                x2 = round(min(w, iter_center(2) + newW / 2));
                y1 = round(max(1, iter_center(1) - newH / 2));
                y2 = round(min(h, iter_center(1) + newH / 2));
                x1 = x1 - 1;
                y1 = y1 - 1;
                inConf = get(sumMap, x2, y2) - get(sumMap,x1, y2) - ...
                    get(sumMap, x2, y1) + get(sumMap, x1, y1);
                fmeasure = inConf * (newH * newW) / p(3) / p(4);

                if fmeasure > curRes
                    curRes = fmeasure;
                    bestScale = i;
                    bestRatio = j;
                    forDecision = inConf / newH / newW;
                end
            end
        end
        if forDecision < 0
            bestScale = 0;
            bestRatio = 0;
        end
        total_scale = total_scale + bestScale;
        total_ratio = total_ratio + bestRatio;
    end
    total_scale = total_scale / 7;
    total_ratio = total_ratio / 7;
    new_p(3) = ( p(3) * (1 + total_scale) );
    new_p(4) = ( p(4) * (1 + total_scale) * (1 + total_ratio));

    best_p = new_p;
    param.conf = curconf;
    param.neg_sample = neg_sample;
    param.est = best_p;
    return;
end

% if the code runs here, it means we cannot find the target in any scale, thus the target is reported missing.
param.neg_sample = [];
param.conf = 0;
end

function res = get(map, x, y)
    if x == 0 || y == 0
        res = 0;
    else
        res = map(x, y);
    end
end

function [x1,y1,x2,y2] = find_box_in_map(tmp)

x1 = find( sum(tmp, 1) > 0, 1 );
y1 = find( sum(tmp, 2) > 0, 1 );
x2 = size(tmp, 2) + 1 - find( fliplr(sum(tmp, 1)) > 0, 1 );
y2 = size(tmp, 1) + 1 - find( flipud(sum(tmp, 2)) > 0, 1 );

end


function [nlZ, dnlZ, HnlZ] = mcs_gp_optimizer(gpstruct, theta)
%MCS_GP_OPTIMIZER Wrapper function for GP optimization

fixed = gpstruct.fixed;
hyp = gpstruct.hyp;
inf = gpstruct.inf;
mean = gpstruct.mean;
cov = gpstruct.cov;
lik = gpstruct.lik;
x = gpstruct.x;
y = gpstruct.y;

newtheta = unwrap2vec(hyp);
newtheta(~fixed) = theta;
theta = rewrap(hyp, newtheta);

f = @() (feval(inf{:}, theta, mean, cov, lik, x, y));

if nargout <= 1
    [~, nlZ] = f();
    return;

elseif nargout == 2
    [~, nlZ, dnlZ] = f();

elseif nargout > 2
    [~, nlZ, dnlZ, ~, ~, HnlZ] = f();

    HnlZ = HnlZ.value;
    HnlZ(fixed,:) = [];
    HnlZ(:,fixed) = [];
end

dnlZ = unwrap2vec(dnlZ);
dnlZ(fixed) = [];

end


function cS = corundum

cs_Crn = crystalSymmetry('-32/m',[1 1 2.729]);
N = Miller({0,0,1},{1,0,2},{1,1,3},{2,-1,-1},{1,1,-1},{7,-14,-3},cs_Crn);
dist = [0.5, 1.49, 2.35, 1.53, 9.95, 9.95];
cS = crystalShape(N./dist);


% Copyright 2011 Zdenek Kalal
%
% This file is part of TLD.
%
% TLD 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.
%
% TLD 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 TLD.  If not, see <http://www.gnu.org/licenses/>.

function tld = tldUpdateDetectorConservative(tld,I)

bb    = tld.bb(:,I);
img   = tld.img{I};

% Consistency =============================================================

pPatt  = tldGetPattern(img,bb,tld.model.patchsize);
[pConf1,~,pIsin] = tldNN(pPatt,tld);

% Inconsistency: colildes with negative
if pConf1 < 0.5
%     disp('Fast change.');
    tld.valid(I) = 0;
    return;
end
if pIsin(3) == 1 || var(pPatt) < tld.var
%     disp('In negative data.');
    return;
end

% If strong position, identify indexes that will be removed and updated
pRank  = pIsin(2);
nRank  = []; % indexes to patches that will be removed from PEX
nEx    = []; % patches that will be added to NEX
if ~isnan(pRank)

    id_far = bb_overlap(bb,tld.dt{I}.bb) < 0.2;
    id_inP = tld.dt{I}.isin(1,:) == 1;
    nRank  = tld.dt{I}.isin(2,id_far & id_inP);

    % Inconsistency: negative patterns is in PEX with lower rank
    if any(nRank<=pRank),
        disp('inconsistent 2');
        return;
    end

    nEx = tld.dt{I}.patch(:,id_far);
end
tld.nEx{I} = nEx;
% Fern ====================================================================

% Generate positive patterns from current frame
overlap  = bb_overlap(bb,tld.grid);
[pX,pEx] = tldGeneratePositiveData(tld,overlap,img,tld.p_par_update);
pY       = ones(1,size(pX,2));

% Always use negative patterns from the first frame
% nX       = tld.X{1}(:,tld.Y{1}==0);
% idx      = pseudorandom_indexes(size(nX,2),10);
% nX       = nX(:,idx);

% If the current frame is valid, use negativ data from surrounding
% if pConf2 > tld.model.thr_nn_valid
idx = overlap < tld.n_par.overlap & tld.tmp.conf >= 0;

    %idx = pseudorandom_indexes(size(nX,2),10);
    %nX = nX(:,idx);

% Update fern
fern(2,[pX tld.tmp.patt(:,idx)],[pY zeros(1,sum(idx))],tld.model.thr_fern,2);

% Debug
if 0
    figure(10); clf;
    subplot(121);
    imshow(img.input);bb_draw(tld.dt{I}.bb); title('before update');
    subplot(122);
    fern(4,img,tld.control.maxbbox,tld.var,tld.tmp.conf,tld.tmp.patt);
    id_dt  = tld.tmp.conf > tld.model.thr_fern*tld.model.num_trees;

    imshow(img.input); bb_draw(tld.grid(:,id_dt));
end

% Nearest neighbour =======================================================

% Remove patterns from PEX
tld.pex(:,nRank) = [];

% Always use negative patches from the first frame
idx = pseudorandom_indexes(size(tld.nEx{1},2),10);
nEx = [nEx tld.nEx{1}(:,idx) tld.nEx{I-1}];

% Update NN
% TODO sort
tld = tldTrainNN(pEx,nEx,tld);

% sound(tld.gong(1:200));


function t_qps_matpower(quiet)
%T_QPS_MATPOWER  Tests of QPS_MATPOWER QP solvers.

%   MP-Opt-Model
%   Copyright (c) 2010-2020, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%
%   This file is part of MP-Opt-Model.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://matpower.org for more info.

if nargin < 1
    quiet = 0;
end

algs = {'DEFAULT', 'BPMPD', 'MIPS', 250, 'IPOPT', 'OT', 'CPLEX', 'MOSEK', 'GUROBI', 'CLP', 'GLPK'};
names = {'DEFAULT', 'BPMPD_MEX', 'MIPS', 'sc-MIPS', 'IPOPT', 'linprog/quadprog', 'CPLEX', 'MOSEK', 'Gurobi', 'CLP', 'glpk'};
check = {[], 'bpmpd', [], [], 'ipopt', 'quadprog', 'cplex', 'mosek', 'gurobi', 'clp', 'glpk'};
does_qp = [1 1 1 1 1 1 1 1 1 1 0];

n = 36;
nqp = 28;
t_begin(n*length(algs), quiet);

diff_alg_warn_id = 'optim:linprog:WillRunDiffAlg';
if have_feature('quadprog') && have_feature('quadprog', 'vnum') == 7.005
    s1 = warning('query', diff_alg_warn_id);
    warning('off', diff_alg_warn_id);
end

for k = 1:length(algs)
    if ~isempty(check{k}) && ~have_feature(check{k})
        t_skip(n, sprintf('%s not installed', names{k}));
    else
        opt = struct('verbose', 0, 'alg', algs{k});
        mpopt = struct( ...
            'verbose', 0, ...
            'opf', struct( ...
                'violation', 5e-6 ), ...
            'cplex', struct( ...
                'lpmethod', 0, ...
                'qpmethod', 0, ...
                'opt', 0 ), ...
            'mosek', struct( ...
                'lp_alg', 0, ...
                'max_it', 0, ...
                'gap_tol', 0, ...
                'max_time', 0, ...
                'num_threads', 0, ...
                'opt', 0 ) ...
        );
        if strcmp(names{k}, 'DEFAULT') || strcmp(names{k}, 'MIPS') || strcmp(names{k}, 'sc-MIPS')
            opt.mips_opt.comptol = 1e-8;
        end
%         if strcmp(names{k}, 'linprog/quadprog')
%             opt.verbose = 2;
%             opt.linprog_opt.Algorithm = 'interior-point';
%             opt.linprog_opt.Algorithm = 'active-set';
%             opt.linprog_opt.Algorithm = 'simplex';
%             opt.linprog_opt.Algorithm = 'dual-simplex';
%         end
        if strcmp(names{k}, 'CPLEX')
%           alg = 0;        %% default uses barrier method with NaN bug in lower lim multipliers
            alg = 2;        %% use dual simplex
            mpopt.cplex.lpmethod = alg;
            mpopt.cplex.qpmethod = min([4 alg]);
            opt.cplex_opt = cplex_options([], mpopt);
        end
        if strcmp(names{k}, 'MOSEK')
%             sc = mosek_symbcon;
%             alg = sc.MSK_OPTIMIZER_DUAL_SIMPLEX;    %% use dual simplex
%             alg = sc.MSK_OPTIMIZER_INTPNT;          %% use interior point
%             mpopt.mosek.lp_alg = alg;
            mpopt.mosek.gap_tol = 1e-10;
%             mpopt.mosek.opts.MSK_DPAR_INTPNT_TOL_PFEAS = 1e-10;
%             mpopt.mosek.opts.MSK_DPAR_INTPNT_TOL_DFEAS = 1e-10;
%             mpopt.mosek.opts.MSK_DPAR_INTPNT_TOL_INFEAS = 1e-10;
%             mpopt.mosek.opts.MSK_DPAR_INTPNT_TOL_REL_GAP = 1e-10;
            vnum = have_feature('mosek', 'vnum');
            if vnum >= 8
%                 mpopt.mosek.opts.MSK_DPAR_INTPNT_QO_TOL_PFEAS = 1e-10;
%                 mpopt.mosek.opts.MSK_DPAR_INTPNT_QO_TOL_DFEAS = 1e-10;
%                 mpopt.mosek.opts.MSK_DPAR_INTPNT_QO_TOL_INFEAS = 1e-10;
%                 mpopt.mosek.opts.MSK_DPAR_INTPNT_QO_TOL_MU_RED = 1e-10;
                mpopt.mosek.opts.MSK_DPAR_INTPNT_QO_TOL_REL_GAP = 1e-10;
            end
%             opt.verbose = 3;
            opt.mosek_opt = mosek_options([], mpopt);
        end

        t = sprintf('%s - 3-d LP : ', names{k});
        %% based on example from 'doc linprog'
        c = [-5; -4; -6];
        A = [1 -1  1;
             -3  -2  -4;
             3  2  0];
        l = [-Inf; -42; -Inf];
        u = [20; Inf; 30];
        xmin = [0; 0; 0];
        x0 = [];
        [x, f, s, out, lam] = qps_matpower([], c, A, l, u, xmin, [], [], opt);
        t_is(s, 1, 12, [t 'success']);
        t_is(x, [0; 15; 3], 6, [t 'x']);
        t_is(f, -78, 6, [t 'f']);
        t_is(lam.mu_l, [0;1.5;0], 9, [t 'lam.mu_l']);
        t_is(lam.mu_u, [0;0;0.5], 9, [t 'lam.mu_u']);
        if strcmp(algs{k}, 'CLP') && ~have_feature('opti_clp')
            t_skip(2, [t 'lam.lower/upper : MEXCLP does not return multipliers on var bounds']);
        else
            t_is(lam.lower, [1;0;0], 9, [t 'lam.lower']);
            t_is(lam.upper, zeros(size(x)), 9, [t 'lam.upper']);
        end

        if does_qp(k)
            t = sprintf('%s - unconstrained 3-d quadratic : ', names{k});
            %% from http://www.akiti.ca/QuadProgEx0Constr.html
            H = [5 -2 -1; -2 4 3; -1 3 5];
            c = [2; -35; -47];
            x0 = [0; 0; 0];
            [x, f, s, out, lam] = qps_matpower(H, c, [], [], [], [], [], [], opt);
            t_is(s, 1, 12, [t 'success']);
            t_is(x, [3; 5; 7], 8, [t 'x']);
            t_is(f, -249, 11, [t 'f']);
            t_ok(isempty(lam.mu_l), [t 'lam.mu_l']);
            t_ok(isempty(lam.mu_u), [t 'lam.mu_u']);
            t_is(lam.lower, zeros(size(x)), 13, [t 'lam.lower']);
            t_is(lam.upper, zeros(size(x)), 13, [t 'lam.upper']);

            t = sprintf('%s - constrained 2-d QP : ', names{k});
            %% example from 'doc quadprog'
            H = [   1   -1;
                    -1  2   ];
            c = [-2; -6];
            A = [   1   1;
                    -1  2;
                    2   1   ];
            l = [];
            u = [2; 2; 3];
            xmin = [0; 0];
            x0 = [];
            [x, f, s, out, lam] = qps_matpower(H, c, A, l, u, xmin, [], x0, opt);
            t_is(s, 1, 12, [t 'success']);
            t_is(x, [2; 4]/3, 7, [t 'x']);
            t_is(f, -74/9, 6, [t 'f']);
            t_is(lam.mu_l, [0;0;0], 13, [t 'lam.mu_l']);
            t_is(lam.mu_u, [28;4;0]/9, 4, [t 'lam.mu_u']);
            if strcmp(algs{k}, 'CLP') && ~have_feature('opti_clp')
                t_skip(2, [t 'lam.lower/upper : MEXCLP does not return multipliers on var bounds']);
            else
                t_is(lam.lower, zeros(size(x)), 7, [t 'lam.lower']);
                t_is(lam.upper, zeros(size(x)), 13, [t 'lam.upper']);
            end

            t = sprintf('%s - constrained 4-d QP : ', names{k});
            %% from https://v8doc.sas.com/sashtml/iml/chap8/sect12.htm
            H = [   1003.1  4.3     6.3     5.9;
                    4.3     2.2     2.1     3.9;
                    6.3     2.1     3.5     4.8;
                    5.9     3.9     4.8     10  ];
            c = zeros(4,1);
            A = [   1       1       1       1;
                    0.17    0.11    0.10    0.18    ];
            l = [1; 0.10];
            u = [1; Inf];
            xmin = zeros(4,1);
            x0 = [1; 0; 0; 1];
            [x, f, s, out, lam] = qps_matpower(H, c, A, l, u, xmin, [], x0, opt);
            t_is(s, 1, 12, [t 'success']);
            t_is(x, [0; 2.8; 0.2; 0]/3, 5, [t 'x']);
            t_is(f, 3.29/3, 6, [t 'f']);
            t_is(lam.mu_l, [6.58;0]/3, 6, [t 'lam.mu_l']);
            t_is(lam.mu_u, [0;0], 13, [t 'lam.mu_u']);
            if strcmp(algs{k}, 'CLP') && ~have_feature('opti_clp')
                t_skip(2, [t 'lam.lower/upper : MEXCLP does not return multipliers on var bounds']);
            else
                t_is(lam.lower, [2.24;0;0;1.7667], 4, [t 'lam.lower']);
                t_is(lam.upper, zeros(size(x)), 13, [t 'lam.upper']);
            end

            t = sprintf('%s - (struct) constrained 4-d QP : ', names{k});
            p = struct('H', H, 'A', A, 'l', l, 'u', u, 'xmin', xmin, 'x0', x0, 'opt', opt);
            [x, f, s, out, lam] = qps_matpower(p);
            t_is(s, 1, 12, [t 'success']);
            t_is(x, [0; 2.8; 0.2; 0]/3, 5, [t 'x']);
            t_is(f, 3.29/3, 6, [t 'f']);
            t_is(lam.mu_l, [6.58;0]/3, 6, [t 'lam.mu_l']);
            t_is(lam.mu_u, [0;0], 13, [t 'lam.mu_u']);
            if strcmp(algs{k}, 'CLP') && ~have_feature('opti_clp')
                t_skip(2, [t 'lam.lower/upper : MEXCLP does not return multipliers on var bounds']);
            else
                t_is(lam.lower, [2.24;0;0;1.7667], 4, [t 'lam.lower']);
                t_is(lam.upper, zeros(size(x)), 13, [t 'lam.upper']);
            end
        else
            t_skip(nqp, sprintf('%s does not handle QP problems', names{k}));
        end

        t = sprintf('%s - infeasible LP : ', names{k});
        p = struct('A', sparse([1 1]), 'c', [1;1], 'u', -1, 'xmin', [0;0], 'opt', opt);
        [x, f, s, out, lam] = qps_matpower(p);
        t_ok(s <= 0, [t 'no success']);
    end
end

if have_feature('quadprog') && have_feature('quadprog', 'vnum') == 7.005
    warning(s1.state, diff_alg_warn_id);
end

t_end;


%% Copyright (C) 2019, 2022 Colin B. Macdonald
%%
%% This file is part of OctSymPy.
%%
%% OctSymPy 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 software 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 software; see the file COPYING.
%% If not, see <http://www.gnu.org/licenses/>.

%% -*- texinfo -*-
%% @documentencoding UTF-8
%% @defmethod @@sym eval (@var{f})
%% Symbolic expression to double, taking values from workspace.
%%
%% For expressions without symbols, @code{eval} does the same thing
%% as @code{double}:
%% @example
%% @group
%% f = 2*sin(sym(3))
%%   @result{} f = (sym) 2⋅sin(3)
%% @c doctest: +SKIP_IF(compare_versions (OCTAVE_VERSION(), '6.0.0', '<'))
%% eval(f)
%%   @result{} ans = 0.2822
%% @c doctest: +SKIP_IF(compare_versions (OCTAVE_VERSION(), '6.0.0', '<'))
%% double(f)
%%   @result{} ans = 0.2822
%% @end group
%% @end example
%%
%% For an expression containing symbols, @code{eval} looks in the
%% workspace for variables whose names match the symbols.  It then
%% evaluates the expression using the values from those variables.
%% For example:
%% @example
%% @group
%% syms x y
%% f = x*sin(y)
%%   @result{} f = (sym) x⋅sin(y)
%% @end group
%%
%% @group
%% x = 2.1
%%   @result{} x = 2.1000
%% y = 2.9
%%   @result{} y = 2.9000
%% @end group
%%
%% @group
%% f
%%   @result{} f = (sym) x⋅sin(y)
%%
%% @c doctest: +SKIP_IF(compare_versions (OCTAVE_VERSION(), '6.0.0', '<'))
%% eval(f)
%%   @result{} ans = 0.5024
%% @end group
%% @end example
%%
%% @seealso{@@sym/subs}
%% @end defmethod

function g = eval(f)

  if (nargin ~= 1)
    print_usage ();
  end

  %% take values of x from the workspace
  in = findsymbols (f);
  out = {};
  i = 1;
  while (i <= length (in))
    xstr = char (in{i});
    try
      xval = evalin ('caller', xstr);
      foundit = true;
    catch
      foundit = false;
    end
    if (foundit)
      out{i} = xval;
      i = i + 1;
    else
      in(i) = [];  % erase that input
    end
  end

  try
    %% Fails if the workspace doesn't have values for all symbols.
    % Could also fail for fcns with broken "roundtrip"
    fh = function_handle(f, 'vars', in);
    g = fh(out{:});
    return
  catch
    % no-op
  end

  %% Instead, try substituting and converting to double.
  g = subs (f, in, out);
  try
    g = double (g);
  catch
    % just g then
  end

end

%!error eval (sym(1), 2)

%!assert (isnumeric (eval (sym(3))))
%!assert (isnumeric (eval (sin (sym(3)))))

%!test
%! syms x y
%! f = 2*x*y;
%! x = 3;
%! y = 4;
%! g = eval (f);
%! assert (isequal (g, 24))

%!test
%! syms x y
%! f = 2*x*y;
%! clear y
%! x = 3;
%! g = eval (f);
%! assert (isequal (g, 6*sym('y')))

%!test
%! % do not convert inputs to sym, for SMT compat
%! nearpi = pi + 1e-14;  % sym could make this pi
%! x = sym('x');
%! f = 2*x;
%! x = nearpi;
%! d = eval (f);
%! assert (abs (d - 2*pi) > 1e-15)


function [Ff, res] = loadPROC(root, mname, datExp, npl, plane_um, pix_um, f0)

root    = fullfile(root, mname,datExp);
fdir    = dir(root);
blockstring = fdir(3).name;
root    = fullfile(root,blockstring);

clear Fc

ik = 0;
med0 = [];
igpl = [1:npl];
statall = [];
redcell = [];

for iplane = igpl
    fname = sprintf('F_%s_%s_plane%d_Nk*_proc.mat', mname, datExp, iplane);

    fs = dir((fullfile(root, fname)));
    fname = fs(1).name;
    load(fullfile(root, fname))

    ds = (ceil((npl+1)/2) - iplane)/npl;
    %
    Fcell = dat.F.Fcell;

    [NN NT] = size(Fcell{1});

    iscell  = logical(dat.cl.iscell);
    statall = cat(1, statall, dat.stat(iscell)');
    redcell = cat(1, redcell, dat.cl.redcell(iscell));

    for j = 1:length(Fcell)
        F = Fcell{j}(iscell, :);
        F = register_F(F', ds)';
        Fc{iplane,j} = F;
    end
    %
    icells = find(iscell);
    for ip = 1:length(icells)
        ik = ik+1;
        med0(ik,:) = [pix_um*dat.stat(icells(ip)).med plane_um * iplane];
    end
end
%%

Ls = cellfun(@(x) size(x,2), Fc);
Lmax = min(Ls(igpl, :), [], 1);
%
Ff  = [];
for iplane = igpl
    F0 = [];
    for j = [1:size(Fc,2)]
        F0 = cat(2, F0, Fc{iplane, j}(:, 1:Lmax(j)));
    end
    Ff = cat(1, Ff, F0);
end
Ff = Ff';

med = med0;
if npl>=9
    goodcell = remove_doubles2(Ff, med);
    %
    Ff      = Ff(:, goodcell);
    med     = med(goodcell, :);
    statall = statall(goodcell);
    redcell = redcell(goodcell);
end
%
nCum = [0 cumsum(Lmax)];
%
[NT NN] = size(Ff);

Ff = single(Ff);

%%
tic
[NT NN] = size(Ff);
parfor i = 1:NN
    Ff(:,i) = Ff(:,i) - ordfilt2(Ff(:,i), 31 * ceil(f0/3), true(201 * ceil(f0/3),1), 'symmetric');
    Ff(:,i) = Ff(:,i) - median(Ff(:,i));
end

res.med     = med;
res.statall = statall;
res.redcell = redcell;
%%

res.nCum = nCum;


classdef dm_converter_mpc2_legacy < mp.dm_converter_mpc2
%MP.DM_CONVERTER_MPC2_LEGACY  MATPOWER data model converter for MATPOWER case v2.

%   MATPOWER
%   Copyright (c) 2021-2022, Power Systems Engineering Research Center (PSERC)
%   by Ray Zimmerman, PSERC Cornell
%
%   This file is part of MATPOWER.
%   Covered by the 3-clause BSD License (see LICENSE file for details).
%   See https://matpower.org for more info.

%     properties
%     end     %% properties

    methods
        function dm = legacy_user_mod_inputs(obj, dm, mpopt, dc)
            %% pre-process input related to user vars, constraints, costs

            %% create (read-only) copies of individual fields for convenience
            mpc = dm.source;
            [~, bus, gen, ~, ~, A, l, u, mpopt, ...
                N, fparm, H, Cw, z0, zl, zu, ~] = opf_args(mpc, mpopt);

            %% data dimensions
            nb   = size(bus, 1);    %% number of buses
            ng   = size(gen, 1);    %% number of dispatchable injections
            nlin = size(A, 1);      %% number of linear user constraints
            nw = size(N, 1);        %% number of general cost vars, w
            if dc
                %% reduce A and/or N from AC dimensions to DC dimensions, if needed
                if nlin || nw
                    acc = [nb+(1:nb) 2*nb+ng+(1:ng)];   %% Vm and Qg columns
                    if nlin && size(A, 2) >= 2*nb + 2*ng
                        %% make sure there aren't any constraints on Vm or Qg
                        if any(any(A(:, acc)))
                            error('mp.dm_converter_mpc2_legacy/legacy_user_mod_inputs: attempting to solve DC OPF with user constraints on Vm or Qg');
                        end
                        A(:, acc) = [];         %% delete Vm and Qg columns
                    end
                    if nw && size(N, 2) >= 2*nb + 2*ng
                        %% make sure there aren't any costs on Vm or Qg
                        if any(any(N(:, acc)))
                            [ii, jj] = find(N(:, acc));
                            ii = unique(ii);    %% indices of w with potential non-zero cost terms from Vm or Qg
                            if any(Cw(ii)) || (~isempty(H) && any(any(H(:, ii))))
                                error('mp.dm_converter_mpc2_legacy/legacy_user_mod_inputs: attempting to solve DC OPF with user costs on Vm or Qg');
                            end
                        end
                        N(:, acc) = [];         %% delete Vm and Qg columns
                    end
                end
                nv = 0;
                nq = 0;
            else
                nv = nb;
                nq = ng;
            end

            %% get number of user vars, check consistency
            nx = nb+nv + ng+nq;  %% number of standard OPF control variables
            if nlin
                nz = size(A, 2) - nx;   %% number of user z variables
                if nz < 0
                    error('mp.dm_converter_mpc2_legacy/legacy_user_mod_inputs: user supplied A matrix must have at least %d columns.', nx);
                end
            else
                nz = 0;               %% number of user z variables
                if nw                 %% still need to check number of columns of N
                    if size(mpc.N, 2) ~= nx;
                        error('mp.dm_converter_mpc2_legacy/legacy_user_mod_inputs: user supplied N matrix must have %d columns.', nx);
                    end
                end
            end

            %% package up parameters
            z = struct( 'nz', nz, ...           %% num user variables
                        'z0', z0, ...
                        'zl', zl, ...
                        'zu', zu    );
            lin = struct(   'nlin', nlin, ...   %% num user linear constraints
                            'A', A, ...
                            'l', l, ...
                            'u', u  );
            cost =  struct( 'nw', nw, ...       %% num user cost rows
                            'N', N, ...
                            'Cw', Cw    );
            nlc = obj.legacy_user_nln_constraints(dm, mpopt);

            if ~isempty(H)
                cost.H = H;
            end
            if ~isempty(fparm)
                cost.dd = fparm(:, 1);
                cost.rh = fparm(:, 2);
                cost.kk = fparm(:, 3);
                cost.mm = fparm(:, 4);
            end
            dm.userdata.legacy_opf_user_mods = struct( ...
                'z', z, ...
                'lin', lin, ...
                'nlc', {nlc}, ...
                'cost', cost );
        end

        function uc = legacy_user_nln_constraints(obj, dm, mpopt)
            mpc = dm.source;

            %% check for user-defined nonlinear constraints
            nnle = 0;   %% number of nonlinear user-defined equality cons
            nnli = 0;   %% number of nonlinear user-defined inequality cons
            if isfield(mpc, 'user_constraints')
                if isfield(mpc.user_constraints, 'nle')
                    for k = 1:length(mpc.user_constraints.nle)
                        nnle = nnle + mpc.user_constraints.nle{k}{2};
                    end
                end
                if isfield(mpc.user_constraints, 'nli')
                    for k = 1:length(mpc.user_constraints.nli)
                        nnli = nnli + mpc.user_constraints.nli{k}{2};
                    end
                end
            end

            %% initialize cell array for add_nln_constraint() args
            uc = cell(nnle+nnli, 1);

            %% user-defined nonlinear equalities
            if nnle
                for k = 1:length(mpc.user_constraints.nle)
                    nlc = mpc.user_constraints.nle{k};
                    fcn  = eval(['@(x)' nlc{3} '(x, nlc{6}{:})']);
                    hess = eval(['@(x, lam)' nlc{4} '(x, lam, nlc{6}{:})']);
                    uc{k} = {nlc{1:2}, 1, fcn, hess, nlc{5}};
                end
            end

            %% user-defined nonlinear inequalities
            if nnli
                for k = 1:length(mpc.user_constraints.nli)
                    nlc = mpc.user_constraints.nli{k};
                    fcn  = eval(['@(x)' nlc{3} '(x, nlc{6}{:})'])
                    hess = eval(['@(x, lam)' nlc{4} '(x, lam, nlc{6}{:})'])
                    uc{nnle+k} = {nlc{1:2}, 0, fcn, hess, nlc{5}};
                end
            end
        end
    end     %% methods
end         %% classdef


classdef BOWKMeansTrainer < handle
    %BOWKMEANSTRAINER  KMeans-based class to train visual vocabulary using the bag of visual words approach
    %
    % kmeans-based class for training the *bag of visual words* vocabulary
    % from a set of descriptors.
    %
    % ## Example
    %
    %     % create bag of visual words
    %     trainer = cv.BOWKMeansTrainer(K);
    %     dictionary = trainer.cluster(train_descs);
    %
    %     % Compute histogram of visual word occurrences of an image
    %     extractor = cv.BOWImgDescriptorExtractor('SIFT','BruteForce');
    %     extractor.Vocabulary = dictionary;
    %     descs = extractor.compute(im, keypoints);
    %
    % ## References
    % > "Visual Categorization with Bags of Keypoints" by
    % > Gabriella Csurka, Christopher R. Dance, Lixin Fan, Jutta Willamowski,
    % > Cedric Bray, 2004.
    %
    % See also: cv.BOWKMeansTrainer.BOWKMeansTrainer,
    %  cv.BOWImgDescriptorExtractor, bagOfFeatures
    %

    properties (SetAccess = private)
        % Object ID
        id
    end

    methods
        function this = BOWKMeansTrainer(dictionarySize, varargin)
            %BOWKMEANSTRAINER  The constructor
            %
            %     trainer = cv.BOWKMeansTrainer(dictionarySize)
            %     [...] = cv.BOWKMeansTrainer(...,'OptionName', optionValue, ...)
            %
            % ## Input
            % * __dictionarySize__ Number of clusters.
            %
            % ## Options
            % * __Criteria__ The algorithm termination criteria, that is, the
            %   maximum number of iterations and/or the desired accuracy. The
            %   accuracy is specified as `Criteria.epsilon`. As soon as each
            %   of the cluster centers moves by less than `Criteria.epsilon`
            %   on some iteration, the algorithm stops. default
            %   `struct('type','Count+EPS', 'maxCount',100, 'epsilon',eps('float'))`
            % * __Attempts__ The number of times the algorithm is executed
            %   using different initial labelings. The algorithm returns the
            %   labels that yield the best compactness. default 3.
            % * __Initialization__ Method to initialize seeds, default 'PP'.
            %   One of the followings:
            %   * __Random__ Select random initial centers in each attempt.
            %   * __PP__ Use kmeans++ center initialization by
            %     Arthur and Vassilvitskii [Arthur2007].
            %
            % See also: cv.BOWKMeansTrainer, cv.kmeans
            %
            this.id = BOWKMeansTrainer_(0, 'new', dictionarySize, varargin{:});
        end

        function delete(this)
            %DELETE  Destructor
            %
            %     trainer.delete()
            %
            % See also: cv.BOWKMeansTrainer
            %
            if isempty(this.id), return; end
            BOWKMeansTrainer_(this.id, 'delete');
        end

        function descs = getDescriptors(this)
            %GETDESCRIPTORS  Returns a training set of descriptors
            %
            %     descs = trainer.getDescriptors()
            %
            % ## Output
            % * __descs__ a cell array of descriptors
            %
            % See also: cv.BOWKMeansTrainer.descriptorsCount
            %
            descs = BOWKMeansTrainer_(this.id, 'getDescriptors');
        end

        function count = descriptorsCount(this)
            %DESCRIPTORSCOUNT  Returns the count of all descriptors stored in the training set
            %
            %     count = trainer.descriptorsCount()
            %
            % ## Output
            % * __count__ is a numeric value
            %
            % See also: cv.BOWKMeansTrainer.add
            %
            count = BOWKMeansTrainer_(this.id, 'descriptorsCount');
        end

        function add(this, descs)
            %ADD  Adds descriptors to a training set
            %
            %     trainer.add(descs)
            %
            % ## Input
            % * __descs__ Descriptors to add to a training set. Each row of
            %   the descriptors matrix is a descriptor.
            %
            % The training set is clustered using cluster method to construct
            % the vocabulary.
            %
            % See also: cv.BOWKMeansTrainer.cluster
            %
            BOWKMeansTrainer_(this.id, 'add', descs);
        end

        function clear(this)
            %CLEAR  Clear training descriptors
            %
            %     trainer.clear()
            %
            % See also: cv.BOWKMeansTrainer.add
            %
            BOWKMeansTrainer_(this.id, 'clear');
        end

        function centers = cluster(this, descs)
            %CLUSTER  Clusters train descriptors
            %
            %     centers = trainer.cluster()
            %     centers = trainer.cluster(descs)
            %
            % ## Input
            % * __descs__ Descriptors to cluster. Each row of the
            %   descriptors matrix is a descriptor. Descriptors are not added
            %   to the inner train descriptor set.
            %
            % ## Output
            % * __centers__ Row vectors of vocabulary descriptors.
            %
            % The vocabulary consists of cluster centers. So, this method
            % returns the vocabulary. In the first variant of the method,
            % train descriptors stored in the object are clustered. In the
            % second variant, input descriptors are clustered.
            %
            % See also: cv.BOWKMeansTrainer.add, cv.kmeans
            %
            if (nargin > 1)
                centers = BOWKMeansTrainer_(this.id, 'cluster', descs);
            else
                centers = BOWKMeansTrainer_(this.id, 'cluster');
            end
        end
    end

end


% Codes written by Soheil Bahrampour
% Feb 14, 2014

% To perform online supervised task driven dictionary Learning on the train
% XArr with lable trls. The classifier is the multiclass qudratic classifier.

% Inputs:
%       XArr onsisting of train samples where different modalities are concatinated.
%       trls is the train lables
%       n is a vector consiting of feature dimension for each modality
%       d is the number of columns in dictionary
%       opts contains the parameters for multi-task optimization
%       nu is the regularization parameter for the classifier
%       ro is the constant for computing learnning rate
%       InitW consists of parameters for initializing W

% Output:
%       D is the Learned Dictionary which is cell array with same size as X
%           consiting of n*d dictionaries learned from different sensors
%       modelQuad is a Quadratic classifier consisting of the linear coeficients W
%       and bias term b

%%% sparce codes from different modalities are averaged to form the final
%%% feature vector which will be used for classification

function [D, modelQuad] = OnlineSupTaskDrivDicLeaDecFusJointQuadC(XArr, Y, n, d, opts, nu, ro, varargin)

iter = opts.iterSupDic;
rho = opts.rho; % regularization term for ADMM
computeCost = opts.computeCost; % flag for computing and ploting cost
batchSize = opts.batchSize;
intercept = opts.intercept;

sum_n = sum(n);
S = length(n); % number of sensors
N = size(XArr,2); % number of train samples
number_classes = size(Y,1);

if nargin > 7 % initial D is provided
    D  = varargin{1};
else
    % Initialize D using (randomy taken) train samples + at least NumPos samples the positive lables
    D = zeros(sum_n,d);
    permut = randperm(N); %randomly shuffle data
    tempIndex = find(trls==lable);
    NumPos = length(tempIndex); % May need to be tuned for the application
    permut2 = randperm(length(tempIndex));
    for s = 1: S
        if d > N
            error('Number of dictionary columns for a given class should be smaller than or equal to the number of train samples in that class');
        end
        D(:,1:d-NumPos) = XArr(:,permut(1:d-NumPos)); %initialize to first d train samples
        D(:,d-NumPos+1:end) = XArr(:, tempIndex(permut2(1,1:NumPos))); % at least one sample from the  positive class
    end
end

if nargin > 8 % % initial model parameters are provided
   modelQuad =  varargin{2};
   W = zeros(d, number_classes,S); % initialize W to small random numbers rather than setting all to zeros
   b = zeros(S, number_classes);
   for s=1:S
       W(:,:,s) = modelQuad{1,s}.W;
       b(s,:) = modelQuad{1,s}.b;
   end
else
    % intialize W using unsisupervised learning
    W = 0.01*randn(d, number_classes,S); % initialize W to small random numbers rather than setting all to zeros
    b = 0.01*zeros(S, number_classes);
end

% learning rate of the stochastic gradient descent algorithm
t0 = floor(N/batchSize)*iter/10; % after finalizing N % for setting the learning rate according to the task-driven dic learning paper: we set t0=T/10 where T is total number of updates
permut = randperm(N);
XArr = XArr(:,permut);
Y = Y(:,permut);

% optimization
if computeCost
    costStep = floor(N/batchSize); % Compute cost every costStep over the last costStep batch of train samples. For each batch of train samples, the cost will be computed before updating the dic using that trian samples
    costTemp = zeros(costStep,1); % to store cost over lasr costStep samples
    costTempCount = 0; % to count how many train sample are passed
    cost = zeros(iter*N,1); %cost value at each iteration
    costIter = 0;
end

step = 0;
BetaTemp2 = zeros(d,batchSize,S);
Do = zeros(sum_n, S*d);
Atemp2 = zeros(d, S, N);
gradD = zeros(sum_n,d);
L = zeros(d*S, d);  % for ADMM
U = zeros(d*S, d);
gradW = zeros(d, number_classes, S);
gradb = zeros(S, number_classes);
for iteration = 1: iter % number of iterations over whole training samples
    for t = 1: batchSize: N-rem(N,batchSize) % to loop over all train samples
        step = step + 1;
        % find sparse code A
        Atemp = zeros(S*d,batchSize); % concatinate sparse vector of differen modals
        temp = 1;
        for s = 1:S
            [L((s-1)*d+1:s*d,:), U((s-1)*d+1:s*d,:)] = factor(D(temp:temp+n(s,1)-1,:), rho);  % cash  L U factroziation for solving ADMM for whole batch
            Do(temp: temp+n(s,1)-1, s:S:S*d) = D(temp:temp+n(s,1)-1, :);
            temp = temp+n(s,1);
        end
        DoTDoAll = Do'*Do; % computate Do'Do for all columns
        for j= 1: batchSize  % if not running in paralle
            alpha = JointADMMEigenMex(D, XArr(:,j+t-1), n, opts.lambda, rho, L, U, opts.iterADMM);
            Atemp(:,j) = alpha(:);
            Atemp2(:,:,j+t-1) = alpha;
            act = zeros(d, 1); % to maintain active rows
            Gamma = zeros(d*S,d*S);
            temp = 1;
            temp_norm = sqrt(sum(alpha.^2,2));
            for row = 1:d
                if  temp_norm(row,1) > 10^(-8)
                    act(row) = 1;
                    Gamma(temp:temp+S-1, temp:temp+S-1) = (eye(S)-alpha(row,:)'*alpha(row,:)/temp_norm(row,1)^2)/temp_norm(row,1);
                    temp = temp + S;
                end
            end
            Gamma = Gamma(1:temp-1,1:temp-1);
            num_act = sum(act); % number of active rows
            act = logical(act);
            %%%
            %  flag = isAlphaValid(D, XArr(:,j+t-1), n, alpha, opts.lambda, act); % only to check optimality condition for alpha, should be commented after debug.
            %%%
            Beta = zeros(d*S,1);
            W_act = W(act,:,:);
            gradLs_actAlphaVec = zeros(num_act,S);
            for s= 1:S
                gradLs_actAlphaVec(:,s) = W_act(:,:,s)*(W_act(:,:,s)'*alpha(act,s) + b(s,:)' - Y(:,j+t-1));
            end
            gradLs_actAt =  gradLs_actAlphaVec';
            gradLs_actAt_vec = gradLs_actAt(:);
            acts = act(:, ones(S,1))'; %repmat(act', S, 1); repmat is slow
            DoTDo = DoTDoAll(acts(:),acts(:));  % compute Do'Do for whole columns and then select relevant columns
            Beta(acts(:),1) = (DoTDo + opts.lambda*Gamma + opts.lambda2*eye(num_act*S))\gradLs_actAt_vec;   %use this iff ill-conditioned (lambda2 not equal zero)
            Beta(acts(:),1) = (DoTDo + opts.lambda*Gamma)\gradLs_actAt_vec;
            BetaTemp2(:,j,:) = reshape(Beta,S,d)';
%             temp = 1; % replaceed with a vectoried version for efficiency
%             for s= 1:S
%                 gradD(temp:temp+n(s,1)-1,:) = -(D(temp:temp+n(s,1)-1,:)*Beta(s:S:S*d,1))*alpha(:, s)' + (XArr(temp:temp+n(s,1)-1,j+t-1) - D(temp:temp+n(s,1)-1,:)*alpha(:, s))*Beta(s:S:S*d,1)' + gradD(temp:temp+n(s,1)-1,:);
%                 temp = temp+n(s,1);
%             end
%             temp2 = y(1,j+t-1)*gradLogReg(y(1,j+t-1)*(Atemp(:,j)'*W+b));
%             gradW = gradW + Atemp(:,j)*temp2;
%             if intercept
%                 gradb = gradb + temp2;
%             end
        end
        temp = 1;
        for s= 1:S
            temp3 = BetaTemp2(:,:,s)*squeeze(Atemp2(:, s, t:t+batchSize-1))';
            gradD(temp:temp+n(s,1)-1,:) = - D(temp:temp+n(s,1)-1,:)*temp3/batchSize + (XArr(temp:temp+n(s,1)-1,t:t+batchSize-1)*BetaTemp2(:,:,s)' - D(temp:temp+n(s,1)-1,:)*temp3')/batchSize;
%             gradD(temp:temp+n(s,1)-1,:) = -(D(temp:temp+n(s,1)-1,:)*BetaTemp2(:,:,s))*squeeze(Atemp2(:, s, t:t+batchSize-1))'/batchSize + (XArr(temp:temp+n(s,1)-1,t:t+batchSize-1) - D(temp:temp+n(s,1)-1,:)*squeeze(Atemp2(:, s, t:t+batchSize-1)))*BetaTemp2(:,:,s)'/batchSize;
            temp = temp+n(s,1);
        end

        temp = 1;
        for s= 1:S
            temp2 = W(:,:,s)'*Atemp(temp:temp+d-1,:) + repmat(b(s,:)', 1, batchSize) - Y(:,t:t+batchSize-1);
            gradW(:,:,s)  = Atemp(temp:temp+d-1,:)*temp2'/batchSize + nu*W(:,:,s);
            if intercept
                gradb(s,:) = sum(temp2,2)'/batchSize;
            end
            temp = temp + d;
        end

%          error = checkGradientW(W,b,gradW, Atemp, Y(:,t), nu); % set batch size to 1 for checking
 %         error = checkGradientD(D,gradD, W,b, XArr(:,t), Y(:,t), nu, n, S, d, opts, rho, opts.iterADMM); % set batch size to 1 for checking

        % compute cost
        if computeCost
            if costTempCount == costStep
                costIter = costIter + 1;
                cost(costIter,1) = mean(costTemp);
                costTempCount = 0;
                costTemp = zeros(costStep,1);
            end
            costTempCount = costTempCount + 1;
             temp = 1;
             for s = 1 : S
                costTemp(costTempCount,1) = costTemp(costTempCount,1) + 0.5*sum(sum((Y(:,t:t+batchSize-1) - W(:,:,s)'*Atemp(temp:temp+d-1,:) - repmat(b(s,:)', 1, batchSize)).^2));
                temp = temp + d;
             end
             costTemp(costTempCount,1)= costTemp(costTempCount,1)/batchSize + nu/2*sum(sum(sum(W.^2))); % note that b is not regularized
        end

        % update W, b, D
        learnRate = min(ro, ro*t0/step);
        W = W - learnRate*gradW;
        if intercept
            b = b - learnRate*gradb;
        end
        D = D - learnRate*gradD;
        temp = 1;
        for s=1:S
            D(temp:temp+n(s,1)-1,:) = projectionDic(D(temp:temp+n(s,1)-1,:));
            temp = temp + n(s,1);
        end
    end
end
if computeCost
    cost = cost(1:costIter,1); % remove extra elements
    figure;plot(cost);
end

for s= 1:S
    modelQuad{1,s}.W = W(:,:,s);
    modelQuad{1,s}.b = b(s,:);
end
end

% function DoTDo = computeDotDo(D, S, n, sum_n, num_act, act)
% Do = zeros(sum_n, S*num_act);
% ind = 1;
% for s= 1:S
%     Do(ind: ind+n(s,1)-1, s:S:S*num_act) = D(ind:ind+n(s,1)-1, act);
%     ind = ind+n(s,1);
% end
% DoTDo = Do'*Do;
% end
%
% function DoTDo = computeDotDo2(D, S, n, sum_n, d) % compute Do'Do for whole columns, not just active and save that
% Do = zeros(sum_n, S*d);
% ind = 1;
% for s= 1:S
%     Do(ind: ind+n(s,1)-1, s:S:S*d) = D(ind:ind+n(s,1)-1, :);
%     ind = ind+n(s,1);
% end
% DoTDo = Do'*Do;
% end

%%%% only with batchSize equal to 1
function error = checkGradientW(W, b, gradW, Atemp, y, nu)
epsil = 0.0001;
error = zeros(size(W,1), 1);
S = size(b,1);
d = size(W,1);
for i=1:size(W,1)
    W2 = W;
    W2(i,1) = W2(i,1) + epsil;
    temp = 1;
    costp = 0;
    for s = 1 : S
        costp = costp + 0.5*sum(sum((y - W2(:,:,s)'*Atemp(temp:temp+d-1,:) - b(s,:)').^2));
        temp = temp + d;
    end
    costp = costp + nu/2*sum(sum(sum(W2.^2))); % Note that b is not regularized
    W2(i,1) = W2(i,1) - 2*epsil;
    temp = 1;
    costn = 0;
    for s = 1 : S
        costn = costn + 0.5*sum(sum((y - W2(:,:,s)'*Atemp(temp:temp+d-1,:) - b(s,:)').^2));
        temp = temp + d;
    end
    costn = costn + nu/2*sum(sum(sum(W2.^2))); % Note that b is not regularized
    error(i,1)=(costp-costn)/2/epsil-gradW(i,1); % this should be close to zero
end
end

%%%% only with batchSize equal to 1
function error = checkGradientD(D,gradD, W, b, X, y, nu, n, S, d, opts, rho, iterADMM)
epsil = 0.0001;
L = zeros(d*S, d);
U = zeros(d*S, d);
error = zeros(d*S, d);

for s= 1:S%S
    temp = 1;
    for s1 = 1:S
        [L((s1-1)*d+1:s1*d,:), U((s1-1)*d+1:s1*d,:)] = factor(D(temp:temp+n(s1,1)-1,:), rho);  % cash  L U factroziation for solving ADMM for whole batch
        temp = temp + n(s1,1);
    end
    temp = sum(n(1:s-1))+1;
    for j =1 : 20%size(D,2)
        for i=1:10%n(s,1)
            D2 = D;
            D2(temp+i-1,j) = D2(temp+i-1,j) + epsil;
            [L((s-1)*d+1:s*d,:), U((s-1)*d+1:s*d,:)] = factor(D2(temp:temp+n(s,1)-1,:), rho);
            alpha = JointADMMEigenMex(D2, X, n, opts.lambda, rho, L, U, iterADMM);
            costp = 0;
            for s1=1:S
                costp = costp + 0.5*sum(sum((y - W(:,:,s1)'*alpha(:,s1) - b(s1,:)').^2));
            end
            costp = costp + nu/2*sum(sum(sum(W.^2))); % Note that b is not regularized
            D2(temp+i-1,j) = D2(temp+i-1,j) - 2*epsil;
            [L((s-1)*d+1:s*d,:), U((s-1)*d+1:s*d,:)] = factor(D2(temp:temp+n(s,1)-1,:), rho);
            alpha = JointADMMEigenMex(D2, X, n, opts.lambda, rho, L, U, iterADMM);
            costn = 0;
            for s1=1:S
                costn = costn + 0.5*sum(sum((y - W(:,:,s1)'*alpha(:,s1) - b(s1,:)').^2));
            end
            costn = costn + nu/2*sum(sum(sum(W.^2))); % Note that b is not regularized
            error(temp+i-1,j)=(costp-costn)/2/epsil-gradD(temp+i-1,j); % this should be close to zero
        end
    end
end
end

function flag = isAlphaValid(D, X, n, alpha, lambda, act) % check optimality condition for joint sparsity given a candidate solution
flag = 0;
checkMat = zeros(size(alpha));
S = size(alpha,2);
temp = 1;
for s=1:S
    checkMat(:,s) = D(temp:temp+n(s,1)-1,:)'*(X(temp:temp+n(s,1)-1,:)-D(temp:temp+n(s,1)-1,:)*alpha(:,s)) ;
    temp = temp + n(s,1);
end
RHS = lambda*alpha(act,:)./repmat(sqrt(sum(alpha(act,:).^2,2)), 1,S);
if norm(checkMat(act,:)-RHS)< 1e-10 % if condition is valid for active set (approximately)
    if isempty(find(sqrt(sum(alpha(~act,:).^2,2)) > lambda, 1))
        flag = 1;
    end
end
end


function Plugin_Timer

global VVar
global VCtl

if VVar.TRCount==1
    tic;
    set(VCtl.h.TimeWait_text,'String', ['Est. Time Left:  ' 'Estimating...']);
    pause(0.0001);
%     set(VCtl.h.TimeBar_text,'String', '0%');
else
    elptime=toc;
    tic;
    lefttime=double(VCtl.TRNum-VVar.TRCount+1)*elptime;
    % Convert seconds to other units
    d = floor(lefttime/86400); % Days
    lefttime = lefttime - d*86400;
    h = floor(lefttime/3600); % Hours
    lefttime = lefttime - h*3600;
    m = floor(lefttime/60); % Minutes
    lefttime = lefttime - m*60;
    s = floor(lefttime); % Seconds
    pause(0.0001);
    set(VCtl.h.TimeWait_text,'String', ['Est. Time Left:  ' num2str(h) ' : ' num2str(m) ' : ' num2str(s)]);
%     set(VCtl.h.TimeBar_text,'String',[num2str((double(VVar.TRCount)/double(VCtl.TRNum))*100, '%02.0f') '%']);

    DoUpdateBar(VCtl.h.TimeBar_axes,double(VVar.TRCount),double(VCtl.TRNum));
    pause(0.001);
end

end


function status = test_wfs_25d(modus)
%TEST_WFS_25D tests behavior of 2.5D WFS
%
%   Usage: status = test_wfs_25d(modus)
%
%   Input parameters:
%       modus   - 0: numerical
%                 1: visual
%
%   Output parameters:
%       status  - true or false

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

status = false;

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

%% ===== Configuration ===================================================
% Parameters
conf = SFS_config;
conf.secondary_sources.geometry = 'linear';
conf.secondary_sources.size = 20;
conf.secondary_sources.number = 512;
conf.secondary_sources.center = [0,6,0];
conf.xref = [0,0,0];
conf.usetapwin = true;
conf.tapwinlen = 0.2;

%
f = 2000;  % temporal frequency
positions = { [0,9,0], [0,-1,0], [0,3,0,0,-1,0] };  % source positions
sources = {'ps', 'pw', 'fs'};
gtsources = {'ps', 'pw', 'ps'};
X = [-2,2];
Y = [-2,2];
Z = 0;

%% ===== Main ============================================================
for idx=1:length(positions)
    xs = positions{idx};
    src = sources{idx};
    gt = gtsources{idx};

    if modus
        figure;
        ddx = 0;
    end

    for driving_functions = {'reference_point', 'reference_line'}

        conf.driving_functions = driving_functions{:};
        Pgt = sound_field_mono(X,Y,Z,[xs(1:3),0,-1,0,1],gt,1,f,conf);
        Pwfs = sound_field_mono_wfs(X,Y,Z,xs,src,f,conf);

        if modus
            subplot(2,2,2*ddx+1);
            imagesc(Y,X,real(Pwfs));
            title(sprintf('%s %s',src,driving_functions{:}),'Interpreter','none');
            set(gca,'YDir','normal');
            colorbar;

            subplot(2,2,2*ddx+2);
            imagesc(Y,X,real(db(1 - Pwfs./Pgt)));
            title(sprintf('%s %s',src,driving_functions{:}),'Interpreter','none');
            set(gca,'YDir','normal');
            colorbar;
            hold on;
            if strcmp('reference_point',conf.driving_functions)
                plot(conf.xref(1),conf.xref(2),'gx');
            else
                plot(conf.xref(1)+X,conf.xref([2,2]),'g--');
            end
            hold off;

            ddx= ddx+1;
        end
    end
end

status = true;


classdef StiffnessTensor2VoigtConverterPS < FourthOrderTensor2VoigtConverterPS

    properties
    end

    methods (Access = public)

        function obj = StiffnessTensor2VoigtConverterPS(tensor)
           obj.computeConversion(tensor)
        end

    end

    methods (Access = protected)

        function selectVoigtTensorClass(obj)
            obj.voigtTensor = StiffnessPlaneStressVoigtTensor();
        end

    end

    methods (Access = protected,Static)

        function f = getVoigtFactor(iv,jv)
           f = 1;
        end

    end

end