Skip to main content

Crate nauticuvs

Crate nauticuvs 

Source
Expand description

§Fast Discrete Curvelet Transform (FDCT)

A pure-Rust implementation of the Fast Discrete Curvelet Transform based on the algorithm of Candès, Demanet, Donoho & Ying (2006).

§What are curvelets?

Curvelets are a multi-scale, multi-directional frame that provides near-optimal sparse representations of images with smooth edges along curves — making them ideal for sonar imagery, seismic data, and medical imaging.

Unlike wavelets (which capture point singularities well), curvelets capture curve singularities with far fewer coefficients. At scale 2^{-j}, curvelets have width ~ 2^{-j} and length ~ 2^{-j/2}, obeying the parabolic scaling relation: width ≈ length².

§Quick start

use ndarray::Array2;
use nauticuvs::{curvelet_forward, curvelet_inverse};

let image = Array2::<f32>::zeros((256, 256));
let coeffs = curvelet_forward(&image, 5).unwrap();
let reconstructed = curvelet_inverse(&coeffs).unwrap();

§Choosing the number of scales

The num_scales parameter (2–10) controls how many frequency octaves the decomposition produces:

num_scalesDetail scalesTypical use
31Small images (≤ 64), fast previews
4–52–3General purpose, most applications
6–84–6Large images (512+), fine-grained analysis

A good 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. Thresholding removes noise while preserving edges:

let noisy_image = Array2::<f32>::zeros((128, 128));
let mut coeffs = curvelet_forward(&noisy_image, 4).unwrap();

// Hard thresholding: zero out small coefficients
coeffs.hard_threshold(0.1);

// Or soft thresholding: shrink magnitudes toward zero
// coeffs.soft_threshold(0.1);

let denoised = curvelet_inverse(&coeffs).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.

§Custom configuration

The default uses 32 directions at the finest detail scale, halving every two scales. You can override this:

use nauticuvs::{curvelet_forward_config, curvelet_inverse, CurveletConfig};

let config = CurveletConfig::new(5).unwrap()
    .with_finest_directions(64).unwrap();   // 64 directions at finest

let image = Array2::<f32>::zeros((256, 256));
let coeffs = curvelet_forward_config(&image, &config).unwrap();
let reconstructed = curvelet_inverse(&coeffs).unwrap();

Or specify directions for every detail scale explicitly:

let config = CurveletConfig::new(5).unwrap()
    .with_directions_per_scale(vec![8, 16, 32]).unwrap();
// 3 detail scales: 8, 16, 32 directions respectively

§Image fusion

Combine two images by selecting the strongest curvelet coefficient at each position (max-abs fusion rule):

let image_a = Array2::<f32>::zeros((128, 128));
let image_b = Array2::<f32>::zeros((128, 128));

let coeffs_a = curvelet_forward(&image_a, 4).unwrap();
let mut fused = curvelet_forward(&image_b, 4).unwrap();

// For each detail subband, keep the coefficient with larger magnitude
for (s, scale_a) in coeffs_a.detail.iter().enumerate() {
    for (d, dir_a) in scale_a.iter().enumerate() {
        for (target, source) in fused.detail[s][d].iter_mut().zip(dir_a.iter()) {
            if source.norm() > target.norm() {
                *target = *source;
            }
        }
    }
}

let fused_image = curvelet_inverse(&fused).unwrap();

§RGB / multi-channel images

The API operates on single-channel Array2<f32>. For multi-channel images, transform each channel independently:

let mut coeffs_r = curvelet_forward(&red, 4).unwrap();
let mut coeffs_g = curvelet_forward(&green, 4).unwrap();
let mut coeffs_b = curvelet_forward(&blue, 4).unwrap();

// Process each channel (e.g., denoise)
coeffs_r.hard_threshold(0.05);
coeffs_g.hard_threshold(0.05);
coeffs_b.hard_threshold(0.05);

let r = curvelet_inverse(&coeffs_r).unwrap();
let g = curvelet_inverse(&coeffs_g).unwrap();
let b = curvelet_inverse(&coeffs_b).unwrap();

§Coefficient structure

The CurveletCoeffs struct stores three tiers:

┌────────────────────────────────────────────┐
│ coarse   (1 isotropic subband, low-freq)   │
├────────────────────────────────────────────┤
│ detail[0]  (N₁ directional subbands)       │
│ detail[1]  (N₂ directional subbands)       │
│   ...                                      │
├────────────────────────────────────────────┤
│ fine     (1 isotropic subband, high-freq)  │
└────────────────────────────────────────────┘

Each subband is an Array2<Complex<f64>> of the same size as the (padded) input. Thresholding operates only on detail coefficients, leaving coarse and fine subbands untouched to preserve low-frequency content and fine texture.

§Parallelism

Enable the parallel feature to process directional subbands concurrently via rayon:

[dependencies]
nauticuvs = { version = "0.1", features = ["parallel"] }

§Precision

  • Public API: accepts Array2<f32> images, returns Array2<f32>.
  • Internal computation: all FFTs, windows, and coefficients use f64.
  • Reconstruction accuracy: relative L2 error < 10⁻⁶ for unmodified coefficients, verified from 16×16 to 64×64 with 2–5 scales.

§Error handling

All fallible operations return Result<T, CurveletError>:

§References

  • Candès, Demanet, Donoho, Ying (2006). “Fast Discrete Curvelet Transforms.” Multiscale Modeling & Simulation, 5(3), 861–899.
  • Candès & Donoho (2004). “New tight frames of curvelets and optimal representations of objects with piecewise C² singularities.”

Structs§

CurveletCoeffs
Multi-scale, multi-directional curvelet coefficients (f64 precision).
CurveletConfig
Configuration for the curvelet transform.

Enums§

CurveletError
Errors produced by curvelet transform operations.

Functions§

curvelet_forward
Compute the forward curvelet transform with default configuration.
curvelet_forward_config
Compute the forward curvelet transform with custom configuration.
curvelet_inverse
Compute the inverse curvelet transform (reconstruction).