Skip to content

Commit efb8529

Browse files
authored
Merge pull request #103 from h-spiess/unifying_scaling_projections
Unified Scale projections and fixed black border
2 parents 3437276 + 62710cb commit efb8529

File tree

3 files changed

+52
-29
lines changed

3 files changed

+52
-29
lines changed

src/projective/affine.jl

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,30 @@ end
1010

1111

1212
"""
13-
ScaleRatio(minlengths) <: ProjectiveTransform
13+
ScaleRatio(ratios) <: ProjectiveTransform
1414
1515
Scales the aspect ratio
1616
"""
1717
struct ScaleRatio{N} <: ProjectiveTransform
1818
ratios::NTuple{N}
1919
end
2020

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

22-
function getprojection(scale::ScaleRatio, bounds; randstate = nothing)
23-
return scaleprojection(scale.ratios)
24+
function getprojection(scale::ScaleRatio{N}, bounds::Bounds{N}; randstate = nothing) where N
25+
return getprojection(ScaleFixed{N}(fixed_sizes(scale, bounds)), bounds; randstate)
26+
end
27+
28+
function projectionbounds(tfm::ScaleRatio{N}, P, bounds::Bounds{N}; randstate = nothing) where N
29+
projectionbounds(ScaleFixed{N}(fixed_sizes(tfm, bounds)), P, bounds; randstate)
2430
end
2531

2632
"""
2733
ScaleKeepAspect(minlengths) <: ProjectiveTransform
2834
29-
Scales the shortest side of `item` to `minlengths`, keeping the
30-
original aspect ratio.
35+
Scales the sides of `item` to be as least as large as `minlengths`,
36+
keeping the original aspect ratio.
3137
3238
## Examples
3339
@@ -42,28 +48,15 @@ struct ScaleKeepAspect{N} <: ProjectiveTransform
4248
minlengths::NTuple{N, Int}
4349
end
4450

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

4654
function getprojection(scale::ScaleKeepAspect{N}, bounds::Bounds{N}; randstate = nothing) where N
47-
# If no scaling needs to be done, return a noop transform
48-
scale.minlengths == length.(bounds.rs) && return IdentityTransformation()
49-
50-
# Offset `minlengths` by 1 to avoid black border on one side
51-
ratio = maximum((scale.minlengths .+ 1) ./ length.(bounds.rs))
52-
upperleft = SVector{N, Float32}(minimum.(bounds.rs)) .- 0.5
53-
P = scaleprojection(Tuple(ratio for _ in 1:N))
54-
if any(upperleft .!= 0)
55-
P = P Translation((Float32.(P(upperleft)) .+ 0.5f0))
56-
end
57-
return P
55+
getprojection(ScaleFixed{N}(fixed_sizes(scale, bounds)), bounds; randstate)
5856
end
5957

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

6962
"""
@@ -80,6 +73,9 @@ end
8073

8174

8275
function getprojection(scale::ScaleFixed, bounds::Bounds{N}; randstate = nothing) where N
76+
# If no scaling needs to be done, return a noop transform
77+
(scale.sizes == length.(bounds.rs)) && return IdentityTransformation()
78+
8379
ratios = (scale.sizes .+ 1) ./ length.(bounds.rs)
8480
upperleft = SVector{N, Float32}(minimum.(bounds.rs)) .- 1
8581
P = scaleprojection(ratios)
@@ -92,7 +88,7 @@ end
9288

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

9894
"""

src/projective/compose.jl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,12 @@ function getprojection(
5454
end
5555

5656
function projectionbounds(composed::ComposedProjectiveTransform, P, bounds; randstate = getrandstate(composed))
57-
return transformbounds(bounds, P)
57+
@assert length(composed.tfms) == length(randstate)
58+
P = CoordinateTransformations.IdentityTransformation()
59+
for (tfm, r) in zip(composed.tfms, randstate)
60+
P_tfm = getprojection(tfm, bounds; randstate = r)
61+
bounds = projectionbounds(tfm, P_tfm, bounds; randstate = r)
62+
P = P_tfm P
63+
end
64+
return bounds
5865
end

test/projective/affine.jl

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,18 +76,33 @@ include("../imports.jl")
7676
@test_nowarn apply(tfm, keypoints)
7777
timage = apply(tfm, image)
7878
tkeypoints = apply(tfm, keypoints)
79-
@test length.(getbounds(timage).rs) == (25, 25)
79+
@test !any(isnan.(timage |> itemdata))
80+
@test getbounds(timage).rs == (1:25, 1:25)
8081
@test getbounds(timage) == getbounds(tkeypoints)
8182
testprojective(tfm)
8283

8384
end
85+
8486
@testset ExtendedTestSet "ScaleRatio" begin
8587
tfm = ScaleRatio((1/2, 1/2))
8688
@test_nowarn apply(tfm, image)
8789
@test_nowarn apply(tfm, keypoints)
8890
timage = apply(tfm, image)
8991
tkeypoints = apply(tfm, keypoints)
90-
@test getbounds(timage).rs == (0:25, 0:25)
92+
@test !any(isnan.(timage |> itemdata))
93+
@test getbounds(timage).rs == (1:25, 1:25)
94+
@test getbounds(timage) == getbounds(tkeypoints)
95+
testprojective(tfm)
96+
end
97+
98+
@testset ExtendedTestSet "ScaleRatioTwice" begin
99+
tfm = ScaleRatio((4/5, 4/5)) |> ScaleRatio((1/2, 1/2))
100+
@test_nowarn apply(tfm, image)
101+
@test_nowarn apply(tfm, keypoints)
102+
timage = apply(tfm, image)
103+
tkeypoints = apply(tfm, keypoints)
104+
@test !any(isnan.(timage |> itemdata))
105+
@test getbounds(timage).rs == (1:20, 1:20)
91106
@test getbounds(timage) == getbounds(tkeypoints)
92107
testprojective(tfm)
93108
end
@@ -96,10 +111,15 @@ include("../imports.jl")
96111
tfm = ScaleKeepAspect((32, 32))
97112

98113
img = rand(RGB{N0f8}, 64, 96)
99-
@test apply(tfm, Image(img)) |> itemdata |> size == (32, 48)
100-
114+
timg = apply(tfm, Image(img)) |> itemdata
115+
timg = apply(tfm, Image(img))
116+
@test getbounds(timg).rs == (1:32, 1:48)
117+
@test !any(isnan.(timg |> itemdata))
101118
img = rand(RGB{N0f8}, 196, 196)
102-
@test apply(tfm, Image(img)) |> itemdata |> size == (32, 32)
119+
timg = apply(tfm, Image(img)) |> itemdata
120+
timg = apply(tfm, Image(img))
121+
@test getbounds(timg).rs == (1:32, 1:32)
122+
@test !any(isnan.(timg |> itemdata))
103123
end
104124
end
105125

0 commit comments

Comments
 (0)