Skip to content

Commit

Permalink
add CropRatio operation
Browse files Browse the repository at this point in the history
  • Loading branch information
Evizero committed May 27, 2017
1 parent 6905395 commit da435b6
Show file tree
Hide file tree
Showing 6 changed files with 253 additions and 4 deletions.
26 changes: 25 additions & 1 deletion docs/usersguide/operations.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ functionality.
+-----------------------+----------------------------------------------------------------------------+
| Scaling and Resizing | :class:`Scale` :class:`Zoom` :class:`Resize` |
+-----------------------+----------------------------------------------------------------------------+
| Cropping | :class:`Crop` :class:`CropNative` :class:`CropSize` |
| Cropping | :class:`Crop` :class:`CropNative` :class:`CropSize` :class:`CropRatio` |
+-----------------------+----------------------------------------------------------------------------+
| Utility Operations | :class:`NoOp` :class:`CacheImage` :class:`Either` |
+-----------------------+----------------------------------------------------------------------------+
Expand Down Expand Up @@ -479,6 +479,30 @@ Cropping
| .. image:: https://raw.githubusercontent.com/JuliaML/FileStorage/master/Augmentor/testpattern_small.png | .. image:: https://raw.githubusercontent.com/JuliaML/FileStorage/master/Augmentor/operations/CropSize.png |
+-----------------------------------------------------------------------------------------------------------+-----------------------------------------------------------------------------------------------------------+

.. class:: CropRatio

Crops out the biggest area around the center of the given
image such that the output image satisfies the specified
aspect ratio (i.e. width divided by height).

For example the operation ``CropRatio(1)`` would denote a crop
for the biggest square around the center of the image, while
``CropRatio(16/9)`` would result in a rectangle with 16:9
aspect ratio.

.. code-block:: jlcon
julia> CropRatio(1)
Crop to 1:1 aspect ratio
julia> CropRatio(2.5)
Crop to 5:2 aspect ratio
+------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------+
| Input | Output for ``CropRatio(1)`` |
+============================================================================================================+============================================================================================================+
| .. image:: https://raw.githubusercontent.com/JuliaML/FileStorage/master/Augmentor/testpattern_small.png | .. image:: https://raw.githubusercontent.com/JuliaML/FileStorage/master/Augmentor/operations/CropRatio.png |
+------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------+

Resizing
***********
Expand Down
1 change: 1 addition & 0 deletions src/Augmentor.jl
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export
Crop,
CropNative,
CropSize,
CropRatio,

Resize,

