%A demo of how Adaboost works.
%
%Some base learners have external dependancies. CART requires the stats
%toolbox (classregtree). RBF SVM requires libsvm (which must be above
%bioinformatics toolbox in the path). NeuralNetwork requires netlab.
%
%The demo displays some 2-D data, and shows the development of the decision
%boundary, margins, error rate and margin distribution as a boosted
%ensemble is trained.
%
%The data is split into two classes - red and blue. The decision boundary
%appears as a yellow contour. The margins are shown on a white->black
%gradient, where white is a confident classification as blue and black is a
%confident classification as red.
%
%To add a single learner, use the "Add Learner" button, or "Add 10" to add
%10 learners. Learners are shown in a scrollable list,
%with their adjusted weights beside them. Weight adjustment involves
%normalisation and then scaling so that 1 represents an average learner
%weight. These weights can be adjusted (even made negative), and learners
%can be enabled or disabled individual using the check box. Right-clicking
%on the checkbox disables all other learners. The 'Reset' button enables
%all learners and sets their weights to those computed by Adaboost.
%
%The 'Plot Error' and 'Plot Margins' buttons open new windows to display
%addition information. 'Plot Error' shows the training error (blue) and the
%generalisation error (red) against the size of the ensemble. 'Plot
%Margins' shows the cdf of the margins for the current ensemble. The height
%of the line above 0 indicates the proportion of the data with that margin
%or below.
%
%The two drop-down menus at the top allow a choice of base learner
%algorithms and data generating functions. When either of these are
%changed, the ensemble is completely reset for the new learner type or data
%set.
classdef boosting_demo < handle

  properties (Constant=true)

    %Starting size of the ensemble
    initial_size = 1;
    %Resolution of the decision boundaries and margins. Large numbers make
    %it slower.
    resolution = 200;

  end

  properties

    fig;
    plot_area;
    learner_labels;
    learner_check;
    learner_weights;
    weight_labels;
    learner_count;
    learner_enabled;
    pretend_weights;
    adaboost;
    inputs;
    outputs;
    g_inputs;
    example_weights;
    g_outputs;
    bgc = [0.8 0.8 0.8];
    data_handle;
    data_colors;
    margin_handle;
    boundary_handle;
    margins;
    predictions;
    x_grid;
    y_grid;
    add_learner_button;
    add_10_button;
    enable_all_button;
    error_rate_button;
    error_fig;
    plotting_error;
    margin_distribution_button;
    margin_fig;
    plotting_margin;
    learner_scroll;
    first_learner = 1;
    base_learner;
    base_learners;
    base_learner_names;
    base_learner_popup;
    data_func;
    data_funcs;
    data_func_names;
    data_func_popup;
    data_points;
    running = true;

  end

  methods

    function a = boosting_demo()

      %Number of data points to generate
      a.data_points = 200;
      %To add a new base learner, add the construction function to this
      %list, then add the name to a.base_learner_names. The base learner
      %must have train(inputs, outputs) and outputs = test(inputs)
      %functions.
      base_learner_num = 6;
      a.base_learners = {@()SVM() @()LinearRegression @()CART() @()OnlineNaiveBayes(0) @()NeuralNetwork(10, 0.01, 2, 'linear', 50), @()Stump()};
      a.base_learner_names = {'RBF SVM', 'Linear Regression', 'CART', 'Naive Bayes', 'Neural Network', 'Decision Stump'};
      a.base_learner = a.base_learners{base_learner_num};
      %To add a new data generator, add the function to this list, where c
      %is the number of data points to generate. The function must return
      %[inputs outputs] where inputs is cX2 and outputs is cX1. Then add
      %the name to the second list.
      data_func_num = 3;
      a.data_funcs = {@(c)DataGen.circle(c, 5, 0.5), @(c)DataGen.checkerboard(c, 10, 2, rand * pi), ...
        @(c)DataGen.gaussians(c, [-2 -2], [2 2], [2 2], [2 2])};
      a.data_func_names = {'Circle', 'Checkerboard', 'Gaussians'};
      a.data_func = a.data_funcs{data_func_num};

      %Make the ensemble
      a.adaboost = Adaboost(boosting_demo.initial_size, a.base_learner);
      %Sort out the main window
      screen_size = get(0, 'ScreenSize');
      a.fig = figure('Position', [(screen_size(3) - 800) / 2 (screen_size(4) - 600) / 2 800 600], 'MenuBar', 'none', ...
        'CloseRequestFcn', @(varargin)a.halt(), 'Name', 'Boosting Demo', 'NumberTitle', 'off');

      %Create the error and margin graphs.
      a.error_fig = figure('Name', 'Error Rates', 'NumberTitle', 'off');
      a.margin_fig = figure('Name', 'Margin Distribution', 'NumberTitle', 'off');
      set(a.error_fig, 'visible', 'off');
      set(a.margin_fig, 'visible', 'off');
      figure(a.fig);
      a.plot_area = axes('Units','Pixels');

      hold on;

      colormap('gray');
      %Plot the data
      a.data_handle = scatter([], [], [], '+', 'SizeDataSource', 'a.example_weights', 'XDataSource', 'a.inputs(:, 1)',...
        'YDataSource', 'a.inputs(:, 2)', 'CDataSource', 'a.data_colors', 'HitTest', 'off',...
        'LineWidth', 3);

      %Plot the margin and boundary contours
      min = -5;
      max = 5;
      step = (max - min) / boosting_demo.resolution;
      range = min:step:max;
      [a.x_grid a.y_grid] = meshgrid(range, range);
      a.margins = repmat(range, size(a.x_grid, 2), 1);
      [null a.margin_handle] = contourf(a.x_grid, a.y_grid, a.margins, 'XDataSource', 'a.x_grid', ...
        'YDataSource', 'a.y_grid', 'ZDataSource', 'a.margins', 'LineStyle', 'none');
      [null a.boundary_handle] = contour(a.x_grid, a.y_grid, a.margins > 0, 1, 'y', 'XDataSource', 'a.x_grid', ...
        'YDataSource', 'a.y_grid', 'ZDataSource', 'a.margins > 0', 'LineWidth', 2);

      hold off;

      %Make GUI components
      a.base_learner_popup = uicontrol('Style', 'popupmenu', 'String', a.base_learner_names, ...
        'Callback', @(varargin)a.base_learner_change(), 'Value', base_learner_num);
      a.data_func_popup = uicontrol('Style', 'popupmenu', 'String', a.data_func_names, ...
        'Callback', @(varargin)a.data_func_change(), 'Value', data_func_num);
      a.add_learner_button = uicontrol('String', 'Add Learner', 'Callback', @(varargin)a.add_learner());
      a.add_10_button = uicontrol('String', 'Add 10', 'Callback', @(varargin)a.add_10());
      a.enable_all_button = uicontrol('String', 'Reset', 'Callback', @(varargin)a.enable_all());
      a.error_rate_button = uicontrol('String', 'Plot Error', 'Callback', @(varargin)a.plot_error());
      a.margin_distribution_button = uicontrol('String', 'Plot Margins', ...
        'Callback', @(varargin)a.plot_margins());
      a.learner_scroll = uicontrol('Style', 'Slider', 'min', 0, 'max', 1);

     % listener = handle.listener(a.learner_scroll,'ActionEvent',@(varargin)a.scroll_learners());
      listener = addlistener(a.learner_scroll,'Action',@(varargin)a.scroll_learners());

      setappdata(a.learner_scroll, 'listener', listener);

      %Generate the data
      a.random_data();
      %Train the ensemble (First learner)
      a.train_adaboost();
      %Layout function
      set(a.fig, 'ResizeFcn', @(varargin)a.do_layout());

    end

    %Stop the program, called when the window is closed
    function halt(a)
      a.running = false;
      if (ishandle(a.error_fig))
        close(a.error_fig);
      end
      if (ishandle(a.margin_fig))
        close(a.margin_fig);
      end
      delete(a.fig);
    end

    function do_layout(a)
      %Layout. Do the side bar, and make the data take up the rest of the
      %window
      try
        figure(a.fig);
        pos = get(a.fig, 'Position');
        fig_width = pos(3) - 370;
        fig_height = pos(4) - 60;
        fig_size = min(fig_width, fig_height);
        set(a.plot_area, 'Position', [350 (pos(4) - fig_size - 30) fig_size fig_size]);
        top = pos(4) - 40;
        set(a.base_learner_popup, 'Position', [10 top 150 20]);
        set(a.data_func_popup, 'Position', [170 top 150 20]);
        top = top - 30;
        set(a.error_rate_button, 'Position', [10 top 100 20]);
        set(a.margin_distribution_button, 'Position', [110 top 100 20]);
        set(a.add_10_button, 'Position', [210 top 100 20]);
        top = top - 30;
        set(a.add_learner_button, 'Position', [10 top 150 20]);
        set(a.enable_all_button, 'Position', [170 top 130 20]);
        top = top - 30;
        set(a.learner_scroll, 'Position', [310 20 20 top]);
        %Create new base learner components as necessary
        for i=1:a.adaboost.k_max
          if i > numel(a.learner_labels)
            a.learner_labels(i) = uicontrol('Style','text','String',['Learner ' num2str(i)], ...
              'BackgroundColor',a.bgc,'HorizontalAlignment','left', 'FontSize', 14,...
              'FontWeight', 'bold');
            a.learner_check(i) = uicontrol('Style', 'Checkbox', 'ButtonDownFcn', ...
              @(varargin)a.individual_learner(i), 'Callback', @(varargin)a.enable_learner(i));
            a.learner_weights(i) = uicontrol('Style','Slider','Min',-1,'Max',1);
            a.weight_labels(i) = uicontrol('Style','text','String','0',...
              'FontSize', 14, 'FontWeight', 'bold');
            listener = handle.listener(a.learner_weights(i),'ActionEvent',@(varargin)a.set_weight(i));
            setappdata(a.learner_weights(i), 'listener', listener);
          end
        end
        for i=1:a.first_learner-1
            set(a.learner_labels(i), 'Visible', 'off');
            set(a.learner_check(i), 'Visible', 'off');
            set(a.learner_weights(i), 'Visible', 'off');
            set(a.weight_labels(i), 'Visible', 'off');
        end
        for i=a.first_learner:a.adaboost.k_max
          if (top > 20)
            set(a.learner_labels(i), 'Visible', 'on');
            set(a.learner_check(i), 'Visible', 'on');
            set(a.learner_weights(i), 'Visible', 'on');
            set(a.weight_labels(i), 'Visible', 'on');
            set(a.learner_labels(i), 'Position',[10 top 110 20]);
            set(a.learner_check(i), 'Position',[120 top 20 20]);
            set(a.learner_weights(i), 'Position',[150 top 100 20]);
            set(a.weight_labels(i), 'Position',[260 top 40 20]);
          else
            set(a.learner_labels(i), 'Visible', 'off');
            set(a.learner_check(i), 'Visible', 'off');
            set(a.learner_weights(i), 'Visible', 'off');
            set(a.weight_labels(i), 'Visible', 'off');
          end
          top = top - 30;
        end
      catch exception
        %Buh
      end

    end

    %Called when the base learner type is changed. Reset the ensemble.
    function base_learner_change(a)
      learner_num = get(a.base_learner_popup, 'Value');
      a.base_learner = a.base_learners{learner_num};
      a.new_ensemble();
    end

    %Called when the data generating function is changed. Reset the
    %ensemble and generate new data.
    function data_func_change(a)
      data_num = get(a.data_func_popup, 'Value');
      a.data_func = a.data_funcs{data_num};
      a.random_data();
      a.new_ensemble();
    end

    %Create a new ensemble, and delete any lingering rubbish.
    function new_ensemble(a)
      for i=2:a.learner_count
        delete(a.learner_labels(i));
        delete(a.learner_check(i));
        delete(a.learner_weights(i));
        delete(a.weight_labels(i));
      end
      a.learner_labels = a.learner_labels(1);
      a.learner_check = a.learner_check(1);
      a.learner_weights = a.learner_weights(1);
      a.weight_labels = a.weight_labels(1);
      a.adaboost = Adaboost(boosting_demo.initial_size, a.base_learner);
      a.learner_count = 1;
      a.first_learner = 1;
      set(a.learner_scroll, 'max', 1);
      set(a.learner_scroll, 'Value', 0);
      a.adaboost.k_max = a.learner_count;
      a.adaboost.k_next = a.learner_count;
      a.learner_enabled = true;
      a.train_adaboost();
    end

    %When the learner scrollbar moves.
    function scroll_learners(a)

      a.first_learner = max(ceil(a.learner_count - get(a.learner_scroll, 'Value')), 1);
      a.do_layout();

    end

    %Plot an error against ensemble size graph in a separate window.
    function plot_error(a)

      preds = cell2mat(arrayfun(@(x)a.adaboost.learners{x}.test(a.inputs) .* a.outputs, ...
        1:a.learner_count, 'UniformOutput', 0));
      weighted_preds = preds .* repmat(a.pretend_weights .* a.learner_enabled, numel(a.outputs), 1);
      cumulative_margins = cumsum(weighted_preds, 2);
      g_preds = cell2mat(arrayfun(@(x)a.adaboost.learners{x}.test(a.g_inputs) .* a.g_outputs, ...
        1:a.learner_count, 'UniformOutput', 0));
      g_weighted_preds = g_preds .* repmat(a.pretend_weights .* a.learner_enabled, numel(a.g_outputs), 1);
      g_cumulative_margins = cumsum(g_weighted_preds, 2);
      figure(a.error_fig);
      set(a.error_fig, 'Name', 'Error Rates', 'NumberTitle', 'off');
      clf;
      hold on;
      plot(1:a.learner_count, mean(cumulative_margins < 0), 'LineWidth', 2);
      plot(1:a.learner_count, mean(g_cumulative_margins < 0), 'r', 'LineWidth', 2);
      hold off;
      a.plotting_error = true;

    end

    %Plot the margin distribution in a separate window.
    function plot_margins(a)

      preds = cell2mat(arrayfun(@(x)a.adaboost.learners{x}.test(a.inputs) .* a.outputs, ...
        1:a.learner_count, 'UniformOutput', 0));
      weighted_preds = preds .* repmat(a.pretend_weights .* a.learner_enabled, numel(a.outputs), 1);
      final_margins = sum(weighted_preds, 2);
      range = -1:0.01:1;
      figure(a.margin_fig);
      set(a.margin_fig, 'Name', 'Margin Distribution', 'NumberTitle', 'off');
      clf;
      plot(range, mean(repmat(final_margins, 1, numel(range)) < repmat(range, numel(final_margins), 1)))
      a.plotting_margin = true;

    end

    %Add 10 learners.
    function add_10(a)

      try
        i = 0;
        while (i < 10 && a.running)
          a.add_learner();
          i = i + 1;
        end
      catch
      end

    end

    %Add one learner.
    function add_learner(a)

      a.learner_count = a.learner_count + 1;
      set(a.learner_scroll, 'max', a.learner_count);
      set(a.learner_scroll, 'Value', a.learner_count - a.first_learner);
      a.adaboost.k_max = a.learner_count;
      a.adaboost.k_next = a.learner_count;
      a.learner_enabled(a.learner_count) = true;
      a.adaboost.train(a.inputs, a.outputs);
      a.pretend_weights = a.adaboost.learner_weights;
      new_predictions = a.adaboost.learners{a.learner_count}.test([a.x_grid(:) a.y_grid(:)]);
      a.predictions = [a.predictions new_predictions];
      a.example_weights = abs(a.adaboost.d_weights) * a.data_points * 25;
      a.do_layout();
      a.refresh_weights();
      a.refresh_enabled();
      a.redraw();

    end

    %Train the first learner in Adaboost.
    function train_adaboost(a)

       a.adaboost.train(a.inputs, a.outputs);
       a.learner_count = a.adaboost.k_max;
       a.learner_enabled = ones(1, a.learner_count);
       a.pretend_weights = a.adaboost.learner_weights';
       a.predictions = cell2mat(arrayfun(@(x)a.adaboost.learners{x}.test([a.x_grid(:) a.y_grid(:)]), ...
        1:a.learner_count, 'UniformOutput', 0));
       a.do_layout();
       a.refresh_weights();
       a.refresh_enabled();
       a.redraw();

    end

    %Set the weights back to Adaboost weights.
    function reset_weights(a)
      a.pretend_weights = a.adaboost.learner_weights;
      a.refresh_weights();
    end

    %Enable a base learner
    function enable_learner(a, i)
      a.learner_enabled(i) = ~a.learner_enabled(i);
      set(a.learner_check(i), 'Value', a.learner_enabled(i));
      a.redraw();
    end

    %Enable all base learners.
    function enable_all(a)
      for j=1:a.learner_count
        a.learner_enabled(j) = true;
        set(a.learner_check(j), 'Value', true);
      end
      a.reset_weights();
      a.redraw();
    end

    %Show one single base learner.
    function individual_learner(a, i)

      if strcmp(get(gcf,'SelectionType'), 'normal')
        a.enable_learner(i);
      else
        for j=1:a.learner_count
          if (j ~= i)
            a.learner_enabled(j) = false;
            set(a.learner_check(j), 'Value', false);
          else
            a.learner_enabled(j) = true;
            set(a.learner_check(j), 'Value', true);
          end
        end
        a.redraw();
      end
    end

    %Change the weight of a base learner.
    function set_weight(a, i)
      a.pretend_weights(i) = get(a.learner_weights(i), 'Value');
      a.refresh_weights();
      a.redraw();
    end

    %Redraw the data
    function redraw(a)

      try
        a.margins = reshape(a.predictions * (a.pretend_weights .* a.learner_enabled)', ...
          size(a.x_grid, 1), size(a.x_grid, 2));
        a.margins = a.margins ./ max(abs(a.margins(:)));

        refreshdata(a.margin_handle, 'caller');
        refreshdata(a.boundary_handle, 'caller');
        refreshdata(a.data_handle, 'caller');
        drawnow;
        if (a.plotting_error)
          a.plot_error();
        end
        if (a.plotting_margin)
          a.plot_margins();
        end
      catch
      end

    end

    %Update the base learner weights (normalise etc.)
    function refresh_weights(a)
      a.pretend_weights = a.pretend_weights / sum(abs(a.pretend_weights));
      for i=1:a.learner_count
        set(a.learner_weights(i), 'Value', a.pretend_weights(i));
        set(a.weight_labels(i), 'String', floor(a.data_points * a.pretend_weights(i) * sum(a.learner_enabled)) / a.data_points);
      end
    end

    %Update the checkboxes to show if learners are enabled.
    function refresh_enabled(a)
      for i=1:a.learner_count
        set(a.learner_check(i), 'Value', a.learner_enabled(i));
      end
    end

    %Generate some data from the required function.
    function random_data(a)

      [a.inputs a.outputs] = a.data_func(a.data_points);
      [a.g_inputs a.g_outputs] = a.data_func(a.data_points.*2);
      a.data_colors = [a.outputs == -1 zeros(a.data_points, 1) a.outputs == 1];
      a.example_weights = ones(a.data_points, 1) * a.data_points;

    end

  end

