Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 2 additions & 1 deletion REQUIRE
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
julia 0.7
ImageCore 0.7.0
Images 0.13.0
ImageCore 0.7.0
10 changes: 8 additions & 2 deletions src/ImageMorphology.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@ __precompile__()
module ImageMorphology

using ImageCore
using Images
Copy link
Copy Markdown
Member

@johnnychen94 johnnychen94 Apr 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's using ImageMorphology in Images.jl, and it will make package dependency complicate.

@timholy How do we deal with this problem in general? Can we directly copy Images.FeatureTransform module to ImageMorphology as temporary not-exported functions?
https://github.com/JuliaImages/Images.jl/blob/bdfd044420fa6ffcd34760f804f0c3ce12186945/src/bwdist.jl

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@johnnychen94 another example where we find something implemented in Images.jl that could live somewhere else. We really need to reorganize concepts around and break Images.jl into smaller packages for reuse. Right now a bunch of functionality lives in the umbrella package, and that is suboptimal.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like @Deepank308 needs this function implemented and merged ASAP to continue his GSoC ImageTracking project.

@juliohm I guess we can copy these methods to FeatureTransform.jl (with comments) and remove it in the future, just like @zygmuntszpak does in https://github.com/zygmuntszpak/ImageBinarization.jl/blob/6e0c81867eaef67b463fa5d52febfca4d0f6196a/src/integral_image.jl ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if copying/pasting helps @johnnychen94 , ideally we would start thinking more seriously about how to reorganize things. There is a lot of code living in the wrong place and we are just compromising the situation further by adopting multiple copies in submodules.

Copy link
Copy Markdown
Member

@johnnychen94 johnnychen94 Apr 17, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might need another two weeks to start the porting work. Let's see how it works then.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@johnnychen94 I don't need this function as of now in my implementations. I was just going through Skeletonization algorithm and found that Julia doesn't have it. So, I implemented and sent a PR.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case, this PR might be pended until FeatureTransform being ported to a standalone module.


abstract type SkeletonizationAlgo end
Comment thread
Deepank308 marked this conversation as resolved.
Outdated
struct MedialAxis <: SkeletonizationAlgo end
Comment thread
Deepank308 marked this conversation as resolved.
Outdated


include("dilation_and_erosion.jl")
include("thinning.jl")

include("skeletonization.jl")
export
dilate,
erode,
Expand All @@ -16,7 +21,8 @@ export
bothat,
morphogradient,
morpholaplace,

MedialAxis,
skeletonize,
thinning,
GuoAlgo

Expand Down
81 changes: 81 additions & 0 deletions src/skeletonization.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# checks if element is essential for connectivity
function compute_essential_indices()
Comment thread
Deepank308 marked this conversation as resolved.
Outdated
essential_index_array = Array{Bool, 1}()
Comment thread
Deepank308 marked this conversation as resolved.
Outdated
for index = 0:511
append!(essential_index_array, maximum(find_components(index)) != maximum(find_components(index & ~2^4)))
end
essential_index_array
end

function find_components(index::Int64)
Comment thread
Deepank308 marked this conversation as resolved.
Outdated
label_components(reshape(digits(index, base=2, pad=9), (3, 3)), trues(3, 3))
end

function corner_table_lookup(img::AbstractArray{Bool, 2}, corner_table::AbstractArray{Int, 1})
corner_score = zeros(Int, size(img) .+ 2)
score_table = [256 128 64; 32 16 8; 4 2 1]

for i = 2:size(img)[1] + 1
for j = 2:size(img)[2] + 1
if img[i - 1, j - 1]
corner_score[i - 1:i + 1, j - 1:j + 1] = corner_score[i - 1:i + 1, j - 1:j + 1] + score_table
end
end
end
corner_table[corner_score[2: size(img)[1] + 1, 2: size(img)[2] + 1] .+ 1]
end

function inner_skeleton_loop(img::AbstractArray{Bool, 2}, order::Array{Int, 1}, table::AbstractArray{Bool, 1})
index = findall(img)[order]
score_table = [256 128 64; 32 16 8; 4 2 1]
result = zeros(Int, size(img))
padded_image = zeros(Int, size(img) .+ 2)
Comment thread
Deepank308 marked this conversation as resolved.
Outdated
padded_image[2: size(img)[1] + 1, 2: size(img)[2] + 1] = img
for i in index
result[i] = table[sum(padded_image[i[1]:i[1] + 2, i[2]:i[2] + 2] .* score_table) + 1]
padded_image[i[1] + 1, i[2] + 1] = result[i]
end
result
end

function skeletonize(img::AbstractArray{Bool}, algo::SkeletonizationAlgo)
Comment thread
Deepank308 marked this conversation as resolved.
Outdated
skeletonize_impl(img, algo)
end

function skeletonize_impl(img::AbstractArray{Bool, 2}, algo::MedialAxis)
Comment thread
Deepank308 marked this conversation as resolved.
Outdated

# check if array has only 0s
if sum(img) == 0
Comment thread
Deepank308 marked this conversation as resolved.
Outdated
return img
end

# check if array has only 1s
if sum(img) == length(img)
img[1, 1:end] = false
img[1:end - 1, end] = false
img[end, 1:end - 1] = false
return convert(Array{Bool}, .~img)
Comment thread
Deepank308 marked this conversation as resolved.
Outdated
end

# build look-up table
patterns = [0:1:511;]
num_pixels_in_patterns_kernel = sum.(digits.(patterns, base=2))
center_is_foreground = convert(Array{Bool, 1}, patterns .& 2^4 .> 0)
Comment thread
Deepank308 marked this conversation as resolved.
Outdated
table = (center_is_foreground .& compute_essential_indices() .| (num_pixels_in_patterns_kernel .< 3))
table = convert(Array{Bool, 1}, table)

# store distance transform
distance = distance_transform(feature_transform(.~img))

# corners handling
corner_table = 9 .- num_pixels_in_patterns_kernel
corner_score = corner_table_lookup(img, corner_table)

# generate order for traversing the shape
dist_corner_pair = CartesianIndex.(round.(Int, distance[img] .* 10), corner_score[img])
first_sort = sortperm(dist_corner_pair, by=x->x[2])
second_sort = sortperm(dist_corner_pair[first_sort], by=x->x[1])
order = first_sort[second_sort]

convert(Array{Bool, 2}, inner_skeleton_loop(img, order, table))
end
16 changes: 16 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,20 @@ using Test
0 0 0 0 0 0])
@test thinning(img) == img
end
@testset "Skeletonization" begin
img = Bool.([0 0 0 0 0
0 1 1 1 0
0 1 1 1 0
0 1 1 1 0
0 1 1 1 0
0 0 0 0 0])
ans = Bool.([0 0 0 0 0
0 1 0 1 0
0 0 1 0 0
0 0 1 0 0
0 1 0 1 0
0 0 0 0 0])

@test skeletonize(img, MedialAxis()) == ans
end
end