Skip to content

Commit 8d2ba49

Browse files
allowed type system to infer array types and added filters
1 parent 04bb0c8 commit 8d2ba49

File tree

5 files changed

+116
-17
lines changed

5 files changed

+116
-17
lines changed

Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1111
NDTools = "98581153-e998-4eef-8d0d-5ec2c052313d"
1212
NFFT = "efe261a4-0d2b-5849-be55-fc731d526b0d"
1313
PaddedViews = "5432bcbf-9aad-5242-b902-cca2824c8663"
14+
SeparableFunctions = "c8c7ead4-852c-491e-a42d-3d43bc74259e"
1415
ShiftedArrays = "1277b4bf-5013-50f5-be3d-901d8477a67a"
1516

1617
[compat]

src/FourierTools.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ include("resampling.jl")
1515
include("custom_fourier_types.jl")
1616
include("fourier_resizing.jl")
1717
include("fourier_shifting.jl")
18+
include("fourier_filtering.jl")
1819
include("fourier_resample_1D_based.jl")
1920
include("fourier_rotate.jl")
2021
include("fourier_shear.jl")

src/fourier_filtering.jl

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
export fourier_filter!, fourier_filter
2+
export filter_gaussian # , filter_gaussian2
3+
4+
"""
5+
"""
6+
function fourier_filter!(arr::AbstractArray{<:Complex, N}, fct=window_gaussian; border_in=0.0, border_out=1.0) where {N}
7+
return fourier_filter_by_1D_FT!(arr, fct; border_in=border_in, border_out=border_out)
8+
end
9+
10+
function fourier_filter!(arr::AbstractArray{<:Int, N}, fct=window_gaussian; kwargs...) where {N}
11+
throw(ArgumentError("FFTW.jl does not accept AbstractArrays{<:Int, N}. Convert array to a Real/Complex."))
12+
end
13+
14+
function fourier_filter!(arr::AbstractArray{<:Real, N}, fct=window_gaussian; border_in=0.0, border_out=1.0) where {N}
15+
return fourier_filter_by_1D_RFT!(arr, fct; border_in=border_in, border_out=border_out)
16+
end
17+
18+
"""
19+
fourier_filter(arr, limits)
20+
21+
Returning a fourier_filtered array.
22+
See [`fourier_filter!`](@ref fourier_filter!) for more details
23+
"""
24+
function fourier_filter(arr, fct=window_gaussian; border_in=0.0, border_out=1.0)
25+
return fourier_filter!(copy(arr), fct; border_in=border_in, border_out=border_out)
26+
end
27+
28+
function fourier_filter_by_1D_FT!(arr::TA, fct=window_gaussian; border_in=0.0, border_out=1.0, dims=(1:ndims(arr))) where {N, TA <: AbstractArray{<:Complex, N}}
29+
# iterates of the dimension d using the corresponding shift
30+
sz = size(arr)
31+
for d in dims #(d, limit) in pairs(limits)
32+
w = TA(ifftshift(fct(real(eltype(arr)), select_sizes(arr,d), border_in=border_in, border_out=border_out)))
33+
#w = TA(collect(ifftshift(fct(real(eltype(arr)), select_sizes(arr,d), border_in=border_in, border_out=border_out))))
34+
# go to fourier space and apply w
35+
fft!(arr, d)
36+
arr .*= w
37+
end
38+
39+
ifft!(arr, dims)
40+
return arr
41+
end
42+
43+
# the idea is the following:
44+
# rfft(x, 1) -> exp shift -> fft(x, 2) -> exp shift -> fft(x, 3) -> exp shift -> ifft(x, [2,3]) -> irfft(x, 1)
45+
# So once we did a rft to shift something we can call the routine for complex arrays to shift
46+
function fourier_filter_by_1D_RFT!(arr::TA, fct=window_gaussian; border_in=0.0, border_out=1.0) where {T<:Real, N, TA<:AbstractArray{T, N}}
47+
sz = size(arr)
48+
for d = 1:length(sz) #(d, limit) in pairs(limits)
49+
p = plan_rfft(arr, d)
50+
51+
arr_ft = p * arr
52+
w = TA(fct(real(eltype(arr)), select_sizes(arr_ft,d), offset=CtrRFFT, scale=2 ./size(arr,d), border_in=border_in, border_out=border_out))
53+
# w = TA(collect(fct(real(eltype(arr)), select_sizes(arr_ft,d), offset=CtrRFFT, scale=2 ./size(arr,d), border_in=border_in, border_out=border_out)))
54+
arr_ft .*= w
55+
# since we now did a single rfft dim, we can switch to the complex routine
56+
# new_limits = ntuple(i -> i ≤ d ? 0 : limits[i], N)
57+
# workaround to mimic in-place rfft
58+
fourier_filter_by_1D_FT!(arr_ft; border_in=border_in, border_out=border_out, dims=(2:ndims(arr_ft)))
59+
# go back to real space now and return because shift_by_1D_FT processed
60+
# the other dimensions already
61+
mul!(arr, inv(p), arr_ft)
62+
return arr # this breaks the for loop and finishes the algorithm
63+
end
64+
return arr
65+
end
66+
67+
function filter_gaussian(arr, sigma=eltype(arr)(1))
68+
sigma_k = 2 ./ (pi .* sigma)
69+
fourier_filter(arr, border_out=sigma_k)
70+
end
71+
72+
# Suggested code by Felix. But it is not as fast as the code above:
73+
#
74+
# function filter_gaussian2(img::AbstractArray{T};
75+
# sigma=one(T), dims=1:ndims(img)) where (T <: Real)
76+
# shiftdims = dims[2:end]
77+
# f = rfft(img, dims)
78+
# return irfft(f .* ifftshift(gaussian(T, size(f),
79+
# offset=CtrRFT,
80+
# sigma=size(img) ./ (T(2π) .*sigma)),
81+
# shiftdims),
82+
# size(img, dims[1]), dims)
83+
# end