end


function [Ireg,O_trans,Spacing,M,B,F] = image_registration(Imoving,Istatic,Options)
% This function image_registration is the most easy way to register two
% 2D or 3D images both affine and nonrigidly.
%
% Features:
% - It can be used with images from different type of scans or modalities.
% - It uses both a rigid transform and a nonrigid b-spline grid transform.
% - It uses grid refinement
% - It can be used with images of different sizes.
% - The function will automaticaly detect if the images can be registered
% with the sum of squared pixel distance (SSD), or when mutual information
% must be used as image similarity measure.
%
% Note: Compile the c-files with compile_c_files to allow 3D, and for more
%   more speed in 2D.
%
% [Ireg,Grid,Spacing,M,B,F] = image_registration(Imoving,Istatic,Options);
%
% Inputs,
%   Imoving : The image which will be registerd
%   Istatic : The image on which Imoving will be registered
%   Options : Registration options, see help below
%
% Outputs,
%   Ireg : The registered moving image
%   Grid: The b-spline controlpoints, can be used to transform another
%       image in the same way: I=bspline_transform(Grid,I,Spacing);
%   Spacing: The uniform b-spline knot spacing
%	M : The affine transformation matrix
%   B : The backwards transformation fields  of the pixels in
%       x,y and z direction seen from the  static image to the moving image.
%		in 2D, Bx=B(:,:,1); By=B(:,:,2);
%   F : The (approximated) forward transformation fields of the pixels in
%       x, y and z direction seen from the moving image to the static image.
%		in 2D, Fx=F(:,:,1); Fy=F(:,:,2);
%       (See the function backwards2forwards)
%
% Options,
%   Options.Similarity: Similarity measure (error) used can be set to:
%               sd : Squared pixel distance
%               mi : Normalized (Local) mutual information
%               d, gd, gc, cc, pi, ld : see image_difference.m.
%   Options.Registration:
%               Rigid    : Translation, Rotation
%               Affine   : Translation, Rotation, Shear, Resize
%               NonRigid : B-spline grid based local registration
%               Both     : Nonrigid and Affine (Default)
%   Options.Penalty: Thin sheet of metal smoothness penalty, default in 2D 1e-3 ,
%				default in 3D, 1e-5
%               if set to zero the registration will take a shorter time, but
%               will give a more distorted transformation field.
%   Options.Interpolation: Linear (default) or Cubic, the final result is
%               always cubic interpolated.
%   Options.MaxRef : Maximum number of grid refinements steps (default 2)
%   Options.Grid: Initial B-spline controlpoints grid, if not defined is initalized
%               with an uniform grid. (Used for in example while
%               registering
%               a number of movie frames)
%   Options.Spacing: Spacing of initial B-spline grid in pixels 1x2 [sx sy] or 1x3 [sx sy sz]
%               sx, sy, sz must be powers of 2, to allow grid refinement.
%   Options.MaskMoving: Image which is transformed in the same way as Imoving and
%               is multiplied with the individual pixel errors
%               before calculation of the te total (mean) similarity
%               error. In case of measuring the mutual information similariy
%               the zero mask pixels will be simply discarded.
%   Options.MaskStatic: Also a Mask but is used  for Istatic
%   Options.Verbose: Display Debug information 0,1 or 2
%
% Corresponding points / landmarks Options,
%   Options.Points1: List N x 2 or N x 3 of landmarks x,y(,z) in Imoving image
%   Options.Points2: List N x 2 or N x 3 of landmarks x,y(,z) in Istatic image, in which
%                     every row correspond to the same row with landmarks
%                     in Points1.
%   Options.PStrength: List Nx1 with the error strength used between the
%                     corresponding points, (lower the strenght of the landmarks
%                     if less sure of point correspondence).
%
%
% Example 2D,
%   % Read two greyscale images of Lena
%   Imoving=imread('images/lenag1.png');
%   Istatic=imread('images/lenag2.png');
%
%   % Register the images
%   [Ireg,O_trans,Spacing,M,B,F] = image_registration(Imoving,Istatic);
%
%   % Show the registration result
%   figure,
%   subplot(2,2,1), imshow(Imoving); title('moving image');
%   subplot(2,2,2), imshow(Istatic); title('static image');
%   subplot(2,2,3), imshow(Ireg); title('registerd moving image');
%   % Show also the static image transformed to the moving image
%   Ireg2=movepixels(Istatic,F);
%   subplot(2,2,4), imshow(Ireg2); title('registerd static image');
%
%  % Show the transformation fields
%   figure,
%   subplot(2,2,1), imshow(B(:,:,1),[]); title('Backward Transf. in x direction');
%   subplot(2,2,2), imshow(F(:,:,2),[]); title('Forward Transf. in x direction');
%   subplot(2,2,3), imshow(B(:,:,1),[]); title('Backward Transf. in y direction');
%   subplot(2,2,4), imshow(F(:,:,2),[]); title('Forward Transf. in y direction');
%
% % Calculate strain tensors
%   E = strain(F(:,:,1),F(:,:,2));
% % Show the strain tensors
%   figure,
%   subplot(2,2,1), imshow(E(:,:,1,1),[-1 1]); title('Strain Tensors Exx');
%   subplot(2,2,2), imshow(E(:,:,1,2),[-1 1]); title('Strain Tensors Exy');
%   subplot(2,2,3), imshow(E(:,:,2,1),[-1 1]); title('Strain Tensors Eyx');
%   subplot(2,2,4), imshow(E(:,:,2,2),[-1 1]); title('Strain Tensors Eyy');
%
% Example 2D with Landmarks,
%
% % Read two images with triangles inside
%   Imoving=imread('images/landmarks1.png');
%   Istatic=imread('images/landmarks2.png');
%
% % Load the corresponding (matched) points
%   load('images/landmarks');
%
% % Register the images affine
%   Options = struct('Points1',Points1,'Points2',Points2,'PStrength',PStrength);
%   Ireg = image_registration(Imoving,Istatic,Options);
%
%   % Show the start images
%   figure, imshow(Imoving+Istatic,[]);
%   % Show the registration result
%   figure, imshow(Ireg+Istatic,[]);
%
% Example 2D Prostate,
% % Read two images
%   Imoving=im2double(imread('images/prostate1.png'));
%   Istatic=im2double(imread('images/prostate2.png'));
%
% % Use mutual information
%   Options.Similarity='mi';
% % Set grid smoothness penalty
%   Options.Penalty = 1e-3;
%   Ireg = image_registration(Imoving,Istatic,Options);
%  % Show the registration result
%   figure,
%   subplot(2,2,1), imshow(Imoving); title('moving image');
%   subplot(2,2,2), imshow(Istatic); title('static image');
%   subplot(2,2,3), imshow(Ireg); title('registerd moving image');
%
% Example 3D,
%   % add needed function paths
%   functiondir=which('image_registration.m');
%   addpath([functiondir(1:end-length('image_registration.m')) '/low_level_examples'])
%
%   % Get the volume data
%   [Imoving,Istatic]=get_example_data;
%
%   % Register the images
%   Ireg = image_registration(Imoving,Istatic);
%
%   % Show the results
%   showcs3(Imoving);
%   showcs3(Istatic);
%   showcs3(Ireg);
%
% Function is written by D.Kroon University of Twente (August 2010)

