Skip to content

Commit 6a275c4

Browse files
fixed a problem with czt which causes wrap-around. This had an effect on resample_czt
1 parent 11467f8 commit 6a275c4

File tree

3 files changed

+52
-18
lines changed

3 files changed

+52
-18
lines changed

src/czt.jl

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export czt, iczt
22

33
"""
4-
czt_1d(xin , scaled , d)
4+
czt_1d(xin , scaled , d; kill_wrap=false, pad_value=zero(eltype(xin)))
55
66
Chirp z transform along a single direction d of an ND array `xin` into the ND array 'xout'.
77
Note that xin and xout can be the same array for inplace operations.
@@ -14,9 +14,11 @@ This code is based on a 2D Matlab version of the CZT, written by H. Gross et al.
1414
+ `xin`: array to transform
1515
+ `scaled`: factor to zoom into during the 1-dimensional czt.
1616
+ `d`: single dimension to transform (as a tuple)
17+
+ `kill_wrap`: if true, the wrapped places will be set to pad_value
18+
+ `pad_value`: the value that the wrap-around will be set to if `kill_wrap` is `true`.
1719
1820
"""
19-
function czt_1d(xin, scaled, d)
21+
function czt_1d(xin, scaled, d; kill_wrap=false, pad_value=zero(eltype(xin)))
2022
sz=size(xin)
2123
# returns the real datatype
2224
rtype = real(eltype(xin))
@@ -72,12 +74,17 @@ function czt_1d(xin, scaled, d)
7274
NDTools.slice(xout,d, o) .-= NDTools.slice(xin,d, 1) .* (1im).^mod(o-midp,4)
7375
end
7476
end
75-
return xout
77+
if kill_wrap && (scaled < 1.0)
78+
nsz = Tuple(d == nd ? ceil(Int64, scaled * size(xin,d)) : size(xin,nd) for nd=1:ndims(xin))
79+
return select_region(select_region(xout, new_size=nsz), new_size=size(xout), pad_value=pad_value)
80+
else
81+
return xout
82+
end
7683
# xout .= g[dsize:(2*dsize-1)] .* reorient(fak, d, Val(ndims(xin)))
7784
end
7885

7986
"""
80-
czt(xin , scale, dims=1:length(size(xin)))
87+
czt(xin , scale, dims=1:length(size(xin)), kill_wrap=false)
8188
Chirp z transform of the ND array `xin`
8289
This code is based on a 2D Matlab version of the CZT, written by H. Gross.
8390
The tuple `scale` defines the zoom factors in the Fourier domain. Each has to be bigger than one.
@@ -90,6 +97,7 @@ The tuple `scale` defines the zoom factors in the Fourier domain. Each has to be
9097
+ `xin`: array to transform
9198
+ `scale`: a tuple of factors (one for each dimension) to zoom into during the czt.
9299
+ `dims`: a tuple of dimensions over which to apply the czt.
100+
+ `kill_wrap`: if true, the wrapped places will be set to zero. Note that the `pad_value` argument is only allowed for 1d czts to not cause confusion.
93101
94102
#Example:
95103
```jdoctest
@@ -126,16 +134,16 @@ julia> zoomed = real.(ift(xft))
126134
0.0239759 -0.028264 0.0541186 -0.0116475 -0.261294 0.312719 -0.261294 -0.0116475 0.0541186 -0.028264
127135
```
128136
"""
129-
function czt(xin::Array{T,N}, scale, dims=1:length(size(xin)))::Array{complex(T),N} where {T,N}
137+
function czt(xin::Array{T,N}, scale, dims=1:length(size(xin)); kill_wrap=false)::Array{complex(T),N} where {T,N}
130138
xout = xin
131139
for d in dims
132-
xout = czt_1d(xout, scale[d], d)
140+
xout = czt_1d(xout, scale[d], d; kill_wrap=kill_wrap)
133141
end
134142
return xout
135143
end
136144

