Project-OSmOSE / OSEkit

OSEkit is an open source suite of tools written in python and dedicated to the management and analysis of data in underwater passive acoustics.
https://osmose.ifremer.fr
Other
3 stars 2 forks source link

Pc add custom scales in spectrogram generation #148

Closed PaulCarvaillo closed 3 months ago

PaulCarvaillo commented 5 months ago

Added functionalities for image scaling before processing. Integrates with spectrogram.py and works seemlessly with spectrogram.process_all_files()

With option spectrogram.custom_scale, the y-axis of the plot is projected to a non-linear y-axis that can be defined in a Scale class. Current options for custom_scales are "log","Audible","Dual_HF_BF", "porp_delph", to be tuned.

Each Scale class also acts as a converter as it inherits from an AbstractScale that has 4 methods: map_freq2scale , map_scale2freq, bbox2scale and scale2bbox. This allows to add new custom scales that will be compatible as long as they have these methods.

Each scale configuration is stored in ScaleSerializer that allows to create custom_scale objects:

expected usage:

    scale = ScaleSerializer().get_scale("porp_delph",sr=312500)
    mapped_freq = np.vectorize(scale.map_freq2scale)(freq)
    plt.pcolormesh(time, freq_custom, log_spectro, cmap=color_map)

to add more scales:

class ScaleSerializer:
    def __init__(self):
        self.configurations = {
            "porp_delph": (
                CustomScale,
                {"frequencies": (22000, 100000), "coefficients": (0.5, 0.2, 0.3)},
            ),
            "Audible": (
                CustomScale,
                {"frequencies": (22000, 100000), "coefficients": (1, 0, 0)},
            ),
            "Dual_LF_HF": (
                CustomScale,
                {"frequencies": (22000, 100000), "coefficients": (0.5, 0, 0.5)},
            ),
            # Add more configurations here
        }

Images generated using spectrogram.process_file(path) and spectrogram.custom_scale = option :

Original file: image

"Audible": Linear scale from 0 to 22kHz (It's only a frequency zoom) Capture d'écran 2024-04-29 215050

"porp-delph": 50% of the display for 0-22kHz, 20% of display for 22kHz-100kHz, 30% of display for 100kHz-SR/2 Capture d'écran 2024-04-29 215358

"Dual_LF_HF": 50% inferior is 0-22kHz, 50% superior is 100kHz-SR/2 Capture d'écran 2024-04-29 215015

Minimal unittests added in test_scales.py

Also added import pwd in a try-except statement in core_utils as is bugs on my local windows environment (maybe should move out of this PR)

TODO: If this modification is considered, we should store custom_scale variable somewhere spec generation metadata. TODO: Add a common method get_yticks in AbstractScale to get nice yticks for display is needed.

PaulCarvaillo commented 4 months ago

If I generate spectrograms for a dataset using

nfft 2048 window_size 2048, overlap 50, with linear scale

==> it will go in a folder named 2048_2048_50

if I also want to generate

nfft 2048 window_size 2048, overlap 50, with PORP-DELPH scale

==> it will try to write in same folder

Currently on MMERMAID, I am trying to quantify the improvement by comparing my model with one scale/the other/both scales, ==> If custom scales are added to Osekit we should find a way to organize these custom-scale spectros.

@cazaudo How would you handle this ? Maybe it's time for an "Augmentation" folder where we can add stretched and distorted images with corresponding labels ? or just add an underscores to a new folder ? (e.g 2048_2048_50_porpdelph or 2048_2048_50_log)

cazaudo commented 4 months ago

i have just made some first tests in local with the creation of a custom scale and everything worked as expected , nice piece of code @PaulCarvaillo ! just made two little commits so your variable fits current definition templates (with a default decorator and value)

just a question : would you be ok to add the term "frequency" to "scale" , "scale" alone i find it a bit ambiguous..

for what concerns the destination folder I suggest to create other folders at the same level as /processed/spectrograms , which is the default linear one eg for "log" we would create /processed/log_spectrograms , subfolders remaining the same inside ..

or indeed we can also add subfolders inside spectrograms as you suggest "e.g 2048_2048_50_porpdelph or 2048_2048_50_log", i like it also perhaps cleaner , anyone an opinion on this ?

cazaudo commented 4 months ago

anyway once this is decided I will take care of coding it and merging your first PR:)

PaulCarvaillo commented 4 months ago

Thanks for the review, 'Frequency_scale' seems good to me ! I think this still needs a few tests, not sure I covered all edge cases and some coefficient combos might still do 0 division errors?

cazaudo commented 3 months ago

@PaulCarvaillo i have made two new commits to be able to merge your PR asap after acceptance by the team of folder naming

PCarvaillo commented 3 months ago

Pytest failing line 161 of test_spectrogam.py: list_welch = list(dataset.path.joinpath(OSMOSE_PATH.welch, "3_44100").glob("*.npz"))

Maybe something to change in OSMOSE_PATH ? Unclear to me what's going wrong.

cazaudo commented 3 months ago

Pytest failing line 161 of test_spectrogam.py: list_welch = list(dataset.path.joinpath(OSMOSE_PATH.welch, "3_44100").glob("*.npz"))

Maybe something to change in OSMOSE_PATH ? Unclear to me what's going wrong.

to be honest most of current tests are deprecated .. it will be one of my main tasks for next July

cazaudo commented 3 months ago

@PaulCarvaillo tell me if OK for you for merging ? i will make integration of documentation and tests work in july

PCarvaillo commented 3 months ago

OK for me.

I will probably make two other PRs soon for: 1- Adding y-ticks to the frequency scale for the APLOSE display. 2- Refining the boundaries of the porpdelph scale based on available literature.