1function [dualwt,info] = dtwfbinit(dualwtdef,varargin)
2%-*- texinfo -*-
3%@deftypefn {Function} dtwfbinit
4%@verbatim
5%DTWFBINIT Dual-Tree Wavelet Filterbank Initialization
6%   Usage:  dualwt=dtwfbinit(dualwtdef);
7%
8%   Input parameters:
9%         dualwtdef : Dual-tree filterbank definition.
10%
11%   Output parameters:
12%         dualwt    : Dual-tree filtarbank structure.
13%
14%   dtwfinit() (a call without aguments) creates an empty structure. It
15%   has the same fields as the struct. returned from WFBTINIT plus a field
16%   to hold nodes from the second tree:
17%
18%      .nodes      Filterbank nodes of the first tree
19%
20%      .dualnodes  Filterbank nodes of the second tree
21%
22%      .children   Indexes of children nodes
23%
24%      .parents    Indexes of a parent node
25%
26%      .forder     Frequency ordering of the resultant frequency bands.
27%
28%   dtwfinit({dualw,J,flag}) creates a structure representing a dual-tree
29%   wavelet filterbank of depth J, using dual-tree wavelet filters
30%   specified by dualw. The shape of the tree is controlled by flag.
31%   Please see help on DTWFB or DTWFBREAL for description of the
32%   parameters.
33%
34%   [dualwt,info]=dtwfinit(...) additionally returns info struct which
35%   provides some information about the computed window:
36%
37%   info.tight
38%      True if the overall tree construct a tight frame.
39%
40%   info.dw
41%      A structure containing basic dual-tree filters as returned from
42%      fwtinit(dualwtdef,'wfiltdt_').
43%
44%   Additional optional flags
45%   -------------------------
46%
47%   'freq','nat'
48%      Frequency or natural ordering of the resulting coefficient subbands.
49%      Default ordering is 'freq'.
50%@end verbatim
51%@strong{Url}: @url{http://ltfat.github.io/doc/wavelets/dtwfbinit.html}
52%@end deftypefn
53
54% Copyright (C) 2005-2016 Peter L. Soendergaard <peter@sonderport.dk>.
55% This file is part of LTFAT version 2.3.1
56%
57% This program is free software: you can redistribute it and/or modify
58% it under the terms of the GNU General Public License as published by
59% the Free Software Foundation, either version 3 of the License, or
60% (at your option) any later version.
61%
62% This program is distributed in the hope that it will be useful,
63% but WITHOUT ANY WARRANTY; without even the implied warranty of
64% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
65% GNU General Public License for more details.
66%
67% You should have received a copy of the GNU General Public License
68% along with this program.  If not, see <http://www.gnu.org/licenses/>.
69
70
71% Output structure definition.
72% The structure has the same fields as returned by wfbtinit
73% but contains additional field .dualnodes containing
74% filters of the dual tree
75%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
76dualwt = wfbtinit();
77dualwt.dualnodes = {};
78%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79info.istight = 0;
80
81if nargin < 1
82    return;
83end
84
85% Frequency or natural ordering
86definput.import = {'wfbtcommon'};
87[flags,kv]=ltfatarghelper({},definput,varargin);
88
89% Strict throws an error if filterbank is not tight and ana: or syn:
90% is not specified.
91do_strict = 0;
92% Dual returns the 'other' filterbank.
93do_dual = 0;
94
95% Check 'strict'
96if iscell(dualwtdef) && ischar(dualwtdef{1}) && strcmpi(dualwtdef{1},'strict')
97   do_strict = 1;
98   dualwtdef = dualwtdef{2:end};
99end
100% Check 'dual'
101if iscell(dualwtdef) && ischar(dualwtdef{1}) && strcmpi(dualwtdef{1},'dual')
102   do_dual = 1;
103   dualwtdef = dualwtdef{2:end};
104end
105
106% FIRST, check if dtwdef is already a struct
107if isstruct(dualwtdef)
108   if isfield(dualwtdef,'nodes') && isfield(dualwtdef,'dualnodes')
109      dualwt = dualwtdef;
110
111       if do_dual || do_strict
112          nodesArg = dualwt.nodes;
113
114          % Undo the frequency ordering
115          if dualwt.freqOrder
116             dualwt = nat2freqOrder(dualwt,'rev');
117          end
118
119          doDualTreeFilt = cellfun(@(nEl) strcmp(nEl.wprefix,'wfiltdt_'),...
120                                   nodesArg);
121
122          if do_dual
123             nodesArg = cellfun(@(nEl) {'dual',nEl},nodesArg,...
124                                'UniformOutput',0);
125          end
126          if do_strict
127             nodesArg = cellfun(@(nEl) {'strict',nEl},nodesArg,...
128                                'UniformOutput',0);
129
130          end
131          info.istight = 1;
132          dualwt.nodes = {};
133          dualwt.dualnodes = {};
134
135
136          rangeRest = 1:numel(nodesArg);
137          rangeRest(dualwt.parents==0) = [];
138
139          for ii=rangeRest
140              if doDualTreeFilt(ii)
141                % This is dual-tree specific filterbank
142                [dualwt.nodes{ii},infotmp] = fwtinit(nodesArg{ii},'wfiltdt_');
143                dualwt.dualnodes{ii} = dualwt.nodes{ii};
144                dualwt.nodes{ii}.h(:,2) = [];
145                dualwt.nodes{ii}.g(:,2) = [];
146                dualwt.dualnodes{ii}.h(:,1) = [];
147                dualwt.dualnodes{ii}.g(:,1) = [];
148              else
149                [dualwt.nodes{ii},infotmp] = fwtinit(nodesArg{ii});
150                dualwt.dualnodes{ii} = dualwt.nodes{ii};
151              end
152              info.istight = info.istight && infotmp.istight;
153          end
154
155          % Treat root separately
156          [rootNode,infotmp] = fwtinit(nodesArg{dualwt.parents==0});
157          dualwt = replaceRoots(dualwt,rootNode);
158          info.istight = info.istight && infotmp.istight;
159
160          % Do the filter frequency shuffling again, since the filters were
161          % overwritten in fwtinit.
162          if dualwt.freqOrder
163             dualwt = nat2freqOrder(dualwt);
164          end
165       end
166
167       % Do filter shuffling if flags.do_freq differs from the wt.freqOrder.
168       % Frequency and natural oreding coincide for DWT.
169       if dualwt.freqOrder && ~flags.do_freq
170          dualwt = nat2freqOrder(dualwt,'rev');
171          dualwt.freqOrder = ~dualwt.freqOrder;
172       elseif ~dualwt.freqOrder && flags.do_freq
173          dualwt = nat2freqOrder(dualwt);
174          dualwt.freqOrder = ~dualwt.freqOrder;
175       end
176   else
177       error('%s: Invalid dual-tree structure format.',upper(mfilename));
178   end
179   return;
180end
181
182% Parse the other params
183% Tree type
184definput.flags.treetype = {'dwt','full','doubleband','quadband',...
185                           'octaband','root'};
186% First stage filterbank
187definput.keyvals.first = [];
188% Leaf filterbank
189definput.keyvals.leaf = [];
190% Depth of the tree
191definput.keyvals.J = [];
192wdef = dualwtdef{1};
193[flags2,kv2,J]=ltfatarghelper({'J'},definput,dualwtdef(2:end));
194complainif_notposint(J,'J');
195
196% Now dtwdef is this {dtw,J,flag,'first',w}
197if do_dual
198   wdef = {'dual',wdef};
199end
200if do_strict
201   wdef = {'strict',wdef};
202end
203
204if ~(ischar(wdef) || iscell(wdef))
205    error('%s: Unrecognized format of dual-tree filters.',upper(mfilename));
206end
207
208% Get the dual-tree filters
209[w, dtinfo] = fwtinit(wdef,'wfiltdt_');
210
211info.istight = dtinfo.istight;
212info.dw = w;
213
214% Determine the first-stage wavelet filters
215if ~isfield(dtinfo,'defaultfirst') && isempty(kv2.first)
216    error('%s: No first stage wavelet filters specified.',upper(mfilename));
217end
218
219
220if ~isempty(kv2.first)
221   if do_dual
222      kv2.first = {'dual',kv2.first};
223   end
224   if do_strict
225      kv2.first = {'strict',kv2.first};
226   end
227
228   [kv2.first, firstinfo] = fwtinit(kv2.first);
229   isfirsttight = firstinfo.istight;
230else
231   kv2.first = dtinfo.defaultfirst;
232   isfirsttight = dtinfo.defaultfirstinfo.istight;
233end
234
235
236
237isleaftight = [];
238if ~(flags2.do_dwt || flags2.do_root)
239    % Determine leaf nodes (only valid for wavelet packets)
240    if ~isfield(dtinfo,'defaultleaf') && isempty(kv2.leaf)
241        error('%s: No leaf wavelet filters specified.',...
242              upper(mfilename));
243    else
244       if isempty(kv2.leaf)
245          kv2.leaf = dtinfo.defaultleaf;
246          isleaftight = dtinfo.defaultleafinfo.istight;
247       else
248          if do_dual
249             kv2.leaf = {'dual',kv2.leaf};
250          end
251          if do_strict
252             kv2.leaf = {'strict',kv2.leaf};
253          end
254          [kv2.leaf, leafinfo] = fwtinit(kv2.leaf);
255          isleaftight = leafinfo.istight;
256       end
257    end
258end
259
260
261% Extract filters for dual trees
262% This is a bit clumsy...
263w1 = w;
264w1.h = w1.h(:,1);
265w1.g = w1.g(:,1);
266
267w2 = w;
268w2.h = w2.h(:,2);
269w2.g = w2.g(:,2);
270
271
272% Initialize both trees
273dualwt  = wfbtinit({w1,J,flags2.treetype}, 'nat');
274dtw2 = wfbtinit({w2,J,flags2.treetype}, 'nat');
275% Merge tree definitions to a single struct.
276dualwt.dualnodes = dtw2.nodes;
277dualwt = replaceRoots(dualwt,kv2.first);
278
279
280% Replace the 'packet leaf nodes' (see Bayram)
281if ~(flags2.do_dwt || flags2.do_root)
282    filtNo = numel(w1.g);
283    if flags2.do_doubleband
284       for jj=1:J-1
285         dualwt = wfbtput(2*(jj+1)-1,1:filtNo-1,kv2.leaf,dualwt,'force');
286       end
287    elseif flags2.do_quadband
288       idx = 1:filtNo-1;
289       idx = [idx,idx+filtNo];
290       dualwt = wfbtput(2,idx,kv2.leaf,dualwt,'force');
291       for jj=1:J-1
292         dualwt = wfbtput(3*(jj+1)-2,1:filtNo-1,kv2.leaf,dualwt,'force');
293         dualwt = wfbtput(3*(jj+1)-1,1:2*filtNo-1,kv2.leaf,dualwt,'force');
294       end
295    elseif flags2.do_octaband
296       idx = 1:filtNo-1;idx = [idx,idx+filtNo];
297       dualwt = wfbtput(2,idx,kv2.leaf,dualwt,'force');
298       idx = 1:2*filtNo-1;idx = [idx,idx+2*filtNo];
299       dualwt = wfbtput(3,idx,kv2.leaf,dualwt,'force');
300       for jj=1:J-1
301         dualwt = wfbtput(4*(jj+1)-3,1:filtNo-1,kv2.leaf,dualwt,'force');
302         dualwt = wfbtput(4*(jj+1)-2,1:2*filtNo-1,kv2.leaf,dualwt,'force');
303         dualwt = wfbtput(4*(jj+1)-1,1:4*filtNo-1,kv2.leaf,dualwt,'force');
304       end
305    elseif flags2.do_full
306       for jj=2:J-1
307          idx = 1:filtNo^jj-1;
308          idx(filtNo^(jj-1))=[];
309          dualwt = wfbtput(jj,idx,kv2.leaf,dualwt,'force');
310       end
311    else
312        error('%s: Something is seriously wrong!',upper(mfilename));
313    end
314end
315
316
317% Do filter shuffling if frequency ordering is required,
318dualwt.freqOrder = flags.do_freq;
319if flags.do_freq
320   dualwt = nat2freqOrder(dualwt);
321end
322
323%
324info.istight = isfirsttight && info.istight;
325if ~isempty(isleaftight)
326   info.istight = info.istight && isleaftight;
327end
328
329
330function dtw = replaceRoots(dtw,rootNode)
331% Replace the root nodes
332
333firstTmp = rootNode;
334firstTmp.h = cellfun(@(hEl) setfield(hEl,'offset',hEl.offset+1),...
335                        firstTmp.h,'UniformOutput',0);
336firstTmp.g = cellfun(@(gEl) setfield(gEl,'offset',gEl.offset+1),...
337                        firstTmp.g,'UniformOutput',0);
338
339% First tree root
340dtw.nodes{dtw.parents==0} = rootNode;
341% Second tree root (shifted by 1 sample)
342dtw.dualnodes{dtw.parents==0} = firstTmp;
343
344
345
346
347