function [NewTessFile, iSurface, I, J] = tess_downsize( TessFile, newNbVertices, Method )
% TESS_DOWNSIZE: Reduces the number of vertices in a surface file.
%
% USAGE:  [NewTessFile, iSurface, I, J] = tess_downsize(TessFile, newNbVertices=[ask], Method=[ask]);
%
% INPUT:
%    - TessFile      : Full path to surface file to decimate
%    - newNbVertices : Desired number of vertices
%    - Method        : {'reducepatch', 'reducepatch_subdiv', 'iso2mesh', 'iso2mesh_project'}
% OUTPUT:
%    - NewTessFile : Filename of the newly created file
%    - iSurface    : Index of the new surface file
%    - I,J         : Indices of the vertices that were kept (see intersect function)

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

%% ===== PARSE INPUTS =====
if (nargin < 3) || isempty(Method)
    Method = [];
end
if (nargin < 2) || isempty(newNbVertices)
    newNbVertices = [];
end
% File name: string or cell array of strings
MultipleFiles = [];
if iscell(TessFile)
    if (length(TessFile) > 1)
        MultipleFiles = TessFile;
    end
    TessFile = TessFile{1};
end
% Save current modifications
panel_scout('SaveModifications');
% Initialize returned values
NewTessFile = '';
iSurface = [];
I = [];
J = [];

%% ===== ASK FOR MISSING OPTIONS =====
% Get the number of vertices
VarInfo = whos('-file',file_fullpath(TessFile),'Vertices');
oldNbVertices = VarInfo.size(1);
% If new number of vertices was not provided: ask user
if isempty(newNbVertices)
    % Ask user the new number of vertices
    newNbVertices = java_dialog('input', 'New number of vertices:', ...
                                         'Resample surface', [], num2str(oldNbVertices));
    if isempty(newNbVertices)
        return
    end
    % Read user input
    newNbVertices = str2double(newNbVertices);
end
% Check if new number of vertices is valid
if isempty(newNbVertices) || isnan(newNbVertices)
    error('Invalid vertices number');
end
if (newNbVertices >= oldNbVertices)
    NewTessFile = TessFile;
    disp(sprintf('TESS> Surface has %d vertices, cannot downsample to %d vertices.', oldNbVertices, newNbVertices));
    return;
end

% Ask for resampling method
if isempty(Method)
    % Ask method
    ind = java_dialog('radio', 'Select the resampling method:', 'Resample surface', [], ...
                      {['Matlab''s reducepatch:
' ...
                        '   | - Inhomogeneous mesh: large faces at the top of the gyri
' ...
                        '   | - Keeps the atlases and the subjects co-registration'], ...
                       ['Matlab''s reducepatch + subdivide large faces:
' ...
                        '   | - The large faces at the top of the gyri are subdivided in three
' ...
                        '   | - Deletes the atlases and the subjects co-registration'], ...
                       ['iso2mesh/CGAL library:
' ...
                        '   | - Homogeneous mesh: all the faces have similar sizes
' ...
                        '   | - Deletes the atlases and the subject co-registration
' ...
                        '   | - If the downsample looks dark, right-click > Swap faces']}, 1);
%                        ['iso2mesh/CGAL + project on the original surface:
' ...
%                         '   | - Homogeneous mesh but possible topological problems
' ...
%                         '   | - Damages the atlases and the subject co-registration']}, 1);
    if isempty(ind)
        return
    end
    % Select corresponding method name
    switch (ind)
        case 1,  Method = 'reducepatch';
        case 2,  Method = 'reducepatch_subdiv';
        case 3,  Method = 'iso2mesh';
        case 4,  Method = 'iso2mesh_project';
    end
end

%% ===== PROCESS MULTIPLE FILES =====
if ~isempty(MultipleFiles)
    for i = 1:length(MultipleFiles)
        [NewTessFile, iSurface, I, J] = tess_downsize( MultipleFiles{i}, newNbVertices, Method );
    end
    return;
end

%% ===== LOAD FILE =====
% Progress bar
bst_progress('start', 'Resample surface', 'Loading file...');
% Load file
TessMat = in_tess_bst(TessFile);
% Prepare variables
TessMat.Faces    = double(TessMat.Faces);
TessMat.Vertices = double(TessMat.Vertices);
dsFactor = newNbVertices / size(TessMat.Vertices, 1);

%% ===== RESAMPLE =====
bst_progress('start', 'Resample surface', ['Resampling surface: ' TessMat.Comment '...']);
% Resampling methods
switch (Method)
    % ===== REDUCEPATCH =====
    % Matlab's reducepatch
    case 'reducepatch'
        % Reduce number of vertices
        [NewTessMat.Faces, NewTessMat.Vertices] = reducepatch(TessMat.Faces, TessMat.Vertices, dsFactor);
        % Find the vertices that were kept by reducepatch
        [tmp, I, J] = intersect(TessMat.Vertices, NewTessMat.Vertices, 'rows');
        % Re-order the vertices so that they are in the same order in the output surface
        [I, iSort] = sort(I);
        NewTessMat.Vertices = TessMat.Vertices(I,:);
        J = J(iSort);
        % Re-order the vertices in the faces
        iSortFaces(J) = 1:length(J);
        NewTessMat.Faces = iSortFaces(NewTessMat.Faces);
        MethodTag = '';
        % Set the
        J = (1:length(J))';

    % ===== REDUCEPATCH + SUBDIV =====
    % Reducepatch + subdivide the large faces into smaller triangles
    case 'reducepatch_subdiv'
        % Reduce number of vertices
        [NewTessMat.Faces, NewTessMat.Vertices] = reducepatch(TessMat.Faces, TessMat.Vertices, dsFactor * 0.94);
        % Find the vertices that were kept by reducepatch
        [tmp, I, J] = intersect(TessMat.Vertices, NewTessMat.Vertices, 'rows');

        % Progress bar
        bst_progress('start', 'Resample surface', 'Analyzing surface...');
        % Calulate face areas and perimeter
        FaceArea  = tess_area(NewTessMat.Vertices, NewTessMat.Faces);
        % Vertex connectivity, normals, Curvature
        VertConn    = tess_vertconn(NewTessMat.Vertices, NewTessMat.Faces);
        [VertNormals, FaceNormals] = tess_normals(NewTessMat.Vertices, NewTessMat.Faces, VertConn);
        Curvature   = tess_curvature(NewTessMat.Vertices, VertConn, VertNormals);
        % Get center of each face
        FaceCenter = (NewTessMat.Vertices(NewTessMat.Faces(:,1),:) + NewTessMat.Vertices(NewTessMat.Faces(:,2),:) + NewTessMat.Vertices(NewTessMat.Faces(:,3),:)) ./ 3;
        % Get center of mass of the vertices
        SurfCenter = mean(NewTessMat.Vertices, 1);
        % Get large faces to subdivide (perimeter or area)
        iBigFaces = find((sum(FaceNormals .* bst_bsxfun(@minus, FaceCenter, [0 0 SurfCenter(3)]), 2) > 0.04) & ...  % Faces pointing outwards (normal in the same direction as position vector)
                         (sum(Curvature(NewTessMat.Faces) > 0, 2) >= 2) & ...       % Curvature has to be > 0
                         (FaceArea > mean(FaceArea) + 1*std(FaceArea)));            % Face area threshold
        % If there are not enough points to add (white matter): perform search on all the surface
        if (length(iBigFaces) < .75 * (newNbVertices - size(NewTessMat.Vertices,1)))
            iBigFaces = find(FaceArea > mean(FaceArea) + 2.5 * std(FaceArea));
        end
        % Display message
        disp(sprintf('BST> Subdividing %d faces from the %d faces generated by reducepatch.', length(iBigFaces), length(NewTessMat.Faces)));

% figure;
        % Loop over each face
        iRmFaces = [];
        bst_progress('start', 'Resample surface', 'Subdividing large faces...', 1, length(iBigFaces));
        for i = 1:length(iBigFaces)
            bst_progress('inc', 1);
            % Get the face and, the positions of its vertices, and the center of the face
            f = NewTessMat.Faces(iBigFaces(i),:);
            v = NewTessMat.Vertices(f,:);
            c = mean(v,1);

            % === BOUNDING BOX ===
            % Get maximum distance to consider around the face
            dmax = 1.2 * max(sqrt(sum(bst_bsxfun(@minus, v, c) .^ 2, 2)));
            % Select the vertices of the high-res surface that in a small sphere around the center of the face
            iVertBox = find(sum(bst_bsxfun(@minus, TessMat.Vertices, c) .^ 2, 2) < dmax.^2);

%             % Display selection
%             cla; hold on;
%             plot3(TessMat.Vertices(iVertBox,1), TessMat.Vertices(iVertBox,2), TessMat.Vertices(iVertBox,3), '.', 'tag', 'ptri');
%             plot3(c(1), c(2), c(3), '*y');
%             patch('Vertices', v, 'Faces', [1 2 3], 'FaceColor', 'r');
%             axis vis3d equal; drawnow; rotate3d on;

            % Get the vertices for the target face in the hi-resolution surface
            s1 = find(I(J == f(1)) == iVertBox);
            s2 = find(I(J == f(2)) == iVertBox);
            s3 = find(I(J == f(3)) == iVertBox);
            % Error?
            if isempty(s1) || isempty(s2) || isempty(s3)
                disp(sprintf('BST> Cannot subdivide big face #%d (box too small), skipping...', i));
                continue;
            end
            % Get a subset of the vertex connectivity matrix
            boxVertConn = TessMat.VertConn(iVertBox, iVertBox);

            % === FIND PATH TO COMMON NODES ===
            % Expand areas around all the vertices until they all overlap
            iter_max = 20;
            iter = 1;
            sx = [];
            while isempty(sx) && (iter < iter_max)
                s1 = union(s1, find(any(boxVertConn(s1,:),1)));
                s2 = union(s2, find(any(boxVertConn(s2,:),1)));
                s3 = union(s3, find(any(boxVertConn(s3,:),1)));
                sx = intersect(intersect(s1, s2), s3);
                iter = iter + 1;
            end

            % Expand areas around all the vertices until they all overlap
            iter_max = 50;
            iter = 1;
            istop = 0;
            d1 = 0;
            d2 = 0;
            d3 = 0;
            while (istop < 2) && (iter <= iter_max)
                % Grow from vertex #1
                i1 = find(any(boxVertConn(s1,:),1));
                s1 = [s1, setdiff(i1,s1)];
                d1 = [d1, i1*0+iter];
                % Grow from vertex #2
                i2 = find(any(boxVertConn(s2,:),1));
                s2 = [s2, setdiff(i2,s2)];
                d2 = [d2, i2*0+iter];
                % Grow from vertex #1
                i3 = find(any(boxVertConn(s3,:),1));
                s3 = [s3, setdiff(i3,s3)];
                d3 = [d3, i1*0+iter];
                % If all the vertices are in the region: stop immediately
                if (length(s1) == length(iVertBox)) && (length(s2) == length(iVertBox)) && (length(s3) == length(iVertBox))
                    istop = 10;
                % Do one more iterations after all the vertices are identified
                elseif (istop > 0) || (all(ismember([s2 s3],s1)) && all(ismember([s1 s3],s2)) && all(ismember([s1 s2],s3)))
                    istop = istop + 1;
                else
                    iter = iter + 1;
                end
            end
            % If an error occured: skip face
            if (iter > iter_max)
                disp(sprintf('BST> Cannot subdivide big face #%d (more than %d nodes distance), skipping...', i, iter_max));
                continue;
            end
            % Take intersection of the three regions
            [sx,ix,jx] = intersect(s1, s2);
            d1 = d1(ix);
            d2 = d2(jx);
            [sx,ix,jx] = intersect(sx, s3);
            d1 = d1(ix);
            d2 = d2(ix);
            d3 = d3(jx);
            dx = d1 + d2 + d3;

