classdef (Abstract) DatasetMC < handle
    %DATASET Abstract class that each dataset must inherit from.
    %
    %   For each new dataset, a new class must be created that inherits
    %   from Dataset. All properties must be implemented to ensure normal
    %   behavior throughout this codebase.
    %
    % Copyright by Holger Caesar, 2014

    % Public properties
    properties
        % General settings
        name
        path

        % Image settings
        imageCount = [];
        imageExt = '.jpg';
        imageFolder = 'Images';
        maxSize = 1024;
        fixSizeX = [];
        imageListFunc = @getImageListAll;

        % Label settings
        annotations    % All available annotations
        annotation     % The currently active annotation

        % Subset settings
        subset = 'train';
        imageSubset = '';
        imageSubsetGen = @(x) '';
    end

    % Private properties
    properties (Access = protected)
    end

    methods
        % Constructor
        function[obj] = DatasetMC()
            % Set default annotation
            obj.setDefaultAnnotation();
        end

        %%% Compatibility functions for older versions
        function[annotationFolder] = annotationFolder(obj)
            annotationFolder = obj.annotation.annotationFolder;
        end
        function[labelFolder] = labelFolder(obj)
            labelFolder = obj.annotation.labelFolder;
        end
        function[labelIdx] = labelIdx(obj)
            labelIdx = obj.annotation.labelIdx;
        end
        function[labelCount] = labelCount(obj)
            labelCount = obj.annotation.labelCount;
        end
        function[labelExt] = labelExt(obj)
            labelExt = obj.annotation.labelExt;
        end
        function[labelFormat] = labelFormat(obj)
            labelFormat = obj.annotation.labelFormat;
        end
        function[namesFile] = namesFile(obj)
            namesFile = obj.annotation.namesFile;
        end
        function[cmap] = cmap(obj)
            cmap = obj.annotation.cmap;
        end

        %%% Getters
        function[imagePath] = getImagePath(obj)
            % [imagePath] = getImagePath(obj)
            %
            % Get the path where the images lie.
            %
            % This function should be overwritten by Dataset
            % implementations if the imageFolder does not lie in the
            % dataset path.

            % Note: using fullfile here is too slow!
            imagePath = [obj.path, filesep, obj.imageFolder];
        end

        function[labelPath] = getLabelPath(obj)
            % [labelPath] = getLabelPath(obj)
            %
            % Get the path where the labels lie.
            %
            % This function should be overwritten by Dataset
            % implementations if the labelFolder does not lie in the
            % dataset path.
            % (formerly known as "getLabelsPath")

            % Note: using fullfile here is too slow!
            labelPath = [obj.path, filesep, obj.labelFolder];
        end

        function[labelImagePath] = getLabelImagePath(obj)
            % [labelImagePath] = getLabelImagePath(obj)
            %
            % Get the path where the image-level labels lie.

            labelImagePath = [obj.path, filesep, obj.annotation.labelImageFolder];
        end

        function[labelPath] = getAnnotationPath(obj)
            % [labelPath] = getAnnotationPath(obj)
            %
            % Get the root path of the annotation lie.
            %
            % This function should be overwritten by Dataset
            % implementations if the annotationFolder does not lie in the
            % dataset path.

            % Note: using fullfile here is too slow!
            labelPath = [obj.path, filesep, obj.annotationFolder];
        end

        function[metaPath] = getMetaPath(obj)
            % [metaPath] = getMetaPath(obj)
            %
            % Get the path where the meta files lie.
            %
            % This function should be overwritten by Dataset
            % implementations if the meta folder does not lie in the
            % dataset path.
            metaPath = fullfile(obj.getAnnotationPath(), obj.annotation.metaFolder);
        end

        function[namePath] = getNamePath(obj)
            % [namePath] = getNamePath(obj)
            %
            % Get the path of the name file.
            %
            % This function should be overwritten by Dataset
            % implementations if the meta folder does not lie in the
            % dataset path.
            % (formerly known as "getNamesPath")
            namePath = fullfile(obj.getMetaPath(), obj.namesFile);
        end

        function imageCount = getImageCount(obj)
            % imageCount = getImageCount(obj)
            %
            % Retrieve the number of images

            if isempty(obj.imageCount),
                % If not done yet, count the images
                [~, imageCount] = obj.getImageList();
                obj.imageCount = imageCount;
            else
                % Otherwise return the known result
                imageCount = obj.imageCount;
            end
        end

        function[annotation] = getAnnotation(obj, annotationName)
            % [annotation] = getAnnotation(obj, [annotationName])
            %
            % Returns the specified annotation struct of this dataset.
            % If no annotationName is set, the default is returned.

            % Return the selected annotation
            if ~exist('annotationName', 'var') || isempty(annotationName),
                % By active flag
                annotation = obj.annotations([obj.annotations.active]);
            else
                % By name
                annotation = obj.annotations(strcmp({obj.annotations.name}, annotationName));
            end
        end

        %%% Setters

        function setLabelIdx(obj, labelIdx)
            % setLabelIdx(obj, labelIdx)
            %
            % Set the label that is to be used and update the number of
            % images available for this label. labelIdx can be empty.

            % Set imageCount
            if isempty(labelIdx),
                obj.imageCount = obj.annotation.imageCount;
            else
                obj.imageCount = [];
            end

            % Set labelIdx
            obj.annotation.labelIdx = labelIdx;
        end

        function setDefaultAnnotation(obj)
            % setDefaultAnnotation(obj)
            %
            % Sets the default annotation for this dataset. This is useful
            % so that new datasets need not do manually, if  they choose to
            % stay with the default parameters.

            obj.annotations = AnnotationMC();
            obj.setActiveAnnotation('Original');
        end

        function setActiveAnnotation(obj, annotationName)
            % setActiveAnnotation(obj, annotationName)
            %
            % Find the annotation, set it to active and all others to
            % inactive.
            sel = strcmp({obj.annotations.name}, annotationName);
            if ~any(sel),
                error('Error: Unknown annotation: %s', annotationName);
            end
            obj.annotations(sel).active = true;
            obj.annotation = obj.annotations(sel);

            negInds = find(~sel);
            for i = 1 : numel(negInds),
                obj.annotations(negInds(i)).active = false;
            end

            % Set new imageListFunc
            obj.imageListFunc = obj.annotation.imageListFunc;

            % Set imageCount if all images are used (can be [])
            if isempty(obj.labelIdx),
                obj.imageCount = obj.annotation.imageCount;
            end
        end

        function setSubset(obj, subset)
            % setSubset(subset)
            %
            % Update the subset (train/val/test) that is used in this class.

            % Update subset names (there might be a nicer way, but it works)
            obj.subset = subset;
            obj.imageSubset = obj.imageSubsetGen(obj, subset);

            % The images should be counted again when necessary
            obj.imageCount = [];
        end

        %%% More complex functions
        function[stuffLabels, thingLabels, stuffLabelInds, thingLabelInds] = getStuffThingLabels(obj)
            % [stuffLabels, thingLabels, stuffLabelInds, thingLabelInds] = getStuffThingLabels(obj)
            %
            % Get names and indices of the stuff and thing labels.

            namesFilePath = fullfile(obj.getMetaPath(), obj.namesFile);
            namesStruct = load(namesFilePath, 'stuffLabels', 'thingLabels', 'stuffLabelInds', 'thingLabelInds');
            stuffLabels = namesStruct.stuffLabels;
            stuffLabelInds = namesStruct.stuffLabelInds;

            if isfield(namesStruct, 'thingLabels'),
                thingLabelInds = namesStruct.thingLabelInds;
                thingLabels = namesStruct.thingLabels;
            else
                thingLabelInds = setdiff((1:obj.labelCount)', stuffLabelInds);
                labelNames = obj.getLabelNames();
                thingLabels = labelNames(thingLabelInds);
            end
        end

        function[names, labelCount] = getLabelNames(obj)
            % [names, labelCount] = getLabelNames(obj)
            %
            % Get a cell of strings that specify the names of each label

            % Specify names file path
            namesFilePath = fullfile(obj.getMetaPath(), obj.namesFile);
            if ~exist(namesFilePath, 'file'),
                error('Error: Names file does not exist! Please execute ds_extractLabelNames() on the current annotation!');
            end

            % Retrieve names and labelCount
            namesStruct = load(namesFilePath, 'names');
            names = namesStruct.names;
            labelCount = numel(names);

            % Warning: If names are in the wrong order, we cannot find out
            % their indices
        end

        function[image] = getImage(obj, imageName, colorDim)
            % [image] = getImage(obj, imageName)
            %
            % Read an image from disk and correct its color dimensionality.
            %
            % Input:
            %   imageName:  file name without extension
            %   colorDim:   1 for grayscale or 3 for rgb
            % Output:
            %   image:      the image in double format.

            % Default arguments
            if ~exist('colorDim', 'var'),
                colorDim = 3;
            end

            % Create path
            imagePath = fullfile(obj.getImagePath(), [imageName, obj.imageExt]);

            % Read in image and convert to double
            image = im2double(imread(imagePath));

            % Resize image if it is too big
            % Attention: This might be dangerous, if boundingboxes still use the original image size.
            assert(~isempty(obj.maxSize) + ~isempty(obj.fixSizeX) <= 1);
            if ~isempty(obj.maxSize),
                imSize = [size(image, 1), size(image, 2)];
                maxImSize = max(imSize);
                if any(imSize > obj.maxSize),
                    newSize = round(imSize / maxImSize * obj.maxSize);
                    image = imresize(image, newSize);

                    % After resize, make sure that the maximum value is 1
                    image(image > 1) = 1;
                    image(image < 0) = 0;
                end
            end

            if ~isempty(obj.fixSizeX),
                % Resize image s.t. the width is fixed throughout the whole
                % dataset.
                if size(image, 2) ~= obj.fixSizeX,
                    image = imresize(image, [nan, obj.fixSizeX]);
                    assert(size(image, 2) == obj.fixSizeX);

                    % After resize, make sure that the maximum value is 1
                    image(image > 1) = 1;
                    image(image < 0) = 0;
                end
            end

            % Correct color if necessary
            if size(image, 3) ~= colorDim,
                if size(image, 3) == 3 && colorDim == 1,
                    image = rgb2gray(image);
                elseif size(image, 3) == 1 && colorDim == 3,
                    image = repmat(image, [1, 1, 3]);
                else
                    error('Error: Invalid color dim!');
                end
            end
        end

        function[imageListTrn, imageCountTrn] = getImageListTrn(obj, varargin)
            % [imageListTst, imageCountTst] = getImageListTrn(obj, varargin)
            %
            % Get a list of only the train images.

            imageList = obj.getImageList(varargin{:});
            trainList = obj.getTrainTestLists();
            imageListTrn = imageList(trainList);
            imageCountTrn = numel(imageListTrn);
        end

        function[imageListTst, imageCountTst] = getImageListTst(obj, varargin)
            % [imageListTst, imageCountTst] = getImageListTst(obj, varargin)
            %
            % Get a list of only the test images.

            imageList = obj.getImageList(varargin{:});
            [~, testList] = obj.getTrainTestLists();
            imageListTst = imageList(testList);
            imageCountTst = numel(imageListTst);
        end

        function[imageList, imageCount] = getImageList(obj, varargin)
            % [imageList, imageCount] = getImageList(obj, removeExt)
            %
            % Get list of images in dataset. This will call the specified
            % imageListFunc which can be updated to match special needs
            % (smaller dataset etc.)

            imageListFilePath = [obj.getMetaPath(), filesep, 'imageList.mat'];
            if exist(imageListFilePath, 'file'),
                imageListFileStruct = load(imageListFilePath, 'imageList');
                imageList = imageListFileStruct.imageList;
                imageCount = numel(imageList);
            else
                [imageList, imageCount] = obj.imageListFunc(obj, varargin{:});
                save(imageListFilePath, 'imageList', '-v6');
            end
        end

        function[imageList, imageCount] = getImageListSubset(obj, subset, varargin)
            % [imageList, imageCount] = getImageListSubset(obj, subset, varargin)
            %
            % Get the image names for a subset (or all).

            % Get images
            if strcmp(subset, 'train'),
                [imageList, imageCount] = obj.getImageListTrn(varargin{:});
            elseif strcmp(subset, 'test'),
                [imageList, imageCount] = obj.getImageListTst(varargin{:});
            elseif strcmp(subset, 'all'),
                [imageList, imageCount] = obj.getImageList(varargin{:});
            else
                error('Error: Unknown subset: %s', subset);
            end
        end

        function[imageList, imageCount] = getImageListAll(obj, removeExt)
            % [imageList, imageCount] = getImageListAll(obj, removeExt)
            %
            % Get list of all images in dataset.

            % Set default arguments
            if ~exist('removeExt', 'var')
                removeExt = true;
            end

            % Get a list of image names and rel. paths
            [imageList, imageCount] = dirSubfolders(obj.getImagePath(), obj.imageExt, removeExt);

            % Check that the images were really there
            if imageCount == 0,
                error('Error: Image input folder is empty!');
            end

            % Check if the number of images are as expected
            if ~isempty(obj.imageCount) && imageCount ~= obj.imageCount,
                error('Error: Dataset is not consistent! This error is non-deterministic, try again!');
            end

            % Save imageCount for other purposes
            obj.imageCount = imageCount;
        end

        function[labelFileList, labelFileCount] = getLabelList(obj, removeExt)
            % [labelFileList, labelFileCount] = getLabelList(obj, removeExt)
            %
            % Get a list of all image label files of the dataset.
            % Similar to getImageList, but with different subset and
            % extension.

            % Set default arguments
            if ~exist('removeExt', 'var')
                removeExt = true;
            end

            [labelFileList, labelFileCount] = obj.getImageList(true);

            % Append extension
            if ~removeExt,
                labelFileList = strcat(labelFileList, obj.labelExt);
            end
        end

        function[trainImages, testImages] = getTrainTestLists(obj)
            % [trainImages, testImages] = getTrainTestLists(obj)
            %
            % Get lists of train and test images of this dataset.
            splitFilePath = fullfile(obj.getMetaPath(), 'splits.mat');
            splitStruct = load(splitFilePath);
            splits = splitStruct.splits;
            trainImages = splits.train;
            testImages = splits.test;

            % Check consistency
            assert(numel(trainImages) + numel(testImages) == obj.imageCount);
            assert(numel(unique([trainImages; testImages])) == obj.imageCount)
        end

        function[labelListNames] = getImLabelList(obj, imageName)
            % [labelListNames] = getImLabelList(obj, imageName)
            %
            % Get a cell of label names that occur in the image ground truth.
            % labelListNames is unique (no duplicates)!

            % Since image labels can differ from pixel-derived image
            % labels, we want to check for image labels first.
            if obj.annotation.hasImageLabels,
                labelListNames = obj.getImLabelListDirectly(imageName);
            elseif obj.annotation.hasPixelLabels || obj.annotation.hasPixelLabelsOnlyTst
                if obj.annotation.hasPixelLabelsOnlyTst,
                    imageListTst = obj.getImageListTst();

                    if ~ismember(imageName, imageListTst),
                        % For unlabeled train images we cannot use the
                        % labelMap
                        labelListNames = obj.getImLabelListDirectly(imageName);
                        return;
                    end
                end

                % Load entire labelMap
                labelMap = obj.getImLabelMap(imageName);
                labelNames = obj.getLabelNames();
                labelInds = unique(labelMap(:));
                labelInds(labelInds == 0) = [];
                labelListNames = labelNames(labelInds);
            else
                error('Error: Cannot find suitable labels!');
            end
        end

        function[labelListNames] = getImLabelListDirectly(obj, imageName)
            % [labelListNames] = getImLabelListDirectly(obj, imageName)
            %
            % Get a cell of label names that occur in the image ground truth.
            % Contrary to getImLabelList() these are not derived from the
            % pixel-level labels.
            % labelListNames is unique (no duplicates)!

            labelPath = [obj.getLabelImagePath(), filesep, imageName, obj.labelExt];
            labelImageFormat = obj.annotation.labelImageFormat;
            if strcmp(labelImageFormat, 'txt-labelList'),
                % Load ground truth
                fileContent = fileread(labelPath);

                % Split lines
                labelListNames = strsplit(fileContent, '\n');
                labelListNames = cellRemoveEmptyEntries(labelListNames);
            elseif strcmp(labelImageFormat, 'mat-labelList'),
                % Load ground truth
                labelStruct = load(labelPath, 'labelList');

                % Split lines
                labelListNames = labelStruct.labelList;
            elseif strcmp(labelImageFormat, 'xml') || strcmp(labelImageFormat, 'xml-ImageNet'),
                % Load ground truth
                fileContent = fileread(labelPath);

                % Extract xml fields
                labelListNames = xmlExtractField(fileContent, 'name');
            else
                error('Unknown label format!');
            end

            % Convert to col format
            labelListNames = labelListNames(:);

            % Make unique
            labelListNames = unique(labelListNames);
        end

        function[labelMap] = getImLabelMap(obj, imageName)
            % labelMap = getImLabelMap(obj, imageName)
            %
            % Load a per-pixel map that shows which pixel has which label.
            %
            % Note: the labelNames don't have the order of the labelMap.
            % Instead all non-occurring names are removed.

            % Check whether a label map is available for this annotation
            labelPixelPath = [obj.getLabelPath(), filesep, imageName, obj.labelExt];
            if exist(labelPixelPath, 'file'),
                if strcmp(obj.labelFormat, 'mat-labelMap'),
                    % Just return labelMap
                    labelMapStruct = load(labelPixelPath, 'S');
                    labelMap = labelMapStruct.S;

                    % Check consistency of labelMap scale
                    assert(isempty(obj.maxSize) || all(size(labelMap) <= obj.maxSize));
                elseif strcmp(obj.labelFormat, 'xml') || strcmp(obj.labelFormat, 'xml-LabelMe'),
                    [labelMap, ~] = xmlToObjectMap(labelPixelPath);
                elseif strcmp(obj.labelFormat, 'xml-ImageNet'),
                    [labelMap, ~] = xmlToObjectMap(labelPixelPath, 'ImageNet');
                elseif strcmp(obj.labelFormat, 'im-labelMap'),
                    labelMap = imread(labelPixelPath);
                else
                    error('Error: Unknown label format!');
                end
            else
                error('Error: No labelMap available for image: %s', imageName);
            end
        end

        function[labelBoxes, labelList] = getImLabelBoxes(obj, imageName, filterCurLabel)
            % [labelBoxes, labelList] = getImLabelBoxes(obj, imageName, filterCurLabel)
            %
            % Load a list of boxes and names for each object in the image.

            if ~exist('filterCurLabel', 'var'),
                filterCurLabel = false;
            end

            if strcmp(obj.labelFormat, 'xml-ImageNet'),
                % Get bounding box
                labelPath = fullfile(obj.getLabelPath(), [imageName, obj.labelExt]);
                [labelBoxes, labelNames, imSize] = imageNetXmlLabelsToBBox(labelPath);

                % Scale bounding boxes to fit resized image
                if ~isempty(obj.maxSize),
                    maxImSize = max(imSize);
                    if any(imSize > obj.maxSize),
                        % Round up to avoid zeros
                        labelBoxes = ceil(labelBoxes ./ maxImSize .* obj.maxSize);
                    end
                end

                % Get label indices for image and all boxes
                hashPath = fullfile(obj.getMetaPath(), 'objectLabelNameToLabelInd_hash.mat');
                hashStruct = load(hashPath);
                boxInds = cellfun(@(x) hashStruct.hash.get(x), labelNames);

                % If requested, use only the boxes that have the correct label
                if filterCurLabel,
                    % Remove all boxes that do not have the current label (or children of that)
                    error('TODO: update this to allow multiple labelInds!');
                    imageLabelInd = obj.labelIdx; %#ok<UNRCH>
                    boxMatches = boxInds == imageLabelInd;
                    labelBoxes = labelBoxes(boxMatches, :);
                    labelList = boxInds(boxMatches);
                else
                    labelList = boxInds;
                end
            else
                error('Error: Label format not supported yet!');
            end
        end

        function[mask] = getImLabelBoxesMask(obj, imageName)
            % Returns a boolean mask where all pixels inside any bounding box
            % are set to true and all others to false.

            % Get boxes
            labelBoxes = obj.getImLabelBoxes(imageName); % box format is [y1, y2, x1, x2]

            % Get image size
            imageSize = obj.getImageSize(imageName);

            % Overwrite mask for each box
            mask = false(imageSize);
            for boxIdx = 1 : size(labelBoxes, 1)
                box = labelBoxes(boxIdx, :);
                mask(box(1):box(2), box(3):box(4)) = true;
            end
        end

        function imageBrowser(obj, startIdx)
            % imageBrowser(obj, startIdx)
            %
            % Start an interactive image browser.
            % You can jump to the next image by pressing a,d.
            % The labels are enlisted upon pressing s

            % Store image index in global variable
            global ds_imageBrowser_idx;
            if exist('startIdx', 'var'),
                ds_imageBrowser_idx = startIdx;
            elseif exist('ds_imageBrowser_idx', 'var') && ~isempty(ds_imageBrowser_idx),
                % Keep current idx
            else
                ds_imageBrowser_idx = 1;
            end

            % Preload image list to avoid overhead on each update
            imageList = obj.getImageList(true);

            function updateImageBrowserIndex(dataset, command)
                fprintf('Pressed key is: %s\n', command);
                if command == 'a',
                    ds_imageBrowser_idx = ds_imageBrowser_idx - 1;
                    if ds_imageBrowser_idx < 1,
                        ds_imageBrowser_idx = dataset.imageCount;
                    end
                    dataset.showWithAnnotation('imageIdx', ds_imageBrowser_idx, 'imageList', imageList, 'displayPlot', true);
                elseif command == 'd',
                    ds_imageBrowser_idx = ds_imageBrowser_idx + 1;
                    if ds_imageBrowser_idx > dataset.imageCount,
                        ds_imageBrowser_idx = 1;
                    end
                    dataset.showWithAnnotation('imageIdx', ds_imageBrowser_idx, 'imageList', imageList, 'displayPlot', true);
                elseif command == 's',
                    labelNames = dataset.showWithAnnotation('imageIdx', ds_imageBrowser_idx, 'imageList', imageList, 'displayPlot', true);
                    for labelInd = 1 : numel(labelNames),
                        fprintf('%d: %s\n', labelIdx, labelNames{labelInd});
                    end
                else
                    % Do nothing
                end
            end

            % Open figure and install key listener
            f = figure();
            set(f, 'KeyPressFcn', @(x, y) updateImageBrowserIndex(obj, get(f, 'CurrentCharacter')));

            % Show an image
            obj.showWithAnnotation('imageIdx', ds_imageBrowser_idx, 'displayPlot', true);
        end

        function[labelNames, image, tiling] = showWithAnnotation(obj, varargin)
            % [labelNames, image, tiling] = showWithAnnotation(obj, varargin)
            %
            % Shows an image either with its active annotation or with the
            % labelNames specified. The image and labelNames are returned
            % for further use.
            %
            % Note: Only imageIdx XOR imageName can be specified!

            % Parse input
            p = inputParser;
            addParameter(p, 'imageIdx', []);
            addParameter(p, 'imageName', []);
            addParameter(p, 'imageList', []);
            addParameter(p, 'labelNames', {});
            addParameter(p, 'labelNamesImage', {});
            addParameter(p, 'labelMap', []);
            addParameter(p, 'showImage', true);
            addParameter(p, 'showLabelMapGT', true);
            addParameter(p, 'showLabelMap', true);
            addParameter(p, 'insertBlobLabelsGT', true);
            addParameter(p, 'insertBlobLabels', true);
            addParameter(p, 'displayPlot', true);
            addParameter(p, 'cmap', @jet);
            parse(p, varargin{:});

            imageIdx = p.Results.imageIdx;
            imageName = p.Results.imageName;
            imageList = p.Results.imageList;
            labelNames = p.Results.labelNames;
            labelNamesImage = p.Results.labelNamesImage;
            labelMap = p.Results.labelMap;
            showImage = p.Results.showImage;
            showLabelMapGT = p.Results.showLabelMapGT;
            showLabelMap = p.Results.showLabelMap;
            insertBlobLabelsGT = p.Results.insertBlobLabelsGT;
            insertBlobLabels = p.Results.insertBlobLabels;
            displayPlot = p.Results.displayPlot;
            cmap = p.Results.cmap;

            % Check arguments
            if ~xor(isempty(imageIdx), isempty(imageName)),
                error('Error: Only imageIdx XOR imageName can be specified!');
            end

            % If necessary, lookup which image name this index corresponds to
            if ~isempty(imageIdx),
                % Load imageList
                if isempty(imageList),
                    [imageList, ~] = obj.getImageList(true);
                end

                % Select image
                if imageIdx >= 1 && imageIdx <= obj.getImageCount(),
                    imageName = imageList{imageIdx};
                else
                    error('Error: Invalid image index!');
                end
            end

            % Prepare image
            imagePath = fullfile(obj.getImagePath(), [imageName, obj.imageExt]);
            image = imread(imagePath);

            % Prepare labelImageGT
            if showLabelMapGT,
                if obj.hasLabelMap(imageName),
                    % Get labelMapGT
                    try
                        labelMapGT = obj.getImLabelMap(imageName);
                        labelNames = obj.getLabelNames();
                    catch e,
                        fprintf('Warning: Cannot load label file! Maybe it doesn''t exist?\n%s\n', e.message);
                        labelMapGT = [];
                        labelNames = [];
                    end
                else
                    error('Error: Cannot show labelMapGT as it does not exist!');
                end

                % Convert labelMapGT to a color image
                if isempty(labelMapGT),
                    showLabelMapGT = false;
                else
                    labelImageGT = labelMapToColorImage(labelMapGT, obj.labelCount, cmap);

                    % Add labelNames
                    if insertBlobLabelsGT && ~isempty(labelNames),
                        labelImageGT = imageInsertBlobLabels(labelImageGT, labelMapGT, labelNames);
                    end
                end
            end

            % Prepare labelImage
            if showLabelMap && ~isempty(labelMap),
                % Convert labelMap to a color image
                labelImage = labelMapToColorImage(labelMap, obj.labelCount, cmap);

                % Add labelNames
                if insertBlobLabels && ~isempty(labelNames),
                    % Fully supervised
                    labelImage = imageInsertBlobLabels(labelImage, labelMap, labelNames);
                end
            end

            % Insert image-level labels
            if ~isempty(labelNamesImage),
                image = imageInsertText(image, labelNamesImage);
            end

            % Concatenate image and labelMap side by side
            tiling = ImageTile();
            if showImage,
                tiling.addImage(image);
            end
            if showLabelMapGT,
                tiling.addImage(labelImageGT);
            end
            if ~isempty(labelMap),
                tiling.addImage(labelImage);
            end
            image = tiling.getTiling('totalX', tiling.getTotalX());

            % Show image
            if displayPlot && ~isempty(image),
                fprintf('Showing image: %s\n', imageName);
                imshow(image);
                set(gcf, 'Name', imageName, 'NumberTitle', 'off')
            end
        end

        function[result] = hasLabelMap(obj, imageName)
            % [result] = hasLabelMap(obj, imageName)
            %
            % Indicates whether the annotation's labelFormat holds a
            % labelMap. Then getImLabelMap(..) can be called.

            assert(~isempty(imageName));
            if obj.annotation.hasPixelLabels,
                result = true;
            elseif obj.annotation.hasPixelLabelsOnlyTst,
                imageListTst = obj.getImageListTst();
                result = ismember(imageName, imageListTst);
            else
                result = false;
            end
        end

        function[labelOrder, labelNames] = getLabelOrder(obj)
            if obj.annotation.hasStuffThingLabels,
                % Put stuff labels first, then things
                [~, ~, stuffLabelInds, thingLabelInds] = obj.getStuffThingLabels();
                labelOrder = [stuffLabelInds; thingLabelInds];
            else
                % Enumerate in default order
                labelOrder = (1:obj.labelCount)';
            end

            if nargout > 1,
                labelNames = obj.getLabelNames();
                labelNames = labelNames(labelOrder);
            end
        end

        function[imageSizes] = getImageSizes(obj)
            % [imageSizes] = getImageSizes(obj)
            %
            % Get the sizes of all images.
            % The order is the same as in getImageList().
            % Note that obj.maxSize and obj.fixSizeX affect the sizes.

            % Load imageSizes from file, if it exists
            imageSizesPath = fullfile(obj.getMetaPath(), 'imageSizes.mat');
            if exist(imageSizesPath, 'file'),
                imageSizesStruct = load(imageSizesPath, 'imageSizes');
                imageSizes = imageSizesStruct.imageSizes;
            else
                % Get image list
                [imageList, imageCount] = obj.getImageList(); %#ok<PROP>

                % Initialize
                imageSizes = nan(imageCount, 2); %#ok<PROP>

                % Load each image and determine its size
                for imageIdx = 1 : imageCount, %#ok<PROP>
                    printProgress('Extracting imageSizes for the first time', imageIdx, imageCount, 50); %#ok<PROP>

                    imageName = imageList{imageIdx};
                    image = obj.getImage(imageName);
                    imageSizes(imageIdx, :) = [size(image, 1), size(image, 2)];
                end

                % Save to disk for next time
                save(imageSizesPath, 'imageSizes', '-v6');
            end
        end

        function[imageSize] = getImageSize(obj, imageName)
            % [imageSize] = getImageSize(obj, imageName)
            %
            % Pretty slow. Datasets with fixed image sizes can just
            % overwrite this and return constants.

            image = obj.getImage(imageName);
            imageSize = [size(image, 1), size(image, 2)];
        end

        function showImageBlob(obj, imageName, blob)
            % showImageBlob(obj, imageName, blob)
            %
            % She the image with the blob emphasized in red color.

            % Get image indices
            image = obj.getImage(imageName);
            [blobSubY, blobSubX] = blobToImageSubs(blob);
            blobInds = sub2indFast(size(image), blobSubY, blobSubX, ones(size(blobSubY)));

            % Set blob pixels to red
            image(blobInds) = min(image(blobInds) + 0.5, 1);

            % Show image
            imshow(image);
        end

        function[image] = getRandImage(obj)
            % [image] = getRandImage(obj)
            %
            % Returns an arbitrary image (can be train or test).

            imageList = obj.getImageList();
            imageName = imageList{randi(numel(imageList))};
            image = obj.getImage(imageName);
        end

        function[imageName] = getRandImageName(obj)
            % [imageName] = getRandImage(obj)
            %
            % Returns the name of an arbitrary image (can be train or test).

            imageList = obj.getImageList();
            imageName = imageList{randi(numel(imageList))};
        end

        function[freqs, imageCount] = getLabelPixelFreqs(obj, subset)
            % [freqs, imageCount] = getLabelPixelFreqs(obj, [subset])
            %
            % Returns the pixel-level frequencies of each label.
            % This is computed on the train set.
            % It ignores invalid pixels (id == 0).

            % Default arguments
            if ~exist('subset', 'var'),
                subset = 'train';
            end

            imPixelFreqPath = fullfile(obj.getMetaPath(), 'labelPixelFreqs.mat');
            if exist(imPixelFreqPath, 'file') && strcmp(subset, 'train'),
                % Load from disk
                imPixelFreqStruct = load(imPixelFreqPath, 'freqs', 'imageCount');
                freqs = imPixelFreqStruct.freqs;
                imageCount = imPixelFreqStruct.imageCount;
            else
                % Get images
                [imageList, imageCount] = obj.getImageListSubset(subset);

                % Init
                labelCount = obj.labelCount;
                freqs = zeros(labelCount, 1);

                for imageIdx = 1 : imageCount,
                    printProgress('Loading pixel-level frequencies for image', imageIdx, imageCount);

                    % Get label map
                    imageName = imageList{imageIdx};
                    labelMap = obj.getImLabelMap(imageName);

                    % Sum frequencies (label 0 is excluded)
                    freqs = freqs + histc(labelMap(:), 1:labelCount);
                end

                % Save to disk
                if strcmp(subset, 'train'),
                    save(imPixelFreqPath, 'freqs', 'imageCount', '-v6');
                end
            end
        end

        function[freqs, imageCount] = getLabelImFreqs(obj, subset)
            % [freqs, imageCount] = getLabelImFreqs(obj, [subset])
            %
            % Returns the image-level frequencies of each label.
            % By default this is computed on the train set to avoid taking
            % a look at test when computing inverse class frequencies to
            % weight the loss of a classifier.
            % It ignores invalid pixels (id == 0).

            % Default arguments
            if ~exist('subset', 'var'),
                subset = 'train';
            end

            imLabelFreqPath = fullfile(obj.getMetaPath(), 'labelImFreqs.mat');
            if exist(imLabelFreqPath, 'file') && strcmp(subset, 'train'),
                % Load from disk
                imLabelFreqStruct = load(imLabelFreqPath, 'freqs', 'imageCount');
                freqs = imLabelFreqStruct.freqs;
                imageCount = imLabelFreqStruct.imageCount;
            else
                % Get images
                [imageList, imageCount] = obj.getImageListSubset(subset);

                % Init
                [labelNames, labelCount] = obj.getLabelNames();
                freqs = zeros(labelCount, 1);

                for imageIdx = 1 : imageCount,
                    printProgress('Loading pixel-level frequencies for image', imageIdx, imageCount);

                    % Get label inds (don't call getImLabelMap to allow
                    % weakly supervised learning)
                    imageName = imageList{imageIdx};
                    labelList = obj.getImLabelList(imageName);
                    labelInds = find(ismember(labelNames, labelList));

                    % Sum frequencies (label 0 is excluded) (special care taken
                    % for empty histos)
                    histo = histc(labelInds, 1:labelCount);
                    histo = histo(:);
                    freqs = freqs + histo;
                end

                % Save to disk
                if strcmp(subset, 'train'),
                    save(imLabelFreqPath, 'freqs', 'imageCount', '-v6');
                end
            end
        end

        function[imLabelLists] = getImLabelLists(obj)
            % [imLabelLists] = getImLabelLists(obj)
            %
            % Extract the labelLists of each image and cache them all in one file.
            % Each labelList does not contain duplicates.

            imLabelListsPath = fullfile(obj.getMetaPath(), 'imLabelLists.mat');
            if exist(imLabelListsPath, 'file'),
                imLabelListsStruct = load(imLabelListsPath, 'imLabelLists');
                imLabelLists = imLabelListsStruct.imLabelLists;
                assert(numel(imLabelLists) == obj.imageCount);
            else
                % Get images
                [imageList, imageCountLoc] = obj.getImageList();

                % Init
                imLabelLists = cell(imageCountLoc, 1);

                % Load each image's labels
                for imageIdx = 1 : imageCountLoc,
                    printProgress('Loading image label lists for image', imageIdx, imageCountLoc);

                    imageName = imageList{imageIdx};
                    labelList = obj.getImLabelList(imageName);
                    imLabelLists{imageIdx} = labelList(:);
                end

                % Save to disk
                save(imLabelListsPath, 'imLabelLists', '-v6');
            end
        end

        function[regionBlobSizes] = getRegionBlobSizes(obj, segmentFolder, subset)
            % [regionBlobSizes] = getRegionBlobSizes(obj, segmentFolder, [subset])

            if ~exist('subset', 'var'),
                subset = 'all';
            end

            % Get images
            [imageList, curImageCount] = obj.getImageListSubset(subset);

            % Init
            regionBlobSizes = cell(curImageCount, 1);

            for imageIdx = 1 : curImageCount,
                imageName = imageList{imageIdx};
                blobsPath = fullfile(segmentFolder, [imageName, '.mat']);
                blobsStruct = load(blobsPath, 'propBlobs');
                regionBlobSizes{imageIdx} = [blobsStruct.propBlobs.size]';
            end
        end

        function[labelList] = getImLabelInds(obj, imageName)
            % [labelList] = getImLabelInds(obj, imageName)

            % Get label names
            [labelListNames] = obj.getImLabelList(imageName);
            labelNames = obj.getLabelNames();

            labelList = find(ismember(labelNames, labelListNames));
        end

        function[rgbMean] = getMeanColor(obj)
            % [rgbMean] = getMeanColor(obj)
            %
            % Compute the mean color of all pixels in all images in the
            % train set. Each image has the same weight, regardless of the
            % number of pixels in it.

            rgbMeanPath = fullfile(obj.getMetaPath(), 'rgbMean.mat');
            if exist(rgbMeanPath, 'file'),
                rgbMeanStruct = load(rgbMeanPath, 'rgbMean');
                rgbMean = rgbMeanStruct.rgbMean;
            else
                % Get image list
                [imageListTrn, imageCountTrn] = obj.getImageListTrn();

                % Init
                rgbSum = zeros(3, 1);

                for imageIdx = 1 : imageCountTrn,
                    printProgress('Computing mean color for image', imageIdx, imageCountTrn, 50);

                    imageName = imageListTrn{imageIdx};
                    image = obj.getImage(imageName);

                    for c = 1 : 3,
                        rgbSum(c) = rgbSum(c) + mean2(image(:, :, c));
                    end
                end

                rgbMean = rgbSum ./ imageCountTrn;

                % Store result
                save(rgbMeanPath, 'rgbMean');
            end
        end

        function[missingLabels] = getMissingImageIndices(obj, subset)
            % [missingLabels] = getMissingImageIndices(obj, subset)

            assert(~isempty(subset));
            missingLabelsPath = fullfile(obj.getMetaPath(), sprintf('missingLabels-%s.mat', subset));
            if exist(missingLabelsPath, 'file'),
                % Load from disk
                missingLabelsStruct = load(missingLabelsPath, 'missingLabels');
                missingLabels = missingLabelsStruct.missingLabels;
            else
                % Get images
                [imageList, imageCountCur] = obj.getImageListSubset(subset);

                missingLabels = false(imageCountCur, 1);
                for imageIdx = 1 : imageCountCur,
                    printProgress('Checking whether image has labels', imageIdx, imageCountCur, 50);

                    imageName = imageList{imageIdx};
                    labels = obj.getImLabelList(imageName);
                    if isempty(labels),
                        missingLabels(imageIdx) = true;
                    end
                end
                missingLabels = find(missingLabels);

                % Write to disk
                save(missingLabelsPath, 'missingLabels');
            end
        end
    end
end


classdef MimProjectsSidePanel < GemListBoxWithTitle
    % MimProjectsSidePanel. Part of the gui for the TD MIM Toolkit.
    %
    %     This class is used internally within the TD MIM Toolkit to help
    %     build the user interface.
    %
    %     MimProjectsSidePanel is part of the side panel and contains the sliding list
    %     box showing project names
    %
    %     Licence
    %     -------
    %     Part of the TD MIM Toolkit. https://github.com/tomdoel
    %     Author: Tom Doel, Copyright Tom Doel 2014.  www.tomdoel.com
    %     Distributed under the MIT licence. Please see website for details.
    %

    properties (Access = private)
        PatientDatabase
        GuiCallback
    end

    properties (SetAccess = private)
        CurrentProjectId
    end

    methods
        function obj = MimProjectsSidePanel(parent, patient_database, gui_callback)
            obj = obj@GemListBoxWithTitle(parent, 'PROJECT', 'Import images', 'Delete project');
            obj.PatientDatabase = patient_database;
            obj.GuiCallback = gui_callback;
        end

        function RepopulateSidePanel(obj, project_id)
            obj.AddProjectsToListBox(project_id);
        end

        function project_has_changed = UpdateSidePanel(obj, project_id)
            project_has_changed = ~strcmp(project_id, obj.CurrentProjectId);
            if project_has_changed
                obj.ListBox.SelectItem(project_id, true);
            end
            obj.CurrentProjectId = project_id;
        end

        function SelectProject(obj, project_id, selected)
            obj.ListBox.SelectItem(project_id, selected);
        end

    end

    methods (Access = protected)

        function AddButtonClicked(obj, ~, event_data)
        end

        function DeleteButtonClicked(obj, ~, event_data)
        end
    end

    methods (Access = private)
        function AddProjectsToListBox(obj, selected_project_id)
            [project_names, project_ids] = obj.PatientDatabase.GetListOfProjects;
            obj.ListBox.ClearItems;

            for index = 1 : numel(project_ids)
                project_name = project_names{index};
                project_id = project_ids{index};
                patient_item = MimProjectNameListItem(obj.ListBox.GetListBox, project_name, project_id, obj.GuiCallback);
                obj.ListBox.AddItem(patient_item);
            end

            obj.ListBox.SelectItem(selected_project_id, true);
        end
    end
end


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

function test_moxunit_util_escape_xml_basics
    aeq=@(a,b)assertEqual(moxunit_util_escape_xml(a),b);

    aeq('asdk','asdk');
    aeq('&','&');
    aeq('"','"');
    aeq('''',''');
    aeq('<','<');
    aeq('>','>');

    aeq('apos&&&apos','apos&&&apos');

    aeq('&amp&','&amp&');
    aeq('&quot','&quot');
    aeq('&apos','&apos');
    aeq('&lt','&lt');
    aeq('&gt','&gt');


% FT_PREAMBLE_PROVENANCE is a helper script that records the time and memory at the
% start of the function. At the end of the function FT_POSTAMBLE_PROVENANCE will
% record and store the time and memory in the output configuration together with
% information about the environment, such as the name of the user and computer, the
% MATLAB and FieldTrip version, etc.
%
% FieldTrip also attempts to uniquely identify the input and the output data. The
% code that deals with tracking the input data structures is found in
% FT_PREAMBLE_LOADVAR. The code that deals with tracking the information about the
% output data structures is found in FT_POSTAMBLE_HISTORY.
%
% Use as
%   ft_preamble provenance
%   .... regular code goes here ...
%   ft_postamble provenance
%
% See also FT_PREAMBLE, FT_POSTAMBLE, FT_POSTAMBLE_PROVENANCE

% Copyright (C) 2011-2016, Robert Oostenveld, DCCN
%
% This file is part of FieldTrip, see http://www.fieldtriptoolbox.org
% for the documentation and details.
%
%    FieldTrip 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.
%
%    FieldTrip 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 FieldTrip. If not, see <http://www.gnu.org/licenses/>.
%
% $Id$

% Record the start time and memory. These are used by ft_postamble_callinfo, which
% stores them in the output cfg.callinfo.  In the mean time, they are stored in the
% function workspace, which is why they should have cryptical names to prevent any
% variable name clashes.

if (isfield(cfg, 'trackcallinfo') && ~istrue(cfg.trackcallinfo))
  % do not track the call information
  return
end

% add the user-specified cfg (before any defaults handling etc.) to the callinfo
% some fields are for internal use only and should not be stored
cfg.callinfo.usercfg = removefields(cfg, ignorefields('provenance'));

if isfield(cfg, 'trackdatainfo') && istrue(cfg.trackdatainfo)
  % compute the MD5 hash of each of the input arguments
  % temporarily remove the cfg field for getting the hash (creating a duplicate of the data, but still has the same mem ref, so no extra mem needed)
  if isequal(iW1aenge_preamble, {'varargin'})
    tmpargin = varargin;
  else
    isvar = cellfun(@(x) exist(x, 'var')==1, iW1aenge_preamble);
    tmpargin = cellfun(@eval, iW1aenge_preamble(isvar), 'UniformOutput', false);
    tmpargin( isvar) = tmpargin;
    tmpargin(~isvar) = {[]};
    clear isvar
  end
  cfg.callinfo.inputhash = cell(1,numel(tmpargin));
  for iargin = 1:numel(tmpargin)
    tmparg = tmpargin{iargin}; % can't get number of bytes with whos unless taken out of it's cell
    if isfield(tmparg,'cfg')
      tmparg = rmfield(tmparg, 'cfg');
    end
    % only calculate md5 when below 2^31 bytes (CalcMD5 can't handle larger input)
    bytenum = whos('tmparg');
    bytenum = bytenum.bytes;
    if bytenum<2^31
      try
        cfg.callinfo.inputhash{iargin} = ft_hash(tmparg);
      catch
        % the mxSerialize mex file is not available on all platforms, do not compute a hash
        % http://bugzilla.fieldtriptoolbox.org/show_bug.cgi?id=2452
      end
    else
      % the data is too large, do not compute a hash
    end
  end
  clear tmpargin iargin tmparg bytenum; % remove the extra references
end

stack = dbstack('-completenames');
% stack(1) is this script
% stack(2) is the calling ft_postamble function
% stack(3) is the main FieldTrip function that we are interested in
stack = stack(3);

% add information about the FieldTrip and MATLAB version used to the configuration
try
  cfg.callinfo.fieldtrip = ft_version();
catch
  cfg.callinfo.fieldtrip = 'unknown';
end
cfg.callinfo.matlab    = version();
cfg.callinfo.computer  = lower(computer); % for example maci64, glnx86, ...

% add information about the execution environment to the configuration
cfg.callinfo.hostname = gethostname();
cfg.callinfo.user     = getusername();
cfg.callinfo.pwd      = pwd;
cfg.callinfo.calltime = clock();

% add information about the function filename and revision to the configuration
cfg.version.name = stack.file;
clear stack

% the revision number is maintained by SVN in the ft_revision variable in the calling function
if ~exist('ft_revision', 'var')
  cfg.version.id   = 'unknown';
else
  cfg.version.id   = ft_revision;
end

ftohDiW7th_FuncTimer = tic();
ftohDiW7th_FuncMem   = memtic();


function ldl_install
%LDL_INSTALL compile and install the LDL package for use in MATLAB.
% Your current working directory must be LDL for this function to work.
%
% Example:
%   ldl_install
%
% See also ldlsparse, ldlsymbol

% Copyright 2006-2007 by Timothy A. Davis, Univ. of Florida

ldl_make
addpath (pwd) ;
fprintf ('LDL has been compiled and installed.  The path:\n') ;
disp (pwd) ;
fprintf ('has been added to your path.  Use pathtool to add it permanently.\n');
ldldemo


function msh = meshStoreSettings(msh, name);
% Store Settings in a mesh, updating any GUIs that reflect this.
%
% msh = meshStoreSettings(msh, <name='Setting#'>);
%
% This code will use the meshSettings command to grab mesh view settings,
% and append the result to the msh.settings field, creating it if it
% doesn't exist. In addition, if a uicontrol is found with the tag
% 'MeshSettingsList', will update the string in this code to reflect the
% new setting.
%
% Also saves it in a meshDir/'MeshSettings.mat' file, since the mesh
% saving/loading has gotten complicated.
%
% ras, 03/14/06.
if notDefined('msh'), msh = viewGet(getSelectedGray, 'selectedMesh');  end

% get the saved settings
meshDir = fileparts(msh.path);
if isempty(meshDir)
    meshDir = viewGet(getSelectedGray, 'meshDir');
end

settingsFile = fullfile(meshDir, 'MeshSettings.mat');
if exist(settingsFile, 'file')
    load(settingsFile, 'settings');
    N = length(settings) + 1; %#ok<NODEF>
else
    N = 1;
end

if notDefined('name')
    name = sprintf('Setting%i',N);
end

S = meshSettings(msh);
S.name = name;
settings(N) = S;

% look for a uicontrol from a mesh setting GUI
h = findobj('Tag', 'MeshSettingsList');

if length(h) > 1
    % more than one menu/list open: loop through lists
    for ii = 1:length(h)
       if isequal( get(h(ii), 'Type'), 'uimenu' ) && ...
           get( get(h(ii), 'Parent'), 'Parent' )==gcf
            h = h(ii);
            break
       end
    end
end

if ~isempty(h)
	% ras 11/07: this can either be a uicontrol listbox (as in the 3D
	% Window and mrViewer) or a uimenu (in the new gray menu)
	if isequal( get(h, 'Type'), 'uicontrol' )
	    set(h, 'String', {settings.name});

	elseif isequal( get(h, 'Type'), 'uimenu' )
		%% make a submenu

		% (prompt for a name now, since renaming later is a little less
		% convenient)
		prompt = {'Enter the new name for the mesh settings'};
	    name = inputdlg(prompt, mfilename, 1, {settings(N).name});
		if isempty(name), return; end   % exit gracefully if cancel

		% the callback should retrieve the selected settings
		cb = sprintf('meshRetrieveSettings([], %i); ', N);

		% if this is the first stored setting, we want a separator between
		% this and the other options (store, rename, delete); otherwise, no
		% separator:
		if N==1, sep = 'on'; else sep = 'off'; end

		uimenu(h, 'Label', name{1}, 'Callback', cb, 'Separator', sep);

		% set this as the name in the settings
		settings(N).name = name{1}; %#ok<NASGU>
	end
end

% save the settings
save(settingsFile, 'settings');

return


function varargout = PlayJAABAResults(varargin)
% PLAYJAABARESULTS MATLAB code for PlayJAABAResults.fig
%      PLAYJAABARESULTS, by itself, creates a new PLAYJAABARESULTS or raises the existing
%      singleton*.
%
%      H = PLAYJAABARESULTS returns the handle to a new PLAYJAABARESULTS or the handle to
%      the existing singleton*.
%
%      PLAYJAABARESULTS('CALLBACK',hObject,eventData,handles,...) calls the local
%      function named CALLBACK in PLAYJAABARESULTS.M with the given input arguments.
%
%      PLAYJAABARESULTS('Property','Value',...) creates a new PLAYJAABARESULTS or raises the
%      existing singleton*.  Starting from the left, property value pairs are
%      applied to the GUI before PlayJAABAResults_OpeningFcn gets called.  An
%      unrecognized property name or invalid value makes property application
%      stop.  All inputs are passed to PlayJAABAResults_OpeningFcn via varargin.
%
%      *See GUI Options on GUIDE's Tools menu.  Choose "GUI allows only one
%      instance to run (singleton)".
%
% See also: GUIDE, GUIDATA, GUIHANDLES

% Edit the above text to modify the response to help PlayJAABAResults

% Last Modified by GUIDE v2.5 10-Jun-2012 21:47:30

% Begin initialization code - DO NOT EDIT
gui_Singleton = 1;
gui_State = struct('gui_Name',       mfilename, ...
                   'gui_Singleton',  gui_Singleton, ...
                   'gui_OpeningFcn', @PlayJAABAResults_OpeningFcn, ...
                   'gui_OutputFcn',  @PlayJAABAResults_OutputFcn, ...
                   'gui_LayoutFcn',  [] , ...
                   'gui_Callback',   []);
if nargin && ischar(varargin{1}) && exist(varargin{1}) %#ok<EXIST>
    gui_State.gui_Callback = str2func(varargin{1});
end

if nargout
    [varargout{1:nargout}] = gui_mainfcn(gui_State, varargin{:});
else
    gui_mainfcn(gui_State, varargin{:});
end
% End initialization code - DO NOT EDIT

% --- Executes just before PlayJAABAResults is made visible.
function PlayJAABAResults_OpeningFcn(hObject, eventdata, handles, varargin)
% This function has no output args, see OutputFcn.
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)
% varargin   command line arguments to PlayJAABAResults (see VARARGIN)

% Choose default command line output for PlayJAABAResults
handles.output = hObject;

persistent expdir;
persistent classifierparamsfile;

[handles.expdir,...
  handles.classifierparamsfile] = ...
  myparse(varargin,'expdir','',...
  'classifierparamsfile','');

if isempty(handles.expdir),
  handles.expdir = uigetdir(expdir,'Experiment directory');
  if ~ischar(handles.expdir),
    delete(handles.figure_PJR);
    return;
  end
  expdir = handles.expdir;
end
if isempty(handles.classifierparamsfile),
  if isempty(classifierparamsfile), classifierparamsfile = ''; end
  if iscell(classifierparamsfile),
    classifierparamsfile = classifierparamsfile{1};
  end
  [filename,path] = uigetfile(classifierparamsfile,'Classifier parameters file','MultiSelect','on');
  if ~ischar(filename) && ~iscell(filename),
    delete(handles.figure_PJR);
    return;
  end
  if iscell(filename),
    handles.classifierparamsfile = cellfun(@(x) fullfile(path,x),filename,'UniformOutput',false);
  else
    handles.classifierparamsfile = fullfile(path,filename);
  end
  classifierparamsfile = handles.classifierparamsfile;
end

handles = InitializeState(handles);

handles = InitializePlots(handles);

handles = SetCurrentFly(handles);

% Update handles structure
guidata(hObject, handles);

% UIWAIT makes PlayJAABAResults wait for user response (see UIRESUME)
% uiwait(handles.figure_PJR);

function handles = InitializeState(handles)

score_norm_prctile = 80;

%% read config parameters
handles.classifierparams = ReadClassifierParamsFile(handles.classifierparamsfile);
handles.nbehaviors = numel(handles.classifierparams);
handles.behaviors = cell(1,handles.nbehaviors);
handles.scorefns = cell(1,handles.nbehaviors);
for i = 1:handles.nbehaviors,
  if iscell(handles.classifierparams(i).behaviors.names),
    handles.behaviors{i} = sprintf('%s_',handles.classifierparams(i).behaviors.names{:});
    handles.behaviors{i} = handles.behaviors{i}(1:end-1);
  else
    handles.behaviors{i} = handles.classifierparams(i).behaviors.names;
  end
  handles.behaviors{i} = regexprep(handles.behaviors{i},'(_|^)([a-z])','${upper($2)}');
  scorefn = handles.classifierparams(i).file.scorefilename;
  handles.scorefns{i} = regexprep(scorefn,'\.mat$','');
end

%% create trx structure

%global STORED_TRX;
STORED_TRX = [];

if isempty(STORED_TRX) || ~strcmp(STORED_TRX.expdirs,handles.expdir),
handles.trx = Trx('trxfilestr',handles.classifierparams(1).file.trxfilename,...
  'moviefilestr',handles.classifierparams(1).file.moviefilename,...
  'perframedir',handles.classifierparams(1).file.perframedir);
if isfield(handles.classifierparams,'perframe') && isfield(handles.classifierparams(1).perframe,'params'),
  handles.trx.SetPerFrameParams(handles.classifierparams(1).perframe.params);
end
if isfield(handles.classifierparams,'perframe') && isfield(handles.classifierparams(1).perframe,'landmark_params'),
  handles.trx.SetLandmarkParams(handles.classifierparams(1).perframe.landmark_params);
end

handles.trx.AddExpDir(handles.expdir,'openmovie',false);
STORED_TRX = handles.trx;
else

  handles.trx = STORED_TRX;
end

%% for reading video

moviename = fullfile(handles.expdir,handles.trx.moviefilestr);
[handles.readframe,handles.nframes,handles.fid,handles.headerinfo] = get_readframe_fcn(moviename);

%% current fly, frame, etc.

handles.target = 1;
handles.t = handles.trx(handles.target).firstframe;
handles.nflies = handles.trx.nflies;

%% playback fps

handles.fps = 3;
handles.dozoom = true;

%% choose weights for each behavior

handles.behaviorweight = nan(handles.nbehaviors,1);
for behaviori = 1:handles.nbehaviors,
  tmp = handles.trx.(handles.scorefns{behaviori});
  if iscell(tmp),
    tmp = [tmp{:}];
  end
  handles.behaviorweight(behaviori) = nnz(tmp>0);
end
handles.behaviorweight = handles.behaviorweight / sum(handles.behaviorweight);
handles.behaviorweight = handles.behaviorweight(:);

%% choose colors for each behavior

tmp = jet(256)*.7;
handles.behavior_colors = tmp(round(linspace(1,256,handles.nbehaviors)),:);

%% initial zoom

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

%% get the score normalizations

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

%% not playing

handles.isplaying = false;

function handles = InitializePlots(handles)

trx_linewidth = 1;
target_linewidth = 2;
trxcolor = 'k';
textcolor = 'k';
fontsize = 30;
bar_width = .8;
barbackcolor = [.5,.5,.5];

%% main axis
% frame
im = handles.readframe(handles.t);
ncolors = size(im,3);
if ncolors == 1,
  handles.him = imagesc(im2double(im),'Parent',handles.haxmain,[0,1]);
  colormap(handles.haxmain,'gray');
else
  handles.him = image(im2double(im),'Parent',handles.haxmain);
end
hold(handles.haxmain,'on');

% plot current fly
handles.htrx = plot(handles.haxmain,nan,nan,'.-','LineWidth',trx_linewidth,'color',trxcolor,'HitTest','off');
handles.htarget = plot(handles.haxmain,nan,nan,'-','LineWidth',target_linewidth,'color','k','HitTest','off');
set(handles.haxmain,'XTick',[],'YTick',[]);

% plot other flies
handles.hothers = nan(1,handles.nflies);
for i = 1:handles.nflies,
  handles.hothers(i) = plot(handles.haxmain,nan,nan,'mo','markerfacecolor','m');
  set(handles.hothers(i),'ButtonDownFcn',@(hObject,eventdata) PlayJAABAResults('fly_ButtonDownFcn',hObject,eventdata,guidata(hObject),i));
end

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

% target info
handles.hinfo = text(0,0,'','Parent',handles.haxmain,'Color',textcolor,...
  'FontUnits','pixels','FontSize',fontsize,...
  'HorizontalAlignment','left',...
  'VerticalAlignment','bottom',...
  'HitTest','off');

% current behaviors
handles.htext = text(nan,nan,'','Parent',handles.haxmain,'Color',textcolor,...
  'FontUnits','pixels','FontSize',fontsize);

%% bar plots
handles.hbar = nan(1,handles.nbehaviors);
handles.hbarback = nan(1,handles.nbehaviors);
for i = 1:handles.nbehaviors,
  handles.hbarback(i) = patch(i+bar_width/2*[-1,1,1,-1,-1],zeros(1,5)+.1,barbackcolor,'EdgeColor','none',...
    'Parent',handles.haxbottom);
  if i == 1,
    hold(handles.haxbottom,'on');
  end
  handles.hbar(i) = patch(i+bar_width/2*[-1,1,1,-1,-1],zeros(1,5)+.1,handles.behavior_colors(i,:),'EdgeColor','none','Parent',handles.haxbottom);
end

% zero line
plot(handles.haxbottom,[0,handles.nbehaviors+1],[0,0],'--','color',[.5,.5,.5]);
set(handles.haxbottom,'XTick',1:handles.nbehaviors,'XTickLabel',handles.behaviors,'YTick',[],...
  'XLim',[0,handles.nbehaviors+1],'YLim',[-1,1],'XColor','w','Color','k','TickLength',[0,0]);

% slider
set(handles.slider_frame,'Min',1,'Max',handles.nframes,...
  'Value',1,...
  'SliderStep',[1/(handles.nframes-1),100/(handles.nframes-1)]);

RecursiveSetKeyPressFcn(handles.figure_PJR);

%% set current fly

function handles = SetCurrentFly(handles)

handles.x = handles.trx(handles.target).x;
handles.y = handles.trx(handles.target).y;
handles.a = handles.trx(handles.target).a;
handles.b = handles.trx(handles.target).b;
handles.theta = handles.trx(handles.target).theta;
fil = [0.000263865082737   0.106450771973592   0.786570725887342   0.106450771973592   0.000263865082737];
fil = fil / sum(fil(:));
handles.smooththeta = imfilter(handles.theta,fil,'same','replicate');
handles.nframes_curr = handles.trx(handles.target).nframes;
handles.firstframe = handles.trx(handles.target).firstframe;
handles.endframe = handles.trx(handles.target).endframe;
handles.scores = nan(handles.nbehaviors,handles.nframes_curr);
handles.sex = handles.trx(handles.target).sex;
for behaviori = 1:handles.nbehaviors,
  handles.scores(behaviori,:) = min(1,max(-1,handles.trx(handles.target).(handles.scorefns{behaviori})/handles.score_norms(behaviori)));
end

if handles.t < handles.firstframe,
  handles.t = handles.firstframe;
end
if handles.t > handles.endframe,
  handles.t = handles.endframe;
end

SetCurrentFrame(handles);

%% set current frame

function SetCurrentFrame(handles,hObject)

if nargin < 2,
  hObject = nan;
end
if hObject ~= handles.slider_frame,
  set(handles.slider_frame,'Value',handles.t);
end
if hObject ~= handles.edit_frame,
  set(handles.edit_frame,'String',handles.t);
end
UpdatePlots(handles);

%% update plots

function UpdatePlots(handles,resetax)

persistent ax;

if nargin > 1 && resetax,
  ax = [];
end

trx_rad = 50;
printnone = true;

im = handles.readframe(handles.t);
set(handles.him,'CData',im2double(im));
i = handles.t+handles.trx(handles.target).off;

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

set(handles.htrx,'XData',handles.x(i0:i1),...
  'YData',handles.y(i0:i1));

updatefly(handles.htarget,handles.x(i),handles.y(i),...
  handles.theta(i),handles.a(i),handles.b(i));

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

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

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

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

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

set(handles.hinfo,'String',sprintf('Target %d, sex = %s',handles.target,handles.sex{i}));

for fly = 1:handles.nflies,
  if fly == handles.target || ...
      handles.t > handles.trx(fly).endframe || ...
      handles.t < handles.trx(fly).firstframe,
    set(handles.hothers(fly),'XData',nan,'YData',nan);
  else
    i = handles.t + handles.trx(fly).off;
    set(handles.hothers(fly),'XData',handles.trx(fly).x(i),...
      'yData',handles.trx(fly).y(i));
  end
end

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

%%

% --- Outputs from this function are returned to the command line.
function varargout = PlayJAABAResults_OutputFcn(hObject, eventdata, handles)
% varargout  cell array for returning output args (see VARARGOUT);
% hObject    handle to figure
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Get default command line output from handles structure
varargout = {};
%varargout{1} = handles.output;

% --- Executes on slider movement.
function slider_frame_Callback(hObject, eventdata, handles)
% hObject    handle to slider_frame (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'Value') returns position of slider
%        get(hObject,'Min') and get(hObject,'Max') to determine range of slider
v = round(get(hObject,'Value'));
if v > handles.endframe,
  v = handles.endframe;
  set(hObject,'Value',v);
elseif v < handles.firstframe,
  v = handles.firstframe;
  set(hObject,'Value',v);
end
handles.t = v;
SetCurrentFrame(handles,hObject);
guidata(hObject,handles);

% --- Executes during object creation, after setting all properties.
function slider_frame_CreateFcn(hObject, eventdata, handles)
% hObject    handle to slider_frame (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: slider controls usually have a light gray background.
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor',[.9 .9 .9]);
end

function edit_frame_Callback(hObject, eventdata, handles)
% hObject    handle to edit_frame (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hints: get(hObject,'String') returns contents of edit_frame as text
%        str2double(get(hObject,'String')) returns contents of edit_frame as a double
v = str2double(get(hObject,'String'));
if isnan(v),
  set(hObject,'Value',num2str(handles.t));
  return;
end
v = max(handles.firstframe,v);
v = min(handles.endframe,v);
v = round(v);
handles.t = v;
SetCurrentFrame(handles,hObject);
guidata(hObject,handles);

% --- Executes during object creation, after setting all properties.
function edit_frame_CreateFcn(hObject, eventdata, handles)
% hObject    handle to edit_frame (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    empty - handles not created until after all CreateFcns called

% Hint: edit controls usually have a white background on Windows.
%       See ISPC and COMPUTER.
if ispc && isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
    set(hObject,'BackgroundColor','white');
end

function handles = stop(handles)

if handles.isplaying,
  pushbutton_play_Callback(handles.pushbutton_play,[],handles);
  handles = guidata(handles.pushbutton_play);
end

function playstop(handles)

pushbutton_play_Callback(handles.pushbutton_play,[],handles);

% --- Executes on button press in pushbutton_play.
function pushbutton_play_Callback(hObject, eventdata, handles)
% hObject    handle to pushbutton_play (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

if handles.isplaying,
  handles.isplaying = false;
  guidata(hObject,handles);
else
  handles.isplaying = true;
  guidata(hObject,handles);
  play(handles);
end

function play(handles)

set(handles.pushbutton_play,'BackgroundColor',[.5,0,0],'String','Stop');

tic;
dt_sec = 1/handles.fps;

while true,

  if ~handles.isplaying,
    break;
  end

  if handles.t == handles.endframe,
    break;
  end

  handles = guidata(handles.pushbutton_play);
  dt = max(1,round(handles.fps*dt_sec));
  %fprintf('dt_sec = %f, dt = %d\n',dt_sec,dt);
  handles.t = handles.t+dt;
  guidata(handles.pushbutton_play,handles);
  SetCurrentFrame(handles);
  drawnow;
  dt_sec = toc;
  tic;
  if dt == 1,
    pause_time = 1/handles.fps - dt_sec;
    if pause_time > 0,
      %fprintf('pausing for %f s\n',pause_time);
      pause(pause_time);
    end
  end

end

set(handles.pushbutton_play,'BackgroundColor',[0,.5,0],'String','Play');
handles = guidata(handles.pushbutton_play);
handles.isplaying = false;
guidata(handles.pushbutton_play,handles);

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

n = numel(x);

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

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

% --- Executes when figure_PJR is resized.
function figure_PJR_ResizeFcn(hObject, eventdata, handles)
% hObject    handle to figure_PJR (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

if ~isfield(handles,'pxwidthradius'),
  return;
end

awidthradius = 3;
aheightradius = 3;
aborder = 1;
aback = .25;
meana = handles.meana;
handles.pxwidthradius = awidthradius*meana;
handles.pxheightradius = aheightradius*meana;
handles.pxborder = aborder*meana;
handles.pxback = meana*aback;

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

function fly_ButtonDownFcn(hObject,eventdata,handles,fly)

if fly == handles.target,
  return;
end

if handles.isplaying,
  handles = stop(handles);
end
handles.target = fly;
handles = SetCurrentFly(handles);
guidata(hObject,handles);

% --------------------------------------------------------------------
function menu_options_Callback(hObject, eventdata, handles)
% hObject    handle to menu_options (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% --------------------------------------------------------------------
function menu_zoom_Callback(hObject, eventdata, handles)
% hObject    handle to menu_zoom (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% --------------------------------------------------------------------
function menu_zoom_showwholevideo_Callback(hObject, eventdata, handles)
% hObject    handle to menu_zoom_showwholevideo (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

handles.dozoom = strcmpi(get(hObject,'Checked'),'on');
guidata(hObject,handles);
UpdateZoom(handles);

function UpdateZoom(handles)

if handles.dozoom,
  set(handles.menu_zoom_showwholevideo,'Checked','off');
  set(handles.menu_zoom_keepflyinaxes,'Checked','on');
  UpdatePlots(handles,true);
else
  set(handles.menu_zoom_showwholevideo,'Checked','on');
  set(handles.menu_zoom_keepflyinaxes,'Checked','off');
  axis(handles.haxmain,'image');
  set(handles.hinfo,'Position',[1,handles.headerinfo.nr]);
end

% --------------------------------------------------------------------
function menu_zoom_keepflyinaxes_Callback(hObject, eventdata, handles)
% hObject    handle to menu_zoom_keepflyinaxes (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

handles.dozoom = strcmpi(get(hObject,'Checked'),'off');
guidata(hObject,handles);
UpdateZoom(handles);

% --- Executes during object deletion, before destroying properties.
function figure_PJR_DeleteFcn(hObject, eventdata, handles)
% hObject    handle to figure_PJR (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

CleanUp(handles);

function CleanUp(handles)

if isfield(handles,'fid'),
  fclose(handles.fid);
end

% --- Executes when user attempts to close figure_PJR.
function figure_PJR_CloseRequestFcn(hObject, eventdata, handles)
% hObject    handle to figure_PJR (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

% Hint: delete(hObject) closes the figure
delete(hObject);

% --------------------------------------------------------------------
function menu_playbackspeed_Callback(hObject, eventdata, handles)
% hObject    handle to menu_playbackspeed (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

while true,

  v = inputdlg('Playback speed (fps):','Playback speed',1,{num2str(handles.fps)});
  v = v{1};
  if ~ischar(v),
    return;
  end
  v = str2double(v);
  if isnan(v) || v <= 0,
    continue;
  end
  break;
end
handles.fps = v;
guidata(hObject,handles);

% --- Executes on key press with focus on figure_PJR and none of its controls.
function figure_PJR_KeyPressFcn(hObject, eventdata, handles)
% hObject    handle to figure_PJR (see GCBO)
% eventdata  structure with the following fields (see FIGURE)
%	Key: name of the key that was pressed, in lower case
%	Character: character interpretation of the key(s) that was pressed
%	Modifier: name(s) of the modifier key(s) (i.e., control, shift) pressed
% handles    structure with handles and user data (see GUIDATA)

switch eventdata.Key,

  case 'leftarrow',
    if ~handles.isplaying,
      handles.t = max(handles.t-1,handles.firstframe);
      guidata(hObject,handles);
      SetCurrentFrame(handles);
    end
  case 'rightarrow',
    if ~handles.isplaying,
      handles.t = min(handles.t+1,handles.endframe);
      guidata(hObject,handles);
      SetCurrentFrame(handles);
    end
  case 'uparrow',
    if ~handles.isplaying,
      handles.t = max(handles.t-30,handles.firstframe);
      guidata(hObject,handles);
      SetCurrentFrame(handles);
    end
  case 'downarrow',
    if ~handles.isplaying,
      handles.t = min(handles.t+30,handles.endframe);
      guidata(hObject,handles);
      SetCurrentFrame(handles);
    end
  case {'space','p'},
    playstop(handles);
end

function RecursiveSetKeyPressFcn(hfig)

hchil = findall(hfig,'-property','KeyPressFcn');
goodidx = true(1,numel(hchil));
for i = 1:numel(hchil),
  if strcmpi(get(hchil(i),'Type'),'uicontrol') && strcmpi(get(hchil(i),'Style'),'edit'),
    goodidx(i) = false;
  end
end
set(hchil(goodidx),'KeyPressFcn',get(hfig,'KeyPressFcn'));

% --------------------------------------------------------------------
function menu_switchtarget_Callback(hObject, eventdata, handles)
% hObject    handle to menu_switchtarget (see GCBO)
% eventdata  reserved - to be defined in a future version of MATLAB
% handles    structure with handles and user data (see GUIDATA)

s = cellstr(num2str((1:handles.nflies)'));
[sel,ok] = listdlg('ListString',s,'SelectionMode','single',...
  'InitialValue',handles.target,'Name','Switch to target',...
  'PromptString','Switch to target: ');
if ~ok,
  return;
end
handles.target = sel;
handles = SetCurrentFly(handles);
guidata(hObject,handles);


    if inpr3 == 1
        FILNAME='private/gshhs_c.b';
        m_gshhs_c('save','coastl.mat');

    elseif inpr3 == 2
        FILNAME='private/gshhs_l.b';
        m_gshhs_l('save','coastl.mat');

    elseif inpr3 == 3
        FILNAME='private/gshhs_i.b';
        m_gshhs_i('save','coastl.mat');

    elseif inpr3 == 4
        FILNAME='private/gshhs_h.b';
        m_gshhs_h('save','coastl.mat');
    end
    load coastl.mat

    coastline = ncst;
    update(mainmap())
    clear  ncst coastl


function  message=read_text(file_name)
      % Copyright 2002-2010 The MathWorks, Inc.
      fid=fopen(file_name,'rt');
      message=char(fread(fid))';
      fclose(fid);


function str_out = structToString(data, show, level, str)
% Display the content of a structure

%--- * --. --- --. .--. ... * ---------------------------------------------
%               ___ ___ ___
%     __ _ ___ / __| _ | __|
%    / _` / _ \ (_ |  _|__ \
%    \__, \___/\___|_| |___/
%    |___/                    v 1.0RC1
%
%--------------------------------------------------------------------------
%  Copyright (C) 2021 Geomatics Research & Development srl (GReD)
%  Written by:       Andrea Gatti
%  Contributors:     Andrea Gatti
%  A list of all the historical goGPS contributors is in CREDITS.nfo
%--------------------------------------------------------------------------
%
%   This program is free software: you can redistribute it and/or modify
%   it under the terms of the GNU General Public License as published by
%   the Free Software Foundation, either version 3 of the License, or
%   (at your option) any later version.
%
%   This program is distributed in the hope that it will be useful,
%   but WITHOUT ANY WARRANTY; without even the implied warranty of
%   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%   GNU General Public License for more details.
%
%   You should have received a copy of the GNU General Public License
%   along with this program.  If not, see <http://www.gnu.org/licenses/>.
%
%--------------------------------------------------------------------------
% 01100111 01101111 01000111 01010000 01010011
%--------------------------------------------------------------------------

    if (nargin  < 2)
        show = true;
    end
    if (nargin  < 3)
        level = 0;
    end
    if (nargin  < 4)
        str = '';
    end

    key = fieldnames(data);
    for k = 1 : numel(key)
        if isstruct(data.(key{k}))
            str = sprintf('%s%*c -> %s\n', str, 4 * level, char(32), key{k});
            str = structToString(data.(key{k}), level+1, false, str);
        else
            tmp = toIniString(key{k}, data.(key{k}));
            str = sprintf('%s%*c -> %s\n', str, 4 * level, char(32), tmp{1});

        end
    end

    if show
        fprintf(str);
    end

    if (nargout == 1)
        str_out = str;
    end

end

% toIniString -----------------------------------------------------
function cell_str = toIniString(variable_name, value, format, cell_str)
% Convert any variable to ini string format
% SYNTAX:
%   cell_str = toIniString(variable_name, value)
%   cell_str = toIniString(variable_name, value, format)
%   cell_str = toIniString(variable_name, value, cell_str)
%   cell_str = toIniString(variable_name, value, format, cell_str)
switch nargin
    case 1
        error('Error in Ini_Manager.toIniString, too few parameters');
    case 2

        format = '';
        cell_str = {};
    case 3
        if iscellstr(format)
            cell_str = format;
            format = '%g';
        else
            cell_str = {};
        end
    case 4
        % ok
end
if ~isdeployed
    variable_name = ['<strong>' variable_name '</strong>'];
end
if ischar(value) % is string
    cell_str{numel(cell_str) + 1} = [variable_name ' = "' value(:)' '"'];
elseif isnumeric(value)
    if isempty(format)
        format = '%g';
    else
        format = strtrim(format);
    end
    if numel(value) > 1 % it's an array of values
        format = [format ' '];
        tmp = sprintf(format,value);
        cell_str{numel(cell_str) + 1} = [variable_name ' = [' tmp(1:end-1) ']'];
        clear tmp;
    else
        cell_str{numel(cell_str) + 1} = [variable_name ' = ' sprintf(format,value)];
    end
else % generic converter (may not work properly)
    toString = @(var) strtrim(regexprep(evalc('disp(var)'), '\n', ''));
    if iscell(value)
        if ~isempty(value) && ischar(value{1})
            cell_str{numel(cell_str) + 1} = [variable_name ' = [' Ini_Manager.strCell2Str(value) ']'];
        else
            cell_str{numel(cell_str) + 1} = [variable_name ' = [' toString(value) ']'];
        end
    else
        cell_str{numel(cell_str) + 1} = [variable_name ' = ' toString(value)];
    end
end

% I want a column array
if size(cell_str,1) < size(cell_str,2)
    cell_str = cell_str';
end
end


function d=ltfatgetdefaults(fname)
%LTFATGETDEFAULTS  Get default parameters of function
%
%   `ltfatgetdefaults(fname)` returns the default parameters
%   of the function `fname` as a cell array.
%
%   `ltfatgetdefaults('all')` returns all the set defaults.
%
%   See also: ltfatsetdefaults, ltfatstart

if nargin<1
    error('%s: Too few input arguments',upper(mfilename));
end;

if strcmpi(fname,'all')
  d=ltfatarghelper('all');
else
  d=ltfatarghelper('get',fname);
end;


classdef MimToolList < handle
    % MimToolList. Stores a list of MimTools
    %
    %
    %
    %     Licence
    %     -------
    %     Part of the TD MIM Toolkit. https://github.com/tomdoel
    %     Author: Tom Doel, Copyright Tom Doel 2014.  www.tomdoel.com
    %     Distributed under the MIT licence. Please see website for details.
    %

    properties (SetAccess = private)
        Tools % Map of tools
    end

    properties (Access = private)
        ToolCallback
        ViewerPanel

        CineTool
        WindowLevelTool
        ZoomTool
        PanTool
        PanMatlabTool
        Rotate3dMatlabTool
        ZoomMatlabTool
        EditTool
        ReplaceColourTool
        MapColourTool
        MarkerPointTool
    end

    methods
        function obj = MimToolList(marker_manager, tool_callback, viewer_panel, image_parameters, image_display_parameters)
            obj.ToolCallback = tool_callback;
            obj.ViewerPanel = viewer_panel;

            obj.CineTool = MimCineTool(image_parameters, tool_callback);
            obj.WindowLevelTool = MimWindowLevelTool(image_display_parameters, tool_callback, viewer_panel);
            obj.ZoomTool = MimZoomTool(image_parameters, tool_callback);
            obj.PanTool = MimPanTool(tool_callback);
            obj.PanMatlabTool = MimPanMatlabTool(tool_callback);
            obj.Rotate3dMatlabTool = MimRotate3dMatlabTool(tool_callback);
            obj.ZoomMatlabTool = MimZoomMatlabTool(tool_callback);
            obj.EditTool = MimEditManager(obj.ViewerPanel);
            obj.ReplaceColourTool = MimReplaceColourTool(obj.ViewerPanel);
            obj.MapColourTool = MimMapColourTool(obj.ViewerPanel);
            obj.MarkerPointTool = MimMarkerPointTool(marker_manager, obj.ViewerPanel);

            tool_list = {obj.ZoomMatlabTool, obj.PanMatlabTool, obj.Rotate3dMatlabTool, obj.MarkerPointTool, obj.WindowLevelTool, obj.CineTool, obj.EditTool, obj.ReplaceColourTool, obj.MapColourTool};

            obj.Tools = containers.Map;
            for tool_set = tool_list
                tool = tool_set{1};
                tag = tool.Tag;
                obj.Tools(tag) = tool;
            end
        end

        function tool = GetCurrentToolForSelectedControl(obj, mouse_is_down, keyboard_modifier)

            tool = obj.GetCurrentTool(mouse_is_down, keyboard_modifier, obj.ViewerPanel.SelectedControl);
        end

        function tool = GetCurrentTool(obj, mouse_is_down, keyboard_modifier, selected_control)
            if ~isempty(keyboard_modifier) && ~isempty(mouse_is_down) && mouse_is_down
                if strcmp(keyboard_modifier, 'extend')
                    tool = obj.PanTool;
                    return;
                elseif strcmp(keyboard_modifier, 'alt')
                    tool = obj.ZoomTool;
                    return;
                end
            end
            tool = obj.Tools(selected_control);
        end

        function tool = GetTool(obj, tag)
            tool = obj.Tools(tag);
        end

        function UpdateTools(obj)
            tool_list = obj.Tools.values;

            % Disable inactive tools before enabling new tools
            for tool_set = tool_list
                tool = tool_set{1};
                if ~strcmp(obj.ViewerPanel.SelectedControl, tool.Tag);
                    tool.Enable(false);
                end
            end

            % Enable tools
            for tool_set = tool_list
                tool = tool_set{1};
                if strcmp(obj.ViewerPanel.SelectedControl, tool.Tag)
                    tool.Enable(true);
                    % Set the context menu on the images. Note it would be hidden by
                    % the images if it were set on the axes
                    obj.ViewerPanel.BackgroundLayer.SetContextMenu(tool.GetContextMenu);
                    obj.ViewerPanel.SegmentationLayer.SetContextMenu(tool.GetContextMenu);
                end
            end
        end

        function input_has_been_processed = ShortcutKeys(obj, key, selected_control)
            % Each tool has a shortcut key to select it
            for tool = obj.Tools.values
                if strcmpi(key, tool{1}.ShortcutKey)
                    obj.ViewerPanel.SetControl(tool{1}.Tag);
                    input_has_been_processed = true;
                    return
                end
            end

            % Otherwise let the currently selected tool process shortcuts
            input_has_been_processed = obj.Tools(selected_control).Keypressed(key);
        end

        function ImageChanged(obj)
            tool = obj.GetCurrentPrimaryTool;
            if ~isempty(tool) && tool.Enabled
                tool.ImageChanged;
            end
        end

        function NewOrientation(obj)
            tool = obj.GetCurrentPrimaryTool;
            if ~isempty(tool) && tool.Enabled
                tool.NewOrientation;
            end
        end

        function OverlayImageChanged(obj)
            tool = obj.GetCurrentPrimaryTool;
            if ~isempty(tool) && tool.Enabled
                tool.OverlayImageChanged;
            end
        end

        function NewSlice(obj)
            tool = obj.GetCurrentPrimaryTool;
            if ~isempty(tool) && tool.Enabled
                tool.NewSlice;
            end
        end

    end

    methods (Access = private)
        function tool = GetCurrentPrimaryTool(obj)
            selected_control = obj.ViewerPanel.SelectedControl;
            if obj.Tools.isKey(selected_control)
                tool = obj.Tools(selected_control);
            else
                tool = [];
            end
        end
    end
end


%% This script takes a directory of files that have been processed by PREP
% and produces reports.

%% Read in the file and set the necessary parameters
dataDir = 'F:\TempData';
summaryFolder = 'F:\TempDataReports';
publishOn = true;

%% Get the directory list
inList = dir(dataDir);
inNames = {inList(:).name};
inTypes = [inList(:).isdir];
inNames = inNames(~inTypes);

%% Setup up the names
basename = 'vep';
summaryReportName = [basename '_summary.html'];
sessionFolder = '.';
summaryFileName = [summaryFolder filesep summaryReportName];
if exist(summaryFileName, 'file')
   delete(summaryFileName);
end

%% Publish the reports
for k = 1:length(inNames)
    [~, theName, theExt] = fileparts(inNames{k});
    if ~strcmpi(theExt, '.set') && ~strcmpi(theExt, '.mat')
        continue;
    end
    sessionReportName = [theName '.pdf'];
    fname = [dataDir filesep inNames{k}];
    load(fname, '-mat');
    sessionFileName = [summaryFolder filesep sessionReportName];
    consoleFID = 1;
    publishPrepReport(EEG, summaryFileName, sessionFileName, consoleFID, publishOn);
end


% eeglab_options() - handle EEGLAB options. This script (not function)
%                    set the various options in the eeg_options() file.
%
% Usage:
%   eeglab_options;
%
% Author: Arnaud Delorme, SCCN, INC, UCSD, 2006-

% Copyright (C) Arnaud Delorme, SCCN, INC, UCSD, 2006-
%
% 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

% load local file
% ---------------
homefolder = '';
try,
    %clear eeg_options; % note: we instead clear this function handle in pop_editoptions()

    if iseeglabdeployed
        com1 = readtxtfile(fullfile(eeglabexefolder, 'eeg_optionsbackup.txt'));
        com2 = readtxtfile(fullfile(eeglabexefolder, 'eeg_options.txt'));
        eval( com1 );
        eval( com2 );
    else
        eeg_optionsbackup;
        icadefs;

        % folder for eeg_options file (also update the pop_editoptions)
        if ~isempty(EEGOPTION_PATH)
             homefolder = EEGOPTION_PATH;
        elseif ispc
             if ~exist('evalc'), eval('evalc = @(x)(eval(x));'); end;
             homefolder = deblank(evalc('!echo %USERPROFILE%'));
        else homefolder = '~';
        end;

        option_file = fullfile(homefolder, 'eeg_options.m');
        oldp = pwd;
        try
            if ~isempty(dir(option_file))
                cd(homefolder);
            else
                tmpp2 = fileparts(which('eeglab_options.m'));
                cd(tmpp2);
            end;
        catch, end;
        eeg_options; % default one with EEGLAB
        cd(oldp);
    end;
    option_savematlab = ~option_savetwofiles;

catch
    lasterr
    disp('Warning: could not access the local eeg_options file');
end;


%XMLDEMO3 Demonstrate how to convert an XMLtree in a simple structure
%
%   Description
%   This script demonstrates the use of the xmltree class to
%   convert an XMLtree (when possible) in a simple structure.
%   This can only be performed when the XML file is simple enough
%   (one element cannot have more than one chardata child).

%   Copyright (C) 2003  Guillaume Flandin

clc;
disp('This demonstration illustrates the use of the xmltree class')
disp('to convert an xmltree in a simple Matlab structure.')
disp(' ')
disp('Press any key to open the XML file ''example.xml''.')
pause;

disp('>> t = xmltree(''example.xml'')')
try,
	t = xmltree('example.xml');
catch,
	disp('Please first launch xmldemo1')
end

disp(' ')
disp('Press any key to display the XML file.')
pause;

save(t);

disp(' ')
disp('Convert the xmltree in a Matlab structure.')
disp(' ')
disp('Press any key to continue.')
disp(' ')
pause; clc;

disp('>> s = convert(tree);')
s = convert(t);

disp(' ')
disp('Display the content of the structure.')
disp(' ')
disp('Press any key to continue.')
disp(' ')
pause;

disp('>> s.entry');
s.entry
disp('>> s.entry.address');
s.entry.address
disp('>> s.entry.address.institute');
s.entry.address.institute

disp(' ');
disp('Press any key to end.')
pause; clc; close all;


function loadAllDirs()
	% Forces all directories to be loaded, including external programs normally not loaded.
	% Biafra Ahanonu
	% started: 2020.05.09 [18:39:52]
	% inputs
		%
	% outputs
		%

	% changelog
		%
	% TODO
		%

	%========================
	% DESCRIPTION
	% options.exampleOption = '';
	% get options
	% options = getOptions(options,varargin);
	% display(options)
	% unpack options into current workspace
	% fn=fieldnames(options);
	% for i=1:length(fn)
	% 	eval([fn{i} '=options.' fn{i} ';']);
	% end
	%========================

	try
		ciapkg.loadBatchFxns('loadEverything');
	catch err
		disp(repmat('@',1,7))
		disp(getReport(err,'extended','hyperlinks','on'));
		disp(repmat('@',1,7))
	end
end