Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GaussianBlur and ColorJitter ops. #84

Merged
merged 14 commits into from
Jul 12, 2021
1 change: 1 addition & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ MLDataPattern = "9920b226-0b2a-5f5f-9153-9aa70a013f8b"
OffsetArrays = "6fe1bfb0-de20-5000-8ca7-80f57d26f881"
Rotations = "6038ab10-8711-5258-84ad-4b1120ba62dc"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2"

[compat]
ComputationalResources = "0.3"
Expand Down
5 changes: 5 additions & 0 deletions src/Augmentor.jl
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ export

ElasticDistortion,

AdjustContrastBrightness,
GaussianBlur,

augment,
augment!,
augmentbatch!,
Expand All @@ -95,6 +98,8 @@ include("operations/resize.jl")
include("operations/scale.jl")
include("operations/zoom.jl")
include("operations/either.jl")
include("operations/brightness.jl")
include("operations/blur.jl")

include("distortionfields.jl")
include("distortedview.jl")
Expand Down
75 changes: 75 additions & 0 deletions src/operations/blur.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import ImageFiltering: imfilter, KernelFactors.gaussian

"""
GaussianBlur <: ImageOperation

Description
--------------

Blurs an image using a Gaussian filter.

Usage
--------------

GaussianBlur(k, [σ])

Arguments
--------------

- **`k`** : `Integer` or `AbstractVector` of `Integer` that denote
the kernel size. It must be an odd positive number.
- **`σ`** : Optional. `Real` or `AbstractVector` of `Real` that denote the
standard deviation. It must be a positive number.
Defaults to `0.3 * ((k - 1) / 2 - 1) + 0.8`.

Examples
--------------

```
using Augmentor
img = testpattern()

# use exactly k=3 and σ=1.0
augment(img, GaussianBlur(3, 1.0))

# pick k and σ randomly from the specified ranges
augment(img, GaussianBlur(3:2:7, 1.0:0.1:2.0))
```
"""
struct GaussianBlur{K <: AbstractVector, S <: AbstractVector} <: ImageOperation
k::K
σ::S

function GaussianBlur(k::K, σ::S) where {K <: AbstractVector{<:Integer},
S <: AbstractVector{<:Real}}
minimum(k) > 0 || throw(ArgumentError("Kernel size must be positive: $(k)"))
minimum(σ) > 0 || throw(ArgumentError("σ must be positive: $(σ)"))
new{K, S}(k, σ)
end
end

GaussianBlur(k, σ) = GaussianBlur(vectorize(k), vectorize(σ))
GaussianBlur(k) = GaussianBlur(k, 0.3 * ((k - 1) / 2 - 1) + 0.8)
barucden marked this conversation as resolved.
Show resolved Hide resolved

randparam(op::GaussianBlur, img) = (safe_rand(op.k), safe_rand(op.σ))

@inline supports_eager(::GaussianBlur) = true

function applyeager(op::GaussianBlur, img::AbstractArray, (k, σ))
kernel = gaussian((σ, σ), (k, k))
barucden marked this conversation as resolved.
Show resolved Hide resolved
return imfilter(img, kernel)
end
barucden marked this conversation as resolved.
Show resolved Hide resolved

function showconstruction(io::IO, op::GaussianBlur)
print(io, typeof(op).name.name, '(', op.k, ", ", op.σ,')')
end

function Base.show(io::IO, op::GaussianBlur)
if get(io, :compact, false)
print(io, "GaussianBlur with k=$(op.k) and σ=$(op.σ)")
else
print(io, "Augmentor.")
showconstruction(io, op)
end
end

101 changes: 101 additions & 0 deletions src/operations/brightness.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import ImageCore: clamp01
import Statistics: mean

"""
AdjustContrastBrightness <: ImageOperation

Description
--------------

Adjusts the brightness and contrast of each pixel.
barucden marked this conversation as resolved.
Show resolved Hide resolved

Usage
--------------

AdjustContrastBrightness(α, β)

Arguments
--------------

- **`α`** : `Real` or `AbstractVector` of `Real` that denote
the coefficient(s) for contrast adjustment.
- **`β`** : `Real` or `AbstractVector` of `Real` that denote
the coefficient(s) for brightness adjustment.

Examples
--------------

```
using Augmentor
img = testpattern()

# use exactly 1.2 for contrast, and one of 0.5 and 0.8 for brightness
augment(img, AdjustContrastBrightness(1.2, [0.5, 0.8]))

# pick the coefficients randomly from the specified ranges
augment(img, AdjustContrastBrightness(0.8:0.1:2.0, 0.5:0.1:1.1))
```
"""
struct AdjustContrastBrightness{T<:AbstractVector} <: ImageOperation
α::T
β::T

