Skip to content
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

feat: regularize/final #500

Merged
merged 23 commits into from
Nov 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f5c51ff
feat: get_new2/3/final
cpaniaguam Nov 7, 2024
95e19d2
Update new2-new3-final.jl
cpaniaguam Nov 7, 2024
8bcdeab
fix: remove extra using statement
cpaniaguam Nov 7, 2024
e7c2bac
Merge branch 'main' into 493-get_new23-final
cpaniaguam Nov 9, 2024
8a7bc4e
fix: add se to function signature
cpaniaguam Nov 11, 2024
ab9bfcf
refactor: add docs and more sensible names
cpaniaguam Nov 14, 2024
590c219
chore: rename module
cpaniaguam Nov 14, 2024
678932b
docs: add docstring to get_final
cpaniaguam Nov 14, 2024
6fb2554
Merge branch 'main' into 493-get_new23-final
cpaniaguam Nov 14, 2024
82bdebe
chore: remove todo
cpaniaguam Nov 15, 2024
bb3c1de
Update regularize-final.jl
cpaniaguam Nov 15, 2024
d98eac0
Merge branch 'main' into 493-get_new23-final
cpaniaguam Nov 18, 2024
e9c2853
Merge branch 'main' into 493-get_new23-final
cpaniaguam Nov 18, 2024
a1bc175
chore: clean up regularize-final
cpaniaguam Nov 18, 2024
ca8a412
doc: clarify docstring
cpaniaguam Nov 18, 2024
c8120eb
refactor: remove redundant impose_minima function implementations
cpaniaguam Nov 18, 2024
fef6a8d
fix: correct inclusion of regularize-final.jl
cpaniaguam Nov 18, 2024
4d35516
fix: update usage of reconstruct function in regularize-final.jl
cpaniaguam Nov 18, 2024
a954da1
refactor: regularize
cpaniaguam Nov 19, 2024
bb87094
test: regularize_fill_holes and regularize_sharpening
cpaniaguam Nov 19, 2024
a8a5789
test: add input data files
cpaniaguam Nov 19, 2024
90b3177
test: add tests for get_final
cpaniaguam Nov 19, 2024
5362d04
feat: add se_disk2
cpaniaguam Nov 19, 2024
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
8 changes: 1 addition & 7 deletions src/IceFloeTracker.jl
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,7 @@ include("morph_fill.jl")
include("imcomplement.jl")
include("imadjust.jl")
include("ice_masks.jl")

const sk_measure = PyNULL()
const sk_exposure = PyNULL()
const getlatlon = PyNULL()



include("regularize-final.jl")

function get_version_from_toml(pth=dirname(dirname(pathof(IceFloeTracker))))::VersionNumber
toml = TOML.parsefile(joinpath(pth, "Project.toml"))
Expand Down
113 changes: 113 additions & 0 deletions src/regularize-final.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
using IceFloeTracker: unsharp_mask, to_uint8, hbreak, morph_fill

# TODO: Add tests for regularize_fill_holes, regularize_sharpening, get_final

"""
regularize_fill_holes(img, local_maxima_mask, factor, segment_mask, L0mask)

Regularize `img` by:
1. increasing the maxima of `img` by a factor of `factor`
2. filtering `img` at positions where either `segment_mask` or `L0mask` are true
3. filling holes

# Arguments
- `img`: The morphological residue image.
- `local_maxima_mask`: The local maxima mask.
- `factor`: The factor to apply to the local maxima mask.
- `segment_mask`: The segment mask -- intersection of bw1 and bw2 in first tiled workflow of `master.m`.
- `L0mask`: zero-labeled pixels from watershed.
"""
function regularize_fill_holes(img, local_maxima_mask, segment_mask, L0mask, factor)
new2 = to_uint8(img .+ local_maxima_mask .* factor)
new2[segment_mask .|| L0mask] .= 0
return IceFloeTracker.MorphSE.fill_holes(new2)
end

"""
regularize_sharpening(img, L0mask, radius, amount, local_maxima_mask, factor, segment_mask, se)

Regularize `img` via sharpening, filtering, reconstruction, and maxima elevating.

# Arguments
- `img`: The input image.
- `L0mask`: zero-labeled pixels from watershed.
- `radius`: The radius of the unsharp mask.
- `amount`: The amount of unsharp mask.
- `local_maxima_mask`: The local maxima mask.
- `factor`: The factor to apply to the local maxima mask.
- `segment_mask`: The segment mask -- intersection of bw1 and bw2 in first tiled workflow of `master.m`.
"""
function regularize_sharpening(
img, L0mask, local_maxima_mask, segment_mask, se, radius, amount, factor
)
new3 = unsharp_mask(img, radius, amount, 255)
new3[L0mask] .= 0
new3 = IceFloeTracker.reconstruct(new3, se, "dilation", false)
new3[segment_mask] .= 0
return to_uint8(new3 + local_maxima_mask .* factor)
end

function _regularize(
morph_residue, local_maxima_mask, segment_mask, L0mask, se; factor, radius, amount
)
reg_fill_holes = regularize_fill_holes(
morph_residue, local_maxima_mask, segment_mask, L0mask, factor[1]
)
reg_sharpened = regularize_sharpening(
reg_fill_holes,
L0mask,
local_maxima_mask,
segment_mask,
se,
radius,
amount,
factor[end],
)
return reg_sharpened
end

