function [w,info] = fwtinit(wdef,prefix)
%FWTINIT Wavelet Filterbank Structure Initialization
% Usage: w = fwtinit(wdef);
% w = fwtinit(wdef,prefix);
% [w,info]=fwtinit(...)
%
% Input parameters:
% wdef : Wavelet filters specification.
% prefix : Function name prefix
%
% Output parameters:
% w : Structure defining the filterbank.
%
% `fwtinit(wdef)` produces a structure describing the analysis
% (field `w.h`) and synthesis (field `w.g`) filterbanks and a hop factors
% (field `w.a`) of a basic wavelet-type filterbank defined by `wdef`.
%
% The analysis filterbank `w.h` is by default used in |fwt| and the
% synthesis filterbank `w.g` in |ifwt|.
%
% Both `w.h` and `w.g` are cell arrays of structs defining FIR filters
% compatible with |filterbank|, |ifilterbank| and related functions.
% More preciselly, each elemement of either cell array is a struct with
% fields `.h` and `.offset` defining impulse response and the initial
% shift respectivelly.
%
% `[w,info]=fwtinit(...)` additionally returns a `info` struct which
% provides some information about the wavelet filterbank:
%
% `info.istight`
% Wavelet filterbank forms a tight frame. In such case, `w.h` and
% `w.g` are identical.
%
% The function is a wrapper for calling all the functions with the
% `wfilt_` prefix defined in the LTFAT wavelets directory.
%
% The possible formats of the `wdef` are the following:
%
% 1) Cell array with first element being the name of the function defining
% the basic wavelet filters (`wfilt_` prefix) and the other elements
% are the parameters of the function.
%
% 2) Character string as concatenation of the name of the wavelet
% filters defining function (as above) and the numeric parameters
% delimited by ':' character. Examples:
%
% `{'db',10}` or `'db10'`
% Daubechies with 10 vanishing moments. It calls `wfilt_db(10)`
% internally.
%
% `{'spline',4,4}` or `'spline4:4'`
% Biorthogonal spline wavelet filters with 4 vanishing moments.
% Calls `wfilt_spline(4,4)` internally.
%
% `{'dden',1}` or `'dden1'`
% Double density wavelet filters. Calls `wfilt_dden(1)` where
% the filters are stored as numerical vectors.
%
% 3) Cell array of one dimensional numerical vectors directly defining
% the wavelet filter impulse responses. By default, outputs of the
% filters are subsampled by a factor equal to the number of the
% filters. Pass additional key-value pair 'a',a (still inside of the
% cell array) to define the custom subsampling factors, e.g.:
% {h1,h2,'a',[2,2]}.
%
% 4) The fourth option is to pass again the structure obtained from the
% |fwtinit| function. The structure is checked whether it has a valid
% format.
%
% 5) Two element cell array. First element is the string `'dual'` and the
% second one is in format 1), 2) or 4). This returns a dual of whatever
% is passed as the second argument.
%
% 6) Two element cell array. First element is the string `'strict'` and the
% second one is in format 1), 2), 4) or 5). This in the non tight case
% the filters has to be defined explicitly using `'ana'` and `'syn'`
% identifiers. See below.
%
% 7) Two element cell array. First element is a cell array of structures
% defining FIR filterbank (.h and .offset fields) as in |filterbankwin|
% and the second element is a numeric vector of subsampling factors.
%
% One can interchange the filter in `w.h` and `w.g` and use the
% filterbank indended for synthesis in |fwt| and vice versa by
% re-using the items 1) and 2) in the following way:
%
% 1) Add `'ana'` or `'syn'` as the first element in the cell array e.g.
% `{'ana','spline',4,4}` or `{'syn','spline',4,4}`.
%
% 2) Add `'ana:'` or `'syn:'` to the beginning of the string e.g.
% `'ana:spline4:4'` or `'syn:spline4:4'`.
%
% This only makes difference if the filterbanks are biorthogonal
% (e.g. `wfilt_spline`) or a general frame (e.g. `'symds2'`), in other
% cases, the analysis and synthesis filters are identical.
%
% Please note that using e.g. `c=fwt(f,'ana:spline4:4',J)` and
% `fhat=ifwt(c,'ana:spline4:4',J,size(f,1))` will not give a perfect
% reconstruction.
%
% The output structure has the following additional field:
%
% w.origArgs
% Original parameters in format 1).
%
% See also: fwt, ifwt, wfilt_db
%
% References: ma08wt
% **Remark:** Function names with the `wfilt_` prefix cannot contain numbers
% and cannot start with 'ana' or 'syn'!
% wavelet filters functions definition prefix
wprefix = 'wfilt_';
waveletsDir = 'wavelets';
% output structure definition
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
w.origArgs = {};
w.wprefix = wprefix;
w.h = {};
w.g = {};
w.a = [];
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
info.istight = 0;
% return empty struct if no argument was passed
if nargin<1
return;
end;
if isempty(wdef)
error('%s: Input argument is empty.',upper(mfilename));
end
if nargin>1
if ischar(prefix) && ~isempty(prefix)
wprefix = prefix;
w.wprefix = wprefix;
else
error('%s: Bad format of prefix.',upper(mfilename));
end
end
do_strict = 0;
do_dual = 0;
% Check 'strict'
if iscell(wdef) && ischar(wdef{1}) && strcmpi(wdef{1},'strict')
do_strict = 1;
wdef = wdef{2:end};
end
if iscell(wdef) && ischar(wdef{1}) && strcmpi(wdef{1},'dual')
do_dual = 1;
wdef = wdef{2:end};
end
if isstruct(wdef)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Process wdef in format 4)%
% Do checks and return quicky %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Check the fields
if isequal(fieldnames(wdef),fieldnames(w))
if ~do_dual && ~do_strict
w = wdef;
%cachw = w;
return;
else
if ~isempty(wdef.origArgs)
wdef = wdef.origArgs;
else
error('%s: The structure was not built using compatible formats.',upper(mfilename));
end
end
else
error('%s: Passed structure has different fields.',upper(mfilename));
end
end
if iscell(wdef)
wname = wdef;
if ~ischar(wname{1})
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Process wdef in format 3)%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if isnumeric(wname{1})
complainDual(do_dual,'numeric cell array');
equalsa = cellfun(@(wEl)strcmp(wEl,'a'),wname);
apos = find(equalsa==1);
if isempty(apos)
apos = numel(wname)+1;
w.a = ones(numel(wname),1)*numel(wname);
else
if apos==numel(wname)-1 && isnumeric(wname{apos+1}) && numel(wname{apos+1})==apos-1
w.a = wname{apos+1};
else
error('%s: Key ''a'' have to be followed by a vector of length %i.',upper(mfilename),apos-1);
end
end
w.h = formatFilters(wname(1:apos-1),[]);
w.g = formatFilters(wname(1:apos-1),[]);
w.origArgs = wname;
elseif iscell(wname{1}) && numel(wname)==2 && numel(wname{1})>1
complainDual(do_dual,'filterbank cell array');
g = wname{1};
a = wname{2};
[g,asan,infotmp]=filterbankwin(g,a,'normal');
if ~infotmp.isfir
error('%s: Only FIR filters are supported.',upper(mfilename));
end
w.h = g;
w.g = g;
w.a = asan(:,1);
w.origArgs = wname;
else
error('%s: Unrecognizer format of the filterbank definition.',upper(mfilename));
end
%cachw = w;
return;
end
elseif ischar(wdef)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Process wdef in format 2)%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
try
wname = parseNameValPair(wdef,wprefix);
% Octave does not support the "catch err" stament, so use "lasterror"
% instead
%catch err
catch
err=lasterror;
% If failed, clean the cache.
cachwDesc = [];
cachw = [];
error(err.message);
end
else
error('%s: First argument must be a string, cell or struct.',upper(mfilename));
end;
do_forceAna = [];
is_tight = 0;
% Check whether wavelet definition starts with ana or syn
if ischar(wname{1}) && numel(wname{1})==3
if strcmpi(wname{1},'ana') || strcmpi(wname{1},'syn')
% Set field only if ana or syn was explicitly specified.
do_forceAna = strcmpi(wname{1},'ana');
wname = wname(2:end);
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% wname now contains wdef in format 1)%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Search for m-file containing string wname
wfiltFile = dir(fullfile(ltfatbasepath,sprintf('%s/%s%s.m',waveletsDir,wprefix,lower(wname{1}))));
if(isempty(wfiltFile))
error('%s: Unknown wavelet type: %s',upper(mfilename),wname{1});
else
% if found, crop '.m' from the filename
tmpFile = wfiltFile.name(1:end-2);
end
% There is a bug in nargout in version 3.6 of Octave, but not in later
% stable versions
if isoctave
octs=strsplit(version,'.');
octN=str2num(octs{1})*1000+str2num(octs{2});
if octN<3008
try
feval(tmpFile);
catch
end;
end;
end;
wfiltNargout = nargout(tmpFile);
if(nargin(tmpFile)~=numel(wname)-1)
error('%s: Incorrect number of parameters to be passed to the %s func.',upper(mfilename),tmpFile);
end
if(wfiltNargout==3)
[w.h, w.g, w.a] = feval(tmpFile,wname{2:end});
elseif(wfiltNargout==4)
[w.h, w.g, w.a, info] = feval(tmpFile,wname{2:end});
else
error('%s: Function %s does not return 3 or 4 arguments.',upper(mfilename),upper(tmpFile));
end
if ~isempty(info)&&isfield(info,'istight')
is_tight = info.istight;
else
info.istight = 0;
end
% d = [];
% if isfield(info,'d')
% d = info.d;
% end
%
% if numel(tmph)~=numel(w.a) || numel(tmpg)~=numel(w.a)
% error('%s: Variables returned by %s have different element counts.',upper(mfilename),upper(tmpFile));
% end
if ~is_tight && do_strict && isempty(do_forceAna)
error(['%s: %s filters does not form a tight frame. Choose either ',...
'''ana:%s'' or ''syn:%s'' '],upper(mfilename),tmpFile,...
wcell2str(wname),wcell2str(wname));
end
% w.h = formatFilters(tmph,d);
% w.g = formatFilters(tmpg,d);
w.origArgs = wname;
if ~isempty(do_forceAna)
if do_dual
do_forceAna = ~do_forceAna;
end
if do_forceAna
w.g = w.h;
w.origArgs = [{'ana'}, w.origArgs];
% Hande the Dual-tree specific stuff
if ~isempty(info) && isfield(info,'defaultfirst')
info.defaultfirst.g = info.defaultfirst.h;
info.defaultfirst.origArgs = [{'ana'},info.defaultfirst.origArgs];
end
if ~isempty(info) && isfield(info,'defaultleaf')
info.defaultleaf.g = info.defaultleaf.h;
info.defaultleaf.origArgs = [{'ana'},info.defaultleaf.origArgs];
end
else
w.h = w.g;
w.origArgs = [{'syn'}, w.origArgs];
% Hande the Dual-tree specific stuff
if ~isempty(info) && isfield(info,'defaultfirst')
info.defaultfirst.h = info.defaultfirst.g;
info.defaultfirst.origArgs = [{'syn'},info.defaultfirst.origArgs];
end
if ~isempty(info) && isfield(info,'defaultleaf')
info.defaultleaf.h = info.defaultleaf.g;
info.defaultleaf.origArgs = [{'syn'},info.defaultleaf.origArgs];
end
end
end
end %END FWTINIT
function filts = formatFilters(cellf,d)
noFilts = numel(cellf);
filts = cell(noFilts,1);
if(isempty(d))
d = findFiltDelays(cellf,'half');
end
for ff=1:noFilts
%filts{ff} = wfiltstruct('FIR');
filts{ff}.h = cellf{ff}(:);
filts{ff}.offset = -d(ff);
end
end %END FORMATFILTERS
function wcell = parseNameValPair(wchar,wprefix)
%PARSENAMEVALPAIR
%Parses string in the following format wnameN1:N2... , where wname have to
%be name of the existing function with wfilt_ prefix. N1,N2,... are doubles
%delimited by character ':'.
%The output is cell array {wname,str2double(N1),str2double(N2),...}
%The wfilt_ function name cannot contain numbers
wcell = {};
numDelimiter = ':';
% Check whether the first 4 characters are 'ana:' or 'syn:'
if numel(wchar)>4
if strcmpi(wchar(1:4),'ana:')
wcell = [wcell,{'ana'}];
wchar = wchar(5:end);
elseif strcmpi(wchar(1:4),'syn:')
wcell = [wcell,{'syn'}];
wchar = wchar(5:end);
end
end
% Take out all numbers from the string
wcharNoNum = wchar(1:find(isstrprop(wchar,'digit')~=0,1)-1);
% List all files satysfying the following: [ltfatbase]/wavelets/wfilt_*.m?
wfiltFiles = dir(fullfile(ltfatbasepath,sprintf('%s/%s*.m','wavelets',wprefix)));
% Get just the filanames without the wfilt_ prefix
wfiltNames = arrayfun(@(fEl) fEl.name(1+find(fEl.name=='_',1):find(fEl.name=='.',1,'last')-1),wfiltFiles,'UniformOutput',0);
% Compare the filenames with a given string
wcharMatch = cellfun(@(nEl) strcmpi(wcharNoNum,nEl),wfiltNames);
% Find index(es) of the matches.
wcharMatchIdx = find(wcharMatch~=0);
% Handle faulty results.
if(isempty(wcharMatchIdx))
dirListStr = cell2mat(cellfun(@(wEl) sprintf('%s, ',wEl), wfiltNames(:)','UniformOutput',0));
if ~all(cellfun(@isempty,wfiltNames))
error('%s: Unknown wavelet filter definition string: %s.\nAccepted are:\n%s',upper(mfilename),wcharNoNum,dirListStr(1:end-2));
else
error('%s: Cannot find %s%s',upper(mfilename),wprefix,wcharNoNum);
end
end
if(numel(wcharMatchIdx)>1)
error('%s: Ambiguous wavelet filter definition string. Probably bug somewhere.',upper(mfilename));
end
match = wfiltNames{wcharMatchIdx};
wcell = [wcell,{match}];
% Extract the numerical parameters from the string (delimited by :)
numString = wchar(numel(match)+1:end);
if(isempty(numString))
error('%s: No numeric parameter specified in %s.',upper(mfilename),wchar);
end
% Parse the numbers.
wcharNum = textscan(numString,'%f','Delimiter',numDelimiter);
if(~isnumeric(wcharNum{1})||any(isnan(wcharNum{1})))
error('%s: Incorrect numeric part of the wavelet filter definition string.',upper(mfilename));
end
wcell = [wcell, num2cell(wcharNum{1}).'];
end %END PARSENAMEVALPAIR
function d = findFiltDelays(cellh,type)
filtNo = numel(cellh);
d = ones(filtNo,1);
for ff=1:filtNo
if(strcmp(type,'half'))
d(ff) = floor((length(cellh{ff})+1)/2);
% elseif(strcmp(type,'energycent'))
% tmphh =cellh{ff};
% tmphLen = length(tmphh);
% ecent = sum((1:tmphLen-1).*tmphh(2:end).^2)/sum(tmphh.^2);
% if(do_ana)
% d(ff) = round(ecent)+1;
% if(rem(abs(d(ff)-d(1)),2)~=0)
% d(ff)=d(ff)+1;
% end
% else
% anad = round(ecent)+1;
% d(ff) = tmphLen-anad;
% if(rem(abs(d(ff)-d(1)),2)~=0)
% d(ff)=d(ff)-1;
% end
% end
else
error('TO DO: Unsupported type.');
end
end
end %END FINDFILTDELAYS
function complainDual(dual,whereStr)
if dual
error('%s: ''dual'' option not allowed for the %s input.',upper(mfilename),whereStr);
end
end % END COMPLAINA
function str = wcell2str(wcell)
strNums = cellfun(@(wEl) [num2str(wEl),':'],wcell(2:end),'UniformOutput',0);
strNums = cell2mat(strNums);
str = [wcell{1},strNums(1:end-1)];
end