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

Tiled workflow feats #451

Closed
wants to merge 102 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
4b4f08a
fix: formatting
cpaniaguam Oct 18, 2024
39d807a
fix: enhance to_uint8 function to support additional numeric types an…
cpaniaguam Oct 18, 2024
d06e438
fix: improve code formatting
cpaniaguam Oct 18, 2024
a13751e
feat: add get_brighten_mask and imbrighten
cpaniaguam Oct 18, 2024
fdbfec8
fix: formatting tilingutils.jl
cpaniaguam Oct 18, 2024
0592404
test: add tests for get_brighten_mask and imbrighten functions
cpaniaguam Oct 18, 2024
1bb13c1
fix: format test-tilingutils.jl
cpaniaguam Oct 18, 2024
0c7b725
WIP: reconstruct
cpaniaguam Oct 18, 2024
976424e
feat: open_by_reconstruction
cpaniaguam Oct 19, 2024
65e8100
test: open_by_reconstruction
cpaniaguam Oct 19, 2024
1d361d3
feat: add reconstruct_erosion
cpaniaguam Oct 19, 2024
7f7dc84
test: add test for reconstruct_erosion
cpaniaguam Oct 19, 2024
167dc69
feat: include reconstruct module in IceFloeTracker
cpaniaguam Oct 19, 2024
60f254e
feat: refactor reconstruction functions
cpaniaguam Oct 19, 2024
34d60cb
test: enhance tests for reconstruction functions
cpaniaguam Oct 19, 2024
a2924ba
fix: handle cases with fewer than two peaks in find_reflectance_peaks
cpaniaguam Oct 20, 2024
846eaf5
refactor: improve readability of find_ice_labels by formatting find_r…
cpaniaguam Oct 20, 2024
9a229e8
feat: add get_ice_labels
cpaniaguam Oct 20, 2024
86ab8ca
refactor: remove persistence of intermediate segmentation outputs in …
cpaniaguam Oct 20, 2024
b179d92
feat: enhance kmeans_segmentation to support customizable parameters
cpaniaguam Oct 20, 2024
695cead
feat: update kmeans_segmentation to support dynamic class count and i…
cpaniaguam Oct 20, 2024
64a3f69
refactor: clean up
cpaniaguam Oct 20, 2024
189dd66
refactor: remove unnecessary blank line in segmentation test
cpaniaguam Oct 20, 2024
df0c71d
feat: add documentation for get_tiles function with customizable row …
cpaniaguam Oct 20, 2024
7c18635
feat: add documentation for imbrighten
cpaniaguam Oct 20, 2024
ef25269
test: get_image_peaks
cpaniaguam Oct 22, 2024
0233872
feat: get_image_peaks, imhist
cpaniaguam Oct 22, 2024
743e83b
feat: get_ice_labels + tests
cpaniaguam Oct 22, 2024
d00a448
feat: get_nlabel and tests
cpaniaguam Oct 22, 2024
6ef5c62
fix: merge conflicts
cpaniaguam Oct 22, 2024
d260cd3
chore: delete extra get_ice_labels function
cpaniaguam Oct 23, 2024
b77e99d
chore: add note about parse_requirements
cpaniaguam Oct 23, 2024
8f1ab45
feat: add impose_minima
cpaniaguam Oct 24, 2024
775528a
test: impose_minima
cpaniaguam Oct 24, 2024
e2c6429
test: impose_minima
cpaniaguam Oct 24, 2024
914138e
test: update get_ice_labels test and add watershed tests
cpaniaguam Oct 25, 2024
5b93948
feat: update get_nlabel to use get_ice_labels_mask and add watershed …
cpaniaguam Oct 25, 2024
cbf733c
feat: rename get_ice_labels to get_ice_labels_mask for clarity
cpaniaguam Oct 25, 2024
6d9659f
refactor: relocate impose_minima
cpaniaguam Oct 25, 2024
4b4f4f5
Merge branch 'tiled-workflow-feats' of https://github.com/WilhelmusLa…
cpaniaguam Oct 25, 2024
ab35fc7
feat: add get_new2/new3
cpaniaguam Oct 28, 2024
9232ad9
test: add test for morph_fill functionality
cpaniaguam Oct 29, 2024
fb160fc
feat: implement morph_fill
cpaniaguam Oct 29, 2024
a84643d
refactor: rename _bridge_filter to _filter
cpaniaguam Oct 29, 2024
7a909f5
feat: refactor morph_fill to use LUT-based fill operator
cpaniaguam Oct 29, 2024
9da85ed
feat: add LUT fill function
cpaniaguam Oct 29, 2024
c143fbd
feat: add TODO for parallelizing _filter implementation
cpaniaguam Oct 29, 2024
a2a7fba
feat: add final processing function for tiling workflow
cpaniaguam Oct 29, 2024
5813caf
Merge branch 'main' into tiled-workflow-feats
cpaniaguam Oct 29, 2024
6ae07ce
fix: bridge
cpaniaguam Oct 29, 2024
bfbc521
fix: test-morph-fill
cpaniaguam Oct 29, 2024
3d956aa
Merge branch 'tiled-workflow-feats' of https://github.com/WilhelmusLa…
Oct 29, 2024
1507f05
fix: include for reconstruct
cpaniaguam Oct 30, 2024
2816430
feat: imadjust
cpaniaguam Oct 31, 2024
21dcdb1
test: imadjust
cpaniaguam Oct 31, 2024
e4d1710
fix: update imadjust test to remove divisor parameter
cpaniaguam Oct 31, 2024
0812474
docs: imadjust
cpaniaguam Oct 31, 2024
e6d46ba
fix: correct typo in unsharp_mask documentation
cpaniaguam Oct 31, 2024
f4cc31a
refactor: rename reconstruct_erosion to reconstruct_dilation for clarity
cpaniaguam Oct 31, 2024
102cff1
refactor: rename reconstruct_erosion; update related tests
cpaniaguam Nov 1, 2024
2522d79
refactor: get_nlabel for clarity
cpaniaguam Nov 1, 2024
e891042
refactor: rename get_nlabel to get_nlabel_relaxation
cpaniaguam Nov 1, 2024
c8ee071
refactor: add default value to factor in get_ice_labels_mask
cpaniaguam Nov 1, 2024
b295078
feat: get_nlabel with relaxation logic
cpaniaguam Nov 1, 2024
7ff502c
refactor: enhance get_ice_labels_mask with debug logging for ice pixe…
cpaniaguam Nov 1, 2024
d148341
refactor: rename morph_residue to morph_residue_labels in get_nlabel …
cpaniaguam Nov 1, 2024
d4b43b6
refactor: change thresholds to keyword argument in get_nlabel for cla…
cpaniaguam Nov 1, 2024
6a079aa
refactor: rename morph_residue to morph_residue_labels in get_nlabel_…
cpaniaguam Nov 1, 2024
d3a27a1
refactor: simplify get_ice_labels_mask calls by removing tile paramet…
cpaniaguam Nov 1, 2024
1048277
refactor: remove tile parameter from get_nlabel_relaxation and update…
cpaniaguam Nov 1, 2024
f99135a
refactor: remove tile parameter from get_nlabel and update references
cpaniaguam Nov 1, 2024
c96c9e7
feat: get_ice_masks with tiling
cpaniaguam Nov 1, 2024
65cc49c
test: add test cases for se_disk4, se_disk20, and se_disk50
cpaniaguam Nov 3, 2024
e13e78d
refactor: _generate_se
cpaniaguam Nov 3, 2024
e5f94a6
refactor: rename make_landmask_se to se_disk50 and update implementation
cpaniaguam Nov 3, 2024
b418335
test: strels
cpaniaguam Nov 3, 2024
77b4335
refactor: update _generate_se! and streamline structuring element fun…
cpaniaguam Nov 3, 2024
0df963d
feat: fillholes! get_segment_mask
cpaniaguam Nov 3, 2024
eac78cd
feat: reconstruct_erosion
cpaniaguam Nov 3, 2024
28d6da8
feat: imhist/histeq
cpaniaguam Nov 3, 2024
ed41b8d
test: histeq
cpaniaguam Nov 3, 2024
f67913d
fix: correct order of extrema variables in impose_minima function
cpaniaguam Nov 4, 2024
8755d9c
feat: parallelize gradient magnitude
cpaniaguam Nov 5, 2024
b1e437e
fix: histeq
cpaniaguam Nov 5, 2024
d4b067c
feat: add support for skimage.morphology
cpaniaguam Nov 5, 2024
3abc9da
refactor: replace MorphSE reconstruction with skimage.morphology.reco…
cpaniaguam Nov 5, 2024
4b80133
refactor: __init__
cpaniaguam Nov 5, 2024
6f22d94
refactor: replace MorphSE reconstruction with skimage.morphology.reco…
cpaniaguam Nov 5, 2024
3839982
test: update reconstruction tests for skimage compatibility
cpaniaguam Nov 5, 2024
333f44e
fix: ensure proper type conversion in imcomplement function
cpaniaguam Nov 5, 2024
e64ccf6
feat: watershed2
cpaniaguam Nov 5, 2024
32874c4
fix: revert reconstruct.jl
cpaniaguam Nov 6, 2024
00f50a4
refactor: replace MorphSE dilation with skimage's dilation in create_…
cpaniaguam Nov 6, 2024
2f16bba
feat: watershed workflows
cpaniaguam Nov 11, 2024
6cd449b
feat: add preprocessing with tiling for ice mask generation
cpaniaguam Nov 11, 2024
6d391b6
feat: enhance get_ice_masks function to return most frequent label an…
cpaniaguam Nov 11, 2024
7887778
feat: add impose_minima function for morphological reconstruction wit…
cpaniaguam Nov 11, 2024
c70fe0b
refactor: optimize imhistp function to calculate number of bins using…
cpaniaguam Nov 11, 2024
af4d9bb
feat: extend get_new3 function to include structuring element paramet…
cpaniaguam Nov 11, 2024
70d541a
feat: add rgb2gray function for converting RGB channels to grayscale …
cpaniaguam Nov 11, 2024
59d9b91
feat: add cv2 import with fallback installation for OpenCV if not pre…
cpaniaguam Nov 11, 2024
338d98f
feat: modify watershed2 function to include segment_mask parameter fo…
cpaniaguam Nov 11, 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
48 changes: 32 additions & 16 deletions src/IceFloeTracker.jl
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,36 @@ export DataFrames, DataFrame, nrow, Not, select!
export Dates, Time, Date, DateTime, @dateformat_str
export addlatlon!, getlatlon, convertcentroid!, converttounits!, dropcols!

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

