Skip to content

Commit 266764e

Browse files
committed
refactor bounds with new Bounds data type
1 parent a9794d5 commit 266764e

File tree

23 files changed

+288
-250
lines changed

23 files changed

+288
-250
lines changed

src/items/image.jl

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,21 +33,20 @@ showitems(item)
3333
```
3434
3535
"""
36-
struct Image{N,T,B} <: AbstractArrayItem{N,T}
36+
struct Image{N,T} <: AbstractArrayItem{N,T}
3737
data::AbstractArray{T,N}
38-
bounds::AbstractArray{<:SVector{N,B},N}
38+
bounds::Bounds{N}
3939
end
4040

41-
Image(data) = Image(data, size(data))
41+
Image(data) = Image(data, Bounds(axes(data)))
4242

4343
function Image(data::AbstractArray{T,N}, sz::NTuple{N,Int}) where {T,N}
44-
bounds = makebounds(sz)
45-
return Image(data, bounds)
44+
return Image(data, Bounds(sz))
4645
end
4746

4847

4948
Base.show(io::IO, item::Image{N,T}) where {N,T} =
50-
print(io, "Image{$N, $T}() with size $(size(itemdata(item)))")
49+
print(io, "Image{$N, $T}() with bounds $(item.bounds)")
5150

5251

5352
function showitem!(img, image::Image{2, <:Colorant})
@@ -72,22 +71,25 @@ getbounds(image::Image) = image.bounds
7271
# We have to pass the inverse of the projection `P` as it uses backward
7372
# mode warping.
7473

75-
function project(P, image::Image{N, T}, indices) where {N, T}
76-
## Transform the bounds along with the image
77-
bounds_ = P.(getbounds(image))
78-
data_ = warp(itemdata(image), inv(P), indices, zero(T))
79-
return Image(data_, makebounds(indices))
74+
function project(P, image::Image{N, T}, bounds::Bounds) where {N, T}
75+
# TODO: make interpolation scheme and boundary conditions configurable
76+
data_ = warp(
77+
itemdata(image),
78+
inv(P),
79+
bounds.rs,
80+
zero(T))
81+
return Image(data_, bounds)
8082
end
8183

8284
# The inplace version `project!` is quite similar. Note `indices` are not needed
8385
# as they are implicitly given by the buffer.
8486

85-
function project!(bufimage::Image, P, image::Image{N, T}, indices) where {N, T}
86-
a = OffsetArray(parent(itemdata(bufimage)), indices)
87+
function project!(bufimage::Image, P, image::Image{N, T}, bounds::Bounds{N}) where {N, T}
88+
a = OffsetArray(parent(itemdata(bufimage)), bounds.rs)
8789
res = warp!(
8890
a,
8991
box_extrapolation(itemdata(image), zero(T)),
9092
inv(P),
9193
)
92-
return Image(res, P.(getbounds(image)))
94+
return Image(res, bounds)
9395
end

src/items/keypoints.jl

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,12 @@ showitems(item)
2323
"""
2424
struct Keypoints{N, T, S<:Union{SVector{N, T}, Nothing}, M} <: AbstractArrayItem{M, S}
2525
data::AbstractArray{S, M}
26-
bounds::AbstractArray{<:SVector{N, Float32}, N}
26+
bounds::Bounds{N}
2727
end
2828

2929

30-
function Keypoints(data::AbstractArray{S, M}, sz::NTuple{N, Int}) where {T, N, S<:Union{SVector{N, T}, Nothing}, M}
31-
return Keypoints{N, T, S, M}(data, makebounds(sz, Float32))
30+
function Keypoints(data, sz::NTuple{N, Int}) where N
31+
return Keypoints(data, Bounds(sz))
3232
end
3333

3434

@@ -39,10 +39,11 @@ Base.show(io::IO, item::Keypoints{N, T, M}) where {N, T, M} =
3939
getbounds(keypoints::Keypoints) = keypoints.bounds
4040

4141

42-
function project(P, keypoints::Keypoints{N, T}, indices) where {N, T}
42+
function project(P, keypoints::Keypoints{N, T}, bounds::Bounds{N}) where {N, T}
43+
# TODO: convert back to `T`?
4344
return Keypoints(
4445
map(fmap(P), keypoints.data),
45-
makebounds(indices),
46+
bounds,
4647
)
4748
end
4849

src/items/mask.jl

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,15 @@ mask = MaskMulti(rand(1:3, 100, 100))
1616
showitems(mask)
1717
```
1818
"""
19-
struct MaskMulti{N, T<:Integer, U, B} <: AbstractArrayItem{N, T}
19+
struct MaskMulti{N, T<:Integer, U} <: AbstractArrayItem{N, T}
2020
data::AbstractArray{T, N}
2121
classes::AbstractVector{U}
22-
bounds::AbstractArray{<:SVector{N, B}, N}
22+
bounds::Bounds{N}
2323
end
2424

2525

2626
function MaskMulti(a::AbstractArray, classes = unique(a))
27-
bounds = makebounds(size(a))
27+
bounds = Bounds(size(a))
2828
minimum(a) >= 1 || error("Class values must start at 1")
2929
return MaskMulti(a, classes, bounds)
3030
end
@@ -39,21 +39,20 @@ Base.show(io::IO, mask::MaskMulti{N, T}) where {N, T} =
3939
getbounds(mask::MaskMulti) = mask.bounds
4040

4141

42-
function project(P, mask::MaskMulti, indices)
42+
function project(P, mask::MaskMulti, bounds::Bounds)
4343
a = itemdata(mask)
4444
etp = mask_extrapolation(a)
45-
res = warp(etp, inv(P), indices)
45+
res = warp(etp, inv(P), bounds.rs)
4646
return MaskMulti(
4747
res,
4848
mask.classes,
49-
makebounds(indices)
49+
bounds
5050
)
5151
end
5252

5353

54-
function project!(bufmask::MaskMulti, P, mask::MaskMulti, indices)
55-
a = OffsetArray(parent(itemdata(bufmask)), indices)
56-
bounds_ = P.(getbounds(mask))
54+
function project!(bufmask::MaskMulti, P, mask::MaskMulti, bounds)
55+
a = OffsetArray(parent(itemdata(bufmask)), bounds.rs)
5756
warp!(
5857
a,
5958
mask_extrapolation(itemdata(mask)),
@@ -62,7 +61,7 @@ function project!(bufmask::MaskMulti, P, mask::MaskMulti, indices)
6261
return MaskMulti(
6362
a,
6463
mask.classes,
65-
makebounds(indices)
64+
bounds
6665
)
6766
end
6867

@@ -96,12 +95,12 @@ mask = MaskBinary(rand(Bool, 100, 100))
9695
showitems(mask)
9796
```
9897
"""
99-
struct MaskBinary{N, B} <: AbstractArrayItem{N, Bool}
98+
struct MaskBinary{N} <: AbstractArrayItem{N, Bool}
10099
data::AbstractArray{Bool, N}
101-
bounds::AbstractArray{<:SVector{N, B}, N}
100+
bounds::Bounds{N}
102101
end
103102

