Skip to content

Commit e22ae1b

Browse files
authored
Merge branch 'master' into master
2 parents 5f6e2df + be89bbd commit e22ae1b

File tree

8 files changed

+206
-2
lines changed

8 files changed

+206
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@
2626
## Build folder of Documenter.jl
2727
docs/build/
2828
Manifest.toml
29+
.vscode

.travis.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ jobs:
1919
Pkg.instantiate()'
2020
- julia --project=docs/ docs/make.jl
2121
after_success: skip
22+
allow_failures:
23+
- julia: nightly
24+

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "Interpolations"
22
uuid = "a98d9a8b-a2ab-59e6-89dd-64a1c18fca59"
3-
version = "0.12.10"
3+
version = "0.13.0"
44

55
[deps]
66
AxisAlgorithms = "13072b0f-2c55-5437-9ae7-d433b7a33950"

src/Interpolations.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -473,5 +473,6 @@ include("utils.jl")
473473
include("io.jl")
474474
include("convenience-constructors.jl")
475475
include("deprecations.jl")
476+
include("lanczos/lanczos.jl")
476477

477478
end # module

src/b-splines/cubic.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ end
77
"""
88
Cubic(bc::BoundaryCondition)
99
10-
Indicate that the corresponding axis should use quadratic interpolation.
10+
Indicate that the corresponding axis should use cubic interpolation.
1111
1212
# Extended help
1313

src/lanczos/lanczos.jl

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
using Base.Cartesian
2+
using StaticArrays
3+
4+
export Lanczos, Lanczos4OpenCV
5+
6+
abstract type AbstractLanczos <: InterpolationType end
7+
8+
"""
9+
Lanczos{N}(a=4)
10+
11+
Lanczos resampling via a kernel with scale parameter `a` and support over `N` neighbors.
12+
13+
This form of interpolation is merely the discrete convolution of the samples with a Lanczos kernel of size `a`. The size is directly related to how "far" the interpolation will reach for information, and has `O(N^2)` impact on runtime. An alternative implementation matching `lanczos4` from OpenCV is available as Lanczos4OpenCV.
14+
"""
15+
struct Lanczos{N} <: AbstractLanczos
16+
a::Int
17+
18+
function Lanczos{N}(a) where N
19+
N < a && @warn "Using a smaller support than scale for Lanczos window. Proceed with caution."
20+
new{N}(a)
21+
end
22+
end
23+
24+
Lanczos(a=4) = Lanczos{a}(a)
25+
26+
"""
27+
LanczosInterpolation
28+
"""
29+
struct LanczosInterpolation{T,N,IT <: DimSpec{AbstractLanczos},A <: AbstractArray{T,N},P <: Tuple{Vararg{AbstractArray,N}}} <: AbstractInterpolation{T,N,IT}
30+
coefs::A
31+
parentaxes::P
32+
it::IT
33+
end
34+
35+
@generated degree(::Lanczos{N}) where {N} = :($N)
36+
37+
getknots(itp::LanczosInterpolation) = axes(itp)
38+
coefficients(itp::LanczosInterpolation) = itp.coefs
39+
itpflag(itp::LanczosInterpolation) = itp.it
40+
41+
size(itp::LanczosInterpolation) = map(length, itp.parentaxes)
42+
axes(itp::LanczosInterpolation) = itp.parentaxes
43+
lbounds(itp::LanczosInterpolation) = map(first, itp.parentaxes)
44+
ubounds(itp::LanczosInterpolation) = map(last, itp.parentaxes)
45+
46+
function interpolate(A::AbstractArray{T}, it::AbstractLanczos) where T
47+
Apad = copy_with_padding(float(T), A, it)
48+
return LanczosInterpolation(Apad, axes(A), it)
49+
end
50+
51+
@inline function (itp::LanczosInterpolation{T,N})(x::Vararg{<:Number,N}) where {T,N}
52+
@boundscheck (checkbounds(Bool, itp, x...) || Base.throw_boundserror(itp, x))
53+
wis = weightedindexes((value_weights,), itpinfo(itp)..., x)
54+
itp.coefs[wis...]
55+
end
56+
57+
function weightedindex_parts(fs, it::AbstractLanczos, ax::AbstractUnitRange{<:Integer}, x)
58+
pos, δx = positions(it, ax, x)
59+
(position = pos, coefs = fmap(fs, it, δx))
60+
end
61+
62+
function positions(it::AbstractLanczos, ax, x)
63+
xf = floorbounds(x, ax)
64+
δx = x - xf
65+
fast_trunc(Int, xf) - degree(it) + 1, δx
66+
end
67+
68+
function value_weights(it::Lanczos, δx::S) where S
69+
N = degree(it)
70+
# short-circuit if integral
71+
isinteger(δx) && return ntuple(i->convert(float(S), i == N - δx), Val(2N))
72+
73+
# LUTs
74+
#it.a === N === 4 && return _lanczos4(δx)
75+
76+
cs = ntuple(i -> lanczos(N - i + δx, it.a, N), Val(2N))
77+
sum_cs = sum(cs)
78+
normed_cs = ntuple(i -> cs[i] / sum_cs, Val(length(cs)))
79+
return normed_cs
80+
end
81+
82+
function padded_axis(ax::AbstractUnitRange, it::AbstractLanczos)
83+
N = degree(it)
84+
return first(ax) - N + 1:last(ax) + N
85+
end
86+
87+
# precise implementations for fast evaluation of common kernels
88+
89+
"""
90+
lanczos(x, a, n=a)
91+
92+
Implementation of the [Lanczos kernel](https://en.wikipedia.org/wiki/Lanczos_resampling)
93+
"""
94+
lanczos(x::T, a::Integer, n=a) where {T} = abs(x) < n ? T(sinc(x) * sinc(x / a)) : zero(T)
95+
96+
"""
97+
Lanczos4OpenCV()
98+
99+
Alternative implementation of Lanczos resampling using algorithm `lanczos4` function of OpenCV:
100+
https://github.com/opencv/opencv/blob/de15636724967faf62c2d1bce26f4335e4b359e5/modules/imgproc/src/resize.cpp#L917-L946
101+
"""
102+
struct Lanczos4OpenCV <: AbstractLanczos end
103+
104+
degree(::Lanczos4OpenCV) = 4
105+
106+
value_weights(::Lanczos4OpenCV, δx::S) where S = ifelse(isinteger(δx),ntuple(i->convert(float(S), i == 4 - δx), Val(8)) ,_lanczos4_opencv(δx))
107+
108+
# s45 = sqrt(2)/2
109+
const s45 = 0.70710678118654752440084436210485
110+
"""
111+
l4_2d_cs is a lookup table that could be generated by
112+
x = (0:7)*45*5
113+
l4_2d_cs = [cosd.(x) sind.(x)]'
114+
"""
115+
const l4_2d_cs = SA[1 0; -s45 -s45; 0 1; s45 -s45; -1 0; s45 s45; 0 -1; -s45 s45]
116+
117+
118+
function _lanczos4_opencv(δx)
119+
p_4 = π / 4
120+
y0 = -(δx + 3) * p_4
121+
s0, c0 = sincos(y0)
122+
cs = ntuple(8) do i
123+
y = (δx + 4 - i) * p_4
124+
# Numerator is the sin subtraction identity
125+
# It is equivalent to the following
126+
# f(δx,i) = sin( π/4*( 5*(i-1)-δx-3 ) )
127+
(l4_2d_cs[i, 1] * s0 + l4_2d_cs[i, 2] * c0) / y^2
128+
end
129+
sum_cs = sum(cs)
130+
normed_cs = ntuple(i -> cs[i] / sum_cs, Val(8))
131+
return normed_cs
132+
end

test/lanczos/runtests.jl

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using Interpolations: degree,
2+
getknots,
3+
coefficients,
4+
itpflag,
5+
DimSpec,
6+
AbstractLanczos,
7+
lbounds,
8+
ubounds
9+
10+
@testset "Lanczos" begin
11+
12+
@testset "Lanczos($N)" for N in 2:4
13+
X = 1:100
14+
X = [X; reverse(X)[2:end]]
15+
itp = interpolate(X, Lanczos(N))
16+
17+
# properties
18+
@test getknots(itp) == axes(itp) == axes(X)
19+
@test coefficients(itp)[axes(itp)...] == X
20+
@test itpflag(itp) isa Lanczos{N}
21+
@test size(itp) == size(X)
22+
@test lbounds(itp) == (firstindex(X),)
23+
@test ubounds(itp) == (lastindex(X),)
24+
25+
# recovers input points exactly
26+
@test itp.(X) == X
27+
28+
@test 1 < itp(1.5) < 2
29+
@test 99 < itp(99.5) < 100
30+
31+
32+
# symmetry check
33+
interpolant = itp.(X)
34+
@test interpolant reverse(interpolant)
35+
36+
end
37+
38+
@testset "Lanczos OpenCV4" begin
39+
X = 1:100
40+
X = [X; reverse(X)[2:end]]
41+
itp = interpolate(X, Lanczos4OpenCV())
42+
43+
# properties
44+
@test getknots(itp) == axes(itp) == axes(X)
45+
@test coefficients(itp)[axes(itp)...] == X
46+
@test itpflag(itp) isa Lanczos4OpenCV
47+
@test size(itp) == size(X)
48+
@test lbounds(itp) == (firstindex(X),)
49+
@test ubounds(itp) == (lastindex(X),)
50+
51+
# recovers input points exactly
52+
@test itp.(X) == X
53+
54+
@test 1 < itp(1.5) < 2
55+
@test 99 < itp(99.5) < 100
56+
57+
58+
# symmetry check
59+
interpolant = itp.(X)
60+
@test interpolant reverse(interpolant)
61+
62+
end
63+
64+
end

test/runtests.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ const isci = get(ENV, "CI", "") in ("true", "True")
2828
# monotonic tests
2929
include("monotonic/runtests.jl")
3030

31+
# lanczos tests
32+
include("lanczos/runtests.jl")
33+
3134
# test gradient evaluation
3235
include("gradient.jl")
3336
isci && println("finished gradient")

0 commit comments

Comments
 (0)