-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5652c56
commit 9ffcfef
Showing
8 changed files
with
503 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
# ImageEdgeDetection | ||
|
||
Currently under construction. | ||
|
||
[](https://zygmuntszpak.github.io/ImageEdgeDetection.jl/stable) | ||
[](https://zygmuntszpak.github.io/ImageEdgeDetection.jl/dev) | ||
[](https://travis-ci.com/zygmuntszpak/ImageEdgeDetection.jl) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.