Skip to content

Commit

Permalink
streamline API for warp and WarpedView and add InvWarpedView (#24)
Browse files Browse the repository at this point in the history
* refactor warp to be consistent with WarpedView and perform backward mode

* clean up WarpedView API and add warpedview

* add InvWarpedView as wrapper around WarpedView

* add ImageInTerminal style visual tests

* enable color on travis and appveyor

* improve and add tests for warp helper functions

* implement a deprecation strategy for old warp

* add warp support for OneTo inds

* add docstring for warpedview and invwarpedview
  • Loading branch information
Evizero committed May 4, 2017
1 parent ee9d526 commit 0551a0d
Show file tree
Hide file tree
Showing 22 changed files with 954 additions and 140 deletions.
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ notifications:
# uncomment the following lines to override the default test script
script:
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
- julia -e 'Pkg.clone(pwd()); Pkg.build("ImageTransformations"); Pkg.test("ImageTransformations"; coverage=VERSION >= v"0.6.0-alpha")'
- julia --color=yes -e 'Pkg.clone(pwd()); Pkg.build("ImageTransformations"); Pkg.test("ImageTransformations"; coverage=VERSION >= v"0.6.0-alpha")'
after_success:
# push coverage results to Codecov
- julia -e 'if VERSION >= v"0.6.0-alpha" cd(Pkg.dir("ImageTransformations")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder()); end'
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,4 @@ build_script:
Pkg.clone(pwd(), \"ImageTransformations\"); Pkg.build(\"ImageTransformations\")"

test_script:
- C:\projects\julia\bin\julia -e "Pkg.test(\"ImageTransformations\")"
- C:\projects\julia\bin\julia --color=yes -e "Pkg.test(\"ImageTransformations\")"
9 changes: 8 additions & 1 deletion src/ImageTransformations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,19 @@ export
imresize,
center,
warp,
WarpedView
WarpedView,
warpedview,
InvWarpedView,
invwarpedview

include("autorange.jl")
include("resizing.jl")
include("interpolations.jl")
include("warp.jl")
include("warpedview.jl")
include("invwarpedview.jl")

@inline _getindex(A, v::StaticVector) = A[convert(Tuple, v)...]

center{T,N}(img::AbstractArray{T,N}) = SVector{N}(map(_center, indices(img)))
_center(ind::AbstractUnitRange) = (first(ind)+last(ind))/2
Expand Down
58 changes: 58 additions & 0 deletions src/interpolations.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# FIXME: upstream https://github.com/JuliaGraphics/ColorVectorSpace.jl/issues/75
@inline _nan(::Type{HSV{Float16}}) = HSV{Float16}(NaN16,NaN16,NaN16)
@inline _nan(::Type{HSV{Float32}}) = HSV{Float32}(NaN32,NaN32,NaN32)
@inline _nan(::Type{HSV{Float64}}) = HSV{Float64}(NaN,NaN,NaN)
@inline _nan{T}(::Type{T}) = nan(T)

# The default values used by extrapolation for off-domain points
@compat const FillType = Union{Number,Colorant,Flat,Periodic,Reflect}
@compat const FloatLike{T<:AbstractFloat} = Union{T,AbstractGray{T}}
@compat const FloatColorant{T<:AbstractFloat} = Colorant{T}
@inline _default_fill{T<:FloatLike}(::Type{T}) = convert(T, NaN)
@inline _default_fill{T<:FloatColorant}(::Type{T}) = _nan(T)
@inline _default_fill{T}(::Type{T}) = zero(T)

box_extrapolation(etp::AbstractExtrapolation) = etp

function box_extrapolation{T}(itp::AbstractInterpolation{T}, fill::FillType = _default_fill(T))
etp = extrapolate(itp, fill)
box_extrapolation(etp)
end

function box_extrapolation{T,N,D<:Union{Linear,Constant}}(parent::AbstractArray{T,N}, degree::D = Linear(), args...)
itp = Interpolations.BSplineInterpolation{T,N,typeof(parent),BSpline{D},OnGrid,0}(parent)
box_extrapolation(itp, args...)
end

function box_extrapolation{T,N}(parent::AbstractArray{T,N}, degree::Interpolations.Degree, args...)
itp = interpolate(parent, BSpline(degree), OnGrid())
box_extrapolation(itp, args...)
end

function box_extrapolation(parent::AbstractArray, fill::FillType)
box_extrapolation(parent, Linear(), fill)
end

function box_extrapolation(itp::AbstractInterpolation, degree::Union{Linear,Constant}, args...)
throw(ArgumentError("Boxing an interpolation in another interpolation is discouraged. Did you specify the parameter \"$degree\" on purpose?"))
end

function box_extrapolation(itp::AbstractInterpolation, degree::Interpolations.Degree, args...)
throw(ArgumentError("Boxing an interpolation in another interpolation is discouraged. Did you specify the parameter \"$degree\" on purpose?"))
end

function box_extrapolation(itp::AbstractExtrapolation, fill::FillType)
throw(ArgumentError("Boxing an extrapolation in another extrapolation is discouraged. Did you specify the parameter \"$fill\" on purpose?"))
end

# This is type-piracy, but necessary if we want Interpolations to be
# independent of OffsetArrays.
function AxisAlgorithms.A_ldiv_B_md!(dest::OffsetArray, F, src::OffsetArray, dim::Integer, b::AbstractVector)
indsdim = indices(parent(src), dim)
indsF = indices(F)[2]
if indsF == indsdim
AxisAlgorithms.A_ldiv_B_md!(parent(dest), F, parent(src), dim, b)
return dest
end
throw(DimensionMismatch("indices $(indices(parent(src))) do not match $(indices(F))"))
end
119 changes: 119 additions & 0 deletions src/invwarpedview.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
"""
InvWarpedView(img, tinv, [indices]) -> wv
Create a view of `img` that lazily transforms any given index `I`
passed to `wv[I]` to correspond to `img[inv(tinv)(I)]`. While
technically this approach is known as backward mode warping, note
that `InvWarpedView` is created by supplying the forward
transformation
The conceptual difference to [`WarpedView`](@ref) is that
`InvWarpedView` is intended to be used when reasoning about the
image is more convenient that reasoning about the indices.
Furthermore, `InvWarpedView` allows simple nesting of
transformations, in which case the transformations will be
composed into a single one.
The optional parameter `indices` can be used to specify the
domain of the resulting `wv`. By default the indices are computed
in such a way that `wv` contains all the original pixels in
`img`.
see [`invwarpedview`](@ref) for more information.
"""
immutable InvWarpedView{T,N,A,F,I,FI<:Transformation,E} <: AbstractArray{T,N}
inner::WarpedView{T,N,A,F,I,E}
inverse::FI
end

function InvWarpedView{T,N,TA,F,I,E}(inner::WarpedView{T,N,TA,F,I,E})
tinv = inv(inner.transform)
InvWarpedView{T,N,TA,F,I,typeof(tinv),E}(inner, tinv)
end

function InvWarpedView(A::AbstractArray, tinv::Transformation, inds::Tuple = autorange(A, tinv))
InvWarpedView(WarpedView(A, inv(tinv), inds), tinv)
end

function InvWarpedView(inner::InvWarpedView, outer_tinv::Transformation)
tinv = compose(outer_tinv, inner.inverse)
InvWarpedView(parent(inner), tinv)
end

Base.parent(A::InvWarpedView) = parent(A.inner)
@inline Base.indices(A::InvWarpedView) = indices(A.inner)

@compat Compat.IndexStyle{T<:InvWarpedView}(::Type{T}) = IndexCartesian()
@inline Base.getindex{T,N}(A::InvWarpedView{T,N}, I::Vararg{Int,N}) = A.inner[I...]

Base.size(A::InvWarpedView) = size(A.inner)
Base.size(A::InvWarpedView, d) = size(A.inner, d)

function ShowItLikeYouBuildIt.showarg(io::IO, A::InvWarpedView)
print(io, "InvWarpedView(")
showarg(io, parent(A))
print(io, ", ")
print(io, A.inverse)
print(io, ')')
end

Base.summary(A::InvWarpedView) = summary_build(A)

"""
invwarpedview(img, tinv, [indices], [degree = Linear()], [fill = NaN]) -> wv
Create a view of `img` that lazily transforms any given index `I`
passed to `wv[I]` to correspond to `img[inv(tinv)(I)]`. While
technically this approach is known as backward mode warping, note
that `InvWarpedView` is created by supplying the forward
transformation. The given transformation `tinv` must accept a
`SVector` as input and support `inv(tinv)`. A useful package to
create a wide variety of such transformations is
[CoordinateTransformations.jl](https://github.com/FugroRoames/CoordinateTransformations.jl).
When invoking `wv[I]`, values for `img` must be reconstructed at
arbitrary locations `inv(tinv)(I)`. `InvWarpedView` serves as a
wrapper around [`WarpedView`](@ref) which takes care of
interpolation and extrapolation. The parameters `degree` and
`fill` can be used to specify the b-spline degree and the
extrapolation scheme respectively.
The optional parameter `indices` can be used to specify the
domain of the resulting `wv`. By default the indices are computed
in such a way that `wv` contains all the original pixels in
`img`.
"""
@inline invwarpedview(A::AbstractArray, tinv::Transformation, args...) =
InvWarpedView(A, tinv, args...)

function invwarpedview{T}(
A::AbstractArray{T},
tinv::Transformation,
degree::Union{Linear,Constant},
fill::FillType = _default_fill(T))
invwarpedview(box_extrapolation(A, degree, fill), tinv)
end

function invwarpedview{T}(
A::AbstractArray{T},
tinv::Transformation,
indices::Tuple,
degree::Union{Linear,Constant},
fill::FillType = _default_fill(T))
invwarpedview(box_extrapolation(A, degree, fill), tinv, indices)
end

function invwarpedview(
A::AbstractArray,
tinv::Transformation,
fill::FillType)
invwarpedview(A, tinv, Linear(), fill)
end

function invwarpedview(
A::AbstractArray,
tinv::Transformation,
indices::Tuple,
fill::FillType)
invwarpedview(A, tinv, indices, Linear(), fill)
end
1 change: 1 addition & 0 deletions src/resizing.jl
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ imresize_type(c::FixedPoint) = typeof(c)
imresize_type(c) = typeof((c*1)/1)

function imresize!{T,S,N}(resized::AbstractArray{T,N}, original::AbstractArray{S,N})
# FIXME: avoid allocation for interpolation
itp = interpolate(original, BSpline(Linear()), OnGrid())
imresize!(resized, itp)
end
Expand Down
Loading

0 comments on commit 0551a0d

Please sign in to comment.