Skip to content

Commit f6cbc8e

Browse files
authored
Merge pull request #73 from JuliaImages/teh/polish
Lots of polish
2 parents 55c1310 + 3acf250 commit f6cbc8e

File tree

10 files changed

+163
-154
lines changed

10 files changed

+163
-154
lines changed

src/ImageSegmentation.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ using LinearAlgebra, Statistics
66
using DataStructures, StaticArrays, ImageCore, ImageFiltering, ImageMorphology, LightGraphs, SimpleWeightedGraphs, RegionTrees, Distances, StaticArrays, Clustering, MetaGraphs
77
import Clustering: kmeans, fuzzy_cmeans
88

9+
const PairOrTuple{K,V} = Union{Pair{K,V},Tuple{K,V}}
10+
911
include("compat.jl")
1012
include("core.jl")
1113
include("region_growing.jl")

src/compat.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
if VERSION <= v"1.0.5"
1+
@static if Base.VERSION <= v"1.0.5"
22
# https://github.com/JuliaLang/julia/pull/29442
33
_oneunit(::CartesianIndex{N}) where {N} = _oneunit(CartesianIndex{N})
44
_oneunit(::Type{CartesianIndex{N}}) where {N} = CartesianIndex(ntuple(x -> 1, Val(N)))
55
else
6-
_oneunit = Base.oneunit
6+
const _oneunit = Base.oneunit
77
end

src/core.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ function region_adjacency_graph(s::SegmentedImage, weight_fn::Function)
9595
while !isempty(t)
9696
temp = pop!(t)
9797
visited[temp] = true
98-
for J in CartesianIndices(_colon(max(Ibegin, temp-I1), min(Iend, temp+I1)))
98+
for J in _colon(max(Ibegin, temp-I1), min(Iend, temp+I1))
9999
if s.image_indexmap[temp] != s.image_indexmap[J]
100100
push!(n,s.image_indexmap[J])
101101
elseif !visited[J]
@@ -268,7 +268,7 @@ end
268268

269269
# Once Base has colon defined here we can replace this
270270
_colon(I::CartesianIndex{N}, J::CartesianIndex{N}) where N =
271-
map((i,j) -> i:j, Tuple(I), Tuple(J))
271+
CartesianIndices(map((i,j) -> i:j, Tuple(I), Tuple(J)))
272272

273273