%             delete(findobj(0, 'tag', 'ptri')); plot3(TessMat.Vertices(iVertBox(s1),1), TessMat.Vertices(iVertBox(s1),2), TessMat.Vertices(iVertBox(s1),3), '.g', 'tag', 'ptri');
%             delete(findobj(0, 'tag', 'ptri')); plot3(TessMat.Vertices(iVertBox(s2),1), TessMat.Vertices(iVertBox(s2),2), TessMat.Vertices(iVertBox(s2),3), '.b', 'tag', 'ptri');
%             delete(findobj(0, 'tag', 'ptri')); plot3(TessMat.Vertices(iVertBox(s3),1), TessMat.Vertices(iVertBox(s3),2), TessMat.Vertices(iVertBox(s3),3), '.y', 'tag', 'ptri');

            % === SELECT VERTICES INSIDE THE FACE ===
            % Convert sx back to full indices list
            sx = iVertBox(sx);
            % Keep only the ones that project on the face INSIDE the triangle
            isInside = bst_intriangle(v(1,:), v(2,:), v(3,:), TessMat.Vertices(sx,:));
            sx = sx(isInside);
            dx = dx(isInside);
            if isempty(sx)
                disp(sprintf('BST> Cannot subdivide big face #%d (no candidate in the triangle), skipping...', i));
                continue;
            end
            % plot3(TessMat.Vertices(sx,1), TessMat.Vertices(sx,2), TessMat.Vertices(sx,3), '.y');

            % === SELECT VERTICES INSIDE THE FACE ===
            % Keep the closest path to all the nodes
            pathLength = min(dx);
            sx = sx(dx <= pathLength + 2);
%             plot3(TessMat.Vertices(sx,1), TessMat.Vertices(sx,2), TessMat.Vertices(sx,3), '.y');

            % === KEEP THE MOST CENTRAL LOCATION ===
            % Find the closest to the face center
            [d,imin] = min(sqrt(sum(bst_bsxfun(@minus, TessMat.Vertices(sx,:), c) .^ 2, 2)));
            sx = sx(imin);
            % Make sure it is not already in the destination surface
            if ismember(sx, I)
                disp(sprintf('BST> Cannot subdivide big face #%d (vertex already selected), skipping...', i));
                continue;
            end
%             plot3(TessMat.Vertices(sx,1), TessMat.Vertices(sx,2), TessMat.Vertices(sx,3), 'og');

            % === ADD VERTEX ===
            % Add the vertex to the list of vertices
            NewTessMat.Vertices = [NewTessMat.Vertices; c];
            iVertNew = size(NewTessMat.Vertices,1);
            I(end+1) = sx;
            J(end+1) = iVertNew;
            % Add the three new faces to the new surface
            NewTessMat.Faces = [NewTessMat.Faces; ...
                                f(1), f(2), iVertNew; ...
                                f(1), iVertNew, f(3); ...
                                iVertNew, f(2), f(3)];
            iRmFaces(end+1) = iBigFaces(i);
        end
        % Verify the unicity of the vertex selection
        if (length(I) ~= length(unique(I)))
            disp('BST> Error: The same vertex was selected multiple times in the high-resolution brain.');
            disp('BST> Using the basic reducepatch results instead...');
            % Call reducepatch only
            [NewTessFile, iSurface, I, J] = tess_downsize( TessFile, newNbVertices, 'reducepatch');
            return;
        end
        % Remove the deleted faces
        NewTessMat.Faces(iRmFaces,:) = [];
        MethodTag = '_subdiv';
        I = [];
        J = [];

    % ===== ISO2MESH =====
    % Using iso2mesh toolbox: good surfaces with equal triangle sizes, but no
    % correspondence of vertices in the original surface, and impossible to reconstruct the info
    case 'iso2mesh'
        % Reduce number of vertices
        NewTessMat = iso2mesh_resample(TessMat, dsFactor);
        if isempty(NewTessMat)
            return;
        end
        % Do not return any correspondence with the original vertices
        MethodTag = '_iso2mesh';
        I = [];
        J = [];

    % ===== ISO2MESH + PROJECT =====
    % Using iso2mesh toolbox: good surfaces with equal triangle sizes, but no
    % correspondence of vertices in the original surface, and impossible to reconstruct the info
    case 'iso2mesh_project'
        % Reduce number of vertices
        NewTessMat = iso2mesh_resample(TessMat, dsFactor);
        if isempty(NewTessMat)
            return;
        end
        Vertices = NewTessMat.Vertices;
        Faces    = NewTessMat.Faces;
        % Progress bar
        bst_progress('start', 'Resample surface', 'Analyzing surface...');
        % Calculate new normals
        newVertNormals = tess_normals(Vertices, Faces);
        % Remove duplicate vertices for Delaunay tesselation
        [delaunayVert, iSrc, iDest] = unique(TessMat.Vertices, 'rows');
        % Get the nearest neighbors
        I = bst_nearest(delaunayVert, Vertices);
        % Convert back to initial indices
        I = iSrc(I);
        % Compute the scalar product of the norms between the nearest neighbors and all the original vertices
        nv = size(TessMat.Vertices, 1);
        P = newVertNormals(:,1) .* TessMat.VertNormals(I) + ...
            newVertNormals(:,2) .* TessMat.VertNormals(nv + I) + ...
            newVertNormals(:,3) .* TessMat.VertNormals(2*nv + I);
        % Values with negative P are most likely on the other side of the sulcus
        iErr = find(P < -0.6);
        iKeep = find(P >= -0.6);

        % Loop on those points to fix them one by one with a different neighbor
        bst_progress('start', 'Resample surface', 'Fixing surface...', 1, length(iErr));
        for i = 1:length(iErr)
            bst_progress('inc', 1);
            % Calculate the scalar product of the normals of the current vertex with all the original vertices
            prodNorm = sum(bst_bsxfun(@times, newVertNormals(iErr(i),:), TessMat.VertNormals), 2);
            % Get indices for which the scalar product is positive
            iProdOk = find(prodNorm > -0.6);
            % Remove the vertices that are already in the mesh
            iProdOk = intersect(iProdOk, iKeep);
            % Find the nearest neighbor
            [m,iFix] = min(sum(bst_bsxfun(@minus, Vertices(iErr(i),:), TessMat.Vertices(iProdOk,:)) .^ 2, 2));
            I(iErr(i)) = iProdOk(iFix);
            % Add the corrected vertex to the list of valid vertices
            iKeep(end+1) = iProdOk(iFix);
        end

        % Find repeated vertices
        if (length(unique(I)) ~= length(I))
            disp('BST> ERROR: Found some duplicated vertices. Surface topology is incorrect...');
        end
        % Replace vertices with their nearest neighbor in the original surface
        Vertices = TessMat.Vertices(I,:);
        % Output structure
        NewTessMat.Faces    = Faces;
        NewTessMat.Vertices = Vertices;
        MethodTag = '_iso2mesh_proj';
end

%% ===== REMOVE FOLDED FACES =====
% Find equal faces
tmpFaces = sort(NewTessMat.Faces, 2);
[tmpFaces, iFaces] = unique(tmpFaces, 'rows');
% If there are some folded faces: delete them
if (length(iFaces) ~= size(NewTessMat.Faces,1))
    iRmFaces = setdiff(1:size(NewTessMat.Faces,1), iFaces);
    NewTessMat.Faces(iRmFaces,:) = [];
end

%% ===== CREATE NEW SURFACE STRUCTURE =====
% Build new filename and Comment
[filepath, filebase, fileext] = bst_fileparts(file_fullpath(TessFile));
NewComment = TessMat.Comment;
% Remove previous '_nbvertV' tags from Comment field
if (NewComment(end) == 'V')
    iUnderscore = strfind(NewComment, '_');
    if isempty(iUnderscore)
        iUnderscore = strfind(NewComment, ' ');
    end
    if ~isempty(~iUnderscore)
        NewComment = NewComment(1:iUnderscore(end)-1);
    end
end
% Remove previous '_nbvertV' tags from filename
if (filebase(end) == 'V')
    iUnderscore = strfind(filebase, '_');
    filebase = filebase(1:iUnderscore(end)-1);
end
% Add a '_nbvertV' tag
NewTessFile = file_unique(bst_fullfile(filepath, sprintf('%s_%dV%s', filebase, newNbVertices, fileext)));
NewComment  = sprintf('%s%s_%dV', NewComment, MethodTag, size(NewTessMat.Vertices,1));

% As per MMII convention - there should be one tessellation file per envelope
% A downsized version of e.g. a cortex is considered as a different envelope
% ans is therefore saved in a separate tessellation file than the original.
NewTessMat.Comment  = NewComment;
% Copy history field
if isfield(TessMat, 'History')
    NewTessMat.History = TessMat.History;
end
% History: Downsample surface
NewTessMat = bst_history('add', NewTessMat, 'downsample', sprintf('Downsample surface: %d -> %d vertices', oldNbVertices, newNbVertices));

