Skip to main content

SWT (Stationary Wavelet Transform) Best Practices Guide

Overview

The Stationary Wavelet Transform (SWT), also known as the Undecimated Wavelet Transform or À Trous Algorithm, provides shift-invariant wavelet analysis through VectorWave's VectorWaveSwtAdapter. This guide covers best practices for using SWT with automatic internal optimizations.

Key Features

Automatic Optimizations (No API Changes Required)

  1. Lazy Filter Caching: Upsampled filters for à trous algorithm are populated on first use and cached per adapter
  2. Parallel Processing: Large signals (≥4096 samples) automatically use multi-threading
  3. Memory Efficiency: Sparse storage available for mostly-zero coefficients
  4. Resource Management: Built-in cleanup for long-running applications

Quick Start

// Preferred: try-with-resources for deterministic cleanup
try (VectorWaveSwtAdapter swt = new VectorWaveSwtAdapter(
Daubechies.DB4, PaddingStrategies.PERIODIC)) {
// Forward transform
MutableMultiLevelMODWTResult result = swt.forward(signal, 4);
// Inverse transform
double[] reconstructed = swt.inverse(result);
} // executor and caches are released here

Best Practices

1. Choosing Decomposition Levels

The optimal number of decomposition levels depends on signal length and wavelet filter length:

int calculateOptimalLevels(int signalLength, int filterLength) {
// Rule: (filterLength - 1) * (2^level - 1) < signalLength
int maxLevel = 1;
while ((filterLength - 1) * (Math.pow(2, maxLevel) - 1) < signalLength) {
maxLevel++;
}

// Practical limit: 3-6 levels is usually sufficient
return Math.min(maxLevel - 1, 6);
}

Guidelines:

  • Short signals (< 512): 2-3 levels
  • Medium signals (512-4096): 3-5 levels
  • Long signals (> 4096): 4-6 levels
  • More levels = more computational cost with diminishing returns

2. Denoising Strategies

Universal Threshold (Automatic)

Best for general-purpose denoising when noise characteristics are unknown:

// Universal threshold calculated as σ√(2log(N))
double[] denoised = swt.denoise(noisySignal, 4, -1, true);

Custom Threshold

When you know the noise level:

double threshold = 0.2; // Based on known noise characteristics
double[] denoised = swt.denoise(noisySignal, 4, threshold, true);

Level-Specific Thresholding

For optimal results when different scales have different noise levels:

MutableMultiLevelMODWTResult result = swt.forward(noisySignal, 4);

// Apply different thresholds at different levels
swt.applyThreshold(result, 1, 0.3, true); // Finest details - higher threshold
swt.applyThreshold(result, 2, 0.2, true);
swt.applyThreshold(result, 3, 0.1, true);
swt.applyThreshold(result, 4, 0.05, true); // Coarsest details - lower threshold

double[] denoised = swt.inverse(result);

3. Memory Management

For Dense Signals

Standard usage is fine for most signals:

VectorWaveSwtAdapter swt = new VectorWaveSwtAdapter(wavelet);
MutableMultiLevelMODWTResult result = swt.forward(signal, levels);

For Sparse Signals

Use sparse storage for signals with many near-zero coefficients:

// Create SWT result
SWTResult denseResult = new SWTResult(approx, details, levels);

// Convert to sparse (3x compression typical)
SWTResult.SparseSWTResult sparseResult = denseResult.toSparse(0.01);

// Reconstruct when needed
SWTResult reconstructed = sparseResult.toFull();

4. Performance Optimization

Reuse Adapters

Create once, use multiple times:

// Good: Reuse adapter (lazy caches; dedicated executor when needed)
try (VectorWaveSwtAdapter swt = new VectorWaveSwtAdapter(Daubechies.DB4)) {
for (double[] signal : signals) {
MutableMultiLevelMODWTResult result = swt.forward(signal, 3);
// Process result
}
} // executor and caches are released here

// Bad: Creating new adapter each time
for (double[] signal : signals) {
VectorWaveSwtAdapter swt = new VectorWaveSwtAdapter(Daubechies.DB4);
// ... inefficient
}

Batch Processing

Process multiple signals efficiently:

try (VectorWaveSwtAdapter swt = new VectorWaveSwtAdapter(wavelet)) {
// Process batch
List<MutableMultiLevelMODWTResult> results = new ArrayList<>();
for (double[] signal : signalBatch) {
results.add(swt.forward(signal, levels));
}
} // resources released here

5. Resource Management

Always clean up in long-running applications:

// Prefer try-with-resources for deterministic cleanup
try (VectorWaveSwtAdapter swt = new VectorWaveSwtAdapter(wavelet)) {
processSignals(swt);
}