"""
get_final(img, label, segment_mask, se_erosion, se_dilation)

Final processing following the tiling workflow.

# Arguments
- `img`: The input image.
- `label`: Mode of most common label in the find_ice_labels workflow.
- `segment_mask`: The segment mask.
- `se_erosion`: structuring element for erosion.
- `se_dilation`: structuring element for dilation.
- `apply_segment_mask=true`: Whether to filter `img` the segment mask.

"""
function get_final(
# this function is used in preprocessing_tiling
img,
segment_mask,
se_erosion,
se_dilation,
apply_segment_mask::Bool=true,
)
_img = hbreak(img)

# slow for big images
_img .= morph_fill(_img)

# TODO: decide on criteria for applying segment mask
apply_segment_mask && (_img[segment_mask] .= false)

# tends to fill more than matlabs imfill
_img .= IceFloeTracker.MorphSE.fill_holes(_img)

# marker image
_img .= branch(_img)

#= opening to remove noise while preserving shape/size
Note the different structuring elements for erosion and dilation =#
mask = sk_morphology.erosion(_img, se_erosion)
mask .= sk_morphology.dilation(mask, se_dilation)

# Restore shape of floes based on the cleaned up `mask`
final = IceFloeTracker.MorphSE.mreconstruct(IceFloeTracker.MorphSE.dilate, _img, mask)
return final
end
8 changes: 7 additions & 1 deletion src/special_strels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,10 @@ function se_disk20()
se = [sum(c.I) <= 11 for c in CartesianIndices((39, 39))]
_generate_se!(se)
return se
end
end

function se_disk2()
se = [sum(c.I) <= 3 for c in CartesianIndices((5,5))]
_generate_se!(se)
return se
end
29 changes: 0 additions & 29 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -120,35 +120,6 @@ function imextendedmin(img::AbstractArray, h::Int=2, conn::Int=2)::BitMatrix
return mask_minima .> 0
end

function impose_minima(I::AbstractArray{T}, BW::AbstractArray{Bool}) where {T<:Integer}
marker = 255 .* BW
mask = imcomplement(min.(I .+ 1, 255 .- marker))
reconstructed = IceFloeTracker.MorphSE.mreconstruct(
IceFloeTracker.MorphSE.dilate, marker, mask
)
return IceFloeTracker.imcomplement(Int.(reconstructed))
end

function impose_minima(
I::AbstractArray{T}, BW::AbstractMatrix{Bool}
) where {T<:AbstractFloat}
# compute shift
a, b = extrema(I)
rng = b - a
h = rng == 0 ? 0.1 : rng / 1000

marker = -Inf * BW .+ (Inf * .!BW)
mask = min.(I .+ h, marker)

return 1 .- IceFloeTracker.MorphSE.mreconstruct(
IceFloeTracker.MorphSE.dilate, 1 .- marker, 1 .- mask
)
end

function imregionalmin(A, conn=2)
return ImageMorphology.local_minima(A; connectivity=conn) .> 0
end

"""
impose_minima(I::AbstractArray{T}, BW::AbstractArray{Bool}) where {T<:Integer}

Expand Down
63 changes: 63 additions & 0 deletions test/test-regularize-final.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using IceFloeTracker:
get_tiles,
regularize_fill_holes,
regularize_sharpening,
_regularize,
se_disk2,
get_final
using DelimitedFiles: readdlm

se = collect(IceFloeTracker.MorphSE.strel_diamond((3, 3)))

test_files_dir = joinpath(@__DIR__, "test_inputs/regularize")

morph_residue = readdlm(joinpath(test_files_dir, "morph_residue.csv"), ',', Int)
local_maxima_mask = readdlm(joinpath(test_files_dir, "local_maxima_mask.csv"), ',', Int)
segment_mask = readdlm(joinpath(test_files_dir, "segment_mask.csv"), ',', Bool)
L0mask = readdlm(joinpath(test_files_dir, "L0mask.csv"), ',', Bool)
expected_regularized_holes_filled = readdlm(
joinpath(test_files_dir, "reg_holes_filled_expected.csv"), ',', Int
)
expected_regularized_sharpened = readdlm(
joinpath(test_files_dir, "reg_sharpened.csv"), ',', Int
)

get_final_input = readdlm(joinpath(test_files_dir, "get_final.csv"), ',', Bool)
se_erosion = se
se_dilation = se_disk2()
get_final_expected = readdlm(joinpath(test_files_dir, "get_final_expected.csv"), ',', Bool)

@testset "regularize/get_final" begin
@testset "regularize_fill_holes/sharpening" begin
reg_holes_filled = regularize_fill_holes(
morph_residue, local_maxima_mask, segment_mask, L0mask, 0.3
)

reg_sharpened = regularize_sharpening(
reg_holes_filled, L0mask, local_maxima_mask, segment_mask, se, 10, 2, 0.5
)

reg = _regularize(
morph_residue,
local_maxima_mask,
segment_mask,
L0mask,
se;
factor=(0.3, 0.5),
radius=10,
amount=2,
)

@test expected_regularized_holes_filled == reg_holes_filled
@test expected_regularized_sharpened == reg_sharpened
@test expected_regularized_sharpened == reg
end

@testset "get_final" begin
get_final_output = get_final(
get_final_input, segment_mask, se_erosion, se_dilation, true
)

@test get_final_output == get_final_expected
end
end
Loading