Skip to content

Commit 87352d2

Browse files
authored
Merge pull request #30 from bionanoimaging/FourierJoin_fix
Fourier join fix
2 parents 11467f8 + 69a9b59 commit 87352d2

File tree

10 files changed

+151
-82
lines changed

10 files changed

+151
-82
lines changed

src/correlations.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
export ccorr
22

3-
43
"""
54
ccorr(u, v[, dims]; centered=false)
65
@@ -13,7 +12,6 @@ If either `u` or `v` is complex we use `fft` and output is hence complex.
1312
1413
Per default the correlation is performed along `min(ndims(u), ndims(v))`.
1514
16-
1715
```jldoctest
1816
julia> ccorr([1,1,0,0], [1,1,0,0], centered=true)
1917
4-element Vector{Float64}:

src/custom_fourier_types.jl

Lines changed: 40 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,31 @@
1-
## This View checks for the index to be L1 or the mirrored version (L2)
2-
# and then replaces the value by half of the parent at L1
1+
"""
2+
FourierSplit{T,N, AA<:AbstractArray{T, N}} <: AbstractArray{T,N}
3+
4+
This View checks for the index to be L1 or the mirrored version (L2)
5+
and then replaces the value by half of the parent at L1
6+
`do_split` is a Bool that indicates whether this mechanism is active.
7+
It is needed for type stability reasons of functions returnting this type.
8+
"""
39
struct FourierSplit{T,N, AA<:AbstractArray{T, N}} <: AbstractArray{T,N}
410
parent::AA # holds the data (or is another view)
511
D::Int # dimension along which to apply to copy
612
L1::Int # low index position to copy from (and half)
713
L2::Int # high index positon to copy to (and half)
14+
do_split::Bool
15+
16+
"""
17+
FourierSplit(parent::AA, D::Int,L1::Int,L2::Int, do_split::Bool) where {T,N, AA<:AbstractArray{T, N}}
818
9-
# This version below is needed to avoid a split for the firs rft dimension but still return half the value
10-
# FFTs and other RFT dimension should use the version without L2
11-
function FourierSplit(parent::AA, D::Int,L1::Int,L2::Int) where {T,N, AA<:AbstractArray{T, N}}
12-
return new{T,N, AA}(parent, D, L1, L2)
19+
This version below is needed to avoid a split for the first rft dimension,
20+
but still return half the value FFTs and other RFT dimension should use the version without L2.
21+
"""
22+
function FourierSplit(parent::AA, D::Int,L1::Int,L2::Int, do_split::Bool) where {T,N, AA<:AbstractArray{T, N}}
23+
return new{T,N, AA}(parent, D, L1, L2, do_split)
1324
end
14-
function FourierSplit(parent::AA, D::Int,L1::Int) where {T,N, AA<:AbstractArray{T, N}}
25+
function FourierSplit(parent::AA, D::Int, L1::Int, do_split::Bool) where {T,N, AA<:AbstractArray{T, N}}
1526
mid = fft_center(size(parent)[D])
1627
L2 = mid + (mid-L1)
17-
return FourierSplit(parent, D,L1,L2)
28+
return FourierSplit(parent, D,L1,L2, do_split)
1829
end
1930
end
2031

@@ -25,7 +36,7 @@ Base.parent(A::FourierSplit) = A.parent
2536
Base.size(A::FourierSplit) = size(parent(A))
2637

2738
@inline function Base.getindex(A::FourierSplit{T,N, <:AbstractArray{T, N}}, i::Vararg{Int,N}) where {T,N}
28-
if i[A.D]==A.L2 || i[A.D]==A.L1 # index along this dimension A.D corrsponds to slice L2
39+
if (i[A.D]==A.L2 || i[A.D]==A.L1) && A.do_split # index along this dimension A.D corrsponds to slice L2
2940
# not that "setindex" in the line below modifies only the index, not the array
3041
@inbounds return parent(A)[Base.setindex(i,A.L1, A.D)...] / 2
3142
else i[A.D]==A.L2
@@ -34,34 +45,45 @@ Base.size(A::FourierSplit) = size(parent(A))
3445
end
3546
end
3647

37-
## This View checks for the index to be L1
38-
# and then replaces the value by add the value at the mirrored position L2
48+
"""
49+
FourierJoin{T,N, AA<:AbstractArray{T, N}} <: AbstractArray{T, N}
50+
51+
This View checks for the index to be L1
52+
and then replaces the value by add the value at the mirrored position L2
53+
`do_join` is a Bool that indicates whether this mechanism is active.
54+
It is needed for type stability reasons of functions returnting this type
55+
"""
3956
struct FourierJoin{T,N, AA<:AbstractArray{T, N}} <: AbstractArray{T, N}
4057
parent::AA
4158
D::Int # dimension along which to apply to copy
4259
L1::Int # low index position to copy from (and half)
4360
L2::Int # high index positon to copy to (and half)
61+
do_join::Bool
4462

45-
# This version below is needed to avoid a split for the firs rft dimension but still return half the value
46-
# FFTs and other RFT dimension should use the version without L2
47-
function FourierJoin(parent::AA, D::Int, L1::Int, L2::Int) where {T, N, AA<:AbstractArray{T, N}}
48-
return new{T, N, AA}(parent, D, L1, L2)
63+
"""
64+
This version below is needed to avoid a split for the
65+
first rft dimension but still return half the value
66+
FFTs and other RFT dimension should use the version without L2
67+
"""
68+
function FourierJoin(parent::AA, D::Int, L1::Int, L2::Int, do_join::Bool) where {T, N, AA<:AbstractArray{T, N}}
69+
return new{T, N, AA}(parent, D, L1, L2, do_join)
4970
end
5071

51-
function FourierJoin(parent::AA, D::Int,L1::Int) where {T, N, AA<:AbstractArray{T, N}}
72+
function FourierJoin(parent::AA, D::Int,L1::Int, do_join::Bool) where {T, N, AA<:AbstractArray{T, N}}
5273
mid = fft_center(size(parent)[D])
5374
L2 = mid + (mid-L1)
54-
return FourierJoin(parent, D, L1, L2)
75+
return FourierJoin(parent, D, L1, L2, do_join)
5576
end
5677
end
78+
5779
Base.IndexStyle(::Type{FS}) where {FS<:FourierJoin} = IndexStyle(parenttype(FS))
5880
parenttype(::Type{FourierJoin{T,N,AA}}) where {T,N,AA} = AA
5981
parenttype(A::FourierJoin) = parenttype(typeof(A))
6082
Base.parent(A::FourierJoin) = A.parent
6183
Base.size(A::FourierJoin) = size(parent(A))
6284

6385
@inline function Base.getindex(A::FourierJoin{T,N, <:AbstractArray{T, N}}, i::Vararg{Int,N}) where {T,N}
64-
if i[A.D]==A.L1
86+
if i[A.D]==A.L1 && A.do_join
6587
@inbounds return (parent(A)[i...] + parent(A)[Base.setindex(i, A.L2, A.D)...])
6688
else
6789
@inbounds return (parent(A)[i...])

src/czt.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ end
7878

7979
"""
8080
czt(xin , scale, dims=1:length(size(xin)))
81+
8182
Chirp z transform of the ND array `xin`
8283
This code is based on a 2D Matlab version of the CZT, written by H. Gross.
8384
The tuple `scale` defines the zoom factors in the Fourier domain. Each has to be bigger than one.
@@ -136,6 +137,7 @@ end
136137