function __init__()
try
copy!(cv2, pyimport("cv2"))
catch e
pyimport("subprocess").run([
"python", "-m", "pip", "install", "opencv-python==4.10.0.84"
])
end

# Import the installed modules

skimage = "scikit-image=0.24.0"
copy!(sk_measure, pyimport_conda("skimage.measure", skimage))
copy!(sk_exposure, pyimport_conda("skimage.exposure", skimage))
copy!(sk_morphology, pyimport_conda("skimage.morphology", skimage))
pyimport_conda("pyproj", "pyproj=3.6.0")
pyimport_conda("rasterio", "rasterio=1.3.7")
pyimport_conda("jinja2", "jinja2=3.1.2")
pyimport_conda("pandas", "pandas=2")
@pyinclude(joinpath(@__DIR__, "latlon.py"))
copy!(getlatlon, py"getlatlon")
return nothing
end

include("utils.jl")
include("persist.jl")
include("landmask.jl")
Expand All @@ -70,31 +100,17 @@ include("branch.jl")
include("special_strels.jl")
include("tilingutils.jl")
include("histogram_equalization.jl")
include("morph_fill.jl")
include("reconstruct.jl")


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

function get_version_from_toml(pth=dirname(dirname(pathof(IceFloeTracker))))::VersionNumber
toml = TOML.parsefile(joinpath(pth, "Project.toml"))
return VersionNumber(toml["version"])
end

