Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 19 additions & 23 deletions src/projective/affine.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,30 @@ end


"""
ScaleRatio(minlengths) <: ProjectiveTransform
ScaleRatio(ratios) <: ProjectiveTransform

Scales the aspect ratio
"""
struct ScaleRatio{N} <: ProjectiveTransform
ratios::NTuple{N}
end

# This allows for roundtrip through ScaleFixed and avoids code duplication
fixed_sizes(scale::ScaleRatio, bounds::Bounds) = round.(Int, scale.ratios .* length.(bounds.rs))

function getprojection(scale::ScaleRatio, bounds; randstate = nothing)
return scaleprojection(scale.ratios)
function getprojection(scale::ScaleRatio{N}, bounds::Bounds{N}; randstate = nothing) where N
return getprojection(ScaleFixed{N}(fixed_sizes(scale, bounds)), bounds; randstate)
end

function projectionbounds(tfm::ScaleRatio{N}, P, bounds::Bounds{N}; randstate = nothing) where N
projectionbounds(ScaleFixed{N}(fixed_sizes(tfm, bounds)), P, bounds; randstate)
end

"""
ScaleKeepAspect(minlengths) <: ProjectiveTransform

Scales the shortest side of `item` to `minlengths`, keeping the
original aspect ratio.
Scales the sides of `item` to be as least as large as `minlengths`,
keeping the original aspect ratio.

## Examples

Expand All @@ -42,28 +48,15 @@ struct ScaleKeepAspect{N} <: ProjectiveTransform
minlengths::NTuple{N, Int}
end

# This allows for roundtrip through ScaleFixed and avoids code duplication
fixed_sizes(scale::ScaleKeepAspect, bounds::Bounds) = round.(Int, maximum(scale.minlengths ./ length.(bounds.rs)) .* length.(bounds.rs))

function getprojection(scale::ScaleKeepAspect{N}, bounds::Bounds{N}; randstate = nothing) where N
# If no scaling needs to be done, return a noop transform
scale.minlengths == length.(bounds.rs) && return IdentityTransformation()

# Offset `minlengths` by 1 to avoid black border on one side
ratio = maximum((scale.minlengths .+ 1) ./ length.(bounds.rs))
upperleft = SVector{N, Float32}(minimum.(bounds.rs)) .- 0.5
P = scaleprojection(Tuple(ratio for _ in 1:N))
if any(upperleft .!= 0)
P = P ∘ Translation((Float32.(P(upperleft)) .+ 0.5f0))
end
return P
getprojection(ScaleFixed{N}(fixed_sizes(scale, bounds)), bounds; randstate)
end

function projectionbounds(tfm::ScaleKeepAspect{N}, P, bounds::Bounds{N}; randstate = nothing) where N
origsz = length.(bounds.rs)
ratio = maximum((tfm.minlengths) ./ origsz)
sz = round.(Int, ratio .* origsz)
bounds_ = transformbounds(bounds, P)
bs_ = offsetcropbounds(sz, bounds_, ntuple(_ -> 0.5, N))
return bs_
projectionbounds(ScaleFixed{N}(fixed_sizes(tfm, bounds)), P, bounds; randstate)
end

"""
Expand All @@ -80,6 +73,9 @@ end


function getprojection(scale::ScaleFixed, bounds::Bounds{N}; randstate = nothing) where N
# If no scaling needs to be done, return a noop transform
(scale.sizes == length.(bounds.rs)) && return IdentityTransformation()

ratios = (scale.sizes .+ 1) ./ length.(bounds.rs)
upperleft = SVector{N, Float32}(minimum.(bounds.rs)) .- 1
P = scaleprojection(ratios)
Expand All @@ -92,7 +88,7 @@ end

function projectionbounds(tfm::ScaleFixed{N}, P, bounds::Bounds{N}; randstate = nothing) where N
bounds_ = transformbounds(bounds, P)
return offsetcropbounds(tfm.sizes, bounds_, ntuple(_ -> 1., N))
return offsetcropbounds(tfm.sizes, bounds_, ntuple(_ -> 0.5, N))
end

"""
Expand Down
9 changes: 8 additions & 1 deletion src/projective/compose.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,5 +54,12 @@ function getprojection(
end

function projectionbounds(composed::ComposedProjectiveTransform, P, bounds; randstate = getrandstate(composed))
return transformbounds(bounds, P)
@assert length(composed.tfms) == length(randstate)
P = CoordinateTransformations.IdentityTransformation()
for (tfm, r) in zip(composed.tfms, randstate)
P_tfm = getprojection(tfm, bounds; randstate = r)
bounds = projectionbounds(tfm, P_tfm, bounds; randstate = r)
P = P_tfm ∘ P
end
return bounds
end
26 changes: 23 additions & 3 deletions test/projective/affine.jl
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,34 @@ include("../imports.jl")
@test_nowarn apply(tfm, keypoints)
timage = apply(tfm, image)
tkeypoints = apply(tfm, keypoints)
@test !any(isnan.(timage |> itemdata))
@test length.(getbounds(timage).rs) == (25, 25)
@test getbounds(timage) == getbounds(tkeypoints)
testprojective(tfm)

end

@testset ExtendedTestSet "ScaleRatio" begin
tfm = ScaleRatio((1/2, 1/2))
@test_nowarn apply(tfm, image)
@test_nowarn apply(tfm, keypoints)
timage = apply(tfm, image)
tkeypoints = apply(tfm, keypoints)
@test getbounds(timage).rs == (0:25, 0:25)
@test !any(isnan.(timage |> itemdata))
@test getbounds(timage).rs == (1:25, 1:25)
@test getbounds(timage) == getbounds(tkeypoints)
testprojective(tfm)
end

@testset ExtendedTestSet "ScaleRatioTwice" begin
tfm = ScaleRatio((4/5, 4/5)) |> ScaleRatio((1/2, 1/2))
@show getbounds(image).rs
@test_nowarn apply(tfm, image)
@test_nowarn apply(tfm, keypoints)
timage = apply(tfm, image)
tkeypoints = apply(tfm, keypoints)
@test !any(isnan.(timage |> itemdata))
@test getbounds(timage).rs == (1:20, 1:20)
@test getbounds(timage) == getbounds(tkeypoints)
testprojective(tfm)
end
Expand All @@ -96,10 +112,14 @@ include("../imports.jl")
tfm = ScaleKeepAspect((32, 32))

img = rand(RGB{N0f8}, 64, 96)
@test apply(tfm, Image(img)) |> itemdata |> size == (32, 48)
timg = apply(tfm, Image(img)) |> itemdata
@test timg |> size == (32, 48)
@test !any(isnan.(timg))

img = rand(RGB{N0f8}, 196, 196)
@test apply(tfm, Image(img)) |> itemdata |> size == (32, 32)
timg = apply(tfm, Image(img)) |> itemdata
@test timg |> size == (32, 32)
@test !any(isnan.(timg))
end
end

Expand Down