Notes:

  • Caches are populated lazily on first use and reused per adapter instance.
  • A dedicated executor may be initialized on demand for large transforms and is shut down on close(); the adapter remains functional after cleanup but may forgo these optimizations until reinitialized.

6. Wavelet Selection

Different wavelets suit different applications:

WaveletBest ForCharacteristics
HaarSharp transitionsShortest filter, fastest
DB2-DB4General purposeGood time-frequency balance
DB6-DB8Smooth signalsBetter frequency resolution
SYM4-SYM8Symmetric featuresNear-linear phase
COIF1-COIF3Vanishing momentsGood for polynomial trends

7. Boundary Handling

Choose appropriate padding strategy:

// Periodic - best for periodic signals
new VectorWaveSwtAdapter(wavelet, PaddingStrategies.PERIODIC);

// Symmetric - best for finite signals
new VectorWaveSwtAdapter(wavelet, PaddingStrategies.SYMMETRIC);

// Zero padding - simple but can introduce artifacts
new VectorWaveSwtAdapter(wavelet, PaddingStrategies.ZERO);

Common Use Cases

Signal Denoising

public double[] denoiseSignal(double[] noisySignal) {
try (VectorWaveSwtAdapter swt = new VectorWaveSwtAdapter(
Daubechies.DB6, PaddingStrategies.SYMMETRIC)) {
// Automatic universal threshold
return swt.denoise(noisySignal, 4, -1, true);
}
}

Feature Extraction

public double[] extractFeatures(double[] signal, int targetLevel) {
try (VectorWaveSwtAdapter swt = new VectorWaveSwtAdapter(
Symlet.SYM4, PaddingStrategies.PERIODIC)) {
// Extract features at specific scale
return swt.extractLevel(signal, 5, targetLevel);
}
}

Multi-Resolution Analysis

public void analyzeSignal(double[] signal) {
try (VectorWaveSwtAdapter swt = new VectorWaveSwtAdapter(Daubechies.DB4)) {
MutableMultiLevelMODWTResult result = swt.forward(signal, 4);
// Analyze each level
for (int level = 1; level <= 4; level++) {
double[] details = result.getMutableDetailCoeffs(level);
double energy = calculateEnergy(details);
System.out.printf("Level %d energy: %.4f\n", level, energy);
}
}
}

Performance Characteristics

Automatic Optimization Triggers

Signal SizeProcessing ModeExpected Performance
< 1024SequentialFast, low overhead
1024-4096Sequential + CacheOptimal for repeated transforms
≥ 4096Parallel + Cache2-4x speedup on multi-core

Memory Usage

  • Base overhead: O(signal_length × levels)
  • With sparse storage: Reduced by compression ratio (typically 3-10x)
  • Filter cache: Negligible (< 1KB per level)

Troubleshooting

Issue: Out of Memory

Solution: Use fewer decomposition levels or sparse storage:

// Reduce levels
swt.forward(signal, 3); // Instead of 6

// Or use sparse storage
SWTResult.SparseSWTResult sparse = result.toSparse(0.01);

Issue: Slow Performance

Solution: Check signal size and reuse adapters:

// Ensure signal is large enough for parallel processing
if (signal.length < 4096) {
// Consider batching smaller signals
}

// Reuse adapter for multiple transforms
VectorWaveSwtAdapter swt = createSharedAdapter();

Issue: Poor Denoising Results

Solution: Tune threshold strategy:

// Try different threshold approaches
// 1. Adjust threshold value
double customThreshold = estimateNoiseLevel(signal) * 3;

// 2. Use soft vs hard thresholding
swt.denoise(signal, levels, threshold, true); // soft
swt.denoise(signal, levels, threshold, false); // hard

// 3. Level-specific thresholds
applyAdaptiveThresholds(result);

Advanced Topics

Custom Filter Wavelets

While the library handles optimization automatically, you can create custom wavelets:

public class CustomWavelet implements DiscreteWavelet {
// Implementation details
}

// SWT adapter automatically optimizes custom wavelets
VectorWaveSwtAdapter swt = new VectorWaveSwtAdapter(new CustomWavelet());

Monitoring Optimization Status

For debugging and performance tuning:

Map<String, Object> stats = swt.getCacheStatistics();
System.out.println("Filter cache size: " + stats.get("filterCacheSize"));
System.out.println("Parallel active: " + stats.get("parallelExecutorActive"));

Summary

The SWT implementation in VectorWave provides:

  1. Automatic optimizations - No special API needed
  2. Shift-invariant analysis - Better for pattern detection
  3. Perfect reconstruction - Numerically stable
  4. Flexible denoising - Multiple threshold strategies
  5. Memory efficiency - Sparse storage options
  6. Resource management - Built-in cleanup

Follow these best practices to get optimal performance and results from SWT in your applications.