Skip to content

Commit d88f839

Browse files
authored
Add 3d rotations (#93)
* Add 3D rotations This adds 3D support to the Rotate transformation, and introduces RotateX, RotateY, and RotateZ for 3D rotations around the x, y, and z axes. * Add check to `getprojection` for `ComposedProjectiveTransform` Ensure the number of transforms equals the number of random states. Previously, the projection would silently be calculated only for the number of transforms equal to the number of random states. * Add RotateX, RotateY, and RotateZ to documentation * Clean up documentation, add 3D rotation to gallery * Use `@docs; canonical=false` for documentation outside of ref.md. This allows documentation to exist on a second page. Previously, documentation was split between multiple pages. Some were in ref.md, and some were in other pages (transformations.md, buffering.md). Now, all documentation is at least in ref.md, but can also be duplicated on other pages with `canonical=false`. * Add missing documentation to ref.md, and transformations.md. * Enable the visualizations in gallery.md, and clean up the code a bit. * Add visualizations in gallery.md for 3D rotations.
1 parent 21161cd commit d88f839

File tree

8 files changed

+192
-92
lines changed

8 files changed

+192
-92
lines changed

docs/src/buffering.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ buffer = apply(tfm, item) # uses apply! internally
2222

2323
Since `Buffered` only stores one buffer, you may run into problems when using it in a multi-threading context where different threads invalidate the buffer before it can be used. In that case, you can use [`DataAugmentation.BufferedThreadsafe`](@ref), a version of `Buffered` that keeps a separate buffer for every thread.
2424

25-
```@docs
25+
```@docs; canonical=false
2626
DataAugmentation.Buffered
2727
DataAugmentation.BufferedThreadsafe
2828
```

docs/src/projective/gallery.md

Lines changed: 31 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,7 @@ the keypoint-based items [`Keypoints`](@ref), [`Polygon`](@ref), and [`BoundingB
77

88
Let's take this picture of a light house:
99

10-
```@setup deps
11-
using DataAugmentation
12-
using MosaicViews
13-
using Images
14-
using TestImages
15-
using StaticArrays
16-
```
17-
18-
```@example
10+
```@example deps
1911
using DataAugmentation
2012
using MosaicViews
2113
using Images
@@ -26,11 +18,6 @@ imagedata = testimage("lighthouse")
2618
imagedata = imresize(imagedata, ratio = 196 / size(imagedata, 1))
2719
```
2820

29-
```@example deps
30-
imagedata = testimage("lighthouse")
31-
imagedata
32-
```
33-
3421
To apply a transformation `tfm` to it, wrap it in
3522
`Image`, apply the transformation and unwrap it using [`itemdata`](@ref):
3623

@@ -74,59 +61,29 @@ apply(tfm, (image, bbox)) |> showitems
7461

7562
Of course, you have to create a 3-dimensional transformation, i.e. `CenterCrop((128, 128, 128))` instead of `CenterCrop((128, 128))`.
7663

77-
## Gallery
78-
```julia
79-
function showtransform(tfm, item, n = 8; ncol = 4)
80-
return mosaicview(
81-
[showitems(apply(tfm, item)) for _ in 1:n],
82-
fillvalue = RGBA(1, 1, 1, 0),
83-
npad = 8,
84-
rowmajor = true,
85-
ncol = ncol)
86-
end
87-
88-
89-
function showtransforms(tfms, item; ncol = length(tfms))
90-
return mosaicview(
91-
[parent(showitems(apply(tfm, item))) for tfm in tfms],
92-
fillvalue = RGBA(1, 1, 1, 0),
93-
npad = 8,
94-
rowmajor = true,
95-
ncol = ncol)
96-
end
97-
98-
nothing # hide
99-
```
100-
101-
### [`RandomResizeCrop`](@ref)`(sz)`
64+
## [`RandomResizeCrop`](@ref)`(sz)`
10265

10366
Resizes the sides so that one of them is no longer than `sz` and crops a region of size `sz` *from a random location*.
10467

105-
```julia
68+
```@example deps
10669
tfm = RandomResizeCrop((128, 128))
70+
showgrid([apply(tfm, (image, bbox)) for _ in 1:6]; ncol=6, npad=8)
10771
```
10872

109-
```julia
110-
o = showtransform(tfm, (image, bbox), 6, ncol=6)
111-
```
112-
113-
### [`CenterResizeCrop`](@ref)
73+
## [`CenterResizeCrop`](@ref)
11474

11575
Resizes the sides so that one of them is no longer than `sz` and crops a region of size `sz` *from the center*.
11676

117-
```julia
77+
```@example deps
11878
tfm = CenterResizeCrop((128, 128))
79+
showgrid([apply(tfm, (image, bbox))]; ncol=6, npad=8)
11980
```
12081

121-
```julia
122-
o = showtransform(tfm, (image, bbox), 1)
123-
```
124-
125-
### [`Crop`](@ref)`(sz[, from])`
82+
## [`Crop`](@ref)`(sz[, from])`
12683

12784
Crops a region of size `sz` from the image, *without resizing* the image first.
12885

129-
```julia
86+
```@example deps
13087
using DataAugmentation: FromOrigin, FromCenter, FromRandom
13188
tfms = [
13289
Crop((128, 128), FromOrigin()),
@@ -136,36 +93,43 @@ tfms = [
13693
Crop((128, 128), FromRandom()),
13794
Crop((128, 128), FromRandom()),
13895
]
96+
showgrid([apply(tfm, (image, bbox)) for tfm in tfms]; ncol=6, npad=8)
13997
```
14098

141-
```julia
142-
o = showtransforms(tfms, (image, bbox))
143-
```
144-
145-
### [`FlipX`](@ref), [`FlipY`](@ref), [`Reflect`](@ref)
99+
## [`FlipX`](@ref), [`FlipY`](@ref), [`Reflect`](@ref)
146100

147101
Flip the data on the horizontally and vertically, respectively. More generally, reflect around an angle from the x-axis.
148102

149-
```julia
103+
```@example deps
150104
tfms = [
151105
FlipX(),
152106
FlipY(),
153107
Reflect(30),
154108
]
109+
showgrid([apply(tfm, (image, bbox)) for tfm in tfms]; ncol=6, npad=8)
155110
```
156111

157-
```julia
158-
o = showtransforms(tfms, (image, bbox))
159-
```
160-
161-
### [`Rotate`](@ref)
112+
## [`Rotate`](@ref), [`RotateX`](@ref), [`RotateY`](@ref), [`RotateZ`](@ref)
162113

163-
Rotate counter-clockwise by an angle.
114+
Rotate a 2D image counter-clockwise by an angle.
164115

165-
```julia
116+
```@example deps
166117
tfm = Rotate(20) |> CenterCrop((256, 256))
118+
showgrid([apply(tfm, (image, bbox)) for _ in 1:6]; ncol=6, npad=8)
167119
```
168120

169-
```julia
170-
o = showtransform(tfm, (image, bbox), 1)
121+
Rotate also works with 3D images in addition to 3D specific transforms RotateX, RotateY, and RotateZ.
122+
123+
```@example deps
124+
image3D = Image([RGB(i, j, k) for i=0:0.01:1, j=0:0.01:1, k=0:0.01:1])
125+
tfms = [
126+
Rotate(20, 30, 40),
127+
Rotate{3}(45),
128+
RotateX(45),
129+
RotateY(45),
130+
RotateZ(45),
131+
]
132+
transformed = [apply(tfm, image3D) |> itemdata for tfm in tfms]
133+
slices = [Image(parent(t[:, :, 50])) for t in transformed]
134+
showgrid(slices; ncol=6, npad=8)
171135
```

docs/src/ref.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,44 @@
11
# Reference
22
```@docs
3+
AdjustBrightness
4+
AdjustContrast
35
BoundingBox
6+
CenterCrop
7+
CenterResizeCrop
8+
Crop
9+
FlipX
10+
FlipY
411
Image
512
Keypoints
613
MaskBinary
714
MaskMulti
15+
Maybe
16+
OneOf
17+
PermuteDims
818
Polygon
19+
RandomCrop
920
RandomResizeCrop
10-
CenterResizeCrop
11-
Crop
21+
Reflect
1222
Rotate
23+
RotateX
24+
RotateY
25+
RotateZ
26+
ScaleKeepAspect
27+
ScaleRatio
28+
WarpAffine
1329
itemdata
1430
showitems
1531
1632
DataAugmentation.AbstractArrayItem
1733
DataAugmentation.AbstractItem
1834
DataAugmentation.ArrayItem
35+
DataAugmentation.Buffered
36+
DataAugmentation.BufferedThreadsafe
1937
DataAugmentation.Categorify
2038
DataAugmentation.ComposedProjectiveTransform
2139
DataAugmentation.FillMissing
2240
DataAugmentation.Identity
41+
DataAugmentation.ImageToTensor
2342
DataAugmentation.Item
2443
DataAugmentation.ItemWrapper
2544
DataAugmentation.MapElem

docs/src/transformations.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,26 @@ Projective transformations include:
2525

2626
Affine transformations are a subgroup of projective transformations that can be composed very efficiently: composing two affine transformations results in another affine transformation. Affine transformations can represent translation, scaling, reflection and rotation. Available `Transform`s are:
2727

28-
```@docs
29-
ScaleRatio
30-
ScaleKeepAspect
28+
```@docs; canonical=false
3129
FlipX
3230
FlipY
3331
Reflect
32+
Rotate
33+
RotateX
34+
RotateY
35+
RotateZ
36+
ScaleKeepAspect
37+
ScaleFixed
38+
ScaleRatio
3439
WarpAffine
40+
Zoom
3541
```
3642

3743
## Crops
3844

3945
To get a cropped result, simply `compose` any `ProjectiveTransform` with
4046

41-
```@docs
47+
```@docs; canonical=false
4248
CenterCrop
4349
RandomCrop
4450
```
@@ -47,17 +53,17 @@ RandomCrop
4753

4854
DataAugmentation.jl currently supports the following color transformations for augmentation:
4955

50-
```@docs
51-
AdjustContrast
56+
```@docs; canonical=false
5257
AdjustBrightness
58+
AdjustContrast
5359
```
5460

5561
# Stochastic transformations
5662
When augmenting data, it is often useful to apply a transformation only with some probability or choose from a set of transformations. Unlike in other data augmentation libraries like *albumentations*, in DataAugmentation.jl you can use wrapper transformations for this functionality.
5763

5864
- [`Maybe`](@ref)`(tfm, p = 0.5)` applies a transformation with probability `p`; and
5965
- [`OneOf`](@ref)`([tfm1, tfm2])` randomly selects a transformation to apply.
60-
```@docs
66+
```@docs; canonical=false
6167
Maybe
6268
OneOf
6369
```
@@ -72,6 +78,6 @@ titems = [apply(tfm, item) for _ in 1:8]
7278
showgrid(titems; ncol = 4, npad = 16)
7379
```
7480

75-
```@docs
81+
```@docs; canonical=false
7682
DataAugmentation.ImageToTensor
7783
```

src/DataAugmentation.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,9 @@ export Item,
7070
RandomCrop,
7171
ScaleFixed,
7272
Rotate,
73+
RotateX,
74+
RotateY,
75+
RotateZ,
7376
RandomResizeCrop,
7477
CenterResizeCrop,
7578
Buffered,

src/projective/affine.jl

Lines changed: 68 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -120,36 +120,92 @@ end
120120

121121
"""
122122
Rotate(γ)
123-
Rotate(γs)
123+
Rotate(distribution)
124+
Rotate(α, β, γ)
125+
Rotate(α_distribution, β_distribution, γ_distribution)
124126
125-
Rotate 2D spatial data around the center by an angle chosen at
126-
uniformly from [-γ, γ], an angle given in degrees.
127+
Rotate spatial data around its center. Rotate(γ) is a 2D rotation
128+
by an angle chosen uniformly from [-γ, γ], an angle given in degrees.
129+
Rotate(α, β, γ) is a 3D rotation by angles chosen uniformly from
130+
[-α, α], [-β, β], and [-γ, γ], for X, Y, and Z rotations.
127131
128132
You can also pass any `Distributions.Sampleable` from which the
129133
angle is selected.
130134
131135
## Examples
132136
133137
```julia
134-
tfm = Rotate(10)
135-
```
138+
tfm2d = Rotate(10)
139+
apply(tfm2d, Image(rand(Float32, 16, 16)))
136140
141+
tfm3d = Rotate(10, 20, 30)
142+
apply(tfm3d, Image(rand(Float32, 16, 16, 16)))
143+
```
137144
"""
138-
struct Rotate{S<:Sampleable} <: ProjectiveTransform
145+
struct Rotate{N, R, S<:Sampleable} <: ProjectiveTransform
139146
dist::S
140147
end
141-
Rotate(γ) = Rotate(Uniform(-abs(γ), abs(γ)))
148+
149+
Rotate{N, R}(s::S) where {N, R, S} = Rotate{N, R, S}(s)
150+
151+
function Rotate{N, R}(s::S) where {N, R, S<:Number}
152+
if s == 0
153+
return Identity()
154+
end
155+
Rotate{N, R, Uniform}(Uniform(-abs(s), abs(s)))
156+
end
157+
158+
Rotate(s) = Rotate{2, Type{RotMatrix{2, Float64}}}(s)
159+
Rotate(sx, sy, sz) = RotateX(sx) |> RotateY(sy) |> RotateZ(sz)
160+
Rotate{3}(s) = Rotate(s, s, s)
161+
Rotate{2}(s) = Rotate(s)
162+
163+
"""
164+
RotateX(γ)
165+
RotateX(distribution)
166+
167+
X-Axis rotation of 3D spatial data around the center by an angle chosen
168+
uniformly from [-γ, γ], an angle given in degrees.
169+
170+
You can also pass any `Distributions.Sampleable` from which the
171+
angle is selected.
172+
"""
173+
RotateX(s) = Rotate{3, Type{RotX{Float64}}}(s)
174+
175+
"""
176+
RotateY(γ)
177+
RotateY(distribution)
178+
179+
Y-Axis rotation of 3D spatial data around the center by an angle chosen
180+
uniformly from [-γ, γ], an angle given in degrees.
181+
182+
You can also pass any `Distributions.Sampleable` from which the
183+
angle is selected.
184+
"""
185+
RotateY(s) = Rotate{3, Type{RotY{Float64}}}(s)
186+
187+
"""
188+
RotateZ(γ)
189+
RotateZ(distribution)
190+
191+
Z-Axis rotation of 3D spatial data around the center by an angle chosen
192+
uniformly from [-γ, γ], an angle given in degrees.
193+
194+
You can also pass any `Distributions.Sampleable` from which the
195+
angle is selected.
196+
"""
197+
RotateZ(s) = Rotate{3, Type{RotZ{Float64}}}(s)
142198

143199
getrandstate(tfm::Rotate) = rand(tfm.dist)
144200

145201
function getprojection(
146-
tfm::Rotate,
147-
bounds::Bounds{2};
148-
randstate = getrandstate(tfm))
202+
tfm::Rotate{N, Type{R}, S},
203+
bounds::Bounds{N};
204+
randstate = getrandstate(tfm)) where {N, R, S}
149205
γ = randstate
150-
middlepoint = SVector{2, Float32}(mean.(bounds.rs))
206+
middlepoint = SVector{N, Float32}(mean.(bounds.rs))
151207
r = γ / 360 * 2pi
152-
return recenter(RotMatrix(convert(Float32, r)), middlepoint)
208+
return recenter(RotMatrix{N, Float32}(R(convert(Float64, r))), middlepoint)
153209
end
154210

155211

src/projective/compose.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ function getprojection(
4040
composed::ComposedProjectiveTransform,
4141
bounds;
4242
randstate = getrandstate(composed))
43+
@assert length(composed.tfms) == length(randstate)
4344
P = CoordinateTransformations.IdentityTransformation()
4445
for (tfm, r) in zip(composed.tfms, randstate)
4546
P_tfm = getprojection(tfm, bounds; randstate = r)

0 commit comments

Comments
 (0)