%% ===== DOWNSAMPLE SCOUTS =====
% Existing atlases
if isfield(TessMat, 'Atlas') && ~isempty(TessMat.Atlas) && ~isempty(I)
    % Copy scout structure
    NewTessMat.Atlas = TessMat.Atlas;
    % Loop on all the scouts, and keep only those vertices
    for iAtlas = 1:length(NewTessMat.Atlas)
        iRmScout = [];
        for iScout = 1:length(NewTessMat.Atlas(iAtlas).Scouts)
            % Replace the old vertices index with the new ones
            [a,b,c] = intersect(NewTessMat.Atlas(iAtlas).Scouts(iScout).Vertices, I);
            NewTessMat.Atlas(iAtlas).Scouts(iScout).Vertices = reshape(sort(J(c)), 1, []);
            % If scout has no vertex left: tag for deletion
            if isempty(NewTessMat.Atlas(iAtlas).Scouts(iScout).Vertices)
                iRmScout(end+1) = iScout;
            end
        end
        % Remove empty scouts
        if ~isempty(iRmScout)
            NewTessMat.Atlas(iAtlas).Scouts(iRmScout) = [];
        end
        % Set scouts seeds
        NewTessMat.Atlas(iAtlas).Scouts = panel_scout('SetScoutsSeed', NewTessMat.Atlas(iAtlas).Scouts, NewTessMat.Vertices);
    end
end
% Selected atlas
if isfield(TessMat, 'iAtlas') && ~isempty(TessMat.iAtlas)
    NewTessMat.iAtlas = TessMat.iAtlas;
end

%% ===== DOWNSAMPLE REGISTRATION MAPS =====
% FreeSurfer spheres
if isfield(TessMat, 'Reg') && isfield(TessMat.Reg, 'Sphere') && isfield(TessMat.Reg.Sphere, 'Vertices') && ~isempty(TessMat.Vertices) && (length(TessMat.Reg.Sphere.Vertices) == length(TessMat.Vertices))
    % Keep only the selected indices
    if ~isempty(I)
        newSphVert = TessMat.Reg.Sphere.Vertices(I,:);
        NewTessMat.Reg.Sphere.Vertices = newSphVert;
    else
        NewTessMat.Reg.Sphere = [];
    end
end

if isfield(TessMat, 'Reg') && isfield(TessMat.Reg, 'SphereLR') && isfield(TessMat.Reg.SphereLR, 'Vertices') && ~isempty(TessMat.Vertices) && (length(TessMat.Reg.SphereLR.Vertices) == length(TessMat.Vertices))
    % Keep only the selected indices
    if ~isempty(I)
        newSphVert = TessMat.Reg.SphereLR.Vertices(I,:);
        NewTessMat.Reg.SphereLR.Vertices = newSphVert;
    else
        NewTessMat.Reg.SphereLR = [];
    end
end

% BrainSuite squares
if isfield(TessMat, 'Reg') && isfield(TessMat.Reg, 'Square') && isfield(TessMat.Reg.Square, 'Vertices') && ~isempty(TessMat.Reg.Square.Vertices) && (length(TessMat.Reg.Square.Vertices) == length(TessMat.Vertices))
    % Keep only the selected indices
    if ~isempty(I)
        newSqVert = TessMat.Reg.Square.Vertices(I,:);
        NewTessMat.Reg.Square.Vertices = newSqVert;
    else
        NewTessMat.Reg.Square = [];
    end
    NewTessMat.Reg.AtlasSquare=TessMat.Reg.AtlasSquare;
end

%% ===== UPDATE DATABASE =====
% Save downsized surface file
bst_save(NewTessFile, NewTessMat, 'v7');
% Make output filename relative
NewTessFile = file_short(NewTessFile);
% Get subject
[sSubject, iSubject] = bst_get('SurfaceFile', TessFile);
% Register this file in Brainstorm database
iSurface = db_add_surface(iSubject, NewTessFile, NewComment);

% Close progress bar
bst_progress('stop');

end