function AdjustContrastBrightness(α::T, β::T) where {T<:AbstractVector{<:Real}}
length(α) > 0 || throw(ArgumentError("Range $(α) is empty"))
length(β) > 0 || throw(ArgumentError("Range $(β) is empty"))
new{T}(α, β)
end
end

AdjustContrastBrightness(α, β) = AdjustContrastBrightness(vectorize(α),
vectorize(β))

randparam(op::AdjustContrastBrightness, img) = (safe_rand(op.α), safe_rand(op.β))

@inline supports_eager(::AdjustContrastBrightness) = true
@inline supports_lazy(::AdjustContrastBrightness) = true

function applyeager(op::AdjustContrastBrightness, img::AbstractArray, (α, β))
M = _get_M(op, img)
return _map_pix.(α, β, M, img)
end

function applylazy(op::AdjustContrastBrightness, img::AbstractArray, (α, β))
M = _get_M(op, img)
return AdjustedView(img, α, β, M)
end

function showconstruction(io::IO, op::AdjustContrastBrightness)
print(io, typeof(op).name.name, '(', op.α, ", ", op.β,')')
end

function Base.show(io::IO, op::AdjustContrastBrightness)
if get(io, :compact, false)
print(io, "Adjust contrast & brightness with coffecients from $(op.α) and $(op.β), respectively")
else
print(io, "Augmentor.")
showconstruction(io, op)
end
end

_map_pix(α, β, M, pix) = clamp01(α * pix + β * M)
_get_M(op::AdjustContrastBrightness, img) = mean(img)

# This wraps an image so that its pixels appear as after the contrast and
# brightness adjustment. This is done in the `getindex` method.
struct AdjustedView{T, P<:AbstractMatrix{T}, R} <: AbstractArray{T, 2}
orig::P
α::R
β::R
M::T

function AdjustedView(img::P, α::R, β::R, M) where {P <: AbstractMatrix,
R <: Real}
new{eltype(P), P, R}(img, α, β, M)
end
end

Base.parent(A::AdjustedView) = A.parent
Base.size(A::AdjustedView) = size(A.orig)
Base.axes(A::AdjustedView) = axes(A.orig)
Base.getindex(A::AdjustedView, i, j) = _map_pix(A.α, A.β, A.M, A.orig[i, j])
36 changes: 36 additions & 0 deletions test/operations/tst_blur.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import ImageFiltering: imfilter, KernelFactors.gaussian

@testset "GaussianBlur" begin
@testset "constructor" begin
@test_throws ArgumentError GaussianBlur(0)
@test_throws ArgumentError GaussianBlur(3, 0)
@test_throws ArgumentError GaussianBlur([1, 3], 0)
@test_throws ArgumentError GaussianBlur([1, 0], 1)
@test_throws ArgumentError GaussianBlur(3, -1:1)
end
@testset "randparam" begin
img = testpattern()
ks = [3, 1:2:5, [1, 3, 5]]
σs = [1, 1:0.1:2, [1, 2]]
for k in ks, σ in σs
op = GaussianBlur(k, σ)
p = @inferred Augmentor.randparam(op, img)
@test p[1] in k
@test p[2] in σ
end
end
@testset "eager" begin
@test Augmentor.supports_eager(GaussianBlur)

k = 3
σ = 3.2

imgs = [testpattern(), camera]

for img in imgs
ref = imfilter(img, gaussian((σ, σ), (k, k)))
res = Augmentor.applyeager(GaussianBlur(k, σ), img)
@test ref == res
end
end
end
1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ tests = [
"operations/tst_zoom.jl",
"operations/tst_distortions.jl",
"operations/tst_either.jl",
"operations/tst_blur.jl",
barucden marked this conversation as resolved.
Show resolved Hide resolved
"tst_operations.jl",
"tst_pipeline.jl",
"tst_augment.jl",
Expand Down