diff --git a/src/core.jl b/src/core.jl index 31806b3..ecf7158 100644 --- a/src/core.jl +++ b/src/core.jl @@ -64,7 +64,7 @@ segment_mean(r::FuzzyCMeansResult) = Dict([(i, segment_mean(r,i)) for i in segme # Dispatch on show function show(io::IO, seg::SegmentedImage) - print(io, "Segmented Image with:\n\t labels map: ", summary(labels_map(seg)), "\n\t number of labels: ", length(segment_labels(seg))) + print(io, "Segmented Image with:\n labels map: ", summary(labels_map(seg)), "\n number of labels: ", length(segment_labels(seg))) end """ diff --git a/src/fast_scanning.jl b/src/fast_scanning.jl index f8dc8f0..9af13ae 100644 --- a/src/fast_scanning.jl +++ b/src/fast_scanning.jl @@ -40,11 +40,15 @@ Segments the N-D image using a fast scanning algorithm and returns a # Examples: -```jldoctest +```jldoctest; setup = :(using Images, ImageSegmentation) julia> img = zeros(Float64, (3,3)); -julia> img[2,:] = 0.5; -julia> img[:,2] = 0.6; + +julia> img[2,:] .= 0.5; + +julia> img[:,2] .= 0.6; + julia> seg = fast_scanning(img, 0.2); + julia> labels_map(seg) 3×3 Array{Int64,2}: 1 4 5 @@ -69,8 +73,9 @@ function fast_scanning(img::AbstractArray{CT,N}, threshold::Union{AbstractArray, neighbourhood(x) = ntuple(i-> x-half_region[i], Val(N)) # Required data structures + TM = meantype(CT) result = fill(-1, axes(img)) # Array to store labels - region_means = Dict{Int, Images.accum(CT)}() # A map conatining (label, mean) pairs + region_means = Dict{Int, TM}() # A map conatining (label, mean) pairs region_pix_count = Dict{Int, Int}() # A map conatining (label, count) pairs temp_labels = IntDisjointSets(0) # Disjoint set to map labels to their equivalence class v_neigh = MVector{N,Int}(undef) # MVector to store valid neighbours diff --git a/src/felzenszwalb.jl b/src/felzenszwalb.jl index 448ec14..9ccf6e6 100644 --- a/src/felzenszwalb.jl +++ b/src/felzenszwalb.jl @@ -80,8 +80,7 @@ function felzenszwalb(edges::Array{ImageEdge}, num_vertices::Int, k::Real, min_s return index_map, num_sets end -meantype(::Type{T}) where T = Images.accum(T) -meantype(::Type{T}) where T<:Integer = Float64 +meantype(::Type{T}) where T = typeof(zero(Images.accum(T))/2) function felzenszwalb(img::AbstractArray{T, 2}, k::Real, min_size::Int = 0) where T<:Union{Real,Color} diff --git a/src/meanshift.jl b/src/meanshift.jl index 368871b..c5e2e96 100644 --- a/src/meanshift.jl +++ b/src/meanshift.jl @@ -69,9 +69,10 @@ function meanshift(img::Array{CT, 2}, spatial_radius::Real, range_radius::Real; clusters = dbscan(modes, 1.414) num_segments = length(clusters) + TM = meantype(CT) result = similar(img, Int) labels = Array(1:num_segments) - region_means = Dict{Int, Images.accum(CT)}() + region_means = Dict{Int, TM}() region_pix_count = Dict{Int, Int}() cluster_idx = 0 @@ -81,7 +82,7 @@ function meanshift(img::Array{CT, 2}, spatial_radius::Real, range_radius::Real; for index in [cluster.core_indices; cluster.boundary_indices] i, j = (index-1)%rows + 1, floor(Int, (index-1)/rows) + 1 result[i, j] = cluster_idx - region_means[cluster_idx] = get(region_means, cluster_idx, zero(Images.accum(CT))) + img[i, j] + region_means[cluster_idx] = get(region_means, cluster_idx, zero(TM)) + img[i, j] end region_means[cluster_idx] /= region_pix_count[cluster_idx] end diff --git a/src/region_growing.jl b/src/region_growing.jl index f312357..24efb0d 100644 --- a/src/region_growing.jl +++ b/src/region_growing.jl @@ -30,18 +30,21 @@ and returns a [`SegmentedImage`](@ref) containing information about the segments # Examples -```jldoctest +```jldoctest; setup = :(using Images, ImageSegmentation) julia> img = zeros(Gray{N0f8},4,4); -julia> img[2:4,2:4] = 1; + +julia> img[2:4,2:4] .= 1; + julia> seeds = [(CartesianIndex(3,1),1),(CartesianIndex(2,2),2)]; + julia> seg = seeded_region_growing(img, seeds); + julia> labels_map(seg) 4×4 Array{Int64,2}: 1 1 1 1 1 2 2 2 1 2 2 2 1 2 2 2 - ``` # Citation: @@ -50,7 +53,7 @@ Albert Mehnert, Paul Jackaway (1997), "An improved seeded region growing algorit Pattern Recognition Letters 18 (1997), 1065-1071 """ function seeded_region_growing(img::AbstractArray{CT,N}, seeds::AbstractVector{Tuple{CartesianIndex{N},Int}}, - kernel_dim::Union{Vector{Int}, NTuple{N, Int}} = ntuple(i->3,N), diff_fn::Function = default_diff_fn) where {CT<:Colorant, N} + kernel_dim::Union{Vector{Int}, NTuple{N, Int}} = ntuple(i->3,N), diff_fn::Function = default_diff_fn) where {CT<:Union{Colorant,Real}, N} length(kernel_dim) == N || error("Dimension count of image and kernel_dim do not match") for dim in kernel_dim dim > 0 || error("Dimensions of the kernel must be positive") @@ -62,7 +65,7 @@ function seeded_region_growing(img::AbstractArray{CT,N}, seeds::AbstractVector{T end function seeded_region_growing(img::AbstractArray{CT,N}, seeds::AbstractVector{Tuple{CartesianIndex{N},Int}}, - neighbourhood::Function, diff_fn::Function = default_diff_fn) where {CT<:Colorant, N} + neighbourhood::Function, diff_fn::Function = default_diff_fn) where {CT<:Union{Colorant,Real}, N} _QUEUE_SZ = 10 @@ -71,6 +74,8 @@ function seeded_region_growing(img::AbstractArray{CT,N}, seeds::AbstractVector{T seed[2] > 0 || error("Seed labels need to be positive integers!") end + TM = meantype(CT) + # Required data structures result = fill(-1, axes(img)) # Array to store labels nhq = Queue{CartesianIndex{N}}(_QUEUE_SZ) # Neighbours holding queue @@ -78,7 +83,7 @@ function seeded_region_growing(img::AbstractArray{CT,N}, seeds::AbstractVector{T qdict = Dict{Float64, Queue{CartesianIndex{N}}}() # A map to get a reference to queue using the δ value labelsq = Queue{Int}(_QUEUE_SZ) # Queue to hold labels holdingq = Queue{CartesianIndex{N}}(_QUEUE_SZ) # Queue to hold points corresponding to the labels in `labelsq` - region_means = Dict{Int, Images.accum(CT)}() # A map containing (label, mean) pairs + region_means = Dict{Int, TM}() # A map containing (label, mean) pairs region_pix_count = Dict{Int, Int}() # A map containing (label, pixel_count) pairs labels = Vector{Int}() # A vector containing list of labels @@ -86,7 +91,7 @@ function seeded_region_growing(img::AbstractArray{CT,N}, seeds::AbstractVector{T for seed in seeds result[seed[1]] = seed[2] region_pix_count[seed[2]] = get(region_pix_count, seed[2], 0) + 1 - region_means[seed[2]] = get(region_means, seed[2], zero(Images.accum(CT))) + (img[seed[1]] - get(region_means, seed[2], zero(Images.accum(CT))))/(region_pix_count[seed[2]]) + region_means[seed[2]] = get(region_means, seed[2], zero(TM)) + (img[seed[1]] - get(region_means, seed[2], zero(TM)))/(region_pix_count[seed[2]]) if ! (seed[2] in labels) push!(labels, seed[2]) end @@ -230,17 +235,19 @@ and returns a [`SegmentedImage`](@ref) containing information about the segments # Examples -```jldoctest +```jldoctest; setup = :(using Images, ImageSegmentation) julia> img = zeros(Gray{N0f8},4,4); -julia> img[2:4,2:4] = 1; + +julia> img[2:4,2:4] .= 1; + julia> seg = unseeded_region_growing(img, 0.2); + julia> labels_map(seg) 4×4 Array{Int64,2}: 1 1 1 1 1 2 2 2 1 2 2 2 1 2 2 2 - ``` """ @@ -257,11 +264,12 @@ function unseeded_region_growing(img::AbstractArray{CT,N}, threshold::Real, end function unseeded_region_growing(img::AbstractArray{CT,N}, threshold::Real, neighbourhood::Function, diff_fn = default_diff_fn) where {CT<:Colorant,N} + TM = meantype(CT) # Required data structures result = fill(-1, axes(img)) # Array to store labels neighbours = PriorityQueue{CartesianIndex{N},Float64}() # Priority Queue containing boundary pixels with δ as the priority - region_means = Dict{Int, Images.accum(CT)}() # A map containing (label, mean) pairs + region_means = Dict{Int, TM}() # A map containing (label, mean) pairs region_pix_count = Dict{Int, Int}() # A map containing (label, pixel_count) pairs labels = Vector{Int}() # Vector containing assigned labels @@ -321,7 +329,7 @@ function unseeded_region_growing(img::AbstractArray{CT,N}, threshold::Real, neig #Update region_means region_pix_count[minlabel] = get(region_pix_count, minlabel, 0) + 1 - region_means[minlabel] = get(region_means, minlabel, zero(Images.accum(CT))) + (pixelval - get(region_means, minlabel, zero(Images.accum(CT))))/(region_pix_count[minlabel]) + region_means[minlabel] = get(region_means, minlabel, zero(TM)) + (pixelval - get(region_means, minlabel, zero(TM)))/(region_pix_count[minlabel]) # Enqueue neighbours of `point` for p in neighbourhood(point) diff --git a/src/region_merging.jl b/src/region_merging.jl index 6939bd9..e30cd6d 100644 --- a/src/region_merging.jl +++ b/src/region_merging.jl @@ -43,12 +43,16 @@ Returns true if `img` is homogeneous. # Examples -```jldoctest -# img is an array with elements of type `Float64` +```jldoctest; setup = :(using Images, ImageSegmentation) +julia> img = 0.1*rand(6, 6); + +julia> img[4:end, 4:end] .+= 10; + julia> function homogeneous(img) min, max = extrema(img) max - min < 0.2 end +homogeneous (generic function with 1 method) julia> t = region_tree(img, homogeneous); ``` @@ -97,12 +101,16 @@ Returns true if `img` is homogeneous. # Examples -```jldoctest -# img is an array with elements of type `Float64` +```jldoctest; setup = :(using Images, ImageSegmentation) +julia> img = 0.1*rand(6, 6); + +julia> img[4:end, 4:end] .+= 10; + julia> function homogeneous(img) min, max = extrema(img) max - min < 0.2 end +homogeneous (generic function with 1 method) julia> seg = region_splitting(img, homogeneous); ``` @@ -110,7 +118,7 @@ julia> seg = region_splitting(img, homogeneous); """ function region_splitting(img::AbstractArray{T,N}, homogeneous::Function) where T<:Union{Colorant, Real} where N rtree = region_tree(img, homogeneous) - seg = SegmentedImage(similar(img, Int), Vector{Int}(), Dict{Int, Images.accum(T)}(), Dict{Int, Int}()) + seg = SegmentedImage(similar(img, Int), Vector{Int}(), Dict{Int, meantype(T)}(), Dict{Int, Int}()) lc = 1 lc = fill_recursive!(seg, seg.image_indexmap, lc, rtree) seg diff --git a/src/watershed.jl b/src/watershed.jl index b2e6396..cc87a18 100644 --- a/src/watershed.jl +++ b/src/watershed.jl @@ -59,14 +59,15 @@ function watershed(img::AbstractArray{T, N}, markers::AbstractArray{S,N}) where end end + TM = meantype(T) num_segments = Int(maximum(segments)) labels = Array(1:num_segments) - region_means = Dict{Int, Images.accum(T)}() + region_means = Dict{Int, TM}() region_pix_count = Dict{Int, Int}() for i in R region_pix_count[segments[i]] = get(region_pix_count, segments[i], 0) + 1 - region_means[segments[i]] = get(region_means, segments[i], zero(Images.accum(T))) + (img[i] - get(region_means, segments[i], zero(Images.accum(T))))/region_pix_count[segments[i]] + region_means[segments[i]] = get(region_means, segments[i], zero(TM)) + (img[i] - get(region_means, segments[i], zero(TM)))/region_pix_count[segments[i]] end return SegmentedImage(segments, labels, region_means, region_pix_count) end diff --git a/test/felzenszwalb.jl b/test/felzenszwalb.jl index 226fad6..85ec9f6 100644 --- a/test/felzenszwalb.jl +++ b/test/felzenszwalb.jl @@ -14,10 +14,11 @@ @test all(label->(label==result.image_indexmap[2,2]), result.image_indexmap[2:3, 2:3]) @test all(label->(label==result.image_indexmap[5,5]), result.image_indexmap[5:6, 5:6]) - @test result.segment_means[result.image_indexmap[1,1]] == zero(Images.accum(T)) - @test result.segment_means[result.image_indexmap[2,2]] == Images.accum(T)(img[2,2]) - @test result.segment_means[result.image_indexmap[2,8]] == Images.accum(T)(img[2,8]) - @test result.segment_means[result.image_indexmap[5,5]] == Images.accum(T)(img[5,5]) + TM = ImageSegmentation.meantype(T) + @test result.segment_means[result.image_indexmap[1,1]] == zero(TM) + @test result.segment_means[result.image_indexmap[2,2]] == TM(img[2,2]) + @test result.segment_means[result.image_indexmap[2,8]] == TM(img[2,8]) + @test result.segment_means[result.image_indexmap[5,5]] == TM(img[5,5]) @test result.segment_pixel_count[result.image_indexmap[1,1]] == 91 @test result.segment_pixel_count[result.image_indexmap[2,2]] == 4 @@ -30,6 +31,7 @@ img[2, 8] = RGB(0.5,0.5,0.5) img[5:6, 5:6] .= RGB(0.8,0.8,0.8) T = RGB{Float64} + TM = ImageSegmentation.meantype(T) result = felzenszwalb(img, 1, 2) @@ -41,8 +43,8 @@ @test all(label->(label==result.image_indexmap[5,5]), result.image_indexmap[5:6, 5:6]) @test result.segment_means[result.image_indexmap[1,1]] ≈ RGB{Float64}(0.5/92, 0.5/92, 0.5/92) - @test result.segment_means[result.image_indexmap[2,2]] == Images.accum(T)(img[2,2]) - @test result.segment_means[result.image_indexmap[5,5]] == Images.accum(T)(img[5,5]) + @test result.segment_means[result.image_indexmap[2,2]] == TM(img[2,2]) + @test result.segment_means[result.image_indexmap[5,5]] == TM(img[5,5]) @test result.segment_pixel_count[result.image_indexmap[1,1]] == 92 @test result.segment_pixel_count[result.image_indexmap[2,2]] == 4 @@ -54,6 +56,7 @@ img[2, 8] = true img[5:6, 5:6] .= true T = Bool + TM = ImageSegmentation.meantype(T) result = felzenszwalb(img, 1, 2) @@ -65,8 +68,8 @@ @test all(label->(label==result.image_indexmap[5,5]), result.image_indexmap[5:6, 5:6]) @test result.segment_means[result.image_indexmap[1,1]] ≈ 1/92 - @test result.segment_means[result.image_indexmap[2,2]] == Images.accum(T)(img[2,2]) - @test result.segment_means[result.image_indexmap[5,5]] == Images.accum(T)(img[5,5]) + @test result.segment_means[result.image_indexmap[2,2]] == TM(img[2,2]) + @test result.segment_means[result.image_indexmap[5,5]] == TM(img[5,5]) @test result.segment_pixel_count[result.image_indexmap[1,1]] == 92 @test result.segment_pixel_count[result.image_indexmap[2,2]] == 4 diff --git a/test/meanshift.jl b/test/meanshift.jl index 3e811cc..8ebc5d9 100644 --- a/test/meanshift.jl +++ b/test/meanshift.jl @@ -5,6 +5,7 @@ img[2, 8] = 0.5 img[5:6, 5:6] .= 0.8 T = Gray{N0f8} + TM = ImageSegmentation.meantype(T) result = meanshift(img, 8, 7/255) @@ -14,10 +15,10 @@ @test all(label->(label==result.image_indexmap[2,2]), result.image_indexmap[2:3, 2:3]) @test all(label->(label==result.image_indexmap[5,5]), result.image_indexmap[5:6, 5:6]) - @test result.segment_means[result.image_indexmap[1,1]] == zero(Images.accum(T)) - @test result.segment_means[result.image_indexmap[2,2]] == Images.accum(T)(img[2,2]) - @test result.segment_means[result.image_indexmap[2,8]] == Images.accum(T)(img[2,8]) - @test result.segment_means[result.image_indexmap[5,5]] == Images.accum(T)(img[5,5]) + @test result.segment_means[result.image_indexmap[1,1]] == zero(TM) + @test result.segment_means[result.image_indexmap[2,2]] == TM(img[2,2]) + @test result.segment_means[result.image_indexmap[2,8]] == TM(img[2,8]) + @test result.segment_means[result.image_indexmap[5,5]] == TM(img[5,5]) @test result.segment_pixel_count[result.image_indexmap[1,1]] == 91 @test result.segment_pixel_count[result.image_indexmap[2,2]] == 4 diff --git a/test/region_growing.jl b/test/region_growing.jl index e9b2d61..d74c491 100644 --- a/test/region_growing.jl +++ b/test/region_growing.jl @@ -1,4 +1,4 @@ -of_accum_type(p::Colorant) = Images.accum(typeof(p))(p) +of_mean_type(p::Colorant) = ImageSegmentation.meantype(typeof(p))(p) @testset "Seeded Region Growing" begin # 2-D image @@ -11,7 +11,7 @@ of_accum_type(p::Colorant) = Images.accum(typeof(p))(p) expected[6:10,4:8] .= 3 expected[3:7,2:6] .= 2 expected_labels = [1,2,3] - expected_means = Dict(1 => of_accum_type(img[3,9]), 2 => of_accum_type(img[5,2]), 3 => of_accum_type(img[9,7])) + expected_means = Dict(1 => of_mean_type(img[3,9]), 2 => of_mean_type(img[5,2]), 3 => of_mean_type(img[9,7])) expected_count = Dict(1 => 56, 2 => 25, 3 => 19) seg = seeded_region_growing(img, seeds) @@ -73,6 +73,25 @@ of_accum_type(p::Colorant) = Images.accum(typeof(p))(p) @test expected_means == seg.segment_means @test seg.image_indexmap == expected + # element-type and InexactError + img = zeros(Int, 4, 4) + img[2:end, 2:end] .= 10 + img[end,end] = 11 + seeds = [(CartesianIndex(1,1), 1), (CartesianIndex(2,2), 2)] + + expected = ones(Int, size(img)) + expected[2:end,2:end] .= 2 + expected_labels = [1,2] + expected_means = Dict(1=>0.0, 2=>91/9) + expected_count = Dict(1=>7, 2=>9) + + seg = seeded_region_growing(img, seeds, (3,3), (v1,v2)->(δ = abs(v1-v2); return δ > 1 ? δ : zero(δ))) + @test all(label->(label in expected_labels), seg.segment_labels) + @test all(label->(label in seg.segment_labels), expected_labels) + @test expected_count == seg.segment_pixel_count + @test expected_means == seg.segment_means + @test seg.image_indexmap == expected + # 3-d image img = zeros(RGB{N0f8},(9,9,9)) img[3:7,3:7,3:7] .= RGB{N0f8}(0.5,0.5,0.5) @@ -83,7 +102,7 @@ of_accum_type(p::Colorant) = Images.accum(typeof(p))(p) expected[3:7,3:7,3:7] .= 2 expected[2:5,5:9,4:6] .= 3 expected_labels = [1,2,3] - expected_means = Dict([(i, of_accum_type(img[seeds[i][1]])) for i in 1:3]) + expected_means = Dict([(i, of_mean_type(img[seeds[i][1]])) for i in 1:3]) expected_count = Dict(1=>571, 2=>98, 3=>60) seg = seeded_region_growing(img, seeds) @@ -119,7 +138,7 @@ of_accum_type(p::Colorant) = Images.accum(typeof(p))(p) expected_means = Dict(1=>RGB{Float64}(0.4,1.0,0.0), 2=>RGB{Float64}(0.0,0.0,0.0)) expected_count = Dict(0=>3, 1=>3, 2=>3) - seg = seeded_region_growing(img, seeds, [3,3], (c1,c2)->abs(of_accum_type(c1).r - of_accum_type(c2).r)) + seg = seeded_region_growing(img, seeds, [3,3], (c1,c2)->abs(of_mean_type(c1).r - of_mean_type(c2).r)) @test all(label->(label in expected_labels), seg.segment_labels) @test all(label->(label in seg.segment_labels), expected_labels) @test expected_count == seg.segment_pixel_count @@ -137,7 +156,7 @@ end expected[6:10,4:8] .= 2 expected[3:7,2:6] .= 3 expected_labels = [1,2,3] - expected_means = Dict(1 => of_accum_type(img[3,9]), 3 => of_accum_type(img[5,2]), 2 => of_accum_type(img[9,7])) + expected_means = Dict(1 => of_mean_type(img[3,9]), 3 => of_mean_type(img[5,2]), 2 => of_mean_type(img[9,7])) expected_count = Dict(1 => 56, 3 => 25, 2 => 19) seg = unseeded_region_growing(img, 0.2) @@ -220,7 +239,7 @@ end expected[3:7,3:7,3:7] .= 2 expected[2:5,5:9,4:6] .= 3 expected_labels = [1,2,3] - expected_means = Dict(1=>of_accum_type(img[1,1,1]), 2=>of_accum_type(img[3,3,3]), 3=>of_accum_type(img[2,5,4])) + expected_means = Dict(1=>of_mean_type(img[1,1,1]), 2=>of_mean_type(img[3,3,3]), 3=>of_mean_type(img[2,5,4])) expected_count = Dict(1=>571, 2=>98, 3=>60) seg = unseeded_region_growing(img, 0.2) @@ -239,10 +258,10 @@ end expected[1:3,2] .= 2 expected[1:3,3] .= 3 expected_labels = [1,2,3] - expected_means = Dict(1=>of_accum_type(img[1,1]), 3=>of_accum_type(img[1,3]), 2=>of_accum_type(img[1,2])) + expected_means = Dict(1=>of_mean_type(img[1,1]), 3=>of_mean_type(img[1,3]), 2=>of_mean_type(img[1,2])) expected_count = Dict(1=>3, 2=>3, 3=>3) - seg = unseeded_region_growing(img, 0.2, [3,3], (c1,c2)->abs(of_accum_type(c1).r - of_accum_type(c2).r)) + seg = unseeded_region_growing(img, 0.2, [3,3], (c1,c2)->abs(of_mean_type(c1).r - of_mean_type(c2).r)) @test all(label->(label in expected_labels), seg.segment_labels) @test all(label->(label in seg.segment_labels), expected_labels) @test expected_count == seg.segment_pixel_count