|
49 | 49 | Apply `CoordinateTransformations.Transformation` to `bounds`.
|
50 | 50 | """
|
51 | 51 | function transformbounds(bounds::Bounds, P::CoordinateTransformations.Transformation)
|
52 |
| - return Bounds(ImageTransformations.autorange(CartesianIndices(bounds.rs), P)) |
| 52 | + return Bounds(_autorange(CartesianIndices(bounds.rs), P)) |
53 | 53 | end
|
54 | 54 |
|
55 | 55 | """
|
@@ -129,3 +129,53 @@ struct Project{T<:CoordinateTransformations.Transformation} <: ProjectiveTransfo
|
129 | 129 | end
|
130 | 130 |
|
131 | 131 | getprojection(tfm::Project, bounds; randstate = nothing) = tfm.P
|
| 132 | + |
| 133 | +# ## ImageTransformations.jl 0.8 internal functionality port |
| 134 | +# |
| 135 | +# Ported from ImageTransformations 0.8, since 0.9 introduced changes that broke |
| 136 | +# some assumptions. |
| 137 | + |
| 138 | +function _autorange(img, tform) |
| 139 | + R = CartesianIndices(axes(img)) |
| 140 | + autorange(R, tform) |
| 141 | +end |
| 142 | + |
| 143 | +function _autorange(R::CartesianIndices, tform) |
| 144 | + tform = _round(tform) |
| 145 | + mn = mx = tform(SVector(first(R).I)) |
| 146 | + for I in ImageTransformations.CornerIterator(R) |
| 147 | + x = tform(SVector(I.I)) |
| 148 | + # we map min and max to prevent type-inference issues |
| 149 | + # (because min(::SVector,::SVector) -> Vector) |
| 150 | + mn = map(min, x, mn) |
| 151 | + mx = map(max, x, mx) |
| 152 | + end |
| 153 | + _autorange(Tuple(mn), Tuple(mx)) |
| 154 | +end |
| 155 | + |
| 156 | +@noinline _autorange(mn::Tuple, mx::Tuple) = map((a,b)->floor(Int,a):ceil(Int,b), mn, mx) |
| 157 | + |
| 158 | + |
| 159 | +# Slightly round/discretize the transformation so that the warpped image size isn't affected by |
| 160 | +# numerical stability |
| 161 | +# https://github.com/JuliaImages/ImageTransformations.jl/issues/104 |
| 162 | +_default_digits(::Type{T}) where T<:Number = _default_digits(floattype(T)) |
| 163 | +# these constants come from eps() digits |
| 164 | +_default_digits(::Type{<:AbstractFloat}) = 15 |
| 165 | +_default_digits(::Type{Float64}) = 15 |
| 166 | +_default_digits(::Type{Float32}) = 7 |
| 167 | + |
| 168 | +function _round(tform::T; kwargs...) where T<:CoordinateTransformations.Transformation |
| 169 | + rounded_fields = map(Base.OneTo(fieldcount(T))) do i |
| 170 | + __round(getfield(tform, i); kwargs...) |
| 171 | + end |
| 172 | + T(rounded_fields...) |
| 173 | +end |
| 174 | +if isdefined(Base, :ComposedFunction) |
| 175 | + _round(tform::ComposedFunction; kwargs...) = _round(tform.outer; kwargs...) ∘ _round(tform.inner; kwargs...) |
| 176 | +end |
| 177 | +_round(tform; kwargs...) = tform |
| 178 | + |
| 179 | +__round(x; kwargs...) = x |
| 180 | +__round(x::AbstractArray; digits=_default_digits(eltype(x)), kwargs...) = round.(x; digits=digits, kwargs...) |
| 181 | +__round(x::T; digits=_default_digits(T), kwargs...) where T<:Number = round(x; digits=digits, kwargs...) |
0 commit comments