diff --git a/Project.toml b/Project.toml index 922a1c1..48102b0 100644 --- a/Project.toml +++ b/Project.toml @@ -1,9 +1,10 @@ name = "ImageContrastAdjustment" uuid = "f332f351-ec65-5f6a-b3d1-319c6670881a" authors = ["Dr. Zygmunt L. Szpak "] -version = "0.3.11" +version = "0.3.12" [deps] +ImageBase = "c817782e-172a-44cc-b673-b171935fbb9e" ImageCore = "a09fc81d-aa75-5fe9-8630-4744c3626534" ImageTransformations = "02fcd773-0e25-5acc-982a-7f6622650795" Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a" diff --git a/src/HistogramAdjustmentAPI/HistogramAdjustmentAPI.jl b/src/HistogramAdjustmentAPI/HistogramAdjustmentAPI.jl index f1b1296..0f6c2dc 100644 --- a/src/HistogramAdjustmentAPI/HistogramAdjustmentAPI.jl +++ b/src/HistogramAdjustmentAPI/HistogramAdjustmentAPI.jl @@ -5,7 +5,7 @@ module HistogramAdjustmentAPI using ImageCore # ColorTypes is sufficient # TODO Relax this to all image color types -const GenericGrayImage = AbstractArray{<:Union{Number, AbstractGray}} +using ..ImageContrastAdjustment: GenericGrayImage """ AbstractImageAlgorithm diff --git a/src/ImageContrastAdjustment.jl b/src/ImageContrastAdjustment.jl index 101d2b0..c3bd7a4 100644 --- a/src/ImageContrastAdjustment.jl +++ b/src/ImageContrastAdjustment.jl @@ -1,20 +1,21 @@ module ImageContrastAdjustment using ImageCore +using ImageBase using ImageTransformations: imresize # Where possible we avoid a direct dependency to reduce the number of [compat] bounds using ImageCore.MappedArrays using Parameters: @with_kw # Same as Base.@kwdef but works on Julia 1.0 +# TODO Relax this to all image color types +const GenericGrayImage = AbstractArray{<:Union{Number, AbstractGray}} + # TODO: port HistogramAdjustmentAPI to ImagesAPI include("HistogramAdjustmentAPI/HistogramAdjustmentAPI.jl") import .HistogramAdjustmentAPI: AbstractHistogramAdjustmentAlgorithm, adjust_histogram, adjust_histogram! -# TODO Relax this to all image color types -const GenericGrayImage = AbstractArray{<:Union{Number, AbstractGray}} -include("core.jl") include("build_histogram.jl") include("algorithms/common.jl") include("algorithms/adaptive_equalization.jl") diff --git a/src/algorithms/common.jl b/src/algorithms/common.jl index 61ef046..ada7439 100644 --- a/src/algorithms/common.jl +++ b/src/algorithms/common.jl @@ -52,7 +52,7 @@ function construct_pdfs(img::GenericGrayImage, targetimg::AbstractArray, nbins:: if eltype(img) <: AbstractGray imin, imax = 0, 1 else - imin, imax = min(minfinite(img), minfinite(targetimg)), max(maxfinite(img), maxfinite(targetimg)) + imin, imax = min(minimum_finite(img), minimum_finite(targetimg)), max(maximum_finite(img), maximum_finite(targetimg)) end edges, histogram = build_histogram(img, nbins, minval = imin, maxval = imax) _, target_histogram = build_histogram(targetimg, edges) diff --git a/src/algorithms/linear_stretching.jl b/src/algorithms/linear_stretching.jl index dc482b0..9e54d25 100644 --- a/src/algorithms/linear_stretching.jl +++ b/src/algorithms/linear_stretching.jl @@ -146,7 +146,7 @@ end function (f::LinearStretching)(out::GenericGrayImage, img::GenericGrayImage) T = eltype(out) FT = eltype(floattype(T)) - img_min, img_max = minfinite(img), maxfinite(img) + img_min, img_max = minimum_finite(img), maximum_finite(img) # explicit annotation is needed because the ?: line mixes three value types: # Nothing, T, and typeof(f.src_minval) src_minval::FT = isnothing(f.src_minval) ? img_min : f.src_minval diff --git a/src/build_histogram.jl b/src/build_histogram.jl index ee577a3..6f679f6 100644 --- a/src/build_histogram.jl +++ b/src/build_histogram.jl @@ -147,8 +147,8 @@ edges, counts = build_histogram(r, 256, minval = 0, maxval = 1) [1] E. Herrholz, "Parsimonious Histograms," Ph.D. dissertation, Inst. of Math. and Comp. Sci., University of Greifswald, Greifswald, Germany, 2011. """ function build_histogram(img::GenericGrayImage, nbins::Integer = 256; - minval::Union{Real,AbstractGray}=minfinite(img), - maxval::Union{Real,AbstractGray}=maxfinite(img)) + minval::Union{Real,AbstractGray}=minimum_finite(img), + maxval::Union{Real,AbstractGray}=maximum_finite(img)) edges = partition_interval(nbins, minval, maxval) build_histogram(img, edges) end @@ -172,8 +172,8 @@ build_histogram(img::AbstractArray{C}) where C<:Color{N0f8} = build_histogram(mappedarray(Gray{N0f8}, img)) function build_histogram(img::AbstractArray{T}, nbins::Integer; - minval::Union{Real,AbstractGray}=minfinite(img), - maxval::Union{Real,AbstractGray}=maxfinite(img)) where T<:Union{N0f8, AbstractGray{N0f8}} + minval::Union{Real,AbstractGray}=minimum_finite(img), + maxval::Union{Real,AbstractGray}=maximum_finite(img)) where T<:Union{N0f8, AbstractGray{N0f8}} edgesraw, countsraw = build_histogram(img) return rebin(edgesraw, countsraw, nbins, minval, maxval) end diff --git a/src/core.jl b/src/core.jl deleted file mode 100644 index e65100f..0000000 --- a/src/core.jl +++ /dev/null @@ -1,74 +0,0 @@ -# These functions are taken from algorithms.jl in -# https://github.com/JuliaImages/Images.jl/. We don't want to depend on the -# entire Images package just for these functions hence I have just copied and -# pasted them here for now. However, as we are refactoring the Images.jl package -# we might want to move these (and some other functions) into a separate -# package. Once that is completed we can import that package and remove this -# file. - -""" -`m = minfinite(A)` calculates the minimum value in `A`, ignoring any values that are not finite (Inf or NaN). -""" -function minfinite(A::AbstractArray{T}) where T - ret = sentinel_min(T) - for a in A - ret = minfinite_scalar(a, ret) - end - ret -end -function minfinite(f, A::AbstractArray) - ret = sentinel_min(typeof(f(first(A)))) - for a in A - ret = minfinite_scalar(f(a), ret) - end - ret -end - -""" -`m = maxfinite(A)` calculates the maximum value in `A`, ignoring any values that are not finite (Inf or NaN). -""" -function maxfinite(A::AbstractArray{T}) where T - ret = sentinel_max(T) - for a in A - ret = maxfinite_scalar(a, ret) - end - ret -end -function maxfinite(f, A::AbstractArray) - ret = sentinel_max(typeof(f(first(A)))) - for a in A - ret = maxfinite_scalar(f(a), ret) - end - ret -end - -minfinite_scalar(a::T, b::T) where {T} = isfinite(a) ? (b < a ? b : a) : b -maxfinite_scalar(a::T, b::T) where {T} = isfinite(a) ? (b > a ? b : a) : b -minfinite_scalar(a::T, b::T) where {T<:Union{Integer,FixedPoint}} = b < a ? b : a -maxfinite_scalar(a::T, b::T) where {T<:Union{Integer,FixedPoint}} = b > a ? b : a -minfinite_scalar(a, b) = minfinite_scalar(promote(a, b)...) -maxfinite_scalar(a, b) = maxfinite_scalar(promote(a, b)...) - -function minfinite_scalar(c1::C, c2::C) where C<:AbstractRGB - C(minfinite_scalar(c1.r, c2.r), - minfinite_scalar(c1.g, c2.g), - minfinite_scalar(c1.b, c2.b)) -end -function maxfinite_scalar(c1::C, c2::C) where C<:AbstractRGB - C(maxfinite_scalar(c1.r, c2.r), - maxfinite_scalar(c1.g, c2.g), - maxfinite_scalar(c1.b, c2.b)) -end - -sentinel_min(::Type{T}) where {T<:Union{Integer,FixedPoint}} = typemax(T) -sentinel_max(::Type{T}) where {T<:Union{Integer,FixedPoint}} = typemin(T) -sentinel_min(::Type{T}) where {T<:AbstractFloat} = convert(T, NaN) -sentinel_max(::Type{T}) where {T<:AbstractFloat} = convert(T, NaN) -sentinel_min(::Type{C}) where {C<:AbstractRGB} = _sentinel_min(C, eltype(C)) -_sentinel_min(::Type{C},::Type{T}) where {C<:AbstractRGB,T} = (s = sentinel_min(T); C(s,s,s)) -sentinel_max(::Type{C}) where {C<:AbstractRGB} = _sentinel_max(C, eltype(C)) -_sentinel_max(::Type{C},::Type{T}) where {C<:AbstractRGB,T} = (s = sentinel_max(T); C(s,s,s)) -sentinel_min(::Type{C}) where {C<:AbstractGray} = _sentinel_min(C, eltype(C)) -_sentinel_min(::Type{C},::Type{T}) where {C<:AbstractGray,T} = C(sentinel_min(T)) -sentinel_max(::Type{C}) where {C<:AbstractGray} = _sentinel_max(C, eltype(C)) -_sentinel_max(::Type{C},::Type{T}) where {C<:AbstractGray,T} = C(sentinel_max(T)) diff --git a/test/core.jl b/test/core.jl index bed8a50..baa9767 100644 --- a/test/core.jl +++ b/test/core.jl @@ -1,20 +1,20 @@ @testset "Core" begin A = [NaN 1 2 3; NaN 6 5 4] - @test ImageContrastAdjustment.minfinite(A) == 1 - @test ImageContrastAdjustment.maxfinite(A) == 6 + @test ImageContrastAdjustment.minimum_finite(A) == 1 + @test ImageContrastAdjustment.maximum_finite(A) == 6 A = rand(10:20, 5, 5) - @test ImageContrastAdjustment.minfinite(A) == minimum(A) - @test ImageContrastAdjustment.maxfinite(A) == maximum(A) + @test ImageContrastAdjustment.minimum_finite(A) == minimum(A) + @test ImageContrastAdjustment.maximum_finite(A) == maximum(A) A = reinterpret(N0f8, rand(0x00:0xff, 5, 5)) - @test ImageContrastAdjustment.minfinite(A) == minimum(A) - @test ImageContrastAdjustment.maxfinite(A) == maximum(A) + @test ImageContrastAdjustment.minimum_finite(A) == minimum(A) + @test ImageContrastAdjustment.maximum_finite(A) == maximum(A) A = rand(Float32,3,5,5) img = colorview(RGB, A) - dc = ImageContrastAdjustment.minfinite(img)-RGB{Float32}(minimum(A, dims=(2,3))...) + dc = ImageContrastAdjustment.minimum_finite(img)-RGB{Float32}(minimum(A, dims=(2,3))...) @test norm(dc) < 1e-6 - dc = ImageContrastAdjustment.maxfinite(img)-RGB{Float32}(maximum(A, dims=(2,3))...) + dc = ImageContrastAdjustment.maximum_finite(img)-RGB{Float32}(maximum(A, dims=(2,3))...) @test norm(dc) < 1e-6 - @test ImageContrastAdjustment.minfinite(x->x^2,[NaN,10,2]) == 4 - @test ImageContrastAdjustment.maxfinite(x->x^2,[NaN,10,2]) == 100 + @test ImageContrastAdjustment.minimum_finite(x->x^2,[NaN,10,2]) == 4 + @test ImageContrastAdjustment.maximum_finite(x->x^2,[NaN,10,2]) == 100 end