137145
"""
138-
iczt(xin , scale, dims=1:length(size(xin)))
146+
iczt(xin , scale, dims=1:length(size(xin)); kill_wrap=false)
139147
Inverse chirp z transform of the ND array `xin`
140148
This code is based on a 2D Matlab version of the CZT, written by H. Gross.
141149
The tuple `scale` defines the zoom factors in the Fourier domain. Each has to be bigger than one.
@@ -146,6 +154,7 @@ The tuple `scale` defines the zoom factors in the Fourier domain. Each has to be
146154
+ `xin`: array to transform
147155
+ `scale`: a tuple of factors (one for each dimension) of the the inverse czt.
148156
+ `dims`: a tuple of dimensions over which to apply the inverse czt.
157+
+ `kill_wrap`: if true, the wrapped places will be set to zero. Note that the `pad_value` argument is only allowed for 1d czts to not cause confusion.
149158
150159
#See also: czt, czt_1d
151160
@@ -184,6 +193,6 @@ julia> iczt(xft,(1.2,1.3))
184193
-0.0965531+0.0404296im -0.159713+0.0637132im 0.48095+0.0775406im 0.67753-0.263814im 0.77553-0.121603im 0.660335-0.00736904im 0.495205-0.135059im -0.163859+0.125535im
185194
```
186195
"""
187-
function iczt( xin , scale, dims=1:length(size(xin)))
188-
conj(czt(conj(xin), scale, dims)) / prod(size(xin))
196+
function iczt( xin , scale, dims=1:length(size(xin)); kill_wrap=false)
197+
conj(czt(conj(xin), scale, dims; kill_wrap=kill_wrap)) / prod(size(xin))
189198
end

src/resampling.jl

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -167,16 +167,27 @@ function upsample2_abs2(mat::AbstractArray{T, N}; dims=1:N) where {T,N}
167167
end
168168

