-
Notifications
You must be signed in to change notification settings - Fork 21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Morphology][Tasks] introduce generic fillholes algorithm #119
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
|
@@ -24,6 +24,7 @@ include("connected.jl") | |||
include("clearborder.jl") | ||||
include("extreme_filter.jl") | ||||
include("extremum.jl") | ||||
include("filholes.jl") | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this be fillholes.jl? |
||||
include("ops/dilate.jl") | ||||
include("ops/erode.jl") | ||||
include("ops/closing.jl") | ||||
|
@@ -130,15 +131,22 @@ export | |||
#feature_transform.jl | ||||
feature_transform, | ||||
distance_transform, | ||||
|
||||
#clearborder | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These comments seem redundant There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||
clearborder, | ||||
|
||||
#fillhole | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||
fillhole, | ||||
fillhole!, | ||||
|
||||
#leveling | ||||
low_leveling, | ||||
low_leveling!, | ||||
high_leveling, | ||||
high_leveling!, | ||||
leveling, | ||||
leveling!, | ||||
|
||||
#extremum | ||||
hmaxima, | ||||
hmaxima!, | ||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,58 @@ | ||||||
""" | ||||||
fillhole(img; [dims]) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Change the name of the file to match the base of the function, please |
||||||
fillhole(img; se) | ||||||
|
||||||
Fill the holes in image 'img'. Could be binary or grascale | ||||||
|
||||||
The `dims` keyword is used to specify the dimension to process by constructing the box shape | ||||||
structuring element [`strel_box(img; dims)`](@ref strel_box). For generic structuring | ||||||
element, the half-size is expected to be either `0` or `1` along each dimension. | ||||||
|
||||||
The output has the same type as input image | ||||||
""" | ||||||
|
||||||
function fillhole(img; dims=coords_spatial(img)) | ||||||
return fillhole(img, strel_box(img, dims)) | ||||||
end | ||||||
|
||||||
function fillhole(img, se) | ||||||
return fillhole!(similar(img), img, se) | ||||||
end | ||||||
|
||||||
function fillhole!(out, img; dims=coords_spatial(img)) | ||||||
return fillhole!(out, img, strel_box(img, dims)) | ||||||
end | ||||||
|
||||||
function fillhole!(out, img, se) | ||||||
return _fillhole!(out, img, se) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need the underscore prefix? Could we combine the function below and this one to a single signature? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The prefix correspon to the most internal call, you will see this pattern in the whole project, something like "private" implem |
||||||
end | ||||||
|
||||||
function _fillhole!(out, img, se) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should consider typing the arguments here and extracting the number of dimensions and element type from the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree i could but why? |
||||||
N = ndims(img) | ||||||
|
||||||
axes(out) == axes(img) || throw(DimensionMismatch("images should have the same axes")) | ||||||
|
||||||
se_size = strel_size(se) | ||||||
if length(se_size) != N | ||||||
msg = "the input structuring element is not for $N dimensional array, instead it is for $(length(se_size)) dimensional array" | ||||||
throw(DimensionMismatch(msg)) | ||||||
end | ||||||
if !all(x -> in(x, (1, 3)), strel_size(se)) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
msg = "structuring element with half-size larger than 1 is invalid" | ||||||
throw(DimensionMismatch(msg)) | ||||||
end | ||||||
|
||||||
tmp = similar(img) | ||||||
|
||||||
# fill marker image with max | ||||||
fill!(tmp, typemax(eltype(img))) | ||||||
# fill borders with 0 | ||||||
dimensions = size(tmp) | ||||||
outerrange = CartesianIndices(map(i -> 1:i, dimensions)) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are we assuming one based indexing here? If so, we may want to use |
||||||
innerrange = CartesianIndices(map(i -> (1 + 1):(i - 1), dimensions)) | ||||||
for i in EdgeIterator(outerrange, innerrange) | ||||||
tmp[i] = 0 | ||||||
end | ||||||
|
||||||
return mreconstruct!(erode, out, tmp, img, se) | ||||||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
@testset "fillhole" begin | ||
#binary | ||
img = Bool[ | ||
0 0 0 0 0 0 0 | ||
0 1 1 1 1 1 0 | ||
0 1 0 0 0 1 0 | ||
0 1 0 0 0 1 0 | ||
0 1 0 0 0 1 0 | ||
0 1 1 1 1 1 0 | ||
0 0 0 0 0 0 0 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you add a test where the 0s at the border are not a single connected component? Also the case where there are no zeros on the border m. Also add a test for multiple interior holes. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that we could say nothing for "holes" in the border, anyway we don't have acess to underlyting image domain, are these really holes |
||
] | ||
|
||
expected = Bool[ | ||
0 0 0 0 0 0 0 | ||
0 1 1 1 1 1 0 | ||
0 1 1 1 1 1 0 | ||
0 1 1 1 1 1 0 | ||
0 1 1 1 1 1 0 | ||
0 1 1 1 1 1 0 | ||
0 0 0 0 0 0 0 | ||
] | ||
|
||
out = fillhole(img) | ||
@test eltype(out) == Bool | ||
@test out == expected | ||
|
||
# in place | ||
out = similar(img) | ||
out = fillhole!(out, img) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need the left side assignment here? |
||
@test out == expected | ||
|
||
# in place diamond | ||
out = similar(img) | ||
out = fillhole!(out, img, strel_diamond((3, 3))) | ||
@test out == expected | ||
|
||
#gray | ||
img = [ | ||
3 3 3 3 3 3 3 3 3 3 | ||
3 4 4 4 3 3 4 4 4 3 | ||
3 4 1 4 3 3 4 1 4 3 | ||
3 4 4 4 3 3 4 4 4 3 | ||
3 3 3 3 3 3 3 3 3 3 | ||
] | ||
|
||
expected = [ | ||
3 3 3 3 3 3 3 3 3 3 | ||
3 4 4 4 3 3 4 4 4 3 | ||
3 4 4 4 3 3 4 4 4 3 | ||
3 4 4 4 3 3 4 4 4 3 | ||
3 3 3 3 3 3 3 3 3 3 | ||
] | ||
|
||
out = fillhole(img) | ||
@test out == expected | ||
|
||
msg = "the input structuring element is not for 1 dimensional array, instead it is for 2 dimensional array" | ||
@test_throws DimensionMismatch(msg) fillhole(rand(10), strel_box((3, 3))) | ||
|
||
se = strel_diamond((7, 7)) | ||
msg = "structuring element with half-size larger than 1 is invalid" | ||
@test_throws DimensionMismatch(msg) fillhole(rand(10, 10), se) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a test for an OffsetArray. Either check for the appropriate exception or correct function. |
||
|
||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,6 +37,7 @@ include("testutils.jl") | |
include("feature_transform.jl") | ||
include("leveling.jl") | ||
include("clearborder.jl") | ||
include("fillholes.jl") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this indented? |
||
@info "Beginning deprecation tests, warnings are expected" | ||
include("deprecations.jl") | ||
end | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why remove the hashes?