Skip to content

Commit

Permalink
Adds API outline and function stubs
Browse files Browse the repository at this point in the history
  • Loading branch information
zygmuntszpak committed Feb 5, 2020
1 parent 5652c56 commit 9ffcfef
Show file tree
Hide file tree
Showing 8 changed files with 503 additions and 1 deletion.
7 changes: 7 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ uuid = "2b14c160-480b-11ea-1b58-656063328ff7"
authors = ["Dr. Zygmunt L. Szpak"]
version = "0.1.0"

[deps]
ColorVectorSpace = "c3611d14-8923-5661-9e6a-0046d554d3a4"
ImageCore = "a09fc81d-aa75-5fe9-8630-4744c3626534"
ImageFiltering = "6a3955dd-da59-5b1f-98d4-e7296123deb5"
MappedArrays = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900"
Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a"

[compat]
julia = "1"

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# ImageEdgeDetection

Currently under construction.

[![Stable](https://img.shields.io/badge/docs-stable-blue.svg)](https://zygmuntszpak.github.io/ImageEdgeDetection.jl/stable)
[![Dev](https://img.shields.io/badge/docs-dev-blue.svg)](https://zygmuntszpak.github.io/ImageEdgeDetection.jl/dev)
[![Build Status](https://travis-ci.com/zygmuntszpak/ImageEdgeDetection.jl.svg?branch=master)](https://travis-ci.com/zygmuntszpak/ImageEdgeDetection.jl)
30 changes: 30 additions & 0 deletions src/EdgeDetectionAPI/EdgeDetectionAPI.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This is a temporary module to validate `AbstractImageFilter` idea
# proposed in https://github.com/JuliaImages/ImagesAPI.jl/pull/3
module EdgeDetectionAPI

using ImageCore # ColorTypes is sufficient

# TODO Relax this to all image color types
const GenericGrayImage = AbstractArray{<:Union{Number, AbstractGray}}

"""
AbstractImageAlgorithm
The root of image algorithms type system
"""
abstract type AbstractImageAlgorithm end

"""
AbstractImageFilter <: AbstractImageAlgorithm
Filters are image algorithms whose input and output are both images
"""
abstract type AbstractImageFilter <: AbstractImageAlgorithm end

include("edge_detection.jl")
include("edge_thinning.jl")

# we do not export any symbols since we don't require
# package developers to implemente all the APIs

end # module EdgeDetectionAPI
169 changes: 169 additions & 0 deletions src/EdgeDetectionAPI/edge_detection.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# usage example for package developer:
#
# import EdgeDetectionAPI: AbstractEdgeDetectionAlgorithm,
# detect_edges, detect_edges!

"""
AbstractEdgeDetectionAlgorithm <: AbstractImageFilter
The root type for `ImageEdgeDetection` package.
Any concrete edge detection algorithm shall subtype it to support
[`detect_edges`](@ref) and [`detect_edges!`](@ref) APIs.
# Examples
All edge detection algorithms in ImageEdgeDetection are called in the
following pattern:
```julia
# first generate an algorithm instance
f = Canny()
# then pass the algorithm to `detect_edges`
img_edges = detect_edges(img, f)
# or use in-place version `thin_edges!`
img_edges = similar(img)
detect_edges!(img_edges, img, f)
```
For more examples, please check [`detect_edges`](@ref),
[`detect_edges!`](@ref) and concrete algorithms.
"""
abstract type AbstractEdgeDetectionAlgorithm <: AbstractImageFilter end

detect_edges!(out::Union{GenericGrayImage, AbstractArray{<:Color3}},
img,
f::AbstractEdgeDetectionAlgorithm,
args...; kwargs...) =
f(out, img, args...; kwargs...)

# TODO: Relax this to all color types
function detect_edges!(img::Union{GenericGrayImage, AbstractArray{<:Color3}},
f::AbstractEdgeDetectionAlgorithm,
args...; kwargs...)
tmp = copy(img)
f(img, tmp, args...; kwargs...)
return img
end

function detect_edges(::Type{T},
img,
f::AbstractEdgeDetectionAlgorithm,
args...; kwargs...) where T
out = similar(Array{T}, axes(img))
detect_edges!(out, img, f, args...; kwargs...)
return out
end

detect_edges(img::AbstractArray{T},
f::AbstractEdgeDetectionAlgorithm,
args...; kwargs...) where T <: Colorant =
detect_edges(T, img, f, args...; kwargs...)

# Do not promote Number to Gray{<:Number}
detect_edges(img::AbstractArray{T},
f::AbstractEdgeDetectionAlgorithm,
args...; kwargs...) where T <: Number =
detect_edges(T, img, f, args...; kwargs...)


# Handle instance where the input is a sequence of images.
detect_edges!(out_sequence::Vector{T},
img_sequence,
f::AbstractEdgeDetectionAlgorithm,
args...; kwargs...) where T <: Union{GenericGrayImage, AbstractArray{<:Color3}} =
f(out_sequence, img_sequence, args...; kwargs...)

# TODO: Relax this to all color types
function detect_edges!(img_sequence::Vector{T},
f::AbstractEdgeDetectionAlgorithm,
args...; kwargs...) where T <: Union{GenericGrayImage, AbstractArray{<:Color3}}
tmp = copy(img_sequence)
f(img_sequence, tmp, args...; kwargs...)
return img_sequence
end

function detect_edges(::Type{T},
img_sequence::Vector{<:AbstractArray},
f::AbstractEdgeDetectionAlgorithm,
args...; kwargs...) where T
N = length(img_sequence)
out_sequence = [similar(Array{T}, axes(img_sequence[n])) for n = 1:N]
detect_edges!(out_sequence, img_sequence, f, args...; kwargs...)
return out_sequence
end

detect_edges(img_sequence::Vector{<:AbstractArray{T}},
f::AbstractEdgeDetectionAlgorithm,
args...; kwargs...) where T <: Colorant =
detect_edges(T, img_sequence, f, args...; kwargs...)

# Do not promote Number to Gray{<:Number}
detect_edges(img_sequence::Vector{<:AbstractArray{T}},
f::AbstractEdgeDetectionAlgorithm,
args...; kwargs...) where T <: Number =
detect_edges(T, img_sequence, f, args...; kwargs...)

### Docstrings

"""
detect_edges!([out,] img, f::AbstractEdgeDetectionAlgorithm, args...; kwargs...)
Detect edges of `img` using algorithm `f`.
# Output
If `out` is specified, it will be changed in place. Otherwise `img` will be changed in place.
# Examples
Just simply pass an algorithm to `detect_edges!`:
```julia
img_edges = similar(img)
detect_edges!(img_edges, img, f)
```
For cases you just want to change `img` in place, you don't necessarily need to manually
allocate `img_edges`; just use the convenient method:
```julia
detect_edges!(img, f)
```
See also: [`detect_edges`](@ref)
"""
detect_edges!

"""
detect_edges([T::Type,] img, f::AbstractEdgeDetectionAlgorithm, args...; kwargs...)
Detect edges of `img` using algorithm `f`.
# Output
The return image `img_edges` is an `Array{T}`.
If `T` is not specified, then it's inferred.
# Examples
Just simply pass the input image and algorithm to `detect_edges`
```julia
img_edges = detect_edges(img, f)
```
This reads as "`detect_edges` of image `img` using algorithm `f`".
You can also explicitly specify the return type:
```julia
img_edges_float32 = detect_edges(Gray{Float32}, img, f)
```
See also [`detect_edges!`](@ref) for in-place edge detection.
"""
detect_edges
169 changes: 169 additions & 0 deletions src/EdgeDetectionAPI/edge_thinning.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# usage example for package developer:
#
# import EdgeDetectionAPI: AbstractEdgeThinningAlgorithm,
# thin_edges, thin_edges!

"""
AbstractEdgeThinningAlgorithm <: AbstractImageFilter
The root type for `ImageEdgeDetection` package.
Any concrete edge thinning algorithm shall subtype it to support
[`thin_edges`](@ref) and [`thin_edges!`](@ref) APIs.
# Examples
All edge thinning algorithms in ImageEdgeDetection are called in the
following pattern:
```julia
# first generate an algorithm instance
f = NonmaximaSuppression()
# then pass the algorithm to `thin_edges`
img_edges = thin_edges(img, f)
# or use in-place version `thin_edges!`
img_edges = similar(img)
thin_edges!(img_edges, img, f)
```
For more examples, please check [`thin_edges`](@ref),
[`thin_edges!`](@ref) and concrete algorithms.
"""
abstract type AbstractEdgeThinningAlgorithm <: AbstractImageFilter end

thin_edges!(out::Union{GenericGrayImage, AbstractArray{<:Color3}},
img,
f::AbstractEdgeThinningAlgorithm,
args...; kwargs...) =
f(out, img, args...; kwargs...)

# TODO: Relax this to all color types
function thin_edges!(img::Union{GenericGrayImage, AbstractArray{<:Color3}},
f::AbstractEdgeThinningAlgorithm,
args...; kwargs...)
tmp = copy(img)
f(img, tmp, args...; kwargs...)
return img
end

function thin_edges(::Type{T},
img,
f::AbstractEdgeThinningAlgorithm,
args...; kwargs...) where T
out = similar(Array{T}, axes(img))
thin_edges!(out, img, f, args...; kwargs...)
return out
end

thin_edges(img::AbstractArray{T},
f::AbstractEdgeThinningAlgorithm,
args...; kwargs...) where T <: Colorant =
thin_edges(T, img, f, args...; kwargs...)

# Do not promote Number to Gray{<:Number}
thin_edges(img::AbstractArray{T},
f::AbstractEdgeThinningAlgorithm,
args...; kwargs...) where T <: Number =
thin_edges(T, img, f, args...; kwargs...)


# Handle instance where the input is a sequence of images.
thin_edges!(out_sequence::Vector{T},
img_sequence,
f::AbstractEdgeThinningAlgorithm,
args...; kwargs...) where T <: Union{GenericGrayImage, AbstractArray{<:Color3}} =
f(out_sequence, img_sequence, args...; kwargs...)

# TODO: Relax this to all color types
function thin_edges!(img_sequence::Vector{T},
f::AbstractEdgeThinningAlgorithm,
args...; kwargs...) where T <: Union{GenericGrayImage, AbstractArray{<:Color3}}
tmp = copy(img_sequence)
f(img_sequence, tmp, args...; kwargs...)
return img_sequence
end

function thin_edges(::Type{T},
img_sequence::Vector{<:AbstractArray},
f::AbstractEdgeThinningAlgorithm,
args...; kwargs...) where T
N = length(img_sequence)
out_sequence = [similar(Array{T}, axes(img_sequence[n])) for n = 1:N]
thin_edges!(out_sequence, img_sequence, f, args...; kwargs...)
return out_sequence
end

thin_edges(img_sequence::Vector{<:AbstractArray{T}},
f::AbstractEdgeThinningAlgorithm,
args...; kwargs...) where T <: Colorant =
thin_edges(T, img_sequence, f, args...; kwargs...)

# Do not promote Number to Gray{<:Number}
thin_edges(img_sequence::Vector{<:AbstractArray{T}},
f::AbstractEdgeThinningAlgorithm,
args...; kwargs...) where T <: Number =
thin_edges(T, img_sequence, f, args...; kwargs...)

### Docstrings

"""
thin_edges!([out,] img, f::AbstractEdgeThinningAlgorithm, args...; kwargs...)
Detect edges of `img` using algorithm `f`.
# Output
If `out` is specified, it will be changed in place. Otherwise `img` will be changed in place.
# Examples
Just simply pass an algorithm to `thin_edges!`:
```julia
img_edges = similar(img)
thin_edges!(img_edges, img, f)
```
For cases you just want to change `img` in place, you don't necessarily need to manually
allocate `img_edges`; just use the convenient method:
```julia
thin_edges!(img, f)
```
See also: [`thin_edges`](@ref)
"""
thin_edges!

"""
thin_edges([T::Type,] img, f::AbstractEdgeThinningAlgorithm, args...; kwargs...)
Detect edges of `img` using algorithm `f`.
# Output
The return image `img_edges` is an `Array{T}`.
If `T` is not specified, then it's inferred.
# Examples
Just simply pass the input image and algorithm to `thin_edges`
```julia
img_edges = thin_edges(img, f)
```
This reads as "`thin_edges` of image `img` using algorithm `f`".
You can also explicitly specify the return type:
```julia
img_edges_float32 = thin_edges(Gray{Float32}, img, f)
```
See also [`thin_edges!`](@ref) for in-place edge thinning.
"""
thin_edges
Loading

0 comments on commit 9ffcfef

Please sign in to comment.