src/fourier_shear.jl

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,17 @@ There is also `shear!` available.
1616
+ `fix_nyquist`: apply a fix to the highest frequency during the Fourier-space application of the exponential factor
1717
+ `adapt_size`: if true, pad the data prior to the shear. The result array will be larger
1818
+ `pad_value`: the value to pad with (only applies if `adapt_size=true`)
19+
+ `assign_wrap=assign_wrap`: replaces wrap-around areas by `pad_value` (only of `adapt_size` is `false`)
1920
2021
For complex arrays we use `fft`, for real array we use `rfft`.
2122
"""
22-
function shear(arr::AbstractArray, Δ, shear_dir_dim=1, shear_dim=2; fix_nyquist=false, adapt_size=false::Bool, pad_value=zero(eltype(arr)))
23+
function shear(arr::AbstractArray, Δ, shear_dir_dim=1, shear_dim=2; fix_nyquist=false, assign_wrap=false, adapt_size=false::Bool, pad_value=zero(eltype(arr)))
2324
if adapt_size
2425
ns = Tuple(d == shear_dir_dim ? size(arr,d)+ceil(Int,abs.(Δ)) : size(arr,d) for d in 1:ndims(arr))
2526
arr2 = collect(select_region(arr, new_size=ns, pad_value=pad_value))
2627
return shear!(arr2, Δ, shear_dir_dim, shear_dim, fix_nyquist=fix_nyquist)
2728
else
28-
return shear!(copy(arr), Δ, shear_dir_dim, shear_dim, fix_nyquist=fix_nyquist)
29+
return shear!(copy(arr), Δ, shear_dir_dim, shear_dim, fix_nyquist=fix_nyquist, assign_wrap=assign_wrap, pad_value=pad_value)
2930
end
3031
end
3132

@@ -40,11 +41,13 @@ For more details see `shear.`
4041
For complex arrays we can completely avoid large memory allocations.
4142
For real arrays, we need at least allocate on array in the fourier space.
4243
"""
43-
function shear!(arr::AbstractArray{<:Complex, N}, Δ, shear_dir_dim=1, shear_dim=2; fix_nyquist=false, assign_wrap=false, pad_value=zero(eltype(arr))) where N
44+
function shear!(arr::TA, Δ, shear_dir_dim=1, shear_dim=2; fix_nyquist=false, assign_wrap=false, pad_value=zero(eltype(arr))) where {N, TA<:AbstractArray{<:Complex, N}}
4445
fft!(arr, shear_dir_dim)
4546