%% ===== iso2mesh_resample =====
% Resample a surface using iso2mesh/CGAL library
% Author: Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)
function NewTessMat = iso2mesh_resample(TessMat, dsFactor)
    % Check if iso2mesh is installed
    if ~exist('meshresample', 'file')
        bst_error(['Please install iso2mesh on your computer:

' ...
               '  1) Visit the website: http://iso2mesh.sourceforge.net
' ...
               '  2) Download the iso2mesh package for your operating system.
' ...
               '  3) Unzip it on your local hard drive.
' ...
               '  4) Add the iso2mesh folder to your Matlab path.
' ...
               '  5) Try again downsampling the surface.

'], 'Install iso2mesh', 0);
        web('http://sourceforge.net/projects/iso2mesh/files/iso2mesh/1.5.0%20%28iso2mesh%202013%29/', '-browser');
        NewTessMat = [];
        return;
    end
    % Running iso2mesh routine
    [Vertices,Faces] = meshresample(TessMat.Vertices, TessMat.Faces, dsFactor);
    % Error handling
    if isempty(Vertices) || isempty(Faces)
        error(['Iso2mesh failed downsampling this surface:' 10 'See Matlab command window for more information.']);
    end
    % Report results
    NewTessMat.Vertices = Vertices;
    NewTessMat.Faces    = Faces;
    % Swap faces
    % NewTessMat.Faces = NewTessMat.Faces(:,[2 1 3]);
end

% OLD VERSION: FUNCTION cgalsimp2 WAS INCLUDED IN BRAINSTORM DISTRIBUTION
% %% ===== iso2mesh_resample =====
% % Resample a surface using iso2mesh/CGAL library
% % Author: Qianqian Fang (fangq<at> nmr.mgh.harvard.edu)
% function NewTessMat = iso2mesh_resample(TessMat, dsFactor, nCall)
%     % First call
%     if (nargin < 3) || isempty(nCall)
%         nCall = 1;
%     end
%     % Get the executable name
%     switch(bst_get('OsType'))
%         case {'linux32', 'linux64'}, exePath = 'cgalsimp2.mexglx';
%         case 'mac32',                exePath = 'cgalsimp2.mexmaci';
%         case 'mac64',                exePath = 'cgalsimp2.mexmaci64';
%         case {'win32', 'win64'},     exePath = 'cgalsimp2.exe';
%         otherwise, error('CGAL executable is not available on your OS.');
%     end
%     % Add the full path
%     exePath = bst_fullfile(bst_get('BrainstormHomeDir'), 'external', 'iso2mesh', exePath);
%     % Get temporary mesh file
%     fin  = file_unique(bst_fullfile(bst_get('BrainstormTmpDir'), 'mesh_in.off'));
%     fout = file_unique(bst_fullfile(bst_get('BrainstormTmpDir'), 'mesh_out.off'));
%     % Save input file
%     out_tess_off(TessMat, fin);
%     % Execute cgalsimp2 with a system call
%     [status, result] = system(['"' exePath '" "' fin '" ' num2str(dsFactor) ' "' fout '"']);
%     if status
%         file_delete(fin, 1);
%         error(['CGAL failed downsampling this surface:' 10 result]);
%     end
%     % Read results
%     NewTessMat = in_tess_off(fout);
%     % Delete mesh files
%     file_delete({fin, fout}, 1);
%     % If no results are produced: Fix the surface and call it again
%     if isempty(NewTessMat.Vertices)
%         % If it is already the second call
%         if (nCall > 2)
%             error('CGAL failed downsampling this surface.');
%         end
%         % Remove duplicate faces
%         TessMat.Faces = removedupelem(TessMat.Faces);
%         % Remove isolated nodes
%         [TessMat.Vertices, TessMat.Faces] = removeisolatednode(TessMat.Vertices, TessMat.Faces);
%         % Run again the function
%         NewTessMat = iso2mesh_resample(TessMat, dsFactor, nCall + 1);
%     end
% end


function exact = p20_exact ( )

%*****************************************************************************80
%
%% P20_EXACT returns the estimated integral for problem 20.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    04 November 2009
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Output, real EXACT, the estimated value of the integral.
%
  exact = 1.5643964440690497731;

  return
end


function value = func ( x )

%*****************************************************************************80
%
%% FUNC evaluates a function of X, as chosen by the user.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    12 October 2005
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, real X, the argument of the function.
%
%    Output, real FUNC, the value of the function.
%
  ifunc = func_set ( 'GET', 'DUMMY' );

  if ( ifunc == 1 )
    value = 1.0;
  elseif ( ifunc == 2 )
    value = x;
  elseif ( ifunc == 3 )
    value = x.^2;
  elseif ( ifunc == 4 )
    value = x.^3;
  elseif ( ifunc == 5 )
    value = x.^4;
  elseif ( ifunc == 6 )
    value = x.^5;
  elseif ( ifunc == 7 )
    value = x.^6;
  elseif ( ifunc == 8 )
    value = x.^7;
  elseif ( ifunc == 9 )
    value = sin ( x );
  elseif ( ifunc == 10 )
    value = exp ( x );
  elseif ( ifunc == 11 )
    value = sqrt ( abs ( x ) );
  else
    value = 0.0;
  end

  return
end


function K = multiKernCompute(kern, varargin)
% MULTIKERNCOMPUTE Compute the MULTI kernel given the parameters and X.
% FORMAT
% DESC computes the kernel parameters for the multiple output block
% kernel given inputs associated with rows and columns.
% ARG kern : the kernel structure for which the matrix is computed.
% ARG x : the input matrix associated with the rows of the kernel.
% ARG x2 : the input matrix associated with the columns of the kernel.
% RETURN k : the kernel matrix computed at the given points.
%
% FORMAT
% DESC computes the kernel matrix for the multiple output block
% kernel given a design matrix of inputs.
% ARG kern : the kernel structure for which the matrix is computed.
% ARG x : input data matrix in the form of a design matrix.
% RETURN k : the kernel matrix computed at the given points.
%
% SEEALSO : multiKernParamInit, kernCompute, kernCreate, multiKernDiagCompute
%
% COPYRIGHT : Neil D. Lawrence, 2006
%
% COPYRIGHT : Pei Gao, 2007

% KERN

if iscell(varargin{1})  % If x is a cell array of input points
  if length(varargin{1}) ~= kern.numBlocks
    error('Time information is not matched among blocks!');
  end
  for i = 1:kern.numBlocks
    dim1(i) = size(varargin{1}{i}, 1);
    if length(varargin)>1  % If x2 is provided it must also be a cell array
      if length(varargin{1}) ~= length(varargin{2})
        error('Time information is not matched within the block!');
      end
      dim2(i) = size(varargin{2}{i}, 1);
    else
      dim2(i) = dim1(i); % If x2 is provided is not provided just use x1
    end

  end

  K = zeros(sum(dim1), sum(dim2));

  for i = 1:kern.numBlocks
    % Compute the i:th diagonal block
    startOne = sum(dim1(1:(i-1))) + 1;
    endOne = sum(dim1(1:i));
    startThree = sum(dim2(1:(i-1))) + 1;
    endThree = sum(dim2(1:i));

    % Compute the block if it has a nonzero amount of rows and columns
    if ((endOne >= startOne) && (endThree >= startThree)),
      if length(varargin)<2
        K(startOne:endOne, startThree:endThree) = multiKernComputeBlock(kern, ...
                                                      varargin{1}{i}, i, i);
      else
        K(startOne:endOne, startThree:endThree) = multiKernComputeBlock(kern, ...
                                       varargin{1}{i}, varargin{2}{i}, i, i);
      end
    end;

    % Compute the off-diagonal blocks in the same row/column as the i:th diagonal block
    for j = 1:i-1
      if ~isempty(kern.block{i}.cross{j})
        startTwo = sum(dim2(1:(j-1))) + 1;
        endTwo =  sum(dim2(1:j));

        if length(varargin)<2
          % If a separate set of column-inputs has not been given,
          % row and column inputs are the same

	  %size(K)
	  %startOne
	  %endOne
	  %startTwo
	  %endTwo
	  %size(varargin{1})
	  %i
	  %j
	  %K(startOne:endOne, startTwo:endTwo)
	  %varargin{1}{i}
	  %varargin{1}{j}
          %kern
          %tempK = multiKernComputeBlock(kern, ...
          %                      varargin{1}{i}, varargin{1}{j}, i, j)

	  % Compute the block if it has a nonzero amount of rows and columns
	  if ((endOne >= startOne) && (endTwo >= startTwo)),
            K(startOne:endOne, startTwo:endTwo) = multiKernComputeBlock(kern, ...
                                  varargin{1}{i}, varargin{1}{j}, i, j);
  	    % Since the row inputs are the same as the column inputs, the
            % corresponding block on the other side of the diagonal is just
            % the transpose of the one just computed.
            K(startTwo:endTwo, startOne:endOne) = K(startOne:endOne, ...
                                                  startTwo:endTwo)';
          end;
        else
          % If a separate set of column-inputs has been given, use that

	  % Compute the block if it has a nonzero amount of rows and columns
	  if ((endOne >= startOne) && (endTwo >= startTwo)),
            K(startOne:endOne, startTwo:endTwo) = multiKernComputeBlock(kern, ...
                                  varargin{1}{i}, varargin{2}{j}, i, j);
          end;

	  % Since the row inputs are not the same as the column inputs, the
          % corresponding block on the other side of the diagonal is not just
          % a transpose, so we compute it separately here.
          startFour = sum(dim1(1:(j-1))) + 1;
          endFour =  sum(dim1(1:j));
	  % Compute the block if it has a nonzero amount of rows and columns
	  if ((endFour >= startFour) && (endThree >= startThree)),
            K(startFour:endFour, startThree:endThree) = ...
                multiKernComputeBlock(kern, varargin{2}{i}, varargin{1}{j}, j, i)';
          end;
        end
      end
    end
  end

else
  dim1 = size(varargin{1}, 1);

  if length(varargin)>1
    dim2 = size(varargin{2}, 1);
  else
    dim2 = dim1;
  end

  K = zeros(kern.numBlocks*dim1, kern.numBlocks*dim2);

  for i = 1:kern.numBlocks
    startOne = (i-1)*dim1 + 1;
    endOne = i*dim1;
    startThree = (i-1)*dim2 + 1;
    endThree = i*dim2;
    K(startOne:endOne, startThree:endThree) = multiKernComputeBlock(kern, ...
                                                      varargin{:}, i, i);
    for j = 1:i-1
      if ~isempty(kern.block{i}.cross{j})
        startTwo = (j-1)*dim2 + 1;
        endTwo = j*dim2;
        K(startOne:endOne, startTwo:endTwo) = multiKernComputeBlock(kern, ...
                                                          varargin{:}, i, j);
        if length(varargin)<2
          K(startTwo:endTwo, startOne:endOne) = K(startOne:endOne, ...
                                                startTwo:endTwo)';
        else
          startFour = (j-1)*dim1 + 1;
          endFour = j*dim1;
          K(startFour:endFour, startThree:endThree) = ...
              multiKernComputeBlock(kern, varargin{end:-1:1}, j, i)';
        end
      end
    end
  end

end


function x = p17_start ( n )

%*****************************************************************************80
%
%% P17_START returns a starting point for optimization for problem 17.
%
%  Licensing:
%
%    This code is distributed under the GNU LGPL license.
%
%  Modified:
%
%    16 March 2000
%
%  Author:
%
%    John Burkardt
%
%  Parameters:
%
%    Input, integer N, the number of variables X.
%
%    Output, real X(N), a starting point for the optimization.
%
  x = [ -3.0, -1.0, -3.0, -1.0 ]';

  return
end


function [idxa,idxb,flag] = gen_train_sample_kissme(label_train, cam_train)

% generate ground truth pairs for training
uni_label = unique(label_train);
idxa = []; % index of the first image in a pair
idxb = []; % index of the second image in a pair
flag = []; % indicate whether two images are of the same identity
for n = 1:length(uni_label)
    curr_label = uni_label(n);
    pos = find(label_train == uni_label(n));
    if length(pos) == 1
        pos = [pos, pos];
    end
    comb = nchoosek(pos,2);
    idxa = [idxa; comb(:, 1)];
    idxb = [idxb; comb(:, 2)];
end
% remove pairs from the same camera
cam1 = cam_train(idxa);
cam2 = cam_train(idxb);
Eq_pos = find(cam1 == cam2);
diff_pos = setdiff(1:length(idxa), Eq_pos);
idxa = idxa(diff_pos);
idxb = idxb(diff_pos);
nPos = length(idxa);
flag = [flag; ones(nPos, 1)];

% generate negative training pairs
nTrainImg = length(label_train);
rand_pos = ceil(rand(150000, 2).*nTrainImg);
ID1 = label_train(rand_pos(:, 1));
ID2 = label_train(rand_pos(:, 2));
Eq_pos = find(ID1 == ID2);
diff_pos = setdiff(1:150000, Eq_pos); % remove pairs of the same identity
rand_pos = rand_pos(diff_pos, :);
cam1 = cam_train(rand_pos(:, 1));
cam2 = cam_train(rand_pos(:, 2));
Eq_pos = find(cam1 == cam2);
diff_pos = setdiff(1:length(rand_pos), Eq_pos);% remove pairs of the same camera

%%%% training image pairs and their ground truth labels %%%%%%%%
idxa = [idxa; rand_pos(diff_pos(1:nPos), 1)];
idxb = [idxb; rand_pos(diff_pos(1:nPos), 2)];
flag = [flag; zeros(nPos, 1)];


function [params] = evalValidTest(model, validData, testData, params)
  startTime = clock;
  [validCosts] = evalCost(model, validData, params);
  [testCosts] = evalCost(model, testData, params);

  validCounts = initCosts();
  validCounts = updateCounts(validCounts, validData);
  validCosts = scaleCosts(validCosts, validCounts);

  testCounts = initCosts();
  testCounts = updateCounts(testCounts, testData);
  testCosts = scaleCosts(testCosts, testCounts);

  modelStr = wInfo(model);
  endTime = clock;
  timeElapsed = etime(endTime, startTime);

  params.curTestPerpWord = exp(testCosts.word);
  [params.scaleTrainCosts] = scaleCosts(params.trainCosts, params.trainCounts);
  logStr = sprintf('# eval %.2f, %d, %d, %.2fK, %.2f, train=%.2f, valid=%.2f, test=%.2f,%s, time=%.2fs', params.curTestPerpWord, ...
    params.epoch, params.iter, params.speed, params.lr, ...
    params.scaleTrainCosts.total, validCosts.total, testCosts.total, modelStr, timeElapsed);
  fprintf(2, '%s\n', logStr);
  fprintf(params.logId, '%s\n', logStr);

  if validCosts.total < params.bestCostValid
    params.bestCostValid = validCosts.total;
    params.costTest = testCosts.total;
    params.testPerplexity = params.curTestPerpWord;

    fprintf(2, '  save model test perplexity %.2f to %s\n', params.testPerplexity, params.modelFile);
    fprintf(params.logId, '  save model test perplexity %.2f to %s\n', params.testPerplexity, params.modelFile);
    save(params.modelFile, 'model', 'params');

    if params.saveHDF
      saveHDF5([params.modelFile '.h5'], model, params);
    end
  end
end


% Copyright (C) 1993-2013, by Peter I. Corke
%
% This file is part of The Machine Vision Toolbox for MATLAB (MVTB).
%
% MVTB is free software: you can redistribute it and/or modify
% it under the terms of the GNU Lesser General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% MVTB 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 Lesser General Public License for more details.
%
% You should have received a copy of the GNU Leser General Public License
% along with MVTB.  If not, see <http://www.gnu.org/licenses/>.
%
% http://www.petercorke.com

%%begin

% We will show how to estimate the background of a scene, those pixels that
% are constant from frame to frame, and to estimate those parts of the scene
% that are changing.  We will demonstrate this on a live video feed.

% Create an object which represents a video camera connected to your computer
% If you have a laptop you should see the video recording light come on
%
% Type the q-key to stop this running

camera = VideoCamera('grey', 'double');

% take a frame from the camera as initial estimate of background
bg = camera.grab();

% rate of change threshold
sigma = 5/255.0;

% now we loop over all frames
while true
    % grab the image
    im = camera.grab();
    % compute the difference between the new frame and background estimate
    d = im-bg;
    % clip it to the range -sigma to sigma
    d = max(min(d, sigma), -sigma);
    % update the background, adjust background pixel values towards the new image
    bg = bg + d;

    % display live, estimated background and motion pixels
    idisp(im, 'nogui', 'figure', 1, 'title', 'live video');
    idisp(bg, 'nogui', 'figure', 2, 'title', 'background image');
    idisp(abs(im-bg), 'nogui', 'figure', 3, 'title', 'motion image');

    drawnow

    % test whether to quit, hit 'q'
    if get(gcf,'CurrentCharacter') == 'q'
        set(gcf,'CurrentCharacter', '-')
        break;
    end;
end
clear camera   % close the video source


function g = computeForwardKinematics(obj)
    % computes the forward kinematics transformation matrix of the
    % coordinate frame
    %
    % Return values:
    % g: the forward transformation matrix under the coordinate
    % system @type SymExpression

    frame = obj;
    while ~isprop(frame, 'TwistPairs') %isempty(frame.TwistPairs)
        frame = frame.Reference;
        if isempty(frame)
            error('The coordinate system is not fully defined.');
        end
    end

    twists = frame.TwistPairs;

    g = eval_math_fun('ForwardKinematics',[twists,{obj.gst0}]);

end


% test reading mouse data

%% data locations

% path to root directory containing the input data
inmoviedir = 'Y:\popcage_enriched\mousetrack_18';
intrxdir = 'Y:\adam\mousetrack_18\Results\Tracks';
% base name of the input experiment
inmovieexpname = 'b6_popcage_18_09.15.11_10.56.24.135';
intrxexpname = 'b6_popcage_18_2011.09.15_10.56.24.135';

% path to root directory to hold the output data
outdatadir = 'C:\Workspace\data';

%% parameters

% scale
mperpx = 0.00098387;
pxpermm = 1/(1000*mperpx);

% i assigned this arbitrarily for now
sex = {'F','M','M','F'};

% whether to make links or to copy
makelinks = true;

% frame interval length
frameintervallength = 108000;

% read in length of movie
inseqfile = fullfile(inmoviedir,[inmovieexpname,'.seq']);
headerinfo = r_readseqinfo(inseqfile);
nframes = headerinfo.m_iNumFrames;

%% main function call

for intervalstart = 1:frameintervallength:nframes,
  intervalend = intervalstart + frameintervallength - 1;
  if intervalend > nframes,
    break;
  end
  frameinterval = [intervalstart,intervalend];
  fprintf('Creating experiment for frames %d to %d...\n',intervalstart,intervalend);
  ConvertMouseTrx2Trx(inmoviedir,intrxdir,outdatadir,inmovieexpname,intrxexpname,...
    pxpermm,sex,'frameinterval',frameinterval,'makelinks',makelinks);
end


function clOut = get_cluster_volume(clIn)
% :Usage:
% ::
%
%    clOut = get_cluster_volume(clIn)
%
% Adapted from:
%
% FORMAT clusters = ihb_getClusters
%
% Get cluster information (use [SPM,VOL,xX,xCon,xSDM] = spm_getSPM;)
%
% ..
%    01.03.01    Sergey Pakhomov
%    01.08.01    last modified
%
%    Only change is to eliminate stuff in beginning - just update the cluster.
%    Tor Wager, 10/18/01
% ..

clOut = clIn;

%----------------------------------------------------------------------------------
% Define default voxel size for canonical image
% This is OK to leave at 1's, because the head plotting utility adjusts
% to millimeters.
%----------------------------------------------------------------------------------
ihbdfl_tal_x_vox = 1;
ihbdfl_tal_y_vox = 1;
ihbdfl_tal_z_vox = 1;

%----------------------------------------------------------------------------------
% Prepare MNI volume data xMNI, yMNI, zMNI, and volMNI
%----------------------------------------------------------------------------------
rMin = min(clOut.XYZmm');
rMax = max(clOut.XYZmm');
voxMNI = clOut.voxSize;

% make all voxMNI positive: Correct bug with negative x voxel size
voxMNI = abs(voxMNI);

[xMNI, yMNI, zMNI] = meshgrid(...
    rMin(1) - voxMNI(1) : voxMNI(1) : rMax(1) + voxMNI(1),...
    rMin(2) - voxMNI(2) : voxMNI(2) : rMax(2) + voxMNI(2),...
    rMin(3) - voxMNI(3) : voxMNI(3) : rMax(3) + voxMNI(3));

volMNI = zeros(size(xMNI));

[x y z] = size(xMNI);

for curVox = 1:clOut.numVox
    tmp = clOut.XYZmm(:, curVox);
    ind1 = (tmp(1) - rMin(1))/voxMNI(1) + 1;
    ind2 = (tmp(2) - rMin(2))/voxMNI(2) + 1;
    ind3 = (tmp(3) - rMin(3))/voxMNI(3) + 1;
    index = round(x*y*ind3 + x*ind1 + ind2 + 1);  % tor changed to avoid rounding error sometimes - 11/13
    volMNI(index) = clOut.Z(curVox);

    % waitbar stuff too much of a pain.
    %nTotalProcessed = nTotalProcessed + 1;
    %waitbar(nTotalProcessed/nTotalVox);
end

sizeMNI = size(volMNI);
sizeMNIprod = prod(sizeMNI);
xMNIreshaped = reshape(xMNI, [1 sizeMNIprod]);
yMNIreshaped = reshape(yMNI, [1 sizeMNIprod]);
zMNIreshaped = reshape(zMNI, [1 sizeMNIprod]);
xyzReshaped = [xMNIreshaped; yMNIreshaped; zMNIreshaped];

% tor eliminated this transformation line to keep in MNI space
%xyzTal = ihb_Mni2Tal(xyzReshaped);

xyzTal = xyzReshaped;

xx = reshape(xyzTal(1, :), sizeMNI);
yy = reshape(xyzTal(2, :), sizeMNI);
zz = reshape(xyzTal(3, :), sizeMNI);

clOut.xMin = ihbdfl_tal_x_vox*floor(min(xyzTal(1, :))/ihbdfl_tal_x_vox);
clOut.yMin = ihbdfl_tal_y_vox*floor(min(xyzTal(2, :))/ihbdfl_tal_y_vox);
clOut.zMin = ihbdfl_tal_z_vox*floor(min(xyzTal(3, :))/ihbdfl_tal_z_vox);

clOut.xMax = ihbdfl_tal_x_vox*ceil(max(xyzTal(1, :))/ihbdfl_tal_x_vox);
clOut.yMax = ihbdfl_tal_y_vox*ceil(max(xyzTal(2, :))/ihbdfl_tal_y_vox);
clOut.zMax = ihbdfl_tal_z_vox*ceil(max(xyzTal(3, :))/ihbdfl_tal_z_vox);

[clOut.xTal, clOut.yTal, clOut.zTal] = meshgrid(clOut.xMin:ihbdfl_tal_x_vox:clOut.xMax,...
                                                clOut.yMin:ihbdfl_tal_y_vox:clOut.yMax,...
                                                clOut.zMin:ihbdfl_tal_z_vox:clOut.zMax);
volTal = interp3(xx, yy, zz, volMNI, clOut.xTal, clOut.yTal, clOut.zTal, 'linear');
index = find(isnan(volTal(:,:,:)));
volTal(index) = 0;
clOut.vTal = volTal;


function [wordS, start, stop] = word(stringS,n)
%function [wordS start stop] = word(stringS,n)
%Returns the n'th blank delimited word in string of words. n
%must be positive. If there are fewer than n words in the string,
%the null string is returned.  The beginning and ending indices are
%given by 'start' and 'stop'.
%Inspired by the REXX function 'word'.
%
%ex:
%   c=word('a and b',3)
%
%LM: 7 March 02, JOD.
%
% Copyright 2010, Joseph O. Deasy, on behalf of the CERR development team.
%
% This file is part of The Computational Environment for Radiotherapy Research (CERR).
%
% CERR development has been led by:  Aditya Apte, Divya Khullar, James Alaly, and Joseph O. Deasy.
%
% CERR has been financially supported by the US National Institutes of Health under multiple grants.
%
% CERR is distributed under the terms of the Lesser GNU Public License.
%
%     This version of CERR is free software: you can redistribute it and/or modify
%     it under the terms of the GNU General Public License as published by
%     the Free Software Foundation, either version 3 of the License, or
%     (at your option) any later version.
%
% CERR is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
% without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
% See the GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with CERR.  If not, see <http://www.gnu.org/licenses/>.

%Get the number of blank-delimited words:
num_words=words(stringS);

len = length(stringS);

if num_words<n
   wordS='';
   start = '';
   stop  = '';
else
   index=1;
   indices_begin=[];
   indices_end=[];
   beginning=0;
   ending=1;
   wordNum = 0;
   while index<= len
      char=stringS(index);
      if ~isspace(char) && beginning==0
        indices_begin=[indices_begin,index];
        ending=0;
        beginning=1;
      end
      if isspace(char) && ending==0
        indices_end=[indices_end,index-1];
        beginning=0;
        ending=1;
        wordNum = wordNum + 1;
        if wordNum == n
          index = len + 1; %finish
        end
      end
      index=index+1;
   end
   %special case: if the last character was not a space,
   %then we need to count it as ending a word:
   pos = len;
   if ~isspace(stringS(pos))
     indices_end=[indices_end,pos];
   end
   wordS=stringS(indices_begin(n):indices_end(n));
   start = indices_begin(n);
   stop  = indices_end(n);
end


%  Copyright (c) 2014, Karen Simonyan
%  All rights reserved.
%  This code is made available under the terms of the BSD license (see COPYING file).

function [res, extra] = eval(config, scores, gt)

    [~,~,info] = vl_pr(gt, scores);

    res = info.auc * 100;
    extra = info;
end


clc
clear all
names = {'DenseFuse', 'RFN-Nest', 'FusionGAN', 'SeAFusion', 'PIAFusion', 'IFCNN', 'PMGI', 'SDNet', 'U2Fusion'};
rows = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'] ;
easy = 1; %% easy=1 用于测试：EN, SF,SD,PSNR,MSE, MI, VIF, AG, CC, SCD, Qabf等指标； easy=0 用于测试：Nabf, SSIM, MS_SSIM, FMI_pixel, FMI_dct, FMI_w等指标
dataset = 'TNO'
row_name1 = 'row1';
row_data1 = 'row2';
for i = 1 : length(names)
    method_name = cellstr(names(i));
    row = rows(i);
    row_name = strrep(row_name1, 'row', row);
    row_data = strrep(row_data1, 'row', row);
    fileFolder=fullfile('../Image/Source-Image', dataset, 'ir'); % 源图像A所在文件夹 此处是'Evaluation\Image\Source-Image\TNO\ir'
    dirOutput=dir(fullfile(fileFolder,'*.*'));
    fileNames = {dirOutput.name};
    [m, num] = size(fileNames);
    ir_dir = fullfile('../Image/Source-Image', dataset, 'ir'); % 源图像A所在文件夹 此处是'Evaluation\Image\Source-Image\TNO\ir'
    vi_dir = fullfile('../Image/Source-Image', dataset, 'vi'); % 源图像B所在文件夹 此处是'Evaluation\Image\Source-Image\TNO\vi'
    Fused_dir = '../';
    Fused_dir = fullfile(Fused_dir, 'Image', 'Algorithm', strcat(cell2mat(names(i)), '_', dataset)); % 融合结果所在文件夹 此处是 'Evaluation\Image\Algorithm\DenseFuse_TNO'
    EN_set = [];    SF_set = [];SD_set = [];PSNR_set = [];
    MSE_set = [];MI_set = [];VIF_set = []; AG_set = [];
    CC_set = [];SCD_set = []; Qabf_set = [];
    SSIM_set = []; MS_SSIM_set = [];
    Nabf_set = [];FMI_pixel_set = [];
    FMI_dct_set = []; FMI_w_set = [];
     for j = 1:num
        if (isequal(fileNames{j}, '.') || isequal(fileNames{j}, '..'))
            continue;
        else
            fileName_source_ir = fullfile(ir_dir, fileNames{j});
            fileName_source_vi = fullfile(vi_dir, fileNames{j});
            fileName_Fusion = fullfile(Fused_dir, fileNames{j});
            ir_image = imread(fileName_source_ir);
            vi_image = imread(fileName_source_vi);
            fused_image   = imread(fileName_Fusion);
            if size(ir_image, 3)>2
                ir_image = rgb2gray(ir_image);
            end

            if size(vi_image, 3)>2
                vi_image = rgb2gray(vi_image);
            end

            if size(fused_image, 3)>2
                fused_image = rgb2gray(fused_image);
            end

            [m, n] = size(fused_image);
        %     fused_image = fused_image(7:m-6, 7:n-6);
            ir_size = size(ir_image);
            vi_size = size(vi_image);
            fusion_size = size(fused_image);
            if length(ir_size) < 3 && length(vi_size) < 3
                [EN, SF,SD,PSNR,MSE, MI, VIF, AG, CC, SCD, Qabf, Nabf, SSIM, MS_SSIM, FMI_pixel, FMI_dct, FMI_w] = analysis_Reference(fused_image,ir_image,vi_image, easy);
                EN_set = [EN_set, EN];SF_set = [SF_set,SF];SD_set = [SD_set, SD];PSNR_set = [PSNR_set, PSNR];
                MSE_set = [MSE_set, MSE];MI_set = [MI_set, MI]; VIF_set = [VIF_set, VIF];
                AG_set = [AG_set, AG]; CC_set = [CC_set, CC];SCD_set = [SCD_set, SCD];
                Qabf_set = [Qabf_set, Qabf]; Nabf_set = [Nabf_set, Nabf];
                SSIM_set = [SSIM_set, SSIM]; MS_SSIM_set = [MS_SSIM_set, MS_SSIM];
                FMI_pixel_set = [FMI_pixel_set, FMI_pixel]; FMI_dct_set = [FMI_dct_set,FMI_dct];
                FMI_w_set = [FMI_w_set, FMI_w];
            else
                disp('unsucessful!')
                disp( fileName_Fusion)
            end

            fprintf('Fusion Method:%s, Image Name: %s\n', cell2mat(names(i)), fileNames{j})
        end
    end
    save_dir = '../Metric'; %存放Excel结果的文件夹
    if exist(save_dir,'dir')==0
        mkdir(save_dir);
    end
    %% 将测试结果写入 Excel， 此处采用writetable， 第一行可能会有问题，算法名在第二行，评估结果从第三行开始
    file_name = fullfile(save_dir, strcat('Metric_', dataset, '.xlsx')); %存放Excel文件的文件名
    if easy ==1
        SD_table = table(SD_set');
        PSNR_table = table(PSNR_set');
        MSE_table = table(MSE_set');
        MI_table = table(MI_set');
        VIF_table = table(VIF_set');
        AG_table = table(AG_set');
        CC_table = table(CC_set');
        SCD_table = table(SCD_set');
        EN_table = table(EN_set');
        Qabf_table = table(Qabf_set');
        SF_table = table(SF_set');
        method_table = table(method_name);

        writetable(SD_table,file_name,'Sheet','SD','Range',row_data);
        writetable(PSNR_table,file_name,'Sheet','PSNR','Range',row_data);
        writetable(MSE_table,file_name,'Sheet','MSE','Range',row_data);
        writetable(MI_table,file_name,'Sheet','MI','Range',row_data);
        writetable(VIF_table,file_name,'Sheet','VIF','Range',row_data);
        writetable(AG_table,file_name,'Sheet','AG','Range',row_data);
        writetable(CC_table,file_name,'Sheet','CC','Range',row_data);
        writetable(SCD_table,file_name,'Sheet','SCD','Range',row_data);
        writetable(EN_table,file_name,'Sheet','EN','Range',row_data);
        writetable(Qabf_table,file_name,'Sheet','Qabf','Range',row_data);
        writetable(SF_table,file_name,'Sheet','SF','Range',row_data);

        writetable(method_table,file_name,'Sheet','SD','Range',row_name);
        writetable(method_table,file_name,'Sheet','PSNR','Range',row_name);
        writetable(method_table,file_name,'Sheet','MSE','Range',row_name);
        writetable(method_table,file_name,'Sheet','MI','Range',row_name);
        writetable(method_table,file_name,'Sheet','VIF','Range',row_name);
        writetable(method_table,file_name,'Sheet','AG','Range',row_name);
        writetable(method_table,file_name,'Sheet','CC','Range',row_name);
        writetable(method_table,file_name,'Sheet','SCD','Range',row_name);
        writetable(method_table,file_name,'Sheet','EN','Range',row_name);
        writetable(method_table,file_name,'Sheet','Qabf','Range',row_name);
        writetable(method_table,file_name,'Sheet','SF','Range',row_name);
    else
        Nabf_table = table(Nabf_set');
        SSIM_table = table(SSIM_set');
        MS_SSIM_table = table(MS_SSIM_set');
        FMI_pixel_table = table(FMI_pixel_set');
        FMI_dct_table = table(FMI_dct_set');
        FMI_w_table = table(FMI_w_set');
        method_table = table(method_name);

        writetable(Nabf_table,file_name,'Sheet','Nabf','Range',row_data);
        writetable(SSIM_table,file_name,'Sheet','SSIM','Range',row_data);
        writetable(MS_SSIM_table,file_name,'Sheet','MS_SSIM','Range',row_data);
        writetable(FMI_pixel_table,file_name,'Sheet','FMI_pixel','Range',row_data);
        writetable(FMI_dct_table,file_name,'Sheet','FMI_dct','Range',row_data);
        writetable(FMI_w_table,file_name,'Sheet','FMI_w','Range',row_data);

        writetable(method_table,file_name,'Sheet','Nabf','Range',row_name);
        writetable(method_table,file_name,'Sheet','SSIM','Range',row_name);
        writetable(method_table,file_name,'Sheet','MS_SSIM','Range',row_name);
        writetable(method_table,file_name,'Sheet','FMI_pixel','Range',row_name);
        writetable(method_table,file_name,'Sheet','FMI_dct','Range',row_name);
        writetable(method_table,file_name,'Sheet','FMI_w','Range',row_name);

    end
end


function [ imdb, roidb ] = imdb_sample_frames(imdb, roidb,max_samples_from_cls, offset )
    if nargin < 4
      offset = 0;
    end
    valid = zeros(size(imdb.image_ids));
    if iscell(max_samples_from_cls)
      [C,ia,ib] = intersect(imdb.image_ids, max_samples_from_cls);
      valid = ia;
    else
      nFrames = imdb.num_frames;
      count=0;
      for i=1:numel(nFrames)
        % shuffled_frames{i} = randperm(nFrames(i),min(nFrames(i),max_samples_from_cls)) + count;
        shuffled_frames{i} = round(linspace(1+offset,nFrames(i)-offset,min(nFrames(i)-2*offset,max_samples_from_cls))) + count;
        count = count + nFrames(i);
      end
      valid = [shuffled_frames{:}];
    end

     % clear images without any object from target_imdb

    roidb.rois = roidb.rois(valid);
    imdb.is_blacklisted = imdb.is_blacklisted(valid);
    imdb.sizes = imdb.sizes(valid,:);
    if isfield(imdb,'flip_from'),  imdb.flip_from = imdb.flip_from(valid); end
    imdb.image_ids = imdb.image_ids(valid);
    imdb.image_at = @(i) ...
    fullfile(imdb.image_dir, [imdb.image_ids{i} '.' imdb.extension]);
    if isfield(imdb,'vid_id'),    imdb.vid_id = imdb.vid_id(valid); end

end


function [re_image] = imresize_gpu(image,re_param)
%This file implements the imresize function running on GPU based on
%vl_nnbilinearsampler.
% Input:
% image         -      image to be processed, gpuArray
% re_param      -   if its size is one, it is the ratio that to be resized
%               -   if it contains two numbers, it is the resized size.
% Output:
% re_image  -      the resized image.
% By Xin Li, 2018-6-5, UCM

if numel(re_param)==1
    sz = size(image);
    output_sz = round(sz(1:2)*re_param);
else
    output_sz = re_param;
end

yi = linspace(-1, 1, output_sz(1));
xi = linspace(-1, 1, output_sz(2));
[xx,yy] = meshgrid(xi,yi);
yyxx = single([yy(:), xx(:)]') ;
g = reshape(yyxx, 2, output_sz(1), output_sz(2), []);
re_image = vl_nnbilinearsampler(image, gpuArray(g));

end


function [llh, nx] = tapas_ti_llh_state(y, x, u, theta, ptheta)
%% Generate state space and compute the likelihood.
%
% aponteeduardo@gmail.com
% copyright (C) 2016
%

nx = tapas_ti_gen_state(y, x, u, theta, ptheta);
llh = tapas_ti_llh(y, nx, u, theta, ptheta);

end


%########################################################################
%
%   - PPGI Toolbox -
%   A MATLAB toolbox for Photoplethysmography Imaging (PPGI)
%
% Author   : Christian S. Pilz
% Company  : The Nature of Space of Time
% Date     : 07.05.2019
%
% Contact  : cpi@partofthestars.com
% Web Page : www.partofthestars.com
%
% Version  : beta0.1
%
%########################################################################
%
%	circular_buffer.m:
%
% Description:
%
%   A  MATLAB circular buffer class to handle streaming data
%

classdef circular_buffer

   properties
        buf_;
        head_;
        tail_;
        max_size_;
        full_;
        cell_
   end

   methods

       function obj = circular_buffer(max_size,cell)

          if nargin > 0
             if isnumeric(max_size)
                obj.max_size_ = max_size;
                obj.buf_= [];
                obj.head_= 1;
                obj.tail_= 1;
                obj.full_= logical(false);
                obj.cell_= logical(cell);
             else
                error('size must be numeric')
             end
          end

       end

       function ret = capacity(obj)
           ret = obj.max_size_;
       end

       function ret =size(obj)
           ret = obj.max_size_;

            if obj.full_== 0
                if obj.head_ >= obj.tail_
                    ret = obj.head_ - obj.tail_;
                else
                    %ret = obj.max_size_ + obj.head_ - obj.tail_;
                    ret = obj.head_ - obj.tail_;
                end
            end
       end

       function obj = put(obj,data)
           if obj.cell_
               obj.buf_{obj.head_} = data;
           else
               obj.buf_(obj.head_,:) = data;
           end

%            if obj.full_
%                obj.tail_ = mod(obj.head_, obj.max_size_)+1;
%            end

           obj.head_ = mod(obj.head_, obj.max_size_)+1;
           obj.full_ = (obj.head_ == obj.tail_);
       end

       function [values, obj] = get(obj,window_size,shift)
           if obj.empty()
               %values=[];
               return;
           end
           try
               if obj.cell_
                   values=obj.buf_(1,obj.tail_:obj.tail_+window_size-1)
               else
                   if (obj.tail_+window_size-1)<=obj.max_size_
                       values=obj.buf_(obj.tail_:obj.tail_+window_size-1,:);
                   else
                       tail_delta=obj.max_size_-obj.tail_;
                       values=obj.buf_(obj.tail_:obj.tail_+tail_delta,:);
                       head_delta=window_size-tail_delta-1;
                       values=[values; obj.buf_(1:head_delta,:)];
                   end
               end
           catch exception
                throw(exception)
           end

           obj.tail_ = mod(obj.tail_ + shift, obj.max_size_);
           if obj.tail_ == 0
               obj.tail_=1;
           end
       end

       function obj = reset(obj)
           obj.head_ = obj.tail_;
           obj.full_ = logical(false);
       end

       function ret = empty(obj)
           %if head and tail are equal, we are empty
           ret = (obj.full_ == 0) & (obj.head_ == obj.tail_);
       end

       function ret = full(obj)
           %if tail is ahead the head by 1, we are full
           ret = obj.full_;
       end
   end
end


%SHOWPOSE Display a camera icon in 3D
%
%   h = cam.showpose(T)
%
%  Create a new camera at pose T, and return the graphics handle.
%
% The camera is depicted as a pyramid with the apex as the camera
% origin and the base plane normal to the optical axis.
% The sides are colored red, green, blue corresponding to the X, Y, Z axes
% respectively.

function h = showpose(c, T, sz)

    if nargin < 2
        T = eye(4,4);
    end
    if nargin < 3
        % get the overall scale factor from the existing graph
        sz = [get(gca, 'Xlim'); get(gca, 'Ylim'); get(gca, 'Zlim')];
        sz = max(sz(:,2)-sz(:,1));
        sz = sz / 20;
    end

    % define pyramid dimensions from the size parameter
    w = sz;
    l = sz*2;

    % define the vertices of the camera
    vertices = [
         0    0    0
         w/2  w/2  l
        -w/2  w/2  l
        -w/2 -w/2  l
         w/2 -w/2  l
        ];

    ud.vertices = vertices;

    % create the camera
    vertices = transformp(T, vertices')';

    % the first index for each face controls the face color
    faces = [
        1 2 5 NaN
        2 1 3 NaN
        3 4 1 NaN
        4 1 5 NaN
        5 2 3 4
        %2 3 4 5
        ];

    colors = [
        1 0 0       % R
        0 1 0       % G
        1 0 0       % R
        0 1 0       % G
        0 0 1       % B
        ];

    if isempty(c.camview)
        % create the camera view
        h = patch('Vertices', vertices, ...
            'Faces', faces, ...
            'FaceVertexCData', colors, ...
            'FaceColor','flat', ...
            'UserData', ud);

        set(h, 'FaceAlpha', 1.0);
        c.camview = h;
        c.camview_sz = sz;
    else
        set(c.camview, 'Vertices', vertices);
    end
end


classdef TestRotatedRect
    %TestRotatedRect

    methods (Static)
        function test_RotatedRect_from3points
            pts = {[10,10], [10,20], [20,20]};
            rrect = cv.RotatedRect.from3points(pts{:});
            validateattributes(rrect, {'struct'}, {'scalar'});
            assert(all(ismember({'center','size','angle'}, fieldnames(rrect))));
        end

        function test_RotatedRect_points
            rrect = struct('center',[10,20], 'size',[5,6], 'angle',30);
            pts = cv.RotatedRect.points(rrect);
            validateattributes(pts, {'numeric'}, {'size',[4 2]});
        end

        function test_RotatedRect_boundingRect
            rrect = struct('center',[10,20], 'size',[5,6], 'angle',30);

            rect = cv.RotatedRect.boundingRect(rrect);
            validateattributes(rect, {'numeric'}, {'vector', 'numel',4, 'integer'});

            rect = cv.RotatedRect.boundingRect2f(rrect);
            validateattributes(rect, {'numeric'}, {'vector', 'numel',4, 'real'});
        end
    end

end


function [scoremaps, locreg_pred, nextreg_pred] = cnn_process_image_tiled(input, net, sigmoid, im_bg_width, im_bg_height, stride)

    max_size = 1000;
    rf = 224; %receptive field
    cut_off = rf/stride;

    num_tiles_x = get_num_tiles(im_bg_width, max_size);
    num_tiles_y = get_num_tiles(im_bg_height, max_size);

    scoremaps = [];
    locreg_pred = [];
    nextreg_pred = [];

    for j = 1:num_tiles_y
        if j == 1
            start_y = 1;
        else
            start_y = (j-1)*(max_size-2*rf)+1;
        end
        if j == num_tiles_y
            end_y = im_bg_height;
        else
            end_y = start_y+max_size-1;
        end

        scoremaps_line = [];
        locreg_pred_line = [];
        nextreg_pred_line = [];

        for i = 1:num_tiles_x
            if i == 1
                start_x = 1;
            else
                start_x = (i-1)*(max_size-2*rf)+1;
            end
            if i == num_tiles_x
                end_x = im_bg_width;
            else
                end_x = start_x+max_size-1;
            end

            input_tile = input(start_y:end_y, start_x:end_x, :);

            [scoremaps_tile, locreg_pred_tile, nextreg_pred_tile] = cnn_process_image(input_tile, net, sigmoid);

            scoremaps_tile = cutoff_tile(scoremaps_tile, num_tiles_x, i, cut_off, true);
            locreg_pred_tile = cutoff_tile(locreg_pred_tile, num_tiles_x, i, cut_off, true);
            nextreg_pred_tile = cutoff_tile(nextreg_pred_tile, num_tiles_x, i, cut_off, true);

            scoremaps_line = cat(2, scoremaps_line, scoremaps_tile);
            locreg_pred_line = cat(2, locreg_pred_line, locreg_pred_tile);
            nextreg_pred_line = cat(2, nextreg_pred_line, nextreg_pred_tile);
        end

        scoremaps_line = cutoff_tile(scoremaps_line, num_tiles_y, j, cut_off, false);
        locreg_pred_line = cutoff_tile(locreg_pred_line, num_tiles_y, j, cut_off, false);
        nextreg_pred_line = cutoff_tile(nextreg_pred_line, num_tiles_y, j, cut_off, false);

        scoremaps = cat(1, scoremaps, scoremaps_line);
        locreg_pred = cat(1, locreg_pred, locreg_pred_line);
        nextreg_pred = cat(1, nextreg_pred, nextreg_pred_line);
    end

end

function [feat_prob, locreg_pred, next_pred] = cnn_process_image_tile(input, net, sigmoid)

    % switch width and height for Caffe
    input = permute(input, [2 1 3]);

    blob_size = [size(input), 1];
    net.blobs('data').reshape(blob_size);

    blob_size = [size(input), 1];

    batch = zeros(blob_size, 'single');
    batch(:,:,:,1) = input;
    batches = cell(1,1);
    batches{1} = batch;
    zs = net.forward(batches);

    % fc6_data = net.blobs('fc6-conv').get_data();

    locreg_pred = [];
    next_pred = [];

    for k = 1:length(zs);
        fmap = zs{k};
        if size(fmap, 3) == 14
            feat_prob = fmap;
            feat_prob = permute(feat_prob, [2 1 3]);
            continue;
        end
        fmap = zs{k};
        sz = size(fmap);
        fmap = reshape(fmap, sz(1), sz(2), 2, []);
        fmap = permute(fmap, [2 1 4 3]);
        if size(fmap, 3) == 14
            locreg_pred = fmap;
        else
            next_pred = fmap;
        end
    end

    if ~sigmoid
        % remove background class
        feat_prob = feat_prob(:,:,2:end);
    end
end

function num = get_num_tiles(sz, max_size)

rf = 224; %receptive field

if sz <= max_size
    num = 1;
    return;
end

k = 0;
while true
    % first and last tiles + middle tiles
    new_size = (max_size-rf)*2 + (max_size-2*rf)*k;
    if new_size >= sz
        break;
    end
    k = k + 1;
end

num = 2 + k;
end

function sm = cutoff_tile(sm, num_tiles, idx, cut_off, is_x)
    new_sz = [2 1 3 4];
    if is_x
        sm = permute(sm, new_sz);
    end

    if num_tiles == 1
    elseif idx == 1
        sm = sm(1:end-cut_off-1, :, :, :);
    elseif idx == num_tiles
        sm = sm(cut_off+1:end, :, :, :);
    else
        sm = sm(cut_off+1:end-cut_off-1, :, :, :);
    end

    if is_x
        sm = permute(sm, new_sz);
    end
end


% eeg_point2lat() - convert latency in data points to latency in ms relative
%                   to the time locking. Used in eeglab().
% Usage:
%       >> [newlat ] = eeg_point2lat( lat_array, [], srate);
%       >> [newlat ] = eeg_point2lat( lat_array, epoch_array,...
%                                 srate, timelimits, timeunit);
% Inputs:
%   lat_array   - latency array in data points assuming concatenated
%                 data epochs (see eeglab() event structure)
%   epoch_array - epoch number corresponding to each latency value
%   srate       - data sampling rate in Hz
%   timelimits  - [min max] timelimits in 'timeunit' units (see below)
%   timeunit    - time unit in second. Default is 1 = seconds.
%
% Outputs:
%   newlat      - converted latency values (in 'timeunit' units) for each epoch
%
% Example:
%   tmpevent = EEG.event;
%   eeg_point2lat( [ tmpevent.latency ], [], EEG.srate, [EEG.xmin EEG.xmax]);
%   % returns the latency of all events in second for a continuous
%   % dataset EEG
%
%   eeg_point2lat( [ tmpevent.latency ], [ tmpevent.epoch ],
%                 EEG.srate, [EEG.xmin EEG.xmax]*1000, 1E-3);
%   % returns the latency of all events in millisecond for a dataset
%   % containing data epochs.
%
%
% Author: Arnaud Delorme, CNL / Salk Institute, 2 Mai 2002
%
% See also: eeg_lat2point(), eeglab(), pop_editieventvals(), pop_loaddat()

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

function newlat = eeg_point2lat( lat_array, epoch_array, srate, timewin, timeunit);

if nargin <3
    help eeg_point2lat;
    return;
end
if isempty( epoch_array )
    epoch_array = ones( size(lat_array) );
end
if nargin <4
    timewin = 0;
end
if nargin <5
	timeunit = 1;
end

if length(lat_array) ~= length(epoch_array)
	if length(epoch_array)~= 1
		disp('eeg_point2lat: latency and epoch arrays must have the same length'); return;
	else
		epoch_array = ones(1,length(lat_array))*epoch_array;
	end
end
if length(timewin) ~= 2
    disp('eeg_point2lat: timelimits array must have length 2'); return;
end
if iscell(epoch_array)
	epoch_array = [ epoch_array{:} ];
end
if iscell(lat_array)
	lat_array = [ lat_array{:} ];
end

timewin = timewin*timeunit;

if length(timewin) == 2
    pnts = (timewin(2)-timewin(1))*srate+1;
else
    pnts = 0;
end
newlat  = ((lat_array - (epoch_array-1)*pnts-1)/srate+timewin(1))/timeunit;
newlat = round(newlat*1E9)*1E-9;


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

global planC stateS;
indexS = planC{end};

    originF = [str2double(get(handles.originFx, 'string')) ...
               str2double(get(handles.originFy, 'string')) ...
               str2double(get(handles.originFz, 'string'))];
    spacingF = [str2double(get(handles.spacingFx, 'string')) ...
               str2double(get(handles.spacingFy, 'string')) ...
               str2double(get(handles.spacingFz, 'string'))];

    originM = [str2double(get(handles.originMx, 'string')) ...
               str2double(get(handles.originMy, 'string')) ...
               str2double(get(handles.originMz, 'string'))];
    spacingM = [str2double(get(handles.spacingMx, 'string')) ...
               str2double(get(handles.spacingMy, 'string')) ...
               str2double(get(handles.spacingMz, 'string'))];

    Hist_Level = str2double(get(handles.Demons_HistLevel, 'string'));
    MatchPoints = str2double(get(handles.Demons_MatchPoints, 'string'));
    iternum = str2double(get(handles.Demons_iternum, 'string'));
    sd = str2double(get(handles.Demons_sd, 'string'));

    output = cell(1, 8);

    aIndex = stateS.currentAxis;
    hAxis = stateS.handle.CERRAxis(aIndex);
    axisInfo = get(hAxis, 'userdata');

    FImg = axisInfo.scanObj(stateS.imageRegistrationBaseDataset).data2M;
    MImg = axisInfo.scanObj(stateS.imageRegistrationMovDataset).data2M;

%     FImg = planC{indexS.scan}(stateS.imageRegistrationBaseDataset).scanArray;
%     MImg = planC{indexS.scan}(stateS.imageRegistrationMovDataset).scanArray;
%
%     %     downsample the input datasets
%     if (get(handles.dsampleCheck,'Value') == get(handles.dsampleCheck,'Max'))
%
%         tic;
%         FImg = imdesample3d(FImg,2,2);
%         MImg = imdesample3d(MImg,2,2);
%         toc;
%         DesampleTime = num2str(floor(toc/60));
%
%     end

    %     call registration method
    tic;
    [im, DF_X, DF_Y] = Demon(int16(FImg), originF, spacingF, ...
                             int16(MImg), originM, spacingM, ...
                             Hist_Level, MatchPoints, iternum, sd);

    toc;

    RegTime = num2str(floor(toc/60));

    imtool(im, [min(im(:)) max(im(:))]);
    imtool(DF_X.^2+DF_Y.^2); colormap colorcube;

% %update the transM;
%
%     Tv = [-Offset(4) Offset(5) 0];
%     rot = eye(3);
%     rot(1) = cos(Offset(1)*pi/180); rot(2,2) = rot(1);
%     rot(1,2) = -sin(Offset(1)*pi/180);
%     rot(2,1) = -rot(1,2);
%
%     TM = eye(4);
%     TM(:,4) = [Tv 1];
%
%     RM = eye(4);
%     RM(1:3, 1:3) = rot;
%
%     newTransform = TM*RM;
%
%     scanSetM = stateS.imageRegistrationMovDataset;
%     oldTransM = getTransM(stateS.imageRegistrationMovDatasetType, scanSetM, planC);
%     if isempty(oldTransM), oldTransM = eye(4); end;
%     planC{indexS.(stateS.imageRegistrationMovDatasetType)}(scanSetM).transM = (newTransform * oldTransM);
% %     planC{indexS.(stateS.imageRegistrationMovDatasetType)}(scanSetM).transM = newTransform;
%
%     %planC{indexS.dose}(scanSetM).transM = (newTransform * oldTransM);
%     sliceCallBack('refresh');
%

end


function uv = compute_flow(this, init, gt)
%
%COMPUTE_FLOW   Compute flow field
%   UV = COMPUTE_FLOW(THIS[, INIT]) computes the flow field UV with
%   algorithm THIS and the optional initialization INIT.
%
%   This is a member function of the class 'ba_optical_flow'.
%
% Authors: Deqing Sun, Department of Computer Science, Brown University
%          Stefan Roth, Department of Computer Science, TU Darmstadt
% Contact: dqsun@cs.brown.edu, sroth@cs.tu-darmstadt.de
% $Date: $
% $Revision: $
%
% Copyright 2007-2010, Brown University, Providence, RI. USA
%
%                          All Rights Reserved
%
% All commercial use of this software, whether direct or indirect, is
% strictly prohibited including, without limitation, incorporation into in
% a commercial product, use in a commercial service, or production of other
% artifacts for commercial purposes.
%
% Permission to use, copy, modify, and distribute this software and its
% documentation for research purposes is hereby granted without fee,
% provided that the above copyright notice appears in all copies and that
% both that copyright notice and this permission notice appear in
% supporting documentation, and that the name of the author and Brown
% University not be used in advertising or publicity pertaining to
% distribution of the software without specific, written prior permission.
%
% For commercial uses contact the Technology Venture Office of Brown University
%
% THE AUTHOR AND BROWN UNIVERSITY DISCLAIM ALL WARRANTIES WITH REGARD TO
% THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
% FITNESS FOR ANY PARTICULAR PURPOSE.  IN NO EVENT SHALL THE AUTHOR OR
% BROWN UNIVERSITY BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL
% DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
% PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
% ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
% THIS SOFTWARE.

  % Frame size
  sz = [size(this.images, 1), size(this.images, 2)];

  % If we have no initialization argument, initialize with all zeros
  if (nargin < 2)
    uv = zeros([sz, 2]);
  else
    uv = init;
  end

  % Preprocess input (gray) sequences
  if this.texture
      % Perform ROF structure texture decomposition
      images  = structure_texture_decomposition_rof( this.images, 1/8, 100, this.alp);

  elseif this.fc

      % Laplacian in flowfusion
      f = fspecial('gaussian', [5 5], 1.5);
      % Linear combine Laplacian image with original images
      images  = this.images- this.alp*imfilter(this.images, f, 'symmetric');

      % Gaussian pre-filering
      %         f = fspecial('gaussian', [3 3], 0.5);  % Li & Huttenlocher
      %         images  = imfilter(this.images, f, 'symmetric');

      % Sobel edge magnitude
      %         Dy = fspecial('sobel')/8;
      %         Dx = Dy';
      %         Ix = imfilter(this.images, Dx, 'symmetric');
      %         Iy = imfilter(this.images, Dy, 'symmetric');
      %         images = sqrt(Ix.^2 + Iy.^2);

      images  = scale_image(images, 0, 255);
  else
      images  = scale_image(this.images, 0, 255);
  end;

  if this.auto_level
      % Automatic determine pyramid level
      this.pyramid_levels  =  1 + floor( log(min(size(images, 1), size(images,2))/16) / log(this.pyramid_spacing) );
  end;

  % Construct image pyramid, using filter setting in Bruhn et al in "Lucas/Kanade.." (IJCV2005') page 218

  % For gnc stage 1
  factor            = sqrt(2);  % sqrt(3) worse
  smooth_sigma      = sqrt(this.pyramid_spacing)/factor;   % or sqrt(3) recommended by Manuel Werlberger
  f                 = fspecial('gaussian', 2*round(1.5*smooth_sigma) +1, smooth_sigma);
  pyramid_images    = compute_image_pyramid(images, f, this.pyramid_levels, 1/this.pyramid_spacing);

  % For gnc stage 2 to gnc_iters
  smooth_sigma      = sqrt(this.gnc_pyramid_spacing)/factor;
  f                 = fspecial('gaussian', 2*round(1.5*smooth_sigma) +1, smooth_sigma);
  gnc_pyramid_images= compute_image_pyramid(images, f, this.gnc_pyramid_levels, 1/this.gnc_pyramid_spacing);

  tic

  for ignc = 1:this.gnc_iters

      if this.display
          disp(['GNC stage: ', num2str(ignc)])
      end

      if ignc == 1
          pyramid_levels = this.pyramid_levels;
      else
          pyramid_levels = this.gnc_pyramid_levels;
      end;

      % Iterate through all pyramid levels starting at the top
      for l = pyramid_levels:-1:1

          if this.display
              disp(['-Pyramid level: ', num2str(l)])
          end

          % Generate copy of algorithm with single pyramid level and the
          % appropriate subsampling
          small = this;

          if ignc == 1
              nsz               = [size(pyramid_images{l}, 1) size(pyramid_images{l}, 2)];
              small.images      = pyramid_images{l};
              small.max_linear  = 1;             % number of linearization performed per warping, 1 OK for quadratic formulation

          else
              small.images         = gnc_pyramid_images{l};
              nsz   = [size(gnc_pyramid_images{l}, 1) size(gnc_pyramid_images{l}, 2)];

          end;

          % Rescale the flow field
          uv        = resample_flow(uv, nsz);

          % Run flow method on subsampled images
          uv = compute_flow_base(small, uv);
      end

      % Update GNC parameters (linearly)
      if this.gnc_iters > 1
          new_alpha  = 1 - ignc / (this.gnc_iters-1);
          this.alpha = min(this.alpha, new_alpha);
          this.alpha = max(0, this.alpha);
      end;

      if true % this.display

          fprintf('energy of solution \t%3.3e\n', -evaluate_log_posterior(small, uv));
          if nargin == 3
              [aae stdae aepe] = flowAngErr(gt(:,:,1), gt(:,:,2), uv(:,:,1), uv(:,:,2), 0);        % ignore 0 boundary pixels
              fprintf('AAE %3.3f STD %3.3f average end point error %3.3f \n', aae, stdae, aepe);
          end;

      end;

      fprintf('GNC stage %d finished, %3.2f minutes passed \n', ignc, toc/60);
  end;

  % minaly improves H&S, slightly improves others
%   if ~isempty(this.median_filter_size)
%       uv(:,:,1) = medfilt2(uv(:,:,1), this.median_filter_size, 'symmetric');
%       uv(:,:,2) = medfilt2(uv(:,:,2), this.median_filter_size, 'symmetric');
%   end;


function M = CGDA_LPP(TrainSample, d, W)

      options.PCARatio =0.9;
      options.ReducedDim=d;
      M= LPP(W, options, TrainSample');

end