Expand Down
127 changes: 124 additions & 3 deletions src/operations/crop.jl
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ julia> augment(img, Crop(1:30, 361:400)) # crop upper right corner
see also
--------------
[`CropNative`](@ref), [`CropSize`](@ref), [`augment`](@ref)
[`CropNative`](@ref), [`CropSize`](@ref), [`CropRatio`](@ref), [`augment`](@ref)
"""
immutable Crop{N,I<:Tuple} <: Operation
indexes::I
Expand Down Expand Up @@ -139,7 +139,7 @@ augment(img, Rotate(45) |> CropNative(1:300, 1:400))
see also
--------------
[`Crop`](@ref), [`CropSize`](@ref), [`augment`](@ref)
[`Crop`](@ref), [`CropSize`](@ref), [`CropRatio`](@ref), [`augment`](@ref)
"""
immutable CropNative{N,I<:Tuple} <: Operation
indexes::I
Expand Down Expand Up @@ -222,7 +222,7 @@ augment(img, Rotate(45) |> CropSize(300, 400))
see also
--------------
[`Crop`](@ref), [`CropNative`](@ref), [`augment`](@ref)
[`CropRatio`](@ref), [`Crop`](@ref), [`CropNative`](@ref), [`augment`](@ref)
"""
immutable CropSize{N} <: Operation
size::NTuple{N,Int}
Expand Down Expand Up @@ -276,3 +276,124 @@ function Base.show{N}(io::IO, op::CropSize{N})
print(io, typeof(op), "($(op.size))")
end
end

# --------------------------------------------------------------------

"""
CropRatio <: Augmentor.Operation
Description
--------------
Crops out the biggest area around the center of the given image
such that the output image satisfies the specified aspect ratio
(i.e. width divided by height).
For example the operation `CropRatio(1)` would denote a crop
for the biggest square around the center of the image.
Usage
--------------
CropSize(ratio)
CropSize(; ratio = 1)
Arguments
--------------
- **`ratio`** : Optional. A number denoting the aspect ratio. For
example specifying `ratio=16/9` would denote a 16:9 aspect
ratio. Defaults to `1`, which describes a square crop.
Examples
--------------
```julia
using Augmentor
img = testpattern()
# crop biggest square around the image center
augment(img, CropRatio(1))
```
see also
--------------
[`CropSize`](@ref), [`Crop`](@ref), [`CropNative`](@ref), [`augment`](@ref)
"""
immutable CropRatio <: Operation
ratio::Float64

function (::Type{CropRatio})(ratio::Real)
ratio > 0 || throw(ArgumentError("ratio has to be greater than 0"))
new(Float64(ratio))
end
end
CropRatio(; ratio = 1.) = CropRatio(ratio)

@inline supports_eager{T<:CropRatio}(::Type{T}) = false
@inline supports_affine{T<:CropRatio}(::Type{T}) = true
@inline supports_view{T<:CropRatio}(::Type{T}) = true
@inline supports_stepview{T<:CropRatio}(::Type{T}) = true

function cropratio_indices(op::CropRatio, img::AbstractMatrix)
h, w = map(length, indices(img)) #convert(Tuple, center(img))
ratio = op.ratio
# compute new size based on ratio
nw = floor(Int, h * ratio)
nh = floor(Int, w / ratio)
nw = nw > 1 ? nw : 1
nh = nh > 1 ? nh : 1
sze = nh < h ? nh : h, nw < w ? nw : w
# compute indices around center for given size
cntr = convert(Tuple, center(img))
corner = map((ci,si)->floor(Int,ci)-floor(Int,si/2)+!isinteger(ci), cntr, sze)
map((b,s)->b:(b+s-1), corner, sze)
end

applyeager(op::CropRatio, img) = plain_array(applyview(op, img))
applylazy(op::CropRatio, img) = applyview(op, img)
applyaffine(op::CropRatio, img) = applyview(op, img)

function applyview(op::CropRatio, img)
direct_view(img, cropratio_indices(op, img))
end

function applystepview(op::CropRatio, img)
direct_view(img, map(StepRange, cropratio_indices(op, img)))
end

function showconstruction(io::IO, op::CropRatio)
print(io, typeof(op).name.name, '(', op.ratio, ')')
end

function Base.show(io::IO, op::CropRatio)
if get(io, :compact, false)
high0 = if op.ratio >= 1
op.ratio
else
1 / op.ratio
end
low, high = 1, high0
found = false
for i = 1:20
high = i * high0
if round(high) == round(high,2)
low = i
found = true
break
end
end
if !found
print(io, "Crop to $(round(op.ratio,2)) aspect ratio")
elseif op.ratio >= 1
print(io, "Crop to $(round(Int,high)):$low aspect ratio")
else
print(io, "Crop to $low:$(round(Int,high)) aspect ratio")
end
else
print(io, "Augmentor.")
showconstruction(io, op)
end
end
74 changes: 74 additions & 0 deletions test/operations/tst_crop.jl
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,77 @@ end
@test @inferred(Augmentor.supports_permute(CropSize)) === false
end
end

# --------------------------------------------------------------------

@testset "CropRatio" begin
@test (CropRatio <: Augmentor.AffineOperation) == false
@test typeof(@inferred(CropRatio())) <: CropRatio <: Augmentor.Operation
@testset "constructor" begin
@test_throws MethodError CropRatio(())
@test_throws MethodError CropRatio(1.,2.)
@test_throws MethodError CropRatio(:a)
@test_throws MethodError CropRatio([:a])
@test_throws ArgumentError CropRatio(-1)
@test_throws ArgumentError CropRatio(0)
op = @inferred(CropRatio(3/4))
@test op === CropRatio(ratio=3/4)
@test str_show(op) == "Augmentor.CropRatio(0.75)"
@test str_showconst(op) == "CropRatio(0.75)"
@test str_showcompact(op) == "Crop to 3:4 aspect ratio"
op = @inferred(CropRatio(1))
@test op === @inferred(CropRatio())
@test op === CropRatio(ratio=1)
@test str_show(op) == "Augmentor.CropRatio(1.0)"
@test str_showconst(op) == "CropRatio(1.0)"
@test str_showcompact(op) == "Crop to 1:1 aspect ratio"
op = @inferred(CropRatio(2.5))
@test op === CropRatio(ratio=2.5)
@test str_show(op) == "Augmentor.CropRatio(2.5)"
@test str_showconst(op) == "CropRatio(2.5)"
@test str_showcompact(op) == "Crop to 5:2 aspect ratio"
op = @inferred(CropRatio(sqrt(2)))
@test str_showcompact(op) == "Crop to 1.41 aspect ratio"
end
@testset "eager" begin
@test_throws MethodError Augmentor.applyeager(CropRatio(10), nothing)
@test_throws MethodError Augmentor.applyeager(CropRatio(2), nothing)
@test @inferred(Augmentor.supports_eager(CropRatio)) === false
for img in (rect, OffsetArray(rect, -2, -1), view(rect, IdentityRange(1:2), IdentityRange(1:3)))
@test @inferred(Augmentor.applyeager(CropRatio(3/2), img)) == rect
@test typeof(Augmentor.applyeager(CropRatio(3/2), img)) <: Array
end
@test @inferred(Augmentor.applyeager(CropRatio(1), rect)) == rect[1:2,1:2]
@test @inferred(Augmentor.applyeager(CropRatio(1), square)) == square
@test @inferred(Augmentor.applyeager(CropRatio(1), square2)) == square2
end
@testset "affine" begin
@test @inferred(Augmentor.isaffine(CropRatio)) === false
@test @inferred(Augmentor.supports_affine(CropRatio)) === true
@test_throws MethodError Augmentor.applyaffine(CropRatio(1), nothing)
@test @inferred(Augmentor.applyaffine(CropRatio(1), rect)) === view(rect, IdentityRange(1:2), IdentityRange(1:2))
@test @inferred(Augmentor.applyaffine(CropRatio(2), square2)) === view(square2, IdentityRange(2:3), IdentityRange(1:4))
@test @inferred(Augmentor.applyaffine(CropRatio(.5), square2)) === view(square2, IdentityRange(1:4), IdentityRange(2:3))
end
@testset "lazy" begin
@test @inferred(Augmentor.supports_lazy(CropRatio)) === true
@test @inferred(Augmentor.applylazy(CropRatio(1), rect)) === view(rect, IdentityRange(1:2), IdentityRange(1:2))
@test @inferred(Augmentor.applylazy(CropRatio(2), square2)) === view(square2, IdentityRange(2:3), IdentityRange(1:4))
@test @inferred(Augmentor.applylazy(CropRatio(.5), square2)) === view(square2, IdentityRange(1:4), IdentityRange(2:3))
end
@testset "view" begin
@test @inferred(Augmentor.supports_view(CropRatio)) === true
@test @inferred(Augmentor.applyview(CropRatio(1), rect)) === view(rect, IdentityRange(1:2), IdentityRange(1:2))
@test @inferred(Augmentor.applyview(CropRatio(2), square2)) === view(square2, IdentityRange(2:3), IdentityRange(1:4))
@test @inferred(Augmentor.applyview(CropRatio(.5), square2)) === view(square2, IdentityRange(1:4), IdentityRange(2:3))
end
@testset "stepview" begin
@test @inferred(Augmentor.supports_stepview(CropRatio)) === true
@test @inferred(Augmentor.applystepview(CropRatio(1), rect)) === view(rect, 1:1:2, 1:1:2)
@test @inferred(Augmentor.applystepview(CropRatio(2), square2)) === view(square2, 2:1:3, 1:1:4)
@test @inferred(Augmentor.applystepview(CropRatio(.5), square2)) === view(square2, 1:1:4, 2:1:3)
end
@testset "permute" begin
@test @inferred(Augmentor.supports_permute(CropRatio)) === false
end
end
Loading

0 comments on commit da435b6

Please sign in to comment.