From cb07a63196f64e19c6293d107cd26f1eefe44d17 Mon Sep 17 00:00:00 2001 From: cpaniaguam Date: Wed, 6 Nov 2024 17:03:04 -0500 Subject: [PATCH 1/5] feat: brighten --- src/IceFloeTracker.jl | 1 + src/brighten.jl | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 src/brighten.jl diff --git a/src/IceFloeTracker.jl b/src/IceFloeTracker.jl index c4f95353..aaebfcbd 100644 --- a/src/IceFloeTracker.jl +++ b/src/IceFloeTracker.jl @@ -71,6 +71,7 @@ include("branch.jl") include("special_strels.jl") include("tilingutils.jl") include("histogram_equalization.jl") +include("brighten.jl") const sk_measure = PyNULL() diff --git a/src/brighten.jl b/src/brighten.jl new file mode 100644 index 00000000..634dd22e --- /dev/null +++ b/src/brighten.jl @@ -0,0 +1,28 @@ +""" + get_brighten_mask(equalized_gray_reconstructed_img, gamma_green) +# Arguments +- `equalized_gray_reconstructed_img`: The equalized gray reconstructed image (uint8 in Matlab). +- `gamma_green`: The gamma value for the green channel (also uint8). +# Returns +Difference equalized_gray_reconstructed_img - gamma_green clamped between 0 and 255. +""" +function get_brighten_mask(equalized_gray_reconstructed_img, gamma_green) + return to_uint8(equalized_gray_reconstructed_img - gamma_green) +end + +""" + imbrighten(img, brighten_mask, bright_factor) +Brighten the image using a mask and a brightening factor. +# Arguments +- `img`: The input image. +- `brighten_mask`: A mask indicating the pixels to brighten. +- `bright_factor`: The factor by which to brighten the pixels. +# Returns +- The brightened image. +""" +function imbrighten(img, brighten_mask, bright_factor) + img = Float64.(img) + brighten_mask = brighten_mask .> 0 + img[brighten_mask] .= img[brighten_mask] * bright_factor + return img = to_uint8(img) +end From 95d4c27ee9440cf76221a3b8358c0bc25a5df5a7 Mon Sep 17 00:00:00 2001 From: cpaniaguam Date: Wed, 6 Nov 2024 17:03:19 -0500 Subject: [PATCH 2/5] test: brighten --- test/test-brighten.jl | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 test/test-brighten.jl diff --git a/test/test-brighten.jl b/test/test-brighten.jl new file mode 100644 index 00000000..2b29fd88 --- /dev/null +++ b/test/test-brighten.jl @@ -0,0 +1,22 @@ +using IceFloeTracker: get_brighten_mask, imbrighten + +@testset "brighten tests" begin + @testset "get_brighten_mask" begin + img = rand(0:255, 5, 5) + bumped_img = img .+ 1 + mask = get_brighten_mask(img, bumped_img) + @test all(mask .== 0) + end + + @testset "imbrighten tests" begin + img = [1 2; 3 4] + brighten_mask = [1 0; 1 0] + + test_cases = [(1.25, [1 2; 4 4]), (0.1, [0 2; 0 4]), (0.9, img)] + + for (bright_factor, expected_result) in test_cases + result = imbrighten(img, brighten_mask, bright_factor) + @test result == expected_result + end + end +end \ No newline at end of file From 89d480d5c738b813e970b4564bf0600817b6977b Mon Sep 17 00:00:00 2001 From: cpaniaguam Date: Wed, 6 Nov 2024 17:03:50 -0500 Subject: [PATCH 3/5] fix: fix to_uint8 for int matrices --- src/histogram_equalization.jl | 54 +++++++++++++++++------------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/histogram_equalization.jl b/src/histogram_equalization.jl index 5ade5ab3..d4441d6f 100644 --- a/src/histogram_equalization.jl +++ b/src/histogram_equalization.jl @@ -4,6 +4,10 @@ function to_uint8(arr::AbstractMatrix{T}) where {T<:AbstractFloat} return img end +function to_uint8(arr::AbstractMatrix{T}) where {T<:Integer} + img = clamp.(arr, 0, 255) + return img +end function anisotropic_diffusion_3D(I) rgbchannels = get_rgb_channels(I) @@ -13,10 +17,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 @@ -34,11 +39,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 @@ -49,7 +56,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 @@ -59,28 +65,24 @@ 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) @@ -88,13 +90,15 @@ function adapthisteq(img::Matrix{T}, nbins=256, clip=0.01) where {T} # 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) @@ -120,10 +124,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, @@ -156,7 +159,6 @@ function _process_image_tiles( return rgbchannels end - """ conditional_histeq( true_color_image, @@ -192,7 +194,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, @@ -203,7 +205,6 @@ function conditional_histeq( ) return rgbchannels_equalized - end """ @@ -227,7 +228,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) @@ -242,16 +242,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, From d0926da71d349aeef2cfaf6fde31bb94b774b9f3 Mon Sep 17 00:00:00 2001 From: Carlos Paniagua Date: Mon, 11 Nov 2024 15:04:11 -0500 Subject: [PATCH 4/5] Update IceFloeTracker.jl --- src/IceFloeTracker.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/IceFloeTracker.jl b/src/IceFloeTracker.jl index 116d1036..5481f345 100644 --- a/src/IceFloeTracker.jl +++ b/src/IceFloeTracker.jl @@ -74,6 +74,7 @@ include("histogram_equalization.jl") include("brighten.jl") include("morph_fill.jl") include("imcomplement.jl") +include("imadjust.jl") const sk_measure = PyNULL() const sk_exposure = PyNULL() From 7de87c3f36ee156d9485529f6dac2c2d3cd81056 Mon Sep 17 00:00:00 2001 From: Carlos Paniagua Date: Mon, 11 Nov 2024 15:46:21 -0500 Subject: [PATCH 5/5] fix: test for imadjust --- test/test-imadjust.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test-imadjust.jl b/test/test-imadjust.jl index e2acdcf2..470e880c 100644 --- a/test/test-imadjust.jl +++ b/test/test-imadjust.jl @@ -1,5 +1,5 @@ @testset "imadjust" begin Random.seed!(123) img = rand(0:255, 100, 100) - sum(IceFloeTracker.imadjust(img)) == 1291155 + @test sum(IceFloeTracker.imadjust(img)) == 1291155 end