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:
% S. Mallat. A Wavelet Tour of Signal Processing, Third Edition: The
% Sparse Way. Academic Press, 3rd edition, 2008.
%
%
% Url: http://ltfat.github.io/doc/wavelets/fwtinit.html
% Copyright (C) 2005-2023 Peter L. Soendergaard <peter@sonderport.dk> and others.
% This file is part of LTFAT version 2.6.0
%
% This program is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% This program is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with this program. If not, see <http://www.gnu.org/licenses/>.
% **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