% add all needed function paths
add_function_paths;

% Disable warning
warning('off', 'MATLAB:maxNumCompThreads:Deprecated')

if(size(Imoving,3)>3), IS3D=true; else IS3D=false; end
% Check for presence of needed functions
if(IS3D), check_mexfiles(); end

% Process inputs
defaultoptions=struct('Similarity',[],'Registration','Both','Penalty',1e-3,'MaxRef',2,'Grid',[],'Spacing',[],'MaskMoving',[],'MaskStatic',[],'Verbose',2,'Points1',[],'Points2',[],'PStrength',[],'Interpolation','Linear','Scaling',[1 1]);
if(IS3D), defaultoptions.Penalty=1e-5; defaultoptions.Scaling=[1 1 1]; end
if(~exist('Options','var')), Options=defaultoptions;
else
    tags = fieldnames(defaultoptions);
    for i=1:length(tags),
        if(~isfield(Options,tags{i})), Options.(tags{i})=defaultoptions.(tags{i}); end
    end
    if(length(tags)~=length(fieldnames(Options))),
        warning('image_registration:unknownoption','unknown options found');
    end
end

% Set parameters
type=Options.Similarity;
O_trans=Options.Grid; Spacing=Options.Spacing;
MASKmoving=Options.MaskMoving; MASKstatic=Options.MaskStatic;
Points1=Options.Points1; Points2=Options.Points2; PStrength=Options.PStrength;

% Start time measurement
if(Options.Verbose>0), tic; end

% Convert the input images to double with range 0..1
if(IS3D)
    [Iclass,Imin,Imax,Imoving,Istatic]=images2doublesingle(Imoving,Istatic);
else
    [Iclass,Imin,Imax,Imoving,Istatic]=images2double(Imoving,Istatic);
end

% Resize the moving image to fit the static image
[Istatic,Imoving,MASKmoving]=images2samesize(Istatic,Imoving,MASKmoving,IS3D);

% Detect if the mutual information or pixel distance can be used as
% similarity measure. By comparing the histograms.
if(isempty(type)), type=check_image_modalities(Imoving,Istatic,Options); end

% Register the moving image affine to the static image
if(~strcmpi(Options.Registration(1),'N'))
    M=affine_registration(O_trans,Spacing,Options,Imoving,Istatic,MASKmoving,MASKstatic,type,Points1,Points2,PStrength,IS3D);
else M=[];
end

% Make the initial b-spline registration grid
[O_trans,Spacing,MaxItt]=Make_Initial_Grid(O_trans,Spacing,Options,Imoving,M,IS3D);

% Register the moving image nonrigid to the static image
if(strcmpi(Options.Registration(1),'N')||strcmpi(Options.Registration(1),'B'))
    [O_trans,Spacing]=nonrigid_registration(O_trans,Spacing,Options,Imoving,Istatic,MASKmoving,MASKstatic,type,Points1,Points2,PStrength,MaxItt,IS3D);
end

% Transform the input image with the found optimal grid.
if ( nargout<5 )
    Ireg=bspline_transform(O_trans,Imoving,Spacing,3);
else
    [Ireg,B]=bspline_transform(O_trans,Imoving,Spacing,3);
end

% Make the forward transformation fields from the backwards
if ( nargout>5 ), F=backwards2forwards(B); end

% Convert the double registered image to the class and range of the input images
Ireg=Back2OldRange(Ireg,Iclass,Imin,Imax);

% End time measurement
if(Options.Verbose>0), toc, end

function add_function_paths()
try
    functionname='image_registration.m';
    functiondir=which(functionname);
    functiondir=functiondir(1:end-length(functionname));
    addpath([functiondir '/functions'])
    addpath([functiondir '/functions_affine'])
    addpath([functiondir '/functions_nonrigid'])
catch me
    disp(me.message);
end

function [Istatic,Imoving,MASKmoving]=images2samesize(Istatic,Imoving,MASKmoving,IS3D)
% Resize the moving image to fit the static image
if(sum(size(Istatic)-size(Imoving))~=0)
    % Resize the moving image to fit the static image
    if(IS3D)
        Imoving=imresize3d(Imoving,[],size(Istatic),'cubic');
        if(~isempty(MASKmoving))
            MASKmoving=imresize3d(MASKmoving,[],size(Istatic),'cubic');
        end
    else
        Imoving = imresize(Imoving,[size(Istatic,1) size(Istatic,2)],'bicubic');
        if(~isempty(MASKmoving))
            MASKmoving = imresize(MASKmoving,[size(Istatic,1) size(Istatic,2)],'bicubic');
        end
    end
end

function [Iclass,Imin,Imax,Imoving,Istatic]=images2double(Imoving,Istatic)
% Store the class of the inputs
Iclass=class(Imoving);

% Convert the inputs to double
Imoving=double(Imoving);
Istatic=double(Istatic);
Imin=min(min(Istatic(:)),min(Imoving(:))); Imax=max(max(Istatic(:)),max(Istatic(:)));
Imoving=(Imoving-Imin)/(Imax-Imin);
Istatic=(Istatic-Imin)/(Imax-Imin);

function Ireg=Back2OldRange(Ireg,Iclass,Imin,Imax)
% Back to old image range
Ireg=Ireg*(Imax-Imin)+Imin;

% Set the class of output to input class
if(strcmpi(Iclass,'uint8')), Ireg=uint8(Ireg); end
if(strcmpi(Iclass,'uint16')), Ireg=uint16(Ireg); end
if(strcmpi(Iclass,'uint32')), Ireg=uint32(Ireg); end
if(strcmpi(Iclass,'int8')), Ireg=int8(Ireg); end
if(strcmpi(Iclass,'int16')), Ireg=int16(Ireg); end
if(strcmpi(Iclass,'int32')), Ireg=int32(Ireg); end
if(strcmpi(Iclass,'single')), Ireg=single(Ireg); end

function M=affine_registration(O_trans,Spacing,Options,Imoving,Istatic,MASKmoving,MASKstatic,type,Points1,Points2,PStrength,IS3D)
% Make smooth for fast affine registration
if(IS3D)
    ISmoving=imgaussian(Imoving,2.5,[10 10 10]);
    ISstatic=imgaussian(Istatic,2.5,[10 10 10]);
else
    ISmoving=imfilter(Imoving,fspecial('gaussian',[10 10],2.5));
    ISstatic=imfilter(Istatic,fspecial('gaussian',[10 10],2.5));
end

% Affine register the smoothed images to get the registration parameters
if(strcmpi(Options.Registration(1),'R'))
    if(Options.Verbose>0), disp('Start Rigid registration'); drawnow; end
    if(IS3D)
        x=[0 0 0 0 0 0];
    else
        % Parameter scaling of the Translation and Rotation
        scale=[1 1 1];
        % Set initial affine parameters
        x=[0 0 0];
    end
elseif(strcmpi(Options.Registration(1),'A'))
    if(Options.Verbose>0), disp('Start Affine registration'); drawnow; end
    % Parameter scaling of the Translation, Rotation, Resize and Shear
    if(IS3D)
        x=[0 0 0 0 0 0 1 1 1 0 0 0 0 0 0];
    else
        scale=[1 1 0.01 0.01 0.01 0.01 0.01];
        % Set initial affine parameters
        x=[0 0 0 1 1 0 0];
    end
elseif(strcmpi(Options.Registration(1),'B'))
    if(Options.Verbose>0), disp('Start Affine part of Non-Rigid registration'); drawnow; end
    % Parameter scaling of the Translation, Rotation, Resize and Shear
    if(IS3D)
        x=[0 0 0 0 0 0 1 1 1 0 0 0 0 0 0];
    else
        scale=[1 1 0.01 0.01 0.01 0.01 0.01];
        % Set initial affine parameters
        x=[0 0 0 1 1 0 0];
    end
else
    warning('image_registrations:unknownoption','unknown registration method');
end

if(Options.Interpolation(1)=='L'), interpolation_mode=0; else interpolation_mode=2; end

% Register Affine with 3 scale spaces
for refine_itt=1:3
    if(refine_itt==1)
        if(IS3D)
            ITmoving=imresize3d(ISmoving,0.25);
            ITstatic=imresize3d(ISstatic,0.25);
        else
            ITmoving=imresize(ISmoving,0.25);
            ITstatic=imresize(ISstatic,0.25);
        end
        Points1t=Points1*0.25; Points2t=Points2*0.25; PStrengtht=PStrength;
        if(IS3D)
            if(~isempty(MASKmoving)), ITMASKmoving = imresize3d(MASKmoving,0.25); else ITMASKmoving=[]; end
            if(~isempty(MASKstatic)), ITMASKstatic = imresize3d(MASKstatic,0.25); else ITMASKstatic=[]; end
        else
            if(~isempty(MASKmoving)), ITMASKmoving = imresize(MASKmoving,0.25); else ITMASKmoving=[]; end
            if(~isempty(MASKstatic)), ITMASKstatic = imresize(MASKstatic,0.25); else ITMASKstatic=[]; end
        end
    elseif(refine_itt==2)
        x(1:2)=x(1:2)*2;
        if(IS3D)
            ITmoving=imresize3d(ISmoving,0.5);
            ITstatic=imresize3d(ISstatic,0.5);
        else
            ITmoving=imresize(ISmoving,0.5);
            ITstatic=imresize(ISstatic,0.5);
        end
        Points1t=Points1*0.5; Points2t=Points2*0.5; PStrengtht=PStrength;
        if(IS3D),
            if(~isempty(MASKmoving)), ITMASKmoving = imresize3d(MASKmoving,0.5); else ITMASKmoving=[]; end
            if(~isempty(MASKstatic)), ITMASKstatic = imresize3d(MASKstatic,0.5); else ITMASKstatic=[]; end
        else
            if(~isempty(MASKmoving)), ITMASKmoving = imresize(MASKmoving,0.5); else ITMASKmoving=[]; end
            if(~isempty(MASKstatic)), ITMASKstatic = imresize(MASKstatic,0.5); else ITMASKstatic=[]; end
        end
    elseif(refine_itt==3)
        if(IS3D),
            x(1:3)=x(1:3)*2;
        else
            x(1:2)=x(1:2)*2;
        end
        ITmoving=Imoving;
        ITstatic=Istatic;
        ITMASKmoving = MASKmoving;
        ITMASKstatic = MASKstatic;
        Points1t=Points1; Points2t=Points2; PStrengtht=PStrength;
    end
    % Minimizer parameters
    % Use struct because expanded optimset is part of the Optimization Toolbox.
    if(IS3D)
        optim=struct('GradObj','on','GoalsExactAchieve',1,'Display','off','StoreN',10,'HessUpdate','lbfgs','MaxIter',100,'MaxFunEvals',1000,'TolFun',1e-7,'DiffMinChange',1e-5);
    else
        optim=struct('GradObj','on','GoalsExactAchieve',1,'Display','off','StoreN',10,'HessUpdate','lbfgs','MaxIter',100,'MaxFunEvals',1000,'TolFun',1e-7,'DiffMinChange',1e-3);
    end
    if(Options.Verbose>0), optim.Display='iter'; end
    if(IS3D)
        % Optimal scaling of values, to make the influence of every parameter in the
        % optimizer almost the same:
        [s_Trans, s_Rotation, s_Scale, s_Shear] = affine_parameter_scaling(size(ITmoving));
        if(strcmpi(Options.Registration(1),'R'))
            scale =[s_Trans s_Rotation];
        else
            if(strcmpi(type,'sd')),
                scale =[s_Trans s_Rotation s_Scale s_Shear];
            else
                scale =[s_Trans s_Rotation s_Scale/10 s_Shear/10];
            end
        end
    end

    % Scale the translation, resize and rotation parameters to scaled
    % optimizer values
    x=x./scale;
    % Find the Afffine deformation
    x=fminlbfgs(@(x)affine_registration_error(x,scale,ITmoving,ITstatic,type,O_trans,Spacing,ITMASKmoving,ITMASKstatic,Points1t,Points2t,PStrengtht,interpolation_mode),x,optim);
    % Scale the translation, resize and rotation parameters to the real values
    x=x.*scale;
end

if(strcmpi(Options.Registration(1),'R'))
    % Make the rigid transformation matrix
    if(IS3D)
        M=make_transformation_matrix(x(1:3),x(4:6));
    else
        M=make_transformation_matrix(x(1:2),x(3));
    end
else
    % Make the affine transformation matrix
    if(IS3D)
        M=make_transformation_matrix(x(1:3),x(4:6),x(7:9),x(10:15));
    else
        M=make_transformation_matrix(x(1:2),x(3),x(4:5),x(6:7));
    end
end

