From 42795ddf60fd201c39c4bc97214ce6f9bf53441e Mon Sep 17 00:00:00 2001 From: Zygmunt Szpak Date: Sat, 11 Sep 2021 20:22:32 +0930 Subject: [PATCH] Introduces AbstractIntensityAdjustmentAlgorithm --- .../ContrastAdjustmentAPI.jl} | 5 +- .../histogram_adjustment.jl | 7 +- .../intensity_adjustment.jl | 177 ++++++++++++++++++ src/ImageContrastAdjustment.jl | 12 +- 4 files changed, 192 insertions(+), 9 deletions(-) rename src/{HistogramAdjustmentAPI/HistogramAdjustmentAPI.jl => ContrastAdjustmentAPI/ContrastAdjustmentAPI.jl} (88%) rename src/{HistogramAdjustmentAPI => ContrastAdjustmentAPI}/histogram_adjustment.jl (95%) create mode 100644 src/ContrastAdjustmentAPI/intensity_adjustment.jl diff --git a/src/HistogramAdjustmentAPI/HistogramAdjustmentAPI.jl b/src/ContrastAdjustmentAPI/ContrastAdjustmentAPI.jl similarity index 88% rename from src/HistogramAdjustmentAPI/HistogramAdjustmentAPI.jl rename to src/ContrastAdjustmentAPI/ContrastAdjustmentAPI.jl index f1b1296..b4dc0c1 100644 --- a/src/HistogramAdjustmentAPI/HistogramAdjustmentAPI.jl +++ b/src/ContrastAdjustmentAPI/ContrastAdjustmentAPI.jl @@ -1,6 +1,6 @@ # This is a temporary module to validate `AbstractImageFilter` idea # proposed in https://github.com/JuliaImages/ImagesAPI.jl/pull/3 -module HistogramAdjustmentAPI +module ContrastAdjustmentAPI using ImageCore # ColorTypes is sufficient @@ -22,8 +22,9 @@ Filters are image algorithms whose input and output are both images abstract type AbstractImageFilter <: AbstractImageAlgorithm end include("histogram_adjustment.jl") +include("intensity_adjustment.jl") # we do not export any symbols since we don't require # package developers to implemente all the APIs -end # module HistogramAdjustmentAPI +end # module ContrastAdjustmentAPI diff --git a/src/HistogramAdjustmentAPI/histogram_adjustment.jl b/src/ContrastAdjustmentAPI/histogram_adjustment.jl similarity index 95% rename from src/HistogramAdjustmentAPI/histogram_adjustment.jl rename to src/ContrastAdjustmentAPI/histogram_adjustment.jl index 296a586..a5b2fb3 100644 --- a/src/HistogramAdjustmentAPI/histogram_adjustment.jl +++ b/src/ContrastAdjustmentAPI/histogram_adjustment.jl @@ -1,12 +1,13 @@ # usage example for package developer: # -# import HistogramAdjustmentAPI: AbstractHistogramAdjustmentAlgorithm, +# import ContrastAdjustmentAPI: AbstractHistogramAdjustmentAlgorithm, # adjust_histogram, adjust_histogram! """ AbstractHistogramAdjustmentAlgorithm <: AbstractImageFilter -The root type for `ImageContrastAdjustment` package. +A root type for `ImageContrastAdjustment` package that relates to algorithms +that manipulate contrast by operating on intensity histograms. Any concrete histogram adjustment algorithm shall subtype it to support [`adjust_histogram`](@ref) and [`adjust_histogram!`](@ref) APIs. @@ -18,7 +19,7 @@ following pattern: ```julia # first generate an algorithm instance -f = LinearStretching() +f = Equalization() # then pass the algorithm to `adjust_histogram` img_adjusted = adjust_histogram(img, f) diff --git a/src/ContrastAdjustmentAPI/intensity_adjustment.jl b/src/ContrastAdjustmentAPI/intensity_adjustment.jl new file mode 100644 index 0000000..2ca71a5 --- /dev/null +++ b/src/ContrastAdjustmentAPI/intensity_adjustment.jl @@ -0,0 +1,177 @@ +# usage example for package developer: +# +# import ContrastAdjustmentAPI: AbstractIntensityAdjustmentAlgorithm, +# adjust_intensity, adjust_intensity! + +""" + AbstractIntensityAdjustmentAlgorithm <: AbstractImageFilter + +A root type for `ImageContrastAdjustment` package that relates to algorithms +that manipulate contrast without operating on intensity histograms. + +Any concrete intensity adjustment algorithm shall subtype it to support +[`adjust_intensity`](@ref) and [`adjust_intensity!`](@ref) APIs. + +# Examples + +All intensity adjustment algorithms in ImageContrastAdjustment are called in the +following pattern: + +```julia +# first generate an algorithm instance +f = LinearStretching() + +# then pass the algorithm to `adjust_intensity` +img_adjusted = adjust_intensity(img, f) + +# or use in-place version `adjust_intensity!` +img_adjusted = similar(img) +adjust_intensity!(img_adjusted, img, f) +``` + +Some algorithms also receive additional information as an argument, +e.g., `gamma` of `GammaCorrection`. + +```julia +# you can explicit specify the parameters +f = GammaCorrection(gamma = 1.4) +``` + +For more examples, please check [`adjust_intensity`](@ref), +[`adjust_intensity!`](@ref) and concrete algorithms. +""" +abstract type AbstractIntensityAdjustmentAlgorithm <: AbstractImageFilter end + +adjust_intensity!(out::Union{GenericGrayImage, AbstractArray{<:Color3}}, + img, + f::AbstractIntensityAdjustmentAlgorithm, + args...; kwargs...) = + f(out, img, args...; kwargs...) + +# TODO: Relax this to all color types +function adjust_intensity!(img::Union{GenericGrayImage, AbstractArray{<:Color3}}, + f::AbstractIntensityAdjustmentAlgorithm, + args...; kwargs...) + tmp = copy(img) + f(img, tmp, args...; kwargs...) + return img +end + +function adjust_intensity(::Type{T}, + img, + f::AbstractIntensityAdjustmentAlgorithm, + args...; kwargs...) where T + out = similar(Array{T}, axes(img)) + adjust_intensity!(out, img, f, args...; kwargs...) + return out +end + +adjust_intensity(img::AbstractArray{T}, + f::AbstractIntensityAdjustmentAlgorithm, + args...; kwargs...) where T <: Colorant = + adjust_intensity(T, img, f, args...; kwargs...) + +# Do not promote Number to Gray{<:Number} +adjust_intensity(img::AbstractArray{T}, + f::AbstractIntensityAdjustmentAlgorithm, + args...; kwargs...) where T <: Number = + adjust_intensity(T, img, f, args...; kwargs...) + + +# Handle instance where the input is a sequence of images. +adjust_intensity!(out_sequence::Vector{T}, + img_sequence, + f::AbstractIntensityAdjustmentAlgorithm, + args...; kwargs...) where T <: Union{GenericGrayImage, AbstractArray{<:Color3}} = + f(out_sequence, img_sequence, args...; kwargs...) + +# TODO: Relax this to all color types +function adjust_intensity!(img_sequence::Vector{T}, + f::AbstractIntensityAdjustmentAlgorithm, + args...; kwargs...) where T <: Union{GenericGrayImage, AbstractArray{<:Color3}} + tmp = copy(img_sequence) + f(img_sequence, tmp, args...; kwargs...) + return img_sequence +end + +function adjust_intensity(::Type{T}, + img_sequence::Vector{<:AbstractArray}, + f::AbstractIntensityAdjustmentAlgorithm, + args...; kwargs...) where T + N = length(img_sequence) + out_sequence = [similar(Array{T}, axes(img_sequence[n])) for n = 1:N] + adjust_intensity!(out_sequence, img_sequence, f, args...; kwargs...) + return out_sequence +end + +adjust_intensity(img_sequence::Vector{<:AbstractArray{T}}, + f::AbstractIntensityAdjustmentAlgorithm, + args...; kwargs...) where T <: Colorant = + adjust_intensity(T, img_sequence, f, args...; kwargs...) + +# Do not promote Number to Gray{<:Number} +adjust_intensity(img_sequence::Vector{<:AbstractArray{T}}, + f::AbstractIntensityAdjustmentAlgorithm, + args...; kwargs...) where T <: Number = + adjust_intensity(T, img_sequence, f, args...; kwargs...) + +### Docstrings + +""" + adjust_intensity!([out,] img, f::AbstractIntensityAdjustmentAlgorithm, args...; kwargs...) + +Adjust intensity 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 `adjust_intensity!`: + +```julia +img_adjusted = similar(img) +adjust_intensity!(img_adjusted, img, f) +``` + +For cases you just want to change `img` in place, you don't necessarily need to manually +allocate `img_adjusted`; just use the convenient method: + +```julia +adjust_intensity!(img, f) +``` + +See also: [`adjust_intensity`](@ref) +""" +adjust_intensity! + +""" + adjust_intensity([T::Type,] img, f::AbstractIntensityAdjustmentAlgorithm, args...; kwargs...) + +Adjust intensity of `img` using algorithm `f`. + +# Output + +The return image `img_adjusted` is an `Array{T}`. + +If `T` is not specified, then it's inferred. +# Examples + +Just simply pass the input image and algorithm to `adjust_intensity` + +```julia +img_adjusted = adjust_intensity(img, f) +``` + +This reads as "`adjust_intensity` of image `img` using algorithm `f`". + +You can also explicitly specify the return type: + +```julia +img_adjusted_float32 = adjust_intensity(Gray{Float32}, img, f) +``` + +See also [`adjust_intensity!`](@ref) for in-place intensity adjustment. +""" +adjust_intensity diff --git a/src/ImageContrastAdjustment.jl b/src/ImageContrastAdjustment.jl index 101d2b0..cecf6a8 100644 --- a/src/ImageContrastAdjustment.jl +++ b/src/ImageContrastAdjustment.jl @@ -7,9 +7,11 @@ using ImageCore.MappedArrays using Parameters: @with_kw # Same as Base.@kwdef but works on Julia 1.0 # TODO: port HistogramAdjustmentAPI to ImagesAPI -include("HistogramAdjustmentAPI/HistogramAdjustmentAPI.jl") -import .HistogramAdjustmentAPI: AbstractHistogramAdjustmentAlgorithm, - adjust_histogram, adjust_histogram! +include("ContrastAdjustmentAPI/ContrastAdjustmentAPI.jl") +import .ContrastAdjustmentAPI: AbstractHistogramAdjustmentAlgorithm, + adjust_histogram, adjust_histogram!, + AbstractIntensityAdjustmentAlgorithm, + adjust_intensity, adjust_intensity! # TODO Relax this to all image color types const GenericGrayImage = AbstractArray{<:Union{Number, AbstractGray}} @@ -37,6 +39,8 @@ export ContrastStretching, build_histogram, adjust_histogram, - adjust_histogram! + adjust_histogram!, + adjust_intensity, + adjust_intensity! end # module