137138
"""
138139
iczt(xin , scale, dims=1:length(size(xin)))
140+
139141
Inverse chirp z transform of the ND array `xin`
140142
This code is based on a 2D Matlab version of the CZT, written by H. Gross.
141143
The tuple `scale` defines the zoom factors in the Fourier domain. Each has to be bigger than one.

src/fftshift_alternatives.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,8 @@ end
101101
"""
102102
fftshift2d_view(mat::AbstractArray{T, N}) where {T, N}
103103
104-
Short-hand for `fftshift_view(mat, (1,2))`.
105-
See `fftshift_view` for details.
104+
Short-hand for `fftshift_view(mat, (1,2))`.
105+
See `fftshift_view` for details.
106106
"""
107107
function fftshift2d_view(mat::AbstractArray{T, N}) where {T, N}
108108
fftshift_view(mat,(1,2))
@@ -111,8 +111,8 @@ end
111111
"""
112112
ifftshift2d_view(mat::AbstractArray{T, N}) where {T, N}
113113
114-
Short-hand for `ifftshift_view(mat, (1,2))` performing only a 2D inverse ft.
115-
See `ifft` for details.
114+
Short-hand for `ifftshift_view(mat, (1,2))` performing only a 2D inverse ft.
115+
See `ifft` for details.
116116
"""
117117
function ifftshift2d_view(mat::AbstractArray{T, N}) where {T, N}
118118
ifftshift_view(mat,(1,2))

src/fourier_filtering.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ function fourier_filter!(arr::AbstractArray{<:Real, N}, wins::AbstractVector; tr
5454
end
5555

5656
"""
57-
fourier_filter(arr::AbstractArray, fct=window_gaussian; kwargs...)
57+
fourier_filter(arr::AbstractArray, fct=window_gaussian; kwargs...)
5858
5959
filters an array by multiplication in Fourierspace. This version uses in-place Fourier-transforms and multiplication whereever possible.
6060
The filter window function is assumed to be separable. Depending on the array type either full-complex or real-to-complex FFTs will be used.