function type=check_image_modalities(Imoving,Istatic,Options)
% Detect if the mutual information or pixel distance can be used as
% similarity measure. By comparing the histograms.
Hmoving = hist(Imoving(:),(1/60)*[0.5:60])./numel(Imoving);
Hstatic = hist(Istatic(:),(1/60)*[0.5:60])./numel(Istatic);
if(sum(abs(Hmoving(:)-Hstatic(:)))>0.25),
    type='mi';
    if(Options.Verbose>0), disp('Multi Modalities, Mutual information is used'); drawnow; end
else
    type='sd';
    if(Options.Verbose>0), disp('Same Modalities, Pixel Distance is used'); drawnow; end
end

function [O_trans,Spacing,MaxItt]=Make_Initial_Grid(O_trans,Spacing,Options,Imoving,M,IS3D)
if(isempty(O_trans)),

    if(isempty(Options.Spacing))
        if(IS3D)
            % Calculate max refinements steps
            MaxItt=min(floor(log2(size(Imoving)/4)));

            % set b-spline grid spacing in x,y and z direction
            Spacing=[2^MaxItt 2^MaxItt 2^MaxItt];
        else
            % Calculate max refinements steps
            MaxItt=min(floor(log2([size(Imoving,1) size(Imoving,2)]/4)));

            % set b-spline grid spacing in x and y direction
            Spacing=[2^MaxItt 2^MaxItt];
        end
    else
        % set b-spline grid spacing in x and y direction
        Spacing=round(Options.Spacing);
        t=Spacing; MaxItt=0; while((nnz(mod(t,2))==0)&&(nnz(t<8)==0)), MaxItt=MaxItt+1; t=t/2; end
    end
    % Make the Initial b-spline registration grid
    if(IS3D)
        O_trans=make_init_grid(Spacing,size(Imoving),M);
    else
        if(strcmpi(Options.Registration(1),'N'))
            O_trans=make_init_grid(Spacing,[size(Imoving,1) size(Imoving,2)]);
        else
            O_trans=make_init_grid(Spacing,[size(Imoving,1) size(Imoving,2)],M);
        end
    end
else
    MaxItt=0;
    TestSpacing=Spacing;
    while(mod(TestSpacing,2)==0), TestSpacing=TestSpacing/2; MaxItt=MaxItt+1; end

    if(IS3D)
        % Calculate center of the image
        mean=size(Imoving)/2;
        % Make center of the image coordinates 0,0
        xd=O_trans(:,:,:,1)-mean(1); yd=O_trans(:,:,:,2)-mean(2); zd=O_trans(:,:,:,3)-mean(3);
        % Calculate the rigid transformed coordinates
        O_trans(:,:,:,1) = mean(1) + M(1,1) * xd + M(1,2) *yd + M(1,3) *zd + M(1,4)* 1;
        O_trans(:,:,:,2) = mean(2) + M(2,1) * xd + M(2,2) *yd + M(2,3) *zd + M(2,4)* 1;
        O_trans(:,:,:,3) = mean(3) + M(3,1) * xd + M(3,2) *yd + M(3,3) *zd + M(3,4)* 1;
    else
        if(~strcmpi(Options.Registration(1),'N'))
            % Calculate center of the image
            mean=size(Imoving)/2;
            % Make center of the image coordinates 0,0
            xd=O_trans(:,:,1)-mean(1); yd=O_trans(:,:,2)-mean(2);
            % Calculate the affine transformed coordinates
            O_trans(:,:,1) = mean(1) + M(1,1) * xd + M(1,2) *yd + M(1,3) * 1;
            O_trans(:,:,2) = mean(2) + M(2,1) * xd + M(2,2) *yd + M(2,3) * 1;
        end
    end
end
% Limit refinements steps to user input
if(Options.MaxRef<MaxItt), MaxItt=Options.MaxRef; end

function [O_trans,Spacing]=nonrigid_registration(O_trans,Spacing,Options,Imoving,Istatic,MASKmoving,MASKstatic,type,Points1,Points2,PStrength,MaxItt,IS3D)
% Non-rigid b-spline grid registration
if(Options.Verbose>0), disp('Start non-rigid b-spline grid registration'); drawnow; end

if(IS3D),
    if (Options.Verbose>0), disp(['Current Grid size : ' num2str(size(O_trans,1)) 'x' num2str(size(O_trans,2)) 'x' num2str(size(O_trans,3)) ]); drawnow; end
else
    if (Options.Verbose>0), disp(['Current Grid size : ' num2str(size(O_trans,1)) 'x' num2str(size(O_trans,2))]); drawnow; end
end

% set registration options.
options.type=type;
options.penaltypercentage=Options.Penalty;
options.interpolation=Options.Interpolation;
options.scaling=Options.Scaling;
options.verbose=false;

% Enable forward instead of central gradient incase of error measure is pixel distance
if(strcmpi(type,'sd')), options.centralgrad=false; end

% Make smooth images for fast registration without local minimums
if(IS3D)
    Hsize=round(0.1667*(size(Istatic,1)/size(O_trans,1)+size(Istatic,2)/size(O_trans,2)+size(Istatic,3)/size(O_trans,3)));
    ISmoving=imgaussian(Imoving,Hsize/5,[Hsize Hsize Hsize]);
    ISstatic=imgaussian(Istatic,Hsize/5,[Hsize Hsize Hsize]);
else
    Hsize=round(0.25*(size(Imoving,1)/size(O_trans,1)+size(Istatic,2)/size(O_trans,2)));
    ISmoving=imfilter(Imoving,fspecial('gaussian',[Hsize Hsize],Hsize/5));
    ISstatic=imfilter(Istatic,fspecial('gaussian',[Hsize Hsize],Hsize/5));
end
resize_per=2^(MaxItt-1);

% Modified by Yuan Zhou on 3.7.2017
resize_per = max(resize_per,1);
% Reshape O_trans from a matrix to a vector.
sizes=size(O_trans); O_trans=O_trans(:);

% Resize the mask to the image size used in the registration
if(IS3D)
    if(~isempty(MASKmoving)), MASKmovingsmall=imresize3d(MASKmoving,1/resize_per);  else MASKmovingsmall=[]; end
    if(~isempty(MASKstatic)), MASKstaticsmall=imresize3d(MASKstatic,1/resize_per);  else MASKstaticsmall=[]; end
else
    if(~isempty(MASKmoving)), MASKmovingsmall=imresize(MASKmoving,1/resize_per);  else MASKmovingsmall=[]; end
    if(~isempty(MASKstatic)), MASKstaticsmall=imresize(MASKstatic,1/resize_per);  else MASKstaticsmall=[]; end
end

% Use struct because expanded optimset is part of the Optimization Toolbox.
if(IS3D)
    optim=struct('GradObj','on','GoalsExactAchieve',0,'StoreN',10,'HessUpdate','lbfgs','Display','off','MaxIter',100,'DiffMinChange',0.03,'DiffMaxChange',1,'MaxFunEvals',1000,'TolX',0.05,'TolFun',1e-8);
else
    optim=struct('GradObj','on','GoalsExactAchieve',0,'StoreN',10,'HessUpdate','lbfgs','Display','off','MaxIter',100,'DiffMinChange',0.001,'DiffMaxChange',1,'MaxFunEvals',1000,'TolX',0.005,'TolFun',1e-8);
end
if(Options.Verbose>0), optim.Display='iter'; end

% Start the b-spline nonrigid registration optimizer
if(IS3D)
    ISmoving_small=imresize3d(ISmoving,1/resize_per,[],'linear');
    ISstatic_small=imresize3d(ISstatic,1/resize_per,[],'linear');
else
    ISmoving_small=imresize(ISmoving,1/resize_per);
    ISstatic_small=imresize(ISstatic,1/resize_per);
end
Spacing_small=Spacing/resize_per;
Points1_small=Points1/resize_per;
Points2_small=Points2/resize_per;
PStrength_small=PStrength;
O_trans = resize_per*fminlbfgs(@(x)bspline_registration_gradient(x,sizes,Spacing_small,ISmoving_small,ISstatic_small,options,MASKmovingsmall,MASKstaticsmall,Points1_small,Points2_small,PStrength_small),O_trans/resize_per,optim);

% Reshape O_trans from a vector to a matrix
O_trans=reshape(O_trans,sizes);

for refine_itt=1:MaxItt
    if (Options.Verbose>0), disp('Registration Refinement'); drawnow; end

    % Refine the b-spline grid
    [O_trans,Spacing]=refine_grid(O_trans,Spacing,size(Imoving));

    % Make smooth images for fast registration without local minimums
    if(IS3D)
        Hsize=round(0.1667*(size(ISmoving,1)/size(O_trans,1)+size(ISstatic,2)/size(O_trans,2)+size(ISstatic,3)/size(O_trans,3)));
        ISmoving=imgaussian(Imoving,Hsize/5,[Hsize Hsize Hsize]);
        ISstatic=imgaussian(Istatic,Hsize/5,[Hsize Hsize Hsize]);
    else
        Hsize=round(0.25*(size(ISmoving,1)/size(O_trans,1)+size(ISstatic,2)/size(O_trans,2)));
        ISmoving=imfilter(Imoving,fspecial('gaussian',[Hsize Hsize],Hsize/5));
        ISstatic=imfilter(Istatic,fspecial('gaussian',[Hsize Hsize],Hsize/5));
    end
    resize_per=2^(MaxItt-1-refine_itt);

    % No smoothing in last registration step
    if(IS3D)
        if(refine_itt==MaxItt), ISmoving=Imoving; ISstatic=Istatic; optim.TolX=0.05; resize_per=1; end
    else
        if(refine_itt==MaxItt), ISmoving=Imoving; ISstatic=Istatic; resize_per=1; optim.TolX = 0.03; end
    end

    if(IS3D),
        if (Options.Verbose>0), disp(['Current Grid size : ' num2str(size(O_trans,1)) 'x' num2str(size(O_trans,2)) 'x' num2str(size(O_trans,3)) ]); drawnow; end
    else
        if (Options.Verbose>0), disp(['Current Grid size : ' num2str(size(O_trans,1)) 'x' num2str(size(O_trans,2))]); drawnow; end
    end

    % Reshape O_trans from a matrix to a vector.
    sizes=size(O_trans); O_trans=O_trans(:);

    % Resize the mask to the image size used in the registration
    if(IS3D)
        if(~isempty(MASKmoving)), MASKmovingsmall=imresize3d(MASKmoving,1/resize_per);  else MASKmovingsmall=[]; end
        if(~isempty(MASKstatic)), MASKstaticsmall=imresize3d(MASKstatic,1/resize_per);  else MASKstaticsmall=[]; end
    else
        if(~isempty(MASKmoving)), MASKmovingsmall=imresize(MASKmoving,1/resize_per);  else MASKmovingsmall=[]; end
        if(~isempty(MASKstatic)), MASKstaticsmall=imresize(MASKstatic,1/resize_per);  else MASKstaticsmall=[]; end
    end

    % Start the b-spline nonrigid registration optimizer
    if(IS3D)
        ISmoving_small=imresize3d(ISmoving,1/resize_per,[],'linear');
        ISstatic_small=imresize3d(ISstatic,1/resize_per,[],'linear');
    else
        ISmoving_small=imresize(ISmoving,1/resize_per);
        ISstatic_small=imresize(ISstatic,1/resize_per);
    end
    Spacing_small=Spacing/resize_per;
    Points1_small=Points1/resize_per;
    Points2_small=Points2/resize_per;
    PStrength_small=PStrength;
    O_trans = resize_per*fminlbfgs(@(x)bspline_registration_gradient(x,sizes,Spacing_small,ISmoving_small,ISstatic_small,options,MASKmovingsmall,MASKstaticsmall,Points1_small,Points2_small,PStrength_small),O_trans/resize_per,optim);

    % Reshape O_trans from a vector to a matrix
    O_trans=reshape(O_trans,sizes);
end

function check_mexfiles()
if(exist('bspline_transform_3d_double','file')~=3)
    error('bspline_transform_3d_double mex function not found, compile the c-file');
end
if(exist('bspline_transform_3d_single','file')~=3)
    error('bspline_transform_3d_single mex function not found, compile the c-file');
end

function [Iclass,Imin,Imax,Imoving,Istatic]=images2doublesingle(Imoving,Istatic)
% Store the class of the inputs
Iclass=class(Imoving);

% Convert uint8, uint32 etc. to single.
if(~strcmpi(Iclass,'single')&&~strcmpi(Iclass,'double'))
    Imoving=single(Imoving);
    Istatic=single(Istatic);
end
Imin=min(min(Istatic(:)),min(Imoving(:))); Imax=max(max(Istatic(:)),max(Istatic(:)));
Imoving=(Imoving-Imin)/(Imax-Imin);
Istatic=(Istatic-Imin)/(Imax-Imin);


function exact = p01_exact ( )

%*****************************************************************************80
%
%% P01_EXACT returns the exact integral for problem 1.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    04 November 2009
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, real EXACT, the value of the integral.
%
  exact = exp ( 1.0 ) - 1.0;

  return