4647
# stores the maximum amount of shift
47-
shift = reshape(fftfreq(size(arr, shear_dir_dim)), NDTools.select_sizes(arr, shear_dir_dim))
48+
TR = real_arr_type(TA)
49+
# shift = TR(reshape(fftfreq(size(arr, shear_dir_dim)), NDTools.select_sizes(arr, shear_dir_dim)))
50+
shift = TR(reorient(fftfreq(size(arr, shear_dir_dim)),shear_dir_dim, Val(N)))
4851

4952
apply_shift_strength!(arr, arr, shift, shear_dir_dim, shear_dim, Δ, fix_nyquist)
5053

@@ -56,12 +59,14 @@ function shear!(arr::AbstractArray{<:Complex, N}, Δ, shear_dir_dim=1, shear_dim
5659
return arr
5760
end
5861

59-
function shear!(arr::AbstractArray{<:Real, N}, Δ, shear_dir_dim=1, shear_dim=2; fix_nyquist=false, assign_wrap=false, pad_value=zero(eltype(arr))) where N
62+
function shear!(arr::TA, Δ, shear_dir_dim=1, shear_dim=2; fix_nyquist=false, assign_wrap=false, pad_value=zero(eltype(arr))) where {N, TA<:AbstractArray{<:Real, N}}
6063
p = plan_rfft(arr, shear_dir_dim)
6164
arr_ft = p * arr
6265

6366
# stores the maximum amount of shift
64-
shift = reshape(rfftfreq(size(arr, shear_dir_dim)), NDTools.select_sizes(arr_ft, shear_dir_dim))
67+
TR = real_arr_type(TA)
68+
# shift = TR(reshape(rfftfreq(size(arr, shear_dir_dim)), NDTools.select_sizes(arr_ft, shear_dir_dim)))
69+
shift = TR(reorient(rfftfreq(size(arr, shear_dir_dim)),shear_dir_dim, Val(N)))
6570

6671
apply_shift_strength!(arr_ft, arr, shift, shear_dir_dim, shear_dim, Δ, fix_nyquist)
6772
# go back to real space
@@ -103,10 +108,12 @@ function assign_shear_wrap!(arr, Δ, shear_dir_dim=1, shear_dim=2, pad_value=zer
103108
end
104109
end
105110

106-
function apply_shift_strength!(arr, arr_orig, shift, shear_dir_dim, shear_dim, Δ, fix_nyquist=false)
111+
function apply_shift_strength!(arr::TA, arr_orig, shift, shear_dir_dim, shear_dim, Δ, fix_nyquist=false) where {T, N, TA<:AbstractArray{T, N}}
107112
#applies the strength to each slice
108-
shift_strength = reshape(fftpos(1, size(arr, shear_dim), CenterFT), NDTools.select_sizes(arr, shear_dim))
109-
113+
# shift_strength = reshape(fftpos(1, size(arr, shear_dim), CenterFT), NDTools.select_sizes(arr, shear_dim))
114+
# TR = real_arr_type(typeof(collect(arr[1:1]))) # There is a problem with circshifted arrays and this way of finding the type.
115+
shift_strength = reorient(fftpos(1, size(arr, shear_dim), CenterFT), shear_dim, Val(N))
116+
110117
# do the exp multiplication in place
111118
e = cispi.(2 .* Δ .* shift .* shift_strength)
112119
# for even arrays we need to fix real property of highest frequency

src/fourier_shifting.jl

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,17 @@ function soft_shift(freqs, shift, fraction=eltype(freqs)(0.1); corner=false)
8080
return cispi.(-freqs .* 2 .* (w .* shift + (1.0 .-w).* rounded_shift))
8181
end
8282

83-
function shift_by_1D_FT!(arr::AbstractArray{<:Complex, N}, shifts; soft_fraction=0, take_real=false, fix_nyquist_frequency=false) where {N}
83+
function shift_by_1D_FT!(arr::TA, shifts; soft_fraction=0, take_real=false, fix_nyquist_frequency=false) where {N, TA<:AbstractArray{<:Complex, N}}
8484
# iterates of the dimension d using the corresponding shift
8585
for (d, shift) in pairs(shifts)
8686
if iszero(shift)
8787
continue
8888
end
8989
# better use reorient from NDTools here?
90-
freqs = reshape(fftfreq(size(arr, d)), ntuple(i -> 1, Val(d-1))..., size(arr,d))
90+
TR = real_arr_type(TA)
91+
freqs = TR(reorient(fftfreq(size(arr, d)),d, Val(N)))
92+
# freqs = TR(reshape(fftfreq(size(arr, d)), ntuple(i -> 1, Val(d-1))..., size(arr,d)))
93+
# @show size(freqs)
9194
# allocates a 1D slice of exp values
9295
if iszero(soft_fraction)
9396
ϕ = cispi.(- freqs .* 2 .* shift)
@@ -126,14 +129,21 @@ end
126129
# the idea is the following:
127130
# rfft(x, 1) -> exp shift -> fft(x, 2) -> exp shift -> fft(x, 3) -> exp shift -> ifft(x, [2,3]) -> irfft(x, 1)
128131
# So once we did a rft to shift something we can call the routine for complex arrays to shift
129-
function shift_by_1D_RFT!(arr::AbstractArray{<:Real, N}, shifts; soft_fraction=0, fix_nyquist_frequency=false, take_real=true) where {T, N}
132+
function shift_by_1D_RFT!(arr::TA, shifts; soft_fraction=0, fix_nyquist_frequency=false, take_real=true) where {N, TA<:AbstractArray{<:Real, N}}
130133
for (d, shift) in pairs(shifts)
131134
if iszero(shift)
132135
continue
133136
end
134137

135-
s = size(arr, d) ÷ 2 + 1
136-
freqs = reshape(fftfreq(size(arr, d))[1:s], ntuple(i -> 1, d-1)..., s)
138+
p = plan_rfft(arr, d)
139+
arr_ft = p * arr
140+
141+
#s1 = select_sizes(arr_ft, d)
142+
s = size(arr_ft,d); # s1[d]
143+
# @show size(arr, d) ÷ 2 + 1
144+
# freqs = TR(reshape(fftfreq(size(arr, d))[1:s], ntuple(i -> 1, d-1)..., s1))
145+
TR = real_arr_type(TA)
146+
freqs = TR(reorient(fftfreq(size(arr, d))[1:s],d, Val(N)))
137147
if iszero(soft_fraction)
138148
ϕ = cispi.(-freqs .* 2 .* shift)
139149
else
@@ -146,9 +156,6 @@ function shift_by_1D_RFT!(arr::AbstractArray{<:Real, N}, shifts; soft_fraction=0
146156
invr = isinf(invr) ? 0 : invr
147157
ϕ[s] = fix_nyquist_frequency ? invr : ϕ[s]
148158
end
149-
p = plan_rfft(arr, d)
150-
151-
arr_ft = p * arr
152159
arr_ft .*= ϕ
153160
# since we now did a single rfft dim, we can switch to the complex routine
154161
new_shifts = ntuple(i -> i d ? 0 : shifts[i], N)

0 commit comments

Comments
 (0)