Skip to main content

A Pure Python Clean-Room Implementation of the Fast Discrete Curvelet Transform

Project description

CurvePy: Fast Discrete Curvelet Transform (FDCT)

Python License Status

CurvePy is a pure Python "clean room" implementation of the Fast Discrete Curvelet Transform via Uniform Wrapping outlined in Emmanuel Candès 2005 paper Fast Discrete Curvelet Transforms

Unlike wavelets, which represent images using points, Curvelets represent images using oriented edges. This makes CurvePy (or the FDCT) a powerful tool for sparse representations of smooth curves, and highly effective at denoising while preserving sharp boundaries.


Results

Denoising

Grayscale images (2-D)

RGB images (3-D)

Curvepy uses a color denoising engine that operatues in the YUV color space (chosen to preserve structural details) while filtering chromatic noise.

Features

  • Fast Discrete Curvelet Transform (FDCT): Implemented via the "wrapping" method outlined in Candès et al. 2005 for computational efficiency ($O(N^2 \log N)$).
  • Color Support: wrapper specifically for RGB images using YUV separation. Processing Luma and Chroma channels independently.
  • Thresholding Design:
    • Soft Thresholding: For artifact-free restoration.
    • Monte Carlo Calibration: Estimates noise levels per wedge and per scale.
  • Module Design: Separates geometry computations, windowing and filtering logic into different modules.

Installation

Clone the repository and install the dependencies:

git clone [https://github.com/yourusername/curvepy.git](https://github.com/yourusername/curvepy.git)
cd curvepy
pip install -r requirements.txt

Usage

  1. Basic Transformation (Grayscale image)
import matpolotlib.pyplot as plt
import skimage.data as data
from curvepy.curvepy import CurveletFrequencyGrid


# Initialize the Transformation engine (512x512 grid, 4 scales)
fdct = CurveletFrequencyGrid(N=512, scales=4)

# Load Image
image = data.camera()

# 1. Forward Transform
coefficients = fdct.forward_transform(image)

# 2. Inverse Transform
reconstructed_image = fdct.inverse_transform(coefficients)

# 3. Plot results
plt.figure(figsize=(12,4))
plt.suptitle("Original vs Reconstructed Image")

plt.subplot(1,2,1)
plt.imshow(image, plt.cmap.gray)
plt.title("Original")

plt.subplot(1,2,2)
plt.imshow(reconstructed_image, plt.cmap.gray)
plt.title("Restored Image")

plt.tight_layout()
plt.show()
  1. Basic Transformation and Denoising (Grayscale image)
import matpolotlib.pyplot as plt
import skimage.data as data
from curvepy.curvepy import CurveletFrequencyGrid
from curvepy.denoise import CurveletDenoise


# Initialize the Transformation engine (512x512 grid, 4 scales)
fdct = CurveletFrequencyGrid(N=512, scales=4)
denoise_engine = CurveletDenoise(fdct)

# Load Image
image = data.camera()

# 1. Forward Transform
coefficients = fdct.forward_transform(image)

# 2. Denoise
restored_img = denoise_engine.denoise(noisy_img, sigma, multiplier)
psnr = denoise_engine.calculate_psnr_rgb(img, restored_img)

# 3. Plot results
plt.figure(figsize=(12,4))
plt.suptitle(f"Original vs restored img for soft thresholding, PSNR = {psnr:.2f} dB, multiplier = {multiplier}")

plt.subplot(1, 2, 1)
plt.imshow(img, cmap=plt.cm.gray)
plt.title("original img")

plt.subplot(1, 2, 2)
plt.imshow(restored_img, cmap=plt.cm.gray)
plt.title("restored img")

plt.tight_layout()
plt.show()
  1. Denoising a Color Image
import matplotlib.pyplot as plt
import skimage.data as data
from curvepy.curvepy import CurveletFrequencyGrid
from curvepy.denoise import ColorCuerveletDenoise

# Setup
fdct = CurveletFrequencyGrid(N=512, scales=4)
denoise_engine = CuerveletDenoise(fdct)
image = denoise_engine.normalize_img(data.astronaut())

# Apply denoising
restored_image = denoise_engine.denoise(image, sigma=0.1, multiplier=1.5)
psnr = denoise_engine.calculate_psnr_rgb(image, restored_image) # Calculate peak SNR value between two images

# Plot results
plt.figure(figsize=(12,4))
plt.suptitle(f"Original vs Restored image via Soft Thresholding, PSNR = {psnr:.2f} dB, multiplier = {multiplier}")

plt.subplot(1, 2, 1)
plt.imshow(img)
plt.title("original img")

plt.subplot(1, 2, 2)
plt.imshow(restored_img)
plt.title("restored img")

plt.tight_layout()
plt.show()

Project Structure

curvepy/
├── curvepy.py # Core Engine: FDCT & IFDCT implementation
├── windows.py # Math: Meyer Window functions (Phi, Psi, V)
├── filters.py # Tools: Thresholding logic & Monte Carlo calibration
└── denoise.py # App: YUV Color wrapper & Denoising pipeline

Theory + How it Works:

Standard 2D Wavelets are isotropic, ie. they treat all directions equally. This doens't work well for images where the curves/outlines of the shapes are what are important to be preserved. This creates a blocky or ringing effect when trying to represent a smooth curve.

Curvelets are anisotropic "needles" that exist at different scales and angles

  • Scales: Captures details of different sizes.
  • Angles: Capture the direction of the geometry.

This allows CurvePy to represent a curved edge with very few coefficients relative to the aformentioneed 2D wavelets, making it ideal for compression and restoration tasks while preserving the geometry is important :)