src/fourier_resizing.jl

Lines changed: 89 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
export select_region_ft, select_region_rft
22
export rft_fix_after, rft_fix_before, ft_fix_after, ft_fix_before
33

4-
54
"""
65
select_region_ft(mat,new_size)
76
@@ -62,12 +61,12 @@ julia> select_region_ft(ffts(x), (5,3))
6261
-86.75+0.0im 165.5+0.0im -86.75+0.0im
6362
```
6463
"""
65-
function select_region_ft(mat,new_size)
64+
function select_region_ft(mat, new_size)
6665
old_size = size(mat)
67-
mat_fixed_before = ft_fix_before(mat,old_size,new_size)
68-
mat_pad = ft_pad(mat_fixed_before,new_size)
66+
mat_fixed_before = ft_fix_before(mat, old_size, new_size)
67+
mat_pad = ft_pad(mat_fixed_before, new_size)
6968
# afterwards we add the highest pos. frequency to the highest lowest one
70-
return ft_fix_after(mat_pad ,old_size,new_size)
69+
return ft_fix_after(mat_pad ,old_size, new_size)
7170
end
7271

7372
"""
@@ -87,12 +86,11 @@ The size of the corresponding real-space array before it was rfted.
8786
The size of the corresponding real-space array view after the operation finished. The Fourier-center
8887
is always assumed to align before and after the padding aperation.
8988
"""
90-
function select_region_rft(mat,old_size, new_size)
91-
rft_old_size = size(mat)
92-
rft_new_size = Base.setindex(new_size,new_size[1]÷2 +1, 1)
89+
function select_region_rft(mat, old_size, new_size)
90+
# rft_old_size = size(mat)
91+
rft_new_size = Base.setindex(new_size,new_size[1] ÷ 2 + 1, 1)
9392
return rft_fix_after(rft_pad(
94-
rft_fix_before(mat,old_size,new_size),
95-
rft_new_size),old_size,new_size)
93+
rft_fix_before(mat, old_size, new_size), rft_new_size), old_size, new_size)
9694
end
9795

9896
"""
@@ -123,75 +121,112 @@ julia> select_region(ones(3,3),new_size=(7,7),center=(1,3))
123121
"""
124122
function select_region(mat; new_size=size(mat), center=ft_center_diff(size(mat)).+1, pad_value=zero(eltype(mat)))
125123
new_size = Tuple(expand_size(new_size, size(mat)))
126-
center = Tuple(expand_size(center, ft_center_diff(size(mat)).+1))
127-
oldcenter = ft_center_diff(new_size).+1
128-
PaddedView(pad_value, mat,new_size, oldcenter .- center.+1);
124+
center = Tuple(expand_size(center, ft_center_diff(size(mat)) .+ 1))
125+
oldcenter = ft_center_diff(new_size) .+ 1
126+
PaddedView(pad_value, mat, new_size, oldcenter .- center.+1);
129127
end
130128