169169
"""
170-
resample_czt(arr, rel_zoom; shear=nothing, shear_dim=nothing, fix_nyquist=false, new_size = size(arr), rel_pad=0.2)
170+
resample_czt(arr, rel_zoom; shear=nothing, shear_dim=nothing, fix_nyquist=false, new_size = size(arr), do_damp=false, rel_pad=0.2, kill_wrap=true)
171171
172172
resamples the image with fixed factors or a list of separable functions using the chirp z transform algorithm.
173173
The data is first padded by a relative amount `rel_pad` which is needed to avoid wrap-around problems.
174174
As opposed to `resample()`, this routine allows for arbitrary non-integer zoom factors.
175-
It is reasonably fast but only allows a stretch (via `rel_zoom`) and a shift (via `shear` in pixels) per line or column
175+
It is reasonably fast but only allows a stretch (via `rel_zoom`) and a shift (via `shear` in pixels) per line or column.
176176
177177
Note that each entry of the tuple in `rel_zoom` or `shear` describes the zoom or shear to apply to all other dimensions individually
178178
per entry along this dimension number.
179179
180+
# Arguments:
181+
+ `arr`: array to resample
182+
+ `rel_zoom`: factors to zoom as a tuple or a tuple of functions defining the zooms
183+
+ `shear`: a tuple of shears or a tuple of shear functions defining the shears
184+
+ `shear_dim`: which dimension to shear
185+
+ `fix_nyquist`: defines whether to apply `fix_nyquist` when using the apply_shift_strength! function.
186+
+ `do_damp`: applies a padding and damping outside the region to zoom, to avoid artefacts
187+
+ `rel_pad`: amount of padding to apply, if `do_damp` is true
188+
+ `kill_wrap`: removes the wrap-around when zooming out.
189+
+ `new_size`: size of the result array. If not provided the same as the input size will be used.
190+
180191
# Examples
181192
```jdoctest
182193
julia> using TestImages, NDTools, View5D
@@ -192,21 +203,28 @@ julia> d = resample_czt(a, (x->1.0,x->1.0), shear=(x->50*x^2,0.0)); # a more com
192203
julia> @ve a,b,c,d # visualize distortions
193204
```
194205
"""
195-
function resample_czt(arr::AbstractArray{T,N}, rel_zoom; shear=nothing, shear_dim=nothing, fix_nyquist=false, new_size = size(arr), rel_pad=0.2, do_damp=false, center=CtrMid) where {T,N}
206+
function resample_czt(arr::AbstractArray{T,N}, rel_zoom; shear=nothing, shear_dim=nothing, fix_nyquist=false, new_size = size(arr), rel_pad=0.2, do_damp=false, center=CtrMid, kill_wrap=true, pad_value=zero(eltype(arr))) where {T,N}
196207
RT = real(T)
197208
orig_size = size(arr)
198209
if do_damp
199210
arr = damp_edge_outside(arr, rel_pad)
200211
else
201212
arr = copy(arr)
202213
end
203-
for d in 1:length(rel_zoom)
214+
for d in eachindex(rel_zoom)
204215
sd = mod(d,ndims(arr))+1
205216
if !isnothing(shear_dim)
206217
sd = shear_dim[d]
207218
end
208219
my_zoom = 1.0
209-
# case of a list of zoom numbers
220+
# resize the array before zooming, in case the result array is larger
221+
# This may be a bit wasteful depending on the zoom factors, but this case also
222+
# covers the zoomed shearing case
223+
if (new_size[d] > size(arr,d))
224+
nz = Tuple(d == nd ? new_size[nd] : size(arr,nd) for nd=1:ndims(arr))
225+
arr = collect(select_region(arr, new_size=nz))
226+
end
227+
# case of a list (or tuple) of zoom numbers
210228
if (isa(rel_zoom, Tuple) && isa(rel_zoom[1], Number)) || isa(rel_zoom, Number)
211229
my_zoom = rel_zoom[d]
212230
f_res = ift(arr, d)
@@ -219,9 +237,9 @@ function resample_czt(arr::AbstractArray{T,N}, rel_zoom; shear=nothing, shear_di
219237
FourierTools.apply_shift_strength!(f_res, f_res, shifts, d, sd, -myshear, fix_nyquist)
220238
end
221239
if T<:Real
222-
f_res = real(FourierTools.czt_1d(f_res, my_zoom, d))
240+
f_res = real(FourierTools.czt_1d(f_res, my_zoom, d; kill_wrap=kill_wrap, pad_value=pad_value))
223241
else
224-
f_res = T.(FourierTools.czt_1d(f_res, my_zoom, d))
242+
f_res = T.(FourierTools.czt_1d(f_res, my_zoom, d; kill_wrap=kill_wrap, pad_value=pad_value))
225243
end
226244
select_region!(f_res, arr)
227245
# case of position dependent zoom functions
@@ -251,9 +269,9 @@ function resample_czt(arr::AbstractArray{T,N}, rel_zoom; shear=nothing, shear_di
251269
end
252270
f_res = let
253271
if T<:Real
254-
real(FourierTools.czt_1d(f_res, my_zoom, 1))
272+
real(FourierTools.czt_1d(f_res, my_zoom, 1; kill_wrap=kill_wrap, pad_value=pad_value))
255273
else
256-
FourierTools.czt_1d(f_res, my_zoom, 1)
274+
FourierTools.czt_1d(f_res, my_zoom, 1; kill_wrap=kill_wrap, pad_value=pad_value)
257275
end
258276
end
259277
select_region!(f_res, slice)

test/czt.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@ using NDTools # this is needed for the select_region! function below.
1010
@test (iczt(czt(y,zoom),zoom), y, rtol=1e-5)
1111
zoom = (2.0,2.0)
1212
@test (czt(y,zoom), select_region(upsample2(ft(y), fix_center=true),new_size=size(y)), rtol=1e-5)
13+
# zoom smaller 1.0 causes wrap around:
14+
zoom = (0.5,2.0)
15+
@test abs(czt(y,zoom)[1,1]) > 1e-5
16+
zoom = (0.5,2.0)
17+
# check if the kill_wrap works
18+
@test abs(czt(y,zoom; kill_wrap=true)[1,1]) == 0.0
19+
@test abs(iczt(y,zoom; kill_wrap=true)[1,1]) == 0.0
1320
# @vt czt(y,zoom) select_region(upsample2(ft(y), fix_center=true), new_size=size(y))
1421
end
1522
end

0 commit comments

Comments
 (0)