pySLFilters module
This module contains all neccessary files to compute the filters used in the pyShearLab2D toolbox. Most of these files are taken from different MATLAB toolboxes and were translated to Python. Credit is given in each individual function.
""" This module contains all neccessary files to compute the filters used in the pyShearLab2D toolbox. Most of these files are taken from different MATLAB toolboxes and were translated to Python. Credit is given in each individual function. Stefan Loock, February 2, 2017 [sloock@gwdg.de] """ import numpy as np from scipy import signal as signal def MakeONFilter(Type,Par=1): """ This is a rewrite of the original Matlab implementation of MakeONFilter.m from the WaveLab850 toolbox. MakeONFilter -- Generate Orthonormal QMF Filter for Wavelet Transform Usage: qmf = MakeONFilter(Type, Par) Inputs: Type: string: 'Haar', 'Beylkin', 'Coiflet', 'Daubechies', 'Symmlet', 'Vaidyanathan', 'Battle' Outputs: qmf: quadrature mirror filter Description The Haar filter (which could be considered a Daubechies-2) was the first wavelet, though not called as such, and is discontinuous. The Beylkin filter places roots for the frequency response function close to the Nyquist frequency on the real axis. The Coiflet filters are designed to give both the mother and father wavelets 2*Par vanishing moments; here Par may be one of 1,2,3,4 or 5. The Daubechies filters are minimal phase filters that generate wavelets which have a minimal support for a given number of vanishing moments. They are indexed by their length, Par, which may be one of 4,6,8,10,12,14,16,18 or 20. The number of vanishing moments is par/2. Symmlets are also wavelets within a minimum size support for a given number of vanishing moments, but they are as symmetrical as possible, as opposed to the Daubechies filters which are highly asymmetrical. They are indexed by Par, which specifies the number of vanishing moments and is equal to half the size of the support. It ranges from 4 to 10. The Vaidyanathan filter gives an exact reconstruction, but does not satisfy any moment condition. The filter has been optimized for speech coding. The Battle-Lemarie filter generate spline orthogonal wavelet basis. The parameter Par gives the degree of the spline. The number of vanishing moments is Par+1. See Also: FWT_PO, IWT_PO, FWT2_PO, IWT2_PO, WPAnalysis References: The books by Daubechies and Wickerhauser. Part of WaveLab850 (http://www-stat.stanford.edu/~wavelab/) """ if Type == 'Haar': onFilter = np.array([1/np.sqrt(2), 1/np.sqrt(2)]) if Type == 'Beylkin': onFilter = np.array([.099305765374, .424215360813, .699825214057, .449718251149, -.110927598348, -.264497231446, .026900308804, .155538731877, -.017520746267, -.088543630623, .019679866044, .042916387274, -.017460408696, -.014365807969, .010040411845, .001484234782, -.002736031626, .000640485329]) if Type == 'Coiflet': if Par == 1: onFilter = np.array([.038580777748, -.126969125396, -.077161555496, .607491641386, .745687558934, .226584265197]) elif Par == 2: onFilter = np.array([.016387336463, -.041464936782, -.067372554722, .386110066823, .812723635450, .417005184424, -.076488599078, -.059434418646, .023680171947, .005611434819, -.001823208871, -.000720549445]) elif Par == 3: onFilter = np.array([-.003793512864, .007782596426, .023452696142, -.065771911281, -.061123390003, .405176902410, .793777222626, .428483476378, -.071799821619, -.082301927106, .034555027573, .015880544864, -.009007976137, -.002574517688, .001117518771, .000466216960, -.000070983303, -.000034599773]) elif Par == 4: onFilter = np.array([.000892313668, -.001629492013, -.007346166328, .016068943964, .026682300156, -.081266699680, -.056077313316, .415308407030, .782238930920, .434386056491, -.066627474263, -.096220442034, .039334427123, .025082261845, -.015211731527, -.005658286686, .003751436157, .001266561929, -.000589020757, -.000259974552, .000062339034, .000031229876, -.000003259680, -.000001784985]) elif Par == 5: onFilter = np.array([-.000212080863, .000358589677, .002178236305, -.004159358782, -.010131117538, .023408156762, .028168029062, -.091920010549, -.052043163216, .421566206729, .774289603740, .437991626228, -.062035963906, -.105574208706, .041289208741, .032683574283, -.019761779012, -.009164231153, .006764185419, .002433373209, -.001662863769, -.000638131296, .000302259520, .000140541149, -.000041340484, -.000021315014, .000003734597, .000002063806, -.000000167408, -.000000095158]) if Type == 'Daubechies': if Par == 4: onFilter = np.array([.482962913145, .836516303738, .224143868042, -.129409522551]) elif Par == 6: onFilter = np.array([.332670552950, .806891509311, .459877502118, -.135011020010, -.085441273882, .035226291882]) elif Par == 8: onFilter = np.array([.230377813309, .714846570553, .630880767930, -.027983769417, -.187034811719, .030841381836, .032883011667, -.010597401785]) elif Par == 10: onFilter = np.array([.160102397974, .603829269797, .724308528438, .138428145901, -.242294887066, -.032244869585, .077571493840, -.006241490213, -.012580751999, .003335725285]) elif Par == 12: onFilter = np.array([.111540743350, .494623890398, .751133908021, .315250351709, -.226264693965, -.129766867567, .097501605587, .027522865530, -.031582039317, .000553842201, .004777257511, -.001077301085]) elif Par == 14: onFilter = np.array([.077852054085, .396539319482, .729132090846, .469782287405, -.143906003929, -.224036184994, .071309219267, .080612609151, -.038029936935, -.016574541631, .012550998556, .000429577973, -.001801640704, .000353713800]) elif Par == 16: onFilter = np.array([.054415842243, .312871590914, .675630736297, .585354683654, -.015829105256, -.284015542962, .000472484574, .128747426620, -.017369301002, -.044088253931, .013981027917, .008746094047, -.004870352993, -.000391740373, .000675449406, -.000117476784]) elif Par==18: onFilter = np.array([.038077947364, .243834674613, .604823123690, .657288078051, .133197385825, -.293273783279, -.096840783223, .148540749338, .030725681479, -.067632829061, .000250947115, .022361662124, -.004723204758, -.004281503682, .001847646883, .000230385764, -.000251963189, .000039347320]) elif Par==20: onFilter = np.array([.026670057901, .188176800078, .527201188932, .688459039454, .281172343661, -.249846424327, -.195946274377, .127369340336, .093057364604, -.071394147166, -.029457536822, .033212674059, .003606553567, -.010733175483, .001395351747, .001992405295, -.000685856695, -.000116466855, .000093588670, -.000013264203]) if Type == 'Symmlet': if Par == 4: onFilter = np.array([-.107148901418, -.041910965125, .703739068656, 1.136658243408, .421234534204, -.140317624179, -.017824701442, .045570345896]) elif Par == 5: onFilter = np.array([.038654795955, .041746864422, -.055344186117, .281990696854, 1.023052966894, .896581648380, .023478923136, -.247951362613, -.029842499869, .027632152958]) elif Par == 6: onFilter = np.array([.021784700327, .004936612372, -.166863215412, -.068323121587, .694457972958, 1.113892783926, .477904371333, -.102724969862, -.029783751299, .063250562660, .002499922093, -.011031867509]) elif Par == 7: onFilter = np.array([.003792658534, -.001481225915, -.017870431651, .043155452582, .096014767936, -.070078291222, .024665659489, .758162601964, 1.085782709814, .408183939725, -.198056706807, -.152463871896, .005671342686, .014521394762]) elif Par == 8: onFilter = np.array([.002672793393, -.000428394300, -.021145686528, .005386388754, .069490465911, -.038493521263, -.073462508761, .515398670374, 1.099106630537, .680745347190, -.086653615406, -.202648655286, .010758611751, .044823623042, -.000766690896, -.004783458512]) elif Par == 9: onFilter = np.array([.001512487309, -.000669141509, -.014515578553, .012528896242, .087791251554, -.025786445930, -.270893783503, .049882830959, .873048407349, 1.015259790832, .337658923602, -.077172161097, .000825140929, .042744433602, -.016303351226, -.018769396836, .000876502539, .001981193736]) elif Par == 10: onFilter = np.array([.001089170447, .000135245020, -.012220642630, -.002072363923, .064950924579, .016418869426, -.225558972234, -.100240215031, .667071338154, 1.088251530500, .542813011213, -.050256540092, -.045240772218, .070703567550, .008152816799, -.028786231926, -.001137535314, .006495728375, .000080661204, -.000649589896]) if Type == 'Vaidyanathan': onFilter = np.array([-.000062906118, .000343631905, -.000453956620, -.000944897136, .002843834547, .000708137504, -.008839103409, .003153847056, .019687215010, -.014853448005, -.035470398607, .038742619293, .055892523691, -.077709750902, -.083928884366, .131971661417, .135084227129, -.194450471766, -.263494802488, .201612161775, .635601059872, .572797793211, .250184129505, .045799334111]) if Type == 'Battle': if Par == 1: onFilterTmp = np.array([0.578163, 0.280931, -0.0488618, -0.0367309, 0.012003, 0.00706442, -0.00274588, -0.00155701, 0.000652922, 0.000361781, -0.000158601, -0.0000867523]) elif Par == 3: onFilterTmp = np.array([0.541736, 0.30683, -0.035498, -0.0778079, 0.0226846, 0.0297468, -0.0121455, -0.0127154, 0.00614143, 0.00579932, -0.00307863, -0.00274529, 0.00154624, 0.00133086, -0.000780468, -0.00065562, 0.000395946, 0.000326749, -0.000201818, -0.000164264, 0.000103307]) elif Par == 5: onFilterTmp = np.array([0.528374, 0.312869, -0.0261771, -0.0914068, 0.0208414, 0.0433544, -0.0148537, -0.0229951, 0.00990635, 0.0128754, -0.00639886, -0.00746848, 0.00407882, 0.00444002, -0.00258816, -0.00268646, 0.00164132, 0.00164659, -0.00104207, -0.00101912, 0.000662836, 0.000635563, -0.000422485, -0.000398759, 0.000269842, 0.000251419, -0.000172685, -0.000159168, 0.000110709, 0.000101113]) onFilter = np.zeros(2*onFilterTmp.size-1) onFilter[onFilterTmp.size-1:2*onFilterTmp.size] = onFilterTmp; onFilter[0:onFilterTmp.size-1] = onFilterTmp[onFilterTmp.size-1:0:-1] return onFilter / np.linalg.norm(onFilter) """ Copyright (c) 1993-5. Jonathan Buckheit and David Donoho Part of Wavelab Version 850 Built Tue Jan 3 13:20:40 EST 2006 This is Copyrighted Material For Copying permissions see COPYING.m Comments? e-mail wavelab@stat.stanford.edu """ def dfilters(fname, type): """ This is a translation of the original Matlab implementation of dfilters.m from the Nonsubsampled Contourlet Toolbox. The following comment is from the original and only applies in so far that not all of the directional filters are implemented in this Python version but only those which are needed for the shearlet toolbox. DFILTERS Generate directional 2D filters [h0, h1] = dfilters(fname, type) Input: fname: Filter name. Available 'fname' are: 'haar': the "Haar" filters 'vk': McClellan transformed of the filter from the VK book 'ko': orthogonal filter in the Kovacevics paper 'kos': smooth 'ko' filter 'lax': 17 x 17 by Lu, Antoniou and Xu 'sk': 9 x 9 by Shah and Kalker 'cd': 7 and 9 McClellan transformed by Cohen and Daubechies 'pkva': ladder filters by Phong et al. 'oqf_362': regular 3 x 6 filter 'dvmlp': regular linear phase biorthogonal filter with 3 dvm 'sinc': ideal filter (*NO perfect recontruction*) 'dmaxflat': diamond maxflat filters obtained from a three stage ladder type: 'd' or 'r' for decomposition or reconstruction filters Output: h0, h1: diamond filter pair (lowpass and highpass) To test those filters (for the PR condition for the FIR case), verify that: conv2(h0, modulate2(h1, 'b')) + conv2(modulate2(h0, 'b'), h1) = 2 (replace + with - for even size filters) To test for orthogonal filter conv2(h, reverse2(h)) + modulate2(conv2(h, reverse2(h)), 'b') = 2 Part of the Nonsubsampled Contourlet Toolbox (http://www.mathworks.de/matlabcentral/fileexchange/10049-nonsubsampled-contourlet-toolbox) """ if fname == 'haar': if type.lower() == 'd': h0 = np.array([1, 1]) / np.sqrt(2) h1 = np.array([-1, 1]) / np.sqrt(2) else: h0 = np.array([1, 1]) / np.sqrt(2) h1 = np.array([1, -1]) / np.sqrt(2) elif fname == 'vk': # in Vetterli and Kovacevic book if type.lower() == 'd': h0 = np.array([1, 2, 1]) / 4 h1 = np.array([-1, -2, 6, -2, -1]) / 4 else: h0 = np.array([-1, 2, 6, 2, -1]) / 4 h1 = np.array([-1, 2, -1]) / 4 t = np.array([[0, 1, 0], [1, 0, 1], [0, 1, 0]]) / 4 # diamon kernel h0 = mctrans(h0, t) h1 = mctrans(h1, t) elif fname == 'ko': # orthogonal filters in Kovacevics thesis a0 = 2 a1 = 0.5 a2 = 1 h0 = np.array([[0, -a1, -a0*a1, 0], [-a2, -a0*a2, -a0, 1], [0, a0*a1*a2, -a1*a2, 0]]) # h1 = qmf2(h0) h1 = np.array([[0, -a1*a2, -a0*a1*a2, 0], [1, a0, -a0*a2, a2], [0, -a0*a1, a1, 0]]) # normalize filter sum and norm norm = np.sqrt(2) / np.sum(h0) h0 = h0 * norm h1 = h1 * norm if type == 'r': # reverse filters for reconstruction h0 = h0[::-1] h1 = h1[::-1] elif fname == 'kos': # smooth orthogonal filters in Kovacevics thesis a0 = -np.sqrt(3) a1 = -np.sqrt(3) a2 = 2+np.sqrt(3) h0 = np.array([[0, -a1, -a0*a1, 0], [-a2, -a0*a2, -a0, 1], [0, a0*a1*a2, -a1*a2, 0]]) # h1 = qmf2(h0) h1 = np.array([[0, -a1*a2, -a0*a1*a2, 0], [1, a0, -a0*a2, a2], [0, -a0*a1, a1, 0]]) # normalize filter sum and norm norm = np.sqrt(2) / np.sum(h0) h0 = h0 * norm h1 = h1 * norm if type == 'r': # reverse filters for reconstruction h0 = h0[::-1] h1 = h1[::-1] elif fname == 'lax': # by lu, antoniou and xu h = np.array([[-1.2972901e-5, 1.2316237e-4, -7.5212207e-5, 6.3686104e-5, 9.4800610e-5, -7.5862919e-5, 2.9586164e-4, -1.8430337e-4], [1.2355540e-4, -1.2780882e-4, -1.9663685e-5, -4.5956538e-5, -6.5195193e-4, -2.4722942e-4, -2.1538331e-5, -7.0882131e-4], [-7.5319075e-5, -1.9350810e-5, -7.1947086e-4, 1.2295412e-3, 5.7411214e-4, 4.4705422e-4, 1.9623554e-3, 3.3596717e-4], [6.3400249e-5, -2.4947178e-4, 4.4905711e-4, -4.1053629e-3, -2.8588307e-3, 4.3782726e-3, -3.1690509e-3, -3.4371484e-3], [9.6404973e-5, -4.6116254e-5, 1.2371871e-3, -1.1675575e-2, 1.6173911e-2, -4.1197559e-3, 4.4911165e-3, 1.1635130e-2], [-7.6955555e-5, -6.5618379e-4, 5.7752252e-4, 1.6211426e-2, 2.1310378e-2, -2.8712621e-3, -4.8422645e-2, -5.9246338e-3], [2.9802986e-4, -2.1365364e-5, 1.9701350e-3, 4.5047673e-3, -4.8489158e-2, -3.1809526e-3, -2.9406153e-2, 1.8993868e-1], [-1.8556637e-4, -7.1279432e-4, 3.3839195e-4, 1.1662001e-2, -5.9398223e-3, -3.4467920e-3, 1.9006499e-1, 5.7235228e-1] ]) h0 = np.sqrt(2) * np.append(h, h[:,-2::-1], 1) h0 = np.append(h0, h0[-2::-1,:], 0) h1 = modulate2(h0, 'b') elif fname == 'sk': # by shah and kalker h = np.array([[0.621729, 0.161889, -0.0126949, -0.00542504, 0.00124838], [0.161889, -0.0353769, -0.0162751, -0.00499353, 0], [-0.0126949, -0.0162751, 0.00749029, 0, 0], [-0.00542504, 0.00499353, 0, 0, 0], [0.00124838, 0, 0, 0, 0]]) h0 = np.append(h[-1:0:-1, -1:0:-1], h[-1:0:-1,:], 1) h0 = np.append(h0, np.append(h[:,-1:0:-1], h, 1), 0)*np.sqrt(2) h1 = modulate2(h0, 'b') elif fname == 'dvmlp': q = np.sqrt(2) b = 0.02 b1 = b*b; h = np.array([[b/q, 0, -2*q*b, 0, 3*q*b, 0, -2*q*b, 0, b/q], [0, -1/(16*q), 0, 9/(16*q), 1/q, 9/(16*q), 0, -1/(16*q), 0], [b/q, 0, -2*q*b, 0, 3*q*b, 0, -2*q*b, 0, b/q]]) g0 = np.array([[-b1/q, 0, 4*b1*q, 0, -14*q*b1, 0, 28*q*b1, 0, -35*q*b1, 0, 28*q*b1, 0, -14*q*b1, 0, 4*b1*q, 0, -b1/q], [0, b/(8*q), 0, -13*b/(8*q), b/q, 33*b/(8*q), -2*q*b, -21*b/(8*q), 3*q*b, -21*b/(8*q), -2*q*b, 33*b/(8*q), b/q, -13*b/(8*q), 0, b/(8*q), 0], [-q*b1, 0, -1/(256*q) + 8*q*b1, 0, 9/(128*q) - 28*q*b1, -1/(q*16), -63/(256*q) + 56*q*b1, 9/(16*q), 87/(64*q)-70*q*b1, 9/(16*q), -63/(256*q) + 56*q*b1, -1/(q*16), 9/(128*q) - 28*q*b1, 0, -1/(256*q) + 8*q*b1, 0, -q*b1], [0, b/(8*q), 0, -13*b/(8*q), b/q, 33*b/(8*q), -2*q*b, -21*b/(8*q), 3*q*b, -21*b/(8*q), -2*q*b, 33*b/(8*q), b/q, -13*b/(8*q), 0, b/(8*q), 0], [-b1/q, 0, 4*b1*q, 0, -14*q*b1, 0, 28*q*b1, 0, -35*q*b1, 0, 28*q*b1, 0, -14*q*b1, 0, 4*b1*q, 0, -b1/q]]) h1 = modulate2(g0, 'b') h0 = h print(h1.shape) print(h0.shape) if type == 'r': h1 = modulate2(h, 'b') h0 = g0 elif fname == 'cd' or fname == '7-9': # by cohen and Daubechies h0 = np.array([0.026748757411, -0.016864118443, -0.078223266529, 0.266864118443, 0.602949018236, 0.266864118443, -0.078223266529, -0.016864118443, 0.026748757411]) g0 = np.array([-0.045635881557, -0.028771763114, 0.295635881557, 0.557543526229, 0.295635881557, -0.028771763114, -0.045635881557]) if type == 'd': h1 = modulate2(g0, 'c') else: h1 = modulate2(h0, 'c') h0 = g0 # use McClellan to obtain 2D filters t = np.array([[0, 1, 0], [1, 0, 1], [0, 1, 0]])/4 # diamond kernel h0 = np.sqrt(2) * mctrans(h0, t) h1 = np.sqrt(2) * mctrans(h1, t) elif fname == 'oqf_362': h0 = np.sqrt(2) / 64 * np.array([[np.sqrt(15), -3, 0], [0, 5, np.sqrt(15)], [-2*np.sqrt(2), 30, 0], [0, 30, 2*np.sqrt(15)], [np.sqrt(15), 5, 0], [0, -3, -np.sqrt(15)]]) h1 = -modulate2(h0, 'b') h1 = -h1[::-1] if type == 'r': h0 = h0[::-1] h1 = -modulate2(h0, 'b') h1 = -h1[::-1] elif fname == 'test': h0 = np.array([[0, 1, 0], [1, 4, 1], [0, 1, 0]]) h1 = np.array([[0, -1, 0], [-1, 4, -1], [0, -1, 0]]) elif fname == 'testDVM': h0 = np.array([[1, 1], [1, 1]]) / np.sqrt(2) h1 = np.array([[-1, 1], [1, -1]]) / np.sqrt(2) elif fname == 'qmf': # by Lu, antoniou and xu # ideal response window m = 2 n = 2 w1d = np.kaiser(4*m+1, 2.6) w = np.zeros((n+m+1,n+m+1)) for n1 in np.arange(-m,m+1): for n2 in np.arange(-n,n+1): w[n1+m,n2+n] = w1d[2*m+n1+n2]*w1d[2*m+n1-n2] h = np.zeros((n+m+1,n+m+1)) for n1 in np.arange(-m,m+1): for n2 in np.arange(-n,n+1): h[n1+m, n2+n] = 0.5*np.sinc((n1+n2)/2) * 0.5*np.sinc((n1-n2)/2) c = np.sum(h) h = np.sqrt(2) * h h0 = h * w h1 = modulate2(h0, 'b') elif fname == 'qmf2': # by Lu, Antoniou and Xu # ideal response window h = np.array([ [-0.001104, 0.002494, -0.001744, 0.004895, -0.000048, -0.000311], [0.008918, -0.002844, -0.025197, -0.017135, 0.003905, -0.000081], [-0.007587, -0.065904, 00.100431, -0.055878, 0.007023, 0.001504], [0.001725, 0.184162, 0.632115, 0.099414, -0.027006, -0.001110], [-0.017935, -0.000491, 0.191397, -0.001787, -0.010587, 0.002060], [0.001353, 0.005635, -0.001231, -0.009052, -0.002668, 0.000596]]) h0 = h/np.sum(h) h1 = modulate2(h0, 'b') elif fname == 'dmaxflat4': M1 = 1/np.sqrt(2) M2 = np.copy(M1) k1 = 1-np.sqrt(2) k3 = np.copy(k1) k2 = np.copy(M1) h = np.array([0.25*k2*k3, 0.5*k2, 1+0.5*k2*k3])*M1 h = np.append(h, h[-2::-1]) g = np.array([-0.125*k1*k2*k3, 0.25*k1*k2, -0.5*k1-0.5*k3-0.375*k1*k2*k3, 1+0.5*k1*k2])*M2 g = np.append(g, h[-2::-1]) B = dmaxflat(4,0) h0 = mctrans(h,B) g0 = mctrans(g,B) h0 = np.sqrt(2) * h0 / np.sum(h0) g0 = np.sqrt(2) * g0 / np.sum(g0) h1 = modulate2(g0, 'b') if type == 'r': h1 = modulate2(h0, 'b') h0 = g0 elif fname == 'dmaxflat5': M1 = 1/np.sqrt(2) M2 = M1 k1 = 1-np.sqrt(2) k3 = k1 k2 = M1 h = np.array([0.25*k2*k3, 0.5*k2, 1+0.5*k2*k3])*M1 h = np.append(h, h[-2::-1]) g = np.array([-0.125*k1*k2*k3, 0.25*k1*k2, -0.5*k1-0.5*k3-0.375*k1*k2*k3, 1+0.5*k1*k2])*M2 g = np.append(g, h[-2::-1]) B = dmaxflat(5,0) h0 = mctrans(h,B) g0 = mctrans(g,B) h0 = np.sqrt(2) * h0 / np.sum(h0) g0 = np.sqrt(2) * g0 / np.sum(g0) h1 = modulate2(g0, 'b') if type == 'r': h1 = modulate2(h0, 'b') h0 = g0 elif fname == 'dmaxflat6': M1 = 1/np.sqrt(2) M2 = M1 k1 = 1-np.sqrt(2) k3 = k1 k2 = M1 h = np.array([0.25*k2*k3, 0.5*k2, 1+0.5*k2*k3])*M1 h = np.append(h, h[-2::-1]) g = np.array([-0.125*k1*k2*k3, 0.25*k1*k2, -0.5*k1-0.5*k3-0.375*k1*k2*k3, 1+0.5*k1*k2])*M2 g = np.append(g, h[-2::-1]) B = dmaxflat(6,0) h0 = mctrans(h,B) g0 = mctrans(g,B) h0 = np.sqrt(2) * h0 / np.sum(h0) g0 = np.sqrt(2) * g0 / np.sum(g0) h1 = modulate2(g0, 'b') if type == 'r': h1 = modulate2(h0, 'b') h0 = g0 elif fname == 'dmaxflat7': M1 = 1/np.sqrt(2) M2 = M1 k1 = 1-np.sqrt(2) k3 = k1 k2 = M1 h = np.array([0.25*k2*k3, 0.5*k2, 1+0.5*k2*k3])*M1 h = np.append(h, h[-2::-1]) g = np.array([-0.125*k1*k2*k3, 0.25*k1*k2, -0.5*k1-0.5*k3-0.375*k1*k2*k3, 1+0.5*k1*k2])*M2 g = np.append(g, h[-2::-1]) B = dmaxflat(7,0) h0 = mctrans(h,B) g0 = mctrans(g,B) h0 = np.sqrt(2) * h0 / np.sum(h0) g0 = np.sqrt(2) * g0 / np.sum(g0) h1 = modulate2(g0, 'b') if type == 'r': h1 = modulate2(h0, 'b') h0 = g0 # The original file supports a case "otherwise" for unrecognized filters # and computes simple 1D wavelet filters for them using wfilters.m # I think we don't need this and skip this for the time being. # IN ORIGINAL MATLAB VERSION: # otherwise # % Assume the "degenerated" case: 1D wavelet filters # [h0,h1] = wfilters(fname, type); return h0, h1 def dmaxflat(N,d): """ THIS IS A REWRITE OF THE ORIGINAL MATLAB IMPLEMENTATION OF dmaxflat.m FROM THE Nonsubsampled Contourlet Toolbox. -- Stefan Loock, Dec 2016. returns 2-D diamond maxflat filters of order 'N' the filters are nonseparable and 'd' is the (0,0) coefficient, being 1 or 0 depending on use. by Arthur L. da Cunha, University of Illinois Urbana-Champaign Aug 2004 """ if (N > 7) or (N < 1): print('Error: N must be in {1,2,...,7}') return 0 if N == 1: h = np.array([[0, 1, 0],[1, 0, 1],[0, 1, 0]])/4 h[1,1] = d elif N == 2: h = np.array([[0, -1, 0],[-1, 0, 10], [0, 10, 0]]) h = np.append(h, np.fliplr(h[:,0:-1]), 1) h = np.append(h, np.flipud(h[0:-1,:]), 0)/32 h[2,2] = d elif N == 3: h = np.array([[0, 3, 0, 2],[3, 0, -27, 0],[0, -27, 0, 174], [2, 0, 174, 0]]) h = np.append(h, np.fliplr(h[:, 0:-1]), 1) h = np.append(h, np.flipud(h[0:-1,:]),0) h[3,3] = d elif N == 4: h = np.array([[0, -5, 0, -3, 0], [-5, 0, 52, 0, 34], [0, 52, 0, -276, 0], [-3, 0, -276, 0, 1454], [0, 34, 0, 1454, 0]])/np.power(2,12) h = np.append(h, np.fliplr(h[:,0:-1]),1) h = np.append(h, np.flipud(h[0:-1,:]),0) h[4,4] = d elif N == 5: h = np.array([[0, 35, 0, 20, 0, 18], [35, 0, -425, 0, -250, 0], [0, -425, 0, 2500, 0, 1610], [20, 0, 2500, 0, -10200, 0], [0, -250, 0, -10200, 0, 47780], [18, 0, 1610, 0, 47780, 0]])/np.power(2,17) h = np.append(h, np.fliplr(h[:,0:-1]),1) h = np.append(h, np.flipud(h[0:-1,:]),0) h[5,5] = d elif N == 6: h = np.array([[0, -63, 0, -35, 0, -30, 0], [-63, 0, 882, 0, 495, 0, 444], [0, 882, 0, -5910, 0, -3420, 0], [-35, 0, -5910, 0, 25875, 0, 16460], [0, 495, 0, 25875, 0, -89730, 0], [-30, 0, -3420, 0, -89730, 0, 389112], [0, 44, 0, 16460, 0, 389112, 0]])/np.power(2,20) h = np.append(h, np.fliplr(h[:,0:-1]),1) h = np.append(h, np.flipud(h[0:-1,:]),0) h[6,6] = d elif N == 7: h = np.array([[0, 231, 0, 126, 0, 105, 0, 100], [231, 0, -3675, 0, -2009, 0, -1715, 0], [0, -3675, 0, 27930, 0, 15435, 0, 13804], [126, 0, 27930, 0, -136514, 0, -77910, 0], [0, -2009, 0, -136514, 0, 495145, 0, 311780], [105, 0, 15435, 0, 495145, 0, -1535709, 0], [0, -1715, 0, -77910, 0, -1534709, 0, 6305740], [100, 0, 13804, 0, 311780, 0, 6305740, 0]])/np.power(2,24) h = np.append(h, np.fliplr(h[:,0:-1]),1) h = np.append(h, np.flipud(h[0:-1,:]),0) h[7,7] = d return h def mctrans(b,t): """ This is a translation of the original Matlab implementation of mctrans.m from the Nonsubsampled Contourlet Toolbox by Arthur L. da Cunha. MCTRANS McClellan transformation H = mctrans(B,T) produces the 2-D FIR filter H that corresponds to the 1-D FIR filter B using the transform T. Convert the 1-D filter b to SUM_n a(n) cos(wn) form Part of the Nonsubsampled Contourlet Toolbox (http://www.mathworks.de/matlabcentral/fileexchange/10049-nonsubsampled-contourlet-toolbox) """ # Convert the 1-D filter b to SUM_n a(n) cos(wn) form # if mod(n,2) != 0 -> error n = (b.size-1)//2 b = np.fft.fftshift(b[::-1]) #inverse fftshift b = b[::-1] a = np.zeros(n+1) a[0] = b[0] a[1:n+1] = 2*b[1:n+1] inset = np.floor((np.asarray(t.shape)-1)/2) inset = inset.astype(int) # Use Chebyshev polynomials to compute h P0 = 1 P1 = t; h = a[1]*P1; rows = int(inset[0]+1) cols = int(inset[1]+1) h[rows-1,cols-1] = h[rows-1,cols-1]+a[0]*P0; for i in range(3,n+2): P2 = 2*signal.convolve2d(t, P1) rows = (rows + inset[0]).astype(int) cols = (cols + inset[1]).astype(int) if i == 3: P2[rows-1,cols-1] = P2[rows-1,cols-1] - P0 else: P2[rows[0]-1:rows[-1],cols[0]-1:cols[-1]] = P2[rows[0]-1:rows[-1], cols[0]-1:cols[-1]] - P0 rows = inset[0] + np.arange(np.asarray(P1.shape)[0])+1 rows = rows.astype(int) cols = inset[1] + np.arange(np.asarray(P1.shape)[1])+1 cols = cols.astype(int) hh = h; h = a[i-1]*P2 h[rows[0]-1:rows[-1], cols[0]-1:cols[-1]] = h[rows[0]-1:rows[-1], cols[0]-1:cols[-1]] + hh P0 = P1; P1 = P2; h = np.rot90(h,2) return h def modulate2(x, type, center=np.array([0, 0])): """ THIS IS A REWRITE OF THE ORIGINAL MATLAB IMPLEMENTATION OF modulate2.m FROM THE Nonsubsampled Contourlet Toolbox. MODULATE2 2D modulation y = modulate2(x, type, [center]) With TYPE = {'r', 'c' or 'b'} for modulate along the row, or column or both directions. CENTER secify the origin of modulation as floor(size(x)/2)+1+center (default is [0, 0]) Part of the Nonsubsampled Contourlet Toolbox (http://www.mathworks.de/matlabcentral/fileexchange/10049-nonsubsampled-contourlet-toolbox) """ size = np.asarray(x.shape) if x.ndim == 1: if np.array_equal(center, [0, 0]): center = 0 origin = np.floor(size/2)+1+center n1 = np.arange(size[0])-origin[0]+1 if x.ndim == 2: n2 = np.arange(size[1])-origin[1]+1 else: n2 = n1 if type == 'r': m1 = np.power(-1,n1) if x.ndim == 1: y = x*m1 else: y = x * np.transpose(np.tile(m1, (size[1], 1))) elif type == 'c': m2 = np.power(-1,n2) if x.ndim == 1: y = x*m2 else: y = x * np.tile(m2, np.array([size[0], 1])) elif type == 'b': m1 = np.power(-1,n1) m2 = np.power(-1,n2) m = np.outer(m1, m2) if x.ndim == 1: y = x * m1 else: y = x * m return y def MirrorFilt(x): """ This is a translation of the original Matlab implementation of MirrorFilt.m from the WaveLab850 toolbox. MirrorFilt -- Apply (-1)^t modulation Usage h = MirrorFilt(l) Inputs l 1-d signal Outputs h 1-d signal with DC frequency content shifted to Nyquist frequency Description h(t) = (-1)^(t-1) * x(t), 1 <= t <= length(x) See Also: DyadDownHi Part of WaveLab850 (http://www-stat.stanford.edu/~wavelab/) """ return np.power(-1,np.arange(x.size))*x """ Copyright (c) 1993. Iain M. Johnstone Part of Wavelab Version 850 Built Tue Jan 3 13:20:40 EST 2006 This is Copyrighted Material For Copying permissions see COPYING.m Comments? e-mail wavelab@stat.stanford.edu """
Functions
def MakeONFilter(
Type, Par=1)
This is a rewrite of the original Matlab implementation of MakeONFilter.m from the WaveLab850 toolbox.
MakeONFilter -- Generate Orthonormal QMF Filter for Wavelet Transform
Usage:
qmf = MakeONFilter(Type, Par)
Inputs:
Type: string: 'Haar', 'Beylkin', 'Coiflet', 'Daubechies', 'Symmlet', 'Vaidyanathan', 'Battle'
Outputs:
qmf: quadrature mirror filter
Description
The Haar filter (which could be considered a Daubechies-2) was the first wavelet, though not called as such, and is discontinuous.
The Beylkin filter places roots for the frequency response function close to the Nyquist frequency on the real axis.
The Coiflet filters are designed to give both the mother and father wavelets 2*Par vanishing moments; here Par may be one of 1,2,3,4 or 5.
The Daubechies filters are minimal phase filters that generate wavelets which have a minimal support for a given number of vanishing moments. They are indexed by their length, Par, which may be one of 4,6,8,10,12,14,16,18 or 20. The number of vanishing moments is par/2.
Symmlets are also wavelets within a minimum size support for a given number of vanishing moments, but they are as symmetrical as possible, as opposed to the Daubechies filters which are highly asymmetrical. They are indexed by Par, which specifies the number of vanishing moments and is equal to half the size of the support. It ranges from 4 to 10.
The Vaidyanathan filter gives an exact reconstruction, but does not satisfy any moment condition. The filter has been optimized for speech coding.
The Battle-Lemarie filter generate spline orthogonal wavelet basis. The parameter Par gives the degree of the spline. The number of vanishing moments is Par+1.
See Also: FWT_PO, IWT_PO, FWT2_PO, IWT2_PO, WPAnalysis
References: The books by Daubechies and Wickerhauser.
Part of WaveLab850 (http://www-stat.stanford.edu/~wavelab/)
def MakeONFilter(Type,Par=1): """ This is a rewrite of the original Matlab implementation of MakeONFilter.m from the WaveLab850 toolbox. MakeONFilter -- Generate Orthonormal QMF Filter for Wavelet Transform Usage: qmf = MakeONFilter(Type, Par) Inputs: Type: string: 'Haar', 'Beylkin', 'Coiflet', 'Daubechies', 'Symmlet', 'Vaidyanathan', 'Battle' Outputs: qmf: quadrature mirror filter Description The Haar filter (which could be considered a Daubechies-2) was the first wavelet, though not called as such, and is discontinuous. The Beylkin filter places roots for the frequency response function close to the Nyquist frequency on the real axis. The Coiflet filters are designed to give both the mother and father wavelets 2*Par vanishing moments; here Par may be one of 1,2,3,4 or 5. The Daubechies filters are minimal phase filters that generate wavelets which have a minimal support for a given number of vanishing moments. They are indexed by their length, Par, which may be one of 4,6,8,10,12,14,16,18 or 20. The number of vanishing moments is par/2. Symmlets are also wavelets within a minimum size support for a given number of vanishing moments, but they are as symmetrical as possible, as opposed to the Daubechies filters which are highly asymmetrical. They are indexed by Par, which specifies the number of vanishing moments and is equal to half the size of the support. It ranges from 4 to 10. The Vaidyanathan filter gives an exact reconstruction, but does not satisfy any moment condition. The filter has been optimized for speech coding. The Battle-Lemarie filter generate spline orthogonal wavelet basis. The parameter Par gives the degree of the spline. The number of vanishing moments is Par+1. See Also: FWT_PO, IWT_PO, FWT2_PO, IWT2_PO, WPAnalysis References: The books by Daubechies and Wickerhauser. Part of WaveLab850 (http://www-stat.stanford.edu/~wavelab/) """ if Type == 'Haar': onFilter = np.array([1/np.sqrt(2), 1/np.sqrt(2)]) if Type == 'Beylkin': onFilter = np.array([.099305765374, .424215360813, .699825214057, .449718251149, -.110927598348, -.264497231446, .026900308804, .155538731877, -.017520746267, -.088543630623, .019679866044, .042916387274, -.017460408696, -.014365807969, .010040411845, .001484234782, -.002736031626, .000640485329]) if Type == 'Coiflet': if Par == 1: onFilter = np.array([.038580777748, -.126969125396, -.077161555496, .607491641386, .745687558934, .226584265197]) elif Par == 2: onFilter = np.array([.016387336463, -.041464936782, -.067372554722, .386110066823, .812723635450, .417005184424, -.076488599078, -.059434418646, .023680171947, .005611434819, -.001823208871, -.000720549445]) elif Par == 3: onFilter = np.array([-.003793512864, .007782596426, .023452696142, -.065771911281, -.061123390003, .405176902410, .793777222626, .428483476378, -.071799821619, -.082301927106, .034555027573, .015880544864, -.009007976137, -.002574517688, .001117518771, .000466216960, -.000070983303, -.000034599773]) elif Par == 4: onFilter = np.array([.000892313668, -.001629492013, -.007346166328, .016068943964, .026682300156, -.081266699680, -.056077313316, .415308407030, .782238930920, .434386056491, -.066627474263, -.096220442034, .039334427123, .025082261845, -.015211731527, -.005658286686, .003751436157, .001266561929, -.000589020757, -.000259974552, .000062339034, .000031229876, -.000003259680, -.000001784985]) elif Par == 5: onFilter = np.array([-.000212080863, .000358589677, .002178236305, -.004159358782, -.010131117538, .023408156762, .028168029062, -.091920010549, -.052043163216, .421566206729, .774289603740, .437991626228, -.062035963906, -.105574208706, .041289208741, .032683574283, -.019761779012, -.009164231153, .006764185419, .002433373209, -.001662863769, -.000638131296, .000302259520, .000140541149, -.000041340484, -.000021315014, .000003734597, .000002063806, -.000000167408, -.000000095158]) if Type == 'Daubechies': if Par == 4: onFilter = np.array([.482962913145, .836516303738, .224143868042, -.129409522551]) elif Par == 6: onFilter = np.array([.332670552950, .806891509311, .459877502118, -.135011020010, -.085441273882, .035226291882]) elif Par == 8: onFilter = np.array([.230377813309, .714846570553, .630880767930, -.027983769417, -.187034811719, .030841381836, .032883011667, -.010597401785]) elif Par == 10: onFilter = np.array([.160102397974, .603829269797, .724308528438, .138428145901, -.242294887066, -.032244869585, .077571493840, -.006241490213, -.012580751999, .003335725285]) elif Par == 12: onFilter = np.array([.111540743350, .494623890398, .751133908021, .315250351709, -.226264693965, -.129766867567, .097501605587, .027522865530, -.031582039317, .000553842201, .004777257511, -.001077301085]) elif Par == 14: onFilter = np.array([.077852054085, .396539319482, .729132090846, .469782287405, -.143906003929, -.224036184994, .071309219267, .080612609151, -.038029936935, -.016574541631, .012550998556, .000429577973, -.001801640704, .000353713800]) elif Par == 16: onFilter = np.array([.054415842243, .312871590914, .675630736297, .585354683654, -.015829105256, -.284015542962, .000472484574, .128747426620, -.017369301002, -.044088253931, .013981027917, .008746094047, -.004870352993, -.000391740373, .000675449406, -.000117476784]) elif Par==18: onFilter = np.array([.038077947364, .243834674613, .604823123690, .657288078051, .133197385825, -.293273783279, -.096840783223, .148540749338, .030725681479, -.067632829061, .000250947115, .022361662124, -.004723204758, -.004281503682, .001847646883, .000230385764, -.000251963189, .000039347320]) elif Par==20: onFilter = np.array([.026670057901, .188176800078, .527201188932, .688459039454, .281172343661, -.249846424327, -.195946274377, .127369340336, .093057364604, -.071394147166, -.029457536822, .033212674059, .003606553567, -.010733175483, .001395351747, .001992405295, -.000685856695, -.000116466855, .000093588670, -.000013264203]) if Type == 'Symmlet': if Par == 4: onFilter = np.array([-.107148901418, -.041910965125, .703739068656, 1.136658243408, .421234534204, -.140317624179, -.017824701442, .045570345896]) elif Par == 5: onFilter = np.array([.038654795955, .041746864422, -.055344186117, .281990696854, 1.023052966894, .896581648380, .023478923136, -.247951362613, -.029842499869, .027632152958]) elif Par == 6: onFilter = np.array([.021784700327, .004936612372, -.166863215412, -.068323121587, .694457972958, 1.113892783926, .477904371333, -.102724969862, -.029783751299, .063250562660, .002499922093, -.011031867509]) elif Par == 7: onFilter = np.array([.003792658534, -.001481225915, -.017870431651, .043155452582, .096014767936, -.070078291222, .024665659489, .758162601964, 1.085782709814, .408183939725, -.198056706807, -.152463871896, .005671342686, .014521394762]) elif Par == 8: onFilter = np.array([.002672793393, -.000428394300, -.021145686528, .005386388754, .069490465911, -.038493521263, -.073462508761, .515398670374, 1.099106630537, .680745347190, -.086653615406, -.202648655286, .010758611751, .044823623042, -.000766690896, -.004783458512]) elif Par == 9: onFilter = np.array([.001512487309, -.000669141509, -.014515578553, .012528896242, .087791251554, -.025786445930, -.270893783503, .049882830959, .873048407349, 1.015259790832, .337658923602, -.077172161097, .000825140929, .042744433602, -.016303351226, -.018769396836, .000876502539, .001981193736]) elif Par == 10: onFilter = np.array([.001089170447, .000135245020, -.012220642630, -.002072363923, .064950924579, .016418869426, -.225558972234, -.100240215031, .667071338154, 1.088251530500, .542813011213, -.050256540092, -.045240772218, .070703567550, .008152816799, -.028786231926, -.001137535314, .006495728375, .000080661204, -.000649589896]) if Type == 'Vaidyanathan': onFilter = np.array([-.000062906118, .000343631905, -.000453956620, -.000944897136, .002843834547, .000708137504, -.008839103409, .003153847056, .019687215010, -.014853448005, -.035470398607, .038742619293, .055892523691, -.077709750902, -.083928884366, .131971661417, .135084227129, -.194450471766, -.263494802488, .201612161775, .635601059872, .572797793211, .250184129505, .045799334111]) if Type == 'Battle': if Par == 1: onFilterTmp = np.array([0.578163, 0.280931, -0.0488618, -0.0367309, 0.012003, 0.00706442, -0.00274588, -0.00155701, 0.000652922, 0.000361781, -0.000158601, -0.0000867523]) elif Par == 3: onFilterTmp = np.array([0.541736, 0.30683, -0.035498, -0.0778079, 0.0226846, 0.0297468, -0.0121455, -0.0127154, 0.00614143, 0.00579932, -0.00307863, -0.00274529, 0.00154624, 0.00133086, -0.000780468, -0.00065562, 0.000395946, 0.000326749, -0.000201818, -0.000164264, 0.000103307]) elif Par == 5: onFilterTmp = np.array([0.528374, 0.312869, -0.0261771, -0.0914068, 0.0208414, 0.0433544, -0.0148537, -0.0229951, 0.00990635, 0.0128754, -0.00639886, -0.00746848, 0.00407882, 0.00444002, -0.00258816, -0.00268646, 0.00164132, 0.00164659, -0.00104207, -0.00101912, 0.000662836, 0.000635563, -0.000422485, -0.000398759, 0.000269842, 0.000251419, -0.000172685, -0.000159168, 0.000110709, 0.000101113]) onFilter = np.zeros(2*onFilterTmp.size-1) onFilter[onFilterTmp.size-1:2*onFilterTmp.size] = onFilterTmp; onFilter[0:onFilterTmp.size-1] = onFilterTmp[onFilterTmp.size-1:0:-1] return onFilter / np.linalg.norm(onFilter)
def MirrorFilt(
x)
This is a translation of the original Matlab implementation of MirrorFilt.m from the WaveLab850 toolbox.
MirrorFilt -- Apply (-1)^t modulation Usage
h = MirrorFilt(l)
Inputs
l 1-d signal
Outputs
h 1-d signal with DC frequency content shifted to Nyquist frequency
Description
h(t) = (-1)^(t-1) * x(t), 1 <= t <= length(x)
See Also: DyadDownHi
Part of WaveLab850 (http://www-stat.stanford.edu/~wavelab/)
def MirrorFilt(x): """ This is a translation of the original Matlab implementation of MirrorFilt.m from the WaveLab850 toolbox. MirrorFilt -- Apply (-1)^t modulation Usage h = MirrorFilt(l) Inputs l 1-d signal Outputs h 1-d signal with DC frequency content shifted to Nyquist frequency Description h(t) = (-1)^(t-1) * x(t), 1 <= t <= length(x) See Also: DyadDownHi Part of WaveLab850 (http://www-stat.stanford.edu/~wavelab/) """ return np.power(-1,np.arange(x.size))*x """ Copyright (c) 1993. Iain M. Johnstone Part of Wavelab Version 850 Built Tue Jan 3 13:20:40 EST 2006 This is Copyrighted Material For Copying permissions see COPYING.m Comments? e-mail wavelab@stat.stanford.edu """
def dfilters(
fname, type)
This is a translation of the original Matlab implementation of dfilters.m from the Nonsubsampled Contourlet Toolbox. The following comment is from the original and only applies in so far that not all of the directional filters are implemented in this Python version but only those which are needed for the shearlet toolbox.
DFILTERS Generate directional 2D filters
[h0, h1] = dfilters(fname, type)
Input:
fname: Filter name. Available 'fname' are: 'haar': the "Haar" filters 'vk': McClellan transformed of the filter from the VK book 'ko': orthogonal filter in the Kovacevics paper 'kos': smooth 'ko' filter 'lax': 17 x 17 by Lu, Antoniou and Xu 'sk': 9 x 9 by Shah and Kalker 'cd': 7 and 9 McClellan transformed by Cohen and Daubechies 'pkva': ladder filters by Phong et al. 'oqf_362': regular 3 x 6 filter 'dvmlp': regular linear phase biorthogonal filter with 3 dvm 'sinc': ideal filter (*NO perfect recontruction*) 'dmaxflat': diamond maxflat filters obtained from a three stage ladder type: 'd' or 'r' for decomposition or reconstruction filters
Output: h0, h1: diamond filter pair (lowpass and highpass)
To test those filters (for the PR condition for the FIR case), verify that: conv2(h0, modulate2(h1, 'b')) + conv2(modulate2(h0, 'b'), h1) = 2 (replace + with - for even size filters)
To test for orthogonal filter conv2(h, reverse2(h)) + modulate2(conv2(h, reverse2(h)), 'b') = 2
Part of the Nonsubsampled Contourlet Toolbox (http://www.mathworks.de/matlabcentral/fileexchange/10049-nonsubsampled-contourlet-toolbox)
def dfilters(fname, type): """ This is a translation of the original Matlab implementation of dfilters.m from the Nonsubsampled Contourlet Toolbox. The following comment is from the original and only applies in so far that not all of the directional filters are implemented in this Python version but only those which are needed for the shearlet toolbox. DFILTERS Generate directional 2D filters [h0, h1] = dfilters(fname, type) Input: fname: Filter name. Available 'fname' are: 'haar': the "Haar" filters 'vk': McClellan transformed of the filter from the VK book 'ko': orthogonal filter in the Kovacevics paper 'kos': smooth 'ko' filter 'lax': 17 x 17 by Lu, Antoniou and Xu 'sk': 9 x 9 by Shah and Kalker 'cd': 7 and 9 McClellan transformed by Cohen and Daubechies 'pkva': ladder filters by Phong et al. 'oqf_362': regular 3 x 6 filter 'dvmlp': regular linear phase biorthogonal filter with 3 dvm 'sinc': ideal filter (*NO perfect recontruction*) 'dmaxflat': diamond maxflat filters obtained from a three stage ladder type: 'd' or 'r' for decomposition or reconstruction filters Output: h0, h1: diamond filter pair (lowpass and highpass) To test those filters (for the PR condition for the FIR case), verify that: conv2(h0, modulate2(h1, 'b')) + conv2(modulate2(h0, 'b'), h1) = 2 (replace + with - for even size filters) To test for orthogonal filter conv2(h, reverse2(h)) + modulate2(conv2(h, reverse2(h)), 'b') = 2 Part of the Nonsubsampled Contourlet Toolbox (http://www.mathworks.de/matlabcentral/fileexchange/10049-nonsubsampled-contourlet-toolbox) """ if fname == 'haar': if type.lower() == 'd': h0 = np.array([1, 1]) / np.sqrt(2) h1 = np.array([-1, 1]) / np.sqrt(2) else: h0 = np.array([1, 1]) / np.sqrt(2) h1 = np.array([1, -1]) / np.sqrt(2) elif fname == 'vk': # in Vetterli and Kovacevic book if type.lower() == 'd': h0 = np.array([1, 2, 1]) / 4 h1 = np.array([-1, -2, 6, -2, -1]) / 4 else: h0 = np.array([-1, 2, 6, 2, -1]) / 4 h1 = np.array([-1, 2, -1]) / 4 t = np.array([[0, 1, 0], [1, 0, 1], [0, 1, 0]]) / 4 # diamon kernel h0 = mctrans(h0, t) h1 = mctrans(h1, t) elif fname == 'ko': # orthogonal filters in Kovacevics thesis a0 = 2 a1 = 0.5 a2 = 1 h0 = np.array([[0, -a1, -a0*a1, 0], [-a2, -a0*a2, -a0, 1], [0, a0*a1*a2, -a1*a2, 0]]) # h1 = qmf2(h0) h1 = np.array([[0, -a1*a2, -a0*a1*a2, 0], [1, a0, -a0*a2, a2], [0, -a0*a1, a1, 0]]) # normalize filter sum and norm norm = np.sqrt(2) / np.sum(h0) h0 = h0 * norm h1 = h1 * norm if type == 'r': # reverse filters for reconstruction h0 = h0[::-1] h1 = h1[::-1] elif fname == 'kos': # smooth orthogonal filters in Kovacevics thesis a0 = -np.sqrt(3) a1 = -np.sqrt(3) a2 = 2+np.sqrt(3) h0 = np.array([[0, -a1, -a0*a1, 0], [-a2, -a0*a2, -a0, 1], [0, a0*a1*a2, -a1*a2, 0]]) # h1 = qmf2(h0) h1 = np.array([[0, -a1*a2, -a0*a1*a2, 0], [1, a0, -a0*a2, a2], [0, -a0*a1, a1, 0]]) # normalize filter sum and norm norm = np.sqrt(2) / np.sum(h0) h0 = h0 * norm h1 = h1 * norm if type == 'r': # reverse filters for reconstruction h0 = h0[::-1] h1 = h1[::-1] elif fname == 'lax': # by lu, antoniou and xu h = np.array([[-1.2972901e-5, 1.2316237e-4, -7.5212207e-5, 6.3686104e-5, 9.4800610e-5, -7.5862919e-5, 2.9586164e-4, -1.8430337e-4], [1.2355540e-4, -1.2780882e-4, -1.9663685e-5, -4.5956538e-5, -6.5195193e-4, -2.4722942e-4, -2.1538331e-5, -7.0882131e-4], [-7.5319075e-5, -1.9350810e-5, -7.1947086e-4, 1.2295412e-3, 5.7411214e-4, 4.4705422e-4, 1.9623554e-3, 3.3596717e-4], [6.3400249e-5, -2.4947178e-4, 4.4905711e-4, -4.1053629e-3, -2.8588307e-3, 4.3782726e-3, -3.1690509e-3, -3.4371484e-3], [9.6404973e-5, -4.6116254e-5, 1.2371871e-3, -1.1675575e-2, 1.6173911e-2, -4.1197559e-3, 4.4911165e-3, 1.1635130e-2], [-7.6955555e-5, -6.5618379e-4, 5.7752252e-4, 1.6211426e-2, 2.1310378e-2, -2.8712621e-3, -4.8422645e-2, -5.9246338e-3], [2.9802986e-4, -2.1365364e-5, 1.9701350e-3, 4.5047673e-3, -4.8489158e-2, -3.1809526e-3, -2.9406153e-2, 1.8993868e-1], [-1.8556637e-4, -7.1279432e-4, 3.3839195e-4, 1.1662001e-2, -5.9398223e-3, -3.4467920e-3, 1.9006499e-1, 5.7235228e-1] ]) h0 = np.sqrt(2) * np.append(h, h[:,-2::-1], 1) h0 = np.append(h0, h0[-2::-1,:], 0) h1 = modulate2(h0, 'b') elif fname == 'sk': # by shah and kalker h = np.array([[0.621729, 0.161889, -0.0126949, -0.00542504, 0.00124838], [0.161889, -0.0353769, -0.0162751, -0.00499353, 0], [-0.0126949, -0.0162751, 0.00749029, 0, 0], [-0.00542504, 0.00499353, 0, 0, 0], [0.00124838, 0, 0, 0, 0]]) h0 = np.append(h[-1:0:-1, -1:0:-1], h[-1:0:-1,:], 1) h0 = np.append(h0, np.append(h[:,-1:0:-1], h, 1), 0)*np.sqrt(2) h1 = modulate2(h0, 'b') elif fname == 'dvmlp': q = np.sqrt(2) b = 0.02 b1 = b*b; h = np.array([[b/q, 0, -2*q*b, 0, 3*q*b, 0, -2*q*b, 0, b/q], [0, -1/(16*q), 0, 9/(16*q), 1/q, 9/(16*q), 0, -1/(16*q), 0], [b/q, 0, -2*q*b, 0, 3*q*b, 0, -2*q*b, 0, b/q]]) g0 = np.array([[-b1/q, 0, 4*b1*q, 0, -14*q*b1, 0, 28*q*b1, 0, -35*q*b1, 0, 28*q*b1, 0, -14*q*b1, 0, 4*b1*q, 0, -b1/q], [0, b/(8*q), 0, -13*b/(8*q), b/q, 33*b/(8*q), -2*q*b, -21*b/(8*q), 3*q*b, -21*b/(8*q), -2*q*b, 33*b/(8*q), b/q, -13*b/(8*q), 0, b/(8*q), 0], [-q*b1, 0, -1/(256*q) + 8*q*b1, 0, 9/(128*q) - 28*q*b1, -1/(q*16), -63/(256*q) + 56*q*b1, 9/(16*q), 87/(64*q)-70*q*b1, 9/(16*q), -63/(256*q) + 56*q*b1, -1/(q*16), 9/(128*q) - 28*q*b1, 0, -1/(256*q) + 8*q*b1, 0, -q*b1], [0, b/(8*q), 0, -13*b/(8*q), b/q, 33*b/(8*q), -2*q*b, -21*b/(8*q), 3*q*b, -21*b/(8*q), -2*q*b, 33*b/(8*q), b/q, -13*b/(8*q), 0, b/(8*q), 0], [-b1/q, 0, 4*b1*q, 0, -14*q*b1, 0, 28*q*b1, 0, -35*q*b1, 0, 28*q*b1, 0, -14*q*b1, 0, 4*b1*q, 0, -b1/q]]) h1 = modulate2(g0, 'b') h0 = h print(h1.shape) print(h0.shape) if type == 'r': h1 = modulate2(h, 'b') h0 = g0 elif fname == 'cd' or fname == '7-9': # by cohen and Daubechies h0 = np.array([0.026748757411, -0.016864118443, -0.078223266529, 0.266864118443, 0.602949018236, 0.266864118443, -0.078223266529, -0.016864118443, 0.026748757411]) g0 = np.array([-0.045635881557, -0.028771763114, 0.295635881557, 0.557543526229, 0.295635881557, -0.028771763114, -0.045635881557]) if type == 'd': h1 = modulate2(g0, 'c') else: h1 = modulate2(h0, 'c') h0 = g0 # use McClellan to obtain 2D filters t = np.array([[0, 1, 0], [1, 0, 1], [0, 1, 0]])/4 # diamond kernel h0 = np.sqrt(2) * mctrans(h0, t) h1 = np.sqrt(2) * mctrans(h1, t) elif fname == 'oqf_362': h0 = np.sqrt(2) / 64 * np.array([[np.sqrt(15), -3, 0], [0, 5, np.sqrt(15)], [-2*np.sqrt(2), 30, 0], [0, 30, 2*np.sqrt(15)], [np.sqrt(15), 5, 0], [0, -3, -np.sqrt(15)]]) h1 = -modulate2(h0, 'b') h1 = -h1[::-1] if type == 'r': h0 = h0[::-1] h1 = -modulate2(h0, 'b') h1 = -h1[::-1] elif fname == 'test': h0 = np.array([[0, 1, 0], [1, 4, 1], [0, 1, 0]]) h1 = np.array([[0, -1, 0], [-1, 4, -1], [0, -1, 0]]) elif fname == 'testDVM': h0 = np.array([[1, 1], [1, 1]]) / np.sqrt(2) h1 = np.array([[-1, 1], [1, -1]]) / np.sqrt(2) elif fname == 'qmf': # by Lu, antoniou and xu # ideal response window m = 2 n = 2 w1d = np.kaiser(4*m+1, 2.6) w = np.zeros((n+m+1,n+m+1)) for n1 in np.arange(-m,m+1): for n2 in np.arange(-n,n+1): w[n1+m,n2+n] = w1d[2*m+n1+n2]*w1d[2*m+n1-n2] h = np.zeros((n+m+1,n+m+1)) for n1 in np.arange(-m,m+1): for n2 in np.arange(-n,n+1): h[n1+m, n2+n] = 0.5*np.sinc((n1+n2)/2) * 0.5*np.sinc((n1-n2)/2) c = np.sum(h) h = np.sqrt(2) * h h0 = h * w h1 = modulate2(h0, 'b') elif fname == 'qmf2': # by Lu, Antoniou and Xu # ideal response window h = np.array([ [-0.001104, 0.002494, -0.001744, 0.004895, -0.000048, -0.000311], [0.008918, -0.002844, -0.025197, -0.017135, 0.003905, -0.000081], [-0.007587, -0.065904, 00.100431, -0.055878, 0.007023, 0.001504], [0.001725, 0.184162, 0.632115, 0.099414, -0.027006, -0.001110], [-0.017935, -0.000491, 0.191397, -0.001787, -0.010587, 0.002060], [0.001353, 0.005635, -0.001231, -0.009052, -0.002668, 0.000596]]) h0 = h/np.sum(h) h1 = modulate2(h0, 'b') elif fname == 'dmaxflat4': M1 = 1/np.sqrt(2) M2 = np.copy(M1) k1 = 1-np.sqrt(2) k3 = np.copy(k1) k2 = np.copy(M1) h = np.array([0.25*k2*k3, 0.5*k2, 1+0.5*k2*k3])*M1 h = np.append(h, h[-2::-1]) g = np.array([-0.125*k1*k2*k3, 0.25*k1*k2, -0.5*k1-0.5*k3-0.375*k1*k2*k3, 1+0.5*k1*k2])*M2 g = np.append(g, h[-2::-1]) B = dmaxflat(4,0) h0 = mctrans(h,B) g0 = mctrans(g,B) h0 = np.sqrt(2) * h0 / np.sum(h0) g0 = np.sqrt(2) * g0 / np.sum(g0) h1 = modulate2(g0, 'b') if type == 'r': h1 = modulate2(h0, 'b') h0 = g0 elif fname == 'dmaxflat5': M1 = 1/np.sqrt(2) M2 = M1 k1 = 1-np.sqrt(2) k3 = k1 k2 = M1 h = np.array([0.25*k2*k3, 0.5*k2, 1+0.5*k2*k3])*M1 h = np.append(h, h[-2::-1]) g = np.array([-0.125*k1*k2*k3, 0.25*k1*k2, -0.5*k1-0.5*k3-0.375*k1*k2*k3, 1+0.5*k1*k2])*M2 g = np.append(g, h[-2::-1]) B = dmaxflat(5,0) h0 = mctrans(h,B) g0 = mctrans(g,B) h0 = np.sqrt(2) * h0 / np.sum(h0) g0 = np.sqrt(2) * g0 / np.sum(g0) h1 = modulate2(g0, 'b') if type == 'r': h1 = modulate2(h0, 'b') h0 = g0 elif fname == 'dmaxflat6': M1 = 1/np.sqrt(2) M2 = M1 k1 = 1-np.sqrt(2) k3 = k1 k2 = M1 h = np.array([0.25*k2*k3, 0.5*k2, 1+0.5*k2*k3])*M1 h = np.append(h, h[-2::-1]) g = np.array([-0.125*k1*k2*k3, 0.25*k1*k2, -0.5*k1-0.5*k3-0.375*k1*k2*k3, 1+0.5*k1*k2])*M2 g = np.append(g, h[-2::-1]) B = dmaxflat(6,0) h0 = mctrans(h,B) g0 = mctrans(g,B) h0 = np.sqrt(2) * h0 / np.sum(h0) g0 = np.sqrt(2) * g0 / np.sum(g0) h1 = modulate2(g0, 'b') if type == 'r': h1 = modulate2(h0, 'b') h0 = g0 elif fname == 'dmaxflat7': M1 = 1/np.sqrt(2) M2 = M1 k1 = 1-np.sqrt(2) k3 = k1 k2 = M1 h = np.array([0.25*k2*k3, 0.5*k2, 1+0.5*k2*k3])*M1 h = np.append(h, h[-2::-1]) g = np.array([-0.125*k1*k2*k3, 0.25*k1*k2, -0.5*k1-0.5*k3-0.375*k1*k2*k3, 1+0.5*k1*k2])*M2 g = np.append(g, h[-2::-1]) B = dmaxflat(7,0) h0 = mctrans(h,B) g0 = mctrans(g,B) h0 = np.sqrt(2) * h0 / np.sum(h0) g0 = np.sqrt(2) * g0 / np.sum(g0) h1 = modulate2(g0, 'b') if type == 'r': h1 = modulate2(h0, 'b') h0 = g0 # The original file supports a case "otherwise" for unrecognized filters # and computes simple 1D wavelet filters for them using wfilters.m # I think we don't need this and skip this for the time being. # IN ORIGINAL MATLAB VERSION: # otherwise # % Assume the "degenerated" case: 1D wavelet filters # [h0,h1] = wfilters(fname, type); return h0, h1
def dmaxflat(
N, d)
THIS IS A REWRITE OF THE ORIGINAL MATLAB IMPLEMENTATION OF dmaxflat.m FROM THE Nonsubsampled Contourlet Toolbox. -- Stefan Loock, Dec 2016.
returns 2-D diamond maxflat filters of order 'N' the filters are nonseparable and 'd' is the (0,0) coefficient, being 1 or 0 depending on use. by Arthur L. da Cunha, University of Illinois Urbana-Champaign Aug 2004
def dmaxflat(N,d): """ THIS IS A REWRITE OF THE ORIGINAL MATLAB IMPLEMENTATION OF dmaxflat.m FROM THE Nonsubsampled Contourlet Toolbox. -- Stefan Loock, Dec 2016. returns 2-D diamond maxflat filters of order 'N' the filters are nonseparable and 'd' is the (0,0) coefficient, being 1 or 0 depending on use. by Arthur L. da Cunha, University of Illinois Urbana-Champaign Aug 2004 """ if (N > 7) or (N < 1): print('Error: N must be in {1,2,...,7}') return 0 if N == 1: h = np.array([[0, 1, 0],[1, 0, 1],[0, 1, 0]])/4 h[1,1] = d elif N == 2: h = np.array([[0, -1, 0],[-1, 0, 10], [0, 10, 0]]) h = np.append(h, np.fliplr(h[:,0:-1]), 1) h = np.append(h, np.flipud(h[0:-1,:]), 0)/32 h[2,2] = d elif N == 3: h = np.array([[0, 3, 0, 2],[3, 0, -27, 0],[0, -27, 0, 174], [2, 0, 174, 0]]) h = np.append(h, np.fliplr(h[:, 0:-1]), 1) h = np.append(h, np.flipud(h[0:-1,:]),0) h[3,3] = d elif N == 4: h = np.array([[0, -5, 0, -3, 0], [-5, 0, 52, 0, 34], [0, 52, 0, -276, 0], [-3, 0, -276, 0, 1454], [0, 34, 0, 1454, 0]])/np.power(2,12) h = np.append(h, np.fliplr(h[:,0:-1]),1) h = np.append(h, np.flipud(h[0:-1,:]),0) h[4,4] = d elif N == 5: h = np.array([[0, 35, 0, 20, 0, 18], [35, 0, -425, 0, -250, 0], [0, -425, 0, 2500, 0, 1610], [20, 0, 2500, 0, -10200, 0], [0, -250, 0, -10200, 0, 47780], [18, 0, 1610, 0, 47780, 0]])/np.power(2,17) h = np.append(h, np.fliplr(h[:,0:-1]),1) h = np.append(h, np.flipud(h[0:-1,:]),0) h[5,5] = d elif N == 6: h = np.array([[0, -63, 0, -35, 0, -30, 0], [-63, 0, 882, 0, 495, 0, 444], [0, 882, 0, -5910, 0, -3420, 0], [-35, 0, -5910, 0, 25875, 0, 16460], [0, 495, 0, 25875, 0, -89730, 0], [-30, 0, -3420, 0, -89730, 0, 389112], [0, 44, 0, 16460, 0, 389112, 0]])/np.power(2,20) h = np.append(h, np.fliplr(h[:,0:-1]),1) h = np.append(h, np.flipud(h[0:-1,:]),0) h[6,6] = d elif N == 7: h = np.array([[0, 231, 0, 126, 0, 105, 0, 100], [231, 0, -3675, 0, -2009, 0, -1715, 0], [0, -3675, 0, 27930, 0, 15435, 0, 13804], [126, 0, 27930, 0, -136514, 0, -77910, 0], [0, -2009, 0, -136514, 0, 495145, 0, 311780], [105, 0, 15435, 0, 495145, 0, -1535709, 0], [0, -1715, 0, -77910, 0, -1534709, 0, 6305740], [100, 0, 13804, 0, 311780, 0, 6305740, 0]])/np.power(2,24) h = np.append(h, np.fliplr(h[:,0:-1]),1) h = np.append(h, np.flipud(h[0:-1,:]),0) h[7,7] = d return h
def mctrans(
b, t)
This is a translation of the original Matlab implementation of mctrans.m from the Nonsubsampled Contourlet Toolbox by Arthur L. da Cunha.
MCTRANS McClellan transformation
H = mctrans(B,T)
produces the 2-D FIR filter H that corresponds to the 1-D FIR filter B using the transform T.
Convert the 1-D filter b to SUM_n a(n) cos(wn) form
Part of the Nonsubsampled Contourlet Toolbox (http://www.mathworks.de/matlabcentral/fileexchange/10049-nonsubsampled-contourlet-toolbox)
def mctrans(b,t): """ This is a translation of the original Matlab implementation of mctrans.m from the Nonsubsampled Contourlet Toolbox by Arthur L. da Cunha. MCTRANS McClellan transformation H = mctrans(B,T) produces the 2-D FIR filter H that corresponds to the 1-D FIR filter B using the transform T. Convert the 1-D filter b to SUM_n a(n) cos(wn) form Part of the Nonsubsampled Contourlet Toolbox (http://www.mathworks.de/matlabcentral/fileexchange/10049-nonsubsampled-contourlet-toolbox) """ # Convert the 1-D filter b to SUM_n a(n) cos(wn) form # if mod(n,2) != 0 -> error n = (b.size-1)//2 b = np.fft.fftshift(b[::-1]) #inverse fftshift b = b[::-1] a = np.zeros(n+1) a[0] = b[0] a[1:n+1] = 2*b[1:n+1] inset = np.floor((np.asarray(t.shape)-1)/2) inset = inset.astype(int) # Use Chebyshev polynomials to compute h P0 = 1 P1 = t; h = a[1]*P1; rows = int(inset[0]+1) cols = int(inset[1]+1) h[rows-1,cols-1] = h[rows-1,cols-1]+a[0]*P0; for i in range(3,n+2): P2 = 2*signal.convolve2d(t, P1) rows = (rows + inset[0]).astype(int) cols = (cols + inset[1]).astype(int) if i == 3: P2[rows-1,cols-1] = P2[rows-1,cols-1] - P0 else: P2[rows[0]-1:rows[-1],cols[0]-1:cols[-1]] = P2[rows[0]-1:rows[-1], cols[0]-1:cols[-1]] - P0 rows = inset[0] + np.arange(np.asarray(P1.shape)[0])+1 rows = rows.astype(int) cols = inset[1] + np.arange(np.asarray(P1.shape)[1])+1 cols = cols.astype(int) hh = h; h = a[i-1]*P2 h[rows[0]-1:rows[-1], cols[0]-1:cols[-1]] = h[rows[0]-1:rows[-1], cols[0]-1:cols[-1]] + hh P0 = P1; P1 = P2; h = np.rot90(h,2) return h
def modulate2(
x, type, center=array([0, 0]))
THIS IS A REWRITE OF THE ORIGINAL MATLAB IMPLEMENTATION OF modulate2.m FROM THE Nonsubsampled Contourlet Toolbox.
MODULATE2 2D modulation
y = modulate2(x, type, [center])
With TYPE = {'r', 'c' or 'b'} for modulate along the row, or column or both directions.
CENTER secify the origin of modulation as floor(size(x)/2)+1+center (default is [0, 0])
Part of the Nonsubsampled Contourlet Toolbox (http://www.mathworks.de/matlabcentral/fileexchange/10049-nonsubsampled-contourlet-toolbox)
def modulate2(x, type, center=np.array([0, 0])): """ THIS IS A REWRITE OF THE ORIGINAL MATLAB IMPLEMENTATION OF modulate2.m FROM THE Nonsubsampled Contourlet Toolbox. MODULATE2 2D modulation y = modulate2(x, type, [center]) With TYPE = {'r', 'c' or 'b'} for modulate along the row, or column or both directions. CENTER secify the origin of modulation as floor(size(x)/2)+1+center (default is [0, 0]) Part of the Nonsubsampled Contourlet Toolbox (http://www.mathworks.de/matlabcentral/fileexchange/10049-nonsubsampled-contourlet-toolbox) """ size = np.asarray(x.shape) if x.ndim == 1: if np.array_equal(center, [0, 0]): center = 0 origin = np.floor(size/2)+1+center n1 = np.arange(size[0])-origin[0]+1 if x.ndim == 2: n2 = np.arange(size[1])-origin[1]+1 else: n2 = n1 if type == 'r': m1 = np.power(-1,n1) if x.ndim == 1: y = x*m1 else: y = x * np.transpose(np.tile(m1, (size[1], 1))) elif type == 'c': m2 = np.power(-1,n2) if x.ndim == 1: y = x*m2 else: y = x * np.tile(m2, np.array([size[0], 1])) elif type == 'b': m1 = np.power(-1,n1) m2 = np.power(-1,n2) m = np.outer(m1, m2) if x.ndim == 1: y = x * m1 else: y = x * m return y