end


function areaBB = computeArea(bb)
%computes area of the bb=[xmin ymin xmax ymax]

if ((bb(1) > bb(3)) || (bb(2) > bb(4)))
    areaBB = 0;
else
    areaBB = (bb(3) - bb(1) + 1) * (bb(4) - bb(2) + 1);
end


function stokes_gnuplot ( header, n, x, y, u, v, s )

%*****************************************************************************80
%
%% STOKES_GNUPLOT writes the Stokes vector field to files for GNUPLOT.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    24 January 2015
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, string HEADER, a header to be used to name the files.
%
%    Input, integer N, the number of evaluation points.
%
%    Input, real X(N), Y(N), the coordinates of the evaluation points.
%
%    Input, real U(N), V(N), the velocity components.
%
%    Input, real S, a scale factor for the velocity vectors.
%

%
%  Write the data file.
%
  data_filename = strcat ( header, '_data.txt' );

  data_unit = fopen ( data_filename, 'wt' );

  for i = 1 : n
    fprintf ( data_unit, '  %10.4f  %10.4f  %10.4f  %10.4f\n', x(i), y(i), s * u(i), s * v(i) );
  end

  fclose ( data_unit );

  fprintf ( 1, '\n' );
  fprintf ( 1, '  Data written to "%s".\n', data_filename );
%
%  Write the command file.
%
  command_filename = strcat ( header, '_commands.txt' );
  plot_filename = strcat ( header, '.png' );

  command_unit = fopen ( command_filename, 'wt' );

  fprintf ( command_unit, '#  %s\n', command_filename );
  fprintf ( command_unit, '#\n' );
  fprintf ( command_unit, 'set term png\n' );
  fprintf ( command_unit, 'set output "%s"\n', plot_filename );
  fprintf ( command_unit, '#\n' );
  fprintf ( command_unit, '#  Add titles and labels.\n' );
  fprintf ( command_unit, '#\n' );
  fprintf ( command_unit, 'set xlabel "<--- X --->"\n' );
  fprintf ( command_unit, 'set ylabel "<--- Y --->"\n' );
  fprintf ( command_unit, 'set title "Stokes velocity field"\n' );
  fprintf ( command_unit, 'unset key\n' );
  fprintf ( command_unit, '#\n' );
  fprintf ( command_unit, '#  Add grid lines.\n' );
  fprintf ( command_unit, '#\n' );
  fprintf ( command_unit, 'set grid\n' );
  fprintf ( command_unit, 'set size ratio -1\n' );
  fprintf ( command_unit, '#\n' );
  fprintf ( command_unit, '#  Timestamp the plot.\n' );
  fprintf ( command_unit, '#\n' );
  fprintf ( command_unit, 'set timestamp\n' );
  fprintf ( command_unit, 'plot "%s" using 1:2:3:4 with vectors \\\n', data_filename );
  fprintf ( command_unit, '  head filled lt 2 linecolor rgb "blue"\n' );
  fprintf ( command_unit, 'quit\n' );

  fclose ( command_unit );

  fprintf ( 1, '  Commands written to "%s".\n', command_filename );

  return
end


function [W,G,IM,C] = remesh_planar_patches(V,F,varargin)
  % REMESH_PLANAR_PATCHES  Find nearly planar patches and retriangulate them.
  %   (V,F) should probably not be self-intersecting, at least not near the
  %   planar patches. This will attempt to maintain non-manifold edges
  %   (untested).
  %
  % [W,G] = remesh_planar_patches(V,F)
  % [W,G,IM,C] = remesh_planar_patches(V,F,'ParameterName',ParameterValue,...)
  %
  % Inputs:
  %   V  #V by 3 list of vertices
  %   F  #F by 3 list of triangle indices
  %   Optional:
  %     'Force' followed by whether to remesh even if it will not improve
  %        the number of vertices {false}.
  %     'MinSize' followed by minimum number of facets in group to consider for
  %       remeshing {4}
  %     'MinDeltaAngle' followd by minimum change in angle between neighboring
  %     facets to be considered co-planar {pi-1e-5}
  %     'Except' followed by list of faces not to remesh
  %
  % Outputs:
  %   W  #W by 3 list of output mesh positions
  %   G  #G by 3 list of output triangle indices
  %   IM  indices from final remove_unreferenced
  %   C  #F list of patch indices
  %

  % force remesh even if there are no internal vertices to remove
  force_remesh = false;
  % Minmimum angle for two neighboring facets to be considered coplanar
  % in radians
  min_delta_angle = pi-1e-5;
  %min_delta_angle = pi-1e-3;
  min_size = 4;
  triangle_flags = '';
  except = [];
  % Map of parameter names to variable names
  params_to_variables = containers.Map( ...
    {'Force','MinSize','MinDeltaAngle','TriangleFlags','Except'}, ...
    {'force_remesh','min_size','min_delta_angle','triangle_flags','except'});
  v = 1;
  while v <= numel(varargin)
    param_name = varargin{v};
    if isKey(params_to_variables,param_name)
      assert(v+1<=numel(varargin));
      v = v+1;
      % Trick: use feval on anonymous function to use assignin to this workspace
      feval(@()assignin('caller',params_to_variables(param_name),varargin{v}));
    else
      error('Unsupported parameter: %s',varargin{v});
    end
    v=v+1;
  end

  % The right way to deal with them would be to make a list of feature edges
  % (aka "exterior edges") on the original mesh and be sure these end up in any
  % planar pataches. They would include the patch boundaries and any other
  % internal non-manifold edges.
  NME = nonmanifold_edges(F);
  C = planar_patches(V,F,'MinDeltaAngle',min_delta_angle,'Except',except);

  [UC,~,ic] = unique(C);
  ucounts = histc(C,UC);
  counts = ucounts(ic);

  %tsurf(F,V,'CData',C);
  %axis equal;
  %drawnow;

  W = V;
  G = F(counts<=min_size,:);
  % loop over connected components
  %for c = reshape(UC(ucounts>min_size),1,[])
  for c = 1:max(C)
    if ucounts(c) > min_size
      Fc = F(C==c,:);
      assert(size(Fc,1)>min_size);
      %S = statistics(V,Fc,'Fast',true);
      %% Not dealing with holes yet
      %if S.num_boundary_loops > 1
      %  warning('Skipping high-genus planar patch...');
      %  G = [G;Fc];
      %  continue;
      %end
      %assert(S.num_boundary_loops == 1);
      uf = unique(Fc);
      % Fit plane to all points
      [~,A] = affine_fit(V(uf,:));
      % Only keep outline points
      O = outline(Fc);

      % Non manifold edges on this patch
      NMEc = NME(all(ismember(NME,uf),2),:);
      E = [O;NMEc];

      uo = unique(E);
      % Nothing to gain by remeshing
      if ~force_remesh && numel(uo) == numel(uf)
        G = [G;Fc];
        continue;
      end
      % only boundary edges projected to plane
      Vuo = V(uo,:)*A;
      J = 1:size(V,1);
      J(uo) = 1:numel(uo);
      % Remap E to Vu
      O = J(O);
      E = J(E);

      % THIS IS INSANELY SLOW
      % DT = delaunayTriangulation(Vuo(:,1),Vuo(:,2));
      % DT.Constraints = E;
      % Gc = DT.ConnectivityList;
      % Triangle is way faster...
      [Wuo,Gc] = triangle(Vuo,E,[],'Quiet','Flags',triangle_flags);
      %tsurf(Gc,Wuo);
      %input('');
      assert(size(Gc,1) >= 1);
      assert(size(Wuo,1) >= size(Vuo,1));
      % easier to remove holes post hoc than pass hole positions to triangle
      AE = adjacency_matrix(E);
      [~,CE] = conncomp(AE);
      if max(CE) > 1
        w = winding_number(Vuo,O,barycenter(Vuo,Gc));
        % should only be 1s and 0s
        Gc = Gc(abs(w)>0.1,:);
      end

      % remap Gc to V
      uo = [uo;(size(W,1)+(1:(size(Wuo,1)-size(Vuo,1))))'];
      Wnew = Wuo((size(Vuo,1)+1):end,:);
      VA = V*A;
      %tsurf(Fc,VA);
      %hold on;
      %scatter(Wnew(:,1),Wnew(:,2));
      %hold off;
      %pause

      I = in_element(VA,Fc,Wnew);
      [~,I] = find(I);
      assert(numel(I) == size(Wnew,1));
      B = barycentric_coordinates( ...
        Wnew,VA((Fc(I,1)),:),VA((Fc(I,2)),:),VA((Fc(I,3)),:));
      Wnew = sum(bsxfun(@times, permute(B,[1 3 2]), ...
        cat(3,V(Fc(I,1),:),V(Fc(I,2),:),V(Fc(I,3),:))),3);
      W = [W;Wnew];
      Gc = uo(Gc);

      %tsurf(Gc,W);
      %pause
      % old mean normal
      oldN = mean(normalizerow(normals(V,Fc)));
      N = mean(normalizerow(normals(W,Gc)));
      flip  = dot(oldN,N) < 0;
      if flip
        Gc = fliplr(Gc);
      end
      %tsurf(Gc,W);
      %input('');
      G = [G;Gc];
      assert(max(G(:))<=size(W,1));
    end
  end

  [W,IM] = remove_unreferenced(W,G);
  G = IM(G);

end


%==============================================================================
% This code is part of the Matlab-based toolbox
% FAIR - Flexible Algorithms for Image Registration.
% For details see
% - https://github.com/C4IR and
% - http://www.siam.org/books/fa06/
%==============================================================================
%
% Tutorial for FAIR:  How to use FAIRplots
%
% - loads (or generates data) based on E9_Hands_MLIR_SSD_mbElas
% - calls FAIRplotswith all options
%
% FAIRplots('clear');                  - clear the function:
% FAIRplots('reset','mode','PIR');     - reset and set mode to parametric image registration
% FAIRplots('init',para);              - initialize (T,R,omega,m,)
% FAIRplots('stop',para);              - display the stopping configuration
% FAIRplots('start',para);             - display the start configuration
% FAIRplots(k,para);                   - display the k'th configuration
%==============================================================================

clear; help(mfilename);

tempFile = fullfile(FAIRpath,'temp',[mfilename,'.mat'])
if ~exist(tempFile,'file'),

  % generate some dummy data to be visualized using FAIRplots
  % load data, initialize image viewer, interpolator, transformation
  E9_Hands_MLIR_SSD_mbElas
  viewImage('reset','viewImage','viewImage2D','colormap','gray(256)');
  imgModel('reset','imgModel','linearInter');
  trafo('reset','trafo','affine2D');

  % shortcurs for transformed image
  X = getCellCenteredGrid(omega,m);
  Rc = imgModel(dataR,omega,X);
  Tc = @(Y)  imgModel(dataT,omega,Y);
  Dc = @(Tc) SSD(Tc,Rc,omega,m);

  % compute the stopping, starting and final values
  builtin('clear','center')
  wStop = trafo('w0');
  Ystop = trafo(wStop,X);
  Tstop = Tc(Ystop);
  Y0    = trafo(wc,X);
  yOpt  = center(yc,m);
  T0    = Tc(Y0);
  Topt  = Tc(yOpt);
  save(tempFile,'dataT','dataR','Tc','Rc','omega','m','Tstop',...
      'Ystop','T0','Y0','Topt','yOpt')
  close all
end;
load(tempFile)

Dc = @(Tc) SSD(Tc,Rc,omega,m);
viewImage('reset','viewImage','viewImage2D','colormap',bone(256),'axis','off');
imgModel('reset','imgModel','linearInter');
trafo('reset','trafo','affine2D');

% reset FAIRplots to have a fresh start
FAIRplots('clear');

% setup the plotting functionality, no plots yet
FAIRplots('reset','mode','PIR')%mfilename);

% initialize the plots, show T and R
FAIRplots('init',struct('Tc',dataT,'Rc',dataR,'omega',omega,'m',m));
FAIRpause(2);

% show the stopping values
para  = struct('Tc',Tstop,'Rc',Rc,'omega',omega,'m',m,'yc',Ystop,'Jc',Dc(Tstop));
FAIRplots('stop',para);
FAIRpause(2);

% show the starting values
para.Tc = T0; para.yc = Y0; para.Jstop = para.Jc; para.Jc = Dc(T0);
FAIRplots('start',para);
FAIRpause(2);

% show the final values
para.Tc = Topt; para.yc = yOpt; para.Jc = Dc(Topt);
para.normdY = norm(yOpt-Y0)/norm(Ystop);
FAIRplots(1,para);
%==============================================================================


function [vw_nuisance, vw_nuisance_comps] = canlab_extract_ventricle_wm_timeseries(mask_image_dir, imgs, varargin)

