# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['curvepy']

package_data = \
{'': ['*']}

install_requires = \
['Cython>=3.0.0',
 'matplotlib>=3.10.8,<4.0.0',
 'numpy>=2.4.0,<3.0.0',
 'pywavelets>=1.9.0,<2.0.0',
 'scikit-image>=0.26.0,<0.27.0',
 'setuptools>=65.0.0']

setup_kwargs = {
    'name': 'curvepy-fdct',
    'version': '1.1.0',
    'description': 'A Pure Python Clean-Room Implementation of the Fast Discrete Curvelet Transform',
    'long_description': '# CurvePy: Fast Discrete Curvelet Transform (FDCT)\n\n![Python](https://img.shields.io/badge/python-3.11%2B-blue)\n![License](https://img.shields.io/badge/license-MIT-green)\n![Status](https://img.shields.io/badge/status-active-success)\n\n**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](chrome-extension://efaidnbmnnnibpcajpcglclefindmkaj/https://math.mit.edu/icg/papers/FDCT.pdf)\n\nUnlike 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.\n\n---\n\n## Results\n\n### Denoising\n\n### Grayscale images (2-D)\n\n### RGB images (3-D)\nCurvepy uses a color denoising engine that operatues in the YUV color space (chosen to preserve structural details) while filtering chromatic noise.\n\n## Features\n* **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)$).\n* **Color Support:** wrapper specifically for RGB images using **YUV** separation. Processing Luma and Chroma channels independently.\n* **Thresholding Design:** \n    * **Soft Thresholding:** For artifact-free restoration.\n    * **Monte Carlo Calibration:** Estimates noise levels per wedge and per scale.\n* **Module Design:** Separates geometry computations, windowing and filtering logic into different modules.\n\n---\n\n## Installation\nClone the repository and install the dependencies:\n\n```bash\ngit clone [https://github.com/yourusername/curvepy.git](https://github.com/yourusername/curvepy.git)\ncd curvepy\npip install -r requirements.txt\n```\n\n## Usage\n1. Basic Transformation (Grayscale image)\n```python\nimport matpolotlib.pyplot as plt\nimport skimage.data as data\nfrom curvepy.curvepy import CurveletFrequencyGrid\n\n\n# Initialize the Transformation engine (512x512 grid, 4 scales)\nfdct = CurveletFrequencyGrid(N=512, scales=4)\n\n# Load Image\nimage = data.camera()\n\n# 1. Forward Transform\ncoefficients = fdct.forward_transform(image)\n\n# 2. Inverse Transform\nreconstructed_image = fdct.inverse_transform(coefficients)\n\n# 3. Plot results\nplt.figure(figsize=(12,4))\nplt.suptitle("Original vs Reconstructed Image")\n\nplt.subplot(1,2,1)\nplt.imshow(image, plt.cmap.gray)\nplt.title("Original")\n\nplt.subplot(1,2,2)\nplt.imshow(reconstructed_image, plt.cmap.gray)\nplt.title("Restored Image")\n\nplt.tight_layout()\nplt.show()\n```\n\n2. Basic Transformation and Denoising (Grayscale image)\n```python\nimport matpolotlib.pyplot as plt\nimport skimage.data as data\nfrom curvepy.curvepy import CurveletFrequencyGrid\nfrom curvepy.denoise import CurveletDenoise\n\n\n# Initialize the Transformation engine (512x512 grid, 4 scales)\nfdct = CurveletFrequencyGrid(N=512, scales=4)\ndenoise_engine = CurveletDenoise(fdct)\n\n# Load Image\nimage = data.camera()\n\n# 1. Forward Transform\ncoefficients = fdct.forward_transform(image)\n\n# 2. Denoise\nrestored_img = denoise_engine.denoise(noisy_img, sigma, multiplier)\npsnr = denoise_engine.calculate_psnr_rgb(img, restored_img)\n\n# 3. Plot results\nplt.figure(figsize=(12,4))\nplt.suptitle(f"Original vs restored img for soft thresholding, PSNR = {psnr:.2f} dB, multiplier = {multiplier}")\n\nplt.subplot(1, 2, 1)\nplt.imshow(img, cmap=plt.cm.gray)\nplt.title("original img")\n\nplt.subplot(1, 2, 2)\nplt.imshow(restored_img, cmap=plt.cm.gray)\nplt.title("restored img")\n\nplt.tight_layout()\nplt.show()\n```\n\n3. Denoising a Color Image\n```python\nimport matplotlib.pyplot as plt\nimport skimage.data as data\nfrom curvepy.curvepy import CurveletFrequencyGrid\nfrom curvepy.denoise import ColorCuerveletDenoise\n\n# Setup\nfdct = CurveletFrequencyGrid(N=512, scales=4)\ndenoise_engine = CuerveletDenoise(fdct)\nimage = denoise_engine.normalize_img(data.astronaut())\n\n# Apply denoising\nrestored_image = denoise_engine.denoise(image, sigma=0.1, multiplier=1.5)\npsnr = denoise_engine.calculate_psnr_rgb(image, restored_image) # Calculate peak SNR value between two images\n\n# Plot results\nplt.figure(figsize=(12,4))\nplt.suptitle(f"Original vs Restored image via Soft Thresholding, PSNR = {psnr:.2f} dB, multiplier = {multiplier}")\n\nplt.subplot(1, 2, 1)\nplt.imshow(img)\nplt.title("original img")\n\nplt.subplot(1, 2, 2)\nplt.imshow(restored_img)\nplt.title("restored img")\n\nplt.tight_layout()\nplt.show()\n```\n\n## Project Structure\ncurvepy/\\\n├── curvepy.py       # Core Engine: FDCT & IFDCT implementation \\\n├── windows.py       # Math: Meyer Window functions (Phi, Psi, V) \\\n├── filters.py       # Tools: Thresholding logic & Monte Carlo calibration \\\n└── denoise.py        # App: YUV Color wrapper & Denoising pipeline\n\n## Theory + How it Works:\nStandard 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.\n\n**Curvelets** are anisotropic "needles" that exist at different scales and angles\n* **Scales:** Captures details of different sizes.\n* **Angles:** Capture the direction of the geometry.\n\nThis 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 :)\n\nTo understand why this matters, we visualized the inner workings of the transform below:\n\nImage\n![Astronaut Image](https://raw.githubusercontent.com/noahmunrokagan/curvepy/refs/heads/master/assets/astronaut.png)\nTransformation Process for Above Image\n![Curvelet Theory Grid](https://raw.githubusercontent.com/noahmunrokagan/curvepy/refs/heads/master/assets/curvelet_theory_image.png) \n\n\n### The 4-Step Pipeline (Explained)\n\n1.  **The Map (Top-Left):**\n    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).\n    * *Center (Grey):* Low-frequency background (blurry shapes).\n    * *Outer Rings (Colors):* High-frequency details (sharp edges).\n\n2.  **The Filter (Top-Right):**\n    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."\n\n3.  **The Needle (Bottom-Left):**\n    This is what that single filter looks like in the real world (Spatial Domain). It isn\'t a point—it\'s a **Needle**.\n\n4.  **The Response (Bottom-Right):**\n    When we drag this Needle across a real photo (The Astronaut), it lights up (turns black) *only* where it finds matching geometry.\n    * Notice the top of the helmet and the flag stripes are clearly visible because they are horizontal.\n    * The vertical rocket boosters in the background are invisible. The horizontal needle doesn\'t notice the vertical lines.\n\n**Summary:** By adding up thousands of these "Needles" at every possible angle and size, CurvePy reconstructs the perfect image—minus the noise.\n\n## Licence\nMIT Licence. Free to use for academic and personal projects.',
    'author': 'Noah Munro-Kagan',
    'author_email': 'noahmunrokagan@gmail.com',
    'maintainer': 'None',
    'maintainer_email': 'None',
    'url': 'https://github.com/noahmunrokagan/curvepy',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.11',
}
from build import *
build(setup_kwargs)

setup(**setup_kwargs)