274274
"""

src/fast_scanning.jl

Lines changed: 3 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -23,46 +23,10 @@ getscalar(a::Real, i...) = a
2323
fast_scanning(img::AbstractArray{CT,N}, block::NTuple{N,Int} = ntuple(i->4,Val(N))) where {CT<:ImageCore.NumberLike,N} =
2424
fast_scanning(img, adaptive_thres(img, block))
2525

26-
"""
27-
seg_img = fast_scanning(img, threshold, [diff_fn])
28-
29-
Segments the N-D image using a fast scanning algorithm and returns a
30-
[`SegmentedImage`](@ref) containing information about the segments.
31-
32-
# Arguments:
33-
* `img` : N-D image to be segmented (arbitrary axes are allowed)
34-
* `threshold` : Upper bound of the difference measure (δ) for considering
35-
pixel into same segment; an `AbstractArray` can be passed
36-
having same number of dimensions as that of `img` for adaptive
37-
thresholding
38-
* `diff_fn` : (Optional) Function that returns a difference measure (δ)
39-
between the mean color of a region and color of a point
40-
41-
# Examples:
42-
43-
```jldoctest; setup = :(using ImageCore, ImageMorphology, ImageSegmentation)
44-
julia> img = zeros(Float64, (3,3));
45-
46-
julia> img[2,:] .= 0.5;
47-
48-
julia> img[:,2] .= 0.6;
49-
50-
julia> seg = fast_scanning(img, 0.2);
51-
52-
julia> labels_map(seg)
53-
3×3 $(Matrix{Int}):
54-
1 4 5
55-
4 4 4
56-
3 4 6
57-
```
58-
59-
# Citation:
60-
61-
Jian-Jiun Ding, Cheng-Jin Kuo, Wen-Chih Hong,
62-
"An efficient image segmentation technique by fast scanning and adaptive merging"
63-
"""
64-
function fast_scanning(img::AbstractArray{CT,N}, threshold::Union{AbstractArray,Real}, diff_fn::Function = default_diff_fn) where {CT<:Union{Colorant,Real},N}
26+
fast_scanning(img::AbstractArray{CT,N}, threshold::Union{AbstractArray,Real}, diff_fn::Function = default_diff_fn) where {CT<:Union{Colorant,Real},N} =
27+
fast_scanning!(fill(-1, axes(img)), img, threshold, diff_fn)
6528

29+
function fast_scanning!(result, img::AbstractArray{CT,N}, threshold::Union{AbstractArray,Real}, diff_fn::DF = default_diff_fn) where {CT<:Union{Colorant,Real},N,DF<:Function}
6630
if threshold isa AbstractArray
6731
ndims(img) == ndims(threshold) || error("Dimension count of image and threshold do not match")
6832
end
@@ -74,7 +38,6 @@ function fast_scanning(img::AbstractArray{CT,N}, threshold::Union{AbstractArray,
7438

7539
# Required data structures
7640
TM = meantype(CT)
77-
result = fill(-1, axes(img)) # Array to store labels
7841
region_means = Dict{Int, TM}() # A map conatining (label, mean) pairs
7942
region_pix_count = Dict{Int, Int}() # A map conatining (label, count) pairs
8043
temp_labels = IntDisjointSets(0) # Disjoint set to map labels to their equivalence class

src/felzenszwalb.jl

Lines changed: 49 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,17 @@
11
"""
2-
```
3-
segments = felzenszwalb(img, k, [min_size])
4-
index_map, num_segments = felzenszwalb(edges, num_vertices, k, [min_size])
5-
```
2+
index_map, num_segments = felzenszwalb(edges, num_vertices, k, min_size=0)
63
7-
Segments an image using Felzenszwalb's graph-based algorithm. The function can be used in either of two ways -
8-
9-
1. `segments = felzenszwalb(img, k, [min_size])`
10-
11-
Segments an image using Felzenszwalb's segmentation algorithm and returns the result as `SegmentedImage`. The algorithm uses
12-
euclidean distance in color space as edge weights for the region adjacency graph.
13-
14-
Parameters:
15-
- img = input image
16-
- k = Threshold for region merging step. Larger threshold will result in bigger segments.
17-
- min_size = Minimum segment size
18-
19-
2. `index_map, num_segments = felzenszwalb(edges, num_vertices, k, [min_size])`
20-
21-
Segments an image represented as Region Adjacency Graph(RAG) using Felzenszwalb's segmentation algorithm. Each pixel/region
22-
corresponds to a node in the graph and weights on each edge measure the dissimilarity between pixels.
4+
Segment an image represented as Region Adjacency Graph(RAG) using Felzenszwalb's segmentation algorithm. Each pixel/region
5+
corresponds to a node in the graph and weights on each edge measure the dissimilarity between pixels.
236
The function returns the number of segments and index mapping from nodes of the RAG to segments.
247
258
Parameters:
26-
- edges = Array of edges in RAG. Each edge is represented as `ImageEdge`.
27-
- num_vertices = Number of vertices in RAG
28-
- k = Threshold for region merging step. Larger threshold will result in bigger segments.
29-
- min_size = Minimum segment size
30-
31-
9+
- `edges`: Array of edges in RAG. Each edge is represented as `ImageEdge`.
10+
- `num_vertices`: Number of vertices in RAG
11+
- `k`: Threshold for region merging step. Larger threshold will result in bigger segments.
12+
- `min_size`: Minimum segment size (in # pixels)
3213
"""
33-
function felzenszwalb(edges::Array{ImageEdge}, num_vertices::Int, k::Real, min_size::Int = 0)
14+
function felzenszwalb(edges::Array{ImageEdge}, num_vertices::Int, k::Float64, min_size::Int = 0)
3415

3516
num_edges = length(edges)
3617
G = IntDisjointSets(num_vertices)
@@ -61,7 +42,7 @@ function felzenszwalb(edges::Array{ImageEdge}, num_vertices::Int, k::Real, min_s
6142
end
6243
end
6344

64-
segments = OrderedSet()
45+
segments = OrderedSet{Int}()
6546
for i in 1:num_vertices
6647
push!(segments, find_root!(G, i))
6748
end
@@ -72,35 +53,59 @@ function felzenszwalb(edges::Array{ImageEdge}, num_vertices::Int, k::Real, min_s
7253
segments2index[s] = i
7354
end
7455

75-
index_map = Array{Int}(undef, num_vertices)
56+
index_map = Vector{Int}(undef, num_vertices)
7657
for i in 1:num_vertices
7758
index_map[i] = segments2index[find_root!(G, i)]
7859
end
7960

8061
return index_map, num_sets
8162
end
63+
felzenszwalb(edges::Array{ImageEdge}, num_vertices::Integer, k::Real, min_size::Integer = 0) =
64+
felzenszwalb(edges, convert(Int, num_vertices)::Int, convert(Float64, k)::Float64, convert(Int, min_size)::Int)
8265

8366
meantype(::Type{T}) where T = typeof(zero(accum_type(T))/2)
8467

85-
function felzenszwalb(img::AbstractArray{T, 2}, k::Real, min_size::Int = 0) where T<:Union{Real,Color}
68+
"""
69+
segments = felzenszwalb(img, k, [min_size])
70+
71+
Segment an image using Felzenszwalb's segmentation algorithm and returns the result as `SegmentedImage`.
72+
The algorithm uses euclidean distance in color space as edge weights for the region adjacency graph.
73+
74+
Parameters:
75+
- `img`: input image
76+
- `k`: Threshold for region merging step. Larger threshold will result in bigger segments.
77+
- `min_size`: Minimum segment size (in # pixels)
78+
"""
79+
function felzenszwalb(img::AbstractArray{T}, k::Real, min_size::Int = 0) where T<:Union{Real,Color}
80+
81+
sz = size(img)
82+
num_vertices = prod(sz)
8683

87-
rows, cols = size(img)
88-
num_vertices = rows*cols
89-
num_edges = 4*rows*cols - 3*rows - 3*cols + 2
90-
edges = Array{ImageEdge}(undef, num_edges)
84+
R = CartesianIndices(img)
85+
L = LinearIndices(img)
86+
Ibegin, Iend = first(R), last(R)
87+
I1 = _oneunit(Ibegin)
9188

92-
R = CartesianIndices(size(img))
93-
I1, Iend = first(R), last(R)
94-
num = 1
89+
# Compute the number of entries per pixel (other than at the image edges)
90+
num_edges = 0
91+
for I in _colon(-I1, I1)
92+
I >= zero(I1) && continue
93+
num_edges += 1
94+
end
95+
num_edges *= num_vertices # now the number for the whole image
96+
edges = Vector{ImageEdge}(undef, num_edges)
97+
98+
num = 0
9599
for I in R
96-
for J in CartesianIndices(_colon(max(I1, I-I1), min(Iend, I+I1)))
100+
imgI = img[I]
101+
for J in _colon(max(Ibegin, I-I1), min(Iend, I+I1))
97102
if I >= J
98103
continue
99104
end
100-
edges[num] = ImageEdge((I[2]-1)*rows+I[1], (J[2]-1)*rows+J[1], sqrt(sum(abs2,(img[I])-meantype(T)(img[J]))))
101-
num += 1
105+
edges[num+=1] = ImageEdge(L[I], L[J], sqrt(abs2(imgI-meantype(T)(img[J]))))
102106
end
103107
end
108+
deleteat!(edges, num+1:num_edges) # compensate for the ones we were missing at the image edges
104109

105110
index_map, num_segments = felzenszwalb(edges, num_vertices, k, min_size)
106111

@@ -109,12 +114,10 @@ function felzenszwalb(img::AbstractArray{T, 2}, k::Real, min_size::Int = 0) wher
109114
region_means = Dict{Int, meantype(T)}()
110115
region_pix_count = Dict{Int, Int}()
111116

112-
for j in axes(img, 2)
113-
for i in axes(img, 1)
114-
result[i, j] = index_map[(j-1)*rows+i]
115-
region_pix_count[result[i,j]] = get(region_pix_count, result[i, j], 0) + 1
116-
region_means[result[i,j]] = get(region_means, result[i,j], zero(meantype(T))) + (img[i, j] - get(region_means, result[i,j], zero(meantype(T))))/region_pix_count[result[i,j]]
117-
end
117+
for I in R
118+
result[I] = index_map[L[I]]
119+
region_pix_count[result[I]] = get(region_pix_count, result[I], 0) + 1
120+
region_means[result[I]] = get(region_means, result[I], zero(meantype(T))) + (img[I] - get(region_means, result[I], zero(meantype(T))))/region_pix_count[result[I]]
118121
end
119122

120123
return SegmentedImage(result, labels, region_means, region_pix_count)

src/meanshift.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ function meanshift(img::Array{CT, 2}, spatial_radius::Real, range_radius::Real;
4141
I1, Iend = first(R), last(R)
4242
I = CartesianIndex(rowbin(pt[1]), colbin(pt[2]), colorbin(pt[3]))
4343

44-
for J in CartesianIndices(_colon(max(I1, I-_oneunit(I)), min(Iend, I+_oneunit(I))))
44+
for J in _colon(max(I1, I-_oneunit(I)), min(Iend, I+_oneunit(I)))
4545
for point in buckets[J]
4646
if dist2(pt, SVector(point[1], point[2], img[point])) <= 1
4747
num += SVector(point[1], point[2], img[point])

0 commit comments

Comments
 (0)