classdef mixture < iosr.dsp.audio
%MIXTURE Class of sound source separation mixture.
%
%   iosr.bss.mixture objects contain information about a mixture of sound
%   sources.
%
%   IOSR.BSS.MIXTURE is a subclass of IOSR.DSP.AUDIO.
%
%   IOSR.BSS.MIXTURE properties:
%       azi_sep         - The azimuthal separation of the widest sources
%                         (read-only)
%       decomp          - The result of the time-frequency decomposition
%                         (mixture) (read-only)
%       decomp_i        - The result of the time-frequency decomposition
%                         (interferer) (read-only)
%       decomp_t        - The result of the time-frequency decomposition
%                         (target) (read-only)
%       decomposition   - Set the time-frequency decomposition. The options
%                         are:
%                             'stft'        : short-time fourier transform
%                                             (default)
%                             'gammatone'   : gammatone filterbank
%       elevation       - The median elevation of the mixture (read-only)
%       filename_t      - Name of the target audio file (based on the
%                         mixture filename) (read-only)
%       filename_i      - Name of the interferer audio file (based on the
%                         Mixture filename) (read-only)
%       gammatone       - Settings for the gammatone filterbank
%                         decomposition. The property is a structure
%                         containing the following fields:
%                             'cfs'     : the centre frequencies of the
%                                         gammatone filterbank
%                             'frame'   : the frame length (in samples)
%       sofa_path       - Path to a SOFA file containing spatialisation data
%       ibm             - The ideal binary mask
%       irm             - The ideal ratio mask
%       int_fns         - A char array containing the filenames of all of
%                         the interfering sources (read-only)
%       interferers     - An array of interferer sources of type
%                         iosr.bss.source
%       signal_t        - The sampled data (target) (read-only)
%       signal_i        - The sampled data (interferer) (read-only)
%       stft            - Settings for the STFT decomposition. The property
%                         is a structure containing the following fields:
%                             'hop'     : the hop size of the STFT
%                             'win'     : the STFT window
%                         See iosr.dsp.stft for more information
%       target          - The target source of type iosr.bss.source
%       tfPower         - The (frame-based) time-frequency power (mixture)
%                         (read-only)
%       tfPower_i       - The (frame-based) time-frequency power
%                         (interferer) (read-only)
%       tfPower_t       - The (frame-based) time-frequency power (target)
%                         (read-only)
%       tir             - The target-to-interferer ratio (target or
%                         interferers are attenuated in order that their
%                         RMS amplitudes have this ratio)
%       wdo             - The w-disjoint orthogonality (read-only)
%       wdo_lw          - The loudness-weighted w-disjoint orthogonality
%                         (read-only)
%
%   IOSR.BSS.MIXTURE methods:
%       mixture         - Create the mixture
%       clearCache      - Clears the internally cached data, reducing
%                         file size.
%       copy            - Create an independent copy of the mixture, its
%                         sources, and any rendered files
%       applyMask       - Apply a time-frequency mask
%       deleteFiles     - Delete the mixture audio files
%       sound_t         - Replay the target
%       sound_i         - Replay the interferer
%       write           - Save the mixture, target, and interferers to audio files
%     Static methods:
%       maskCentroids   - Calculate the centroids of a time-frequency mask
%       mixWdo          - WDO of a mixture
%       mixWdo_lw       - Loudness-weighted WDO of a mixture
%       mixWdo_Stokes   - WDO calculated using Stokes's method
%
%   Note that target and interferer properties may be modified as
%   MIXTURE.TARGET.PROPERTY_NAME and MIXTURE.INTERFERERS(N).PROPERTY_NAME,
%   except for the sampling frequency FS, which cannot be overridden. This
%   ensures that the sampling frequencies are identical for the mixture and
%   its sources.
%
%   See also IOSR.DSP.AUDIO, IOSR.BSS.SOURCE, SOFALOAD, IOSR.DSP.STFT,
%   IOSR.AUDITORY.GAMMATONEFAST.

%   Copyright 2016 University of Surrey.

