Skip to content

Commit

Permalink
Merge pull request #29 from JuliaImages/teh/v0.7
Browse files Browse the repository at this point in the history
Get package working on 0.7
  • Loading branch information
timholy authored Sep 29, 2018
2 parents 6d23970 + fcdda88 commit 884af7c
Show file tree
Hide file tree
Showing 19 changed files with 195 additions and 192 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ os:
- linux
- osx
julia:
- 0.6
- 0.7
- 1.0
- nightly
notifications:
email: false
Expand Down
2 changes: 1 addition & 1 deletion REQUIRE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
julia 0.6
julia 0.7
Images 0.9
ImageFiltering 0.1.3
DataStructures 0.7.1
Expand Down
42 changes: 23 additions & 19 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
environment:
matrix:
- JULIAVERSION: "julialang/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe"
- JULIAVERSION: "julialang/bin/winnt/x64/0.6/julia-0.6-latest-win64.exe"
- JULIAVERSION: "julianightlies/bin/winnt/x86/julia-latest-win32.exe"
- JULIAVERSION: "julianightlies/bin/winnt/x64/julia-latest-win64.exe"
- julia_version: 0.7
- julia_version: 1
- julia_version: nightly

platform:
- x86 # 32-bit
- x64 # 64-bit

# # Uncomment the following lines to allow failures on nightly julia
# # (tests will run but not make your overall status red)
# matrix:
# allow_failures:
# - julia_version: nightly

branches:
only:
Expand All @@ -17,23 +26,18 @@ notifications:
on_build_status_changed: false

install:
# Download most recent Julia Windows binary
- ps: (new-object net.webclient).DownloadFile(
$("http://s3.amazonaws.com/"+$env:JULIAVERSION),
"C:\projects\julia-binary.exe")
# Run installer silently, output to C:\projects\julia
- C:\projects\julia-binary.exe /S /D=C:\projects\julia
- ps: iex ((new-object net.webclient).DownloadString("https://raw.githubusercontent.com/JuliaCI/Appveyor.jl/version-1/bin/install.ps1"))