To understand why this matters, we visualized the inner workings of the transform below:

Image Astronaut Image Transformation Process for Above Image Curvelet Theory Grid

The 4-Step Pipeline (Explained)

  1. The Map (Top-Left): The Frequency Plane is tiled into "Wedges." Each wedge represents a specific combination of Scale (how thick the feature is) and Angle (which way it points).

    • Center (Grey): Low-frequency background (blurry shapes).
    • Outer Rings (Colors): High-frequency details (sharp edges).
  2. The Filter (Top-Right): To detect specific features, we activate just one single wedge. In this example, we selected Scale 3, Wedge 0. This filter is specialized to find "Medium-Fine details that are Horizontal."

  3. The Needle (Bottom-Left): This is what that single filter looks like in the real world (Spatial Domain). It isn't a point—it's a Needle.

  4. The Response (Bottom-Right): When we drag this Needle across a real photo (The Astronaut), it lights up (turns black) only where it finds matching geometry.

    • Notice the top of the helmet and the flag stripes are clearly visible because they are horizontal.
    • The vertical rocket boosters in the background are invisible. The horizontal needle doesn't notice the vertical lines.

Summary: By adding up thousands of these "Needles" at every possible angle and size, CurvePy reconstructs the perfect image—minus the noise.

Licence

MIT Licence. Free to use for academic and personal projects.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

curvepy_fdct-1.0.1.tar.gz (15.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

curvepy_fdct-1.0.1-py3-none-any.whl (15.0 kB view details)

Uploaded Python 3

File details

Details for the file curvepy_fdct-1.0.1.tar.gz.

File metadata

  • Download URL: curvepy_fdct-1.0.1.tar.gz
  • Upload date:
  • Size: 15.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.2.1 CPython/3.11.9 Darwin/24.6.0

File hashes

Hashes for curvepy_fdct-1.0.1.tar.gz
Algorithm Hash digest
SHA256 d272af2fe856c0aea1e6bf0337448a3c664ecddae48a547db87f063736a355bb
MD5 705928d70dd83a5ff7a16cdd72e6116e
BLAKE2b-256 45f043f3644dbf5b9c608bc6c39e7b34b531c52497ad23bdb6794931ef808d89

See more details on using hashes here.

File details

Details for the file curvepy_fdct-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: curvepy_fdct-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 15.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.2.1 CPython/3.11.9 Darwin/24.6.0

File hashes

Hashes for curvepy_fdct-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 1a58b855309db690c2833aa0a02db9a83603c2c7d7897ddfd7369ad086f51839
MD5 1bac33254b74f5a26cf445c528a54551
BLAKE2b-256 2f9ee80884aef47dfe7758b32a088a5982b58ccb0bc61712313c51edc7aac528

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page