Skip to content

Commit 254a0a0

Browse files
authored
Optimize multiple crops (#99)
Update CroppedProjectiveTranform to optimize projective transforms followed by multiple crops, not just one crop. For instance: Rotate(10) |> CenterCrop((100, 100)) |> RandomCrop((50, 50)) Is now optimized to warp only into the 50x50 region, instead of to the 100x100 region.
1 parent 4df4f55 commit 254a0a0

File tree

2 files changed

+70
-38
lines changed

2 files changed

+70
-38
lines changed

src/projective/crop.jl

+37-31
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ function apply(crop::Crop, item::Item; randstate = getrandstate(crop))
2424
return apply(
2525
Project(CoordinateTransformations.IdentityTransformation()) |> crop,
2626
item;
27-
randstate = (nothing, randstate))
27+
randstate = (nothing, (randstate,)))
2828
end
2929

3030

@@ -50,14 +50,14 @@ end
5050

5151

5252

53-
struct CroppedProjectiveTransform{P<:ProjectiveTransform, C<:AbstractCrop} <: ProjectiveTransform
53+
struct CroppedProjectiveTransform{P<:ProjectiveTransform, C<:Tuple} <: ProjectiveTransform
5454
tfm::P
55-
crop::C
55+
crops::C
5656
end
5757

5858

5959
function getrandstate(cropped::CroppedProjectiveTransform)
60-
return (getrandstate(cropped.tfm), getrandstate(cropped.crop))
60+
return (getrandstate(cropped.tfm), getrandstate.(cropped.crops))
6161
end
6262

6363

@@ -69,49 +69,55 @@ function getprojection(
6969
return getprojection(cropped.tfm, bounds; randstate = tfmstate)
7070
end
7171

72-
7372
function projectionbounds(
74-
cropped::CroppedProjectiveTransform{PT, C},
73+
cropped::CroppedProjectiveTransform,
7574
P,
7675
bounds;
77-
randstate = getrandstate(cropped)) where {PT, C<:Crop}
78-
tfmstate, cropstate = randstate
76+
randstate = getrandstate(cropped))
77+
tfmstate, cropstates = randstate
7978
bounds_ = projectionbounds(cropped.tfm, P, bounds; randstate = tfmstate)
80-
return offsetcropbounds(cropped.crop.size, bounds_, cropstate)
79+
for (crop, cropstate) in zip(cropped.crops, cropstates)
80+
bounds_ = cropbounds(crop, bounds_; randstate = cropstate)
81+
end
82+
return bounds_
8183
end
8284

85+
compose(tfm::ProjectiveTransform, crop::AbstractCrop) = CroppedProjectiveTransform(tfm, (crop,))
86+
compose(tfm::ProjectiveTransform, cropped::CroppedProjectiveTransform) =
87+
CroppedProjectiveTransform(tfm |> cropped.tfm, cropped.crops)
8388

84-
function projectionbounds(
85-
cropped::CroppedProjectiveTransform{PT, PadDivisible},
86-
P,
87-
bounds;
88-
randstate = getrandstate(cropped)) where {PT}
89-
tfmstate, cropstate = randstate
90-
bounds_ = projectionbounds(cropped.tfm, P, bounds; randstate = tfmstate)
91-
ranges = bounds_.rs
89+
function compose(composed::ComposedProjectiveTransform, cropped::CroppedProjectiveTransform)
90+
return CroppedProjectiveTransform(composed |> cropped.tfm, cropped.crops)
91+
end
9292

93-
sz = length.(ranges)
94-
pad = (cropped.crop.by .- (sz .% cropped.crop.by)) .% cropped.crop.by
93+
function compose(cropped::CroppedProjectiveTransform, crop::AbstractCrop)
94+
return CroppedProjectiveTransform(cropped.tfm, (cropped.crops..., crop))
95+
end
9596

96-
start = minimum.(ranges)
97-
end_ = start .+ sz .+ pad .- 1
98-
rs = UnitRange.(start, end_)
99-
return Bounds(rs)
97+
function compose(cropped::CroppedProjectiveTransform, projective::ProjectiveTransform)
98+
return Sequence(cropped, projective)
10099
end
101100

101+
function compose(cropped::CroppedProjectiveTransform, composed::ComposedProjectiveTransform)
102+
return Sequence(cropped, composed)
103+
end
102104

103105

104-
compose(tfm::ProjectiveTransform, crop::AbstractCrop) = CroppedProjectiveTransform(tfm, crop)
105-
compose(tfm::ProjectiveTransform, crop::CroppedProjectiveTransform) =
106-
CroppedProjectiveTransform(tfm |> crop.tfm, crop.crop)
106+
cropbounds(crop::Crop, bounds::Bounds; randstate=getrandstate(crop)) = offsetcropbounds(crop.size, bounds, randstate)
107107

108-
function compose(composed::ComposedProjectiveTransform, cropped::CroppedProjectiveTransform)
109-
return CroppedProjectiveTransform(composed |> cropped.tfm, cropped.crop)
108+
function cropbounds(
109+
crop::PadDivisible,
110+
bounds::Bounds;
111+
randstate = getrandstate(crop))
112+
ranges = bounds.rs
110113

111-
end
114+
sz = length.(ranges)
115+
pad = (crop.by .- (sz .% crop.by)) .% crop.by
112116

113-
function compose(cropped::CroppedProjectiveTransform, projective::ProjectiveTransform)
114-
return Sequence(cropped, projective)
117+
start = minimum.(ranges)
118+
end_ = start .+ sz .+ pad .- 1
119+
rs = UnitRange.(start, end_)
120+
return Bounds(rs)
115121
end
116122

117123

test/projective/crop.jl

+33-7
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,39 @@ end
1111

1212
@testset ExtendedTestSet "CroppedProjectiveTransform" begin
1313

14-
image = Image(rand(100, 100))
15-
tfm = Project(Translation(20, 20))
16-
crop = CenterCrop((50, 50))
17-
@test tfm |> crop isa CroppedProjectiveTransform
18-
19-
cropped = tfm |> crop
20-
@test_nowarn apply(cropped, image)
14+
@testset ExtendedTestSet "apply" begin
15+
image = Image(rand(100, 100))
16+
tfm = Project(Translation(20, 20))
17+
crop = CenterCrop((50, 50))
18+
@test tfm |> crop isa CroppedProjectiveTransform
19+
20+
cropped = tfm |> crop
21+
@test_nowarn apply(cropped, image)
22+
end
23+
24+
@testset ExtendedTestSet "multiple crops $(N)D" for N in 2:3
25+
image = Image(rand(ntuple(_->100, N)...))
26+
tfms = [
27+
Project(Translation(ntuple(_->10, N)...)),
28+
CenterCrop(ntuple(_->50, N)),
29+
Crop(ntuple(_->30, N)),
30+
]
31+
32+
# Apply transformatations as composed CroppedProjectiveTransform
33+
composed = compose(tfms...)
34+
@test composed isa CroppedProjectiveTransform
35+
@test_nowarn apply(composed, image)
36+
composedoutput = apply(composed, image) |> itemdata
37+
38+
# Apply transformatations one at a time
39+
sequenceoutput = image
40+
for tfm in tfms
41+
sequenceoutput = apply(tfm, sequenceoutput)
42+
end
43+
sequenceoutput = sequenceoutput |> itemdata
44+
45+
@test composedoutput sequenceoutput
46+
end
2147
end
2248

2349
@testset ExtendedTestSet "PadDivisible" begin

0 commit comments

Comments
 (0)