const IFTVERSION = get_version_from_toml()

function __init__()
copy!(sk_measure, pyimport_conda("skimage.measure", "scikit-image=0.24.0"))
copy!(sk_exposure, pyimport_conda("skimage.exposure", "scikit-image=0.24.0"))
pyimport_conda("pyproj", "pyproj=3.6.0")
pyimport_conda("rasterio", "rasterio=1.3.7")
pyimport_conda("jinja2", "jinja2=3.1.2")
pyimport_conda("pandas", "pandas=2")
@pyinclude(joinpath(@__DIR__, "latlon.py"))
copy!(getlatlon, py"getlatlon")
return nothing
end

include("regionprops.jl")
include("segmentation_a_direct.jl")
include("segmentation_b.jl")
Expand Down
5 changes: 3 additions & 2 deletions src/bridge.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ function _bridge_operator_lut(
return _operator_lut(I, img, nhood, lutbridge)
end

function _bridge_filter(img::T, operator::Function)::T where {T<:AbstractArray{Bool}}
# TODO: see about implemting _filter using parallelization
function _filter(img::T, operator::Function)::T where {T<:AbstractArray{Bool}}
out = zeros(Bool, size(img))
R = CartesianIndices(img)
I_first, I_last = first(R), last(R)
Expand Down Expand Up @@ -79,5 +80,5 @@ julia> bridge(bw)
```
"""
function bridge(bw::T)::T where {T<:AbstractArray{Bool}}
return _bridge_filter(bw, _bridge_operator_lut)
return _filter(bw, _bridge_operator_lut)
end
17 changes: 13 additions & 4 deletions src/find_ice_labels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,15 @@ function find_reflectance_peaks(
possible_ice_threshold::Float64=Float64(75 / 255),
)::Int64
reflectance_channel[reflectance_channel .< possible_ice_threshold] .= 0 #75 / 255

_, counts = ImageContrastAdjustment.build_histogram(reflectance_channel)

locs, _ = Peaks.findmaxima(counts)
sort!(locs; rev=true)
return locs[2] # second greatest peak
Base.sort!(locs; rev=true)

length(locs) > 2 && return locs[2] # second greatest peak

return nothing
end

"""
Expand Down Expand Up @@ -73,8 +78,12 @@ function find_ice_labels(
if sum(abs.(ice_labels)) == 0
ref_image_band_2 = @view(cv[2, :, :])
ref_image_band_1 = @view(cv[3, :, :])
band_2_peak = find_reflectance_peaks(ref_image_band_2, possible_ice_threshold = possible_ice_threshold)
band_1_peak = find_reflectance_peaks(ref_image_band_1, possible_ice_threshold = possible_ice_threshold)
band_2_peak = find_reflectance_peaks(
ref_image_band_2; possible_ice_threshold=possible_ice_threshold
)
band_1_peak = find_reflectance_peaks(
ref_image_band_1; possible_ice_threshold=possible_ice_threshold
)
mask_ice_band_2 = @view(cv[2, :, :]) .> band_2_peak / 255
mask_ice_band_1 = @view(cv[3, :, :]) .> band_1_peak / 255
ice = mask_ice_band_7 .* mask_ice_band_2 .* mask_ice_band_1
Expand Down
108 changes: 76 additions & 32 deletions src/histogram_equalization.jl
Original file line number Diff line number Diff line change
@@ -1,9 +1,28 @@
function to_uint8(arr::AbstractMatrix{T}) where {T<:AbstractFloat}
img = Int.(round.(arr, RoundNearestTiesAway))
img = clamp.(img, 0, 255)
function to_uint8(arr::AbstractMatrix{T}) where {T<:Union{AbstractFloat,Int,Signed}}
img = to_uint8.(arr)
return img
end

"""
rgb2gray(rgbchannels::Array{Float64, 3})

Convert an array of RGB channel data to grayscale in the range [0, 255].
"""
function rgb2gray(rgbchannels::Array{Float64,3})
r, g, b = [to_uint8(rgbchannels[:, :, i]) for i in 1:3]
# Reusing the r array to store the equalized gray image
r .= to_uint8(0.2989 * r .+ 0.5870 * g .+ 0.1140 * b)
return r
end

function to_uint8(num::T) where {T<:Union{AbstractFloat,Int,Signed}}
num = Int(round(num, RoundNearestTiesAway))
return clamp(num, 0, 255)
end

function mat2uint8(arr::AbstractMatrix{T}) where {T<:Union{AbstractFloat,Int,Signed}}
return to_uint8.(arr)
end

function anisotropic_diffusion_3D(I)
rgbchannels = get_rgb_channels(I)
Expand All @@ -13,10 +32,11 @@ function anisotropic_diffusion_3D(I)
end

return rgbchannels

end

function anisotropic_diffusion_2D(I::AbstractMatrix{T}; gradient_threshold::Union{T,Nothing}=nothing, niter::Int=1) where {T}
function anisotropic_diffusion_2D(
I::AbstractMatrix{T}; gradient_threshold::Union{T,Nothing}=nothing, niter::Int=1
) where {T}
if eltype(I) <: Int
I = Gray.(I ./ 255)
end
Expand All @@ -34,11 +54,13 @@ function anisotropic_diffusion_2D(I::AbstractMatrix{T}; gradient_threshold::Unio

for _ in 1:niter
# These are zero-indexed offset arrays
diff_img_north = padded_img[0:end-1, 1:end-1] .- padded_img[1:end, 1:end-1]
diff_img_east = padded_img[1:end-1, 1:end] .- padded_img[1:end-1, 0:end-1]
diff_img_nw = padded_img[0:end-2, 0:end-2] .- I
diff_img_ne = padded_img[0:end-2, 2:end] .- I
diff_img_sw = padded_img[2:end, 0:end-2] .- I
diff_img_north =
padded_img[0:(end - 1), 1:(end - 1)] .- padded_img[1:end, 1:(end - 1)]
diff_img_east =
padded_img[1:(end - 1), 1:end] .- padded_img[1:(end - 1), 0:(end - 1)]
diff_img_nw = padded_img[0:(end - 2), 0:(end - 2)] .- I
diff_img_ne = padded_img[0:(end - 2), 2:end] .- I
diff_img_sw = padded_img[2:end, 0:(end - 2)] .- I
diff_img_se = padded_img[2:end, 2:end] .- I

# Exponential conduction coefficients
Expand All @@ -49,7 +71,6 @@ function anisotropic_diffusion_2D(I::AbstractMatrix{T}; gradient_threshold::Unio
conduct_coeff_sw = exp.(-(abs.(diff_img_sw) ./ gradient_threshold) .^ 2)
conduct_coeff_se = exp.(-(abs.(diff_img_se) ./ gradient_threshold) .^ 2)


# Flux calculations
flux_north = conduct_coeff_north .* diff_img_north
flux_east = conduct_coeff_east .* diff_img_east
Expand All @@ -59,42 +80,40 @@ function anisotropic_diffusion_2D(I::AbstractMatrix{T}; gradient_threshold::Unio
flux_se = conduct_coeff_se .* diff_img_se

# Back to regular 1-indexed arrays
flux_north_diff = flux_north[1:end-1, :] .- flux_north[2:end, :]
flux_east_diff = flux_east[:, 2:end] .- flux_east[:, 1:end-1]
flux_north_diff = flux_north[1:(end - 1), :] .- flux_north[2:end, :]
flux_east_diff = flux_east[:, 2:end] .- flux_east[:, 1:(end - 1)]

# Discrete PDE solution
sum_ = (1 / (dd^2)) .* (flux_nw .+ flux_ne .+ flux_sw .+ flux_se)
I = I .+ diffusion_rate .* (flux_north_diff .- flux_north_diff .+ sum_)

end

return I
end


function imshow(img)
if typeof(img) <: BitMatrix
return Gray.(img)
end
Gray.(img ./ 255)
return Gray.(img ./ 255)
end



function adapthisteq(img::Matrix{T}, nbins=256, clip=0.01) where {T}
# Step 1: Normalize the image to [0, 1] based on its own min and max
image_min, image_max = minimum(img), maximum(img)
image_min, image_max = extrema(img)
normalized_image = (img .- image_min) / (image_max - image_min)

# Step 2: Apply adaptive histogram equalization. equalize_adapthist handles the tiling to 1/8 of the image size (equivalent to 8x8 blocks in MATLAB)
equalized_image = sk_exposure.equalize_adapthist(
normalized_image,
normalized_image;
clip_limit=clip, # Equivalent to MATLAB's 'ClipLimit'
nbins=nbins # Number of histogram bins. 255 is used to match the default in MATLAB script
nbins=nbins, # Number of histogram bins. 255 is used to match the default in MATLAB script
)

# Step 3: Rescale the image back to the original range [image_min, image_max]
final_image = sk_exposure.rescale_intensity(equalized_image, in_range="image", out_range=(image_min, image_max))
final_image = sk_exposure.rescale_intensity(
equalized_image; in_range="image", out_range=(image_min, image_max)
)

# Convert back to the original data type if necessary
final_image = to_uint8(final_image)
Expand All @@ -120,10 +139,9 @@ function get_rgb_channels(img)
greenc = green.(img) * 255
bluec = blue.(img) * 255

return cat(redc, greenc, bluec, dims=3)
return cat(redc, greenc, bluec; dims=3)
end


function _process_image_tiles(
true_color_image,
clouds_red,
Expand All @@ -136,6 +154,7 @@ function _process_image_tiles(
# Apply diffuse (anisotropic diffusion) to each channel of true color image
true_color_diffused = IceFloeTracker.diffusion(float64.(true_color_image), 0.1, 75, 3)

# Get uint8 channels
rgbchannels = get_rgb_channels(true_color_diffused)

# For each tile, compute the entropy in the false color tile, and the fraction of white and black pixels
Expand All @@ -153,10 +172,9 @@ function _process_image_tiles(
end
end

return rgbchannels
return (equalized_gray=rgb2gray(rgbchannels), gammagreen=rgbchannels[:, :, 2])
end


"""
conditional_histeq(
true_color_image,
Expand Down Expand Up @@ -192,7 +210,7 @@ function conditional_histeq(
white_threshold::AbstractFloat=25.5,
white_fraction_threshold::AbstractFloat=0.4,
)
tiles = get_tiles(true_color_image, rblocks=rblocks, cblocks=cblocks)
tiles = get_tiles(true_color_image; rblocks=rblocks, cblocks=cblocks)
rgbchannels_equalized = _process_image_tiles(
true_color_image,
clouds_red,
Expand All @@ -203,7 +221,6 @@ function conditional_histeq(
)

return rgbchannels_equalized

end

"""
Expand All @@ -227,7 +244,6 @@ function conditional_histeq(
white_threshold::AbstractFloat=25.5,
white_fraction_threshold::AbstractFloat=0.4,
)

side_length = IceFloeTracker.get_optimal_tile_size(side_length, size(true_color_image))

tiles = IceFloeTracker.get_tiles(true_color_image, side_length)
Expand All @@ -242,16 +258,16 @@ function conditional_histeq(
)

return rgbchannels_equalized

end

function _get_false_color_cloudmasked(; false_color_image,
function _get_false_color_cloudmasked(;
false_color_image,
prelim_threshold=110.0,
band_7_threshold=200.0,
band_2_threshold=190.0,
)
mask_cloud_ice, clouds_view = IceFloeTracker._get_masks(
false_color_image,
false_color_image;
prelim_threshold=prelim_threshold,
band_7_threshold=band_7_threshold,
band_2_threshold=band_2_threshold,
Expand Down Expand Up @@ -279,3 +295,31 @@ Convert an RGB image to grayscale in the range [0, 255].
function rgb2gray(img::Matrix{RGB{Float64}})
return round.(Int, Gray.(img) * 255)
end

# TODO: make imadjust more general to work with Gray{Float64} and RGB{Float64} types
"""
imadjust(img; low, high)

Adjust the contrast of an image using linear stretching. The image is normalized to [0, 1] and then stretched to the range [low, high].

# Arguments
- `img`: The input image.
- `low`: The lower bound of the stretched image. Default is 0.01.
- `high`: The upper bound of the stretched image. Default is 0.99.

# Returns

The contrast-adjusted image in the range [0, 255].
"""
function imadjust(
img::AbstractArray{<:Integer}; low::T=0.01, high::T=0.99
)::Matrix{Int} where {T<:AbstractFloat}
img = img ./ 255
imgflat = vec(img)
plow = StatsBase.percentile(imgflat, low * 100)
phigh = StatsBase.percentile(imgflat, high * 100)

f = LinearStretching((plow, phigh) => (0.0, 1.0))

return to_uint8(adjust_histogram(img, f) * 255)
end
6 changes: 3 additions & 3 deletions src/landmask.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ function create_landmask(
fill_value_upper::Int=2000,
) where {T<:AbstractMatrix}
landmask_binary = binarize_landmask(landmask_image)
dilated = IceFloeTracker.MorphSE.dilate(landmask_binary, centered(struct_elem))
dilated = sk_morphology.dilation(landmask_binary, centered(struct_elem))
return (
dilated=ImageMorphology.imfill(.!dilated, (fill_value_lower, fill_value_upper)),
non_dilated=.!landmask_binary,
Expand Down Expand Up @@ -52,7 +52,7 @@ Zero out pixels in land and soft ice regions on truecolor image, return RGB imag

# Arguments
- `input_image`: truecolor RGB image
- `landmask_binary`: binary landmask with 1=land, 0=water/ice
- `landmask_binary`: binary landmask with 1=land, 0=water/ice

"""
function apply_landmask(input_image::AbstractMatrix, landmask_binary::BitMatrix)
Expand All @@ -69,7 +69,7 @@ end
"""
remove_landmask(landmask, ice_mask)

Find the pixel indexes that are floating ice rather than soft or land ice. Returns an array of pixel indexes.
Find the pixel indexes that are floating ice rather than soft or land ice. Returns an array of pixel indexes.

# Arguments
- `landmask`: bitmatrix landmask for region of interest
Expand Down
Loading