% function [nuisance, nuisance_comps] = canlab_extract_ventricle_wm_timeseries(mask_image_dir)
%
% This function extracts data from ventricles and white matter for each
% time series. You can include vw_nuisance in your nuisance matrix in the
% first level model estimation.
%
% output: "vw_nuisance" returns the MEAN ventricle and white matter signal
%         1st column: ventricle signal, 2nd column: white matter signal,
%         rows: time series
%
%         "vw_nuisance_comps" returns first 5 components from PCA.
%
% input:
% mask_image_dir: a directory that has mask files for ventricle and white matter.
%        You can use "canlab_create_wm_ventricle_masks.m" to get these mask files.
%        The mask file names must be "ventricles.img' and 'white_matter.img'.
%        e.g) mask_image_dir = fullfile(subjdir, 'Structural/SPGR');
%
% imgs: image file names that you want to extract data from.
%        e.g) imgs = filenames(fullfile(subjdir, 'Functional/Preprocessed/run1/wra*nii'), 'absolute', 'char');
%
% option (varargin): 'noplot' - the default setting is plotting output
%                    variables. You can use 'noplot' option if you don't want the plot.
%
% 5/4/2012 by Tor Wager and Wani Woo

%% --------------------------------------------
% Extract data from ventricles
% ---------------------------------------------

doplot = true;

wh = strcmp(varargin, 'noplot');
if any(wh), doplot = false; varargin(wh) = []; end

vmask = filenames(fullfile(mask_image_dir, 'ventricles.img'), 'char', 'absolute');
wmask = filenames(fullfile(mask_image_dir, 'white_matter.img'), 'char', 'absolute');

vdat = fmri_data(imgs, vmask);

vw_nuisance(:, 1) = mean(vdat.dat, 1)'; % avg across voxels

% save the first 5 components