build_script:
# Need to convert from shallow to complete for Pkg.clone to work
- IF EXIST .git\shallow (git fetch --unshallow)
- C:\projects\julia\bin\julia -e "versioninfo();
Pkg.clone(pwd(), \"ImageSegmentation\"); Pkg.build(\"ImageSegmentation\")"
- echo "%JL_BUILD_SCRIPT%"
- C:\julia\bin\julia -e "%JL_BUILD_SCRIPT%"

test_script:
- C:\projects\julia\bin\julia -e "Pkg.test(\"ImageSegmentation\")"
- echo "%JL_TEST_SCRIPT%"
- C:\julia\bin\julia -e "%JL_TEST_SCRIPT%"

matrix:
allow_failures:
- JULIAVERSION: "julianightlies/bin/winnt/x86/julia-latest-win32.exe"
- JULIAVERSION: "julianightlies/bin/winnt/x64/julia-latest-win64.exe"
# # Uncomment to support code coverage upload. Should only be enabled for packages
# # which would have coverage gaps without running on Windows
# on_success:
# - echo "%JL_CODECOV_SCRIPT%"
# - C:\julia\bin\julia -e "%JL_CODECOV_SCRIPT%"
5 changes: 2 additions & 3 deletions src/ImageSegmentation.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
__precompile__()

module ImageSegmentation

import Base: show

using LinearAlgebra, Statistics
using Images, DataStructures, StaticArrays, ImageFiltering, LightGraphs, SimpleWeightedGraphs, RegionTrees, Distances, StaticArrays, Clustering
import Clustering: kmeans, fuzzy_cmeans

Expand Down Expand Up @@ -51,7 +50,7 @@ export
meanshift,
kmeans,
fuzzy_cmeans,

# types
SegmentedImage,
ImageEdge
Expand Down
19 changes: 11 additions & 8 deletions src/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ Returns a real number corresponding to the weight of the edge between label1 and
function region_adjacency_graph(s::SegmentedImage, weight_fn::Function)

function neighbor_regions!(n::Set{Int}, visited::AbstractArray, s::SegmentedImage, I::CartesianIndex)
R = CartesianRange(indices(s.image_indexmap))
R = CartesianIndices(axes(s.image_indexmap))
I1 = one(CartesianIndex{ndims(visited)})
Ibegin, Iend = first(R), last(R)
t = Vector{CartesianIndex{ndims(visited)}}()
Expand All @@ -90,7 +90,7 @@ function region_adjacency_graph(s::SegmentedImage, weight_fn::Function)
while !isempty(t)
temp = pop!(t)
visited[temp] = true
for J in CartesianRange(max(Ibegin, temp-I1), min(Iend, temp+I1))
for J in CartesianIndices(_colon(max(Ibegin, temp-I1), min(Iend, temp+I1)))
if s.image_indexmap[temp] != s.image_indexmap[J]
push!(n,s.image_indexmap[J])
elseif !visited[J]
Expand All @@ -101,7 +101,7 @@ function region_adjacency_graph(s::SegmentedImage, weight_fn::Function)
n
end

visited = similar(dims->fill(false,dims), indices(s.image_indexmap)) # Array to mark the pixels that are already visited
visited = fill(false, axes(s.image_indexmap)) # Array to mark the pixels that are already visited
G = SimpleWeightedGraph() # The region_adjacency_graph
vert_map = Dict{Int,Int}() # Map that stores (label, vertex) pairs

Expand All @@ -114,7 +114,7 @@ function region_adjacency_graph(s::SegmentedImage, weight_fn::Function)
end

# add edges to graph
for p in CartesianRange(indices(s.image_indexmap))
for p in CartesianIndices(axes(s.image_indexmap))
if !visited[p]
n = Set{Int}()
neighbor_regions!(n, visited, s, p)
Expand Down Expand Up @@ -182,7 +182,7 @@ function rem_segment!(s::SegmentedImage, label::Int, diff_fn::Function)
s.segment_pixel_count[minc_label] += s.segment_pixel_count[label]
s.segment_means[minc_label] += (s.segment_means[label] - s.segment_means[minc_label])*s.segment_pixel_count[label]/s.segment_pixel_count[minc_label]

for i in CartesianRange(indices(s.image_indexmap))
for i in CartesianIndices(axes(s.image_indexmap))
s.image_indexmap[i] = s.segment_labels[vert_map[s.image_indexmap[i]]]
end

Expand Down Expand Up @@ -216,7 +216,6 @@ Returns true if label `label` is to be removed otherwise false.
A difference measure between label to be removed and its neighbors. `isless` must be
defined for objects of the type of `d`.
"""

function prune_segments(s::SegmentedImage, is_rem::Function, diff_fn::Function)

G, vert_map = region_adjacency_graph(s, (i,j)->1)
Expand Down Expand Up @@ -244,7 +243,7 @@ function prune_segments(s::SegmentedImage, is_rem::Function, diff_fn::Function)

result = similar(s.image_indexmap)
labels = Vector{Int}()
region_means = similar(s.segment_means)
region_means = empty(s.segment_means)
region_pix_count = Dict{Int, Int}()

m_type = eltype(values(region_means))
Expand All @@ -253,11 +252,15 @@ function prune_segments(s::SegmentedImage, is_rem::Function, diff_fn::Function)
push!(labels, i)
end

for p in CartesianRange(indices(result))
for p in CartesianIndices(axes(result))
result[p] = find_root(u, vert_map[s.image_indexmap[p]])
region_pix_count[result[p]] = get(region_pix_count, result[p], 0) + 1
region_means[result[p]] = get(region_means, result[p], zero(m_type)) + (s.segment_means[s.image_indexmap[p]] - get(region_means, result[p], zero(m_type)))/(region_pix_count[result[p]])
end
SegmentedImage(result, labels, region_means, region_pix_count)

end

# Once Base has colon defined here we can replace this
_colon(I::CartesianIndex{N}, J::CartesianIndex{N}) where N =
map((i,j) -> i:j, Tuple(I), Tuple(J))
36 changes: 18 additions & 18 deletions src/fast_scanning.jl
Original file line number Diff line number Diff line change
@@ -1,27 +1,27 @@
sharpness(img::AbstractArray{CT,N}) where {CT<:Images.NumberLike,N} = var(imfilter(img, Kernel.Laplacian(ntuple(i->true, Val{N}))))
sharpness(img::AbstractArray{CT,N}) where {CT<:Images.NumberLike,N} = var(imfilter(img, Kernel.Laplacian(ntuple(i->true, Val(N)))))

function adaptive_thres(img::AbstractArray{CT,N}, block::NTuple{N,Int}) where {CT<:Images.NumberLike,N}
threshold = zeros(Float64, block)
block_length = CartesianIndex(ntuple(i->ceil(Int,length(indices(img,i))/block[i]),Val{N}))
block_length = CartesianIndex(ntuple(i->ceil(Int,length(axes(img,i))/block[i]),Val(N)))
net_s = sharpness(img)
net_var = var(img)
net_end = last(CartesianRange(indices(img)))
for i in CartesianRange(block)
si = CartesianIndex(ntuple(j->(i[j]-1)*block_length[j]+1,Val{N}))
ei = min(si + block_length - 1, net_end)
net_end = last(CartesianIndices(axes(img)))
for i in CartesianIndices(block)
si = CartesianIndex(ntuple(j->(i[j]-1)*block_length[j]+1,Val(N)))
ei = min(si + block_length - one(si), net_end)
wi = view(img, map((i,j)->i:j, si.I, ei.I)...)
threshold[i] = 0.02 + min(sharpness(wi)/net_s*0.04,0.1) + min(var(wi)/net_var*0.04,0.1)
end
threshold
end

getscalar(A::AbstractArray{T,N}, i::CartesianIndex{N}, block_length::CartesianIndex{N}) where {T<:Real,N} =
A[CartesianIndex(ntuple(j->(i[j]-1)÷block_length[j]+1, Val{N}))]
A[CartesianIndex(ntuple(j->(i[j]-1)÷block_length[j]+1, Val(N)))]

getscalar(a::Real, i...) = a

fast_scanning(img::AbstractArray{CT,N}, block::NTuple{N,Int} =
ntuple(i->4,Val{N})) where {CT<:Images.NumberLike,N} = fast_scanning(img, adaptive_thres(img, block))
fast_scanning(img::AbstractArray{CT,N}, block::NTuple{N,Int} = ntuple(i->4,Val(N))) where {CT<:Images.NumberLike,N} =
fast_scanning(img, adaptive_thres(img, block))

"""
seg_img = fast_scanning(img, threshold, [diff_fn])
Expand All @@ -30,7 +30,7 @@ Segments the N-D image using a fast scanning algorithm and returns a
[`SegmentedImage`](@ref) containing information about the segments.
# Arguments:
* `img` : N-D image to be segmented (arbitrary indices are allowed)
* `img` : N-D image to be segmented (arbitrary axes are allowed)
* `threshold` : Upper bound of the difference measure (δ) for considering
pixel into same segment; an `AbstractArray` can be passed
having same number of dimensions as that of `img` for adaptive
Expand Down Expand Up @@ -64,20 +64,20 @@ function fast_scanning(img::AbstractArray{CT,N}, threshold::Union{AbstractArray,
end

# Neighbourhood function
_diagmN = diagm([1 for i in 1:N])
half_region::NTuple{N,CartesianIndex{N}} = ntuple(i-> CartesianIndex{N}(ntuple(j->_diagmN[j,i], Val{N})), Val{N})
neighbourhood(x) = ntuple(i-> x-half_region[i], Val{N})
_diagmN = Diagonal([1 for i in 1:N])
half_region::NTuple{N,CartesianIndex{N}} = ntuple(i-> CartesianIndex{N}(ntuple(j->_diagmN[j,i], Val(N))), Val(N))
neighbourhood(x) = ntuple(i-> x-half_region[i], Val(N))

# Required data structures
result = similar(dims->fill(-1,dims), indices(img)) # Array to store labels
result = fill(-1, axes(img)) # Array to store labels
region_means = Dict{Int, Images.accum(CT)}() # 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}() # MVector to store valid neighbours
v_neigh = MVector{N,Int}(undef) # MVector to store valid neighbours

block_length = CartesianIndex(ntuple(i->ceil(Int,length(indices(img,i))/size(threshold,i)),Val{N}))
block_length = CartesianIndex(ntuple(i->ceil(Int,length(axes(img,i))/size(threshold,i)), Val(N)))

for point in CartesianRange(indices(img))
for point in CartesianIndices(axes(img))
sz = 0
same_label = true
prev_label = 0
Expand Down Expand Up @@ -132,7 +132,7 @@ function fast_scanning(img::AbstractArray{CT,N}, threshold::Union{AbstractArray,
end
end

for point in CartesianRange(indices(img))
for point in CartesianIndices(axes(img))
result[point] = find_root(temp_labels, result[point])
end

Expand Down
16 changes: 8 additions & 8 deletions src/felzenszwalb.jl
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ function felzenszwalb(edges::Array{ImageEdge}, num_vertices::Int, k::Real, min_s

num_sets = length(segments)
segments2index = Dict{Int, Int}()
for i in 1:num_sets
segments2index[segments[i]]=i
for (i, s) in enumerate(segments)
segments2index[s] = i
end

index_map = Array{Int}(num_vertices)
index_map = Array{Int}(undef, num_vertices)
for i in 1:num_vertices
index_map[i] = segments2index[find_root(G, i)]
end
Expand All @@ -88,13 +88,13 @@ function felzenszwalb(img::AbstractArray{T, 2}, k::Real, min_size::Int = 0) wher
rows, cols = size(img)
num_vertices = rows*cols
num_edges = 4*rows*cols - 3*rows - 3*cols + 2
edges = Array{ImageEdge}(num_edges)
edges = Array{ImageEdge}(undef, num_edges)

R = CartesianRange(size(img))
R = CartesianIndices(size(img))
I1, Iend = first(R), last(R)
num = 1
for I in R
for J in CartesianRange(max(I1, I-I1), min(Iend, I+I1))
for J in CartesianIndices(_colon(max(I1, I-I1), min(Iend, I+I1)))
if I >= J
continue
end
Expand All @@ -110,8 +110,8 @@ function felzenszwalb(img::AbstractArray{T, 2}, k::Real, min_size::Int = 0) wher
region_means = Dict{Int, meantype(T)}()
region_pix_count = Dict{Int, Int}()

for j in indices(img, 2)
for i in indices(img, 1)
for j in axes(img, 2)
for i in axes(img, 1)
result[i, j] = index_map[(j-1)*rows+i]
region_pix_count[result[i,j]] = get(region_pix_count, result[i, j], 0) + 1
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]]
Expand Down
23 changes: 11 additions & 12 deletions src/meanshift.jl
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
"""
```
segments = meanshift(img, spatial_radius, range_radius; iter=50, eps=0.01))
```
```
Segments the image using meanshift clustering. Returns a `SegmentedImage`.
Parameters:
Parameters:
- img = input grayscale image
- spatial_radius/range_radius = bandwidth parameters of truncated normal kernel. Controlling the size of the kernel determines the resolution of the mode detection.
- iter/eps = stopping criterion for meanshift procedure. The algorithm stops after iter iterations or if kernel center moves less than eps distance in an update step, whichever comes first.
"""

function meanshift(img::Array{CT, 2}, spatial_radius::Real, range_radius::Real; iter::Int = 50, eps::Real = 0.01) where CT

rows, cols = size(img)
Expand All @@ -21,12 +20,12 @@ function meanshift(img::Array{CT, 2}, spatial_radius::Real, range_radius::Real;
rowbin(x)::Int = min(floor(x/spatial_radius) +1, rowbins)
colbin(x)::Int = min(floor(x/spatial_radius) +1, colbins)

buckets = Array{Vector{CartesianIndex{2}}}(rowbins, colbins, colorbins)
for i in CartesianRange(size(buckets))
buckets[i]=Array{CartesianIndex{2}}(0)
buckets = Array{Vector{CartesianIndex{2}}}(undef, rowbins, colbins, colorbins)
for i in CartesianIndices(size(buckets))
buckets[i]=Array{CartesianIndex{2}}(undef, 0)
end

for i in CartesianRange(size(img))
for i in CartesianIndices(size(img))
push!( buckets[rowbin(i[1]), colbin(i[2]), colorbin(img[i])], i)
end

Expand All @@ -38,11 +37,11 @@ function meanshift(img::Array{CT, 2}, spatial_radius::Real, range_radius::Real;
den = 0.0
num = SVector(0.0, 0.0, 0.0)

R = CartesianRange(size(buckets))
R = CartesianIndices(size(buckets))
I1, Iend = first(R), last(R)
I = CartesianIndex(rowbin(pt[1]), colbin(pt[2]), colorbin(pt[3]))

for J in CartesianRange(max(I1, I-1), min(Iend, I+1))
for J in CartesianIndices(_colon(max(I1, I-one(I)), min(Iend, I+one(I))))
for point in buckets[J]
if dist2(pt, SVector(point[1], point[2], img[point])) <= 1
num += SVector(point[1], point[2], img[point])
Expand All @@ -54,9 +53,9 @@ function meanshift(img::Array{CT, 2}, spatial_radius::Real, range_radius::Real;
return den<=0 ? pt : num/den
end

modes = Array{Float64}(3, rows*cols)
modes = Array{Float64}(undef, 3, rows*cols)

for i in CartesianRange(size(img))
for i in CartesianIndices(size(img))
pt::SVector{3, Float64} = SVector(i[1], i[2], img[i])
for j in 1:iter
nextpt = neighborhood_mean(pt)
Expand Down
Loading

0 comments on commit 884af7c

Please sign in to comment.