131129
function ft_pad(mat, new_size)
132-
return select_region(mat;new_size=new_size)
130+
return select_region(mat; new_size = new_size)
133131
end
134132

135133
function rft_pad(mat, new_size)
136134
c2 = rft_center_diff(size(mat))
137-
c2 = Base.setindex(c2, new_size[1].÷2, 1);
138-
return select_region(mat;new_size=new_size, center=c2.+1)
135+
c2 = Base.setindex(c2, new_size[1] 2, 1);
136+
return select_region(mat; new_size=new_size, center = c2 .+ 1)
139137
end
140138

141-
function ft_fix_before(mat, size_old, size_new; start_dim=1)
142-
for d = start_dim:ndims(mat)
143-
sn = size_new[d]
144-
so = size_old[d]
145-
if sn < so && iseven(sn)
146-
L1 = (size_old[d] -size_new[d] )÷2 +1
147-
mat = FourierJoin(mat, d, L1)
148-
end
149-
end
150-
return mat
139+
"""
140+
ft_fix_before(mat::MT, size_old, size_new, ::Val{N})::FourierJoin{T,N,MT} where {T,N, MT<:AbstractArray{T,N}}
141+
142+
implements the specialized (highest dimension) version of a recursive dimension-specific function that returns an array type which knows
143+
how to access (joins) certain elements.
144+
"""
145+
function ft_fix_before(mat::MT, size_old, size_new, ::Val{N})::FourierJoin{T,N,MT} where {T,N, MT<:AbstractArray{T,N}}
146+
sn = size_new[N]
147+
so = size_old[N]
148+
do_join = (sn < so && iseven(sn))
149+
L1 = (size_old[N] - size_new[N] ) ÷ 2 + 1
150+
return FourierJoin(mat, N, L1, do_join)
151151
end
152152

153-
function ft_fix_after(mat,size_old,size_new; start_dim=1)
154-
start_dim
155-
ndims(mat)
156-
for d=start_dim:ndims(mat)
157-
sn = size_new[d]
158-
so = size_old[d]
159-
if sn > so && iseven(so)
160-
L1 = (size_new[d]-size_old[d])÷2+1
161-
mat = FourierSplit(mat,d,L1)
162-
end
163-
# if equal do nothing
153+
"""
154+
ft_fix_before(mat::MT, size_old, size_new, ::Val{D}=Val(1)) where {D, T, N, MT<:AbstractArray{T,N}}
155+
156+
implements the general version of a recursive dimension-specific function that returns an array type which knows
157+
how to access (joins) certain elements.
158+
"""
159+
function ft_fix_before(mat::MT, size_old, size_new, ::Val{D}=Val(1)) where {D, T, N, MT<:AbstractArray{T,N}}
160+
if D <= N
161+
sn = size_new[D]
162+
so = size_old[D]
163+
do_join = (sn < so && iseven(sn))
164+
L1 = (size_old[D] - size_new[D] )÷2 +1
165+
mat = FourierJoin(mat, D, L1, do_join)
166+
return ft_fix_before(mat, size_old, size_new, Val(D + 1))
167+
else
168+
L1 = (size_old[N] -size_new[N] )÷2 +1
169+
return FourierJoin(mat, N, L1, false)
164170
end
165-
return mat
166171
end
167172

