From 19574902f4aa9574c6d5f55e9776ca689e723650 Mon Sep 17 00:00:00 2001 From: Christof Stocker Date: Fri, 21 Apr 2017 15:03:40 +0200 Subject: [PATCH 01/18] refactor warp to be consistent with WarpedView --- src/ImageTransformations.jl | 2 + src/warp.jl | 82 +++++++++++++++++++------------------ test/warp.jl | 14 +++---- 3 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/ImageTransformations.jl b/src/ImageTransformations.jl index 4f254de..ad58264 100644 --- a/src/ImageTransformations.jl +++ b/src/ImageTransformations.jl @@ -28,6 +28,8 @@ include("resizing.jl") include("warp.jl") include("warpedview.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 diff --git a/src/warp.jl b/src/warp.jl index 819e654..81848f2 100644 --- a/src/warp.jl +++ b/src/warp.jl @@ -1,46 +1,29 @@ -# None of these types are provided by this package, so the following line seems unclean -# @inline Base.getindex(A::AbstractExtrapolation, v::StaticVector) = A[convert(Tuple, v)...] -# furthermore it would be ambiguous with getindex(::Extrapolation, xs...) after https://github.com/tlycken/Interpolations.jl/pull/141 -@inline _getindex(A, v::StaticVector) = A[convert(Tuple, v)...] - -warp(img::AbstractArray, args...) = warp(interpolate(img, BSpline(Linear()), OnGrid()), args...) - -@inline _dst_type{T<:Colorant,S}(::Type{T}, ::Type{S}) = ccolor(T, S) -@inline _dst_type{T<:Number,S}(::Type{T}, ::Type{S}) = T - -function warp{T,S}(::Type{T}, img::AbstractArray{S}, args...) - TCol = _dst_type(T,S) - TNorm = eltype(TCol) - apad, pad = Interpolations.prefilter(TNorm, TCol, img, typeof(BSpline(Linear())), typeof(OnGrid())) - itp = Interpolations.BSplineInterpolation(TNorm, apad, BSpline(Linear()), OnGrid(), pad) - warp(itp, args...) -end - -@compat const FloatLike{T<:AbstractFloat} = Union{T,AbstractGray{T}} -@compat const FloatColorant{T<:AbstractFloat} = Colorant{T} - -# The default values used by extrapolation for off-domain points -@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) - -warp{T}(img::AbstractInterpolation{T}, tform, fill=_default_fill(T)) = warp(extrapolate(img, fill), tform) - """ - warp(img, tform) -> imgw + warp(img, tform, [degree = Linear()], [fill = NaN]) -> imgw Transform the coordinates of `img`, returning a new `imgw` satisfying `imgw[x] = img[tform(x)]`. `tform` should be defined using -CoordinateTransformations.jl. +[CoordinateTransformations.jl](https://github.com/FugroRoames/CoordinateTransformations.jl). # Interpolation scheme -At off-grid points, `imgw` is calculated by interpolation. The default -is linear interpolation, used when `img` is a plain array, and `NaN` -values are used to indicate locations for which `tform(x)` was outside -the bounds of the input `img`. For more control over the interpolation -scheme---and how beyond-the-edge points are handled---pass it in as an -`AbstractExtrapolation` from Interpolations.jl. +At off-grid points, `imgw` is calculated by interpolation. The +degree of the b-spline can be specified with the optional +parameter `degree`, which can take the values `Linear()` or +`Constant()`. + +The b-spline interpolation is used when `img` is a plain array +and the `img[tform(x)]` is inbound. In the case `tform(x)` maps +to indices outside the original `img`, the value / extrapolation +scheme denoted by the optional parameter `fill` (which defaults +to `NaN` if the element type supports it, and `0` otherwise) is +used to indicate locations for which `tform(x)` was outside the +bounds of the input `img`. + +For more control over the interpolation scheme --- and how +beyond-the-edge points are handled --- pass it in as an +`AbstractInterpolation` or `AbstractExtrapolation` from +[Interpolations.jl](https://github.com/JuliaMath/Interpolations.jl). # The meaning of the coordinates @@ -87,20 +70,41 @@ julia> indices(imgr) (Base.OneTo(906),Base.OneTo(905)) ``` """ -function warp(img::AbstractExtrapolation, tform) +function warp{T}(img::AbstractExtrapolation{T}, tform) inds = autorange(img, tform) - out = OffsetArray(Array{eltype(img)}(map(length, inds)), inds) + out = OffsetArray(Array{T}(map(length, inds)), inds) warp!(out, img, tform) end function warp!(out, img::AbstractExtrapolation, tform) tinv = inv(tform) - for I in CartesianRange(indices(out)) + @inbounds for I in CartesianRange(indices(out)) out[I] = _getindex(img, tinv(SVector(I.I))) end out end +# 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) + +function warp{T,N}(img::AbstractArray{T,N}, tform, degree::Union{Linear,Constant} = Linear(), args...) + itp = Interpolations.BSplineInterpolation{T,N,typeof(img),Interpolations.BSpline{typeof(degree)},OnGrid,0}(img) + warp(itp, tform, args...) +end + +function warp{T,N}(img::AbstractArray{T,N}, tform, fill::FillType, args...) + warp(img, tform, Linear(), fill) +end + +function warp{T}(img::AbstractInterpolation{T}, tform, fill::FillType = _default_fill(T)) + warp(extrapolate(img, fill), tform) +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) diff --git a/test/warp.jl b/test/warp.jl index 75a3949..a72361e 100644 --- a/test/warp.jl +++ b/test/warp.jl @@ -17,15 +17,12 @@ img_camera = testimage("camera") @testset "warp" begin imgr = @inferred(warp(img_camera, tfm)) @test indices(imgr) == ref_inds + @test eltype(imgr) == eltype(img_camera) - for T in (Float32,Gray,RGB) # TODO: remove this signature completely - imgr = @inferred(warp(T, img_camera, tfm)) - @test indices(imgr) == ref_inds - @test eltype(imgr) <: T - end - - imgr = @inferred(warp(Gray, img_camera, tfm)) - imgr2 = warp(Gray, imgr, inv(tfm)) + imgr = @inferred(warp(img_camera, tfm, 1)) + @test eltype(imgr) == eltype(img_camera) + imgr2 = @inferred warp(imgr, inv(tfm)) + @test eltype(imgr2) == eltype(img_camera) # look the same but are not similar enough to pass test # @test imgr2[indices(img_camera)...] ≈ img_camera end @@ -87,6 +84,7 @@ ref_img_pyramid_quad = Float64[ @testset "warp" begin imgr = warp(img_pyramid, tfm1) @test indices(imgr) == (0:6, 0:6) + @test eltype(imgr) == eltype(img_pyramid) # Use map and === because of the NaNs @test nearlysame(round.(Float64.(parent(imgr)),3), round.(ref_img_pyramid,3)) From fccebb165a75cfc2ba7dc549e0902f0beb1ab3b6 Mon Sep 17 00:00:00 2001 From: Christof Stocker Date: Fri, 21 Apr 2017 18:10:42 +0200 Subject: [PATCH 02/18] clean up WarpedView API and add warpedview --- src/ImageTransformations.jl | 3 +- src/warpedview.jl | 88 +++++++++++++++++++++++-------------- 2 files changed, 58 insertions(+), 33 deletions(-) diff --git a/src/ImageTransformations.jl b/src/ImageTransformations.jl index ad58264..e90b2ce 100644 --- a/src/ImageTransformations.jl +++ b/src/ImageTransformations.jl @@ -21,7 +21,8 @@ export imresize, center, warp, - WarpedView + WarpedView, + warpedview include("autorange.jl") include("resizing.jl") diff --git a/src/warpedview.jl b/src/warpedview.jl index 1496fc7..4b27f40 100644 --- a/src/warpedview.jl +++ b/src/warpedview.jl @@ -1,37 +1,45 @@ -immutable WarpedView{T,N,A<:AbstractArray,F1<:Transformation,F2<:Transformation,I} <: AbstractArray{T,N} +_build_extrapolation(etp::AbstractExtrapolation) = etp + +function _build_extrapolation{T}(itp::AbstractInterpolation{T}, fill::FillType = _default_fill(T)) + etp = extrapolate(itp, fill) + _build_extrapolation(etp) +end + +function _build_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) + _build_extrapolation(itp, args...) +end + +immutable WarpedView{T,N,A<:AbstractArray,F1<:Transformation,I,F2<:Transformation,E<:AbstractExtrapolation} <: AbstractArray{T,N} parent::A transform::F1 - transform_inv::F2 indices::I + inverse::F2 + extrapolation::E - function (::Type{WarpedView{T,N,A,F1,F2,I}}){T,N,A<:AbstractArray,F1<:Transformation,F2<:Transformation,I}( - parent::A, tform::F1, tinv::F2, indices::I) + function (::Type{WarpedView{T,N,TA,F,I}}){T,N,TA<:AbstractArray,F<:Transformation,I<:Tuple}( + parent::TA, + tform::F, + indices::I) @assert eltype(parent) == T - new{T,N,A,F1,F2,I}(parent, tform, tinv, indices) + etp = _build_extrapolation(parent) + tinv = inv(tform) + new{T,N,TA,F,I,typeof(tinv),typeof(etp)}(parent, tform, indices, tinv, etp) end end -function WarpedView{T,N,F<:Transformation}(inner::WarpedView{T,N}, outer_tform::F) +function WarpedView(inner::WarpedView, outer_tform::Transformation) tform = compose(outer_tform, inner.transform) - tinv = inv(tform) - etp = parent(inner) - inds = autorange(etp, tform) - WarpedView{T,N,typeof(etp),typeof(tform),typeof(tinv),typeof(inds)}(etp, tform, tinv, inds) -end - -function WarpedView{T,N}(parent::AbstractArray{T,N}, args...) - itp = Interpolations.BSplineInterpolation{T,N,typeof(parent),Interpolations.BSpline{Interpolations.Linear},OnGrid,0}(parent) - WarpedView(itp, args...) + A = parent(inner) + inds = autorange(A, tform) + WarpedView(A, tform, inds) end -function WarpedView{T,F<:Transformation}(itp::AbstractInterpolation{T}, tform::F, fill=_default_fill(T)) - WarpedView(extrapolate(itp, fill), tform) -end - -function WarpedView{T,N,F<:Transformation}(etp::AbstractExtrapolation{T,N}, tform::F) - inds = autorange(etp, tform) - tinv = inv(tform) - WarpedView{T,N,typeof(etp),F,typeof(tinv),typeof(inds)}(etp, tform, tinv, inds) +function WarpedView{T,N,F<:Transformation,I<:Tuple}( + A::AbstractArray{T,N}, + tform::F, + inds::I = autorange(A, tform)) + WarpedView{T,N,typeof(A),F,I}(A, tform, inds) end Base.parent(A::WarpedView) = A.parent @@ -39,24 +47,40 @@ Base.parent(A::WarpedView) = A.parent @compat Compat.IndexStyle{T<:WarpedView}(::Type{T}) = IndexCartesian() @inline Base.getindex{T,N}(A::WarpedView{T,N}, I::Vararg{Int,N}) = - _getindex(A.parent, A.transform_inv(SVector(I))) + _getindex(A.extrapolation, A.inverse(SVector(I))) Base.size(A::WarpedView) = OffsetArrays.errmsg(A) Base.size(A::WarpedView, d) = OffsetArrays.errmsg(A) -# This will return the next non-standard parent -# This way only those extrapolations/interpolations are displayed -# that are different to the default settings -_next_custom(A) = A -_next_custom(A::Interpolations.FilledExtrapolation) = _next_custom(A.itp) -_next_custom{T,N,TI,IT<:BSpline{Linear},GT<:OnGrid}(A::Interpolations.BSplineInterpolation{T,N,TI,IT,GT}) = _next_custom(A.coefs) - function ShowItLikeYouBuildIt.showarg(io::IO, A::WarpedView) print(io, "WarpedView(") - showarg(io, _next_custom(parent(A))) + showarg(io, parent(A)) print(io, ", ") print(io, A.transform) print(io, ')') end Base.summary(A::WarpedView) = summary_build(A) + +""" +TODO +""" +@inline warpedview(A::AbstractArray, tform::Transformation, args...) = + WarpedView(A, tform, args...) + +function warpedview{T}( + A::AbstractArray{T}, + tform::Transformation, + degree::Union{Linear,Constant}, + fill::FillType = _default_fill(T), + args...) + warpedview(_build_extrapolation(A, degree, fill), tform, args...) +end + +function warpedview( + A::AbstractArray, + tform::Transformation, + fill::FillType, + args...) + warpedview(_build_extrapolation(A, Linear(), fill), tform, args...) +end From 1015bfde23d8f66b6f43904eb9dadcdc2ab9b2ee Mon Sep 17 00:00:00 2001 From: Christof Stocker Date: Fri, 21 Apr 2017 23:22:47 +0200 Subject: [PATCH 03/18] refactor warp and WarpedView to proper backward mode --- src/ImageTransformations.jl | 6 +-- src/invwarpedview.jl | 74 +++++++++++++++++++++++++++++++ src/warp.jl | 70 +++++++++++++++++------------- src/warpedview.jl | 86 ------------------------------------- test/warp.jl | 39 ++++++++--------- 5 files changed, 136 insertions(+), 139 deletions(-) create mode 100644 src/invwarpedview.jl delete mode 100644 src/warpedview.jl diff --git a/src/ImageTransformations.jl b/src/ImageTransformations.jl index e90b2ce..af450cb 100644 --- a/src/ImageTransformations.jl +++ b/src/ImageTransformations.jl @@ -21,13 +21,13 @@ export imresize, center, warp, - WarpedView, - warpedview + InvWarpedView, + invwarpedview include("autorange.jl") include("resizing.jl") include("warp.jl") -include("warpedview.jl") +include("invwarpedview.jl") @inline _getindex(A, v::StaticVector) = A[convert(Tuple, v)...] diff --git a/src/invwarpedview.jl b/src/invwarpedview.jl new file mode 100644 index 0000000..0cae391 --- /dev/null +++ b/src/invwarpedview.jl @@ -0,0 +1,74 @@ +immutable InvWarpedView{T,N,A<:AbstractArray,F1<:Transformation,I,F2<:Transformation,E<:AbstractExtrapolation} <: AbstractArray{T,N} + parent::A + transform::F1 + indices::I + inverse::F2 + extrapolation::E + + function (::Type{InvWarpedView{T,N,TA,F,I}}){T,N,TA<:AbstractArray,F<:Transformation,I<:Tuple}( + parent::TA, + tform::F, + indices::I) + @assert eltype(parent) == T + etp = _box_extrapolation(parent) + tinv = inv(tform) + new{T,N,TA,F,I,typeof(tinv),typeof(etp)}(parent, tform, indices, tinv, etp) + end +end + +function InvWarpedView(inner::InvWarpedView, outer_tform::Transformation) + tform = compose(outer_tform, inner.transform) + A = parent(inner) + inds = autorange(A, tform) + InvWarpedView(A, tform, inds) +end + +function InvWarpedView{T,N,F<:Transformation,I<:Tuple}( + A::AbstractArray{T,N}, + tform::F, + inds::I = autorange(A, tform)) + InvWarpedView{T,N,typeof(A),F,I}(A, tform, inds) +end + +Base.parent(A::InvWarpedView) = A.parent +@inline Base.indices(A::InvWarpedView) = A.indices + +@compat Compat.IndexStyle{T<:InvWarpedView}(::Type{T}) = IndexCartesian() +@inline Base.getindex{T,N}(A::InvWarpedView{T,N}, I::Vararg{Int,N}) = + _getindex(A.extrapolation, A.inverse(SVector(I))) + +Base.size(A::InvWarpedView) = OffsetArrays.errmsg(A) +Base.size(A::InvWarpedView, d) = OffsetArrays.errmsg(A) + +function ShowItLikeYouBuildIt.showarg(io::IO, A::InvWarpedView) + print(io, "InvWarpedView(") + showarg(io, parent(A)) + print(io, ", ") + print(io, A.transform) + print(io, ')') +end + +Base.summary(A::InvWarpedView) = summary_build(A) + +""" +TODO +""" +@inline invwarpedview(A::AbstractArray, tform::Transformation, args...) = + InvWarpedView(A, tform, args...) + +function invwarpedview{T}( + A::AbstractArray{T}, + tform::Transformation, + degree::Union{Linear,Constant}, + fill::FillType = _default_fill(T), + args...) + invwarpedview(_box_extrapolation(A, degree, fill), tform, args...) +end + +function invwarpedview( + A::AbstractArray, + tform::Transformation, + fill::FillType, + args...) + invwarpedview(A, tform, Linear(), fill, args...) +end diff --git a/src/warp.jl b/src/warp.jl index 81848f2..3d84d21 100644 --- a/src/warp.jl +++ b/src/warp.jl @@ -1,8 +1,33 @@ +# 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(parent::AbstractArray, fill::FillType) + _box_extrapolation(parent, Linear(), fill) +end + """ - warp(img, tform, [degree = Linear()], [fill = NaN]) -> imgw + warp(img, tform, [indices], [degree = Linear()], [fill = NaN]) -> imgw Transform the coordinates of `img`, returning a new `imgw` satisfying -`imgw[x] = img[tform(x)]`. `tform` should be defined using +`imgw[I] = img[tform(I)]`. This approach is known as backward +mode warping. The transformation `tform` should be defined using [CoordinateTransformations.jl](https://github.com/FugroRoames/CoordinateTransformations.jl). # Interpolation scheme @@ -13,11 +38,11 @@ parameter `degree`, which can take the values `Linear()` or `Constant()`. The b-spline interpolation is used when `img` is a plain array -and the `img[tform(x)]` is inbound. In the case `tform(x)` maps -to indices outside the original `img`, the value / extrapolation +and `img[tform(I)]` is inbound. In the case `tform(I)` maps +to indices outside the original `img`, the value or extrapolation scheme denoted by the optional parameter `fill` (which defaults to `NaN` if the element type supports it, and `0` otherwise) is -used to indicate locations for which `tform(x)` was outside the +used to indicate locations for which `tform(I)` was outside the bounds of the input `img`. For more control over the interpolation scheme --- and how @@ -28,7 +53,7 @@ beyond-the-edge points are handled --- pass it in as an # The meaning of the coordinates The output array `imgw` has indices that would result from applying -`tform` to the indices of `img`. This can be very handy for keeping +`inv(tform)` to the indices of `img`. This can be very handy for keeping track of how pixels in `imgw` line up with pixels in `img`. If you just want a plain array, you can "strip" the custom indices @@ -48,7 +73,7 @@ julia> indices(img) julia> tfm = recenter(RotMatrix(pi/4), center(img)) AffineMap([0.707107 -0.707107; 0.707107 0.707107], [347.01,-68.7554]) -julia> imgw = warp(img, tfm); +julia> imgw = warp(img, inv(tfm)); julia> indices(imgw) (-196:709,-68:837) @@ -59,7 +84,7 @@ julia> img0 = OffsetArray(img, -30:481, -384:383); # origin near top of image julia> rot = LinearMap(RotMatrix(pi/4)) LinearMap([0.707107 -0.707107; 0.707107 0.707107]) -julia> imgw = warp(img0, rot); +julia> imgw = warp(img0, inv(rot)); julia> indices(imgw) (-293:612,-293:611) @@ -70,39 +95,26 @@ julia> indices(imgr) (Base.OneTo(906),Base.OneTo(905)) ``` """ -function warp{T}(img::AbstractExtrapolation{T}, tform) - inds = autorange(img, tform) +function warp{T}(img::AbstractExtrapolation{T}, tform, inds::Tuple = autorange(img, inv(tform))) out = OffsetArray(Array{T}(map(length, inds)), inds) warp!(out, img, tform) end function warp!(out, img::AbstractExtrapolation, tform) - tinv = inv(tform) @inbounds for I in CartesianRange(indices(out)) - out[I] = _getindex(img, tinv(SVector(I.I))) + out[I] = _getindex(img, tform(SVector(I.I))) end out end -# 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) - -function warp{T,N}(img::AbstractArray{T,N}, tform, degree::Union{Linear,Constant} = Linear(), args...) - itp = Interpolations.BSplineInterpolation{T,N,typeof(img),Interpolations.BSpline{typeof(degree)},OnGrid,0}(img) - warp(itp, tform, args...) -end - -function warp{T,N}(img::AbstractArray{T,N}, tform, fill::FillType, args...) - warp(img, tform, Linear(), fill) +function warp{T,N}(img::AbstractArray{T,N}, tform, inds::Tuple, args...) + etp = _box_extrapolation(img, args...) + warp(etp, tform, inds) end -function warp{T}(img::AbstractInterpolation{T}, tform, fill::FillType = _default_fill(T)) - warp(extrapolate(img, fill), tform) +function warp{T,N}(img::AbstractArray{T,N}, tform, args...) + etp = _box_extrapolation(img, args...) + warp(etp, tform) end # This is type-piracy, but necessary if we want Interpolations to be diff --git a/src/warpedview.jl b/src/warpedview.jl deleted file mode 100644 index 4b27f40..0000000 --- a/src/warpedview.jl +++ /dev/null @@ -1,86 +0,0 @@ -_build_extrapolation(etp::AbstractExtrapolation) = etp - -function _build_extrapolation{T}(itp::AbstractInterpolation{T}, fill::FillType = _default_fill(T)) - etp = extrapolate(itp, fill) - _build_extrapolation(etp) -end - -function _build_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) - _build_extrapolation(itp, args...) -end - -immutable WarpedView{T,N,A<:AbstractArray,F1<:Transformation,I,F2<:Transformation,E<:AbstractExtrapolation} <: AbstractArray{T,N} - parent::A - transform::F1 - indices::I - inverse::F2 - extrapolation::E - - function (::Type{WarpedView{T,N,TA,F,I}}){T,N,TA<:AbstractArray,F<:Transformation,I<:Tuple}( - parent::TA, - tform::F, - indices::I) - @assert eltype(parent) == T - etp = _build_extrapolation(parent) - tinv = inv(tform) - new{T,N,TA,F,I,typeof(tinv),typeof(etp)}(parent, tform, indices, tinv, etp) - end -end - -function WarpedView(inner::WarpedView, outer_tform::Transformation) - tform = compose(outer_tform, inner.transform) - A = parent(inner) - inds = autorange(A, tform) - WarpedView(A, tform, inds) -end - -function WarpedView{T,N,F<:Transformation,I<:Tuple}( - A::AbstractArray{T,N}, - tform::F, - inds::I = autorange(A, tform)) - WarpedView{T,N,typeof(A),F,I}(A, tform, inds) -end - -Base.parent(A::WarpedView) = A.parent -@inline Base.indices(A::WarpedView) = A.indices - -@compat Compat.IndexStyle{T<:WarpedView}(::Type{T}) = IndexCartesian() -@inline Base.getindex{T,N}(A::WarpedView{T,N}, I::Vararg{Int,N}) = - _getindex(A.extrapolation, A.inverse(SVector(I))) - -Base.size(A::WarpedView) = OffsetArrays.errmsg(A) -Base.size(A::WarpedView, d) = OffsetArrays.errmsg(A) - -function ShowItLikeYouBuildIt.showarg(io::IO, A::WarpedView) - print(io, "WarpedView(") - showarg(io, parent(A)) - print(io, ", ") - print(io, A.transform) - print(io, ')') -end - -Base.summary(A::WarpedView) = summary_build(A) - -""" -TODO -""" -@inline warpedview(A::AbstractArray, tform::Transformation, args...) = - WarpedView(A, tform, args...) - -function warpedview{T}( - A::AbstractArray{T}, - tform::Transformation, - degree::Union{Linear,Constant}, - fill::FillType = _default_fill(T), - args...) - warpedview(_build_extrapolation(A, degree, fill), tform, args...) -end - -function warpedview( - A::AbstractArray, - tform::Transformation, - fill::FillType, - args...) - warpedview(_build_extrapolation(A, Linear(), fill), tform, args...) -end diff --git a/test/warp.jl b/test/warp.jl index a72361e..55ae1e9 100644 --- a/test/warp.jl +++ b/test/warp.jl @@ -15,36 +15,32 @@ img_camera = testimage("camera") ref_inds = (-78:591, -78:591) @testset "warp" begin - imgr = @inferred(warp(img_camera, tfm)) + imgr = @inferred(warp(img_camera, inv(tfm))) @test indices(imgr) == ref_inds @test eltype(imgr) == eltype(img_camera) - imgr = @inferred(warp(img_camera, tfm, 1)) + imgr = @inferred(warp(img_camera, inv(tfm), 1)) @test eltype(imgr) == eltype(img_camera) - imgr2 = @inferred warp(imgr, inv(tfm)) + imgr2 = @inferred warp(imgr, tfm) @test eltype(imgr2) == eltype(img_camera) # look the same but are not similar enough to pass test # @test imgr2[indices(img_camera)...] ≈ img_camera end - @testset "WarpedView" begin - wv = @inferred(WarpedView(img_camera, tfm)) - @test summary(wv) == "-78:591×-78:591 WarpedView(::Array{Gray{N0f8},2}, AffineMap([0.92388 0.382683; -0.382683 0.92388], [-78.6334,$(SPACE)117.683])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @testset "InvWarpedView" begin + wv = @inferred(InvWarpedView(img_camera, tfm)) + @test summary(wv) == "-78:591×-78:591 InvWarpedView(::Array{Gray{N0f8},2}, AffineMap([0.92388 0.382683; -0.382683 0.92388], [-78.6334,$(SPACE)117.683])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" @test_throws ErrorException size(wv) @test_throws ErrorException size(wv, 1) @test indices(wv) == ref_inds @test eltype(wv) === eltype(img_camera) - @test typeof(parent(wv)) <: Interpolations.AbstractExtrapolation - @test typeof(parent(wv).itp) <: Interpolations.AbstractInterpolation - @test parent(wv).itp.coefs === img_camera + @test parent(wv) === img_camera # check nested transformation using the inverse - wv2 = @inferred(WarpedView(wv, inv(tfm))) + wv2 = @inferred(InvWarpedView(wv, inv(tfm))) @test indices(wv2) == indices(img_camera) @test eltype(wv2) === eltype(img_camera) - @test typeof(parent(wv2)) <: Interpolations.AbstractExtrapolation - @test typeof(parent(wv2).itp) <: Interpolations.AbstractInterpolation - @test parent(wv2).itp.coefs === img_camera + @test parent(wv2) === img_camera @test wv2 ≈ img_camera end end @@ -82,42 +78,43 @@ ref_img_pyramid_quad = Float64[ tfm2 = LinearMap(RotMatrix(-pi/4)) @testset "warp" begin - imgr = warp(img_pyramid, tfm1) + imgr = warp(img_pyramid, inv(tfm1)) @test indices(imgr) == (0:6, 0:6) @test eltype(imgr) == eltype(img_pyramid) # Use map and === because of the NaNs @test nearlysame(round.(Float64.(parent(imgr)),3), round.(ref_img_pyramid,3)) @testset "OffsetArray" begin - imgr_cntr = warp(img_pyramid_cntr, tfm2) + imgr_cntr = warp(img_pyramid_cntr, inv(tfm2)) @test indices(imgr_cntr) == (-3:3, -3:3) @test nearlysame(parent(imgr_cntr), parent(imgr)) end @testset "Quadratic Interpolation" begin itp = interpolate(img_pyramid_cntr, BSpline(Quadratic(Flat())), OnCell()) - imgrq_cntr = warp(itp, tfm2) + imgrq_cntr = warp(itp, inv(tfm2)) @test indices(imgrq_cntr) == (-3:3, -3:3) @test nearlysame(round.(Float64.(parent(imgrq_cntr)),3), round.(ref_img_pyramid_quad,3)) end end - @testset "WarpedView" begin - imgr = WarpedView(img_pyramid, tfm1) + @testset "InvWarpedView" begin + imgr = InvWarpedView(img_pyramid, tfm1) @test indices(imgr) == (0:6, 0:6) # Use map and === because of the NaNs @test nearlysame(round.(Float64.(imgr[0:6, 0:6]),3), round.(ref_img_pyramid,3)) @testset "OffsetArray" begin - imgr_cntr = WarpedView(img_pyramid_cntr, tfm2) + imgr_cntr = InvWarpedView(img_pyramid_cntr, tfm2) @test indices(imgr_cntr) == (-3:3, -3:3) @test nearlysame(imgr_cntr[indices(imgr_cntr)...], imgr[indices(imgr)...]) end @testset "Quadratic Interpolation" begin itp = interpolate(img_pyramid_cntr, BSpline(Quadratic(Flat())), OnCell()) - imgrq_cntr = WarpedView(itp, tfm2) - @test summary(imgrq_cntr) == "-3:3×-3:3 WarpedView(interpolate(::OffsetArray{Gray{Float64},2}, BSpline(Quadratic(Flat())), OnCell()), LinearMap([0.707107 0.707107; -0.707107 0.707107])) with element type ColorTypes.Gray{Float64}" + imgrq_cntr = InvWarpedView(itp, tfm2) + @test parent(imgrq_cntr) === itp + @test summary(imgrq_cntr) == "-3:3×-3:3 InvWarpedView(interpolate(::OffsetArray{Gray{Float64},2}, BSpline(Quadratic(Flat())), OnCell()), LinearMap([0.707107 0.707107; -0.707107 0.707107])) with element type ColorTypes.Gray{Float64}" @test indices(imgrq_cntr) == (-3:3, -3:3) @test nearlysame(round.(Float64.(imgrq_cntr[indices(imgrq_cntr)...]),3), round.(ref_img_pyramid_quad,3)) end From c2576224df4bdcbbecc633b16421b00ba3486d81 Mon Sep 17 00:00:00 2001 From: Christof Stocker Date: Sat, 22 Apr 2017 00:26:32 +0200 Subject: [PATCH 04/18] add ImageInTerminal style visual tests --- test/REQUIRE | 1 + test/reference/invwarpedview_cameraman.txt | 17 ++++++ .../invwarpedview_cameraman_rotate_r22deg.txt | 11 ++++ test/reference/warp_cameraman.txt | 17 ++++++ .../warp_cameraman_rotate_r22deg.txt | 11 ++++ .../warp_cameraman_rotate_r22deg_white.txt | 11 ++++ test/runtests.jl | 58 ++++++++++++++++++- test/warp.jl | 11 ++-- 8 files changed, 131 insertions(+), 6 deletions(-) create mode 100644 test/reference/invwarpedview_cameraman.txt create mode 100644 test/reference/invwarpedview_cameraman_rotate_r22deg.txt create mode 100644 test/reference/warp_cameraman.txt create mode 100644 test/reference/warp_cameraman_rotate_r22deg.txt create mode 100644 test/reference/warp_cameraman_rotate_r22deg_white.txt diff --git a/test/REQUIRE b/test/REQUIRE index d27d873..29fce5c 100644 --- a/test/REQUIRE +++ b/test/REQUIRE @@ -1,5 +1,6 @@ TestImages FixedPointNumbers 0.3.0 +ImageInTerminal @windows ImageMagick @linux ImageMagick @osx QuartzImageIO diff --git a/test/reference/invwarpedview_cameraman.txt b/test/reference/invwarpedview_cameraman.txt new file mode 100644 index 0000000..ed6ef35 --- /dev/null +++ b/test/reference/invwarpedview_cameraman.txt @@ -0,0 +1,17 @@ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ \ No newline at end of file diff --git a/test/reference/invwarpedview_cameraman_rotate_r22deg.txt b/test/reference/invwarpedview_cameraman_rotate_r22deg.txt new file mode 100644 index 0000000..aebdc97 --- /dev/null +++ b/test/reference/invwarpedview_cameraman_rotate_r22deg.txt @@ -0,0 +1,11 @@ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ \ No newline at end of file diff --git a/test/reference/warp_cameraman.txt b/test/reference/warp_cameraman.txt new file mode 100644 index 0000000..cd72d92 --- /dev/null +++ b/test/reference/warp_cameraman.txt @@ -0,0 +1,17 @@ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ \ No newline at end of file diff --git a/test/reference/warp_cameraman_rotate_r22deg.txt b/test/reference/warp_cameraman_rotate_r22deg.txt new file mode 100644 index 0000000..aebdc97 --- /dev/null +++ b/test/reference/warp_cameraman_rotate_r22deg.txt @@ -0,0 +1,11 @@ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ \ No newline at end of file diff --git a/test/reference/warp_cameraman_rotate_r22deg_white.txt b/test/reference/warp_cameraman_rotate_r22deg_white.txt new file mode 100644 index 0000000..8861851 --- /dev/null +++ b/test/reference/warp_cameraman_rotate_r22deg_white.txt @@ -0,0 +1,11 @@ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 5887ab4..600c1c5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,11 +1,67 @@ using CoordinateTransformations, TestImages, ImageCore, Colors, FixedPointNumbers, OffsetArrays, Interpolations -using Base.Test +using Base.Test, ImageInTerminal refambs = detect_ambiguities(CoordinateTransformations, Base, Core) using ImageTransformations ambs = detect_ambiguities(ImageTransformations, CoordinateTransformations, Base, Core) @test isempty(setdiff(ambs, refambs)) +reference_path(filename) = joinpath(dirname(@__FILE__), "reference", "$(filename).txt") + +function test_reference_impl{T<:Colorant}(filename, img::AbstractArray{T}) + res = ImageInTerminal.encodeimg(ImageInTerminal.SmallBlocks(), ImageInTerminal.TermColor256(), img, 20, 40)[1] + test_reference_impl(filename, res) +end + +function test_reference_impl{T<:String}(filename, actual::AbstractArray{T}) + try + reference = replace.(readlines(reference_path(filename)), ["\n"], [""]) + try + @assert reference == actual # to throw error + catch # test failed + println("Test for \"$filename\" failed.") + println("- REFERENCE -------------------") + println.(reference) + println("-------------------------------") + println("- ACTUAL ----------------------") + println.(actual) + println("-------------------------------") + if isinteractive() + print("Replace reference with actual result? [y/n] ") + answer = first(readline()) + if answer == 'y' + write(reference_path(filename), join(actual, "\n")) + end + else + error("You need to run the tests interactively with 'include(\"test/runtests.jl\")' to update reference images") + end + end + catch ex + if isa(ex, SystemError) # File doesn't exist + println("Reference file for \"$filename\" does not exist.") + println("- NEW CONTENT -----------------") + println.(actual) + println("-------------------------------") + if isinteractive() + print("Create reference file with above content? [y/n] ") + answer = first(readline()) + if answer == 'y' + write(reference_path(filename), join(actual, "\n")) + end + else + error("You need to run the tests interactively with 'include(\"test/runtests.jl\")' to create new reference images") + end + else + throw(ex) + end + end +end + +# using a macro looks more consistent +macro test_reference(filename, expr) + esc(:(test_reference_impl($filename, $expr))) +end + tests = [ "autorange.jl", "resizing.jl", diff --git a/test/warp.jl b/test/warp.jl index 55ae1e9..bc6f509 100644 --- a/test/warp.jl +++ b/test/warp.jl @@ -3,11 +3,7 @@ nearlysame(x, y) = x ≈ y || (isnan(x) & isnan(y)) nearlysame(A::AbstractArray, B::AbstractArray) = all(map(nearlysame, A, B)) #img_square = Gray{N0f8}.(reshape(linspace(0,1,9), (3,3))) -SPACE = if VERSION < v"0.6.0-dev.2505" # julia PR #20288 - "" -else - " " -end +SPACE = VERSION < v"0.6.0-dev.2505" ? "" : " " # julia PR #20288 img_camera = testimage("camera") @testset "Constructor" begin @@ -18,11 +14,14 @@ img_camera = testimage("camera") imgr = @inferred(warp(img_camera, inv(tfm))) @test indices(imgr) == ref_inds @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg" imgr imgr = @inferred(warp(img_camera, inv(tfm), 1)) @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_white" imgr imgr2 = @inferred warp(imgr, tfm) @test eltype(imgr2) == eltype(img_camera) + @test_reference "warp_cameraman" imgr2[indices(img_camera)...] # look the same but are not similar enough to pass test # @test imgr2[indices(img_camera)...] ≈ img_camera end @@ -30,6 +29,7 @@ img_camera = testimage("camera") @testset "InvWarpedView" begin wv = @inferred(InvWarpedView(img_camera, tfm)) @test summary(wv) == "-78:591×-78:591 InvWarpedView(::Array{Gray{N0f8},2}, AffineMap([0.92388 0.382683; -0.382683 0.92388], [-78.6334,$(SPACE)117.683])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test_reference "invwarpedview_cameraman_rotate_r22deg" wv @test_throws ErrorException size(wv) @test_throws ErrorException size(wv, 1) @test indices(wv) == ref_inds @@ -38,6 +38,7 @@ img_camera = testimage("camera") # check nested transformation using the inverse wv2 = @inferred(InvWarpedView(wv, inv(tfm))) + @test_reference "invwarpedview_cameraman" wv2 @test indices(wv2) == indices(img_camera) @test eltype(wv2) === eltype(img_camera) @test parent(wv2) === img_camera From 38bcb4037e793980c87adf3950f61c0a6ae27a5e Mon Sep 17 00:00:00 2001 From: Christof Stocker Date: Sat, 22 Apr 2017 17:04:49 +0200 Subject: [PATCH 05/18] enable color on travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1697ed6..fdaf071 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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' From 6a2d808e728472950166e75f1f286c6efb0dbbd6 Mon Sep 17 00:00:00 2001 From: Christof Stocker Date: Sat, 22 Apr 2017 17:17:46 +0200 Subject: [PATCH 06/18] try to enable color on appveyor --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 9306951..aeae9ef 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -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\")" From e5e6ccb140e832c0921481b567120cbf10d15054 Mon Sep 17 00:00:00 2001 From: Christof Stocker Date: Sun, 23 Apr 2017 23:52:52 +0200 Subject: [PATCH 07/18] add tests for warp helper functions --- src/ImageTransformations.jl | 1 + src/interpolations.jl | 41 +++++++++++++++++ src/warp.jl | 36 --------------- test/interpolations.jl | 89 +++++++++++++++++++++++++++++++++++++ test/runtests.jl | 2 + 5 files changed, 133 insertions(+), 36 deletions(-) create mode 100644 src/interpolations.jl create mode 100644 test/interpolations.jl diff --git a/src/ImageTransformations.jl b/src/ImageTransformations.jl index af450cb..0f64577 100644 --- a/src/ImageTransformations.jl +++ b/src/ImageTransformations.jl @@ -26,6 +26,7 @@ export include("autorange.jl") include("resizing.jl") +include("interpolations.jl") include("warp.jl") include("invwarpedview.jl") diff --git a/src/interpolations.jl b/src/interpolations.jl new file mode 100644 index 0000000..26ad64d --- /dev/null +++ b/src/interpolations.jl @@ -0,0 +1,41 @@ +# 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(parent::AbstractArray, fill::FillType) + _box_extrapolation(parent, Linear(), fill) +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 diff --git a/src/warp.jl b/src/warp.jl index 3d84d21..37b8d69 100644 --- a/src/warp.jl +++ b/src/warp.jl @@ -1,27 +1,3 @@ -# 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(parent::AbstractArray, fill::FillType) - _box_extrapolation(parent, Linear(), fill) -end - """ warp(img, tform, [indices], [degree = Linear()], [fill = NaN]) -> imgw @@ -116,15 +92,3 @@ function warp{T,N}(img::AbstractArray{T,N}, tform, args...) etp = _box_extrapolation(img, args...) warp(etp, tform) 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 diff --git a/test/interpolations.jl b/test/interpolations.jl new file mode 100644 index 0000000..81f5ed6 --- /dev/null +++ b/test/interpolations.jl @@ -0,0 +1,89 @@ +@testset "_default_fill" begin + @test_throws UndefVarError _default_fill + @test typeof(ImageTransformations._default_fill) <: Function + import ImageTransformations._default_fill + + @test @inferred(_default_fill(N0f8)) === N0f8(0) + @test @inferred(_default_fill(Int)) === 0 + @test @inferred(_default_fill(Float16)) === NaN16 + @test @inferred(_default_fill(Float32)) === NaN32 + @test @inferred(_default_fill(Float64)) === NaN + + @test @inferred(_default_fill(Gray{N0f8})) === Gray{N0f8}(0) + @test @inferred(_default_fill(Gray{Float16})) === Gray{Float16}(NaN16) + @test @inferred(_default_fill(Gray{Float32})) === Gray{Float32}(NaN32) + @test @inferred(_default_fill(Gray{Float64})) === Gray{Float64}(NaN) + + @test @inferred(_default_fill(GrayA{N0f8})) === GrayA{N0f8}(0,0) + @test @inferred(_default_fill(GrayA{Float16})) === GrayA{Float16}(NaN16,NaN16) + @test @inferred(_default_fill(GrayA{Float32})) === GrayA{Float32}(NaN32,NaN32) + @test @inferred(_default_fill(GrayA{Float64})) === GrayA{Float64}(NaN,NaN) + + @test @inferred(_default_fill(RGB{N0f8})) === RGB{N0f8}(0,0,0) + @test @inferred(_default_fill(RGB{Float16})) === RGB{Float16}(NaN16,NaN16,NaN16) + @test @inferred(_default_fill(RGB{Float32})) === RGB{Float32}(NaN32,NaN32,NaN32) + @test @inferred(_default_fill(RGB{Float64})) === RGB{Float64}(NaN,NaN,NaN) + + @test @inferred(_default_fill(RGBA{N0f8})) === RGBA{N0f8}(0,0,0,0) + @test @inferred(_default_fill(RGBA{Float16})) === RGBA{Float16}(NaN16,NaN16,NaN16,NaN16) + @test @inferred(_default_fill(RGBA{Float32})) === RGBA{Float32}(NaN32,NaN32,NaN32,NaN32) + @test @inferred(_default_fill(RGBA{Float64})) === RGBA{Float64}(NaN,NaN,NaN,NaN) + + @test @inferred(_default_fill(HSV{Float16})) === HSV{Float16}(NaN16,NaN16,NaN16) + @test @inferred(_default_fill(HSV{Float32})) === HSV{Float32}(NaN32,NaN32,NaN32) + @test @inferred(_default_fill(HSV{Float64})) === HSV{Float64}(NaN,NaN,NaN) +end + +@testset "_box_extrapolation" begin + @test_throws UndefVarError _box_extrapolation + @test typeof(ImageTransformations._box_extrapolation) <: Function + import ImageTransformations._box_extrapolation + + img = rand(Gray{N0f8}, 2, 2) + + etp = @inferred _box_extrapolation(img) + @test @inferred(_box_extrapolation(etp)) === etp + @test summary(etp) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Gray{N0f8}(0.0)) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test typeof(etp) <: Interpolations.FilledExtrapolation + @test etp.fillvalue === Gray{N0f8}(0.0) + @test etp.itp.coefs === img + + etp2 = @inferred _box_extrapolation(etp.itp) + @test summary(etp2) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Gray{N0f8}(0.0)) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test typeof(etp2) <: Interpolations.FilledExtrapolation + @test etp2.fillvalue === Gray{N0f8}(0.0) + @test etp2 !== etp + @test etp2.itp === etp.itp + + etp2 = @inferred _box_extrapolation(etp.itp, Flat()) + @test summary(etp2) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Flat()) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test typeof(etp2) <: Interpolations.Extrapolation + @test etp2 !== etp + @test etp2.itp === etp.itp + + etp = @inferred _box_extrapolation(img, 1) + @test summary(etp) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Gray{N0f8}(1.0)) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test typeof(etp) <: Interpolations.FilledExtrapolation + @test etp.fillvalue === Gray{N0f8}(1.0) + @test etp.itp.coefs === img + + etp = @inferred _box_extrapolation(img, Flat()) + @test @inferred(_box_extrapolation(etp)) === etp + @test summary(etp) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Flat()) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test typeof(etp) <: Interpolations.Extrapolation + @test etp.itp.coefs === img + + etp = @inferred _box_extrapolation(img, Constant()) + @test summary(etp) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Constant()), OnGrid()), Gray{N0f8}(0.0)) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test typeof(etp) <: Interpolations.FilledExtrapolation + @test etp.itp.coefs === img + + etp = @inferred _box_extrapolation(img, Constant(), Flat()) + @test summary(etp) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Constant()), OnGrid()), Flat()) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test typeof(etp) <: Interpolations.Extrapolation + @test etp.itp.coefs === img +end + +@testset "AxisAlgorithms.A_ldiv_B_md" begin + # TODO +end diff --git a/test/runtests.jl b/test/runtests.jl index 600c1c5..dc432e5 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -18,6 +18,7 @@ function test_reference_impl{T<:String}(filename, actual::AbstractArray{T}) reference = replace.(readlines(reference_path(filename)), ["\n"], [""]) try @assert reference == actual # to throw error + @test true # to increase test counter if reached catch # test failed println("Test for \"$filename\" failed.") println("- REFERENCE -------------------") @@ -65,6 +66,7 @@ end tests = [ "autorange.jl", "resizing.jl", + "interpolations.jl", "warp.jl", ] From 28aa3f7f22e8a08a46a344df82876bb5d61e99dc Mon Sep 17 00:00:00 2001 From: Christof Stocker Date: Tue, 25 Apr 2017 11:35:39 +0200 Subject: [PATCH 08/18] improve warp helper functions --- src/interpolations.jl | 22 +++++++++++++++------- src/invwarpedview.jl | 4 ++-- src/warp.jl | 4 ++-- test/interpolations.jl | 33 ++++++++++++++++++++------------- 4 files changed, 39 insertions(+), 24 deletions(-) diff --git a/src/interpolations.jl b/src/interpolations.jl index 26ad64d..43c195d 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -12,20 +12,28 @@ @inline _default_fill{T<:FloatColorant}(::Type{T}) = _nan(T) @inline _default_fill{T}(::Type{T}) = zero(T) -_box_extrapolation(etp::AbstractExtrapolation) = etp +box_extrapolation(etp::AbstractExtrapolation) = etp -function _box_extrapolation{T}(itp::AbstractInterpolation{T}, fill::FillType = _default_fill(T)) +function box_extrapolation{T}(itp::AbstractInterpolation{T}, fill::FillType = _default_fill(T)) etp = extrapolate(itp, fill) - _box_extrapolation(etp) + box_extrapolation(etp) end -function _box_extrapolation{T,N,D<:Union{Linear,Constant}}(parent::AbstractArray{T,N}, degree::D = Linear(), args...) +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...) + box_extrapolation(itp, args...) end -function _box_extrapolation(parent::AbstractArray, fill::FillType) - _box_extrapolation(parent, Linear(), fill) +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::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 diff --git a/src/invwarpedview.jl b/src/invwarpedview.jl index 0cae391..75b8883 100644 --- a/src/invwarpedview.jl +++ b/src/invwarpedview.jl @@ -10,7 +10,7 @@ immutable InvWarpedView{T,N,A<:AbstractArray,F1<:Transformation,I,F2<:Transforma tform::F, indices::I) @assert eltype(parent) == T - etp = _box_extrapolation(parent) + etp = box_extrapolation(parent) tinv = inv(tform) new{T,N,TA,F,I,typeof(tinv),typeof(etp)}(parent, tform, indices, tinv, etp) end @@ -62,7 +62,7 @@ function invwarpedview{T}( degree::Union{Linear,Constant}, fill::FillType = _default_fill(T), args...) - invwarpedview(_box_extrapolation(A, degree, fill), tform, args...) + invwarpedview(box_extrapolation(A, degree, fill), tform, args...) end function invwarpedview( diff --git a/src/warp.jl b/src/warp.jl index 37b8d69..e39f118 100644 --- a/src/warp.jl +++ b/src/warp.jl @@ -84,11 +84,11 @@ function warp!(out, img::AbstractExtrapolation, tform) end function warp{T,N}(img::AbstractArray{T,N}, tform, inds::Tuple, args...) - etp = _box_extrapolation(img, args...) + etp = box_extrapolation(img, args...) warp(etp, tform, inds) end function warp{T,N}(img::AbstractArray{T,N}, tform, args...) - etp = _box_extrapolation(img, args...) + etp = box_extrapolation(img, args...) warp(etp, tform) end diff --git a/test/interpolations.jl b/test/interpolations.jl index 81f5ed6..fb89c00 100644 --- a/test/interpolations.jl +++ b/test/interpolations.jl @@ -34,51 +34,58 @@ @test @inferred(_default_fill(HSV{Float64})) === HSV{Float64}(NaN,NaN,NaN) end -@testset "_box_extrapolation" begin - @test_throws UndefVarError _box_extrapolation - @test typeof(ImageTransformations._box_extrapolation) <: Function - import ImageTransformations._box_extrapolation +@testset "box_extrapolation" begin + @test_throws UndefVarError box_extrapolation + @test typeof(ImageTransformations.box_extrapolation) <: Function + import ImageTransformations.box_extrapolation img = rand(Gray{N0f8}, 2, 2) - etp = @inferred _box_extrapolation(img) - @test @inferred(_box_extrapolation(etp)) === etp + etp = @inferred box_extrapolation(img) + @test @inferred(box_extrapolation(etp)) === etp @test summary(etp) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Gray{N0f8}(0.0)) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" @test typeof(etp) <: Interpolations.FilledExtrapolation @test etp.fillvalue === Gray{N0f8}(0.0) @test etp.itp.coefs === img - etp2 = @inferred _box_extrapolation(etp.itp) + @test_throws ArgumentError box_extrapolation(etp, 0) + @test_throws ArgumentError box_extrapolation(etp, Flat()) + @test_throws ArgumentError box_extrapolation(etp, Constant()) + @test_throws ArgumentError box_extrapolation(etp, Constant(), Flat()) + @test_throws ArgumentError box_extrapolation(etp.itp, Constant()) + @test_throws ArgumentError box_extrapolation(etp.itp, Constant(), Flat()) + + etp2 = @inferred box_extrapolation(etp.itp) @test summary(etp2) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Gray{N0f8}(0.0)) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" @test typeof(etp2) <: Interpolations.FilledExtrapolation @test etp2.fillvalue === Gray{N0f8}(0.0) @test etp2 !== etp @test etp2.itp === etp.itp - etp2 = @inferred _box_extrapolation(etp.itp, Flat()) + etp2 = @inferred box_extrapolation(etp.itp, Flat()) @test summary(etp2) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Flat()) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" @test typeof(etp2) <: Interpolations.Extrapolation @test etp2 !== etp @test etp2.itp === etp.itp - etp = @inferred _box_extrapolation(img, 1) + etp = @inferred box_extrapolation(img, 1) @test summary(etp) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Gray{N0f8}(1.0)) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" @test typeof(etp) <: Interpolations.FilledExtrapolation @test etp.fillvalue === Gray{N0f8}(1.0) @test etp.itp.coefs === img - etp = @inferred _box_extrapolation(img, Flat()) - @test @inferred(_box_extrapolation(etp)) === etp + etp = @inferred box_extrapolation(img, Flat()) + @test @inferred(box_extrapolation(etp)) === etp @test summary(etp) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Flat()) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" @test typeof(etp) <: Interpolations.Extrapolation @test etp.itp.coefs === img - etp = @inferred _box_extrapolation(img, Constant()) + etp = @inferred box_extrapolation(img, Constant()) @test summary(etp) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Constant()), OnGrid()), Gray{N0f8}(0.0)) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" @test typeof(etp) <: Interpolations.FilledExtrapolation @test etp.itp.coefs === img - etp = @inferred _box_extrapolation(img, Constant(), Flat()) + etp = @inferred box_extrapolation(img, Constant(), Flat()) @test summary(etp) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Constant()), OnGrid()), Flat()) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" @test typeof(etp) <: Interpolations.Extrapolation @test etp.itp.coefs === img From 2bdbce4198767451b7573c06ae4eaceb3d6c97f3 Mon Sep 17 00:00:00 2001 From: Christof Stocker Date: Wed, 26 Apr 2017 20:18:24 +0200 Subject: [PATCH 09/18] prototype deprecation strategy --- src/warp.jl | 103 ++++++++++++++++++++++++++++++++++++++++--- test/deprecations.jl | 7 +++ test/runtests.jl | 1 + test/warp.jl | 4 +- 4 files changed, 109 insertions(+), 6 deletions(-) create mode 100644 test/deprecations.jl diff --git a/src/warp.jl b/src/warp.jl index e39f118..0e6247e 100644 --- a/src/warp.jl +++ b/src/warp.jl @@ -71,11 +71,12 @@ julia> indices(imgr) (Base.OneTo(906),Base.OneTo(905)) ``` """ -function warp{T}(img::AbstractExtrapolation{T}, tform, inds::Tuple = autorange(img, inv(tform))) +function warp_new{T}(img::AbstractExtrapolation{T}, tform, inds::Tuple = autorange(img, inv(tform))) out = OffsetArray(Array{T}(map(length, inds)), inds) warp!(out, img, tform) end +# this function was never exported, so no need to deprecate function warp!(out, img::AbstractExtrapolation, tform) @inbounds for I in CartesianRange(indices(out)) out[I] = _getindex(img, tform(SVector(I.I))) @@ -83,12 +84,104 @@ function warp!(out, img::AbstractExtrapolation, tform) out end -function warp{T,N}(img::AbstractArray{T,N}, tform, inds::Tuple, args...) +function warp_new(img::AbstractArray, tform, inds::Tuple, args...) etp = box_extrapolation(img, args...) - warp(etp, tform, inds) + warp_new(etp, tform, inds) end -function warp{T,N}(img::AbstractArray{T,N}, tform, args...) +function warp_new(img::AbstractArray, tform, args...) etp = box_extrapolation(img, args...) - warp(etp, tform) + warp_new(etp, tform) end + +# # after deprecation period: +# @deprecate warp_new(img::AbstractArray, tform, args...) warp(img, tform, args...) +# @deprecate warp_old(img::AbstractArray, tform, args...) warp(img, tform, args...) + +""" + warp(img, tform, [indices], [degree = Linear()], [fill = NaN]) -> imgw + +!!! warning "Deprecation Warning!" + + This method with the signature `warp(img, tform, args...)` is + deprecated in favour of the cleaner interpretation `warp(img, + inv(tform), args...)`. Set `const warp = + ImageTransformations.warp_new` right after package import to + change to the new behaviour right away. + +Transform the coordinates of `img`, returning a new `imgw` +satisfying `imgw[I] = img[inv(tform(I))]`. This approach is known +as backward mode warping. The transformation `tform` should be +defined using +[CoordinateTransformations.jl](https://github.com/FugroRoames/CoordinateTransformations.jl). + +# Interpolation scheme + +At off-grid points, `imgw` is calculated by interpolation. The +degree of the b-spline can be specified with the optional +parameter `degree`, which can take the values `Linear()` or +`Constant()`. + +The b-spline interpolation is used when `img` is a plain array +and `img[inv(tform(I))]` is inbound. In the case `inv(tform(I))` +maps to indices outside the original `img`, the value or +extrapolation scheme denoted by the optional parameter `fill` +(which defaults to `NaN` if the element type supports it, and `0` +otherwise) is used to indicate locations for which +`inv(tform(I))` was outside the bounds of the input `img`. + +For more control over the interpolation scheme --- and how +beyond-the-edge points are handled --- pass it in as an +`AbstractInterpolation` or `AbstractExtrapolation` from +[Interpolations.jl](https://github.com/JuliaMath/Interpolations.jl). + +# The meaning of the coordinates + +The output array `imgw` has indices that would result from applying +`tform` to the indices of `img`. This can be very handy for keeping +track of how pixels in `imgw` line up with pixels in `img`. + +If you just want a plain array, you can "strip" the custom indices +with `parent(imgw)`. + +# Examples: a 2d rotation (see JuliaImages documentation for pictures) + +```jldoctest +julia> using Images, CoordinateTransformations, TestImages, OffsetArrays + +julia> img = testimage("lighthouse"); + +julia> indices(img) +(Base.OneTo(512),Base.OneTo(768)) + +# Rotate around the center of `img` +julia> tfm = recenter(RotMatrix(pi/4), center(img)) +AffineMap([0.707107 -0.707107; 0.707107 0.707107], [347.01,-68.7554]) + +julia> imgw = warp(img, tfm); + +julia> indices(imgw) +(-196:709,-68:837) + +# Alternatively, specify the origin in the image itself +julia> img0 = OffsetArray(img, -30:481, -384:383); # origin near top of image + +julia> rot = LinearMap(RotMatrix(pi/4)) +LinearMap([0.707107 -0.707107; 0.707107 0.707107]) + +julia> imgw = warp(img0, rot); + +julia> indices(imgw) +(-293:612,-293:611) + +julia> imgr = parent(imgw); + +julia> indices(imgr) +(Base.OneTo(906),Base.OneTo(905)) +``` +""" +@generated function warp_old(img::AbstractArray, tform, args...) + warn("'warp(img, tform)' is deprecated in favour of the cleaner interpretation 'warp(img, inv(tform))'. Set 'const warp = ImageTransformations.warp_new' right after package import to change to the new behaviour right away.") + :(warp_new(img, inv(tform), args...)) +end +const warp = warp_old diff --git a/test/deprecations.jl b/test/deprecations.jl new file mode 100644 index 0000000..8f2624c --- /dev/null +++ b/test/deprecations.jl @@ -0,0 +1,7 @@ +info("Start of deprecation warnings") + +img_camera = testimage("camera") +tfm = recenter(RotMatrix(-pi/8), center(img_camera)) +imgr = @inferred(ImageTransformations.warp_old(img_camera, tfm)) +@test eltype(imgr) == eltype(img_camera) +@test_reference "warp_cameraman_rotate_r22deg" imgr diff --git a/test/runtests.jl b/test/runtests.jl index dc432e5..8b54712 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -68,6 +68,7 @@ tests = [ "resizing.jl", "interpolations.jl", "warp.jl", + "deprecations.jl", ] for t in tests diff --git a/test/warp.jl b/test/warp.jl index bc6f509..a512721 100644 --- a/test/warp.jl +++ b/test/warp.jl @@ -1,3 +1,5 @@ +const warp = ImageTransformations.warp_new + # helper function to compare NaN nearlysame(x, y) = x ≈ y || (isnan(x) & isnan(y)) nearlysame(A::AbstractArray, B::AbstractArray) = all(map(nearlysame, A, B)) @@ -10,7 +12,7 @@ img_camera = testimage("camera") tfm = recenter(RotMatrix(-pi/8), center(img_camera)) ref_inds = (-78:591, -78:591) - @testset "warp" begin + @testset "warp_new" begin imgr = @inferred(warp(img_camera, inv(tfm))) @test indices(imgr) == ref_inds @test eltype(imgr) == eltype(img_camera) From 106f2d0815fe0d3fbb8c2fbf2d2d23b9c25f7d10 Mon Sep 17 00:00:00 2001 From: Christof Stocker Date: Thu, 27 Apr 2017 13:58:55 +0200 Subject: [PATCH 10/18] work in a round of reviews --- src/warp.jl | 113 ++++++++++----------------------------------------- test/warp.jl | 25 ++++++------ 2 files changed, 35 insertions(+), 103 deletions(-) diff --git a/src/warp.jl b/src/warp.jl index 0e6247e..a9d4603 100644 --- a/src/warp.jl +++ b/src/warp.jl @@ -3,7 +3,8 @@ Transform the coordinates of `img`, returning a new `imgw` satisfying `imgw[I] = img[tform(I)]`. This approach is known as backward -mode warping. The transformation `tform` should be defined using +mode warping. The transformation `tform` must accept a `SVector` as +input. A useful package to create a wide variety of such transformations is [CoordinateTransformations.jl](https://github.com/FugroRoames/CoordinateTransformations.jl). # Interpolation scheme @@ -14,15 +15,13 @@ parameter `degree`, which can take the values `Linear()` or `Constant()`. The b-spline interpolation is used when `img` is a plain array -and `img[tform(I)]` is inbound. In the case `tform(I)` maps -to indices outside the original `img`, the value or extrapolation -scheme denoted by the optional parameter `fill` (which defaults -to `NaN` if the element type supports it, and `0` otherwise) is -used to indicate locations for which `tform(I)` was outside the -bounds of the input `img`. +and `img[tform(I)]` is inbound. In the case `tform(I)` maps to +indices outside the original `img`, those locations are set to a +value `fill` (which defaults to `NaN` if the element type supports +it, and `0` otherwise). For more control over the interpolation scheme --- and how -beyond-the-edge points are handled --- pass it in as an +beyond-the-edge points are handled --- pass `img` as an `AbstractInterpolation` or `AbstractExtrapolation` from [Interpolations.jl](https://github.com/JuliaMath/Interpolations.jl). @@ -37,7 +36,7 @@ with `parent(imgw)`. # Examples: a 2d rotation (see JuliaImages documentation for pictures) -```jldoctest +``` julia> using Images, CoordinateTransformations, TestImages, OffsetArrays julia> img = testimage("lighthouse"); @@ -46,10 +45,10 @@ julia> indices(img) (Base.OneTo(512),Base.OneTo(768)) # Rotate around the center of `img` -julia> tfm = recenter(RotMatrix(pi/4), center(img)) +julia> tfm = recenter(RotMatrix(-pi/4), center(img)) AffineMap([0.707107 -0.707107; 0.707107 0.707107], [347.01,-68.7554]) -julia> imgw = warp(img, inv(tfm)); +julia> imgw = warp(img, tfm); julia> indices(imgw) (-196:709,-68:837) @@ -57,10 +56,10 @@ julia> indices(imgw) # Alternatively, specify the origin in the image itself julia> img0 = OffsetArray(img, -30:481, -384:383); # origin near top of image -julia> rot = LinearMap(RotMatrix(pi/4)) +julia> rot = LinearMap(RotMatrix(-pi/4)) LinearMap([0.707107 -0.707107; 0.707107 0.707107]) -julia> imgw = warp(img0, inv(rot)); +julia> imgw = warp(img0, rot); julia> indices(imgw) (-293:612,-293:611) @@ -99,89 +98,21 @@ end # @deprecate warp_old(img::AbstractArray, tform, args...) warp(img, tform, args...) """ - warp(img, tform, [indices], [degree = Linear()], [fill = NaN]) -> imgw - -!!! warning "Deprecation Warning!" - - This method with the signature `warp(img, tform, args...)` is - deprecated in favour of the cleaner interpretation `warp(img, - inv(tform), args...)`. Set `const warp = - ImageTransformations.warp_new` right after package import to - change to the new behaviour right away. - -Transform the coordinates of `img`, returning a new `imgw` -satisfying `imgw[I] = img[inv(tform(I))]`. This approach is known -as backward mode warping. The transformation `tform` should be -defined using -[CoordinateTransformations.jl](https://github.com/FugroRoames/CoordinateTransformations.jl). - -# Interpolation scheme - -At off-grid points, `imgw` is calculated by interpolation. The -degree of the b-spline can be specified with the optional -parameter `degree`, which can take the values `Linear()` or -`Constant()`. + warp(img, tform, [indices], [degree = Linear()], [fill = NaN]) -The b-spline interpolation is used when `img` is a plain array -and `img[inv(tform(I))]` is inbound. In the case `inv(tform(I))` -maps to indices outside the original `img`, the value or -extrapolation scheme denoted by the optional parameter `fill` -(which defaults to `NaN` if the element type supports it, and `0` -otherwise) is used to indicate locations for which -`inv(tform(I))` was outside the bounds of the input `img`. - -For more control over the interpolation scheme --- and how -beyond-the-edge points are handled --- pass it in as an -`AbstractInterpolation` or `AbstractExtrapolation` from -[Interpolations.jl](https://github.com/JuliaMath/Interpolations.jl). +`warp` is transitioning to a different interpretation of the +transformation, and you are using the old version. -# The meaning of the coordinates +More specifically, this method with the signature `warp(img, +tform, args...)` is deprecated in favour of the new +interpretation, which is equivalent to calling `warp(img, +inv(tform), args...)` right now. -The output array `imgw` has indices that would result from applying -`tform` to the indices of `img`. This can be very handy for keeping -track of how pixels in `imgw` line up with pixels in `img`. - -If you just want a plain array, you can "strip" the custom indices -with `parent(imgw)`. - -# Examples: a 2d rotation (see JuliaImages documentation for pictures) - -```jldoctest -julia> using Images, CoordinateTransformations, TestImages, OffsetArrays - -julia> img = testimage("lighthouse"); - -julia> indices(img) -(Base.OneTo(512),Base.OneTo(768)) - -# Rotate around the center of `img` -julia> tfm = recenter(RotMatrix(pi/4), center(img)) -AffineMap([0.707107 -0.707107; 0.707107 0.707107], [347.01,-68.7554]) - -julia> imgw = warp(img, tfm); - -julia> indices(imgw) -(-196:709,-68:837) - -# Alternatively, specify the origin in the image itself -julia> img0 = OffsetArray(img, -30:481, -384:383); # origin near top of image - -julia> rot = LinearMap(RotMatrix(pi/4)) -LinearMap([0.707107 -0.707107; 0.707107 0.707107]) - -julia> imgw = warp(img0, rot); - -julia> indices(imgw) -(-293:612,-293:611) - -julia> imgr = parent(imgw); - -julia> indices(imgr) -(Base.OneTo(906),Base.OneTo(905)) -``` +To change to the new behaviour, set `const warp = +ImageTransformations.warp_new` right after package import. """ @generated function warp_old(img::AbstractArray, tform, args...) - warn("'warp(img, tform)' is deprecated in favour of the cleaner interpretation 'warp(img, inv(tform))'. Set 'const warp = ImageTransformations.warp_new' right after package import to change to the new behaviour right away.") + Base.depwarn("'warp(img, tform)' is deprecated in favour of the new interpretation 'warp(img, inv(tform))'. Set 'const warp = ImageTransformations.warp_new' right after package import to change to the new behaviour right away. See https://github.com/JuliaImages/ImageTransformations.jl/issues/25 for more background information", :warp) :(warp_new(img, inv(tform), args...)) end const warp = warp_old diff --git a/test/warp.jl b/test/warp.jl index a512721..fc18f90 100644 --- a/test/warp.jl +++ b/test/warp.jl @@ -9,25 +9,26 @@ SPACE = VERSION < v"0.6.0-dev.2505" ? "" : " " # julia PR #20288 img_camera = testimage("camera") @testset "Constructor" begin - tfm = recenter(RotMatrix(-pi/8), center(img_camera)) + tfm = recenter(RotMatrix(pi/8), center(img_camera)) ref_inds = (-78:591, -78:591) @testset "warp_new" begin - imgr = @inferred(warp(img_camera, inv(tfm))) + imgr = @inferred(warp(img_camera, tfm)) @test indices(imgr) == ref_inds @test eltype(imgr) == eltype(img_camera) @test_reference "warp_cameraman_rotate_r22deg" imgr - imgr = @inferred(warp(img_camera, inv(tfm), 1)) + imgr = @inferred(warp(img_camera, tfm, 1)) @test eltype(imgr) == eltype(img_camera) @test_reference "warp_cameraman_rotate_r22deg_white" imgr - imgr2 = @inferred warp(imgr, tfm) + imgr2 = @inferred warp(imgr, inv(tfm)) @test eltype(imgr2) == eltype(img_camera) @test_reference "warp_cameraman" imgr2[indices(img_camera)...] # look the same but are not similar enough to pass test # @test imgr2[indices(img_camera)...] ≈ img_camera end + tfm = recenter(RotMatrix(-pi/8), center(img_camera)) @testset "InvWarpedView" begin wv = @inferred(InvWarpedView(img_camera, tfm)) @test summary(wv) == "-78:591×-78:591 InvWarpedView(::Array{Gray{N0f8},2}, AffineMap([0.92388 0.382683; -0.382683 0.92388], [-78.6334,$(SPACE)117.683])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" @@ -77,45 +78,45 @@ ref_img_pyramid_quad = Float64[ @testset "Result against reference" begin - tfm1 = recenter(RotMatrix(-pi/4), center(img_pyramid)) - tfm2 = LinearMap(RotMatrix(-pi/4)) + tfm1 = recenter(RotMatrix(pi/4), center(img_pyramid)) + tfm2 = LinearMap(RotMatrix(pi/4)) @testset "warp" begin - imgr = warp(img_pyramid, inv(tfm1)) + imgr = warp(img_pyramid, tfm1) @test indices(imgr) == (0:6, 0:6) @test eltype(imgr) == eltype(img_pyramid) # Use map and === because of the NaNs @test nearlysame(round.(Float64.(parent(imgr)),3), round.(ref_img_pyramid,3)) @testset "OffsetArray" begin - imgr_cntr = warp(img_pyramid_cntr, inv(tfm2)) + imgr_cntr = warp(img_pyramid_cntr, tfm2) @test indices(imgr_cntr) == (-3:3, -3:3) @test nearlysame(parent(imgr_cntr), parent(imgr)) end @testset "Quadratic Interpolation" begin itp = interpolate(img_pyramid_cntr, BSpline(Quadratic(Flat())), OnCell()) - imgrq_cntr = warp(itp, inv(tfm2)) + imgrq_cntr = warp(itp, tfm2) @test indices(imgrq_cntr) == (-3:3, -3:3) @test nearlysame(round.(Float64.(parent(imgrq_cntr)),3), round.(ref_img_pyramid_quad,3)) end end @testset "InvWarpedView" begin - imgr = InvWarpedView(img_pyramid, tfm1) + imgr = InvWarpedView(img_pyramid, inv(tfm1)) @test indices(imgr) == (0:6, 0:6) # Use map and === because of the NaNs @test nearlysame(round.(Float64.(imgr[0:6, 0:6]),3), round.(ref_img_pyramid,3)) @testset "OffsetArray" begin - imgr_cntr = InvWarpedView(img_pyramid_cntr, tfm2) + imgr_cntr = InvWarpedView(img_pyramid_cntr, inv(tfm2)) @test indices(imgr_cntr) == (-3:3, -3:3) @test nearlysame(imgr_cntr[indices(imgr_cntr)...], imgr[indices(imgr)...]) end @testset "Quadratic Interpolation" begin itp = interpolate(img_pyramid_cntr, BSpline(Quadratic(Flat())), OnCell()) - imgrq_cntr = InvWarpedView(itp, tfm2) + imgrq_cntr = InvWarpedView(itp, inv(tfm2)) @test parent(imgrq_cntr) === itp @test summary(imgrq_cntr) == "-3:3×-3:3 InvWarpedView(interpolate(::OffsetArray{Gray{Float64},2}, BSpline(Quadratic(Flat())), OnCell()), LinearMap([0.707107 0.707107; -0.707107 0.707107])) with element type ColorTypes.Gray{Float64}" @test indices(imgrq_cntr) == (-3:3, -3:3) From a2aa62768d7c28e02cb0b8cc91f7d0e7489ff658 Mon Sep 17 00:00:00 2001 From: Christof Stocker Date: Thu, 27 Apr 2017 23:57:12 +0200 Subject: [PATCH 11/18] work in a round of reviews --- src/interpolations.jl | 7 +- src/resizing.jl | 1 + src/warp.jl | 51 +++++---- test/interpolations.jl | 103 ++++++++++-------- .../warp_cameraman_rotate_r22deg_flat.txt | 11 ++ .../warp_cameraman_rotate_r22deg_periodic.txt | 11 ++ test/warp.jl | 31 ++++++ 7 files changed, 143 insertions(+), 72 deletions(-) create mode 100644 test/reference/warp_cameraman_rotate_r22deg_flat.txt create mode 100644 test/reference/warp_cameraman_rotate_r22deg_periodic.txt diff --git a/src/interpolations.jl b/src/interpolations.jl index 43c195d..2af7e62 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -24,11 +24,16 @@ function box_extrapolation{T,N,D<:Union{Linear,Constant}}(parent::AbstractArray{ 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...) +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 diff --git a/src/resizing.jl b/src/resizing.jl index 67ba6d8..73b4d48 100644 --- a/src/resizing.jl +++ b/src/resizing.jl @@ -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 diff --git a/src/warp.jl b/src/warp.jl index a9d4603..8d2a649 100644 --- a/src/warp.jl +++ b/src/warp.jl @@ -1,38 +1,41 @@ """ warp(img, tform, [indices], [degree = Linear()], [fill = NaN]) -> imgw -Transform the coordinates of `img`, returning a new `imgw` satisfying -`imgw[I] = img[tform(I)]`. This approach is known as backward -mode warping. The transformation `tform` must accept a `SVector` as -input. A useful package to create a wide variety of such transformations is +Transform the coordinates of `img`, returning a new `imgw` +satisfying `imgw[I] = img[tform(I)]`. This approach is known as +backward mode warping. The transformation `tform` must accept a +`SVector` as input. A useful package to create a wide variety of +such transformations is [CoordinateTransformations.jl](https://github.com/FugroRoames/CoordinateTransformations.jl). -# Interpolation scheme +# Reconstruction scheme -At off-grid points, `imgw` is calculated by interpolation. The -degree of the b-spline can be specified with the optional -parameter `degree`, which can take the values `Linear()` or -`Constant()`. +During warping, values for `img` must be reconstructed at +arbitrary locations `tform(I)` which do not lie on to the lattice +of pixels. How this reconstruction is done depends on the type of +`img` and the optional parameter `degree`. -The b-spline interpolation is used when `img` is a plain array -and `img[tform(I)]` is inbound. In the case `tform(I)` maps to -indices outside the original `img`, those locations are set to a -value `fill` (which defaults to `NaN` if the element type supports -it, and `0` otherwise). +When `img` is a plain array, b-spline interpolation is used with +`degree = Linear()` for linear interpolation or `degree = +Constant()` for nearest neighbor interpolation. In the case +`tform(I)` maps to indices outside the original `img`, those +locations are set to a value `fill` (which defaults to `NaN` if +the element type supports it, and `0` otherwise). -For more control over the interpolation scheme --- and how +For more control over the reconstruction scheme --- and how beyond-the-edge points are handled --- pass `img` as an `AbstractInterpolation` or `AbstractExtrapolation` from [Interpolations.jl](https://github.com/JuliaMath/Interpolations.jl). # The meaning of the coordinates -The output array `imgw` has indices that would result from applying -`inv(tform)` to the indices of `img`. This can be very handy for keeping -track of how pixels in `imgw` line up with pixels in `img`. +The output array `imgw` has indices that would result from +applying `inv(tform)` to the indices of `img`. This can be very +handy for keeping track of how pixels in `imgw` line up with +pixels in `img`. -If you just want a plain array, you can "strip" the custom indices -with `parent(imgw)`. +If you just want a plain array, you can "strip" the custom +indices with `parent(imgw)`. # Examples: a 2d rotation (see JuliaImages documentation for pictures) @@ -46,7 +49,7 @@ julia> indices(img) # Rotate around the center of `img` julia> tfm = recenter(RotMatrix(-pi/4), center(img)) -AffineMap([0.707107 -0.707107; 0.707107 0.707107], [347.01,-68.7554]) +AffineMap([0.707107 0.707107; -0.707107 0.707107], [-196.755,293.99]) julia> imgw = warp(img, tfm); @@ -111,8 +114,8 @@ inv(tform), args...)` right now. To change to the new behaviour, set `const warp = ImageTransformations.warp_new` right after package import. """ -@generated function warp_old(img::AbstractArray, tform, args...) - Base.depwarn("'warp(img, tform)' is deprecated in favour of the new interpretation 'warp(img, inv(tform))'. Set 'const warp = ImageTransformations.warp_new' right after package import to change to the new behaviour right away. See https://github.com/JuliaImages/ImageTransformations.jl/issues/25 for more background information", :warp) - :(warp_new(img, inv(tform), args...)) +function warp_old(img::AbstractArray, tform, args...) + Base.depwarn("'warp(img, tform)' is deprecated in favour of the new interpretation 'warp(img, inv(tform))'. Set 'const warp = ImageTransformations.warp_new' right after package import to change to the new behaviour right away. See https://github.com/JuliaImages/ImageTransformations.jl/issues/25 for more background information", :warp_old) + warp_new(img, inv(tform), args...) end const warp = warp_old diff --git a/test/interpolations.jl b/test/interpolations.jl index fb89c00..7073300 100644 --- a/test/interpolations.jl +++ b/test/interpolations.jl @@ -1,94 +1,103 @@ @testset "_default_fill" begin @test_throws UndefVarError _default_fill @test typeof(ImageTransformations._default_fill) <: Function - import ImageTransformations._default_fill - - @test @inferred(_default_fill(N0f8)) === N0f8(0) - @test @inferred(_default_fill(Int)) === 0 - @test @inferred(_default_fill(Float16)) === NaN16 - @test @inferred(_default_fill(Float32)) === NaN32 - @test @inferred(_default_fill(Float64)) === NaN - - @test @inferred(_default_fill(Gray{N0f8})) === Gray{N0f8}(0) - @test @inferred(_default_fill(Gray{Float16})) === Gray{Float16}(NaN16) - @test @inferred(_default_fill(Gray{Float32})) === Gray{Float32}(NaN32) - @test @inferred(_default_fill(Gray{Float64})) === Gray{Float64}(NaN) - - @test @inferred(_default_fill(GrayA{N0f8})) === GrayA{N0f8}(0,0) - @test @inferred(_default_fill(GrayA{Float16})) === GrayA{Float16}(NaN16,NaN16) - @test @inferred(_default_fill(GrayA{Float32})) === GrayA{Float32}(NaN32,NaN32) - @test @inferred(_default_fill(GrayA{Float64})) === GrayA{Float64}(NaN,NaN) - - @test @inferred(_default_fill(RGB{N0f8})) === RGB{N0f8}(0,0,0) - @test @inferred(_default_fill(RGB{Float16})) === RGB{Float16}(NaN16,NaN16,NaN16) - @test @inferred(_default_fill(RGB{Float32})) === RGB{Float32}(NaN32,NaN32,NaN32) - @test @inferred(_default_fill(RGB{Float64})) === RGB{Float64}(NaN,NaN,NaN) - - @test @inferred(_default_fill(RGBA{N0f8})) === RGBA{N0f8}(0,0,0,0) - @test @inferred(_default_fill(RGBA{Float16})) === RGBA{Float16}(NaN16,NaN16,NaN16,NaN16) - @test @inferred(_default_fill(RGBA{Float32})) === RGBA{Float32}(NaN32,NaN32,NaN32,NaN32) - @test @inferred(_default_fill(RGBA{Float64})) === RGBA{Float64}(NaN,NaN,NaN,NaN) - - @test @inferred(_default_fill(HSV{Float16})) === HSV{Float16}(NaN16,NaN16,NaN16) - @test @inferred(_default_fill(HSV{Float32})) === HSV{Float32}(NaN32,NaN32,NaN32) - @test @inferred(_default_fill(HSV{Float64})) === HSV{Float64}(NaN,NaN,NaN) + + @test @inferred(ImageTransformations._default_fill(N0f8)) === N0f8(0) + @test @inferred(ImageTransformations._default_fill(Int)) === 0 + @test @inferred(ImageTransformations._default_fill(Float16)) === NaN16 + @test @inferred(ImageTransformations._default_fill(Float32)) === NaN32 + @test @inferred(ImageTransformations._default_fill(Float64)) === NaN + + @test @inferred(ImageTransformations._default_fill(Gray{N0f8})) === Gray{N0f8}(0) + @test @inferred(ImageTransformations._default_fill(Gray{Float16})) === Gray{Float16}(NaN16) + @test @inferred(ImageTransformations._default_fill(Gray{Float32})) === Gray{Float32}(NaN32) + @test @inferred(ImageTransformations._default_fill(Gray{Float64})) === Gray{Float64}(NaN) + + @test @inferred(ImageTransformations._default_fill(GrayA{N0f8})) === GrayA{N0f8}(0,0) + @test @inferred(ImageTransformations._default_fill(GrayA{Float16})) === GrayA{Float16}(NaN16,NaN16) + @test @inferred(ImageTransformations._default_fill(GrayA{Float32})) === GrayA{Float32}(NaN32,NaN32) + @test @inferred(ImageTransformations._default_fill(GrayA{Float64})) === GrayA{Float64}(NaN,NaN) + + @test @inferred(ImageTransformations._default_fill(RGB{N0f8})) === RGB{N0f8}(0,0,0) + @test @inferred(ImageTransformations._default_fill(RGB{Float16})) === RGB{Float16}(NaN16,NaN16,NaN16) + @test @inferred(ImageTransformations._default_fill(RGB{Float32})) === RGB{Float32}(NaN32,NaN32,NaN32) + @test @inferred(ImageTransformations._default_fill(RGB{Float64})) === RGB{Float64}(NaN,NaN,NaN) + + @test @inferred(ImageTransformations._default_fill(RGBA{N0f8})) === RGBA{N0f8}(0,0,0,0) + @test @inferred(ImageTransformations._default_fill(RGBA{Float16})) === RGBA{Float16}(NaN16,NaN16,NaN16,NaN16) + @test @inferred(ImageTransformations._default_fill(RGBA{Float32})) === RGBA{Float32}(NaN32,NaN32,NaN32,NaN32) + @test @inferred(ImageTransformations._default_fill(RGBA{Float64})) === RGBA{Float64}(NaN,NaN,NaN,NaN) + + @test @inferred(ImageTransformations._default_fill(HSV{Float16})) === HSV{Float16}(NaN16,NaN16,NaN16) + @test @inferred(ImageTransformations._default_fill(HSV{Float32})) === HSV{Float32}(NaN32,NaN32,NaN32) + @test @inferred(ImageTransformations._default_fill(HSV{Float64})) === HSV{Float64}(NaN,NaN,NaN) end @testset "box_extrapolation" begin @test_throws UndefVarError box_extrapolation @test typeof(ImageTransformations.box_extrapolation) <: Function - import ImageTransformations.box_extrapolation img = rand(Gray{N0f8}, 2, 2) - etp = @inferred box_extrapolation(img) - @test @inferred(box_extrapolation(etp)) === etp + etp = @inferred ImageTransformations.box_extrapolation(img) + @test @inferred(ImageTransformations.box_extrapolation(etp)) === etp @test summary(etp) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Gray{N0f8}(0.0)) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" @test typeof(etp) <: Interpolations.FilledExtrapolation @test etp.fillvalue === Gray{N0f8}(0.0) @test etp.itp.coefs === img - @test_throws ArgumentError box_extrapolation(etp, 0) - @test_throws ArgumentError box_extrapolation(etp, Flat()) - @test_throws ArgumentError box_extrapolation(etp, Constant()) - @test_throws ArgumentError box_extrapolation(etp, Constant(), Flat()) - @test_throws ArgumentError box_extrapolation(etp.itp, Constant()) - @test_throws ArgumentError box_extrapolation(etp.itp, Constant(), Flat()) + @test_throws ArgumentError ImageTransformations.box_extrapolation(etp, 0) + @test_throws ArgumentError ImageTransformations.box_extrapolation(etp, Flat()) + @test_throws ArgumentError ImageTransformations.box_extrapolation(etp, Quadratic(Flat())) + @test_throws ArgumentError ImageTransformations.box_extrapolation(etp, Quadratic(Flat()), Flat()) + @test_throws ArgumentError ImageTransformations.box_extrapolation(etp, Constant()) + @test_throws ArgumentError ImageTransformations.box_extrapolation(etp, Constant(), Flat()) + @test_throws ArgumentError ImageTransformations.box_extrapolation(etp.itp, Constant()) + @test_throws ArgumentError ImageTransformations.box_extrapolation(etp.itp, Constant(), Flat()) - etp2 = @inferred box_extrapolation(etp.itp) + etp2 = @inferred ImageTransformations.box_extrapolation(etp.itp) @test summary(etp2) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Gray{N0f8}(0.0)) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" @test typeof(etp2) <: Interpolations.FilledExtrapolation @test etp2.fillvalue === Gray{N0f8}(0.0) @test etp2 !== etp @test etp2.itp === etp.itp - etp2 = @inferred box_extrapolation(etp.itp, Flat()) + etp2 = @inferred ImageTransformations.box_extrapolation(etp.itp, Flat()) @test summary(etp2) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Flat()) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" @test typeof(etp2) <: Interpolations.Extrapolation @test etp2 !== etp @test etp2.itp === etp.itp - etp = @inferred box_extrapolation(img, 1) + etp = @inferred ImageTransformations.box_extrapolation(img, 1) @test summary(etp) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Gray{N0f8}(1.0)) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" @test typeof(etp) <: Interpolations.FilledExtrapolation @test etp.fillvalue === Gray{N0f8}(1.0) @test etp.itp.coefs === img - etp = @inferred box_extrapolation(img, Flat()) - @test @inferred(box_extrapolation(etp)) === etp + etp = @inferred ImageTransformations.box_extrapolation(img, Flat()) + @test @inferred(ImageTransformations.box_extrapolation(etp)) === etp @test summary(etp) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Flat()) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" @test typeof(etp) <: Interpolations.Extrapolation @test etp.itp.coefs === img - etp = @inferred box_extrapolation(img, Constant()) + etp = @inferred ImageTransformations.box_extrapolation(img, Constant()) @test summary(etp) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Constant()), OnGrid()), Gray{N0f8}(0.0)) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" @test typeof(etp) <: Interpolations.FilledExtrapolation @test etp.itp.coefs === img - etp = @inferred box_extrapolation(img, Constant(), Flat()) + etp = @inferred ImageTransformations.box_extrapolation(img, Constant(), Flat()) @test summary(etp) == "2×2 extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Constant()), OnGrid()), Flat()) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" @test typeof(etp) <: Interpolations.Extrapolation @test etp.itp.coefs === img + + imgfloat = Float64.(img) + etp = @inferred ImageTransformations.box_extrapolation(imgfloat, Quadratic(Flat())) + @test typeof(etp) <: Interpolations.FilledExtrapolation + @test summary(etp) == "2×2 extrapolate(interpolate(::Array{Float64,2}, BSpline(Quadratic(Flat())), OnGrid()), NaN) with element type Float64" + + etp = @inferred ImageTransformations.box_extrapolation(imgfloat, Cubic(Flat()), Flat()) + @test typeof(etp) <: Interpolations.Extrapolation + @test summary(etp) == "2×2 extrapolate(interpolate(::Array{Float64,2}, BSpline(Cubic(Flat())), OnGrid()), Flat()) with element type Float64" end @testset "AxisAlgorithms.A_ldiv_B_md" begin diff --git a/test/reference/warp_cameraman_rotate_r22deg_flat.txt b/test/reference/warp_cameraman_rotate_r22deg_flat.txt new file mode 100644 index 0000000..79bca11 --- /dev/null +++ b/test/reference/warp_cameraman_rotate_r22deg_flat.txt @@ -0,0 +1,11 @@ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ \ No newline at end of file diff --git a/test/reference/warp_cameraman_rotate_r22deg_periodic.txt b/test/reference/warp_cameraman_rotate_r22deg_periodic.txt new file mode 100644 index 0000000..a526f52 --- /dev/null +++ b/test/reference/warp_cameraman_rotate_r22deg_periodic.txt @@ -0,0 +1,11 @@ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ \ No newline at end of file diff --git a/test/warp.jl b/test/warp.jl index fc18f90..7624bcd 100644 --- a/test/warp.jl +++ b/test/warp.jl @@ -26,6 +26,20 @@ img_camera = testimage("camera") @test_reference "warp_cameraman" imgr2[indices(img_camera)...] # look the same but are not similar enough to pass test # @test imgr2[indices(img_camera)...] ≈ img_camera + + imgr = @inferred(warp(img_camera, tfm, Flat())) + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_flat" imgr + imgr = @inferred(warp(img_camera, tfm, ref_inds, Flat())) + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_flat" imgr + + imgr = @inferred(warp(img_camera, tfm, Constant(), Periodic())) + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_periodic" imgr + imgr = @inferred(warp(img_camera, tfm, ref_inds, Constant(), Periodic())) + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_periodic" imgr end tfm = recenter(RotMatrix(-pi/8), center(img_camera)) @@ -75,6 +89,15 @@ ref_img_pyramid_quad = Float64[ NaN NaN -0.038 0.205 -0.038 NaN NaN; NaN NaN NaN 0.003 NaN NaN NaN; ] +ref_img_pyramid_grid = Float64[ + NaN NaN NaN NaN NaN NaN NaN; + NaN NaN NaN 0.157977 NaN NaN NaN; + NaN NaN 0.223858 0.654962 0.223858 NaN NaN; + NaN 0.157977 0.654962 1.0 0.654962 0.157977 NaN; + NaN NaN 0.223858 0.654962 0.223858 NaN NaN; + NaN NaN NaN 0.157977 NaN NaN NaN; + NaN NaN NaN NaN NaN NaN NaN; +] @testset "Result against reference" begin @@ -92,6 +115,10 @@ ref_img_pyramid_quad = Float64[ imgr_cntr = warp(img_pyramid_cntr, tfm2) @test indices(imgr_cntr) == (-3:3, -3:3) @test nearlysame(parent(imgr_cntr), parent(imgr)) + + imgr_cntr = warp(img_pyramid_cntr, tfm2, (-1:1,-1:1)) + @test indices(imgr_cntr) == (-1:1, -1:1) + @test nearlysame(parent(imgr_cntr), imgr[2:4,2:4]) end @testset "Quadratic Interpolation" begin @@ -99,6 +126,10 @@ ref_img_pyramid_quad = Float64[ imgrq_cntr = warp(itp, tfm2) @test indices(imgrq_cntr) == (-3:3, -3:3) @test nearlysame(round.(Float64.(parent(imgrq_cntr)),3), round.(ref_img_pyramid_quad,3)) + + imgrq_cntr = warp(img_pyramid_cntr, tfm2, Quadratic(Flat())) + @test indices(imgrq_cntr) == (-3:3, -3:3) + @test nearlysame(round.(Float64.(parent(imgrq_cntr)),3), round.(ref_img_pyramid_grid,3)) end end From b79619d028b9683b84a37cd9294963f33fffb381 Mon Sep 17 00:00:00 2001 From: Christof Stocker Date: Sat, 29 Apr 2017 21:04:13 +0200 Subject: [PATCH 12/18] reintroduce WarpedView --- src/ImageTransformations.jl | 3 ++ src/invwarpedview.jl | 58 +++++++++++++-------------------- src/warpedview.jl | 65 +++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 36 deletions(-) create mode 100644 src/warpedview.jl diff --git a/src/ImageTransformations.jl b/src/ImageTransformations.jl index 0f64577..c53fdc3 100644 --- a/src/ImageTransformations.jl +++ b/src/ImageTransformations.jl @@ -21,6 +21,8 @@ export imresize, center, warp, + WarpedView, + warpedview, InvWarpedView, invwarpedview @@ -28,6 +30,7 @@ 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)...] diff --git a/src/invwarpedview.jl b/src/invwarpedview.jl index 75b8883..22440eb 100644 --- a/src/invwarpedview.jl +++ b/src/invwarpedview.jl @@ -1,41 +1,27 @@ -immutable InvWarpedView{T,N,A<:AbstractArray,F1<:Transformation,I,F2<:Transformation,E<:AbstractExtrapolation} <: AbstractArray{T,N} - parent::A - transform::F1 - indices::I - inverse::F2 - extrapolation::E +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 (::Type{InvWarpedView{T,N,TA,F,I}}){T,N,TA<:AbstractArray,F<:Transformation,I<:Tuple}( - parent::TA, - tform::F, - indices::I) - @assert eltype(parent) == T - etp = box_extrapolation(parent) - tinv = inv(tform) - new{T,N,TA,F,I,typeof(tinv),typeof(etp)}(parent, tform, indices, tinv, etp) - 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(inv),E}(inner, tinv) end -function InvWarpedView(inner::InvWarpedView, outer_tform::Transformation) - tform = compose(outer_tform, inner.transform) - A = parent(inner) - inds = autorange(A, tform) - InvWarpedView(A, tform, inds) +function InvWarpedView(A::AbstractArray, tinv::Transformation, inds::Tuple = autorange(A, tinv)) + InvWarpedView(WarpedView(A, inv(tinv), inds), tinv) end -function InvWarpedView{T,N,F<:Transformation,I<:Tuple}( - A::AbstractArray{T,N}, - tform::F, - inds::I = autorange(A, tform)) - InvWarpedView{T,N,typeof(A),F,I}(A, tform, inds) +function InvWarpedView(inner::InvWarpedView, outer_tinv::Transformation) + tinv = compose(outer_tinv, inner.inverse) + InvWarpedView(parent(inner), tinv) end -Base.parent(A::InvWarpedView) = A.parent -@inline Base.indices(A::InvWarpedView) = A.indices +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}) = - _getindex(A.extrapolation, A.inverse(SVector(I))) +@inline Base.getindex{T,N}(A::InvWarpedView{T,N}, I::Vararg{Int,N}) = A.inner[I...] Base.size(A::InvWarpedView) = OffsetArrays.errmsg(A) Base.size(A::InvWarpedView, d) = OffsetArrays.errmsg(A) @@ -44,7 +30,7 @@ function ShowItLikeYouBuildIt.showarg(io::IO, A::InvWarpedView) print(io, "InvWarpedView(") showarg(io, parent(A)) print(io, ", ") - print(io, A.transform) + print(io, A.inverse) print(io, ')') end @@ -53,22 +39,22 @@ Base.summary(A::InvWarpedView) = summary_build(A) """ TODO """ -@inline invwarpedview(A::AbstractArray, tform::Transformation, args...) = - InvWarpedView(A, tform, args...) +@inline invwarpedview(A::AbstractArray, tinv::Transformation, args...) = + InvWarpedView(A, tinv, args...) function invwarpedview{T}( A::AbstractArray{T}, - tform::Transformation, + tinv::Transformation, degree::Union{Linear,Constant}, fill::FillType = _default_fill(T), args...) - invwarpedview(box_extrapolation(A, degree, fill), tform, args...) + invwarpedview(box_extrapolation(A, degree, fill), tinv, args...) end function invwarpedview( A::AbstractArray, - tform::Transformation, + tinv::Transformation, fill::FillType, args...) - invwarpedview(A, tform, Linear(), fill, args...) + invwarpedview(A, tinv, Linear(), fill, args...) end diff --git a/src/warpedview.jl b/src/warpedview.jl new file mode 100644 index 0000000..8c266a7 --- /dev/null +++ b/src/warpedview.jl @@ -0,0 +1,65 @@ +immutable WarpedView{T,N,A<:AbstractArray,F<:Transformation,I<:Tuple,E<:AbstractExtrapolation} <: AbstractArray{T,N} + parent::A + transform::F + indices::I + extrapolation::E + + function (::Type{WarpedView{T,N,TA,F,I}}){T,N,TA<:AbstractArray,F<:Transformation,I<:Tuple}( + parent::TA, + tform::F, + indices::I) + @assert eltype(parent) == T + etp = box_extrapolation(parent) + new{T,N,TA,F,I,typeof(etp)}(parent, tform, indices, etp) + end +end + +function WarpedView{T,N,F<:Transformation,I<:Tuple}( + A::AbstractArray{T,N}, + tform::F, + inds::I = autorange(A, inv(tform))) + WarpedView{T,N,typeof(A),F,I}(A, tform, inds) +end + +Base.parent(A::WarpedView) = A.parent +@inline Base.indices(A::WarpedView) = A.indices + +@compat Compat.IndexStyle{T<:WarpedView}(::Type{T}) = IndexCartesian() +@inline Base.getindex{T,N}(A::WarpedView{T,N}, I::Vararg{Int,N}) = + _getindex(A.extrapolation, A.transform(SVector(I))) + +Base.size(A::WarpedView) = OffsetArrays.errmsg(A) +Base.size(A::WarpedView, d) = OffsetArrays.errmsg(A) + +function ShowItLikeYouBuildIt.showarg(io::IO, A::WarpedView) + print(io, "WarpedView(") + showarg(io, parent(A)) + print(io, ", ") + print(io, A.transform) + print(io, ')') +end + +Base.summary(A::WarpedView) = summary_build(A) + +""" +TODO +""" +@inline warpedview(A::AbstractArray, tform::Transformation, args...) = + WarpedView(A, tform, args...) + +function warpedview{T}( + A::AbstractArray{T}, + tform::Transformation, + degree::Union{Linear,Constant}, + fill::FillType = _default_fill(T), + args...) + warpedview(box_extrapolation(A, degree, fill), tform, args...) +end + +function warpedview( + A::AbstractArray, + tform::Transformation, + fill::FillType, + args...) + warpedview(A, tform, Linear(), fill, args...) +end From a805cc375aa8107a6bb76a724a6af849fe767b46 Mon Sep 17 00:00:00 2001 From: Christof Stocker Date: Sun, 30 Apr 2017 16:36:11 +0200 Subject: [PATCH 13/18] add warp support for OneTo inds --- src/warp.jl | 5 ++++- .../warp_cameraman_rotate_r22deg_crop.txt | 17 ++++++++++++++ ...arp_cameraman_rotate_r22deg_crop_white.txt | 17 ++++++++++++++ test/warp.jl | 22 +++++++++++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 test/reference/warp_cameraman_rotate_r22deg_crop.txt create mode 100644 test/reference/warp_cameraman_rotate_r22deg_crop_white.txt diff --git a/src/warp.jl b/src/warp.jl index 8d2a649..c5f9a13 100644 --- a/src/warp.jl +++ b/src/warp.jl @@ -74,10 +74,13 @@ julia> indices(imgr) ``` """ function warp_new{T}(img::AbstractExtrapolation{T}, tform, inds::Tuple = autorange(img, inv(tform))) - out = OffsetArray(Array{T}(map(length, inds)), inds) + out = _allocate_array(T, inds) warp!(out, img, tform) end +_allocate_array{T,N}(::Type{T}, inds::NTuple{N,Base.OneTo}) = Array{T}(map(length, inds)) +_allocate_array{T,N}(::Type{T}, inds::NTuple{N,AbstractUnitRange}) = OffsetArray(Array{T}(map(length, inds)), inds) + # this function was never exported, so no need to deprecate function warp!(out, img::AbstractExtrapolation, tform) @inbounds for I in CartesianRange(indices(out)) diff --git a/test/reference/warp_cameraman_rotate_r22deg_crop.txt b/test/reference/warp_cameraman_rotate_r22deg_crop.txt new file mode 100644 index 0000000..e735cb9 --- /dev/null +++ b/test/reference/warp_cameraman_rotate_r22deg_crop.txt @@ -0,0 +1,17 @@ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ \ No newline at end of file diff --git a/test/reference/warp_cameraman_rotate_r22deg_crop_white.txt b/test/reference/warp_cameraman_rotate_r22deg_crop_white.txt new file mode 100644 index 0000000..1626b84 --- /dev/null +++ b/test/reference/warp_cameraman_rotate_r22deg_crop_white.txt @@ -0,0 +1,17 @@ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ +▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ \ No newline at end of file diff --git a/test/warp.jl b/test/warp.jl index 7624bcd..b67357d 100644 --- a/test/warp.jl +++ b/test/warp.jl @@ -14,11 +14,31 @@ img_camera = testimage("camera") @testset "warp_new" begin imgr = @inferred(warp(img_camera, tfm)) + @test typeof(imgr) <: OffsetArray @test indices(imgr) == ref_inds @test eltype(imgr) == eltype(img_camera) @test_reference "warp_cameraman_rotate_r22deg" imgr + imgr2 = imgr[indices(img_camera)...] + @test_reference "warp_cameraman_rotate_r22deg_crop" imgr2 + + imgr = @inferred(warp(img_camera, tfm, indices(img_camera))) + @test typeof(imgr) <: Array + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_crop" imgr + + imgr = @inferred(warp(img_camera, tfm, indices(img_camera), 1)) + @test typeof(imgr) <: Array + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_crop_white" imgr + + imgr = @inferred(warp(img_camera, tfm, indices(img_camera), Linear(), 1)) + @test typeof(imgr) <: Array + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_crop_white" imgr + imgr = @inferred(warp(img_camera, tfm, 1)) + @test typeof(imgr) <: OffsetArray @test eltype(imgr) == eltype(img_camera) @test_reference "warp_cameraman_rotate_r22deg_white" imgr imgr2 = @inferred warp(imgr, inv(tfm)) @@ -28,6 +48,7 @@ img_camera = testimage("camera") # @test imgr2[indices(img_camera)...] ≈ img_camera imgr = @inferred(warp(img_camera, tfm, Flat())) + @test typeof(imgr) <: OffsetArray @test eltype(imgr) == eltype(img_camera) @test_reference "warp_cameraman_rotate_r22deg_flat" imgr imgr = @inferred(warp(img_camera, tfm, ref_inds, Flat())) @@ -35,6 +56,7 @@ img_camera = testimage("camera") @test_reference "warp_cameraman_rotate_r22deg_flat" imgr imgr = @inferred(warp(img_camera, tfm, Constant(), Periodic())) + @test typeof(imgr) <: OffsetArray @test eltype(imgr) == eltype(img_camera) @test_reference "warp_cameraman_rotate_r22deg_periodic" imgr imgr = @inferred(warp(img_camera, tfm, ref_inds, Constant(), Periodic())) From fcf939c11d5c309b0e7ac64ccce55f6483b440d9 Mon Sep 17 00:00:00 2001 From: Christof Stocker Date: Sun, 30 Apr 2017 23:19:26 +0200 Subject: [PATCH 14/18] finish warpedview tests --- src/invwarpedview.jl | 33 +++++-- src/warpedview.jl | 34 ++++++-- test/warp.jl | 199 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 246 insertions(+), 20 deletions(-) diff --git a/src/invwarpedview.jl b/src/invwarpedview.jl index 22440eb..b664311 100644 --- a/src/invwarpedview.jl +++ b/src/invwarpedview.jl @@ -5,7 +5,7 @@ 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(inv),E}(inner, tinv) + InvWarpedView{T,N,TA,F,I,typeof(tinv),E}(inner, tinv) end function InvWarpedView(A::AbstractArray, tinv::Transformation, inds::Tuple = autorange(A, tinv)) @@ -23,8 +23,8 @@ Base.parent(A::InvWarpedView) = parent(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) = OffsetArrays.errmsg(A) -Base.size(A::InvWarpedView, d) = OffsetArrays.errmsg(A) +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(") @@ -46,15 +46,30 @@ function invwarpedview{T}( A::AbstractArray{T}, tinv::Transformation, degree::Union{Linear,Constant}, - fill::FillType = _default_fill(T), - args...) - invwarpedview(box_extrapolation(A, degree, fill), tinv, args...) + 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, - fill::FillType, - args...) - invwarpedview(A, tinv, Linear(), fill, args...) + indices::Tuple, + fill::FillType) + invwarpedview(A, tinv, indices, Linear(), fill) end diff --git a/src/warpedview.jl b/src/warpedview.jl index 8c266a7..0baacf6 100644 --- a/src/warpedview.jl +++ b/src/warpedview.jl @@ -28,8 +28,11 @@ Base.parent(A::WarpedView) = A.parent @inline Base.getindex{T,N}(A::WarpedView{T,N}, I::Vararg{Int,N}) = _getindex(A.extrapolation, A.transform(SVector(I))) -Base.size(A::WarpedView) = OffsetArrays.errmsg(A) -Base.size(A::WarpedView, d) = OffsetArrays.errmsg(A) +Base.size{T,N,TA,F}(A::WarpedView{T,N,TA,F}) = OffsetArrays.errmsg(A) +Base.size{T,N,TA,F}(A::WarpedView{T,N,TA,F}, d) = OffsetArrays.errmsg(A) + +Base.size{T,N,TA,F}(A::WarpedView{T,N,TA,F,NTuple{N,Base.OneTo{Int}}}) = map(length, A.indices) +Base.size{T,N,TA,F}(A::WarpedView{T,N,TA,F,NTuple{N,Base.OneTo{Int}}}, d) = d <= N ? length(A.indices[d]) : 1 function ShowItLikeYouBuildIt.showarg(io::IO, A::WarpedView) print(io, "WarpedView(") @@ -51,15 +54,30 @@ function warpedview{T}( A::AbstractArray{T}, tform::Transformation, degree::Union{Linear,Constant}, - fill::FillType = _default_fill(T), - args...) - warpedview(box_extrapolation(A, degree, fill), tform, args...) + fill::FillType = _default_fill(T)) + warpedview(box_extrapolation(A, degree, fill), tform) +end + +function warpedview{T}( + A::AbstractArray{T}, + tform::Transformation, + indices::Tuple, + degree::Union{Linear,Constant}, + fill::FillType = _default_fill(T)) + warpedview(box_extrapolation(A, degree, fill), tform, indices) +end + +function warpedview( + A::AbstractArray, + tform::Transformation, + fill::FillType) + warpedview(A, tform, Linear(), fill) end function warpedview( A::AbstractArray, tform::Transformation, - fill::FillType, - args...) - warpedview(A, tform, Linear(), fill, args...) + indices::Tuple, + fill::FillType) + warpedview(A, tform, indices, Linear(), fill) end diff --git a/test/warp.jl b/test/warp.jl index b67357d..2791fa1 100644 --- a/test/warp.jl +++ b/test/warp.jl @@ -8,7 +8,7 @@ nearlysame(A::AbstractArray, B::AbstractArray) = all(map(nearlysame, A, B)) SPACE = VERSION < v"0.6.0-dev.2505" ? "" : " " # julia PR #20288 img_camera = testimage("camera") -@testset "Constructor" begin +@testset "Interface tests" begin tfm = recenter(RotMatrix(pi/8), center(img_camera)) ref_inds = (-78:591, -78:591) @@ -64,10 +64,110 @@ img_camera = testimage("camera") @test_reference "warp_cameraman_rotate_r22deg_periodic" imgr end + @testset "warpedview" begin + imgr = @inferred(warpedview(img_camera, tfm)) + @test imgr == @inferred(WarpedView(img_camera, tfm)) + @test summary(imgr) == "-78:591×-78:591 WarpedView(::Array{Gray{N0f8},2}, AffineMap([0.92388 -0.382683; 0.382683 0.92388], [117.683,$(SPACE)-78.6334])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test_throws ErrorException size(imgr) + @test_throws ErrorException size(imgr, 1) + @test_throws ErrorException size(imgr, 5) + @test parent(imgr) === img_camera + @test typeof(imgr) <: WarpedView + @test indices(imgr) == ref_inds + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg" imgr + + imgr2 = imgr[indices(img_camera)...] + @test_reference "warp_cameraman_rotate_r22deg_crop" imgr2 + + imgr = @inferred(warpedview(img_camera, tfm, indices(img_camera))) + @test imgr == @inferred(WarpedView(img_camera, tfm, indices(img_camera))) + @test summary(imgr) == "512×512 WarpedView(::Array{Gray{N0f8},2}, AffineMap([0.92388 -0.382683; 0.382683 0.92388], [117.683,$(SPACE)-78.6334])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test @inferred(size(imgr)) == size(img_camera) + @test @inferred(size(imgr,3)) == 1 + @test parent(imgr) === img_camera + @test indices(imgr) === indices(img_camera) + @test typeof(imgr) <: WarpedView + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_crop" imgr + + imgr = @inferred(warpedview(img_camera, tfm, indices(img_camera), 1)) + @test summary(imgr) == "512×512 WarpedView(extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Gray{N0f8}(1.0)), AffineMap([0.92388 -0.382683; 0.382683 0.92388], [117.683,$(SPACE)-78.6334])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test @inferred(size(imgr)) == size(img_camera) + @test @inferred(size(imgr,3)) == 1 + @test typeof(parent(imgr)) <: Interpolations.FilledExtrapolation + @test parent(imgr).itp.coefs === img_camera + @test indices(imgr) === indices(img_camera) + @test typeof(imgr) <: WarpedView + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_crop_white" imgr + + imgr = @inferred(warpedview(img_camera, tfm, indices(img_camera), Linear(), 1)) + @test summary(imgr) == "512×512 WarpedView(extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Gray{N0f8}(1.0)), AffineMap([0.92388 -0.382683; 0.382683 0.92388], [117.683,$(SPACE)-78.6334])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test @inferred(size(imgr)) == size(img_camera) + @test @inferred(size(imgr,3)) == 1 + @test typeof(parent(imgr)) <: Interpolations.FilledExtrapolation + @test parent(imgr).itp.coefs === img_camera + @test indices(imgr) === indices(img_camera) + @test typeof(imgr) <: WarpedView + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_crop_white" imgr + + imgr = @inferred(warpedview(img_camera, tfm, 1)) + @test summary(imgr) == "-78:591×-78:591 WarpedView(extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Gray{N0f8}(1.0)), AffineMap([0.92388 -0.382683; 0.382683 0.92388], [117.683,$(SPACE)-78.6334])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test_throws ErrorException size(imgr) + @test_throws ErrorException size(imgr, 1) + @test_throws ErrorException size(imgr, 5) + @test typeof(parent(imgr)) <: Interpolations.FilledExtrapolation + @test parent(imgr).itp.coefs === img_camera + @test typeof(imgr) <: WarpedView + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_white" imgr + imgr2 = @inferred warpedview(imgr, inv(tfm)) + @test eltype(imgr2) == eltype(img_camera) + @test_reference "warp_cameraman" imgr2[indices(img_camera)...] + # look the same but are not similar enough to pass test + # @test imgr2[indices(img_camera)...] ≈ img_camera + + imgr = @inferred(warpedview(img_camera, tfm, Flat())) + @test summary(imgr) == "-78:591×-78:591 WarpedView(extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Flat()), AffineMap([0.92388 -0.382683; 0.382683 0.92388], [117.683,$(SPACE)-78.6334])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test_throws ErrorException size(imgr) + @test_throws ErrorException size(imgr, 1) + @test_throws ErrorException size(imgr, 5) + @test typeof(parent(imgr)) <: Interpolations.Extrapolation + @test parent(imgr).itp.coefs === img_camera + @test typeof(imgr) <: WarpedView + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_flat" imgr + imgr = @inferred(warpedview(img_camera, tfm, ref_inds, Flat())) + @test eltype(imgr) == eltype(img_camera) + @test indices(imgr) === ref_inds + @test_reference "warp_cameraman_rotate_r22deg_flat" imgr + + imgr = @inferred(warpedview(img_camera, tfm, Constant(), Periodic())) + @test summary(imgr) == "-78:591×-78:591 WarpedView(extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Constant()), OnGrid()), Periodic()), AffineMap([0.92388 -0.382683; 0.382683 0.92388], [117.683,$(SPACE)-78.6334])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test_throws ErrorException size(imgr) + @test_throws ErrorException size(imgr, 1) + @test_throws ErrorException size(imgr, 5) + @test typeof(parent(imgr)) <: Interpolations.Extrapolation + @test parent(imgr).itp.coefs === img_camera + @test typeof(imgr) <: WarpedView + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_periodic" imgr + + imgr = @inferred(warpedview(img_camera, tfm, ref_inds, Constant(), Periodic())) + @test summary(imgr) == "-78:591×-78:591 WarpedView(extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Constant()), OnGrid()), Periodic()), AffineMap([0.92388 -0.382683; 0.382683 0.92388], [117.683,$(SPACE)-78.6334])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test_throws ErrorException size(imgr) + @test_throws ErrorException size(imgr, 1) + @test_throws ErrorException size(imgr, 5) + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_periodic" imgr + end + tfm = recenter(RotMatrix(-pi/8), center(img_camera)) - @testset "InvWarpedView" begin + @testset "invwarpedview" begin wv = @inferred(InvWarpedView(img_camera, tfm)) - @test summary(wv) == "-78:591×-78:591 InvWarpedView(::Array{Gray{N0f8},2}, AffineMap([0.92388 0.382683; -0.382683 0.92388], [-78.6334,$(SPACE)117.683])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test wv ≈ @inferred(InvWarpedView(WarpedView(img_camera, inv(tfm)))) @test_reference "invwarpedview_cameraman_rotate_r22deg" wv @test_throws ErrorException size(wv) @test_throws ErrorException size(wv, 1) @@ -82,6 +182,99 @@ img_camera = testimage("camera") @test eltype(wv2) === eltype(img_camera) @test parent(wv2) === img_camera @test wv2 ≈ img_camera + + imgr = @inferred(invwarpedview(img_camera, tfm)) + @test imgr == @inferred(InvWarpedView(img_camera, tfm)) + @test summary(imgr) == "-78:591×-78:591 InvWarpedView(::Array{Gray{N0f8},2}, AffineMap([0.92388 0.382683; -0.382683 0.92388], [-78.6334,$(SPACE)117.683])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test_throws ErrorException size(imgr) + @test_throws ErrorException size(imgr, 1) + @test_throws ErrorException size(imgr, 5) + @test parent(imgr) === img_camera + @test typeof(imgr) <: InvWarpedView + @test indices(imgr) == ref_inds + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg" imgr + + imgr2 = imgr[indices(img_camera)...] + @test_reference "warp_cameraman_rotate_r22deg_crop" imgr2 + + imgr = @inferred(invwarpedview(img_camera, tfm, indices(img_camera))) + @test imgr == @inferred(InvWarpedView(img_camera, tfm, indices(img_camera))) + @test summary(imgr) == "512×512 InvWarpedView(::Array{Gray{N0f8},2}, AffineMap([0.92388 0.382683; -0.382683 0.92388], [-78.6334,$(SPACE)117.683])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test @inferred(size(imgr)) == size(img_camera) + @test @inferred(size(imgr,3)) == 1 + @test parent(imgr) === img_camera + @test indices(imgr) === indices(img_camera) + @test typeof(imgr) <: InvWarpedView + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_crop" imgr + + imgr = @inferred(invwarpedview(img_camera, tfm, indices(img_camera), 1)) + @test summary(imgr) == "512×512 InvWarpedView(extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Gray{N0f8}(1.0)), AffineMap([0.92388 0.382683; -0.382683 0.92388], [-78.6334,$(SPACE)117.683])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test @inferred(size(imgr)) == size(img_camera) + @test @inferred(size(imgr,3)) == 1 + @test typeof(parent(imgr)) <: Interpolations.FilledExtrapolation + @test parent(imgr).itp.coefs === img_camera + @test indices(imgr) === indices(img_camera) + @test typeof(imgr) <: InvWarpedView + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_crop_white" imgr + + imgr = @inferred(invwarpedview(img_camera, tfm, indices(img_camera), Linear(), 1)) + @test summary(imgr) == "512×512 InvWarpedView(extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Gray{N0f8}(1.0)), AffineMap([0.92388 0.382683; -0.382683 0.92388], [-78.6334,$(SPACE)117.683])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test @inferred(size(imgr)) == size(img_camera) + @test @inferred(size(imgr,3)) == 1 + @test typeof(parent(imgr)) <: Interpolations.FilledExtrapolation + @test parent(imgr).itp.coefs === img_camera + @test indices(imgr) === indices(img_camera) + @test typeof(imgr) <: InvWarpedView + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_crop_white" imgr + + imgr = @inferred(invwarpedview(img_camera, tfm, 1)) + @test summary(imgr) == "-78:591×-78:591 InvWarpedView(extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Gray{N0f8}(1.0)), AffineMap([0.92388 0.382683; -0.382683 0.92388], [-78.6334,$(SPACE)117.683])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test_throws ErrorException size(imgr) + @test_throws ErrorException size(imgr, 1) + @test_throws ErrorException size(imgr, 5) + @test typeof(parent(imgr)) <: Interpolations.FilledExtrapolation + @test parent(imgr).itp.coefs === img_camera + @test typeof(imgr) <: InvWarpedView + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_white" imgr + + imgr = @inferred(invwarpedview(img_camera, tfm, Flat())) + @test summary(imgr) == "-78:591×-78:591 InvWarpedView(extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Linear()), OnGrid()), Flat()), AffineMap([0.92388 0.382683; -0.382683 0.92388], [-78.6334,$(SPACE)117.683])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test_throws ErrorException size(imgr) + @test_throws ErrorException size(imgr, 1) + @test_throws ErrorException size(imgr, 5) + @test typeof(parent(imgr)) <: Interpolations.Extrapolation + @test parent(imgr).itp.coefs === img_camera + @test typeof(imgr) <: InvWarpedView + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_flat" imgr + imgr = @inferred(invwarpedview(img_camera, tfm, ref_inds, Flat())) + @test eltype(imgr) == eltype(img_camera) + @test indices(imgr) === ref_inds + @test_reference "warp_cameraman_rotate_r22deg_flat" imgr + + imgr = @inferred(invwarpedview(img_camera, tfm, Constant(), Periodic())) + @test summary(imgr) == "-78:591×-78:591 InvWarpedView(extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Constant()), OnGrid()), Periodic()), AffineMap([0.92388 0.382683; -0.382683 0.92388], [-78.6334,$(SPACE)117.683])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test_throws ErrorException size(imgr) + @test_throws ErrorException size(imgr, 1) + @test_throws ErrorException size(imgr, 5) + @test typeof(parent(imgr)) <: Interpolations.Extrapolation + @test parent(imgr).itp.coefs === img_camera + @test typeof(imgr) <: InvWarpedView + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_periodic" imgr + + imgr = @inferred(invwarpedview(img_camera, tfm, ref_inds, Constant(), Periodic())) + @test summary(imgr) == "-78:591×-78:591 InvWarpedView(extrapolate(interpolate(::Array{Gray{N0f8},2}, BSpline(Constant()), OnGrid()), Periodic()), AffineMap([0.92388 0.382683; -0.382683 0.92388], [-78.6334,$(SPACE)117.683])) with element type ColorTypes.Gray{FixedPointNumbers.Normed{UInt8,8}}" + @test_throws ErrorException size(imgr) + @test_throws ErrorException size(imgr, 1) + @test_throws ErrorException size(imgr, 5) + @test eltype(imgr) == eltype(img_camera) + @test_reference "warp_cameraman_rotate_r22deg_periodic" imgr end end From 5ca002826472fd92a723b44405d96b1435067cfc Mon Sep 17 00:00:00 2001 From: Christof Stocker Date: Tue, 2 May 2017 12:05:15 +0200 Subject: [PATCH 15/18] add docstring for warpedview --- src/warp.jl | 18 ++++++++++----- src/warpedview.jl | 57 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 7 deletions(-) diff --git a/src/warp.jl b/src/warp.jl index c5f9a13..0ecdf86 100644 --- a/src/warp.jl +++ b/src/warp.jl @@ -15,12 +15,18 @@ arbitrary locations `tform(I)` which do not lie on to the lattice of pixels. How this reconstruction is done depends on the type of `img` and the optional parameter `degree`. -When `img` is a plain array, b-spline interpolation is used with -`degree = Linear()` for linear interpolation or `degree = -Constant()` for nearest neighbor interpolation. In the case -`tform(I)` maps to indices outside the original `img`, those -locations are set to a value `fill` (which defaults to `NaN` if -the element type supports it, and `0` otherwise). +When `img` is a plain array, then on-grid b-spline interpolation +will be used. It is possible to configure what degree of b-spline +to use with the parameter `degree`. For example one can use +`degree = Linear()` for linear interpolation, `degree = +Constant()` for nearest neighbor interpolation, or `degree = +Quadratic(Flat())` for quadratic interpolation. + +In the case `tform(I)` maps to indices outside the original +`img`, those locations are set to a value `fill` (which defaults +to `NaN` if the element type supports it, and `0` otherwise). The +parameter `fill` also accepts extrapolation schemes, such as +`Flat()`, `Periodic()` or `Reflect()`. For more control over the reconstruction scheme --- and how beyond-the-edge points are handled --- pass `img` as an diff --git a/src/warpedview.jl b/src/warpedview.jl index 0baacf6..04f1a1c 100644 --- a/src/warpedview.jl +++ b/src/warpedview.jl @@ -1,3 +1,19 @@ +""" + WarpedView(img, tform, [indices]) -> wv + +Create a view of `img` that lazily transforms any given index `I` +passed to `wv[I]` to correspond to `img[tform(I)]`. This approach +is known as backward mode warping. + +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`. To do this `inv(tform)` has to be computed. If the given +transformation `tform` does not support `inv`, then the parameter +`indices` has to be specified manually. + +see [`warpedview`](@ref) for more information. +""" immutable WarpedView{T,N,A<:AbstractArray,F<:Transformation,I<:Tuple,E<:AbstractExtrapolation} <: AbstractArray{T,N} parent::A transform::F @@ -45,7 +61,46 @@ end Base.summary(A::WarpedView) = summary_build(A) """ -TODO + warpedview(img, tform, [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[tform(I)]`. This approach +is known as backward mode warping. The given transformation +`tform` must accept a `SVector` as input. 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 `tform(I)` which do not lie on to the lattice +of pixels. How this reconstruction is done depends on the type of +`img` and the optional parameter `degree`. When `img` is a plain +array, then on-grid b-spline interpolation will be used, where +the pixel of `img` will serve as the coeficients. It is possible +to configure what degree of b-spline to use with the parameter +`degree`. The two possible values are `degree = Linear()` for +linear interpolation, or `degree = Constant()` for nearest +neighbor interpolation. + +In the case `tform(I)` maps to indices outside the domain of +`img`, those locations are set to a value `fill` (which defaults +to `NaN` if the element type supports it, and `0` otherwise). +Additionally, the parameter `fill` also accepts extrapolation +schemes, such as `Flat()`, `Periodic()` or `Reflect()`. + +The optional parameter `indices` can be used to specify the +domain of the resulting `WarpedView`. By default the indices are +computed in such a way that the resulting `WarpedView` contains +all the original pixels in `img`. To do this `inv(tform)` has to +be computed. If the given transformation `tform` does not support +`inv`, then the parameter `indices` has to be specified manually. + +`warpedview` is essentially a non-coping, lazy version of +[`warp`](@ref). As such, the two functions share the same +interface, with one important difference. `warpedview` will +insist that the resulting `WarpedView` will be a view of `img` +(i.e. `parent(warpedview(img, ...)) === img`). Consequently, +`warpedview` restricts the parameter `degree` to be either +`Linear()` or `Constant()`. """ @inline warpedview(A::AbstractArray, tform::Transformation, args...) = WarpedView(A, tform, args...) From 86d0afbec0935a781eb4f3dbe6775d05b4c127c4 Mon Sep 17 00:00:00 2001 From: Christof Stocker Date: Wed, 3 May 2017 09:25:29 +0200 Subject: [PATCH 16/18] add docstring for invwarpedview --- src/invwarpedview.jl | 45 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/src/invwarpedview.jl b/src/invwarpedview.jl index b664311..d6f812b 100644 --- a/src/invwarpedview.jl +++ b/src/invwarpedview.jl @@ -1,3 +1,25 @@ +""" + 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, +`InvWarpedView` is created using 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 @@ -37,7 +59,28 @@ end Base.summary(A::InvWarpedView) = summary_build(A) """ -TODO + 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, +`InvWarpedView` is created using 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...) From ad5cb02a4c83ada5daf6a2616e73c968d9df7616 Mon Sep 17 00:00:00 2001 From: Christof Stocker Date: Wed, 3 May 2017 09:36:32 +0200 Subject: [PATCH 17/18] fix 0.6 ambiguities --- src/interpolations.jl | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/interpolations.jl b/src/interpolations.jl index 2af7e62..47aa1eb 100644 --- a/src/interpolations.jl +++ b/src/interpolations.jl @@ -33,6 +33,10 @@ 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 From 1ae4634ec333bdeafcb36b3b4fce1f8644fcf742 Mon Sep 17 00:00:00 2001 From: Christof Stocker Date: Thu, 4 May 2017 17:41:51 +0200 Subject: [PATCH 18/18] work in a round of reviews --- src/invwarpedview.jl | 15 ++++++++------- src/warp.jl | 5 +---- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/invwarpedview.jl b/src/invwarpedview.jl index d6f812b..2cefcb5 100644 --- a/src/invwarpedview.jl +++ b/src/invwarpedview.jl @@ -3,8 +3,9 @@ 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, -`InvWarpedView` is created using the forward transformation. +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 @@ -63,11 +64,11 @@ Base.summary(A::InvWarpedView) = summary_build(A) 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, -`InvWarpedView` is created using 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 +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 diff --git a/src/warp.jl b/src/warp.jl index 0ecdf86..af295fa 100644 --- a/src/warp.jl +++ b/src/warp.jl @@ -80,13 +80,10 @@ julia> indices(imgr) ``` """ function warp_new{T}(img::AbstractExtrapolation{T}, tform, inds::Tuple = autorange(img, inv(tform))) - out = _allocate_array(T, inds) + out = similar(Array{T}, inds) warp!(out, img, tform) end -_allocate_array{T,N}(::Type{T}, inds::NTuple{N,Base.OneTo}) = Array{T}(map(length, inds)) -_allocate_array{T,N}(::Type{T}, inds::NTuple{N,AbstractUnitRange}) = OffsetArray(Array{T}(map(length, inds)), inds) - # this function was never exported, so no need to deprecate function warp!(out, img::AbstractExtrapolation, tform) @inbounds for I in CartesianRange(indices(out))