Skip to content

Commit c84f5f1

Browse files
committed
feat: add function to fill interior holes
1 parent 4504343 commit c84f5f1

File tree

5 files changed

+80
-4
lines changed

5 files changed

+80
-4
lines changed

src/IceFloeTracker.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ end
7979
8080
Module for morphological operations with structuring element functionality adapted from ImageMorphology v0.4.3.
8181
82-
This module is temporary until v0.5 of ImageMorphology is relaeased.
82+
This module is temporary until v0.5 of ImageMorphology is released.
8383
8484
Main functionality is `dilate(img, se)` for landmask computations.
8585
@@ -136,5 +136,6 @@ module MorphSE
136136
include("morphSE/closing.jl")
137137
include("morphSE/bothat.jl")
138138
include("morphSE/mreconstruct.jl")
139+
include("morphSE/fill_holes.jl")
139140
end
140141
end

src/morphSE/fill_holes.jl

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# From https://github.com/JuliaImages/ImageMorphology.jl/pull/119
2+
3+
"""
4+
fill_holes(img; [dims])
5+
fill_holes(img; se)
6+
Fill the holes in image 'img'. Could be binary or grascale
7+
The `dims` keyword is used to specify the dimension to process by constructing the box shape
8+
structuring element [`strel_box(img; dims)`](@ref strel_box). For generic structuring
9+
element, the half-size is expected to be either `0` or `1` along each dimension.
10+
The output has the same type as input image
11+
"""
12+
13+
function fill_holes(img; dims=coords_spatial(img))
14+
return fill_holes(img, strel_box(img, dims))
15+
end
16+
17+
function fill_holes(img, se)
18+
return fill_holes!(similar(img), img, se)
19+
end
20+
21+
function fill_holes!(out, img; dims=coords_spatial(img))
22+
return fill_holes!(out, img, strel_box(img, dims))
23+
end
24+
25+
function fill_holes!(out, img, se)
26+
return _fill_holes!(out, img, se)
27+
end
28+
29+
function _fill_holes!(out, img, se)
30+
N = ndims(img)
31+
32+
axes(out) == axes(img) || throw(DimensionMismatch("images should have the same axes"))
33+
34+
se_size = strel_size(se)
35+
if length(se_size) != N
36+
msg = "the input structuring element is not for $N dimensional array, instead it is for $(length(se_size)) dimensional array"
37+
throw(DimensionMismatch(msg))
38+
end
39+
if !all(x -> in(x, (1, 3)), strel_size(se))
40+
msg = "structuring element with half-size larger than 1 is invalid"
41+
throw(DimensionMismatch(msg))
42+
end
43+
44+
tmp = similar(img)
45+
46+
# fill marker image with max
47+
fill!(tmp, typemax(eltype(img)))
48+
# fill borders with 0
49+
dimensions = size(tmp)
50+
outerrange = CartesianIndices(map(i -> 1:i, dimensions))
51+
innerrange = CartesianIndices(map(i -> (1 + 1):(i - 1), dimensions))
52+
for i in EdgeIterator(outerrange, innerrange)
53+
tmp[i] = 0
54+
end
55+
56+
return mreconstruct!(erode, out, tmp, img, se)
57+
end

test/runtests.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ testnames = [n[6:(end - 3)] for n in alltests]
1717

1818
## Put the filenames to test below
1919

20-
to_test = #alltests # uncomment this line to run all tests or add individual files below
20+
to_test = alltests # uncomment this line to run all tests or add individual files below
2121
[
2222
# "test-create-landmask.jl",
2323
# "test-create-cloudmask.jl",
@@ -30,7 +30,7 @@ to_test = #alltests # uncomment this line to run all tests or add individual fil
3030
# "test-segmentation-b.jl",
3131
# "test-segmentation-c.jl",
3232
# "test-segmentation-d-e.jl",
33-
"test-segmentation-f.jl",
33+
# "test-segmentation-f.jl",
3434
# "test-bwtraceboundary.jl",
3535
# "test-resample-boundary.jl",
3636
# "test-regionprops.jl",

test/test-morphSE.jl

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,31 @@
99
se = IceFloeTracker.MorphSE.strel_box((n, n))
1010
@test IceFloeTracker.MorphSE.dilate(a, se) == ones(Int, n, n)
1111

12-
# Bothat, opening, erode using output from Matlab
12+
# Bothat, opening, erode, filling holes, reconstruction using output from Matlab
1313
A = zeros(Bool, 41, 41)
1414
A[(21 - 10):(21 + 10), (21 - 10):(21 + 10)] .= 1
15+
I = falses(8, 8)
16+
I[1:8, 3:6] .= 1
17+
I[[CartesianIndex(4, 4), CartesianIndex(5, 5)]] .= 0
18+
I
1519
se = centered(IceFloeTracker.se_disk4())
20+
21+
#read in expected files from MATLAB
1622
path = joinpath(test_data_dir, "morphSE")
1723
erode_withse_exp = readdlm(joinpath(path, "erode_withse1_exp.csv"), ',', Bool)
1824
bothat_withse_exp = readdlm(joinpath(path, "bothat_withse1_exp.csv"), ',', Bool)
1925
open_withse_exp = readdlm(joinpath(path, "open_withse1_exp.csv"), ',', Bool)
2026
reconstruct_exp = readdlm(joinpath(path, "reconstruct_exp.csv"), ',', Int64)
2127
matrix_A = readdlm(joinpath(path, "mat_a.csv"), ',', Int64)
2228
matrix_B = readdlm(joinpath(path, "mat_b.csv"), ',', Int64)
29+
filled_holes_exp = readdlm(joinpath(path, "filled_holes.csv"), ',', Int64)
30+
31+
#run tests
2332
@test open_withse_exp == IceFloeTracker.MorphSE.opening(A, se)
2433
@test erode_withse_exp == IceFloeTracker.MorphSE.erode(A, se)
2534
@test bothat_withse_exp == IceFloeTracker.MorphSE.bothat(A, se)
2635
@test reconstruct_exp == IceFloeTracker.MorphSE.mreconstruct(
2736
IceFloeTracker.MorphSE.dilate, matrix_B, matrix_A
2837
)
38+
@test filled_holes_exp == IceFloeTracker.MorphSE.fill_holes(I)
2939
end
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
0,0,1,1,1,1,0,0
2+
0,0,1,1,1,1,0,0
3+
0,0,1,1,1,1,0,0
4+
0,0,1,1,1,1,0,0
5+
0,0,1,1,1,1,0,0
6+
0,0,1,1,1,1,0,0
7+
0,0,1,1,1,1,0,0
8+
0,0,1,1,1,1,0,0

0 commit comments

Comments
 (0)