104-
function MaskBinary(a::AbstractArray{Bool, N}, bounds = makebounds(size(a))) where N
103+
function MaskBinary(a::AbstractArray{Bool, N}, bounds = Bounds(size(a))) where N
105104
return MaskBinary(a, bounds)
106105
end
107106

@@ -110,26 +109,25 @@ Base.show(io::IO, mask::MaskBinary{N}) where {N} =
110109

111110
getbounds(mask::MaskBinary) = mask.bounds
112111

113-
function project(P, mask::MaskBinary, indices)
112+
function project(P, mask::MaskBinary, bounds::Bounds)
114113
etp = mask_extrapolation(itemdata(mask))
115114
return MaskBinary(
116-
warp(etp, inv(P), indices),
117-
makebounds(indices),
115+
warp(etp, inv(P), bounds.rs),
116+
bounds,
118117
)
119118
end
120119

121120

122-
function project!(bufmask::MaskBinary, P, mask::MaskBinary, indices)
123-
bounds_ = P.(getbounds(mask))
124-
a = OffsetArray(parent(itemdata(bufmask)), indices)
121+
function project!(bufmask::MaskBinary, P, mask::MaskBinary, bounds)
122+
a = OffsetArray(parent(itemdata(bufmask)), bounds.rs)
125123
res = warp!(
126124
a,
127125
mask_extrapolation(itemdata(mask)),
128126
inv(P),
129127
)
130128
return MaskBinary(
131129
a,
132-
makebounds(indices)
130+
bounds
133131
)
134132
end
135133

src/projective/affine.jl

Lines changed: 54 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,21 @@ end
4040

4141

4242
function getprojection(scale::ScaleKeepAspect{N}, bounds; randstate = nothing) where N
43-
ratio = maximum(scale.minlengths ./ boundssize(bounds))
44-
return scaleprojection(Tuple(ratio for _ in 1:N))
43+
ratio = maximum(scale.minlengths ./ length.(bounds.rs))
44+
upperleft = SVector{N, Float32}(minimum.(bounds.rs)) .- 1
45+
P = scaleprojection(Tuple(ratio for _ in 1:N))
46+
if upperleft != SVector(0, 0)
47+
P = P Translation(-upperleft)
48+
end
49+
return P
50+
end
51+
52+
function projectionbounds(tfm::ScaleKeepAspect{N}, P, bounds::Bounds{N}; randstate = nothing) where N
53+
origsz = length.(bounds.rs)
54+
ratio = maximum(tfm.minlengths ./ origsz)
55+
sz = round.(Int, ratio .* origsz)
56+
bounds_ = transformbounds(bounds, P)
57+
return offsetcropbounds(sz, bounds_, ntuple(_ -> 1., N))
4558
end
4659

