Skip to content

Commit 6f0dbc4

Browse files
authored
fix freqkernel (#207)
1 parent b4f3b7b commit 6f0dbc4

File tree

6 files changed

+55
-21
lines changed

6 files changed

+55
-21
lines changed

Project.toml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "ImageFiltering"
22
uuid = "6a3955dd-da59-5b1f-98d4-e7296123deb5"
33
author = ["Tim Holy <[email protected]>", "Jan Weidner <[email protected]>"]
4-
version = "0.6.19"
4+
version = "0.6.20"
55

66
[deps]
77
CatIndices = "aafaddc9-749c-510e-ac4f-586e18779b91"
@@ -11,7 +11,6 @@ DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
1111
FFTViews = "4f61f5a4-77b1-5117-aa51-3ab5ef4ef0cd"
1212
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
1313
ImageCore = "a09fc81d-aa75-5fe9-8630-4744c3626534"
14-
ImageMetadata = "bc367c6b-8a6b-528e-b4bd-a4b897500b49"
1514
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
1615
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
1716
Requires = "ae029012-a4dd-5104-9daa-d747884805df"

src/ImageFiltering.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module ImageFiltering
33
using FFTW
44
using ImageCore, FFTViews, OffsetArrays, StaticArrays, ComputationalResources, TiledIteration
55
# Where possible we avoid a direct dependency to reduce the number of [compat] bounds
6+
# using FixedPointNumbers: Normed, N0f8 # reexported by ImageCore
67
using ImageCore.MappedArrays
78
using Statistics, LinearAlgebra
89
using ColorVectorSpace # for filtering RGB arrays

src/mapwindow.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,9 @@ resolve_window(window::AbstractArray) = resolve_window((window...,))
133133
resolve_window(window::AbstractUnitRange) = (window,)
134134
resolve_window(window::Indices) = window
135135

136+
# avoid method ambiguity between ::Dims and ::Indices
137+
resolve_window(window::Tuple{}) = throw(ArgumentError("empty window"))
138+
136139
resolve_border(border::AbstractString) = borderinstance(border)
137140
resolve_border(border::BorderSpecAny) = border
138141

src/utils.jl

Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
"""
22
shiftedkernel = centered(kernel)
33
4-
Shift the origin-of-coordinates to the center of `kernel`. The
5-
center-element of `kernel` will be accessed by `shiftedkernel[0, 0,
6-
...]`.
4+
Shift the origin-of-coordinates to the center of `kernel`.
5+
The center-element of `kernel` will be accessed by `shiftedkernel[0, 0, ...]`.
76
87
This function makes it easy to supply kernels using regular Arrays,
98
and provides compatibility with other languages that do not support
@@ -21,20 +20,35 @@ end
2120
kernfft = freqkernel([T::Type], kern, sz=size(kern); rfft=false)
2221
2322
Return a frequency-space representation of `kern`.
24-
This embeds `kern` in an array of size `sz`, in a manner that implicitly imposes periodic boundary
25-
conditions, and then returns the fourier transform. This is sometimes called the optical transfer
26-
function, and known in some frameworks as `psf2otf`. If `rfft` is `true`, the fft for real-valued
27-
arrays (`rfft`) is returned instead and the first dimension size will be approximately half of `sz[1]`.
28-
29-
`kern` should be zero-centered, i.e., `kern[0, 0]` should reference the center of your kernel.
30-
See [`centered`](@ref). Optionally specify the numeric type `T` (which must be one of the
31-
types supported by FFTW, either `Float32` or `Float64`).
23+
This embeds `kern` in an array of size `sz`,
24+
in a manner that implicitly imposes periodic boundary conditions,
25+
and then returns the Fourier transform (frequency response).
26+
This is sometimes called the optical transfer function,
27+
and is known in some frameworks as `psf2otf`.
28+
If `rfft` is `true`, the FFT for real-valued arrays (`rfft`) is returned instead
29+
and the first dimension size will be approximately half of `sz[1]`.
30+
31+
`kern` should be zero-centered, i.e.,
32+
`kern[0, 0]` should reference the center of your kernel,
33+
and `sz` must be large enough to support `kern`.
34+
See [`centered`](@ref).
35+
Optionally specify the numeric type `T`
36+
(which must be one of the types supported by FFTW,
37+
either `Float32` or `Float64`).
3238
3339
The inverse of `freqkernel` is [`spacekernel`](@ref).
3440
"""
3541
function freqkernel(::Type{T}, kern::AbstractArray, sz::Dims=size(kern); rfft=false) where T<:Union{Float32,Float64}
36-
wrapindex(i, s) = i<1 ? i+s : i
37-
all(size(kern) .<= sz) || throw(DimensionMismatch("kernel size $(size(kern)) is bigger than supplied size $sz"))
42+
wrapindex(i, s) = 1 + (i<0 ? i+s : i)
43+
all(size(kern) .<= sz) ||
44+
throw(DimensionMismatch("kernel size $(size(kern)) exceeds supplied size $sz"))
45+
rhs = Tuple(last(CartesianIndices(kern)))
46+
lhs = Tuple(first(CartesianIndices(kern)))
47+
limit = collect((sz .+ 1) 2) # handle odd and even sizes
48+
all(rhs .< limit) ||
49+
throw(DimensionMismatch("kernel last index $rhs >= limit $limit"))
50+
all(-limit .<= lhs) ||
51+
throw(DimensionMismatch("kernel first index $lhs < limit $(-limit)"))
3852
kernw = zeros(T, sz...)
3953
for I in CartesianIndices(kern)
4054
J = CartesianIndex(map(wrapindex, Tuple(I), sz))
@@ -48,17 +62,22 @@ freqkernel(kern::AbstractArray{T}, args...; rfft=false) where T =
4862
"""
4963
kern = spacekernel(kernfft, axs; rfftsz=0)
5064
51-
Return a real-space representation of `kernfft`, a frequency-space representation of a kernel.
52-
This performs an inverse fourier transform, implicitly imposes periodic boundary conditions,
53-
and then trims & truncates axes of the output to `axs`. By default `kernfft` is assumed to have
54-
been generated by `fft`; if it was instead generated by `rfft`, specify the original size
55-
of the first dimension. (If `kernfft` was generated by [`freqkernel`](@ref), this is just `sz[1]`.)
65+
Return a real-space representation of `kernfft`,
66+
the frequency-space representation of a kernel.
67+
This performs an inverse Fourier transform,
68+
implicitly imposes periodic boundary conditions,
69+
and then trims & truncates axes of the output to `axs`.
70+
By default `kernfft` is assumed to have been generated by `fft`;
71+
if it was instead generated by `rfft`,
72+
the specify the original size of the first dimension.
73+
(If `kernfft` was generated by [`freqkernel`](@ref), this is just `sz[1]`.)
5674
5775
The inverse of `spacekernel` is [`freqkernel`](@ref).
5876
"""
5977
function spacekernel(kernfft::AbstractArray, axs::Indices; rfftsz=0)
60-
wrapindex(i, s) = i<1 ? i+s : i
78+
wrapindex(i, s) = 1 + (i<0 ? i+s : i)
6179
kernw = rfftsz > 0 ? irfft(kernfft, rfftsz) : ifft(kernfft)
80+
# there could be some checking of axs vs size(kernfft) here
6281
kern = zeros(eltype(kernw), axs...)
6382
sz = size(kernw)
6483
for I in CartesianIndices(kern)

test/basic.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,15 @@ end
109109
k2 = spacekernel(kfft, axes(k); rfftsz=31)
110110
@test k2 k
111111

112+
k = centered([0, 1, 0] * [0, 1, 0]') # Kronecker impulse
113+
@test freqkernel(k, (8, 8)) ones(8,8) # flat frequency response
114+
115+
@test_throws DimensionMismatch freqkernel(ones(5), (3,)) # kernel too big
116+
k = OffsetArray(ones(3), (-5,)) # index -4:-2 too far left
117+
@test_throws DimensionMismatch freqkernel(k, (5,))
118+
k = OffsetArray(ones(3), (+1,)) # index 2:4 too far right
119+
@test_throws DimensionMismatch freqkernel(k, (5,))
120+
112121
for T in (Float64, Float32, Float16, N0f16)
113122
k = T.(Kernel.gaussian(3))
114123
kfft = freqkernel(k)

test/mapwindow.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ using ImageFiltering: IdentityUnitRange
7171
Amin = groundtruth(min, A, w)
7272
@test minval == Amin
7373
end
74+
75+
@test_throws ArgumentError mapwindow(sum, ones(5,5), ()) # resolve_window
76+
7477
# offsets
7578
@testset "offsets" begin
7679
n = 5

0 commit comments

Comments
 (0)