Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor Moments using the functor API #37

Merged
merged 1 commit into from
Jul 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 22 additions & 18 deletions src/moments.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
struct Moments <: AbstractImageBinarizationAlgorithm end


@doc raw"""
```
binarize(Moments(), img)
```
Moments <: AbstractImageBinarizationAlgorithm
Moments()

binarize([T,] img, f::Moments)
binarize!([out,] img, f::Moments)

The following rule determines the binarization threshold: if one assigns all
observations below the threshold to a value z₀ and all observations above the
Expand All @@ -13,7 +12,8 @@ must match the moments of this specially constructed bilevel histogram.

# Output

Returns the binarized image as an `Array{Gray{Bool},2}`.
Return the binarized image as an `Array{Gray{T}}` of size `size(img)`. If
`T` is not specified, it is inferred from `out` and `img`.

# Details

Expand Down Expand Up @@ -57,9 +57,9 @@ the four equations to obtain ``q_0`` and ``q_1``, and then chooses the threshold

The function argument is described in more detail below.

## `img`
## `img::AbstractArray`

An `AbstractArray` representing an image. The image is automatically converted
The image that needs to be binarized. The image is automatically converted
to `Gray` in order to construct the requisite graylevel histogram.


Expand All @@ -71,19 +71,23 @@ Binarize the "cameraman" image in the `TestImages` package.
using TestImages, ImageBinarization

img = testimage("cameraman")
img_binary = binarize(Moments(), img)
img_binary = binarize(img, Moments())
```

# Reference

[1] W.-H. Tsai, “Moment-preserving thresolding: A new approach,” Computer Vision, Graphics, and Image Processing, vol. 29, no. 3, pp. 377–393, Mar. 1985. [doi:10.1016/0734-189x(85)90133-1](https://doi.org/10.1016/0734-189x%2885%2990133-1)
"""
function binarize(algorithm::Moments, img::AbstractArray{T,2}) where T <: Colorant
img₀₁ = zeros(Gray{Bool}, axes(img))
edges, counts = build_histogram(img, 256)
t = find_threshold(HistogramThresholding.Moments(), counts[1:end], edges)
for i in CartesianIndices(img)
img₀₁[i] = img[i] < t ? 0 : 1
end
img₀₁
struct Moments <: AbstractImageBinarizationAlgorithm end

function (f::Moments)(out::GenericGrayImage, img::GenericGrayImage)
edges, counts = build_histogram(img, 256)
t = find_threshold(HistogramThresholding.Moments(), counts[1:end], edges)
for i in CartesianIndices(img)
out[i] = img[i] < t ? 0 : 1
end
out
end

(f::Moments)(out::GenericGrayImage, img::AbstractArray{<:Color3}) =
f(out, of_eltype(Gray, img))
Binary file added test/References/Moments_Color3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test/References/Moments_Gray.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 55 additions & 9 deletions test/moments.jl
Original file line number Diff line number Diff line change
@@ -1,23 +1,69 @@
@testset "moments" begin
original_image = testimage("lena")
for T in (Gray{N0f8}, Gray{N0f16}, Gray{Float32}, Gray{Float64})
img = T.(original_image)
img₀₁ = binarize(Moments(), img)
@info "Test: Moments"

# Check original image is unchanged.
@test img == T.(testimage("lena"))
@testset "API" begin
img_gray = imresize(testimage("lena_gray_256"); ratio=0.25)
img = copy(img_gray)

# binarize
f = Moments()
binarized_img_1 = binarize(img, f)
@test img == img_gray # img unchanged
@test eltype(binarized_img_1) == Gray{N0f8}

binarized_img_2 = binarize(Gray{Bool}, img, f)
@test img == img_gray # img unchanged
@test eltype(binarized_img_2) == Gray{Bool}

binarized_img_3 = similar(img, Bool)
binarize!(binarized_img_3, img, f)
@test img == img_gray # img unchanged
@test eltype(binarized_img_3) == Bool

binarized_img_4 = copy(img_gray)
binarize!(binarized_img_4, f)
@test eltype(binarized_img_4) == Gray{N0f8}

@test binarized_img_1 == binarized_img_2
@test binarized_img_1 == binarized_img_3
@test binarized_img_1 == binarized_img_4
end

@testset "Types" begin
# Gray
img_gray = imresize(testimage("lena_gray_256"); ratio=0.25)
f = Moments()

type_list = generate_test_types([Float32, N0f8], [Gray])
for T in type_list
img = T.(img_gray)
@test_reference "References/Moments_Gray.png" Gray.(binarize(img, f))
end

# Color3
img_color = imresize(testimage("lena_color_256"); ratio=0.25)
f = Moments()

type_list = generate_test_types([Float32, N0f8], [RGB, Lab])
for T in type_list
img = T.(img_gray)
@test_reference "References/Moments_Color3.png" Gray.(binarize(img, f))
end
end

@testset "Numerical" begin
# Check that the image only has ones or zeros.
img = imresize(testimage("lena_gray_256"); ratio=0.25)
f = Moments()
img₀₁ = binarize(img, f)
non_zeros = findall(x -> x != 0.0 && x != 1.0, img₀₁)
@test length(non_zeros) == 0

# Check type of binarized image.
@test typeof(img₀₁) == Array{Gray{Bool},2}

# Check that ones and zeros have been assigned to the correct side of the threshold.
maxval, maxpos = findmax(Gray.(img))
@test img₀₁[maxpos] == 1
minval, minpos = findmin(Gray.(img))
@test img₀₁[minpos] == 0
end

end
4 changes: 2 additions & 2 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ include("testutils.jl")

@testset "ImageBinarization.jl" begin
include("util.jl")

include("adaptive_threshold.jl")
# include("balanced.jl")
# include("entropy.jl")
# include("intermodes.jl")
# include("minimum.jl")
# include("minimum_error.jl")
# include("moments.jl")
include("moments.jl")
# include("niblack.jl")
# include("otsu.jl")
# include("polysegment.jl")
Expand Down