168-
function rft_fix_first_dim_before(mat,size_old,size_new;dim=1)
169-
sn = size_new[dim] # Note that this dim is the corresponding real-space size
170-
so = size_old[dim] # Note that this dim is the corresponding real-space size
171-
if sn < so && iseven(sn) # result size is even upon cropping
172-
L1 = size_new[dim] ÷ 2 + 1
173-
mat = FourierJoin(mat, dim, L1, L1) # a hack to dublicate the value
173+
function ft_fix_after(mat::MT, size_old, size_new, ::Val{N})::FourierSplit{T,N,MT} where {T, N, MT<:AbstractArray{T,N}}
174+
sn = size_new[N]
175+
so = size_old[N]
176+
do_split = (sn > so && iseven(so))
177+
L1 = (size_new[N] - size_old[N]) ÷ 2 + 1
178+
return FourierSplit(mat, N, L1, do_split)
179+
end
180+
181+
function ft_fix_after(mat::MT, size_old, size_new, ::Val{D}=Val(1)) where {D, T, N, MT<:AbstractArray{T,N}}
182+
if D <= N
183+
sn = size_new[D]
184+
so = size_old[D]
185+
do_split = (sn > so && iseven(so))
186+
L1 = (size_new[D]-size_old[D])÷2+1
187+
mat = FourierSplit(mat, D, L1, do_split)
188+
return ft_fix_after(mat, size_old, size_new, Val(D + 1))
189+
else
190+
L1 = (size_new[N]-size_old[N])÷2+1
191+
return FourierSplit(mat, N, L1, false)
174192
end
193+
end
194+
195+
function rft_fix_first_dim_before(mat, size_old, size_new; dim=1)
196+
# Note that this dim is the corresponding real-space size
197+
sn = size_new[dim]
198+
so = size_old[dim]
199+
# result size is even upon cropping
200+
do_join = (sn < so && iseven(sn))
201+
L1 = size_new[dim] ÷ 2 + 1
202+
# a hack to dublicate the value
203+
mat = FourierJoin(mat, dim, L1, L1, do_join)
175204
return mat
176205
end
177206

178207
function rft_fix_first_dim_after(mat,size_old,size_new;dim=1)
179-
sn = size_new[dim] # Note that this dim is the corresponding real-space size
180-
so = size_old[dim] # Note that this dim is the corresponding real-space size
181-
if sn > so && iseven(so) # source size is even upon padding
182-
L1 = size_old[dim] ÷ 2 + 1
183-
mat = FourierSplit(mat,dim,L1,-1) # This hack prevents a second position to be affected
184-
end
208+
# Note that this dim is the corresponding real-space size
209+
sn = size_new[dim]
210+
so = size_old[dim]
211+
# source size is even upon padding
212+
do_split = (sn > so && iseven(so))
213+
L1 = size_old[dim] ÷ 2 + 1
214+
# This hack prevents a second position to be affected
215+
mat = FourierSplit(mat, dim, L1, -1, do_split)
185216
# if equal do nothing
186217
return mat
187218
end
188219

189220
function rft_fix_before(mat,size_old,size_new)
190-
mat=rft_fix_first_dim_before(mat,size_old,size_new;dim=1) # ignore the first dimension
191-
ft_fix_before(mat,size_old,size_new;start_dim=2) # ignore the first dimension
221+
# ignore the first dimension
222+
mat=rft_fix_first_dim_before(mat,size_old,size_new;dim=1)
223+
# ignore the first dimension since it starts at Val(2)
224+
ft_fix_before(mat, size_old, size_new, Val(2))
192225
end
193226

194227
function rft_fix_after(mat,size_old,size_new)
195-
mat = rft_fix_first_dim_after(mat,size_old,size_new;dim=1) # ignore the first dimension
196-
ft_fix_after(mat,size_old,size_new;start_dim=2) # ignore the first dimension
228+
# ignore the first dimension
229+
mat = rft_fix_first_dim_after(mat,size_old,size_new;dim=1)
230+
# ignore the first dimension since it starts at Val(2)
231+
ft_fix_after(mat, size_old, size_new, Val(2))
197232
end

src/nfft_nd.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ end
169169

170170
"""
171171
plan_nfft_nd(src::AbstractArray{T,D}, dst_fkt::Function, dst_size=size(src); is_in_pixels=false, is_adjoint=false, kwargs...)
172+
172173
Plans an n-dimensional non-uniform FFT on grids with a regular topology defined via the function `dst_fkt`.
173174
174175
# Arguments

src/resampling.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,7 @@ end
270270

271271
"""
272272
barrel_pin(arr, rel=0.5)
273+
273274
emulates a barrel (`rel>0`) or a pincushion (`rel<0`) distortion. The distortions are calculated using `resample_czt()` with separable quadratic zooms.
274275
275276
See also: `resample_czt()`

0 commit comments

Comments
 (0)