A lightweight, high-performance Python library for continuous wavelet transform (CWT) using Morlet wavelet filter bank, with GPU computing support.
❗ Note this project is still in early development.
This Python library provides an implementation of the Morlet wavelet transform, a powerful time-frequency analysis method that offers an intuitive approach to understanding signal characteristics. The implementation is inspired by the pioneering work of French geophysicist Jean Morlet, leveraging his original, highly intuitive formulation which laid the foundation for the Continuous Wavelet Transform (CWT).
Unlike more abstract modern formulations of the CWT, Jean Morlet's method is deeply rooted in physical intuition, making it particularly accessible for comprehending how signals vary in frequency over time.
- Intuitive Implementation: Directly implements Morlet's original and physically intuitive formulation of the wavelet transform, as detailed in his 1982 papers.
- High Performance: Supports computations on both the CPU and GPU, enabling efficient processing of large datasets and high-frequency signals.
- Pythonic Design: Developed as a user-friendly Python library, making it accessible for researchers and developers.
If you use uv
as your Python package manager, you can install MorletX using the following command:
# Install as a Git dependency source using `uv` (recommended)
$ uv add git+https://github.com/nimanzik/MorletX
If you prefer to use pip
, you need to install it from source:
# Install from source using `pip`
$ git clone https://github.com/nimanzik/MorletX.git
$ cd MorletX
$ pip install .
To compute the scalogram (the output of the CWT):
import numpy as np
from morletx.core import MorletFilterBank
>>> data = ... # some signal data
>>> fs = ... # sampling frequency of the signal
>>> filter_bank = MorletFilterBank(
... n_octaves=8, # Number of octaves to cover
... n_intervals=4, # Number of intervals (filters) per octave
... shape_ratio=5, # Shape ratio of the Morlet wavelet
... duration=2.0, # Duration of the Morlet wavelet
... sampling_freq=fs, # Sampling frequency of the signal
... array_engine="cupy", # Choices: "numpy" or "cupy"
... )
>>> mode = "magnitude" # Choices: "magnitude", "power", "complex"
>>> scalogram = filter_bank.transform(data, mode=mode, detach_from_device=True)
Switching between CPU and GPU computation is as simple as changing the array_engine
parameter to either "numpy"
or "cupy"
.
-
For CPU computation (and storing the results as NumPy arrays):
>>> filter_bank = MorletFilterBank(..., array_engine="numpy")
-
For GPU computation (and storing the results as CuPy arrays):
>>> filter_bank = MorletFilterBank(..., array_engine="cupy")
When using GPU for computation, the results will be returned as so-called "device arrays". To move the results to the host (CPU) memory, use the detach_from_device
parameter:
>>> filter_bank = MorletFilterBank(..., array_engine="cupy")
>>> scalogram_as_numpy = filter_bank.transform(..., detach_from_device=True)
By default, the detach_from_device
parameter is set to False
, meaning the results will be stored as device arrays when using GPU for computation (note that it has no effect on CPU computation).
Both PyTorch and CuPy support __cuda_array_interface__
, so zero-copy data exchange between CuPy and PyTorch can be achieved at no cost.
The only requirement is that the tensor must be already on GPU before exchanging data. Therefore, make sure that detach_from_device=False
(which is the default behavior) when doing the transformation.
PyTorch supports zero-copy data exchange through DLPack
, so you can get the results as a PyTorch tensor as follows:
>>> import torch
>>> filter_bank = MorletFilterBank(..., array_engine="cupy")
>>> scalogram_as_cupy = filter_bank.transform(..., detach_from_device=False)
>>> scalogram_as_torch = torch.from_dlpack(scalogram_as_cupy)
There are quick-and-ready methods to visualise both the filter bank and the computed scalogram.
- To visualise the scalogram:
>>> import matplotlib.pyplot as plt
>>> fig_sgram, ax_sgram = plt.subplots()
>>> filter_bank.plot_scalogram(ax=ax_sgram, scalogram=scalogram, mode=mode)
- To visualise the frequency responses of the filter bank:
>>> fig_fbank, ax_fbank = plt.subplots()
>>> filter_bank.plot_responses(ax=ax_fbank, n_fft=512)
Here is an example of the computed scalogram for a signal with a sampling frequency of 16 kHz:
A significant innovation introduced by Morlet is the shape ratio,
❗ This section will be expanded and detailed in the future.
This example shows how to use MorletX
to compute the wavelet transform of an acoustic Fin-Whale signal. The marimo notebook for this example is also available for interactive exploration.
Report any issues or bugs on GitHub Issues.
- Nima Nooshiri – [email protected]