nauticuvs
Nauticuvs — Fast Discrete Curvelet Transform (FDCT) — a pure-Rust implementation for 2D image analysis.
Published on crates.io as part of the CESARops ecosystem — supporting volunteer Search & Rescue (SAR) teams.
What are curvelets?
Curvelets are a multi-scale, multi-directional frame designed to efficiently represent images with edges along smooth curves. They obey parabolic scaling:
width ≈ length²
This makes them dramatically more efficient than wavelets for representing:
- Side-scan sonar imagery (seafloor features, shadows, edges)
- Seismic data (reflectors, fault lines)
- Medical images (tissue boundaries)
- Any image with curvilinear singularities
Quick start
use Array2;
use ;
// Grayscale image as a 2D f32 array
let image = zeros;
// Forward transform (5 scales)
let coeffs = curvelet_forward.unwrap;
// Inverse transform — reconstruction is perfect (< 1e-6 relative error)
let reconstructed = curvelet_inverse.unwrap;
Choosing the number of scales
The num_scales parameter (2–10) controls frequency octave decomposition:
num_scales |
Detail scales | Typical use |
|---|---|---|
| 3 | 1 | Small images (≤ 64), fast previews |
| 4–5 | 2–3 | General purpose, most applications |
| 6–8 | 4–6 | Large images (512+), fine-grained analysis |
Rule of thumb: num_scales ≈ log₂(min(rows, cols)) - 2
Denoising
Curvelets are particularly effective for denoising. Noise spreads uniformly across curvelet coefficients, while edges concentrate energy in a few large coefficients:
let mut coeffs = curvelet_forward.unwrap;
// Hard thresholding — zero out small detail coefficients
coeffs.hard_threshold;
// Or soft thresholding — shrink magnitudes toward zero
// coeffs.soft_threshold(0.1);
let denoised = curvelet_inverse.unwrap;
Threshold selection: A common choice is the universal threshold σ · √(2 · ln(N)),
where σ is the noise standard deviation and N is the total number of pixels.
Both threshold methods only affect detail subbands — coarse (low-frequency) and fine (high-frequency) scales are preserved.
Image fusion
Combine two images by selecting the strongest curvelet coefficient at each position:
let coeffs_a = curvelet_forward.unwrap;
let mut fused = curvelet_forward.unwrap;
// Max-abs fusion: keep the stronger coefficient at each position
for in coeffs_a.detail.iter.enumerate
let fused_image = curvelet_inverse.unwrap;
Custom configuration
Control the number of angular directions per scale:
use ;
// 64 directions at finest detail scale (default is 32)
let config = new.unwrap
.with_finest_directions.unwrap;
let coeffs = curvelet_forward_config.unwrap;
Or specify directions for every detail scale explicitly:
// 5 scales = 3 detail scales
let config = new.unwrap
.with_directions_per_scale.unwrap;
Direction counts must be ≥ 4 and multiples of 4.
RGB / multi-channel images
The API operates on single-channel Array2<f32>. For RGB, transform each channel independently:
let mut coeffs_r = curvelet_forward.unwrap;
let mut coeffs_g = curvelet_forward.unwrap;
let mut coeffs_b = curvelet_forward.unwrap;
// Denoise each channel
coeffs_r.hard_threshold;
coeffs_g.hard_threshold;
coeffs_b.hard_threshold;
let r = curvelet_inverse.unwrap;
let g = curvelet_inverse.unwrap;
let b = curvelet_inverse.unwrap;
Coefficient structure
For num_scales = 5, the coefficient hierarchy is:
┌────────────────────────────────────────────┐
│ coarse (1 isotropic subband, low-freq) │ ← scale 0
├────────────────────────────────────────────┤
│ detail[0] (16 directional subbands) │ ← scale 1
│ detail[1] (16 directional subbands) │ ← scale 2
│ detail[2] (32 directional subbands) │ ← scale 3
├────────────────────────────────────────────┤
│ fine (1 isotropic subband, high-freq) │ ← scale 4
└────────────────────────────────────────────┘
Each subband is an Array2<Complex<f64>>. Accessing individual subbands:
let coeffs = curvelet_forward.unwrap;
// Coarse (low-frequency) subband
let coarse = &coeffs.coarse;
// Detail scale 1, direction 5
let subband = &coeffs.detail;
// Iterate all detail coefficients
for in coeffs.detail.iter.enumerate
// Total coefficient count
println!;
Parallelism
Enable the parallel feature to process directional subbands concurrently via rayon:
[]
= { = "0.1", = ["parallel"] }
This parallelizes:
- Window construction (per direction)
- Subband extraction in the forward transform
- Subband reconstruction in the inverse transform
Precision
- Public API: accepts
Array2<f32>images, returnsArray2<f32> - Internal computation: all FFTs, windows, and coefficients use
f64 - Reconstruction: relative L2 error < 10⁻⁶ for unmodified coefficients, tested on 16×16 through 64×64 with 2–5 scales
Error handling
All operations return Result<T, CurveletError>:
| Error | Cause |
|---|---|
ZeroDimension |
Image has 0 rows or columns |
NonFiniteInput |
Image contains NaN or Inf |
InvalidScaleCount |
num_scales not in [2, 10] |
InvalidDirectionCount |
Direction count < 4 or not a multiple of 4 |
DirectionCountMismatch |
Wrong number of entries in per-scale direction list |
InconsistentCoeffs |
Coefficient array dimensions don't match config |
Dependencies
| Crate | Purpose |
|---|---|
ndarray |
N-dimensional array operations |
rustfft |
FFT implementation |
num-complex |
Complex number arithmetic |
thiserror |
Error types |
rayon |
Optional parallelization (parallel feature) |
Project Philosophy
This crate is a core component of the CESARops (Civilian Emergency Search and Rescue Operations Platform) and Sonar Sniffer projects. Our mission is to provide powerful, professional-grade tools to Search and Rescue (SAR) teams at no cost.
- Free for SAR: This library, along with Sonar Sniffer and the CESARops platform, will always be free for civilian and volunteer SAR organizations.
- Community Driven: We believe in open-source, privacy-first software built by the SAR community, for the SAR community.
- Sustainable Development: To fund further development, we may offer this software under a commercial license to the public or for-profit entities. The revenue generated will support the maintenance and enhancement of the free tools available to SAR teams.
This nauticuvs crate provides the signal processing foundation for advanced sonar and image analysis, which is critical for features like drift prediction and object detection in challenging environments.
Contributing
We are actively looking for contributors to help finish and expand the CESARops ecosystem. Whether you are a developer, a designer, a SAR professional, or just someone with a passion for helping others, there are many ways to get involved.
Please see our main repository for contribution guidelines and to learn more about how you can help.
Sponsors / Donate
nauticuvs and the CESARops ecosystem are free for volunteer SAR teams, forever. If you'd like to help keep the lights on, any contribution is appreciated.
| Platform | Link |
|---|---|
| Cash App | $nautidogsailing |
| GitHub Sponsors | github.com/sponsors/festeraeb |
References
- Candès, E.J., Demanet, L., Donoho, D.L., Ying, L. (2006). "Fast Discrete Curvelet Transforms." Multiscale Modeling & Simulation, 5(3), 861–899.
- Candès, E.J. & Donoho, D.L. (2004). "New tight frames of curvelets and optimal representations of objects with piecewise C² singularities." Comm. Pure Appl. Math., 57(2), 219–266.
License
MIT OR Apache-2.0