%TODO: Add more decompositions.

    properties (AbortSet)
        decomposition = 'stft'  % The time-frequency decomposition
        gammatone = struct      % Settings for the gammatone filterbank decomposition
        sofa_path               % Path to a SOFA file containing spatialisation data
        stft = struct           % Settings for the STFT decomposition
        tir = 0                 % The target to interferer ratio
        interferers             % An array of interferer sources of type iosr.bss.source
        target                  % The target source of type iosr.bss.source
    end

    properties (Dependent, SetAccess = protected)
        signal          % The sampled data (read-only)
    end

    properties (Dependent, SetAccess = private)
        azi_sep     % The azimuthal separation of the widest sources (read-only)
        decomp      % The result of the time-frequency decomposition (mixture) (read-only)
        decomp_i    % The result of the time-frequency decomposition (interferer) (read-only)
        decomp_t    % The result of the time-frequency decomposition (target) (read-only)
        elevation   % The median elevation of the mixture (read-only)
        filename_t  % Name of the target audio file (read-only)
        filename_i  % Name of the interferer audio file (read-only)
        ibm         % Ideal binary mask (read-only)
        irm         % Ideal ratio mask (read-only)
        int_fns     % Filenames of all of the interfering sources (read-only)
        numchans    % Number of audio channels in the mixture (read-only)
        signal_t    % Return target (read-only)
        signal_i    % Return interferer (read-only)
        tfPower     % The time-frequency power (mixture) (read-only)
        tfPower_i   % The time-frequency power (interferer) (read-only)
        tfPower_t   % The time-frequency power (target) (read-only)
        wdo         % Return the w-disjoint orthogonality metric (read-only)
        wdo_lw      % Return the loudness-weighted w-disjoint orthogonality metric (read-only)
        wdo_stokes  % Return the w-disjoint orthogonality metric using Stokes's method (read-only)
    end

    properties (Access = private)
        cached_decomp               % the cached mixture decomposition
        decomp_cached = false       % whether the mixture decomposition is cached
        cached_decomp_i             % the cached interferer decomposition
        decomp_i_cached = false     % whether the interferer decomposition is cached
        cached_decomp_t             % the cached target decomposition
        decomp_t_cached = false     % whether the target decomposition is cached
        cached_tfPower              % the cached mixture TF power
        tfPower_cached = false      % whether the mixture TF power is cached
        cached_tfPower_i            % the cached interferer TF power
        tfPower_i_cached = false    % whether the interferer TF power is cached
        cached_tfPower_t            % the cached target TF power
        tfPower_t_cached = false    % whether the target TF power is cached
    end

    methods

        % constructor
        function obj = mixture(target,interferers,varargin)
        %MIXTURE Create a mixture
        %
        %   OBJ = IOSR.BSS.MIXTURE(TARGET,INTERFERER) creates a mixture by
        %   summing together the target source and the interferer
        %   source(s). The sources are mixed together such that their RMS
        %   amplitudes are equal (target-to-interferer ratio is 0dB). The
        %   OBJ sampling rate is equal to the target sampling rate.
        %   Information about the sources' spatial location is ignored.
        %
        %   OBJ = IOSR.BSS.MIXTURE(...,'PARAMETER',VALUE) allows additional
        %   options to be specified. The options are ({} indicate
        %   defaults):
        %
        %       'decomposition' : {'stft'} | 'gammatone'
        %       'filename'      : {[]} | str
        %           A filename used when writing the file with the write()
        %           method. The filename may also be set when calling the
        %           write() method. Filenames for the target and interferer
        %           are determined automatically, by append the filename
        %           with '_target' and '_interferer' respectively.
        %       'fs'            : {obj.target.fs} | scalar
        %           The sampling frequency of the mixture. All HRTFs and/or
        %           sources will be resampled to this frequency each time
        %           the signal is requested.
        %       'gammatone'
        %           Settings for the gammatone filterbank
        %           decomposition. The property is a structure
        %           containing the following fields:
        %               'cfs'     : the centre frequencies of the gammatone
        %                           filterbank (default is 64 channels
        %                           equally spaced on the ERB scale between
        %                           20Hz and the Nyquist limit
        %               'frame'   : the frame length (in samples) (the
        %                           default is 20ms)
        %       'sofa_path'     : {[]} | str
        %           A path to a SOFA file containing HRTFs that are
        %           convolved with sources in order to generate the
        %           mixture.
        %       'stft'
        %           Settings for the STFT decomposition. The property
        %           is a structure containing the following fields:
        %               'hop'     : the hop size of the STFT (the default
        %                           is 512)
        %               'win'     : the STFT window (the default is 1024)
        %       'tir'           : {0} | scalar
        %           The RMS ratio of the target and interfer sources.
        %           Interferer sources are individually set to this level
        %           prior to their summation.
        %
        %   OBJ = IOSR.BSS.MIXTURE creates an empty mixture object with
        %   empty target and interferer sources.
        %
        %   To speed up time-frequency operations, the  IOSR.BSS.MIXTURE
        %   class caches time-frequency data internally, which can lead to
        %   large file sizes if the object is saved. Use the CLEARCACHE()
        %   method to empty the internal cache.
        %
        %   Note that this is a handle class, as is IOSR.BSS.SOURCE. Target
        %   and interferer(s) are hence passed by reference. Use the COPY()
        %   method to create an independent copy of the mixture and its
        %   sources.

            if nargin > 0

                assert(nargin>1, 'iosr:mixture:nargin', 'Not enough input arguments')

                propNames = {'filename','fs','sofa_path','tir','decomposition','stft','gammatone'};

                % set sources
                obj.target = target;
                obj.interferers = interferers;

                % defaults
                obj.fs = obj.target.fs;
                obj.sofa_path = [];
                obj.tir = 0;
                obj.rendered = false;

                % default decomposition settings
                obj.stft = struct('win',1024,'hop',512);
                obj.gammatone = struct(...
                    'cfs',iosr.auditory.makeErbCFs(20, obj.fs/2, 64),...
                    'frame', round(0.01*obj.fs)...
                );

                % read parameter/value inputs
                if nargin > 1 % if parameters are specified
                    obj.set_properties(varargin,propNames)
                end

                % set sample rate to SOFA file if set and fs not specified
                if ~isempty(obj.sofa_path) && all(~strcmp('fs',varargin))
                    SOFAobj = SOFAload(obj.sofa_path); % load SOFA object
                    obj.fs = SOFAobj.Data.SamplingRate;
                end

                % ensure fs for sources matches instance
                obj.target.fs = obj.fs;
                for n = 1:numel(obj.interferers)
                    obj.interferers(n).fs = obj.fs;
                end

                % set parent
                obj.target.parent = obj;
                for n = 1:numel(obj.interferers)
                    obj.interferers(n).parent = obj;
                end

            end

        end

        function z = applyMask(obj,m)
        %APPLYMASK Apply a time-frequency mask to the mixture
        %
        %   Z = IOSR.BSS.MIXTURE.APPLYMASK(M) applies the time-frequency
        %   mask M to the mixture. The mixture is transformed according to
        %   the objects decomposition settings. The transform is then
        %   multiplied with M. Lastly, the result is then inverse
        %   transformed (or reynthesised). The time-domain output Z is
        %   returned.

            switch lower(obj.decomposition)
                case 'stft'
                    s = obj.decomp;
                    % correct dims
                    d = obj.stft_shifted_dims(s);
                    s = ipermute(s,d);
                    m = ipermute(m,d);
                    for c = 1:obj.numchans
                        % apply mask
                        z(:,c) = iosr.bss.applyMask( ...
                            s(:,:,c), ...
                            m(:,:,min(c,size(m,3))), ...
                            obj.stft.win, ...
                            obj.stft.hop, ...
                            obj.fs ...
                        ); %#ok<AGROW>
                    end
                case 'gammatone'
                    x = obj.signal;
                    for c = 1:obj.numchans
                        z(:,c) =  iosr.bss.resynthesise( ...
                            x(:,c), ...
                            obj.fs, ...
                            iosr.bss.cfs2fcs(obj.gammatone.cfs, obj.fs), ...
                            m(:,:,c), ...
                            'frame_length', obj.gammatone.frame, ...
                            'filter', 'sinc', ...
                            'kernel', gausswin(obj.gammatone.frame) ...
                        ); %#ok<AGROW>
                    end
            end

            z = obj.setlength(z,length(obj.signal));

        end

        function sound_t(obj)
        %SOUND_T Replay the target signal
        %
        %   IOSR.BSS.MIXTURE.SOUND_T() replays the target signal.

            obj.replay(obj.signal_t)

        end

        function sound_i(obj)
        %SOUND_I Replay the interferer signal
        %
        %   IOSR.BSS.MIXTURE.SOUND_I() replays the interferer signal.

            obj.replay(obj.signal_i)

        end

        function write(obj,filename)
        %WRITE Save the mixture to an audio file
        %
        %   IOSR.BSS.MIXTURE.WRITE() writes the mixture to an audio file
        %   specified by MIXTURE.FILENAME, and also writes the target and
        %   interferer signals to automatically-determined file names
        %   (MIXTURE.FILENAME_T and MIXTURE.FILENAME_I respectively).
        %
        %   IOSR.BSS.MIXTURE.WRITE(FILENAME) uses the specified FILENAME
        %   and updates MIXTURE.FILENAME.

            if exist('filename','var')==1
                fn = filename;
            else
                fn = obj.filename;
            end

            if obj.rendered && exist(fn, 'file') == 2
                if strcmp(obj.filename,fn)
                    % nothing to do
                else
                    copyfile(obj.filename,fn);
                    copyfile(obj.filename_t,obj.make_target_filename(fn));
                    copyfile(obj.filename_i,obj.make_interferer_filename(fn));
                end
            else
                % check filename is valid
                obj.filename = fn;
                assert(ischar(obj.filename) && ~isempty(obj.filename),'iosr:mixture:invalidFilename','FILENAME must be a non-empty char array. Set filename as MIXTURE.FILENAME or MIXTURE.WRITE(FILENAME).')

                % normalize
                M = obj.signal;
                T = obj.signal_t;
                I = obj.signal_i;
                [~,gain] = obj.normalize([M T I]);
                M = M.*gain;
                T = T.*gain;
                I = I.*gain;

                % ensure path
                obj.ensure_path(obj.filename)

                % write audio files
                audiowrite(obj.filename,M,obj.fs);
                audiowrite(obj.filename_t,T,obj.fs);
                audiowrite(obj.filename_i,I,obj.fs);

                % set flag to indicate mixture has been rendered to an audio file
                obj.rendered = true;

            end

        end

        function deleteFiles(obj)
        %DELETEFILES Delete the mixture audio files

            if exist(obj.filename, 'file') == 2
                delete(obj.filename);
            end
            if exist(obj.filename_t, 'file') == 2
                delete(obj.filename_t);
            end
            if exist(obj.filename_i, 'file') == 2
                delete(obj.filename_i);
            end
            obj.rendered = false;

        end

        function clearCache(obj)
        %CLEARCACHE Clear the internal cache

            obj.updateCachedFalse();
            obj.cached_decomp = [];
            obj.cached_decomp_i = [];
            obj.cached_decomp_t = [];
            obj.cached_tfPower = [];
            obj.cached_tfPower_i = [];
            obj.cached_tfPower_t = [];
        end

        % set/validate properties

        % set decomposition
        function set.decomposition(obj,val)
            assert(any(strcmpi(val,{'stft','gammatone'})), 'iosr:mixture:invalidDecomposition', '''decomposition'' must be ''stft'' or ''gammatone''');
            obj.updateCachedFalse();
            obj.decomposition = val;
        end

        % set gammatone settings
        function set.gammatone(obj,val)
            assert(isstruct(val), 'iosr:mixture:invalidGammatoneStruct', '''gammatone'' must be a struct')
            assert(all(ismember(fieldnames(val)',{'cfs','frame'})), 'iosr:mixture:invalidGammatoneFields', 'gammatone structure must contain fields ''cfs'' and ''frame''')
            obj.updateCachedFalse;
            obj.gammatone = val;
        end

        % set stft settings
        function set.stft(obj,val)
            assert(isstruct(val), 'iosr:mixture:invalidStftStruct', '''stft'' must be a struct')
            assert(all(ismember(fieldnames(val)',{'win','hop'})), 'iosr:mixture:invalidStftFields', 'stft structure must contain fields ''win'' and ''hop''')
            obj.updateCachedFalse;
            obj.stft = val;
        end

        % set tir
        function set.tir(obj,val)
            obj.tir = val;
            obj.property_changed('tir',val);
        end

        % validate sofa_path
        function set.sofa_path(obj,val)
            assert(ischar(val) || isempty(val), 'iosr:mixture:invalidSofaPath', 'sofa_path must be a char array or an empty array')
            if ~isempty(val)
                assert(exist(val,'file')==2, 'iosr:mixture:invalidSofaPath', 'SOFA file does not exist')
            end
            obj.sofa_path = val;
            obj.property_changed('sofa_path',val);
        end

        % validate interferers
        function set.interferers(obj,val)
            assert(isa(val,'iosr.bss.source'), 'iosr:mixture:invalidInterferers', 'INTERFERERS must be of type source')
            obj.interferers = val;
            obj.property_changed('interferers',val);
        end

        % validate target
        function set.target(obj,val)
            assert(isa(val,'iosr.bss.source') && numel(val)==1, 'iosr:mixture:invalidTarget', 'TARGET must be a scalar of type source')
            obj.target = val;
            obj.property_changed('target',val);
        end

        % dependent properties

        % get decomposition result (mixture)
        function s = get.decomp(obj)
            if obj.decomp_cached
                s = obj.cached_decomp;
            else
                s = obj.decompose(obj.signal);
                obj.cached_decomp = s;
                obj.decomp_cached = true;
            end
        end

        % get decomposition result (interferer)
        function s = get.decomp_i(obj)
            if obj.decomp_i_cached
                s = obj.cached_decomp_i;
            else
                s = obj.decompose(obj.signal_i);
                obj.cached_decomp_i = s;
                obj.decomp_i_cached = true;
            end
        end

        % get decomposition result (target)
        function s = get.decomp_t(obj)
            if obj.decomp_t_cached
                s = obj.cached_decomp_t;
            else
                s = obj.decompose(obj.signal_t);
                obj.cached_decomp_t = s;
                obj.decomp_t_cached = true;
            end
        end

        % get azimuthal separation
        function s = get.azi_sep(obj)
            [s,~] = get_loc(obj);
        end

        % get median elevation
        function e = get.elevation(obj)
            [~,e] = get_loc(obj);
        end

        % target filename
        function fn = get.filename_t(obj)
            fn = obj.make_target_filename(obj.filename);
        end

        % interferer filename
        function fn = get.filename_i(obj)
            fn = obj.make_interferer_filename(obj.filename);
        end

        % ibm
        function m = get.ibm(obj)
            switch lower(obj.decomposition)
                case 'stft'
                    for c = 1:obj.numchans
                        [~,m(:,:,c)] = iosr.bss.idealMasks( ...
                            obj.decomp_t(:,:,c), ...
                            obj.decomp_i(:,:,c) ...
                        ); %#ok<AGROW>
                    end
                case 'gammatone'
                    T = obj.tfPower_t;
                    I = obj.tfPower_i;
                    m = +(T>I);
            end
        end

        % irm
        function m = get.irm(obj)
            switch lower(obj.decomposition)
                case 'stft'
                    for c = 1:obj.numchans
                        m(:,:,c) = iosr.bss.idealMasks( ...
                            obj.decomp_t(:,:,c), ...
                            obj.decomp_i(:,:,c) ...
                        ); %#ok<AGROW>
                    end
                case 'gammatone'
                    T = obj.tfPower_t;
                    I = obj.tfPower_i;
                    m = T./(T+I);
            end
            m(isnan(m) | isinf(m)) = 0;
        end

        % filenames of all interferers
        function fns = get.int_fns(obj)
            for n = 1:length(obj.interferers)
                if n==1
                    fns = obj.interferers(n).filename;
                else
                    fns = [fns ', ' obj.interferers(n).filename]; %#ok<AGROW>
                end
            end
        end

        % number of audio channels
        function n = get.numchans(obj)
            if obj.hrtf_is_set()
                s = SOFAload(obj.sofa_path);
                n = size(s.Data.IR,2);
            else
                iN = [obj.interferers.numchans];
                n = max([obj.target.numchans; iN(:)]);
            end
        end

        % return target signal
        function signal_t = get.signal_t(obj)
            if obj.rendered && exist(obj.filename_t,'file')==2 % don't bother calculating
                signal_t = audioread(obj.filename_t);
            else % calculate
                signal_t = return_source(obj,obj.target);
                if obj.tir<0
                    % attenuate according to TIR
                    Trms = iosr.dsp.rms(signal_t(:));
                    Irms = iosr.dsp.rms(obj.signal_i(:));
                    signal_t = signal_t./(Trms/Irms); % match to interferer
                    signal_t = signal_t.*(10^(obj.tir/20)); % attenuate
                end
            end
        end

        % return interferer signal
        function signal_i = get.signal_i(obj)
            if obj.rendered && exist(obj.filename_i,'file')==2 % don't bother calculating
                signal_i = audioread(obj.filename_i);
            else % calculate
                signal_i = zeros(1, obj.numchans); % initialise
                maxlength = 0; % initialise
                for n = 1:numel(obj.interferers) % step through each interferer
                    % source signal (ensure 2-channel)
                    s = obj.return_source(obj.interferers(n));
                    % ensure signals are same length, or zero-pad
                    maxlength = max([length(s) maxlength]);
                    s = obj.setlength(s,maxlength);
                    signal_i = obj.setlength(signal_i,maxlength);
                    % add source to interferer
                    signal_i = signal_i + s;
                end
                if obj.tir>=0
                    % attenuate according to TIR
                    Trms = iosr.dsp.rms(obj.signal_t(:));
                    Irms = iosr.dsp.rms(signal_i(:));
                    signal_i = signal_i./(Irms/Trms); % match to interferer
                    signal_i = signal_i./(10^(obj.tir/20)); % attenuate
                end
            end
        end

        % return mixture signal
        function signal = get.signal(obj)
            if obj.rendered && exist(obj.filename,'file')==2 % don't bother calculating
                signal = audioread(obj.filename);
            else % calculate
                % return target and interferers
                T = obj.signal_t;
                I = obj.signal_i;
                % mix, ensuring equal length
                maxlength = max([length(T) length(I)]);
                signal = obj.setlength(T,maxlength) + obj.setlength(I,maxlength);
            end
        end

        % return time-frequency power (mixture)
        function val = get.tfPower(obj)
            if obj.tfPower_cached
                val = obj.cached_tfPower;
            else
                val = obj.tf_power(obj.decomp);
                obj.cached_tfPower = val;
                obj.tfPower_cached = true;
            end
        end

        % return time-frequency power (interferer)
        function val = get.tfPower_i(obj)
            if obj.tfPower_i_cached
                val = obj.cached_tfPower_i;
            else
                val = obj.tf_power(obj.decomp_i);
                obj.cached_tfPower_i = val;
                obj.tfPower_i_cached = true;
            end
        end

        % return time-frequency power (target)
        function val = get.tfPower_t(obj)
            if obj.tfPower_t_cached
                val = obj.cached_tfPower_t;
            else
                val = obj.tf_power(obj.decomp_t);
                obj.cached_tfPower_t = val;
                obj.tfPower_t_cached = true;
            end
        end

        % return wdo
        function w = get.wdo(obj)
            w = obj.mixWdo(abs(obj.decomp_t), abs(obj.decomp_i));
        end

        % return loudness-weighted wdo
        function w = get.wdo_lw(obj)
            [S,f] = obj.decompose(obj.signal_t);
            w = obj.mixWdo_lw(abs(S), abs(obj.decomp_i), f);
        end

        % return Stokes's wdo
        function w = get.wdo_stokes(obj)
            w = obj.mixWdo_Stokes(obj.irm);
        end

    end

    methods (Static, Access = public)

        function [C, Ct, Cf] = maskCentroids(m, fs, decomposition, nfft, hop)
        %MASKCENTROIDS Return various time-frequency mask centroids
        %
        %   C = IOSR.BSS.MIXTURE.MASKCENTROIDS(M,FS,NFFT,HOP) returns the
        %   time-frequency mask centroid C calculated from mask M, sample
        %   rate FS, FFT-length NFFT, and and hop size HOP. The mask
        %   centroid is a unitless measure of the centroid of a
        %   time-frequency mask. The mask centroid is a measure of the
        %   frequency content of a time-frequency mask.
        %
        %   [C,Ct,Cf] = IOSR.BSS.MIXTURE.MASKCENTROIDS(...) returns the
        %   time-related spectral centroid (in Hz) Ct and the
        %   frequency-related spectral centroid (in seconds) Cf.

            assert(ischar(decomposition), 'iosr:mixture:invalidDecomposition', '''decomposition'' must be a char array')

            numchans = size(m,3);
            frame_frequency = fs/hop;
            switch lower(decomposition)
                case 'stft'
                    assert(isscalar(nfft), 'iosr:mixture:invalidNfft', '''nfft'' must be a scalar')
                    quefrency = fs/nfft;
                case 'gammatone'
                    assert(isvector(nfft), 'iosr:mixture:invalidNfft', '''cfs'' must be a scalar')
                    cfs = nfft;
                    quefrency = fs/min(diff(iosr.auditory.erbRate2hz(cfs)));
                otherwise
                    error('iosr:mixture:unknownDecomp','Unknown decomposition.')
            end

            f_dct = repmat((((0:size(m,1)-1)./size(m,1)).*frame_frequency)',1,size(m,2));
            c_dct = repmat(((0:size(m,2)-1)./size(m,2)).*quefrency,size(m,1),1);

            C = zeros(1, numchans);
            Ct = zeros(1, numchans);
            Cf = zeros(1, numchans);
            for c = 1:numchans
                X = abs(dct(m(:,:,c)));
                sumX = sum(sum(X));
                C(c) = sum(sum(X.*f_dct.*c_dct))./sumX;
                Ct(c) = sum(sum(X.*f_dct))./sumX;
                Cf(c) = sum(sum(X.*c_dct))./sumX;
            end

        end

        function w = mixWdo(st, si)
        %MIXWDO Calculate the mixture w-disjoint orthogonality
        %
        %   W = IOSR.BSS.MIXTURE.MIXWDO(St,Si) calculates the w-disjoint
        %   orthogonality of a target and interferer(s) from their
        %   time-frequency magnitudes St and Si. The w-disjoint
        %   orthogonality is estimated by the two-dimensional
        %   cross-correlation coefficient of the source magnitudes.

            numchans = size(st,3);
            w = zeros(1,numchans);
            for c = 1:numchans
                w(c) = corr2(st(:,:,c),si(:,:,c));
            end

        end

        function w = mixWdo_lw(st, si, f)
        %MIXWDO_LW Calculate the loudness-weighted w-disjoint orthogonality
        %
        %   W = IOSR.BSS.MIXTURE.MIXWDO_LW(St,Si,F) calculates the
        %   loudness-weighted w-disjoint orthogonality of a target and
        %   interferer(s) from their time-frequency magnitudes St and Si.
        %   The weighting is calculated from the ISO-226 65-phon equal
        %   loudness contour using the frequency vector F containing the
        %   frequency of each bin of St and Si. The magnitudes are
        %   multiplied with the loudness-weighting factor prior to
        %   calculating the WDO.
        %
        %   See also IOSR.AUDITORY.LOUDWEIGHT.

            lw = iosr.auditory.loudWeight(f(:), 65)';
            lw = repmat(lw,size(st,1),1,size(st,3));
            w = iosr.bss.mixture.mixWdo(st.*lw,si.*lw);

        end

        function w = mixWdo_Stokes(irm)
        %MIXWDO_LW Calculate the w-disjoint orthogonality using Stokes's method
        %
        %   W = IOSR.BSS.MIXTURE.MIXWDO_STOKES(IRM) calculates the
        %   w-disjoint orthogonality using Stokes's method [1]. The method
        %   utilizes the ideal ratio mask (IRM), which is 0.5 when the
        %   target and interferer have equal power, and tends towards 0 or
        %   1 when either source dominates.
        %
        %   References
        %
        %   [1] Stokes, Tobias W. (2015) Improving the perceptual quality
        %       of single-channel blind audio source separation. Doctoral
        %       thesis, University of Surrey.

            h = hist(irm(:),11)./numel(irm);
            hw = [0:0.2:1 0.8:-0.2:0];
            w = sum(h.*hw);

        end

    end

    methods (Static, Access = private)

        function y = setlength(x,signal_length)
        %SETLENGTH Crop or zero-pad signal to specified length

            d = size(x);
            if size(x,1)>signal_length % need to crop
                subsidx = [{1:signal_length} repmat({':'},1,ndims(x)-1)];
                y = x(subsidx{:});
            elseif size(x,1)<signal_length % need to zero-pad
                y = [x; zeros([signal_length-size(x,1),d(2:end)])];
            else % do nothing
                y = x;
            end

        end

        function fn = append_filename(filename,append)
        %APPEND_FILENAME Append strings to MIXTURE.FILENAME

            if isempty(filename) % do nothing if filename is empty
                fn = [];
            else % append
                [filepath,name,ext] = fileparts(filename); % break up filename
                newname = [name append ext]; % append
                if isempty(filepath) % only filename specified
                    fn = newname;
                else % path specified
                    fn = [filepath filesep newname];
                end
            end

        end

        function d = stft_shifted_dims(s)
        %STFT_SHIFTED_DIMS Dim order for STFT, swapping time and bins

            dim_order = 1:ndims(s);
            if ndims(s) > 2 %#ok<ISMAT>
                d = dim_order([2 1 dim_order(3:end)]);
            else
                d = [2 1];
            end
        end

    end

    methods (Access = private)

        function c = hrtf_is_set(obj)
        %HRTF_IS_SET Determine whether HRTFs are specified

            c = ~isempty(obj.sofa_path);

        end

        function s = return_source(obj,src)
        %RETURN_SOURCE Return signal from specified source

            % ensure correct channel count
            if ~src.precomposed && obj.hrtf_is_set()
                src.numchans = 1; % signal will be spatialised
            else
                src.numchans = obj.numchans; % signal will be mixed directly
            end
            x = src.signal;
            % return/convolve signal
            if ~src.precomposed && obj.hrtf_is_set() % convolve
                s = obj.spat(obj.sofa_path,x,src.azimuth,src.elevation,obj.fs);
            else % return directly
                s = x;
            end

        end

        function [s,em] = get_loc(obj)
        %GET_LOC Return location information

            % get angles from the sources
            a = zeros(numel(obj.interferers)+1,1);
            e = a;
            for n = 1:numel(obj.interferers)
                a(n) = obj.interferers(n).azimuth;
                e(n) = obj.interferers(n).elevation;
            end
            a(end) = obj.target.azimuth;
            e(end) = obj.target.elevation;

            % get separation
            if any(a<0) % assume angles are -179:180
                s = abs(max(a)-min(a));
            else % assume angles are 0:359
                s = (360-max(a))+min(a);
            end
            s = mod(s,180);

            % get elevation
            em = median(e);

        end

        function fn = make_target_filename(obj,filename)
        %MAKE_TARGET_FILENAME Return the automated target filename

            fn = obj.append_filename(filename,'_target');

        end

        function fn = make_interferer_filename(obj,filename)
        %MAKE_INTERFERER_FILENAME Return the automated interferer filename

            fn = obj.append_filename(filename,'_interferer');

        end

        function [s,f,t] = decompose(obj,x)
        %DECOMPOSE Transform a signal into the time-frequency domain

            switch lower(obj.decomposition)
                case 'stft'
                    for c = 1:obj.numchans
                        [s(:,:,c),f,t] = iosr.dsp.stft(x(:,c), obj.stft.win, obj.stft.hop, obj.fs); %#ok<AGROW>
                    end
                    % put time down column
                    s = permute(s,obj.stft_shifted_dims(s));
                    s = obj.setlength(s, obj.get_frame_count(length(obj.signal)));
                case 'gammatone'
                    f = obj.gammatone.cfs;
                    t = (0:length(x)-1)./obj.fs;
                    for c = 1:obj.numchans
                        s(:,:,c) = iosr.auditory.gammatoneFast(x(:,c), obj.gammatone.cfs, obj.fs); %#ok<AGROW>
                    end
            end

        end

        function y = tf_power(obj,x)
        %TF_POWER Calculate TF power.

            switch lower(obj.decomposition)
                case 'gammatone'
                    frame_count = obj.get_frame_count(size(x,1));
                    y = zeros(frame_count,length(obj.gammatone.cfs),obj.numchans);
                    for m = 1:frame_count
                        samples = (m-1)*obj.gammatone.frame+1:m*obj.gammatone.frame;
                        for N = 1:size(x,3)
                            y(m,:,N) = sum(x(samples,:,N).^2);
                        end
                    end
                case 'stft'
                    y = abs(x).^2;
            end

        end

        function frame_count = get_frame_count(obj, signal_length)
        %GET_FRAME_COUNT Calculate number of T-F frames.

            assert(isscalar(signal_length), 'iosr:mixture:invalidSignalLength', 'SIGNAL_LENGTH must be a scalar.')

            switch lower(obj.decomposition)
                case 'gammatone'
                    frame_count = floor(signal_length/obj.gammatone.frame);
                case 'stft'
                    frame_count = fix((signal_length-(obj.nfft-obj.stft.hop))/obj.stft.hop);
            end

        end

        function n = nfft(obj)
        %NFFT return the fft length

            if isscalar(obj.stft.win)
                n = obj.stft.win;
            else
                n = length(obj.stft.win);
            end
        end

        function updateCachedFalse(obj)
            obj.decomp_cached = false;
            obj.decomp_i_cached = false;
            obj.decomp_t_cached = false;
            obj.tfPower_cached = false;
            obj.tfPower_i_cached = false;
            obj.tfPower_t_cached = false;
        end

    end

    methods(Access = protected)

        function property_changed(obj,name,val)
        %PROPERTY_CHANGED handle property changes

            obj.rendered = false;
            obj.updateCachedFalse();

            switch lower(name)
                case 'fs'
                    obj.target.fs = val;
                    for n = 1:numel(obj.interferers)
                        obj.interferers(n).fs = val;
                    end
            end

        end

        function cpObj = copyElement(obj)
        %COPYELEMENT Overload copy method with additional functionality

            % Make a shallow copy of all properties
            cpObj = copyElement@iosr.dsp.audio(obj);

            % Changed rendered file name
            cpObj.filename = obj.append_filename(cpObj.filename,'_copy');

            % copy files
            if obj.rendered
                if exist(obj.filename,'file')==2
                    copyfile(obj.filename,cpObj.filename)
                end
                if exist(obj.filename_i,'file')==2
                    copyfile(obj.filename_i,cpObj.filename_i)
                end
                if exist(obj.filename_t,'file')==2
                    copyfile(obj.filename_t,cpObj.filename_t)
                end
            end

            % Make a deep copy of target and interferers
            cpObj.target = copy(obj.target);
            cpObj.interferers = copy(obj.interferers);

        end

    end

end


function [subplot_handles,LabelsFontSize] = subplotplus(subplot_array,varargin)
% SUBPLOTPLUS Enhanced subplots creation function.
%
%   [subplot_handles,LabelsFontSize] = subplotplus(subplot_array,varargin)
%   is an enhanced alternative to the inherit subplot() function of
%   MATLAB. Using subplotplus() almost any layout of subplots can be
%   created in a particular figure. The desired subplot layout is described
%   using a complex cell array where each "atomic" cell, within the cell
%   array corresponds to a subplot. The function scans the content of the
%   input cell array and build the desired matching subplot layout in the
%   figure. The size, alignment and font size of the subplots is
%   automatically adjusted for best appearance. The function is recursive
%   in nature so any level of subplot divisions can be created. The
%   returned arguments are the created subplot handles as well as the
%   optimal label (X and Y) font size that should be used for a specific
%   subplot for best appearance.
%
%   Input arguments:
%   + subplot_array: A cell array that describes the desired subplot
%   layput. For example, a 1x2 subplot layout where the right-end subplot is
%   further divided down to a column of 3 additional subplots is described
%   by the following cell expression: {{[]},{{[]};{[]};{[]}}}. A group of
%   subplot cells, sharing the same "parent" cell, can be glued together in order
%   to save figure space using the '-g' option at the cell's content (instead of
%   empty group []). For example, if the 3 columned subplots of the
%   previous example are to be glued together, sharing the same X-axis, the
%   follwing cell expression should be used:
%   {{[]},{{['...-g...']};{['...-g...']};{['...-g...']}}}.
%   + varargin: only used for function recursion and should not be used by the user.
%
%   Output arguments:
%   + subplot_handles: A list of the created subplot handles by their order
%   of appearnace in the figure, starting from the left-top corner in a
%   clockwise manner.
%   + LabelsFontSize: A matching list of X/Y-axis optimal labels size for further
%   user work on the subplots.
%
%   Alon Geva
%   $Revision: 1.00 $  $Date: 12/01/2012 18:52:53 $

arraydim =  cell_dimension(subplot_array);

if (nargin == 1)
    clf;
    for i=1:arraydim,
        subplot(prod(arraydim),1,i);
    end
    subplot_handles = get(gcf,'children');
    subplot_location = [0.015 0 1-0.015 1]; %full plot region
    subplot_handles = flipud(subplot_handles);
    original_subplot_handles = subplot_handles;
    LabelsFontSize = zeros(length(original_subplot_handles),2);
else
    subplot_handles = varargin{1};
    subplot_location = varargin{2};
    LabelsFontSize = varargin{3};
    original_subplot_handles = -1;
end

subplotarraysize=size(subplot_array);
%i=1;
glue_FLAG = 0;
if ~iscell(subplot_array{1,1})
    if ischar(subplot_array{1,1})
        glue_FLAG = ~isempty(regexp(subplot_array{1,1},'-g','start'));
    end
elseif ~iscell(subplot_array{1,1}{1,1})
    if ischar(subplot_array{1,1}{1,1})
        glue_FLAG = ~isempty(regexp(subplot_array{1,1}{1,1},'-g','start'));
    end
end

if (glue_FLAG == 1)%(strcmp(subplot_array{1,1},'-g')==1)%(~isempty(regexp(subplot_array{1,1},'-g','start')))
    subplot_location(3) = subplot_location(3)*0.8;
    subplot_location(4) = subplot_location(4)*0.8;
    subplot_location(1) = subplot_location(1)+subplot_location(3)*0.1;
    subplot_location(2) = subplot_location(2)+subplot_location(4)*0.1;
end

for m=1:subplotarraysize(1),
    for n=1:subplotarraysize(2),
        %s = size(subplot_array{m,n});

        if (iscell(subplot_array{m,n}))
            recursion_FLAG = iscell(subplot_array{m,n}{1,1});
        else
            recursion_FLAG = 0;
        end

        if (~recursion_FLAG)%iscell(subplot_array{m,n}{1,1})%(s(1)<=1 && s(2)<=1)%
            M = subplotarraysize(1);
            N = subplotarraysize(2);
            if (~glue_FLAG)%(strcmp(subplot_array{1,1},'-g')~=1)%(isempty(regexp(subplot_array{1,1},'-g','start')))%
                set(subplot_handles(1),'position',[subplot_location(1)+(2*n-1)*subplot_location(3)/(2*N)-0.5*subplot_location(3)/N/(0.5/0.3347),...
                                                    subplot_location(2)+subplot_location(4)-(2*m-1)*subplot_location(4)/(2*M)-0.5*subplot_location(4)/M/(0.5/0.3412),...
                                                    subplot_location(3)/N/(0.5/0.3347),...
                                                    subplot_location(4)/M/(0.5/0.3412)]);
                Xfontfactor = subplot_location(3)/N/(0.5/0.3347);
                Yfontfactor = subplot_location(4)/M/(0.5/0.3412);
            else
                set(subplot_handles(1),'position',[subplot_location(1)+(2*n-1)*subplot_location(3)/(2*N)-0.5*subplot_location(3)/N/(0.5/0.5),...
                                                    subplot_location(2)+subplot_location(4)-(2*m-1)*subplot_location(4)/(2*M)-0.5*subplot_location(4)/M/(0.5/0.5),...
                                                    subplot_location(3)/N/(0.5/0.5),...
                                                    subplot_location(4)/M/(0.5/0.5)]);
                Xfontfactor = subplot_location(3)/N/(0.5/0.5);
                Yfontfactor = subplot_location(4)/M/(0.5/0.5);

                if (m == 1)
                    %set(subplot_handles(1),'XAxis','on');
                    set(subplot_handles(1),'XAxisLocation','top');
                elseif (m == subplotarraysize(1))
                    %set(subplot_handles(1),'XAxis','on');
                    set(subplot_handles(1),'XAxisLocation','bottom');
                else
                    set(subplot_handles(1),'XAxisLocation','bottom');
                end

                if (n == 1)
                    set(subplot_handles(1),'YAxisLocation','left');
                elseif (n == subplotarraysize(2))
                    set(subplot_handles(1),'YAxisLocation','right');
                else
                    set(subplot_handles(1),'YAxisLocation','right');
                end

%                 if (subplotarraysize(1) > 1)
%                     ylimmode = get(subplot_handles(1),'YLimMode');
%                     if (strcmp(ylimmode,'auto')==1)
%                         %ytick = get(subplot_handles(1),'YTick');
%                         ylim = get(subplot_handles(1),'YLim');
%                         set(subplot_handles(1),'YLimMode','manual','YLim',[ylim(1)-0.1*diff(ylim) ylim(2)+0.1*diff(ylim)]);
%                         %set(subplot_handles(1),'YTickMode','manual','YTick',ytick);
%                     end
%                 end
%
%                 if (subplotarraysize(2) > 1)
%                     xlimmode = get(subplot_handles(1),'XLimMode');
%                     if (strcmp(xlimmode,'auto')==1)
%                         %ytick = get(subplot_handles(1),'YTick');
%                         xlim = get(subplot_handles(1),'XLim');
%                         set(subplot_handles(1),'XLimMode','manual','XLim',[xlim(1)-0.1*diff(xlim) xlim(2)+0.1*diff(xlim)]);
%                         %set(subplot_handles(1),'YTickMode','manual','YTick',ytick);
%                     end
%                 end

            end

            set(subplot_handles(1),'XGrid','on','YGrid','on','Box','on','XMinorTick','on','YMinorTick','on','XMinorGrid','off','YMinorGrid','off');

            fontsize = 10+3*log10(min(Xfontfactor,Yfontfactor));
            if (fontsize > 10)
                fontsize = 10;
            elseif (fontsize < 1)
                fontsize = 1;
            end

            LabelsFontSize_tmp = 10+3*log10([Xfontfactor,Yfontfactor]);
            LabelsFontSize_tmp(LabelsFontSize_tmp > 10) = 10;
            LabelsFontSize_tmp(LabelsFontSize_tmp < 1) = 1;
            LabelsFontSize(length(LabelsFontSize)-length(subplot_handles)+1,:) = LabelsFontSize_tmp;

            set(subplot_handles(1),'FontSize',fontsize);
            subplot_handles = subplot_handles(2:end);
            %i = i + 1; %advancing subplots handle index
        else
            [subplot_handles,LabelsFontSize] = subplotplus(subplot_array{m,n},...
                                                        subplot_handles(1:end),...
                                                        [subplot_location(1)+(n-1)*subplot_location(3)/subplotarraysize(2),...
                                                         subplot_location(2)+subplot_location(4)-m*subplot_location(4)/subplotarraysize(1),...
                                                         subplot_location(3)/subplotarraysize(2),...
                                                         subplot_location(4)/subplotarraysize(1)],...
                                                         LabelsFontSize);
        end
    end
end

if (original_subplot_handles ~= -1) %root of recurrence
    subplot_handles = original_subplot_handles;
end

%%
function dimension = cell_dimension(C)

cellsize=size(C);
%p=prod(size(cell));
dimension=0;

for m=[1:cellsize(1)],
    for n=[1:cellsize(2)],
        s = size(C{m,n});
        if (s(1)<=1 && s(2)<=1)
            dimension = dimension + 1;
        else
            dimension = dimension + cell_dimension(C{m,n});
        end
    end
end


classdef testShowingError < test

    properties (Access = protected)
        error
    end

    properties (Abstract, Access = protected)
        tol
    end

    methods (Access = protected)
        function printTestPassed(obj)
            cprintf('green',obj.FileName);
            cprintf('green',' PASSED.');
            cprintf('black',['Error: ',num2str(obj.error),'\n']);
        end

        function printTestNotPassed(obj)
            cprintf('red',obj.FileName);
            cprintf('red',' FAILED.');
            cprintf('red',['Error: ',num2str(obj.error),'\n']);
        end

        function hasPassed = hasPassed(obj)
            obj.computeError()
            hasPassed = obj.error < obj.tol();
        end
    end

    methods (Abstract, Access = protected)
        computeError(obj)
    end
end


function install_paths()
%THE SCRIPT to install the sltoolbox paths to matlab system

fp = mfilename('fullpath');
rootdir = fileparts(fp);

subfolders = { ...
    'ann'; ...
    'cluster'; ...
    'core'; ...
    'discrete'; ...
    'ExpDL'; ...
    'fileio'; ...
    'graph'; ...
    'imgproc'; ...
    'interp'; ...
    'kernel'; ...
    'learn'; ...
    'manifold'; ...
    'perfeval'; ...
    'regression'; ...
    'smallmat'; ...
    'stat'; ...
    'subspace'; ...
    'subspace_ex'; ...
    'tensor'; ...
    'text'; ...
    'utils'; ...
    'utils_ex'; ...
    'visualize'; ...
    'xmlkits'};

n = length(subfolders);
folderpaths = cell(1, n);

for i = 1 : n
    folderpaths{i} = fullfile(rootdir, subfolders{i});
    fprintf('Add path: %s\n', folderpaths{i});
end

addpath(folderpaths{:});
savepath;

disp('All paths have been added.');
disp(' ');  % a blank line


classdef TitleColorable < handle
    % Mixin for component with Title font color

    % Copyright 2020-2021 The MathWorks Inc.

    %% Properties
    properties (AbortSet)

        % Title color
        TitleColor (1,3) double {wt.validators.mustBeBetweenZeroAndOne} = [0 0 0]

    end %properties

    %% Internal properties
    properties (AbortSet, Transient, NonCopyable, ...
            Access = {?wt.abstract.BaseWidget, ?wt.test.BaseWidgetTest} )

        % List of graphics controls to apply to
        TitleColorableComponents (:,1) matlab.graphics.Graphics

    end %properties

    %% Accessors
    methods

        function set.TitleColor(obj,value)
            obj.TitleColor = value;
            obj.updateTitleColorableComponents()
        end

        function set.TitleColorableComponents(obj,value)
            obj.TitleColorableComponents = value;
            obj.updateTitleColorableComponents()
        end

    end %methods

    %% Methods
    methods (Access = protected)

        function updateTitleColorableComponents(obj)

            wt.utility.fastSet(obj.TitleColorableComponents,"FontColor",obj.TitleColor)

        end %function

    end %methods

end %classdef


disp('Installing proxTV with no multi-thread support...');
cd src
mex -lmwblas -lmwlapack -lm CXXOPTIMFLAGS=-O3 CXXFLAGS="\$CFLAGS" LDFLAGS="\$LDFLAGS" solveTV1_PNc.cpp TVopt.cpp
mex -lmwblas -llapack -lm CXXOPTIMFLAGS=-O3 CXXFLAGS="\$CFLAGS" LDFLAGS="\$LDFLAGS" solveTV2_morec2.cpp TVopt.cpp
mex -lmwblas -llapack -lm CXXOPTIMFLAGS=-O3 CXXFLAGS="\$CFLAGS" LDFLAGS="\$LDFLAGS" solveTVgen_PDykstrac.cpp TVopt.cpp
cd ..
disp('proxTV successfully installed.');


function [sens] = ft_read_sens(filename, varargin)

% FT_READ_SENS read sensor positions from various manufacturer specific files. See
% further down for the list of file types that are supported.
%
% Use as
%   grad = ft_read_sens(filename, ...)  % for gradiometers
%   elec = ft_read_sens(filename, ...)  % for electrodes
%
% Additional options should be specified in key-value pairs and can be
%   'fileformat'     = string, see the list of supported file formats (the default is determined automatically)
%   'senstype'       = string, can be 'eeg', 'meg' or 'nirs', specifies which type of sensors to read from the file (default = 'eeg')
%   'coordsys'       = string, 'head' or 'dewar' (default = 'head')
%   'coilaccuracy'   = scalar, can be empty or a number (0, 1 or 2) to specify the accuracy (default = [])
%
% The electrode, gradiometer and optode structures are defined in more detail
% in FT_DATATYPE_SENS.
%
% Files from the following acquisition systems and analysis platforms file formats
% are supported.
%   asa_elc besa_elp besa_pos besa_sfp yokogawa_ave yokogawa_con yokogawa_raw 4d
%   4d_pdf 4d_m4d 4d_xyz ctf_ds ctf_res4 itab_raw itab_mhd netmeg neuromag_fif
%   neuromag_mne neuromag_mne_elec neuromag_mne_grad polhemus_fil polhemus_pos
%   zebris_sfp spmeeg_mat eeglab_set localite_pos artinis_oxy3 artinis_oxyproj matlab
%
% See also FT_READ_HEADER, FT_DATATYPE_SENS, FT_PREPARE_VOL_SENS, FT_COMPUTE_LEADFIELD,

% Copyright (C) 2005-2018 Robert Oostenveld
%
% 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$

% optionally get the data from the URL and make a temporary local copy
filename = fetch_url(filename);

% get the options
fileformat     = ft_getopt(varargin, 'fileformat', ft_filetype(filename));
senstype       = ft_getopt(varargin, 'senstype');         % can be eeg or meg, default is automatic when []
coordsys       = ft_getopt(varargin, 'coordsys', 'head'); % this is used for ctf and neuromag_mne, it can be head or dewar
coilaccuracy   = ft_getopt(varargin, 'coilaccuracy');     % empty, or a number between 0 to 2

realtime = any(strcmp(fileformat, {'fcdc_buffer', 'ctf_shm', 'fcdc_mysql'}));

if realtime
  % skip the rest of the initial checks to increase the speed for realtime operation
else
  % test whether the file exists
  if ~exist(filename, 'file')
    ft_error('file ''%s'' does not exist', filename);
  end
end

switch fileformat
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  % read the content from various files that contain EEG electrode positions
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  case 'asa_elc'
    sens = read_asa_elc(filename);

  case 'artinis_oxy3'
    ft_hastoolbox('artinis', 1);
    hdr = read_artinis_oxy3(filename, false);
    sens = hdr.opto;

  case 'artinis_oxyproj'
    ft_hastoolbox('artinis', 1);
    hdr = read_artinis_oxyproj(filename);
    sens = hdr.opto;

  case 'polhemus_pos'
    sens = read_polhemus_pos(filename);

  case 'besa_elp'
    ft_error('unknown fileformat for electrodes or gradiometers');
    % the code below does not yet work
    fid = fopen_or_error(filename);
    % the ascii file contains: type, label, angle, angle
    tmp = textscan(fid, '%s%s%f%f');
    fclose(fid);
    sel = strcmpi(tmp{1}, 'EEG');  % type can be EEG or POS
    sens.label = tmp{2}(sel);
    az = tmp{3}(sel) * pi/180;
    el = tmp{4}(sel) * pi/180;
    r  = ones(size(el));
    [x, y, z] = sph2cart(az, el, r);
    sens.chanpos = [x y z];

  case 'besa_pos'
    tmp = importdata(filename);
    if ~isnumeric(tmp)
      ft_error('unexpected file format for fileformat=besa_pos')
    end
    [nchan,nrow] = size(tmp);
    if nrow==3
      sens.pnt = tmp;
    elseif nrow==9
      pnt1 = tmp(:,1:3);  % bottom coil
      pnt2 = tmp(:,4:6);  % top coil
      ori  = tmp(:,7:9);  % orientation of bottom coil
      sens.pnt = [pnt1; pnt2];
      sens.ori = [ori; ori];
      sens.tra = [eye(nchan) -eye(nchan)];
    else
      ft_error('unexpected file format for fileformat=besa_pos')
    end
    [p, f, x] = fileparts(filename);
    elpfile = fullfile(p, [f '.elp']);
    elafile = fullfile(p, [f '.ela']);
    if exist(elpfile, 'file')
      ft_warning('reading channel labels from %s', elpfile);
      % read the channel names from the accompanying ELP file
      lbl = importdata(elpfile);
      sens.label = strrep(lbl.textdata(:,2) ,'''', '');
    elseif exist(elafile, 'file')
      ft_warning('reading channel labels from %s', elafile);
      % read the channel names from the accompanying ELA file
      lbl = importdata(elafile);
      lbl = strrep(lbl, 'MEG ', ''); % remove the channel type
      lbl = strrep(lbl, 'EEG ', ''); % remove the channel type
      sens.label = lbl;
    else
      % the file does not have channel labels in it
      ft_warning('creating fake channel names for besa_pos');
      for i=1:nchan
        sens.label{i} = sprintf('%03d', i);
      end
    end

  case 'besa_sfh'
    sfh = readBESAsfh(filename);
    sens.label   = sfh.SurfacePointsLabels(:);
    sens.elecpos = sfh.SurfacePointsCoordinates(:,1:3);
    sel = true(sfh.NrOfPoints, 1);
    for i=1:sfh.NrOfPoints
      tok = tokenize(sens.label{i}, '_');
      sens.label{i} = tok{2};
      sel(i) = ~strcmp(tok{1}, 'Fid');
    end
    sens.label   = sens.label(sel);
    sens.elecpos = sens.elecpos(sel,:);

  case 'besa_sfp'
    [lab, pos] = read_besa_sfp(filename);
    sens.label   = lab;
    sens.elecpos = pos;

  case 'bioimage_mgrid'
    sens = read_bioimage_mgrid(filename);

  case {'ctf_ds', 'ctf_res4', 'ctf_old', 'neuromag_fif', 'neuromag_mne', '4d', '4d_pdf', '4d_m4d', '4d_xyz', 'yokogawa_ave', 'yokogawa_con', 'yokogawa_raw', 'ricoh_ave', 'ricoh_con', 'itab_raw' 'itab_mhd', 'netmeg'}
    % gradiometer information is always stored in the header of the MEG dataset, hence uses the standard fieldtrip/fileio ft_read_header function
    hdr = ft_read_header(filename, 'headerformat', fileformat, 'coordsys', coordsys, 'coilaccuracy', coilaccuracy);
    % sometimes there can also be electrode position information in the header
    if isfield(hdr, 'elec') && isfield(hdr, 'grad')
      if isempty(senstype)
        % set the default
        ft_warning('both electrode and gradiometer information is present, returning the electrode information by default');
        senstype = 'eeg';
      end
      switch lower(senstype)
        case 'eeg'
          sens = hdr.elec;
        case 'meg'
          sens = hdr.grad;
        otherwise
          ft_error('incorrect specification of senstype');
      end
    elseif isfield(hdr, 'grad')
      sens = hdr.grad;
    elseif isfield(hdr, 'elec')
      sens = hdr.elec;
    else
      ft_error('neither electrode nor gradiometer information is present');
    end

  case {'curry_dat', 'curry_cdt'}

    hdr = ft_read_header(filename);

    if ~isempty(hdr.orig.sensorpos)
      sens.elecpos = hdr.orig.sensorpos';
      sens.label   = hdr.label(1:size(sens.elecpos, 1));
    end

  case 'fcdc_buffer'
    % the online header should have a binary blob with the sensor information
    hdr = ft_read_header(filename, 'headerformat', fileformat);
    if isfield(hdr, 'orig') && isfield(hdr.orig, 'neuromag_header')
      hdr = decode_fif(hdr.orig.neuromag_header);
    end
    if isfield(hdr, 'orig') && isfield(hdr.orig, 'ctf_res4')
      hdr = decode_res4(hdr.orig.ctf_res4);
    end
    if isempty(senstype)
      % set the default
      ft_warning('both electrode and gradiometer information is present, returning the electrode information by default');
      senstype = 'eeg';
    end
    switch lower(senstype)
      case 'eeg'
        sens = hdr.elec;
      case 'meg'
        sens = hdr.grad;
      otherwise
        ft_error('incorrect specification of senstype');
    end

  case 'neuromag_mne_grad'
    % the file can contain both, force reading the gradiometer info
    % note that this functionality overlaps with senstype=eeg/meg
    hdr = ft_read_header(filename, 'headerformat', 'neuromag_mne', 'coordsys', coordsys, 'coilaccuracy', coilaccuracy);
    sens = hdr.grad;

  case 'neuromag_mne_elec'
    % the file can contain both, force reading the electrode info
    % note that this functionality overlaps with senstype=eeg/meg
    hdr = ft_read_header(filename, 'headerformat', 'neuromag_mne', 'coordsys', coordsys, 'coilaccuracy', coilaccuracy);
    sens = hdr.elec;

  case {'spmeeg_mat', 'eeglab_set'}
    % this is for EEG formats where electrode positions can be stored with the data
    hdr = ft_read_header(filename, 'coordsys', coordsys, 'coilaccuracy', coilaccuracy);
    if isfield(hdr, 'grad')
      sens = hdr.grad;
    elseif isfield(hdr, 'elec')
      sens = hdr.elec;
    else
      ft_error('no electrodes or gradiometers found in the file')
    end

  case 'polhemus_fil'
    % these are created at the FIL in London with a polhemus tracker
    [sens.fid.pnt, sens.pnt, sens.fid.label] = read_polhemus_fil(filename, 0);
    % the file does not have channel labels in it
    ft_warning('no channel names in polhemus file, using numbers instead');
    for i=1:size(sens.pnt, 1)
      sens.label{i} = sprintf('%03d', i);
    end

  case 'matlab'
    % MATLAB files can contain all sensor arrays
    matfile = filename;   % this solves a problem with the MATLAB compiler v3
    var = whos('-file', filename);
    sel = intersect({'elec', 'grad', 'opto', 'sens', 'elc'}, {var(:).name});
    if numel(sel)==1
      % read the specific variable
      sens = loadvar(matfile, sel{1});
    else
      % read whatever variable is in the file, this will error if the file contains multiple variables
      sens = loadvar(matfile);
    end

  case 'zebris_sfp'
    % these are created by a Zebris tracker, at CRC in Liege at least.
    [sens.fid.pnt, sens.chanpos, sens.fid.label, sens.label] = read_zebris(filename, 0);
    % convert to columns
    sens.label = sens.label(:);
    sens.fid.label = sens.fid.label(:);

  case '4d_el_ascii'
    fid = fopen_or_error(filename, 'rt');
    c = textscan(fid, '%s%s%f%f%f');
    l = c{:,1}; % label
    s = c{:,2}; % status, it can be 'Collected' or empty
    x = c{:,3};
    y = c{:,4};
    z = c{:,5};
    % shift the columns with one where the status is not specified
    sel = isnan(z);
    z(sel) = y(sel);
    y(sel) = x(sel);
    x(sel) = str2double(s(sel));
    s(sel) = {''};
    fclose(fid);
    if false
      % return all positions, including the ones that do not correspond to
      % electrodes per see, such as the fiducials and localizer coils
      sens          = [];
      sens.label    = l;
      sens.elecpos  = [x y z];
    else
      % split the electrodes and fiducials
      % this is consistent with zebris_sfp and with the output of ft_read_headshape
      sens            = [];
      sens.label      = l(~sel);
      sens.elecpos    = [x(~sel) y(~sel) z(~sel)];
      sens.fid.label  = l(sel);
      sens.fid.pnt    = [x(sel) y(sel) z(sel)];
    end

  case {'localite_pos','localite_ins'}
    if ~usejava('jvm') % Using xml2struct requires java
      fid = fopen_or_error(filename);

      % Read marker-file and store contents in cells of strings
      tmp = textscan(fid,'%s');

      fclose(fid);

      % Search for cells that contain coordinates
      selx = strncmp('data0',tmp{1},5);
      sely = strncmp('data1',tmp{1},5);
      selz = strncmp('data2',tmp{1},5);
      sellab = strncmp('description',tmp{1},5);

      % Extract cells that contain coordinates
      xtemp  = tmp{1}(selx);
      ytemp  = tmp{1}(sely);
      ztemp  = tmp{1}(selz);
      labtemp = tmp{1}(sellab);

      % Determine which channels are set. In localite channels that are not set
      % automatically receive coordinates [0, 0, 0] and should therefore
      % be discarded.
      settemp = tmp{1}(strncmp('set',tmp{1},3));
      selset = strncmp('set="f',settemp,6);

      % Remove channels that are not set
      xtemp(selset) = [];
      ytemp(selset) = [];
      ztemp(selset) = [];
      labtemp(selset) = [];

      % Convert cells that contain coordinates from string to double
      x = [];
      y = [];
      z = [];
      lbl = [];

      for i=1:numel(xtemp)
        x(i,1) = str2double(xtemp{i}(8:end-1));
        y(i,1) = str2double(ytemp{i}(8:end-1));
        z(i,1) = str2double(ztemp{i}(8:end-3));
        lbl{i,1} = labtemp{i}(14:end-1);
      end

      % Create and fill sens structure
      sens = [];
      sens.elecpos = [x y z];
      sens.chanpos = sens.elecpos;
      sens.label = lbl;
    else
      tmp = xml2struct(filename);

      sens = [];

      % Loop through structure obtained from xml-file and store
      % coordinate information into sens structure of channels that are
      % set.
      for i=1:numel(tmp)
        if strcmp(tmp(i).Marker.set, 'true')
          sens.elecpos(i,1) = str2double(tmp(i).Marker.ColVec3D.data0);
          sens.elecpos(i,2) = str2double(tmp(i).Marker.ColVec3D.data1);
          sens.elecpos(i,3) = str2double(tmp(i).Marker.ColVec3D.data2);
          sens.label{i} = tmp(i).Marker.description;
        end
      end

      sens.chanpos = sens.elecpos;
    end

  case 'easycap_txt'
    % Read the file and store all contents in cells of strings
    fid = fopen_or_error(filename);
    tmp = textscan(fid,'%s%s%s%s');
    fclose(fid);

    sens = [];
    if all(cellfun(@isempty, tmp{4}))
      % it contains theta and phi
      sens.label = tmp{1}(2:end);
      theta = cellfun(@str2double, tmp{2}(2:end));
      phi   = cellfun(@str2double, tmp{3}(2:end));
      radians = @(x) pi*x/180;
      ft_warning('assuming a head radius of 85 mm');
      x = 85*cos(radians(phi)).*sin(radians(theta));
      y = 85*sin(radians(theta)).*sin(radians(phi));
      z = 85*cos(radians(theta));
      sens.unit = 'mm';
      sens.elecpos = [x y z];
      sens.chanpos = [x y z];
    else
      % it contains X, Y, Z
      sens.label = tmp{1}(2:end);
      x = cellfun(@str2double, tmp{2}(2:end));
      y = cellfun(@str2double, tmp{3}(2:end));
      z = cellfun(@str2double, tmp{4}(2:end));
      sens.elecpos = [x y z];
      sens.chanpos = [x y z];
    end

  case 'neuromag_iso'
    ft_hastoolbox('mne', 1);
    FIFF = fiff_define_constants();
    [fid, tree, dir] = fiff_open(filename);
    isotrak = fiff_dir_tree_find(tree, FIFF.FIFFB_ISOTRAK);
    sel = find([isotrak.dir.kind]==FIFF.FIFF_DIG_POINT);
    sens = [];
    sens.elecpos = nan(numel(sel),3);
    sens.chanpos = nan(numel(sel),3);
    coordsys     = nan(numel(sel),1);
    for i=sel
      tag = fiff_read_tag(fid,isotrak.dir(i).pos);
      sens.elecpos(i,:) = tag.data.r;
      sens.chanpos(i,:) = tag.data.r;
      coordsys(i)       = tag.data.coord_frame;
    end
    fclose(fid);

    if all(coordsys==FIFF.FIFFV_COORD_DEVICE)
      sens.coordsys = 'device';
    elseif all(coordsys==FIFF.FIFFV_COORD_ISOTRAK)
      sens.coordsys = 'isotrak';
    elseif all(coordsys==FIFF.FIFFV_COORD_HPI)
      sens.coordsys = 'hpi';
    elseif all(coordsys==FIFF.FIFFV_COORD_HEAD)
      sens.coordsys = 'head';
    else
      sens.coordsys = 'unknown';
    end

    ft_warning('creating fake channel names for neuromag_iso');
    for i=1:size(sens.chanpos,1)
      sens.label{i} = sprintf('%d', i);
    end

  case 'neuromag_cal'
    dat = cell(1,14);
    [dat{:}] = textread(filename, '%s%f%f%f%f%f%f%f%f%f%f%f%f%f');
    label = dat{1};
    x = dat{2};
    y = dat{3};
    z = dat{4};
    rot = cat(2, dat{5:13});
    cal = dat{14}; % ??
    % construct the sensor structucture
    % it seems that the channel positions are expressed in dewar cordinates
    % it would be possible to use coil_def.dat to construct the coil positions
    sens.label = label;
    sens.chanpos = [x y z];

  otherwise
    ft_error('unknown fileformat for electrodes or gradiometers');
end % switch fileformat

% ensure that the sensor description is up-to-date
% this will also add chantype and units to the sensor array if missing
sens = ft_datatype_sens(sens);

% ensure that the output is consistent with the type requested by the user
if strcmpi(senstype, 'meg')
  assert(isfield(sens,'coilpos'), 'cannot read gradiometer information from %s', filename);
elseif strcmpi(senstype, 'eeg')
  assert(isfield(sens,'elecpos'), 'cannot read electrode information from %s', filename);
else
  % it is empty if not specified by the user, in that case either one is fine
end


% dtiConvertFibersToMat.m
%
% This script takes n .pdb or .Bfloat fiber groups for a group of subjects
% and saves them as .mat files so they can be easily loaded in dtiFiberUI.
%
% HISTORY:
% 07/27/2009 LMP wrote the thing.
%

%%
baseDir = '/biac3/wandell4/data/reading_longitude/';
yr = {'dti_adults'};
subs = {'aab050307','ah051003','am090121','ams051015','as050307','aw040809','bw040922','ct060309','db061209','dla050311'...
    'gd040901','gf050826','gm050308','jl040902','jm061209','jy060309','ka040923','mbs040503','me050126','mo061209',...
    'mod070307','mz040828','pp050208','rfd040630','rk050524','sc060523','sd050527','sn040831','sp050303','tl051015'};

fibers = {'scoredFG_Controls_Mori_Occ_CC_100k_top1000_LEFT'...
    'scoredFG_Controls_Mori_Occ_CC_100k_top1000_RIGHT'...
    'scoredFG_Controls_Mori_Occ_CC_100k_top10000_LEFT'...
    'scoredFG_Controls_Mori_Occ_CC_100k_top10000_RIGHT'...
    'scoredFG_Controls_Mori_Occ_CC_100k_top20000_LEFT'...
    'scoredFG_Controls_Mori_Occ_CC_100k_top20000_RIGHT'};

%%  Loops through subs

for ii=1:length(subs)
    for jj=1:length(yr)
        sub = dir(fullfile(baseDir,yr{jj},[subs{ii} '*']));
        if ~isempty(sub)
            subDir = fullfile(baseDir,yr{jj},sub.name);
            fiberDir = fullfile(subDir,'dti06','fibers','conTrack');
            mFiberDir = fullfile(subDir,'dti06','fibers','conTrack','occ_MORI_clean');

            disp(['Processing ' subDir '...']);

            dt = dtiLoadDt6(fullfile(subDir,'dti06','dt6.mat'));
            xform = dt.xformToAcpc;

            c = 0;
            for kk=1:numel(fibers)
            c = (c+1);
                filename = fullfile(fiberDir,[fibers{kk} '.pdb']);
                if ~exist(filename) || isempty(filename)
                    filename =fullfile(fiberDir,[fibers{kk} '.Bfloat']);
                end
                [fg,filename] = mtrImportFibers(filename,xform);
                fg.name = fibers{kk};
                if mod(c,2) == 0
                    fg.colorRgb = [255 20 20];
                else
                    fg.colorRgb = [20 20 255];
                end
                dtiWriteFiberGroup(fg,fullfile(mFiberDir,fg.name));
            end

        else disp('No data for this subject');
        end
    end

end
disp('Done!');


% ------------------------------------------------------------------------
%  Copyright (C)
%  Torr Vision Group (TVG)
%  University of Oxford - UK
%
%  Qizhu Li <liqizhu@robots.ox.ac.uk>
%  August 2018
% ------------------------------------------------------------------------
% This file is part of the weakly-supervised training method presented in:
%    Qizhu Li*, Anurag Arnab*, Philip H.S. Torr,
%    "Weakly- and Semi-Supervised Panoptic Segmentation,"
%    European Conference on Computer Vision (ECCV) 2018.
% Please consider citing the paper if you use this code.
% ------------------------------------------------------------------------
% This function visualises the original prediction, original prediction
% scores, processed semantic iterative GT, and generated instance
% iterative GT.
%  INPUT:
%  - opts : options generated by get_opts.m
%  - results : results generated by the preceding code
%
%  DEMO:
%  - See scripts/run_sub.m
% ------------------------------------------------------------------------

function visualise_results_cam_mandg(opts, results)

if ~opts.visualise_results
    return;
end

f = figure('units','normalized','outerposition',[0 0 1 1], 'Visible', 'off');

ax(1) = subplot(2, 2, 1, 'replace');
imshow(results.pred, opts.cmap);
c(1) = colorbar(ax(1));
c(1).Limits = [0, 19];
c(1).Ticks = 0.5:1:18.5;
c(1).TickLabels = opts.objectNames;
c(1).FontSize = 8;
title(ax(1), '1. CAM prediction');

ax(2) = subplot(2, 2, 2, 'replace');
imshow(results.mandg_pred, results.mandg_cmap);
title(ax(2), '2. MCG&Grabcut Masks');

ax(3) = subplot(2, 2, 3, 'replace');
imshow(results.final_pred, opts.cmap);
c(3) = colorbar(ax(3));
c(3).Limits = [0, 19];
c(3).Ticks = 0.5:1:18.5;
c(3).TickLabels = opts.objectNames;
c(3).FontSize = 8;
title(ax(3), '3. Processed semantic GT');

ax(4) = subplot(2, 2, 4, 'replace');
imshow(results.ins_pred, opts.cmap);
c(4) = colorbar(ax(4));
num_instances = numel(results.ins_info);
c(4).Limits = [1, num_instances + 1];
c(4).Ticks = 1.5:1:(num_instances+0.5);
c(4).TickLabels = strcat(strsplit(num2str(extractfield(results.ins_info, 'ins_id'))), ...
    {': '}, extractfield(results.ins_info, 'name'));
c(4).FontSize = 8;
title(ax(4), '2. Generated instance GT');

f.Visible = 'on';

end


classdef SelectiveSearchSegmentation < handle
    %SELECTIVESEARCHSEGMENTATION  Selective search segmentation algorithm
    %
    % The class implements the algorithm described in [uijlings2013selective].
    %
    % ## References
    % [uijlings2013selective]:
    % > Jasper RR Uijlings, Koen EA van de Sande, Theo Gevers, and Arnold WM
    % > Smeulders. "Selective search for object recognition". International
    % > journal of computer vision, 104(2):154-171, 2013.
    %
    % See also: cv.SelectiveSearchSegmentation.process
    %

    properties (SetAccess = private)
        % Object ID
        id
    end

    %% Constructor/destructor
    methods
        function this = SelectiveSearchSegmentation()
            %SELECTIVESEARCHSEGMENTATION  Constructor
            %
            %     obj = cv.SelectiveSearchSegmentation()
            %
            % See also: cv.SelectiveSearchSegmentation.process
            %
            this.id = SelectiveSearchSegmentation_(0, 'new');
        end

        function delete(this)
            %DELETE  Destructor
            %
            %     obj.delete()
            %
            % See also: cv.SelectiveSearchSegmentation
            %
            if isempty(this.id), return; end
            SelectiveSearchSegmentation_(this.id, 'delete');
        end
    end

    %% Algorithm
    methods
        function clear(this)
            %CLEAR  Clears the algorithm state
            %
            %     obj.clear()
            %
            % See also: cv.SelectiveSearchSegmentation.empty,
            %  cv.SelectiveSearchSegmentation.load
            %
            SelectiveSearchSegmentation_(this.id, 'clear');
        end

        function b = empty(this)
            %EMPTY  Checks if detector object is empty
            %
            %     b = obj.empty()
            %
            % ## Output
            % * __b__ Returns true if the detector object is empty (e.g in the
            %   very beginning or after unsuccessful read).
            %
            % See also: cv.SelectiveSearchSegmentation.clear,
            %  cv.SelectiveSearchSegmentation.load
            %
            b = SelectiveSearchSegmentation_(this.id, 'empty');
        end

        function save(this, filename)
            %SAVE  Saves the algorithm parameters to a file
            %
            %     obj.save(filename)
            %
            % ## Input
            % * __filename__ Name of the file to save to.
            %
            % This method stores the algorithm parameters in the specified
            % XML or YAML file.
            %
            % See also: cv.SelectiveSearchSegmentation.load
            %
            SelectiveSearchSegmentation_(this.id, 'save', filename);
        end

        function load(this, fname_or_str, varargin)
            %LOAD  Loads algorithm from a file or a string
            %
            %     obj.load(fname)
            %     obj.load(str, 'FromString',true)
            %     obj.load(..., 'OptionName',optionValue, ...)
            %
            % ## Input
            % * __fname__ Name of the file to read.
            % * __str__ String containing the serialized model you want to
            %   load.
            %
            % ## Options
            % * __ObjName__ The optional name of the node to read (if empty,
            %   the first top-level node will be used). default empty
            % * __FromString__ Logical flag to indicate whether the input is a
            %   filename or a string containing the serialized model.
            %   default false
            %
            % This method reads algorithm parameters from the specified XML or
            % YAML file (either from disk or serialized string). The previous
            % algorithm state is discarded.
            %
            % See also: cv.SelectiveSearchSegmentation.save
            %
            SelectiveSearchSegmentation_(this.id, 'load', fname_or_str, varargin{:});
        end

        function name = getDefaultName(this)
            %GETDEFAULTNAME  Returns the algorithm string identifier
            %
            %     name = obj.getDefaultName()
            %
            % ## Output
            % * __name__ This string is used as top level XML/YML node tag
            %   when the object is saved to a file or string.
            %
            % See also: cv.SelectiveSearchSegmentation.save,
            %  cv.SelectiveSearchSegmentation.load
            %
            name = SelectiveSearchSegmentation_(this.id, 'getDefaultName');
        end
    end

    %% SelectiveSearchSegmentation
    methods
        function setBaseImage(this, img)
            %SETBASEIMAGE  Set a image used by switch functions to initialize the class
            %
            %     obj.setBaseImage(img)
            %
            % ## Input
            % * __img__ The image.
            %
            % See also: cv.SelectiveSearchSegmentation.process
            %
            SelectiveSearchSegmentation_(this.id, 'setBaseImage', img);
        end

        function switchToSingleStrategy(this, varargin)
            %SWITCHTOSINGLESTRATEGY  Initialize the class with the 'Single strategy' parameters
            %
            %     obj.switchToSingleStrategy()
            %     obj.switchToSingleStrategy('OptionName',optionValue, ...)
            %
            % ## Options
            % * __K__ The k parameter for the graph segmentation. default 200
            % * __Sigma__ The sigma parameter for the graph segmentation.
            %   default 0.8
            %
            % As described in [uijlings2013selective].
            %
            % See also: cv.SelectiveSearchSegmentation.switchToSelectiveSearchFast
            %
            SelectiveSearchSegmentation_(this.id, 'switchToSingleStrategy', varargin{:});
        end

        function switchToSelectiveSearchFast(this, varargin)
            %SWITCHTOSELECTIVESEARCHFAST  Initialize the class with the 'Selective search fast' parameters
            %
            %     obj.switchToSelectiveSearchFast()
            %     obj.switchToSelectiveSearchFast('OptionName',optionValue, ...)
            %
            % ## Options
            % * __BaseK__ The k parameter for the first graph segmentation.
            %   default 150
            % * __IncK__ The increment of the k parameter for all graph
            %   segmentations. default 150
            % * __Sigma__ The sigma parameter for the graph segmentation.
            %   default 0.8
            %
            % As described in [uijlings2013selective].
            %
            % See also: cv.SelectiveSearchSegmentation.switchToSelectiveSearchQuality
            %
            SelectiveSearchSegmentation_(this.id, 'switchToSelectiveSearchFast', varargin{:});
        end

        function switchToSelectiveSearchQuality(this, varargin)
            %SWITCHTOSELECTIVESEARCHQUALITY  Initialize the class with the 'Selective search fast' parameters
            %
            %     obj.switchToSelectiveSearchQuality()
            %     obj.switchToSelectiveSearchQuality('OptionName',optionValue, ...)
            %
            % ## Options
            % * __BaseK__ The k parameter for the first graph segmentation.
            %   default 150
            % * __IncK__ The increment of the k parameter for all graph
            %   segmentations. default 150
            % * __Sigma__ The sigma parameter for the graph segmentation.
            %   default 0.8
            %
            % As described in [uijlings2013selective].
            %
            % See also: cv.SelectiveSearchSegmentation.switchToSingleStrategy
            %
            SelectiveSearchSegmentation_(this.id, 'switchToSelectiveSearchQuality', varargin{:});
        end

        function addImage(this, img)
            %ADDIMAGE  Add a new image in the list of images to process
            %
            %     obj.addImage(img)
            %
            % ## Input
            % * __img__ The image.
            %
            % See also: cv.SelectiveSearchSegmentation.clearImages
            %
            SelectiveSearchSegmentation_(this.id, 'addImage', img);
        end

        function clearImages(this)
            %CLEARIMAGES  Clear the list of images to process
            %
            %     obj.clearImages()
            %
            % See also: cv.SelectiveSearchSegmentation.addImage
            %
            SelectiveSearchSegmentation_(this.id, 'clearImages');
        end

        function addGraphSegmentation(this, varargin)
            %ADDGRAPHSEGMENTATION  Add a new graph segmentation in the list of graph segementations to process
            %
            %     obj.addGraphSegmentation()
            %     obj.addGraphSegmentation('OptionName',optionValue, ...)
            %
            % ## Options
            % * __Sigma__ The sigma parameter, used to smooth image.
            %   default 0.5
            % * __K__ The k parameter of the algorithm. default 300
            % * __MinSize__ The minimum size of segments. default 100
            %
            % See also: cv.SelectiveSearchSegmentation.clearGraphSegmentations,
            %  cv.GraphSegmentation
            %
            SelectiveSearchSegmentation_(this.id, 'addGraphSegmentation', varargin{:});
        end

        function clearGraphSegmentations(this)
            %CLEARGRAPHSEGMENTATIONS  Clear the list of graph segmentations to process
            %
            %     obj.clearGraphSegmentations()
            %
            % See also: cv.SelectiveSearchSegmentation.addGraphSegmentation,
            %  cv.GraphSegmentation
            %
            SelectiveSearchSegmentation_(this.id, 'clearGraphSegmentations');
        end

        function addStrategy(this, stype, varargin)
            %ADDSTRATEGY  Add a new strategy in the list of strategy to process
            %
            %     obj.addStrategy(stype)
            %     obj.addStrategy('Multiple', stype, stype, ...)
            %
            % ## Input
            % * __stype__ The strategy type for the selective search
            %   segmentation algorithm, one of:
            %   * __Color__ Color-based strategy.
            %   * __Size__ Size-based strategy.
            %   * __Texture__ Texture-based strategy.
            %   * __Fill__ Fill-based strategy.
            %   * __Multiple__ Regroup multiple strategies, where all
            %     sub-strategies have equal weights.
            %
            % The classes are implemented from the algorithm described in
            % [uijlings2013selective].
            %
            % See also: cv.SelectiveSearchSegmentation.clearStrategies
            %
            SelectiveSearchSegmentation_(this.id, 'addStrategy', stype, varargin{:});
        end

        function clearStrategies(this)
            %CLEARSTRATEGIES  Clear the list of strategy to process
            %
            %     obj.clearStrategies()
            %
            % See also: cv.SelectiveSearchSegmentation.addStrategy
            %
            SelectiveSearchSegmentation_(this.id, 'clearStrategies');
        end

        function rects = process(this)
            %PROCESSIMAGE  Based on all images, graph segmentations and stragies, computes all possible rects and return them
            %
            %     rects = obj.process()
            %
            % ## Output
            % * __rects__ The list of rects as a Nx4 numeric matrix
            %   `[x,y,w,h; ...]`. The first ones are more relevents than the
            %   lasts ones.
            %
            % See also: cv.SelectiveSearchSegmentation.setBaseImage
            %
            rects = SelectiveSearchSegmentation_(this.id, 'process');
        end
    end

end


function vis_multicut(expidx, bRecompute, firstidx, nImgs, cidxs)

p = exp_params(expidx);
exp_dir = [p.expDir '/' p.shortName];
fprintf('exp_dir: %s\n',exp_dir);

resDir = [exp_dir '/multicut/'];
visDir = [exp_dir '/multicut/vis/'];

if (isfield(p,'testGTnopad'))
    load(p.testGTnopad,'annolist');
    bProject = true;
else
    load(p.testGT,'annolist');
    bProject = false;
end

if (nargin < 2)
    bRecompute = true;
end

if (nargin < 3)
    firstidx = 1;
end

if (nargin < 4)
    nImgs = length(annolist);
end

if (nargin < 5)
    cidxs = p.cidxs;
end

lastidx = firstidx + nImgs - 1;

colors = {'r','g','b','c','m','y'};
markers = {'+','o','s'}; %,'x','.','-','s','d'
scrsz = get(0,'ScreenSize');
figure('Position',[1 scrsz(4) scrsz(3)/2 scrsz(4)]);

for imgidx = firstidx:lastidx
    out_img_fname = [visDir '/imgidx_' padZeros(num2str(imgidx),4) '_cidx_1_14' '.png'];
    if (exist(out_img_fname, 'file') == 2) && ~bRecompute
        continue;
    end

    try
        templ = [resDir '/imgidx_' padZeros(num2str(imgidx),4) '_*'];
        files = dir(templ);
        fname = fullfile(resDir, files(end).name);
        %fname = [resDir '/imgidx_' padZeros(num2str(imgidx),4) '_cidx_' num2str(cidxs(1)) '_' num2str(cidxs(end))];
        load(fname,'unLab','unPos');
        fprintf('imgidx: %d\n',imgidx);
        clf;
        subplot(2,1,1);
        imgname = annolist(imgidx).image.name;
        img = imread(imgname);
        imagesc(img); axis equal; hold on;

        keypoints.imgname = imgname;
        keypoints.det = unPos;

        if (bProject)
            keypoints = projectKeypoints(keypoints,fileparts(p.testGTnopad),fileparts(p.testGT));
        end

        cb = keypoints.det;

        for j = 1:size(unLab,1)
            lp = unLab(j,1);
            cb_cur = cb(j,:);
            if (lp < 1000)
                lp = cidxs(lp+1)-1;
                if (lp <= 5)
                    plot(cb_cur(1),cb_cur(2),[colors{mod(lp,6)+1} markers{1}],'MarkerSize',5);
                elseif (lp > 5 && lp <= 11)
                    plot(cb_cur(1),cb_cur(2),[colors{mod(lp,6)+1} markers{2}],'MarkerSize',5);
                else
                    plot(cb_cur(1),cb_cur(2),[colors{mod(lp,6)+1} markers{3}],'MarkerSize',5);
                end
            else
                plot(cb_cur(1),cb_cur(2),'kx','MarkerSize',5);
            end
        end
        axis off;

        subplot(2,1,2);
        imagesc(img); axis equal; hold on;
        idxs = find(unLab(:,1) < 1000);
        lp_uniq = unique(unLab(idxs,2));
        for j = 1:length(idxs)
            lp = find(lp_uniq == unLab(idxs(j),2))-1;
            cb_cur = cb(idxs(j),:);
            if (lp <= 5)
                plot(cb_cur(1),cb_cur(2),[colors{mod(lp,6)+1} markers{1}],'MarkerSize',5);
            elseif (lp > 5 && lp <= 11)
                plot(cb_cur(1),cb_cur(2),[colors{mod(lp,6)+1} markers{2}],'MarkerSize',5);
            else
                plot(cb_cur(1),cb_cur(2),[colors{mod(lp,6)+1} markers{3}],'MarkerSize',5);
            end
        end
        axis off;
        print(gcf,'-dpng',out_img_fname);
    catch
    end
end

end


% mcmcTrainScript
% 5-fold cross-validation training

LOAD = 0;

ncv = 5;

imdir = '/IUS/vmr7/dhoiem/context/images/all_images';
outdir = '/IUS/vmr7/dhoiem/context/data';

if LOAD
    load([outdir '/rand_indices.mat']);
    load([outdir '/allimsegs2.mat']);
    load([outdir '/mcmcSuperpixelData.mat']);
    load([outdir '/mcmcSuperpixelClassifier.mat']);
    load([outdir '/mcmcEdgeData.mat']);
    load([outdir '/mcmcEdgeClassifier.mat']);
    load([outdir '/mcmcSegmentationClassifierCV.mat']);
    load([outdir '/mcmcGtSegmentData2.mat']);
    load([outdir '/mcmcSegmentClassifierCV2.mat']);
end

nimages = numel(imsegs);

if ~exist('spfeatures')
    spfeatures = mcmcGetAllSuperpixelData(imdir, imsegs);
    save([outdir '/mcmcSuperpixelData.mat'], 'spfeatures');
end

if ~exist('vclassifierSP')
    [vclassifierSP, hclassifierSP] = ...
        mcmcTrainSuperpixelClassifier(spfeatures(cluster_images), imsegs(cluster_images));
    save([outdir '/mcmcSuperpixelClassifier.mat'], 'vclassifierSP', 'hclassifierSP');
end

if ~exist('efeatures')
    [efeatures, adjlist] = mcmcGetAllEdgeData(spfeatures, imsegs);
    save([outdir '/mcmcEdgeData.mat'], 'efeatures', 'adjlist');
end

if ~exist('eclassifier')
    eclassifier = mcmcTrainEdgeClassifier(efeatures(cluster_images), ...
        adjlist(cluster_images), imsegs(cluster_images));
    save([outdir '/mcmcEdgeClassifier.mat'], 'eclassifier');
end

% if ~exist('gtmaps')
%     [gtmaps, gtlabels] = mcmcGetGroundTruthSegmentations(imsegs, adjlist);
%     gtsegfeatures = mcmcGetAllSegmentFeatures(imsegs, imdir, gtmaps, spfeatures, vclassifierSP, hclassifierSP);
%     save([outdir '/mcmcGtSegmentPerfectData.mat'], 'gtmaps', 'gtlabels', 'gtsegfeatures');
% end
%
% if ~exist('vclassifier')
%     for k = 1:ncv
%         trainind = setdiff([1:numel(imsegs)], ...
%             cv_images(floor((k-1)*numel(cv_images)/ncv)+1:floor(k*numel(cv_images)/ncv)));
%         [vclassifier(k), hclassifier(k)] = ...
%             mcmcTrainSegmentClassifier(imsegs(trainind), gtsegfeatures(trainind), gtlabels(trainind));
%     end
%     save([outdir '/mcmcSegmentClassifierPerfectCV.mat'], 'vclassifier', 'hclassifier');
% end

if ~exist('segclassifier')
    [segdata, seglabels, segimind] = mcmcGenerateRandomSegments3(imsegs(cv_images), imdir,  ...
        adjlist(cv_images), spfeatures(cv_images), efeatures(cv_images), vclassifierSP, hclassifierSP, ...
        eclassifier);

    for k = 1:ncv
        trainind = setdiff([1:numel(cv_images)], ...
            [floor((k-1)*numel(cv_images)/ncv)+1:floor(k*numel(cv_images)/ncv)]);
        segclassifier(k) = mcmcTrainSegmentationClassifier(segdata, seglabels, segimind, trainind);
    end
    save([outdir '/mcmcSegmentationClassifierCV.mat'], 'segclassifier', 'segdata', 'seglabels', 'segimind');
end

if ~exist('gtfeatures')
    [gtfeatures, gtlabels, gtlabelprobs, gtweights] = mcmcGetAllGoodSegments(...
        imsegs(cv_images), imdir, vclassifierSP, hclassifierSP, eclassifier, ...
        segclassifier, spfeatures(cv_images), adjlist(cv_images), efeatures(cv_images), ncv);
    save([outdir '/mcmcGtSegmentData2.mat'], 'gtfeatures', 'gtlabels', 'gtlabelprobs', 'gtweights');
end

if ~exist('vclassifier')
    for k = 1:ncv
        trainind = setdiff([1:numel(cv_images)], ...
            cv_images(floor((k-1)*numel(cv_images)/ncv)+1:floor(k*numel(cv_images)/ncv)));
        [vclassifier(k), hclassifier(k)] = ...
            mcmcTrainSegmentClassifier2(gtfeatures(trainind), gtlabels(trainind), gtweights(trainind));
    end
    save([outdir '/mcmcSegmentClassifierCV2.mat'], 'vclassifier', 'hclassifier');
end

% if ~exist('vregressor')
%     for k = 1:ncv
%         trainind = setdiff([1:numel(cv_images)], ...
%             cv_images(floor((k-1)*numel(cv_images)/ncv)+1:floor(k*numel(cv_images)/ncv)));
%         [vregressor(k), hregressor(k)] = ...
%             mcmcTrainSegmentRegressor(gtfeatures(trainind), gtlabelprobs(trainind), gtweights(trainind));
%     end
%     save([outdir '/mcmcSegmentRegressorCV.mat'], 'vregressor', 'hregressor');
% end


% ebcdic2ascii : Converts EBCDIC formatted text to ASCII formatted text
%
% CALL : ascii=ebcdic2ascii(ebcdic);
%
% ebcdic : Array on unsigned integers
% ascii  : Array on unsigned integers
%
% (C) 2002-2004, Thomas Mejer Hansen, tmh@gfy.ku.dk/thomas@cultpenguin.com
%

%
%    This program is free software; you can redistribute it and/or modify
%    it under the terms of the GNU General Public License as published by
%    the Free Software Foundation; either version 2 of the License, or
%    (at your option) any later version.
%
%    This program is distributed in the hope that it will be useful,
%    but WITHOUT ANY WARRANTY; without even the implied warranty of
%    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
%    GNU General Public License for more details.
%
%    You should have received a copy of the GNU General Public License
%    along with this program; if not, write to the Free Software
%    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
%
function ascii=ebcdic2ascii(ebcdic)

  ebcdic_arr=[
    '00';'01';'02';'03';'04';'05';'06';'07';'08';'09';
    '0A';'0B';'0C';'0D';'0E';'0F';'10';'11';'12';'13';
    '14';'15';'16';'17';'18';'19';'1A';'1B';'1C';'1D';
    '1E';'1F';'20';'21';'22';'23';'24';'25';'26';'27';
    '28';'29';'2A';'2B';'2C';'2D';'2E';'2F';'30';'31';
    '32';'33';'34';'35';'36';'37';'38';'39';'3A';'3B';
    '3C';'3D';'3E';'3F';'40';'4A';'4B';'4C';'4D';'4E';
    '4F';'50';'5A';'5B';'5C';'5D';'5E';'5F';'60';'61';
    '6A';'6B';'6C';'6D';'6E';'6F';'79';'7A';'7B';'7C';
    '7D';'7E';'7F';'81';'82';'83';'84';'85';'86';'87';
    '88';'89';'91';'92';'93';'94';'95';'96';'97';'98';
    '99';'A1';'A2';'A3';'A4';'A5';'A6';'A7';'A8';'A9';
    'C0';'C1';'C2';'C3';'C4';'C5';'C6';'C7';'C8';'C9';
    'D0';'D1';'D2';'D3';'D4';'D5';'D6';'D7';'D8';'D9';
    'E0';'E2';'E3';'E4';'E5';'E6';'E7';'E8';'E9';'F0';
    'F1';'F2';'F3';'F4';'F5';'F6';'F7';'F8';'F9';'FF';
    '00';'01';'02';'03';'37';'2D';'2E';'2F';'2F';'16';
    '05';'25';'0B';'0C';'0D';'10';'11';'12';'13';'3C';
    '3D';'32';'26';'18';'3F';'27';'1C';'1D';'1D';'1E';
    '1F';'07';'40';'5A';'7F';'7B';'5B';'6C';'50';'7D';
    '4D';'5D';'5C';'4E';'6B';'60';'4B';'61';'F0';'F1';
    'F2';'F3';'F4';'F5';'F6';'F7';'F8';'F9';'7A';'5E';
    '4C';'7E';'6E';'6F';'7C';'00';'E0';'00';'00';'6D';
    '79';'C0';'4F';'D0';'A1'];

ascii_arr=[
    '00';'01';'02';'03';'9C';'09';'86';'7F';'97';'8D';
    '8E';'0B';'0C';'0D';'0E';'0F';'10';'11';'12';'13';
    '9D';'85';'08';'87';'18';'19';'92';'8F';'1C';'1D';
    '1E';'1F';'80';'81';'82';'83';'84';'0A';'17';'1B';
    '88';'89';'8A';'8B';'8C';'05';'06';'07';'90';'91';
    '16';'93';'94';'95';'96';'04';'98';'99';'9A';'9B';
    '14';'15';'9E';'1A';'20';'A2';'2E';'3C';'28';'2B';
    '7C';'26';'21';'24';'2A';'29';'3B';'AC';'2D';'2F';
    'A6';'2C';'25';'5F';'3E';'3F';'60';'3A';'23';'40';
    '27';'3D';'22';'61';'62';'63';'64';'65';'66';'67';
    '68';'69';'6A';'6B';'6C';'6D';'6E';'6F';'70';'71';
    '72';'7E';'73';'74';'75';'76';'77';'78';'79';'7A';
    '7B';'41';'42';'43';'44';'45';'46';'47';'48';'49';
    '7D';'4A';'4B';'4C';'4D';'4E';'4F';'50';'51';'52';
    '5C';'53';'54';'55';'56';'57';'58';'59';'5A';'30';
    '31';'32';'33';'34';'35';'36';'37';'38';'39';'9F';
    '00';'01';'02';'03';'04';'05';'06';'07';'07';'08';
    '09';'0A';'0B';'0C';'0D';'10';'11';'12';'13';'14';
    '15';'16';'17';'18';'1A';'1B';'1C';'1D';'1D';'1E';
    '1F';'7F';'20';'21';'22';'23';'24';'25';'26';'27';
    '28';'29';'2A';'2B';'2C';'2D';'2E';'2F';'30';'31';
    '32';'33';'34';'35';'36';'37';'38';'39';'3A';'3B';
    '3C';'3D';'3E';'3F';'40';'5B';'5C';'5D';'5E';'5F';
    '60';'7B';'7C';'7D';'7E'
];

ascii_dec_array=hex2dec(ascii_arr);

ebcdic_dec_array=hex2dec(ebcdic_arr);

for i=1:length(ebcdic)
  m=find(ebcdic_dec_array==ebcdic(i));
  if length(m)>1,
    m=m(1);
  end
  if length(m)==0,
    ascii(i)=0;
  else
    ascii(i)=(ascii_dec_array(m));
  end
end


function result = quickif(condition,ontrue,onfalse)
% Select one of two possible values dependent on a condition
% Result = quickif(Condition,OnTrue,OnFalse)
%
% In:
%   Condition : conditional expression
%
%   OnTrue : value to return if condition evaluates to true
%
%   OnFalse : value to return if condition evaluates to false
%
% Out:
%   Result : either OnTrue or OnFalse
%
% Notes:
%   Note that both branches will always be evaluated, regardless of the value of Condition, since
%   quickif() is a regular function call. This is purely a convenience function that allows to use
%   conditional expressions inside other expressions.
%
%                                Christian Kothe, Swartz Center for Computational Neuroscience, UCSD
%                                2013-10-16

% Copyright (C) Christian Kothe, SCCN, 2013, christian@sccn.ucsd.edu
%
% 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

if condition
    result = ontrue;
else
    result = onfalse;
end