Skip to content

Commit

Permalink
Fix and test clustering
Browse files Browse the repository at this point in the history
This has one breaking change: while the docs advertise that
"All the segmentation algorithms (except Fuzzy C-means) return a
struct SegmentedImage," that's not been true for `kmeans`.
This changes the output so that `kmeans` does return a `SegmentedImage`.
This is a breaking change, because the types are not interchangeable.
This became apparent when trying to add tests for `kmeans`, which
have been lacking.

Before releasing this, we may want to make a second breaking change:
currently, ImageSegmentation provides a new meaning for
`Matrix{Gray{T}}` than Clustering.jl provides for `Matrix{T}`.
This breaks our abstraction that `Gray ≈ Number`.
A way to fix that would be to have `ImageSegmentation.kmeans`
be a different function from `Clustering.kmeans`, and obviously
have the one in ImageSegmentation call the one in Clustering.
  • Loading branch information
timholy committed Aug 31, 2021
1 parent 55c1310 commit 9fc02d6
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 8 deletions.
3 changes: 2 additions & 1 deletion src/ImageSegmentation.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import Base: show

using LinearAlgebra, Statistics
using DataStructures, StaticArrays, ImageCore, ImageFiltering, ImageMorphology, LightGraphs, SimpleWeightedGraphs, RegionTrees, Distances, StaticArrays, Clustering, MetaGraphs
using ImageCore.MappedArrays: of_eltype
import Clustering: kmeans, fuzzy_cmeans

include("compat.jl")
Expand Down Expand Up @@ -41,7 +42,7 @@ export
kmeans,
fuzzy_cmeans,
merge_segments,

# types
SegmentedImage,
ImageEdge
Expand Down
10 changes: 4 additions & 6 deletions src/clustering.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
function img_to_data(img::AbstractArray{T,N}) where T<:Colorant where N
AT = accum_type(T)
aimg = AT.(img)
pimg = parent(aimg)
ch = channelview(pimg)
data = reshape(ch, :, *(size(pimg)...))
aimg = of_eltype(accum_type(T), img)
ch = channelview(aimg)
return reshape(ch, :, *(size(img)...))
end

kmeans(img::AbstractArray{T,N}, args...; kwargs...) where {T<:Colorant,N} =
kmeans(img_to_data(img), args...; kwargs...)
SegmentedImage(kmeans(img_to_data(img), args...; kwargs...), img)

fuzzy_cmeans(img::AbstractArray{T,N}, args...; kwargs...) where {T<:Colorant,N} =
fuzzy_cmeans(img_to_data(img), args...; kwargs...)
7 changes: 7 additions & 0 deletions src/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ struct SegmentedImage{T<:AbstractArray,U<:Union{Colorant,Real}}
segment_pixel_count::Dict{Int,Int}
end

SegmentedImage(r::Clustering.KmeansResult, img) =
SegmentedImage(reshape(r.assignments, axes(img)),
collect(1:length(r.counts)),
Dict(zip(1:length(r.counts), colorview(ImageCore.ColorTypes.base_colorant_type(eltype(img)), r.centers))),
Dict(zip(1:length(r.counts), r.counts))
)

"""
edge = ImageEdge(index1, index2, weight)
Expand Down
17 changes: 17 additions & 0 deletions test/clustering.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
@testset "Clustering" begin
# Test the example in the docs
path = download("https://github.com/JuliaImages/juliaimages.github.io/raw/source/docs/src/pkgs/segmentation/assets/flower.jpg")
img = load(path)
r = fuzzy_cmeans(img, 3, 2)
@test size(r.centers) == (3,3)
@test size(r.weights, 1) == length(img)
@test all((1), sum(r.weights; dims=2))
cmin, cmax = extrema(sum(r.weights; dims=1))
@test cmax < 3*cmin

# Also with kmeans
r = kmeans(img, 3)
nc = last.(sort([pr for pr in segment_pixel_count(r)]; by=first))
@test sum(nc) == length(img)
@test 3*minimum(nc) > maximum(nc)
end
3 changes: 2 additions & 1 deletion test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
using ImageSegmentation, Images, Test, SimpleWeightedGraphs, LightGraphs, StaticArrays, DataStructures
using RegionTrees: isleaf, Cell, split!
using RegionTrees: isleaf, Cell, split!
using MetaGraphs: MetaGraph, clear_props!, get_prop, has_prop, set_prop!, props, vertices

using Documenter
Expand All @@ -12,4 +12,5 @@ include("fast_scanning.jl")
include("watershed.jl")
include("region_merging.jl")
include("meanshift.jl")
include("clustering.jl")
include("merge_segments.jl")

0 comments on commit 9fc02d6

Please sign in to comment.