4760
"""
@@ -57,12 +70,22 @@ struct ScaleFixed{N} <: ProjectiveTransform
5770
end
5871

5972

60-
function getprojection(scale::ScaleFixed{N}, bounds; randstate = nothing) where N
61-
ratios = scale.sizes ./ boundssize(bounds)
62-
return scaleprojection(ratios)
73+
function getprojection(scale::ScaleFixed, bounds; randstate = nothing)
74+
ratios = scale.sizes ./ length.(bounds.rs)
75+
upperleft = SVector{2, Float32}(minimum.(bounds.rs)) .- 1
76+
P = scaleprojection(ratios)
77+
if upperleft != SVector(0, 0)
78+
P = P Translation(-upperleft)
79+
end
80+
return P
6381
end
6482

6583

84+
function projectionbounds(tfm::ScaleFixed{N}, P, bounds::Bounds{N}; randstate = nothing) where N
85+
bounds_ = transformbounds(bounds, P)
86+
return offsetcropbounds(tfm.sizes, bounds_, (1., 1.))
87+
end
88+
6689
"""
6790
Zoom(scales = (1, 1.2)) <: ProjectiveTransform
6891
Zoom(distribution)
@@ -78,7 +101,7 @@ Zoom(scales::NTuple{2, T} = (1., 1.2)) where T = Zoom(Uniform(scales[1], scales[
78101

79102
getrandstate(tfm::Zoom) = rand(tfm.dist)
80103

81-
function getprojection(tfm::Zoom, bounds::AbstractArray{<:SVector{N}}; randstate = getrandstate(tfm)) where N
104+
function getprojection(tfm::Zoom, bounds::Bounds{N}; randstate = getrandstate(tfm)) where N
82105
ratio = randstate
83106
return scaleprojection(ntuple(_ -> ratio, N))
84107
end
@@ -109,12 +132,12 @@ getrandstate(tfm::Rotate) = rand(tfm.dist)
109132

110133
function getprojection(
111134
tfm::Rotate,
112-
bounds::AbstractArray{<:SVector{N, T}};
113-
randstate = getrandstate(tfm)) where {N, T}
135+
bounds::Bounds{2};
136+
randstate = getrandstate(tfm))
114137
γ = randstate
115-
middlepoint = sum(bounds) ./ length(bounds)
138+
middlepoint = SVector{2, Float32}(mean.(bounds.rs))
116139
r = γ / 360 * 2pi
117-
return recenter(RotMatrix(convert(T, r)), middlepoint)
140+
return recenter(RotMatrix(convert(Float32, r)), middlepoint)
118141
end
119142

120143

@@ -140,16 +163,32 @@ end
140163

141164

142165
function getprojection(tfm::Reflect, bounds; randstate = getrandstate(tfm))
143-
midpoint = sum(bounds) ./ length(bounds)
144166
r = tfm.γ / 360 * 2pi
145-
return recenter(reflectionmatrix(r), midpoint)
167+
return centered(LinearMap(reflectionmatrix(r)), bounds)
168+
end
169+
170+
"""
171+
centered(P, bounds)
172+
173+
Transform `P` so that is applied around the center of `bounds`
174+
instead of the origin
175+
"""
176+
function centered(P, bounds::Bounds{2})
177+
upperleft = minimum.(bounds.rs)
178+
bottomright = maximum.(bounds.rs)
179+
180+
midpoint = SVector{2, Float32}((bottomright .- upperleft) ./ 2) .+ SVector{2, Float32}(.5, .5)
181+
return recenter(P, midpoint)
146182
end
147183

148184

149185
FlipX() = Reflect(180)
150186
FlipY() = Reflect(90)
151187

152-
reflectionmatrix(r) = SMatrix{2, 2, Float32}(cos(2r), sin(2r), sin(2r), -cos(2r))
188+
function reflectionmatrix(r)
189+
A = SMatrix{2, 2, Float32}(cos(2r), sin(2r), sin(2r), -cos(2r))
190+
return round.(A; digits = 12)
191+
end
153192

154193

155194
"""
@@ -171,12 +210,12 @@ struct PinOrigin <: ProjectiveTransform end
171210

172211
function getprojection(::PinOrigin, bounds; randstate = nothing)
173212
# TODO: translate by actual minimum x and y coordinates
174-
return Translation(-bounds[1])
213+
return Translation((-SVector{2, Float32}(minimum.(bounds.rs))) .+ 1)
175214
end
176215

177216
function apply(::PinOrigin, item::Union{Image, MaskMulti, MaskBinary}; randstate = nothing)
178217
item = @set item.data = parent(itemdata(item))
179-
item = @set item.bounds = makebounds(size(itemdata(item)))
218+
item = @set item.bounds = Bounds(size(itemdata(item)))
180219
return item
181220
end
182221

0 commit comments

Comments
 (0)