[~, nuisance_comps] = pca(vdat.dat', 'econ');

vw_nuisance_comps = nuisance_comps(:, 1:5);

% ---------------------------------------------
% Extract data from white matter
% ---------------------------------------------

wdat = fmri_data(imgs, wmask);

vw_nuisance(:, 2) = mean(wdat.dat, 1)'; % avg across voxels

% save the first 5 components

[~, n2] = pca(wdat.dat', 'econ');

vw_nuisance_comps = [vw_nuisance_comps n2(:, 1:5)];

%% plot

if doplot

    create_figure('vw_nuisance', 3, 1)

    plot(zscore(vw_nuisance));
    title('averages (z-scores)')

    subplot(3, 1, 2)

    plot(vw_nuisance_comps(:, 1:5))
    title('ventricle components')

    subplot(3, 1, 3)

    plot(vw_nuisance_comps(:, 6:10))
    title('white matter components')
end

return


% PRTBRVMVB - PRT BRV Multivariate Binary Observation Model

classdef prtBrvMvb < prtBrv & prtBrvVbOnline & prtBrvVbMembershipModel & prtBrvVbOnlineMembershipModel

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % Properties required by prtAction
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    properties (SetAccess = private)
        name = 'Multi-varite Binary Bayesian Random Variable';
        nameAbbreviation = 'BRVMVB';
    end

    properties (SetAccess = protected)
        isSupervised = false;
        isCrossValidateValid = true;
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % Methods for prtBrv
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    methods

        function self = estimateParameters(self, x)
            self = conjugateUpdate(self, self, x);
        end

        function y = predictivePdf(self, x)
            %%%% FIXME
            % The true predictive here is a product of beta-binomials
            % Since that isn't implemented yet we use the average
            % variational loglikelihood

            y = conjugateVariationalAverageLogLikelihood(self, x);
        end

        function val = getNumDimensions(self)
            val = length(self.model.countOfOnes);
        end

        function self = initialize(self, x)
            x = self.parseInputData(x);
            if ~self.model.isValid
                self.model = self.model.defaultParameters(size(x,2));
            end
        end

        % Optional methods
        %------------------------------------------------------------------
        function kld = conjugateKld(obj, priorObj)
            betaKlds = zeros(1,length(obj.model.countOfOnes));
            for iDim = 1:length(obj.model.countOfOnes)
                betaKlds(iDim) = prtRvUtilDirichletKld([obj.model.countOfZeros(iDim) obj.model.countOfOnes(iDim)],[priorObj.model.countOfZeros(iDim) priorObj.model.countOfOnes(iDim)]);
            end

            kld = sum(betaKlds);
        end

        function s = posteriorMeanStruct(obj)
            s.probabilities = obj.model.countOfOnes./(obj.model.countOfOnes + obj.model.countOfZeros);
        end

        function plotCollection(objs,colors)

            nComponents = length(objs);

            if nargin < 2
                colors = prtPlotUtilClassColors(nComponents);
            end

            countOfOnesMat = cell2mat(arrayfun(@(c)c.model.countOfOnes,objs,'uniformOutput',false));
            countOfZerosMat = cell2mat(arrayfun(@(c)c.model.countOfZeros,objs,'uniformOutput',false));

            probMat = countOfOnesMat ./ (countOfOnesMat + countOfZerosMat);

            h = plot(probMat');

            for iLine = 1:length(h)
                set(h(iLine),'color',colors(iLine,:));
            end
            title('Posterior Mean Probabilities')
            ylim([0 1])
            xlim([1 size(probMat,2)])

        end

        function val = plotLimits(self)
            val = [1 self.nDimensions 0 1];
        end
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % Methods for prtBrvVb
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    methods
        function [self, training] = vbBatch(self,x)
            % Since we are purely conjugate we actually don't need vbBatch
            % However we must implement it.
            self = conjugateUpdate(self,x);
            training = struct([]);
        end
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % Methods for prtBrvMembershipModel
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    methods

        function [phiMat, priorObjs] = collectionInitialize(objs, priorObjs, X) % Vector of objects

            learningInitialMembershipFactor = objs(1).initFudgeFactor;

            [classMeans,kmMembership] = prtUtilKmeans(X,length(objs),'handleEmptyClusters','random','distanceMetricFn',@prtDistanceHamming,'maxIterations',100,'logicalMeans',true); %#ok<ASGLU>

            phiMat = zeros(size(X,1),length(objs));
            for iComp = 1:length(objs)
                phiMat(kmMembership == iComp, iComp) = learningInitialMembershipFactor;
            end
            phiMat(phiMat==0) = (1-learningInitialMembershipFactor)./(length(objs)-1);

            % We should normalize this just in case the
            % learningInitialMembershipFactor was set poorly
            phiMat = bsxfun(@rdivide,phiMat,sum(phiMat,2));

        end

        function obj = weightedConjugateUpdate(obj, priorObj, x, weights)

            x = obj.parseInputData(x);
            priorObj = priorObj.initialize(x);

            if nargin < 4 || isempty(weights)
                weights = ones(size(x,1),1);
            end

            obj.model.countOfOnes = sum(bsxfun(@times,x,weights)) + priorObj.model.countOfOnes;

            obj.model.countOfZeros = sum(bsxfun(@times,~x,weights)) + priorObj.model.countOfZeros;

        end

        function self = conjugateUpdate(self, prior, x)
            x = self.parseInputData(x);

            self = weightedConjugateUpdate(self, prior, x, ones(size(x,1),1));
        end
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % Methods for prtBrvVbMembershipModel
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    methods
        function y = conjugateVariationalAverageLogLikelihood(obj, x)

            [logProb0, logProb1] = obj.expectedValueLogProbabilities;

            y = sum(bsxfun(@times,x,logProb1) + bsxfun(@times,~x,logProb0),2);
        end
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % Methods for prtBrvVbOnlineMembershipModel
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    methods
        function obj = vbOnlineInitialize(obj, x) %#ok<INUSD>

            randDraw = (rand(1,obj.nDimensions)>0.5);

            obj.model.countOfOnes = randDraw+0.5;
            obj.model.countOfZeros  = (1-randDraw) + 0.5;
        end

        function [self, training] = vbOnlineUpdate(self, priorObj, x, lambda, D, prevObj)
            x = self.parseInputData(x);
            [self, training] = vbOnlineWeightedUpdate(self, priorObj, x, ones(size(x,1),1), lambda, D, prevObj);
        end

        function [obj, training] = vbOnlineWeightedUpdate(obj, priorObj, x, weights, lambda, D, prevObj)
            x = obj.parseInputData(x);

            S = size(x,1);

            if nargin < 4 || isempty(weights)
                weights = ones(size(x,1),1);
            end

            obj.model.countOfOnes = (D/S*sum(bsxfun(@times,x,weights)) + priorObj.model.countOfOnes)*lambda + prevObj.model.countOfOnes*(1-lambda);
            obj.model.countOfZeros = (D/S*sum(bsxfun(@times,~x,weights)) + priorObj.model.countOfZeros)*lambda + prevObj.model.countOfZeros*(1-lambda);

            training = struct([]);
        end
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % Properties for prtBrvMvb use
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    properties
        model = prtBrvMvbHierarchy;
    end

    properties (Hidden)
        initFudgeFactor = 1; % Between zero and 1, probably > 0.9
    end

    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    % Methods for prtBrvMvb use
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    methods
        function self = prtBrvMvb(varargin)
            if nargin < 1
                return
            end
            self = constructorInputParse(self,varargin{:});
        end

        function [logProb0, logProb1] = expectedValueLogProbabilities(obj)
            psi1 = psi(obj.model.countOfOnes);
            psi0 = psi(obj.model.countOfZeros);

            psiSum = psi(obj.model.countOfOnes+obj.model.countOfZeros);

            logProb1 = psi1 - psiSum;
            logProb0 = psi0 - psiSum;
        end

        function model = modelDraw(obj)
            model.probabilities = zeros(1,length(obj.model.countOfOnes));
            for iDim = 1:length(obj.model.countOfOnes)
                model.probabilities(iDim) = prtRvUtilDirichletRnd([obj.model.countOfZeros(iDim) obj.model.countOfOnes(iDim)]);
            end
        end
    end

    methods (Hidden)
        function x = parseInputData(self,x) %#ok<MANU>
            if isnumeric(x) || islogical(x)
                return
            elseif prtUtilIsSubClass(class(x),'prtDataSetBase')
                x = x.getObservations();
            else
                error('prt:prtBrvMvb:parseInputData','prtBrvMvb requires a prtDataSet or a numeric 2-D matrix');
            end
        end
    end
end


%finds best source and distance to the best source
% if distance is negative, the best source cannot be found (for example, because the propagation was stopped before it reached this point)
% Danil Kirsanov, 09/2007

function [source_id, distance] = geodesic_best_source(algorithm, destination)

global geodesic_library;

if nargin == 2
    d = geodesic_convert_surface_points({destination});

    tmp = 1;
    [source_id, tmp1, distance] = calllib(geodesic_library, 'distance_and_source', algorithm.id, d, tmp);
    source_id = source_id + 1;
else                                    %return distances and sources for all vertices
    tmp = libpointer('doublePtrPtr');
    tmp1 = libpointer('int32PtrPtr');

    [num_vertices, d, s] = calllib(geodesic_library, 'distance_and_source_for_all_vertices', algorithm.id, tmp, tmp1);

    setdatatype(d, 'doublePtr', num_vertices);
    distance = d.Value;
    setdatatype(s, 'int32Ptr', num_vertices);
    source_id = s.Value + 1;
end


% This is material illustrating the methods from the book
% Financial Modelling  - Theory, Implementation and Practice with Matlab
% source
% Wiley Finance Series
% ISBN 978-0-470-74489-5
%
% Date: 02.05.2012
%
% Authors:  Joerg Kienitz
%           Daniel Wetterau
%
% Please send comments, suggestions, bugs, code etc. to
% kienitzwetterau_FinModelling@gmx.de
%
% (C) Joerg Kienitz, Daniel Wetterau
%
% Since this piece of code is distributed via the mathworks file-exchange
% it is covered by the BSD license
%
% This code is being provided solely for information and general
% illustrative purposes. The authors will not be responsible for the
% consequences of reliance upon using the code or for numbers produced
% from using the code.

function [c,fc] = FInvSABR2(x, a, b, r, n, f, t)
% This function computes the inverse of a SABR cumulative distribution
% Since it is based on FSABR2 it is very fast!
% x is the quantile for which we have to find the value to use
f = @(k) psabr_4(a, b, r, n, f, k, t); % handle
f2 = @(t) (x - FSABR2(t,f));           % objective function

% Use simple bisection
a = 0.001;  b = 1;                     % lower and upper bound
eps = 1e-3;         % threshold
k = 0;              % index

fc = zeros(1,10);   % initialize

while(k <= 100)
    k = k+1;
    c=0.5*(b+a);
    fc = f2(c);
    if( (-eps <= fc) && (fc <= eps))
        break;
    elseif (f2(c) < 0)
        b = c;
    else
        a = c;
    end
end

end


filename='Cantileverbeam_Hexahedra_Linear_Structured';
ptype = 'MACRO';
method = 'SIMPALL';
materialType = 'ISOTROPIC';
initial_case = 'holes';
cost = {'compliance'};
weights = [1];
constraint = {'volumeConstraint'};
optimizer = 'HAMILTON-JACOBI';
incrementFactor = 1;
designVariable = 'LevelSet';
filterType = 'P1';
constraint_case = 'INEQUALITY';

nsteps = 10;
Vfrac_final = 0.15;
Perimeter_target=3.5;
optimality_final =1e-4;
constr_final =1e-4;

BCscale_factor = 0.3;
HJiter0 = 1;
e2 = 1;
N_holes = [17 9 9];
R_holes = 0.2;
phase_holes = [0 0 0];

Vfrac_initial = 0.3;
optimality_initial = 5e-2;
constr_initial = 5e-2;

TOL.rho_plus = 1;
TOL.rho_minus = 0;
TOL.E_plus = 1;
TOL.E_minus = 1e-3;

% maxiter = 1;
TOL.nu_plus = 0.3;
TOL.nu_minus = 0.3;

plotting = 1;
printing = 0;
monitoring = 1;
monitoring_interval = 1;


% This files demos creating a interactive html file from a triangular mesh
% and a second set of deformed vertices

% Written by Benjamin Irving 2013/03/25

%From example 1 a mesh can be plotted in matlab as follows:
%Creating the mesh
[x,y,z,v] = flow;
fv=isosurface(x,y,z,v,-3);

% Creating a second set of vertices from the first set... and setting the
% second and third dimensions of the vertices to 0 as one example of a
% mesh transformation
fv2=fv;
fv2.vertices(:,2)=0;
fv2.vertices(:,3)=0;

% running the html creating function
x3mesh_deform(fv.faces, fv.vertices, fv2.vertices, 'name', 'Example1')
disp('Mesh exported to html. File is in htmlfigs subfolder')


% closest fly, based on dcenter
function [data,units,mind] = compute_closestfly_center(trx,n,dosave_d)

if nargin < 3,
  dosave_d = true;
end

flies = trx.exp2flies{n};
nflies = numel(flies);
closestfly = cell(1,nflies);
mind = cell(1,nflies);

parfor i1 = 1:nflies,
  fly1 = flies(i1);
  flies2 = flies(trx.roi(fly1)==trx.roi(flies));
  dcenter = inf(numel(flies2),trx(fly1).nframes);
  for i2 = 1:numel(flies2),
    fly2 = flies2(i2);
    if fly1 == fly2,
      continue;
    end
    dcenter(i2,:) = dcenter_pair(trx,fly1,fly2);
  end
  [mind{i1},closesti] = min(dcenter,[],1);
  closestfly{i1} = flies2(closesti);
  closestfly{i1}(isnan(mind{i1})) = nan;
end

% so that we don't compute dcenter twice
if dosave_d,
  data = mind; %#ok<NASGU>
  units = parseunits('mm'); %#ok<NASGU>
  filename = trx.GetPerFrameFile('dcenter',n);
  save(filename,'data','units');
end

data = closestfly;
units = parseunits('unit');


function flag = is_lv_on_the_left(scimat, m, rot)
% IS_LV_ON_THE_LEFT  Check whether the left ventricle is on the left hand
% side of the image.
%
% FLAG = is_lv_on_the_left(SCIMAT, M, ROT)
%
%   SCIMAT is a struct with the whole tissue segmentation (see "help
%   scimat" for details on SCIMAT structs).
%
%   M, ROT are a 3-vector and a (3, 3)-matrix that represent the centroid
%   and main axes of the heart, respectively.
%
%   FLAG is a boolean. It is true of the left ventricle is on the left hand
%   side of the image with respect to the coordinate system defined by ROT.

% Author: Ramon Casero <rcasero@gmail.com>
% Copyright © 2010-2014 University of Oxford
% Version: 0.1.2
%
% University of Oxford means the Chancellor, Masters and Scholars of
% the University of Oxford, having an administrative office at
% Wellington Square, Oxford OX1 2JD, UK.
%
% This file is part of Gerardus.
%
% 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. The offer of this
% program under the terms of the License is subject to the License
% being interpreted in accordance with English Law and subject to any
% action against the University of Oxford being under the jurisdiction
% of the English Courts.
%
% You should have received a copy of the GNU General Public License
% along with this program.  If not, see <http://www.gnu.org/licenses/>.

% check arguments
narginchk(3, 3);
nargoutchk(0, 1);

% cut a whole tissue plane orthogonal to the vertical axis
im = scimat_intersect_plane(scimat, m, rot(:, 3));

% the computed "X axis" of the orientation can be pointing more or less in
% the direction of of X or -X. We compute the angle
alpha = acosd(dot(rot(:, 1), [1 0 0]'));

% now we rotate the intersected plane to get an idea of whether the LV is
% going to be on the left or right hand side of the rotated volume
im = imrotate(im, alpha);

% label connected components
[lab, nlabs] = bwlabel(~im);

% get number of voxels in each label
nvox = zeros( nlabs, 1 );
for L = 1:nlabs
    nvox(L) = sum( lab(:) == L );
end

% sort labels by decreasing size
[nvox, idx] = sort(nvox, 1, 'descend');

% the largest component is the background; the 2nd and 3rd largest should
% be the RV and LV, in either order

% compute the eccentricity of each label (the opposite of the roundness)
stat2 = regionprops(double(lab == idx(2)), 'Eccentricity');
stat3 = regionprops(double(lab == idx(3)), 'Eccentricity');

% we vote whether the 2nd largest component is actually the RV: it is more
% eccentric
if (stat2.Eccentricity > stat3.Eccentricity)
    idxrv = idx(2);
    idxlv = idx(3);
else
    idxrv = idx(3);
    idxlv = idx(2);
end

% get the centroids of both components
crv = regionprops(double(lab == idxrv), 'Centroid');
clv = regionprops(double(lab == idxlv), 'Centroid');

% is the LV placed on the left hand side?
flag = clv.Centroid(1) < crv.Centroid(1);


filename='Cantilever_triangle_coarse';
 ptype = 'MACRO';
method = 'SIMPALL';
materialType = 'ISOTROPIC';
initial_case = 'full';
cost = {'compliance';'perimeter'};
weights = [1, 0.1];
constraint = {'volumeConstraint'};
optimizer = 'PROJECTED GRADIENT';
incrementFactor = 1;
designVariable = 'Density';
filterType = 'P1';

nsteps = 5;
Vfrac_final = 0.4;
Perimeter_target = 1;
optimality_final = 1e-3;
constr_final =1e-3;

Vfrac_initial = 1;
optimality_initial = 1e-2;
constr_initial = 1e-3;
TOL.rho_plus = 1;
TOL.rho_minus = 0;
TOL.E_plus = 1;
TOL.E_minus = 1e-3;
TOL.nu_plus = 1/3;
TOL.nu_minus = 1/3;


% dipoledensity() - compute and optionally plot a measure of the 3-D spatial
%                   (in)homogeneity of a specified (large) set of 1- or 2-dipole
%                   component models, either as physical dipole density or as
%                   dipole-position entropy across subjects. In either case,
%                   take into account either all the dipoles, or only the nearest
%                   dipole from each of the subjects. If no output arguments,
%                   or if 'plot','on', paints a 3-D density|entropy brain image
%                   on slices of the Montreal Neurological Institute (MNI) mean
%                   MR brain image ('standard_BESA/avg152t1.mat'). Calls
%                   dipplot(),
%                   mri3dplot(), and Fieldtrip function find_inside_vol().
% Usage:
%               >> [dens3d mri] = dipoledensity( dipoles, 'key',val, ... );
%
% Inputs:
%    dipoles - this may be either the same dipole structure given as input to
%              the dipplot() function, a 3 by n array of dipole localization or
%              a cell array containing arguments for the dipplot function. Note that
%              the 'coordformat' option below defines the coordinate space for these
%              dipoles (default is MNI). See help dipplot for more information.
%
% Optional 'key', val input pairs:
%  'mri'        - [string or struct] mri file (matlab format or file format read
%                 by fcdc_read_mri). See dipplot.m help for more information.
%  'method'     - ['alldistance'|'distance'|'entropy'|'relentropy'] method for
%                            computing density:
%                 'alldistance' - {default} take into account the gaussian-weighted
%                            distances from each voxel to all the dipoles. See
%                            'methodparam' (below) to specify a standard deviation
%                            (in mm) for the gaussian weight kernel.
%                 'distance' - take into account only the distances to the nearest
%                              dipole for each subject. See 'methodparam' (below).
%                 'entropy' - taking into account only the nearest dipole to each
%                             voxel for each subject. See 'methodparam' below.
%                 'relentropy' - as in 'entropy,' but take into account all the
%                             dipoles for each subject.
% 'methodparam' - [number] for 'distance'|'alldistance' methods (see above), the
%                 standard deviation (in mm) of the 3-D gaussian smoothing kernel.
%                 For 'entropy'|'relentropy' methods, the number of closest dipoles
%                 to include {defaults: 20 mm | 20 dipoles }
% 'subsample'   - [integer] subsampling of native MNI image {default: 2 -> 2x2x2}
% 'weight'      - [(1,ncomps) array] for 'distance'|'alldistance' methods, the
%                 relative weight of each component dipole {default: ones()}
% 'coordformat' - ['mni'|'spherical'] coordinate format if dipole location or
%                 a structure is given as input. Default is 'mni'.
% 'subjind'     - [(1,ncomps) array] subject index for each dipole model. If two
%                 dipoles are in one component model, give only one subject index.
% 'nsessions'   - [integer] for 'alldistance' method, the number of sessions to
%                 divide the output values by, so that the returned measure is
%                 dipole density per session {default: 1}
% 'plot'        - ['on'|'off'] force plotting dipole density|entropy
%                 {default: 'on' if no output arguments, else 'off'}
% 'dipplot'     - ['on'|'off'] plot the dipplot image (used for converting
%                 coordinates (default is 'off')
% 'plotargs'    - {cell array} plotting arguments for mri3dplot() function.
% 'volmesh_fname' - [string] precomputed mesh volume file name. If not
%                 given as input the function will recompute it (it can take from
%                 five to 20 minutes). By default this function save the volume file
%                 mesh into a file named volmesh_local.mat in the current
%                 folder.
%
% Outputs:
%  dens3d       - [3-D num array] density in dipoles per cubic centimeter. If output
%                 is returned, no plot is produced unless 'plot','on' is specified.
%  mri          - {MRI structure} used in mri3dplot().
%
% Example:
%         >> fakedipoles = (rand(3,10)-0.5)*80;
%         >> [dens3d mri] = dipoledensity( fakedipoles, 'coordformat', 'mni');
%         >> mri3dplot(dens3d,mri); % replot if no output is given above
%                                   % function is called automatically
%
% ------------------------------------
% NOTES:   to do multiple subject causal-weighted density map,
% (1) concatenate dipplot coord matrices for all subject
% (2) make g.subjind vector [ones(1,ncompsS1) 2*ones(1,ncompsS2) ... N*ones(1,ncompssN)]
% (3) concatenate normalized outflows for all subjects to form weight vector
% (4) call dipoledensity function with method = 'entropy' or 'relentropy'
% ------------------------------------
%
% See also:
%           EEGLAB: dipplot(), mri3dplot(), Fieldtrip: find_inside_vol()
%
% Author: Arnaud Delorme & Scott Makeig, SCCN, INC, UCSD

% Copyright (C) Arnaud Delorme & Scott Makeig, SCCN/INC/UCSD, 2003-
%
% 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 2 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, write to the Free Software
% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

function [prob3d, mri] = dipoledensity(dipplotargs, varargin)

    % TO DO: return in dipplot() the real 3-D location of dipoles (in posxyz)
    %        FIX the dimension order here

prob3d = []; mri = [];
if nargin < 1
   help dipoledensity
   return
end

g = finputcheck(varargin, { 'subjind'     'integer'  []               [];
                            'method' 'string' { 'relentropy' 'entropy' 'distance' 'alldistance' } 'alldistance';
                            'methodparam' 'real'     []               20;
                            'weight'      { 'real' 'cell' }  []               [];
                            'smooth'      'real'     []               0;
                            'nsessions'   'integer'  []               1;
                            'subsample'   'integer'  []               2;
                            'plotargs'    'cell'     []               {};
                            'plot'        'string'  { 'on' 'off' }    fastif(nargout == 0, 'on', 'off');
                            'dipplot'     'string'  { 'on' 'off' }   'off';
                            'coordformat' 'string'  { 'mni' 'spherical' }   'mni';
                            'normalization' 'string'  { 'on' 'off' } 'on';
                            'volmesh_fname' 'string'  []  'volmesh_local.mat';
                            'mri'         { 'struct' 'string' } [] ''});
if isstr(g), error(g); end;
if ~strcmpi(g.method, 'alldistance') & isempty(g.subjind)
    error('Subject indices are required for this method');
end;
if ~iscell(g.weight), g.weight = { g.weight }; end;

% plotting dipplot
% ----------------
if ~iscell(dipplotargs) % convert input
    if ~isstruct(dipplotargs)
        if size(dipplotargs,1) == 3, dipplotargs = dipplotargs';
        elseif size(dipplotargs,2) ~= 3
            error('If an array of dipoles is given as entry, there must be 3 columns or 3 rows for x y z');
        end;
        model = [];
        for idip = 1:length(dipplotargs)
            model(idip).posxyz = dipplotargs(idip,:);
            model(idip).momxyz = [1 0 0];
            model(idip).rv = 0.5;
        end;
        dipplotargs = model;
    end;
    dipplotargs = { dipplotargs 'coordformat' g.coordformat };
else
    dipplotargs = { dipplotargs{:} 'coordformat' g.coordformat };
end;
struct = dipplot(dipplotargs{:}, 'plot', g.dipplot);
if nargout == 0
    drawnow;
end;

% retrieve coordinates in MNI space
% ---------------------------------
if 0 % deprecated
     % find dipoles
     % ------------
    hmesh = findobj(gcf, 'tag', 'mesh');
    if isempty(hmesh), error('Current figure must contain dipoles'); end;
    hh = [];
    disp('Finding dipoles...');
    dips = zeros(1,200);
    for index = 1:1000
        hh = [ hh(:); findobj(gcf, 'tag', ['dipole' int2str(index) ]) ];
        dips(index) = length(findobj(gcf, 'tag', ['dipole' int2str(index) ]));
    end;

    disp('Retrieving dipole positions ...');
    count = 1;
    for index = 1:length(hh)
        tmp = get(hh(index), 'userdata');
        if length(tmp) == 1
            allx(count) = tmp.eleccoord(1,1);
            ally(count) = tmp.eleccoord(1,2);
            allz(count) = tmp.eleccoord(1,3);
            alli(count) = index;
            count = count + 1;
        end;
    end;
end;

% check weights
% -------------
if ~isempty(g.weight{1})
    if ~iscell(g.weight)
        if length(g.weight) ~= length(struct)
            error('There must be as many elements in the weight matrix as there are dipoles')
        end;
    else
        if length(g.weight{1}) ~= length(struct) || length(g.weight{1}) ~= length(g.weight{end})
            error('There must be as many elements in the weight matrix as there are dipoles')
        end;
    end;
else
    g.weight = { ones( 1, length(struct)) };
end;
if ~isempty(g.subjind)
    if length(g.subjind) ~= length(struct)
        error('There must be as many element in the subject matrix as there are dipoles')
    end;
else
    g.subjind = ones( 1, length(struct));
end;

% decoding dipole locations
% -------------------------
disp('Retrieving dipole positions ...');
count = 1;
for index = 1:length(struct)
    dips = size(struct(index).eleccoord,1);
    for dip = 1:dips
        allx(count) = struct(index).eleccoord(dip,1);
        ally(count) = struct(index).eleccoord(dip,2);
        allz(count) = struct(index).eleccoord(dip,3);
        alli(count) = index;
        allw1(count) = g.weight{1}(  index)/dips;
        allw2(count) = g.weight{end}(index)/dips;
        alls(count) = g.subjind(index);
        count = count + 1;
    end;
end;
g.weight{1}    = allw1;
g.weight{end}  = allw2;
g.subjind = alls;

% read MRI file
% -------------
if isempty(g.mri) % default MRI file
    dipfitdefs;
    load('-mat', template_models(1).mrifile); % load mri variable
    g.mri = mri;
end
if isstr(g.mri)
    try,
        mri = load('-mat', g.mri);
        mri = mri.mri;
    catch,
        disp('Failed to read Matlab file. Attempt to read MRI file using function read_fcdc_mri');
        try,
            warning off;
            mri = read_fcdc_mri(g.mri);
            mri.anatomy = round(gammacorrection( mri.anatomy, 0.8));
            mri.anatomy = uint8(round(mri.anatomy/max(reshape(mri.anatomy, prod(mri.dim),1))*255));
            % WARNING: if using double instead of int8, the scaling is different
            % [-128 to 128 and 0 is not good]
            % WARNING: the transform matrix is not 1, 1, 1 on the diagonal, some slices may be
            % misplaced
            warning on;
        catch,
            error('Cannot load file using read_fcdc_mri');
        end;
    end;
    g.mri = mri; % output the anatomic mri image
end;

% reserve array for density
% -------------------------
prob3d = {zeros(ceil(g.mri.dim/g.subsample)) };
for i = 2:length(g.weight), prob3d{i} = prob3d{1}; end;

% compute voxel size
% ------------------
point1 = g.mri.transform * [ 1 1 1 1 ]';
point2 = g.mri.transform * [ 2 2 2 1 ]';
voxvol = sum((point1(1:3)-point2(1:3)).^2)*g.subsample^3; % in mm

% compute global subject entropy if necessary
% -------------------------------------------
vals   = unique(g.subjind); % the unique subject indices
if strcmpi(g.method, 'relentropy') | strcmpi(g.method, 'entropy') %%%%% entropy %%%%%%%
    newind = zeros(size(g.subjind));
    for index = 1:length(vals) % foreach subject in the cluster
        tmpind = find(g.subjind == vals(index)); % dipoles for the subject
        totcount(index) = length(tmpind); % store the number of subject dipoles
        newind(tmpind) = index; % put subject index into newind
    end;
    g.subjind = newind;
    gp = totcount/sum(totcount);
    globent = -sum(gp.*log(gp));
end;

% compute volume inside head mesh
% -------------------------------
dipfitdefs; % get the location of standard BEM volume file
tmp = load('-mat',DIPOLEDENSITY_STDBEM); % load MNI mesh

if isempty(g.volmesh_fname)  % default
    filename = [ '/home/arno/matlab/MNI_VoxelTsearch' int2str(g.subsample) '.mat' ];
else
    filename = g.volmesh_fname; %
end
if ~exist(filename)
    disp('Computing volume within head mesh...');
    [X Y Z]           = meshgrid(g.mri.xgrid(1:g.subsample:end)+g.subsample/2, ...
                                 g.mri.ygrid(1:g.subsample:end)+g.subsample/2, ...
                                 g.mri.zgrid(1:g.subsample:end)+g.subsample/2);
    [indX indY indZ ] = meshgrid(1:length(g.mri.xgrid(1:g.subsample:end)), ...
                                 1:length(g.mri.ygrid(1:g.subsample:end)), ...
                                 1:length(g.mri.zgrid(1:g.subsample:end)));
    allpoints = [ X(:)'    ; Y(:)'   ; Z(:)' ];
    allinds   = [ indX(:)' ; indY(:)'; indZ(:)' ];
    allpoints = g.mri.transform * [ allpoints ; ones(1, size(allpoints,2)) ];
    allpoints(4,:) = [];

    olddir = pwd;
    tmppath = which('ft_electroderealign');
    tmppath = fullfile(fileparts(tmppath), 'private');
    cd(tmppath);
    [Inside Outside] = find_inside_vol(allpoints', tmp.vol); % from Fieldtrip
    cd(olddir);
    disp('Done.');

    if 0 % old code using Delaunay %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
        P = tmp.vol.bnd(1).pnt;
        T = delaunayn(P); % recompute triangularization (the original one is not compatible
                          % with tsearchn) get coordinates of all points in the volume
        % search for points inside or ouside the volume (takes about 14 minutes!)
        IO = tsearchn(P, T, allpoints');
        Inside        = find(isnan(IO));
        Outside       = find(~isnan(IO));
        disp('Done.');
    end; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    try,
        save('-mat', filename, 'allpoints', 'allinds', 'Inside', 'Outside');
        disp('Saving file containing inside/outide voxel indices...');
    catch, end;
else
    disp('Loading file containing inside/outide voxel indices...');
    load('-mat',filename);
end;
InsidePoints  = allpoints(:, Inside);
InsideIndices = allinds(:, Inside);

% scan grid and compute entropy at each voxel
% -------------------------------------------
edges = [0.5:1:length(vals)+0.5];

if ~strcmpi(g.method, 'alldistance')
    fprintf('Computing (of %d):', size(InsideIndices,2));
    % entropy calculation: have to scan voxels
    % ----------------------------------------
    for i = 1:size(InsideIndices,2)

        alldists = (InsidePoints(1,i) - allx).^2 ...
                 + (InsidePoints(2,i) - ally).^2 ...
                 + (InsidePoints(3,i) - allz).^2;
        [tmpsort indsort] = sort(alldists); % sort dipoles by distance
        tmpweights{1}   = g.weight{1}(  indsort);
        tmpweights{end} = g.weight{end}(indsort);

        if strcmpi(g.method, 'relentropy') | strcmpi(g.method, 'entropy') %%%%% entropy %%%%%%%

            subjs  = g.subjind(indsort(1:g.methodparam)); % get subject indices of closest dipoles
            p      = histc(subjs, edges);
            if strcmpi(g.method, 'relentropy')
                p      = p(1:end-1)./totcount;
                % this should be uniform if p conforms to global count for all subjects
            end;
            p      = p/sum(p);
            p(find(p == 0)) = [];
            for tmpi = 1:length(g.weight)
                prob3d{1}(InsideIndices(1,i), InsideIndices(2,i), InsideIndices(3,i)) = -sum(p.*log(p));
            end;
        else
            % distance to each subject
            ordsubjs  = g.subjind(indsort);
            for index = 1:length(vals) % for each subject
                tmpind = find(ordsubjs == vals(index));
                if strcmpi(g.method,'distance')
                    use_dipoles(index) = tmpind(1); % find their nearest dipole
                end
            end;
            for tmpi = 1:length(g.weight)
                prob3d{tmpi}(InsideIndices(1,i), InsideIndices(2,i), InsideIndices(3,i)) = ...
                   sum(tmpweights{tmpi}(use_dipoles).*exp(-tmpsort(use_dipoles)/ ...
                           (2*g.methodparam^2))); % 3-D gaussian smooth
            end;
        end;
        if mod(i,100) == 0, fprintf('%d ', i); end;
    end;
else % 'alldistance'
    % distance calculation: can scan dipoles instead of voxels (since linear)
    % --------------------------------------------------------
    %alldists = allx.^2 + ally.^2 + allz.^2;
    %figure; hist(alldists); return; % look at distribution of distances

    fprintf('Computing (of %d):', size(allx,2));
    for tmpi=1:length(g.weight)
        tmpprob{tmpi} = zeros(1, size(InsidePoints,2));
    end;
    if length(g.weight) > 1, tmpprob2 = tmpprob; end;
    for i = 1:size(allx,2)
        alldists = (InsidePoints(1,:) - allx(i)).^2 + ...
                   (InsidePoints(2,:) - ally(i)).^2 + ...
                   (InsidePoints(3,:) - allz(i)).^2;
%         alldists = 1;  % TM
        for tmpi=1:length(g.weight)
            tmpprob{tmpi} = tmpprob{tmpi} + g.weight{tmpi}(i)*exp(-alldists/(2*g.methodparam^2)); % 3-D gaussian smooth
            if any(isinf(tmpprob{tmpi})), error('Infinite value in probability calculation'); end;
        end;
        if mod(i,50) == 0, fprintf('%d ', i); end;
    end;
    % copy values to 3-D mesh
    % -----------------------
    for i = 1:length(Inside)
        pnts = allinds(:,Inside(i));
        for tmpi = 1:length(g.weight)
            prob3d{tmpi}(pnts(1), pnts(2), pnts(3)) = tmpprob{tmpi}(i);
        end;
    end;

end;
fprintf('\n');

% normalize for points inside and outside the volume
% --------------------------------------------------
if strcmpi(g.method, 'alldistance') && strcmpi(g.normalization,'on')
    for i =1:length(g.weight)
        disp('Normalizing to dipole/mm^3');
        if any(prob3d{i}(:)<0)
            fprintf('WARNING: Some probabilities are negative, this will likely cause problems when normalizing probabilities.\n');
            fprintf('It is highly recommended to turn normaliziation off by using ''normalization'' key to ''off''.\n');
        end;
        totval = sum(prob3d{i}(:));  % total values in the head
        totdip = size(allx,2);   % number of dipoles
        voxvol;                  % volume o af a voxel in mm^3
        prob3d{i} = prob3d{i}/totval*totdip/voxvol*1000; % time 1000 to get cubic centimeters
        prob3d{i} = prob3d{i}/g.nsessions;
    end;
end;

% resample matrix
% ----------------
if g.subsample ~= 1
    for i =1:length(g.weight)
        prob3d{i} = prob3d{i}/g.subsample;
        newprob3d = zeros(g.mri.dim);
        X = ceil(g.mri.xgrid/g.subsample);
        Y = ceil(g.mri.ygrid/g.subsample);
        Z = ceil(g.mri.zgrid/g.subsample);
        for index = 1:size(newprob3d,3)
            newprob3d(:,:,index) = prob3d{i}(X,Y,Z(index));
        end;
        prob3d{i} = newprob3d;
    end;
end;

% 3-D smoothing
% -------------
if g.smooth ~= 0
    disp('Smoothing...');
    for i =1:length(g.weight)
        prob3d{i} = smooth3d(prob3d{i}, g.smooth);
    end;
end;

% plotting
% --------
if strcmpi(g.plot, 'off')
    close gcf;
else
    mri3dplot( prob3d, g.mri, g.plotargs{:}); % plot the density using mri3dplot()
end;
return;