-
Notifications
You must be signed in to change notification settings - Fork 7
/
createWavelets.jl
311 lines (268 loc) · 10.2 KB
/
createWavelets.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
"""
mother(this::CWT{W, T, <:ContWaveClass, N}, s::Real, nInOctave::Int,
ω::AbstractArray{<:Real,1}) where {W, T, N} -> daughter
Given a CWT object, return a rescaled version of the mother wavelet, in the
fourier domain. ω is the frequency, which is fftshift-ed. s is the scale
variable.
"""
function mother(this::CWT{W,T,Morlet,N},
s::Real,
sWidth::Real,
ω::AbstractArray{<:Real,1}) where {W,T,N}
constant = this.σ[3] * (π)^(1 / 4)
gauss = exp.(-(this.σ[1] .- ω / s) .^ 2 / (sWidth^2))
shift = this.σ[2] * exp.(-1 / 2 * (ω / s) .^ 2)
daughter = constant .* (gauss .- shift)
return normalize(daughter, s, this.p)
end
function mother(this::CWT{W,T,<:Paul,N},
s::Real,
sWidth::Real,
ω::AbstractArray{<:Real,1}) where {W,T,N}
daughter = zeros(length(ω))
constant = (2^this.α) / sqrt((this.α) * gamma(2 * (this.α)))
polynomial = (ω[ω.>=0] / s) .^ (this.α)
expDecay = exp.(-(ω[ω.>=0] / s))
daughter[ω.>=0] = constant .* polynomial .* expDecay
return normalize(daughter, s, this.p)
end
function mother(this::CWT{W,T,<:Dog,N},
s::Real,
sWidth::Real,
ω::AbstractArray{<:Real,1}) where {W,T,N}
constant = -(-im)^(this.α) / sqrt(gamma((this.α) + 1 / 2))
polynomial = (ω / s) .^ (this.α)
gauss = exp.(-(ω / s) .^ 2 / 2)
daughter = constant .* polynomial .* gauss
return normalize(daughter, s, this.p)
end
function mother(this::CWT{W,T,Morse,N},
s::Real,
sWidth::Real,
ω::AbstractArray{<:Real,1}) where {W,T,N}
ga = this.waveType.ga
be = this.waveType.be
cf = this.waveType.cf
p = this.p
fo = morsefreq(this)
fact = cf / fo
# ω = LinRange(0,1-(1/len),len)
# om = 2 * pi * ω./ fact / max(1, s)
#om = 2 * pi * (ω / s)./ fact
# om = (ω / s) / cf
# om = (ω / s) / fact
om = ω / s
if be == 0
daughter = @. 2 * exp(-om^ga)
else
daughter = @. 2 * exp(-be * log(fo) + fo^ga + be * log(om) - om^ga)
end
daughter[1] = 1 / 2 * daughter[1] # Due to unit step function
# Ensure nice lowpass filters for beta=0;
# Otherwise, doesn't matter since wavelets vanishes at zero frequency
if any(daughter .!= daughter)
@warn "the given values of gamma and beta are numerically unstable"
daughter[daughter.!=daughter] .= 0
end
return ContinuousWavelets.normalize(daughter, s, p)
end
function mother(this::CWT{W,T,<:ContOrtho,N},
s::Real,
itpψ,
ω::AbstractArray{<:Real,1},
n,
n1) where {W,T,N}
daughter = itpψ(range(1, stop = length(itpψ), step = length(itpψ) / n1 * s))
daughter = padTo(daughter, n)
daughter = circshift(daughter, -round(Int, n1 / s / 2))
return daughter
end
function normalize(daughter, s, p)
if isinf(p)
normTerm = maximum(abs.(daughter))
else
normTerm = s^(1 / p)
end
return daughter ./ normTerm
end
"""
father(c::CWT, ω, averagingType::aT) where {aT<:Averaging}
This creates the averaging function, which covers the low frequency
information, and is emphatically not analytic. `aT` determines whether it has the
same form as the wavelets (`Father`), or just a bandpass `Dirac`.
`c.averagingLength` gives the number of octaves (base 2) that are covered by
the averaging function. The width is then derived so that it matches the next
wavelet at 1σ.
For the Morlet wavelet, the distribution is just a Gaussian, so it has variance
1/s^2 and mean σ[1]*s set the variance so that the averaging function has σ/2 at
the central frequency of the last scale.
For the Paul wavelets, it's a easy calculation to see that the mean of a paul
wavelet of order m is (m+1)/s, while σ=sqrt(m+1)/s. So we set the variance so
that the averaging function has 1σ at the central frequency of the last scale.
The derivative of a Gaussian (Dog) has a pretty nasty form for the mean and
variance; eventually, if you set 1/2 * σ_{averaging}=⟨ω⟩_{highest scale wavelet}, you
will get the scale of the averaging function to be
`s*gamma((c.α+2)/2)/sqrt(gamma((c.α+1)/2)*(gamma((c.α+3)/2)-gamma((c.α+2)/2)))`
"""
function father(c::CWT, ω, averagingType::Father, sWidth)
s = 2^(getMinScaling(c) + c.averagingLength - 1)
s0, ω_shift = locationShift(c, s, ω, 1)
averaging = adjust(c) .* mother(c, s0, 1, ω_shift)
end
function father(c::CWT{W,T,<:Dog,N}, ω, averagingType::Father, sWidth) where {W,T,N}
s = 2^(getMinScaling(c) + c.averagingLength - 1)
s0, ω_shift = locationShift(c, s, ω, 1)
averaging = -adjust(c) .* mother(c, s0, 1, ω_shift)
end
function father(c::CWT{B,T,W},
ω,
averagingType::Father,
fullVersion,
s,
N,
n1) where {W<:ContOrtho,B,T}
itp = genInterp(fullVersion)
φ = itp(range(1, stop = length(fullVersion), step = length(fullVersion) / n1 * s))
φ = circshift(padTo(φ, N), -round(Int, n1 / s / 2))
end
# dirac version (that is, just a window around zero)
function father(c::CWT{<:WaveletBoundary,T}, ω, averagingType::Dirac, sWidth) where {T}
s = 2^(getMinScaling(c) + c.averagingLength)
averaging = zeros(T, size(ω))
upperBound = getUpperBound(c, s)
averaging[abs.(ω).<=upperBound] .= 1
return averaging
end
function father(c::CWT{W,T,<:Morse},
ω,
averagingType::ContinuousWavelets.Father,
sWidth) where {W,T}
s = 2^(getMinScaling(c) + c.averagingLength - 1)
s0, ω_shift = locationShift(c, s, ω, sWidth)
averaging = adjust(c) .* mother(c, s0, sWidth, ω_shift)
return averaging
end
@doc """
computeWavelets(n1::Integer, c::CWT{B,CT,W}; T = Float64, space = false) where {B<:WaveletBoundary,W,CT} -> daughters, ω
Precomputes the wavelets used by transform. For details, see cwt.
"""
function computeWavelets(n1::Integer,
c::CWT{B,CT,W};
T = Float64,
space = false) where {B<:WaveletBoundary,W,CT}
nOctaves, totalWavelets, sRange, sWidth = getNWavelets(n1, c)
# padding determines the actual number of elements
n, nSpace = setn(n1, c)
# indicates whether we should keep a spot for the father wavelet
isAve = !(typeof(c.averagingType) <: NoAve)
# I guess matlab did occasionally do something useful
ω = computeOmega(n1, nSpace, n)
daughters = analyticOrNot(c, n, totalWavelets)
# if the nOctaves is small enough there are none not covered by the
# averaging, just use that
if totalWavelets == 1
@warn "an averaging length of $(c.averagingLength) results in fewer octaves than the minimum frequency $(getMinScaling(c)) for this wavelet type. Either decreasing the averagingLength or increase extraOctaves"
onlyFather = father(c, ω, c.averagingType, sWidth[1])
return onlyFather, ω
end
for (curWave, s) in enumerate(sRange)
daughters[:, curWave+isAve] = mother(c, s, sWidth[curWave], ω)
end
if isAve # should we include the father?
@debug "c = $(c), $(c.averagingType), size(ω)= $(size(ω))"
daughters[:, 1] = father(c, ω, c.averagingType, sWidth[1])# [1:(n1+1)]
end
# adjust by the frame bound
if c.frameBound > 0
daughters = daughters .* (c.frameBound / norm(daughters, 2))
end
testFourierDomainProperties(daughters, isAve)
if space
x = zeros(T, n1)
x[ceil(Int, n1 / 2)] = 1
return cwt(x, c, daughters), ω
else
return (daughters, ω)
end
end
function computeWavelets(n1::Integer,
c::CWT{B,CT,W};
T = Float64,
space = false) where {B<:WaveletBoundary,W<:ContOrtho,CT}
# padding determines the actual number of elements
n, nSpace = setn(n1, c)
nOctaves, totalWavelets, sRange, sWidth = getNWavelets(n1, c)
# indicates whether we should keep a spot for the father wavelet
isAve = !(typeof(c.averagingType) <: NoAve)
ω = computeOmega(n1, nSpace, n)
daughters = zeros(nSpace, totalWavelets)
φ, ψ, ψLen = getContWaveFromOrtho(c, nSpace)
itpψ = genInterp(ψ)
# if the nOctaves is small enough there are none not covered by the
# averaging, so just use that
if totalWavelets == 1
onlyFather = father(c, ω, c.averagingType, φ, c.averagingLength, nSpace, n1)
return rfft(onlyFather, 1), ω
end
for (curWave, s) in enumerate(sRange)
daughters[:, curWave+isAve] = mother(c, s, itpψ, ω, nSpace, n1)
end
if isAve
daughters[:, 1] = father(c, ω, c.averagingType, φ, sRange[1], nSpace, n1)
end
# switch to fourier domain and normalize appropriately
daughters = rfft(daughters, 1)
repeatSRange = [sRange[1:isAve]...; sRange...]
for i = 1:size(daughters, 2)
daughters[:, i] = normalize(daughters[:, i], repeatSRange[i], c.p)
end
# adjust by the frame bound
if c.frameBound > 0
daughters = daughters .* (c.frameBound / norm(daughters, 2))
end
testFourierDomainProperties(daughters, isAve)
if space
x = zeros(T, n1)
x[ceil(Int, n1 / 2)] = 1
return cwt(x, c, daughters), ω
else
return (daughters, ω)
end
end
# not
function analyticOrNot(c::CWT{W,T,<:Union{Dog,ContOrtho},N}, n, totalWavelets) where {W,T,N}
# Odd derivatives (and any orthogonal wavelets) introduce an imaginary
# term, so we need a complex representation
if abs(c.α % 2) == 1
daughters = zeros(Complex{T}, n, totalWavelets)
else
daughters = zeros(T, n, totalWavelets)
end
return daughters
end
function analyticOrNot(c::CWT{W,T,<:Union{Morlet,Paul,Morse},N},
n,
totalWavelets) where {W,T,N}
daughters = zeros(T, n, totalWavelets)
return daughters
end
function computeOmega(nOriginal, nSpace, nFreq)
range(0, nOriginal >> 1 + 1, length = nFreq) # max size is the last frequency in the rfft of the original data size
end
# convenience methods
# it's ok to just hand the total size, even if we're not transforming across
# all dimensions
function computeWavelets(Y::Tuple, c::CWT{W}; T = Float64) where {W<:WaveletBoundary}
return computeWavelets(Y[1], c; T = T)
end
function computeWavelets(Y::AbstractArray{<:Integer},
c::CWT{W};
T = Float64) where {W<:WaveletBoundary}
return computeWavelets(Y[1], c, T = T)
end
# also ok to just hand the whole thing being transformed
function computeWavelets(Y::AbstractArray{<:Number},
c::CWT{W};
T = Float64) where {W<:WaveletBoundary}
return computeWavelets(size(Y)[1], c, T = T)
end