├── data ├── Lena.jpg └── barbara.jpg ├── diffcurve ├── utils.py ├── fdct2d │ ├── fdct_wrapping_window.m │ ├── numpy_frontend.py │ ├── torch_frontend.py │ ├── jax_frontend.py │ ├── curvelet_2d.py │ ├── fdct_wrapping.m │ └── ifdct_wrapping.m └── plot_utils.py ├── setup.py ├── .gitignore ├── README.md └── environment.yml /data/Lena.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liutianlin0121/diffcurve/HEAD/data/Lena.jpg -------------------------------------------------------------------------------- /data/barbara.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/liutianlin0121/diffcurve/HEAD/data/barbara.jpg -------------------------------------------------------------------------------- /diffcurve/utils.py: -------------------------------------------------------------------------------- 1 | '''Shared utility functions''' 2 | 3 | from pathlib import Path 4 | import numpy as np 5 | 6 | def get_project_root() -> Path: 7 | '''Get the root dir of the project''' 8 | return Path(__file__).parent.parent -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from setuptools import setup 3 | 4 | setup(name='diffcurve', 5 | version='0.1', 6 | description='diffcurve', 7 | author='Tianlin Liu', 8 | author_email='t.liu@unibas.ch', 9 | license='MIT', 10 | packages=['diffcurve'], 11 | zip_safe=False) 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .ipynb_checkpoints/ 2 | *.pyc 3 | __pycache__/ 4 | deprecated/ 5 | .DS_Store 6 | */.ipynb_checkpoints/* 7 | */. DS_Store/* 8 | .db 9 | .owncloudsync.log 10 | .db-shm 11 | .db-wal 12 | .sync_5de6efbf0140.db-shm 13 | *.h5 14 | *.pth 15 | __pycache__/ 16 | *.egg-info/ 17 | .installed.cfg 18 | *.egg 19 | *.pyc 20 | .vscode/ 21 | **/diffcurve.egg-info 22 | __pycache__/ 23 | /save_curvelet_systems.ipynb 24 | /experimental -------------------------------------------------------------------------------- /diffcurve/fdct2d/fdct_wrapping_window.m: -------------------------------------------------------------------------------- 1 | function [wl,wr] = fdct_wrapping_window(x) 2 | 3 | % fdct_wrapping_window.m - Creates the two halves of a C^inf compactly supported window 4 | % 5 | % Inputs 6 | % x vector or matrix of abscissae, the relevant ones from 0 to 1 7 | % 8 | % Outputs 9 | % wl,wr vector or matrix containing samples of the left, resp. right 10 | % half of the window 11 | % 12 | % Used at least in fdct_wrapping.m and ifdct_wrapping.m 13 | % 14 | % By Laurent Demanet, 2004 15 | 16 | wr = zeros(size(x)); 17 | wl = zeros(size(x)); 18 | x(abs(x) < 2^-52) = 0; 19 | wr((x > 0) & (x < 1)) = exp(1-1./(1-exp(1-1./x((x > 0) & (x < 1))))); 20 | wr(x <= 0) = 1; 21 | wl((x > 0) & (x < 1)) = exp(1-1./(1-exp(1-1./(1-x((x > 0) & (x < 1)))))); 22 | wl(x >= 1) = 1; 23 | normalization = sqrt(wl.^2 + wr.^2); 24 | wr = wr ./ normalization; 25 | wl = wl ./ normalization; 26 | 27 | -------------------------------------------------------------------------------- /diffcurve/fdct2d/numpy_frontend.py: -------------------------------------------------------------------------------- 1 | '''2D discrete curvelet transform in numpy''' 2 | import numpy as np 3 | from numpy import fft 4 | 5 | 6 | def perform_fft2(spatial_input: np.ndarray): 7 | """Perform fast fourier transform in 2D. 8 | The ifftshift and fftshift shift the origin of the signal. See 9 | http://www.cvl.isy.liu.se/education/undergraduate/tsbb14/laborationerFiler/Lab3_student.pdf 10 | """ 11 | 12 | return fft.fftshift(fft.fft2(fft.ifftshift(spatial_input), norm='ortho')) 13 | 14 | 15 | def perform_ifft2(frequency_input: np.ndarray): 16 | """Perform inverse fast fourier transform in 2D. 17 | The ifftshift and fftshift shift the origin of the signal. See 18 | http://www.cvl.isy.liu.se/education/undergraduate/tsbb14/laborationerFiler/Lab3_student.pdf 19 | """ 20 | return fft.fftshift(fft.ifft2(fft.ifftshift(frequency_input), 21 | norm='ortho')) 22 | 23 | 24 | def numpy_fdct_2d(img, curvelet_system): 25 | """2d fast discrete curvelet in numpy 26 | 27 | Args: 28 | img: 2D array 29 | curvelet_system: curvelet waveforms in the frequency domain 30 | 31 | Returns: 32 | coeffs: curvelet coefficients 33 | """ 34 | x_freq = perform_fft2(img) 35 | conj_curvelet_system = np.conj(curvelet_system) 36 | coeffs = perform_ifft2(x_freq * conj_curvelet_system) 37 | return coeffs 38 | 39 | 40 | def numpy_ifdct_2d(coeffs, curvelet_system, curvelet_support_size): 41 | """2d inverse fast discrete curvelet in numpy 42 | 43 | Args: 44 | coeffs: curvelet coefficients 45 | curvelet_system: curvelet waveforms in the frequency domain 46 | curvelet_support_size: size of the support of each curvelet wedge 47 | 48 | Returns: 49 | decom: image decomposed in different scales and orientation in the 50 | curvelet basis. 51 | """ 52 | 53 | coeffs_freq = perform_fft2(coeffs) 54 | 55 | decomp = perform_ifft2( 56 | coeffs_freq * curvelet_system)\ 57 | * np.expand_dims(curvelet_support_size, [1, 2]) 58 | return decomp 59 | -------------------------------------------------------------------------------- /diffcurve/fdct2d/torch_frontend.py: -------------------------------------------------------------------------------- 1 | '''2D discrete curvelet transform in torch''' 2 | import torch 3 | 4 | 5 | def torch_perform_fft2(spatial_input): 6 | """Perform fast fourier transform in 2D. 7 | The ifftshift and fftshift shift the origin of the signal. See 8 | http://www.cvl.isy.liu.se/education/undergraduate/tsbb14/laborationerFiler/Lab3_student.pdf 9 | """ 10 | 11 | return torch.fft.fftshift(torch.fft.fft2(torch.fft.ifftshift( 12 | spatial_input), norm='ortho')) 13 | 14 | 15 | def torch_perform_ifft2(frequency_input): 16 | """Perform inverse fast fourier transform in 2D. 17 | The ifftshift and fftshift shift the origin of the signal. See 18 | http://www.cvl.isy.liu.se/education/undergraduate/tsbb14/laborationerFiler/Lab3_student.pdf 19 | """ 20 | return torch.fft.fftshift(torch.fft.ifft2(torch.fft.ifftshift( 21 | frequency_input), norm='ortho')) 22 | 23 | 24 | def torch_fdct_2d(img, curvelet_system): 25 | """2d fast discrete curvelet in torch 26 | 27 | Args: 28 | img: 2D array 29 | curvelet_system: curvelet waveforms in the frequency domain 30 | 31 | Returns: 32 | coeffs: curvelet coefficients 33 | """ 34 | x_freq = torch_perform_fft2(img) 35 | conj_curvelet_system = torch.conj(curvelet_system) 36 | coeffs = torch_perform_ifft2(x_freq * conj_curvelet_system) 37 | coeffs = torch_perform_ifft2(x_freq * conj_curvelet_system) 38 | 39 | return coeffs 40 | 41 | 42 | def torch_ifdct_2d(coeffs, curvelet_system, curvelet_support_size): 43 | """2d inverse fast discrete curvelet in torch 44 | 45 | Args: 46 | coeffs: curvelet coefficients 47 | curvelet_system: curvelet waveforms in the frequency domain 48 | curvelet_support_size: size of the support of each curvelet wedge 49 | 50 | Returns: 51 | decom: image decomposed in different scales and orientation in the 52 | curvelet basis. 53 | """ 54 | coeffs_freq = torch_perform_fft2(coeffs) 55 | unsqueezed_support_size = curvelet_support_size[..., None, None] 56 | decom = torch_perform_ifft2( 57 | coeffs_freq * curvelet_system) * unsqueezed_support_size 58 | return decom 59 | -------------------------------------------------------------------------------- /diffcurve/fdct2d/jax_frontend.py: -------------------------------------------------------------------------------- 1 | '''2D discrete curvelet transform in jax''' 2 | import jax.numpy as jnp 3 | from jax.config import config 4 | config.update("jax_enable_x64", True) 5 | 6 | 7 | def jax_perform_fft2(spatial_input): 8 | """Perform fast fourier transform in 2D. 9 | The ifftshift and fftshift shift the origin of the signal. See 10 | http://www.cvl.isy.liu.se/education/undergraduate/tsbb14/laborationerFiler/Lab3_student.pdf 11 | """ 12 | return jnp.fft.fftshift(jnp.fft.fft2(jnp.fft.ifftshift(spatial_input), 13 | norm='ortho')) 14 | 15 | 16 | def jax_perform_ifft2(frequency_input): 17 | """Perform inverse fast fourier transform in 2D. 18 | The ifftshift and fftshift shift the origin of the signal. See 19 | http://www.cvl.isy.liu.se/education/undergraduate/tsbb14/laborationerFiler/Lab3_student.pdf 20 | """ 21 | return jnp.fft.fftshift(jnp.fft.ifft2(jnp.fft.ifftshift(frequency_input), 22 | norm='ortho')) 23 | 24 | 25 | def jax_fdct_2d(img, curvelet_system): 26 | """2d fast discrete curvelet in jax 27 | 28 | Args: 29 | img: 2D array 30 | curvelet_system: curvelet waveforms in the frequency domain 31 | 32 | Returns: 33 | coeffs: curvelet coefficients 34 | """ 35 | x_freq = jax_perform_fft2(img) 36 | conj_curvelet_system = jnp.conj(curvelet_system) 37 | coeffs = jax_perform_ifft2(x_freq * conj_curvelet_system) 38 | return coeffs 39 | 40 | 41 | def jax_ifdct_2d(coeffs, curvelet_system, curvelet_support_size): 42 | """2d inverse fast discrete curvelet in jax 43 | 44 | Args: 45 | coeffs: curvelet coefficients 46 | curvelet_system: curvelet waveforms in the frequency domain 47 | curvelet_support_size: size of the support of each curvelet wedge 48 | 49 | Returns: 50 | decom: image decomposed in different scales and orientation in the 51 | curvelet basis. 52 | """ 53 | coeffs_freq = jax_perform_fft2(coeffs) 54 | 55 | decomp = jax_perform_ifft2( 56 | coeffs_freq * curvelet_system)\ 57 | * jnp.expand_dims(curvelet_support_size, [1, 2]) 58 | return decomp 59 | -------------------------------------------------------------------------------- /diffcurve/fdct2d/curvelet_2d.py: -------------------------------------------------------------------------------- 1 | '''Get curvelet system''' 2 | from pathlib import Path 3 | import numpy as np 4 | import matlab.engine 5 | from diffcurve import utils 6 | from diffcurve.fdct2d.numpy_frontend import perform_fft2 7 | 8 | project_root = utils.get_project_root() 9 | 10 | 11 | def get_curvelet_system(img_length: int, img_width: int, dct_kwargs): 12 | """get curvelet waveforms in the frequency domain. 13 | 14 | Waveforms in the spatial domain can be recovered in the following way: 15 | 16 | from diffcurve.fdct2d.numpy_frontend import perform_ifft2 17 | curvelet_idx = 0 18 | perform_ifft2(curvelet_system[curvelet_idx]) 19 | 20 | Args: 21 | img_length (int): the length of the image to be curvelet transformed 22 | img_width (int): the width of the image to be curvelet transformed 23 | dct_kwargs (int): settings for the curvelet transforms 24 | 25 | Returns: 26 | _type_: _description_ 27 | """ 28 | eng = matlab.engine.start_matlab() 29 | eng.cd(str(Path.joinpath(project_root, "diffcurve/fdct2d"))) 30 | 31 | zeros = np.zeros((img_length, img_width)) 32 | zero_coeffs = eng.fdct_wrapping(zeros, 33 | dct_kwargs['is_real'], 34 | dct_kwargs['finest'], 35 | dct_kwargs['nbscales'], 36 | dct_kwargs['nbangles_coarse']) 37 | 38 | all_scales_all_wedges_curvelet_coeffs = [] 39 | curvelet_coeff_dim = [] 40 | for (scale_idx, curvelets_scale) in enumerate(zero_coeffs): 41 | for (wedge_idx, curvelet_wedge) in enumerate(curvelets_scale): 42 | coeff_length = len(curvelet_wedge) 43 | coeff_width = len(curvelet_wedge[0]) 44 | curvelet_coeff_dim.append((coeff_length, coeff_width)) 45 | coord_vert = int(coeff_length / 2) 46 | coord_horiz = int(coeff_width / 2) 47 | 48 | tmp = zero_coeffs 49 | tmp[scale_idx][wedge_idx][coord_vert][coord_horiz] = 1 50 | out = np.array(eng.ifdct_wrapping( 51 | tmp, float(dct_kwargs['is_real']), 52 | float(img_length), float(img_width))) 53 | out = perform_fft2(out) 54 | all_scales_all_wedges_curvelet_coeffs.append(out) 55 | tmp[scale_idx][wedge_idx][coord_vert][coord_horiz] = 0 56 | 57 | all_scales_all_wedges_curvelet_coeffs = np.array( 58 | all_scales_all_wedges_curvelet_coeffs) 59 | curvelet_coeff_dim = np.array(curvelet_coeff_dim) 60 | return all_scales_all_wedges_curvelet_coeffs, curvelet_coeff_dim 61 | 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Diffcurve: differentiable curvelet transform 5 | ===================================== 6 | 7 | 8 | Diffcurve is a Python library that integrates the curvelet transform into differentiable programming pipelines such as PyTorch and JAX. The curvelet transform decomposes an image into a set of directional components at different scales. It is useful for representing images with sharp discontinuities such as edges and corners, as it allows for a sparse representation of these features while preserving high fidelity. 9 | 10 | 11 | # Usage 12 | 13 | Diffcurve consists of two steps: 14 | 15 | 1. Generating the curvelet waveforms for images at a prescribed size. This step requires MATLAB to be installed on your device, as it calls a few MATLAB functions from Python using the [MATLAB Engine API for Python](https://www.mathworks.com/help/matlab/matlab-engine-for-python.html). 16 | 17 | 2. Using the curvelet waveforms to perform the curvelet transform. The same set of curvelet waveforms can be used for all images of the same size. This step is differentiable and supports deep learning APIs. It can be run on GPUs and TPUs. 18 | 19 | 20 | To view a complete demonstration of Diffcurve, please refer to the Jupyter notebooks located in the `diffcurve/diffcurve/notebooks/` directory. In what follows, we provide a brief overview of the main steps. 21 | 22 | 23 | ## Step 1: Generate a curvelet system 24 | 25 | We refer a curvelet system as a set of curvelet waveforms of different scales and orientations in the Fourier domain. 26 | 27 | ```python 28 | dct_kwargs = { 29 | 'is_real': 0.0, # complex-valued curvelets 30 | 'finest': 2.0, # use wavelets at the finest scale 31 | 'nbscales': 6.0, # number of scales 32 | 'nbangles_coarse': 16.0, # number of angles at the 2nd coarsest scale 33 | } 34 | 35 | # Under the hood, the following `get_curvelet_system` function 36 | # calls two MATLAB functions, `fdct_wrapping.m` and `ifdct_wrapping.m` 37 | # in `diffcurve/diffcurve/fdct2d/`. 38 | curvelet_system, curvelet_coeff_dim = get_curvelet_system( 39 | img_length=512, img_width=512, dct_kwargs) 40 | ``` 41 | 42 | 43 | ## Step 2: Perform curvelet transform using the generated curvelet system 44 | 45 | 46 | 47 | In PyTorch: 48 | 49 | ```python 50 | from diffcurve.fdct2d.torch_frontend import torch_fdct_2d, torch_ifdct_2d 51 | 52 | # Forward curvelet transform. The input image lena_img 53 | # is of size (512, 512), which is consistent with the 54 | # shape of `curvelet_system`. 55 | torch_coeff = torch_fdct_2d( 56 | torch.from_numpy(lena_img), 57 | torch.from_numpy(curvelet_system)) 58 | 59 | # Inverse curvelet transform. The tensor `decomp` below is a weighted 60 | # collection of curvelets that represent the Lena_img image at 61 | # different scales and orientations. By summing the array with 62 | # decomp.sum(0), we can reconstruct the Lena_img image 63 | # with high fidelity. 64 | torch_decomp = torch_ifdct_2d(torch_coeff, 65 | torch.from_numpy(curvelet_system), 66 | torch.from_numpy(curvelet_support_size)) 67 | 68 | 69 | coeff = np.array(torch_coeff.detach().cpu()) 70 | decomp = np.array(torch_decomp.detach().cpu()) 71 | 72 | mse = np.mean( (decomp.sum(0).real - lena_img) ** 2 ) 73 | 74 | print(mse) #1.9523397759671602e-31 75 | 76 | ``` 77 | 78 | In JAX: 79 | 80 | ```python 81 | from diffcurve.fdct2d.jax_frontend import jax_fdct_2d, jax_ifdct_2d 82 | 83 | # Forward curvelet transform. The input image lena_img is of size 84 | # (512, 512), which is consistent with the shape of `curvelet_system`. 85 | coeff = jax_fdct_2d(lena_img, curvelet_system) 86 | 87 | # Inverse curvelet transform. The tensor `decomp` below is a weighted 88 | # collection of curvelets that represent the Lena_img image at 89 | # different scales and orientations. By summing the array with 90 | # decomp.sum(0), we can reconstruct the Lena_img image 91 | # with high fidelity. 92 | decomp = jax_ifdct_2d(coeff, curvelet_system, 93 | curvelet_support_size ) 94 | 95 | mse = np.mean( (decomp.sum(0).real - lena_img) ** 2 ) 96 | 97 | print(mse) #1.1598556547941844e-31 98 | 99 | ``` 100 | 101 | 102 | 103 | 104 | # Installation 105 | 106 | ``` 107 | conda env create -n diffcurve --file environment.yml 108 | ``` 109 | 110 | Install MATLAB and [MATLAB Engine API for Python](https://www.mathworks.com/help/matlab/matlab-engine-for-python.html). 111 | 112 | 113 | 114 | Diffcurve is currently under active development. If you have any suggestions, please submit an issue or contact me at t.liu at unibas.ch. -------------------------------------------------------------------------------- /diffcurve/plot_utils.py: -------------------------------------------------------------------------------- 1 | """Common utils for used by different dataset builders. 2 | 3 | Many of these were taken from the dival library: 4 | https://github.com/jleuschn/dival 5 | """ 6 | 7 | from math import ceil 8 | import matplotlib.pyplot as plt 9 | import numpy as np 10 | from warnings import warn 11 | 12 | 13 | def simpleaxis(ax): 14 | ax.spines['top'].set_visible(False) 15 | ax.spines['right'].set_visible(False) 16 | ax.get_xaxis().tick_bottom() 17 | ax.get_yaxis().tick_left() 18 | 19 | 20 | def remove_frame(ax): 21 | ax.spines['top'].set_visible(False) 22 | ax.spines['right'].set_visible(False) 23 | ax.spines['bottom'].set_visible(False) 24 | ax.spines['left'].set_visible(False) 25 | ax.get_xaxis().set_ticks([]) 26 | ax.get_yaxis().set_ticks([]) 27 | 28 | 29 | def plot_image(x, fig=None, ax=None, **kwargs): 30 | """Plot image using matplotlib's :meth:`imshow` method. 31 | 32 | Parameters 33 | ---------- 34 | x : array-like or PIL image 35 | The image data. For further information see `imshow documentation 36 | `_. 37 | fig : :class:`matplotlib.figure.Figure`, optional 38 | The figure to plot the image in. If ``fig is None``, but `ax` is given, 39 | it is retrieved from `ax`. If both ``fig is None`` and ``ax is None``, 40 | a new figure is created. 41 | ax : :class:`matplotlib.axes.Axes`, optional 42 | The axes to plot the image in. If `None`, an axes object is created 43 | in `fig`. 44 | kwargs : dict, optional 45 | Keyword arguments passed to ``ax.imshow``. 46 | 47 | Returns 48 | ------- 49 | im : :class:`matplotlib.image.AxesImage` 50 | The image that was plotted. 51 | ax : :class:`matplotlib.axes.Axes` 52 | The axes the image was plotted in. 53 | """ 54 | if fig is None: 55 | if ax is None: 56 | fig = plt.figure() 57 | else: 58 | fig = ax.get_figure() 59 | if ax is None: 60 | ax = fig.add_subplot(111) 61 | kwargs.setdefault('cmap', 'gray') 62 | xticks = kwargs.pop('xticks', None) 63 | yticks = kwargs.pop('yticks', None) 64 | if xticks is not None: 65 | ax.set_xticks(xticks) 66 | if yticks is not None: 67 | ax.set_yticks(yticks) 68 | im = ax.imshow(np.asarray(x), **kwargs) 69 | return im, ax 70 | 71 | 72 | def plot_images(x_list, nrows=1, ncols=-1, fig=None, vrange='equal', 73 | cbar='auto', rect=None, fig_size=None, **kwargs): 74 | """Plot multiple images using matplotlib's :meth:`imshow` method in 75 | subplots. 76 | 77 | Parameters 78 | ---------- 79 | x_list : sequence of (array-like or PIL image) 80 | List of the image data. For further information see `imshow 81 | documentation 82 | `_. 83 | nrows : int, optional 84 | The number of subplot rows (the default is 1). If -1, it is computed by 85 | ``ceil(len(x_list)/ncols)``, or set to 1 if `ncols` is not given. 86 | ncols : int, optional 87 | The number of subplot columns. If -1, it is computed by 88 | ``ceil(len(x_list)/nrows)`` (default). If both `nrows` and `ncols` are 89 | given, the value of `ncols` is ignored. 90 | vrange : {``'equal'``, ``'individual'``} or [list of ](float, float),\ 91 | optional 92 | Value ranges for the colors of the images. 93 | If a string is passed, the range is auto-computed: 94 | 95 | ``'equal'`` 96 | The same colors are used for all images. 97 | ``'individual'`` 98 | The colors differ between the images. 99 | 100 | If a tuple of floats is passed, it is used for all images. 101 | If a list of tuples of floats is passed, each tuple is used for one 102 | image. 103 | cbar : {``'one'``, ``'many'``, ``'auto'``, ``'none'``}, optional 104 | Colorbar option. 105 | If ``cbar=='one'``, one colorbar is shown. Only possible if the value 106 | ranges used for the colors (cf. `vrange`) are the same for all images. 107 | If ``cbar=='many'``, a colorbar is shown for every image. 108 | If ``cbar=='auto'``, either ``'one'`` or ``'many'`` is chosen, 109 | depending on whether `vrange` is equal for all images. 110 | If ``cbar=='none'``, no colorbars are shown. 111 | fig : :class:`matplotlib.figure.Figure`, optional 112 | The figure to plot the images in. If `None`, a new figure is created. 113 | kwargs : dict, optional 114 | Keyword arguments passed to `plot_image`, which in turn passes them to 115 | ``imshow``. 116 | 117 | Returns 118 | ------- 119 | im : ndarray of :class:`matplotlib.image.AxesImage` 120 | The images that were plotted. 121 | ax : ndarray of :class:`matplotlib.axes.Axes` 122 | The axes the images were plotted in. 123 | """ 124 | try: 125 | x_list = list(x_list) 126 | except TypeError: 127 | raise TypeError('x_list must be iterable. Pass a sequence or use ' 128 | '`plot_image` to plot single images.') 129 | for i in range(len(x_list)): 130 | x_list[i] = np.asarray(x_list[i]) 131 | if fig is None: 132 | fig = plt.figure() 133 | if nrows is None or nrows == -1: 134 | if ncols is None or ncols == -1: 135 | nrows = 1 136 | else: 137 | nrows = ceil(len(x_list)/ncols) 138 | ncols = ceil(len(x_list)/nrows) 139 | if rect is None: 140 | rect = [0.1, 0.1, 0.8, 0.8] 141 | if fig_size is not None: 142 | fig.set_size_inches(fig_size) 143 | if isinstance(vrange, str): 144 | if vrange == 'equal': 145 | vrange_ = [(min((np.min(x) for x in x_list)), 146 | max((np.max(x) for x in x_list)))] * len(x_list) 147 | VRANGE_EQUAL = True 148 | elif vrange == 'individual': 149 | vrange_ = [(np.min(x), np.max(x)) for x in x_list] 150 | VRANGE_EQUAL = False 151 | else: 152 | raise ValueError("`vrange` must be 'equal' or 'individual'") 153 | elif isinstance(vrange, tuple) and len(vrange) == 2: 154 | vrange_ = [vrange] * len(x_list) 155 | VRANGE_EQUAL = True 156 | else: 157 | vrange_ = vrange 158 | VRANGE_EQUAL = False 159 | if not VRANGE_EQUAL: 160 | if cbar == 'one': 161 | warn("cannot use cbar='one' when vrange is not equal for all" 162 | "images, falling back to cbar='many'") 163 | if cbar != 'none': 164 | cbar = 'many' 165 | elif cbar == 'auto': 166 | cbar = 'one' 167 | ax = fig.subplots(nrows, ncols) 168 | if isinstance(ax, plt.Axes): 169 | ax = np.atleast_1d(ax) 170 | im = np.empty(ax.shape, dtype=object) 171 | for i, (x, ax_, v) in enumerate(zip(x_list, ax.flat, vrange_)): 172 | im_, _ = plot_image(x, ax=ax_, vmin=v[0], vmax=v[1], **kwargs) 173 | im.flat[i] = im_ 174 | if cbar == 'many': 175 | fig.colorbar(im_, ax=ax_) 176 | if cbar == 'one': 177 | fig.colorbar(im[0], ax=ax) 178 | return im, ax 179 | -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: diffcurve 2 | channels: 3 | - nvidia 4 | - conda-forge 5 | - defaults 6 | dependencies: 7 | - _libgcc_mutex=0.1=conda_forge 8 | - _openmp_mutex=4.5=2_kmp_llvm 9 | - alsa-lib=1.2.8=h166bdaf_0 10 | - anyio=3.6.2=pyhd8ed1ab_0 11 | - aom=3.5.0=h27087fc_0 12 | - appdirs=1.4.4=pyh9f0ad1d_0 13 | - argon2-cffi=21.3.0=pyhd8ed1ab_0 14 | - argon2-cffi-bindings=21.2.0=py39hb9d737c_3 15 | - asttokens=2.2.1=pyhd8ed1ab_0 16 | - attr=2.5.1=h166bdaf_1 17 | - attrs=22.2.0=pyh71513ae_0 18 | - babel=2.11.0=pyhd8ed1ab_0 19 | - backcall=0.2.0=pyh9f0ad1d_0 20 | - backports=1.0=pyhd8ed1ab_3 21 | - backports.functools_lru_cache=1.6.4=pyhd8ed1ab_0 22 | - beautifulsoup4=4.11.1=pyha770c72_0 23 | - bleach=6.0.0=pyhd8ed1ab_0 24 | - brotli=1.0.9=h166bdaf_8 25 | - brotli-bin=1.0.9=h166bdaf_8 26 | - brotlipy=0.7.0=py39hb9d737c_1005 27 | - bzip2=1.0.8=h7f98852_4 28 | - c-ares=1.18.1=h7f98852_0 29 | - ca-certificates=2022.12.7=ha878542_0 30 | - cairo=1.16.0=ha61ee94_1014 31 | - certifi=2022.12.7=pyhd8ed1ab_0 32 | - cffi=1.15.1=py39he91dace_3 33 | - charset-normalizer=2.1.1=pyhd8ed1ab_0 34 | - comm=0.1.2=pyhd8ed1ab_0 35 | - contourpy=1.0.7=py39h4b4f3f3_0 36 | - cryptography=39.0.0=py39h079d5ae_0 37 | - cuda-nvcc=12.0.76=0 38 | - cudatoolkit=11.3.1=h9edb442_11 39 | - cudnn=8.4.1.50=hed8a83a_0 40 | - cycler=0.11.0=pyhd8ed1ab_0 41 | - dbus=1.13.6=h5008d03_3 42 | - debugpy=1.6.6=py39h227be39_0 43 | - decorator=5.1.1=pyhd8ed1ab_0 44 | - defusedxml=0.7.1=pyhd8ed1ab_0 45 | - entrypoints=0.4=pyhd8ed1ab_0 46 | - executing=1.2.0=pyhd8ed1ab_0 47 | - expat=2.5.0=h27087fc_0 48 | - ffmpeg=5.1.2=gpl_h8dda1f0_106 49 | - fftw=3.3.10=nompi_hf0379b8_106 50 | - flake8=6.0.0=pyhd8ed1ab_0 51 | - flit-core=3.8.0=pyhd8ed1ab_0 52 | - font-ttf-dejavu-sans-mono=2.37=hab24e00_0 53 | - font-ttf-inconsolata=3.000=h77eed37_0 54 | - font-ttf-source-code-pro=2.038=h77eed37_0 55 | - font-ttf-ubuntu=0.83=hab24e00_0 56 | - fontconfig=2.14.2=h14ed4e7_0 57 | - fonts-conda-ecosystem=1=0 58 | - fonts-conda-forge=1=0 59 | - fonttools=4.38.0=py39hb9d737c_1 60 | - freeglut=3.2.2=h9c3ff4c_1 61 | - freetype=2.12.1=hca18f0e_1 62 | - gettext=0.21.1=h27087fc_0 63 | - glib=2.74.1=h6239696_1 64 | - glib-tools=2.74.1=h6239696_1 65 | - gmp=6.2.1=h58526e2_0 66 | - gnutls=3.7.8=hf3e180e_0 67 | - graphite2=1.3.13=h58526e2_1001 68 | - gst-plugins-base=1.21.3=h4243ec0_1 69 | - gstreamer=1.21.3=h25f0c4b_1 70 | - gstreamer-orc=0.4.33=h166bdaf_0 71 | - harfbuzz=6.0.0=h8e241bc_0 72 | - hdf5=1.12.2=nompi_h4df4325_101 73 | - icu=70.1=h27087fc_0 74 | - idna=3.4=pyhd8ed1ab_0 75 | - importlib-metadata=6.0.0=pyha770c72_0 76 | - importlib_metadata=6.0.0=hd8ed1ab_0 77 | - importlib_resources=5.10.2=pyhd8ed1ab_0 78 | - ipykernel=6.21.0=pyh210e3f2_0 79 | - ipython=8.9.0=pyh41d4057_0 80 | - ipython_genutils=0.2.0=py_1 81 | - jack=1.9.21=h583fa2b_2 82 | - jasper=2.0.33=h0ff4b12_1 83 | - jax=0.4.2=pyhd8ed1ab_0 84 | - jaxlib=0.4.1=cuda112py39h17d59b0_201 85 | - jedi=0.18.2=pyhd8ed1ab_0 86 | - jinja2=3.1.2=pyhd8ed1ab_1 87 | - jpeg=9e=h166bdaf_2 88 | - json5=0.9.5=pyh9f0ad1d_0 89 | - jsonschema=4.17.3=pyhd8ed1ab_0 90 | - jupyter_client=8.0.2=pyhd8ed1ab_0 91 | - jupyter_core=5.2.0=py39hf3d152e_0 92 | - jupyter_events=0.6.3=pyhd8ed1ab_0 93 | - jupyter_server=2.1.0=pyhd8ed1ab_0 94 | - jupyter_server_terminals=0.4.4=pyhd8ed1ab_1 95 | - jupyterlab=3.5.3=pyhd8ed1ab_0 96 | - jupyterlab_pygments=0.2.2=pyhd8ed1ab_0 97 | - jupyterlab_server=2.19.0=pyhd8ed1ab_0 98 | - keyutils=1.6.1=h166bdaf_0 99 | - kiwisolver=1.4.4=py39hf939315_1 100 | - krb5=1.20.1=h81ceb04_0 101 | - lame=3.100=h166bdaf_1003 102 | - lcms2=2.14=hfd0df8a_1 103 | - ld_impl_linux-64=2.40=h41732ed_0 104 | - lerc=4.0.0=h27087fc_0 105 | - libabseil=20220623.0=cxx17_h05df665_6 106 | - libaec=1.0.6=hcb278e6_1 107 | - libblas=3.9.0=16_linux64_openblas 108 | - libbrotlicommon=1.0.9=h166bdaf_8 109 | - libbrotlidec=1.0.9=h166bdaf_8 110 | - libbrotlienc=1.0.9=h166bdaf_8 111 | - libcap=2.66=ha37c62d_0 112 | - libcblas=3.9.0=16_linux64_openblas 113 | - libclang=15.0.7=default_had23c3d_0 114 | - libclang13=15.0.7=default_h3e3d535_0 115 | - libcups=2.3.3=h36d4200_3 116 | - libcurl=7.87.0=hdc1c0ab_0 117 | - libdb=6.2.32=h9c3ff4c_0 118 | - libdeflate=1.17=h0b41bf4_0 119 | - libdrm=2.4.114=h166bdaf_0 120 | - libedit=3.1.20191231=he28a2e2_2 121 | - libev=4.33=h516909a_1 122 | - libevent=2.1.10=h28343ad_4 123 | - libffi=3.4.2=h7f98852_5 124 | - libflac=1.4.2=h27087fc_0 125 | - libgcc-ng=12.2.0=h65d4601_19 126 | - libgcrypt=1.10.1=h166bdaf_0 127 | - libgfortran-ng=12.2.0=h69a702a_19 128 | - libgfortran5=12.2.0=h337968e_19 129 | - libglib=2.74.1=h606061b_1 130 | - libglu=9.0.0=he1b5a44_1001 131 | - libgpg-error=1.46=h620e276_0 132 | - libgrpc=1.51.1=h30feacc_0 133 | - libhwloc=2.8.0=h32351e8_1 134 | - libiconv=1.17=h166bdaf_0 135 | - libidn2=2.3.4=h166bdaf_0 136 | - libjpeg-turbo=2.1.4=h166bdaf_0 137 | - liblapack=3.9.0=16_linux64_openblas 138 | - liblapacke=3.9.0=16_linux64_openblas 139 | - libllvm15=15.0.7=hadd5161_0 140 | - libnghttp2=1.51.0=hff17c54_0 141 | - libnsl=2.0.0=h7f98852_0 142 | - libogg=1.3.4=h7f98852_1 143 | - libopenblas=0.3.21=pthreads_h78a6416_3 144 | - libopencv=4.7.0=py39hb375605_0 145 | - libopus=1.3.1=h7f98852_1 146 | - libpciaccess=0.17=h166bdaf_0 147 | - libpng=1.6.39=h753d276_0 148 | - libpq=15.1=hb675445_3 149 | - libprotobuf=3.21.12=h3eb15da_0 150 | - libsndfile=1.2.0=hb75c966_0 151 | - libsodium=1.0.18=h36c2ea0_1 152 | - libsqlite=3.40.0=h753d276_0 153 | - libssh2=1.10.0=hf14f497_3 154 | - libstdcxx-ng=12.2.0=h46fd767_19 155 | - libsystemd0=252=h2a991cd_0 156 | - libtasn1=4.19.0=h166bdaf_0 157 | - libtiff=4.5.0=h6adf6a1_2 158 | - libtool=2.4.7=h27087fc_0 159 | - libudev1=252=h166bdaf_0 160 | - libunistring=0.9.10=h7f98852_0 161 | - libuuid=2.32.1=h7f98852_1000 162 | - libva=2.17.0=h0b41bf4_0 163 | - libvorbis=1.3.7=h9c3ff4c_0 164 | - libvpx=1.11.0=h9c3ff4c_3 165 | - libwebp-base=1.2.4=h166bdaf_0 166 | - libxcb=1.13=h7f98852_1004 167 | - libxkbcommon=1.0.3=he3ba5ed_0 168 | - libxml2=2.10.3=h7463322_0 169 | - libzlib=1.2.13=h166bdaf_4 170 | - llvm-openmp=15.0.7=h0cdce71_0 171 | - lz4-c=1.9.4=hcb278e6_0 172 | - magma=2.6.2=hc72dce7_0 173 | - markupsafe=2.1.2=py39h72bdee0_0 174 | - matplotlib=3.6.3=py39hf3d152e_0 175 | - matplotlib-base=3.6.3=py39he190548_0 176 | - matplotlib-inline=0.1.6=pyhd8ed1ab_0 177 | - mccabe=0.7.0=pyhd8ed1ab_0 178 | - mistune=2.0.4=pyhd8ed1ab_0 179 | - mkl=2022.2.1=h84fe81f_16997 180 | - mpg123=1.31.2=hcb278e6_0 181 | - munkres=1.1.4=pyh9f0ad1d_0 182 | - mysql-common=8.0.32=ha901b37_0 183 | - mysql-libs=8.0.32=hd7da12d_0 184 | - nbclassic=0.4.8=pyhd8ed1ab_0 185 | - nbclient=0.7.2=pyhd8ed1ab_0 186 | - nbconvert=7.2.9=pyhd8ed1ab_0 187 | - nbconvert-core=7.2.9=pyhd8ed1ab_0 188 | - nbconvert-pandoc=7.2.9=pyhd8ed1ab_0 189 | - nbformat=5.7.3=pyhd8ed1ab_0 190 | - nccl=2.14.3.1=h0800d71_0 191 | - ncurses=6.3=h27087fc_1 192 | - nest-asyncio=1.5.6=pyhd8ed1ab_0 193 | - nettle=3.8.1=hc379101_1 194 | - ninja=1.11.0=h924138e_0 195 | - notebook=6.5.2=pyha770c72_1 196 | - notebook-shim=0.2.2=pyhd8ed1ab_0 197 | - nspr=4.35=h27087fc_0 198 | - nss=3.82=he02c5a1_0 199 | - numpy=1.24.1=py39h7360e5f_0 200 | - opencv=4.7.0=py39hf3d152e_0 201 | - openh264=2.3.1=h27087fc_1 202 | - openjpeg=2.5.0=hfec8fc6_2 203 | - openssl=3.0.7=h0b41bf4_2 204 | - opt_einsum=3.3.0=pyhd8ed1ab_1 205 | - p11-kit=0.24.1=hc5aa10d_0 206 | - packaging=23.0=pyhd8ed1ab_0 207 | - pandoc=2.19.2=h32600fe_1 208 | - pandocfilters=1.5.0=pyhd8ed1ab_0 209 | - parso=0.8.3=pyhd8ed1ab_0 210 | - pcre2=10.40=hc3806b6_0 211 | - pexpect=4.8.0=pyh1a96a4e_2 212 | - pickleshare=0.7.5=py_1003 213 | - pillow=9.4.0=py39ha08a7e4_0 214 | - pip=23.0=pyhd8ed1ab_0 215 | - pixman=0.40.0=h36c2ea0_0 216 | - pkgutil-resolve-name=1.3.10=pyhd8ed1ab_0 217 | - platformdirs=2.6.2=pyhd8ed1ab_0 218 | - ply=3.11=py_1 219 | - pooch=1.6.0=pyhd8ed1ab_0 220 | - prometheus_client=0.16.0=pyhd8ed1ab_0 221 | - prompt-toolkit=3.0.36=pyha770c72_0 222 | - psutil=5.9.4=py39hb9d737c_0 223 | - pthread-stubs=0.4=h36c2ea0_1001 224 | - ptyprocess=0.7.0=pyhd3deb0d_0 225 | - pulseaudio=16.1=ha8d29e2_1 226 | - pure_eval=0.2.2=pyhd8ed1ab_0 227 | - py-opencv=4.7.0=py39hcca971b_0 228 | - pycodestyle=2.10.0=pyhd8ed1ab_0 229 | - pycparser=2.21=pyhd8ed1ab_0 230 | - pyflakes=3.0.1=pyhd8ed1ab_0 231 | - pygments=2.14.0=pyhd8ed1ab_0 232 | - pyopenssl=23.0.0=pyhd8ed1ab_0 233 | - pyparsing=3.0.9=pyhd8ed1ab_0 234 | - pyqt=5.15.7=py39h5c7b992_3 235 | - pyqt5-sip=12.11.0=py39h227be39_3 236 | - pyrsistent=0.19.3=py39h72bdee0_0 237 | - pysocks=1.7.1=pyha2e5f31_6 238 | - python=3.9.15=hba424b6_0_cpython 239 | - python-dateutil=2.8.2=pyhd8ed1ab_0 240 | - python-fastjsonschema=2.16.2=pyhd8ed1ab_0 241 | - python-json-logger=2.0.4=pyhd8ed1ab_0 242 | - python_abi=3.9=3_cp39 243 | - pytorch=1.13.1=cuda112py39hb0b7ed5_200 244 | - pytz=2022.7.1=pyhd8ed1ab_0 245 | - pyyaml=6.0=py39hb9d737c_5 246 | - pyzmq=25.0.0=py39h0be026e_0 247 | - qt-main=5.15.6=h602db52_6 248 | - re2=2022.06.01=h27087fc_1 249 | - readline=8.1.2=h0f457ee_0 250 | - requests=2.28.2=pyhd8ed1ab_0 251 | - rfc3339-validator=0.1.4=pyhd8ed1ab_0 252 | - rfc3986-validator=0.1.1=pyh9f0ad1d_0 253 | - scipy=1.10.0=py39h7360e5f_0 254 | - send2trash=1.8.0=pyhd8ed1ab_0 255 | - setuptools=66.1.1=pyhd8ed1ab_0 256 | - sip=6.7.6=py39h227be39_0 257 | - six=1.16.0=pyh6c4a22f_0 258 | - sleef=3.5.1=h9b69904_2 259 | - sniffio=1.3.0=pyhd8ed1ab_0 260 | - soupsieve=2.3.2.post1=pyhd8ed1ab_0 261 | - stack_data=0.6.2=pyhd8ed1ab_0 262 | - svt-av1=1.4.1=hcb278e6_0 263 | - tbb=2021.7.0=h924138e_1 264 | - terminado=0.17.1=pyh41d4057_0 265 | - tinycss2=1.2.1=pyhd8ed1ab_0 266 | - tk=8.6.12=h27826a3_0 267 | - toml=0.10.2=pyhd8ed1ab_0 268 | - tomli=2.0.1=pyhd8ed1ab_0 269 | - torchvision=0.14.1=cuda112py39h1a1de93_0 270 | - tornado=6.2=py39hb9d737c_1 271 | - traitlets=5.9.0=pyhd8ed1ab_0 272 | - typing-extensions=4.4.0=hd8ed1ab_0 273 | - typing_extensions=4.4.0=pyha770c72_0 274 | - tzdata=2022g=h191b570_0 275 | - unicodedata2=15.0.0=py39hb9d737c_0 276 | - urllib3=1.26.14=pyhd8ed1ab_0 277 | - wcwidth=0.2.6=pyhd8ed1ab_0 278 | - webencodings=0.5.1=py_1 279 | - websocket-client=1.5.0=pyhd8ed1ab_0 280 | - wheel=0.38.4=pyhd8ed1ab_0 281 | - x264=1!164.3095=h166bdaf_2 282 | - x265=3.5=h924138e_3 283 | - xcb-util=0.4.0=h166bdaf_0 284 | - xcb-util-image=0.4.0=h166bdaf_0 285 | - xcb-util-keysyms=0.4.0=h166bdaf_0 286 | - xcb-util-renderutil=0.3.9=h166bdaf_0 287 | - xcb-util-wm=0.4.1=h166bdaf_0 288 | - xorg-fixesproto=5.0=h7f98852_1002 289 | - xorg-inputproto=2.3.2=h7f98852_1002 290 | - xorg-kbproto=1.0.7=h7f98852_1002 291 | - xorg-libice=1.0.10=h7f98852_0 292 | - xorg-libsm=1.2.3=hd9c2040_1000 293 | - xorg-libx11=1.7.2=h7f98852_0 294 | - xorg-libxau=1.0.9=h7f98852_0 295 | - xorg-libxdmcp=1.1.3=h7f98852_0 296 | - xorg-libxext=1.3.4=h7f98852_1 297 | - xorg-libxfixes=5.0.3=h7f98852_1004 298 | - xorg-libxi=1.7.10=h7f98852_0 299 | - xorg-libxrender=0.9.10=h7f98852_1003 300 | - xorg-renderproto=0.11.1=h7f98852_1002 301 | - xorg-xextproto=7.3.0=h7f98852_1002 302 | - xorg-xproto=7.0.31=h7f98852_1007 303 | - xz=5.2.6=h166bdaf_0 304 | - yaml=0.2.5=h7f98852_2 305 | - zeromq=4.3.4=h9c3ff4c_1 306 | - zipp=3.12.0=pyhd8ed1ab_0 307 | - zlib=1.2.13=h166bdaf_4 308 | - zstd=1.5.2=h3eb15da_6 309 | - pip: 310 | - matlabengineforpython==9.13 311 | prefix: /home/liu0003/anaconda3/envs/diffcurve 312 | -------------------------------------------------------------------------------- /diffcurve/fdct2d/fdct_wrapping.m: -------------------------------------------------------------------------------- 1 | function C = fdct_wrapping(x, is_real, finest, nbscales, nbangles_coarse) 2 | 3 | % fdct_wrapping.m - Fast Discrete Curvelet Transform via wedge wrapping - Version 1.0 4 | % 5 | % Inputs 6 | % x M-by-N matrix 7 | % 8 | % Optional Inputs 9 | % is_real Type of the transform 10 | % 0: complex-valued curvelets 11 | % 1: real-valued curvelets 12 | % [default set to 0] 13 | % finest Chooses one of two possibilities for the coefficients at the 14 | % finest level: 15 | % 1: curvelets 16 | % 2: wavelets 17 | % [default set to 2] 18 | % nbscales number of scales including the coarsest wavelet level 19 | % [default set to ceil(log2(min(M,N)) - 3)] 20 | % nbangles_coarse 21 | % number of angles at the 2nd coarsest level, minimum 8, 22 | % must be a multiple of 4. [default set to 16] 23 | % 24 | % Outputs 25 | % C Cell array of curvelet coefficients. 26 | % C{j}{l}(k1,k2) is the coefficient at 27 | % - scale j: integer, from finest to coarsest scale, 28 | % - angle l: integer, starts at the top-left corner and 29 | % increases clockwise, 30 | % - position k1,k2: both integers, size varies with j 31 | % and l. 32 | % If is_real is 1, there are two types of curvelets, 33 | % 'cosine' and 'sine'. For a given scale j, the 'cosine' 34 | % coefficients are stored in the first two quadrants (low 35 | % values of l), the 'sine' coefficients in the last two 36 | % quadrants (high values of l). 37 | % 38 | % See also ifdct_wrapping.m, fdct_wrapping_param.m 39 | % 40 | % By Laurent Demanet, 2004 41 | 42 | X = fftshift(fft2(ifftshift(x)))/sqrt(prod(size(x))); 43 | [N1,N2] = size(X); 44 | if nargin < 2, is_real = 0; end; 45 | if nargin < 3, finest = 2; end; 46 | if nargin < 4, nbscales = ceil(log2(min(N1,N2)) - 3); end; 47 | if nargin < 5, nbangles_coarse = 16; end; 48 | 49 | % Initialization: data structure 50 | nbangles = [1, nbangles_coarse .* 2.^(ceil((nbscales-(nbscales:-1:2))/2))]; 51 | if finest == 2, nbangles(nbscales) = 1; end; 52 | C = cell(1,nbscales); 53 | for j = 1:nbscales 54 | C{j} = cell(1,nbangles(j)); 55 | end; 56 | 57 | % Loop: pyramidal scale decomposition 58 | M1 = N1/3; 59 | M2 = N2/3; 60 | if finest == 1, 61 | 62 | % Initialization: smooth periodic extension of high frequencies 63 | bigN1 = 2*floor(2*M1)+1; 64 | bigN2 = 2*floor(2*M2)+1; 65 | equiv_index_1 = 1+mod(floor(N1/2)-floor(2*M1)+(1:bigN1)-1,N1); 66 | equiv_index_2 = 1+mod(floor(N2/2)-floor(2*M2)+(1:bigN2)-1,N2); 67 | X = X(equiv_index_1,equiv_index_2); 68 | % Invariant: equiv_index_1(floor(2*M1)+1) == (N1 + 2 - mod(N1,2))/2 69 | % is the center in frequency. Same for M2, N2. 70 | window_length_1 = floor(2*M1) - floor(M1) - 1 - (mod(N1,3)==0); 71 | window_length_2 = floor(2*M2) - floor(M2) - 1 - (mod(N2,3)==0); 72 | % Invariant: floor(M1) + floor(2*M1) == N1 - (mod(M1,3)~=0) 73 | % Same for M2, N2. 74 | coord_1 = 0:(1/window_length_1):1; 75 | coord_2 = 0:(1/window_length_2):1; 76 | [wl_1,wr_1] = fdct_wrapping_window(coord_1); 77 | [wl_2,wr_2] = fdct_wrapping_window(coord_2); 78 | lowpass_1 = [wl_1, ones(1,2*floor(M1)+1), wr_1]; 79 | if mod(N1,3)==0, lowpass_1 = [0, lowpass_1, 0]; end; 80 | lowpass_2 = [wl_2, ones(1,2*floor(M2)+1), wr_2]; 81 | if mod(N2,3)==0, lowpass_2 = [0, lowpass_2, 0]; end; 82 | lowpass = lowpass_1'*lowpass_2; 83 | Xlow = X .* lowpass; 84 | 85 | scales = nbscales:-1:2; 86 | 87 | else 88 | 89 | M1 = M1/2; 90 | M2 = M2/2; 91 | window_length_1 = floor(2*M1) - floor(M1) - 1; 92 | window_length_2 = floor(2*M2) - floor(M2) - 1; 93 | coord_1 = 0:(1/window_length_1):1; 94 | coord_2 = 0:(1/window_length_2):1; 95 | [wl_1,wr_1] = fdct_wrapping_window(coord_1); 96 | [wl_2,wr_2] = fdct_wrapping_window(coord_2); 97 | lowpass_1 = [wl_1, ones(1,2*floor(M1)+1), wr_1]; 98 | lowpass_2 = [wl_2, ones(1,2*floor(M2)+1), wr_2]; 99 | lowpass = lowpass_1'*lowpass_2; 100 | hipass = sqrt(1 - lowpass.^2); 101 | Xlow_index_1 = ((-floor(2*M1)):floor(2*M1)) + ceil((N1+1)/2); 102 | Xlow_index_2 = ((-floor(2*M2)):floor(2*M2)) + ceil((N2+1)/2); 103 | Xlow = X(Xlow_index_1, Xlow_index_2) .* lowpass; 104 | Xhi = X; 105 | Xhi(Xlow_index_1, Xlow_index_2) = Xhi(Xlow_index_1, Xlow_index_2) .* hipass; 106 | C{nbscales}{1} = fftshift(ifft2(ifftshift(Xhi)))*sqrt(prod(size(Xhi))); 107 | if is_real, C{nbscales}{1} = real(C{nbscales}{1}); end; 108 | 109 | scales = (nbscales-1):-1:2; 110 | 111 | end; 112 | for j = scales, 113 | 114 | M1 = M1/2; 115 | M2 = M2/2; 116 | window_length_1 = floor(2*M1) - floor(M1) - 1; 117 | window_length_2 = floor(2*M2) - floor(M2) - 1; 118 | coord_1 = 0:(1/window_length_1):1; 119 | coord_2 = 0:(1/window_length_2):1; 120 | [wl_1,wr_1] = fdct_wrapping_window(coord_1); 121 | [wl_2,wr_2] = fdct_wrapping_window(coord_2); 122 | lowpass_1 = [wl_1, ones(1,2*floor(M1)+1), wr_1]; 123 | lowpass_2 = [wl_2, ones(1,2*floor(M2)+1), wr_2]; 124 | lowpass = lowpass_1'*lowpass_2; 125 | hipass = sqrt(1 - lowpass.^2); 126 | Xhi = Xlow; % size is 2*floor(4*M1)+1 - by - 2*floor(4*M2)+1 127 | Xlow_index_1 = ((-floor(2*M1)):floor(2*M1)) + floor(4*M1) + 1; 128 | Xlow_index_2 = ((-floor(2*M2)):floor(2*M2)) + floor(4*M2) + 1; 129 | Xlow = Xlow(Xlow_index_1, Xlow_index_2); 130 | Xhi(Xlow_index_1, Xlow_index_2) = Xlow .* hipass; 131 | Xlow = Xlow .* lowpass; % size is 2*floor(2*M1)+1 - by - 2*floor(2*M2)+1 132 | 133 | % Loop: angular decomposition 134 | l = 0; 135 | nbquadrants = 2 + 2*(~is_real); 136 | nbangles_perquad = nbangles(j)/4; 137 | for quadrant = 1:nbquadrants 138 | M_horiz = M2 * (mod(quadrant,2)==1) + M1 * (mod(quadrant,2)==0); 139 | M_vert = M1 * (mod(quadrant,2)==1) + M2 * (mod(quadrant,2)==0); 140 | if mod(nbangles_perquad,2), 141 | wedge_ticks_left = round((0:(1/(2*nbangles_perquad)):.5)*2*floor(4*M_horiz) + 1); 142 | wedge_ticks_right = 2*floor(4*M_horiz) + 2 - wedge_ticks_left; 143 | wedge_ticks = [wedge_ticks_left, wedge_ticks_right(end:-1:1)]; 144 | else 145 | wedge_ticks_left = round((0:(1/(2*nbangles_perquad)):.5)*2*floor(4*M_horiz) + 1); 146 | wedge_ticks_right = 2*floor(4*M_horiz) + 2 - wedge_ticks_left; 147 | wedge_ticks = [wedge_ticks_left, wedge_ticks_right((end-1):-1:1)]; 148 | end; 149 | wedge_endpoints = wedge_ticks(2:2:(end-1)); % integers 150 | wedge_midpoints = (wedge_endpoints(1:(end-1)) + wedge_endpoints(2:end))/2; 151 | % integers or half-integers 152 | 153 | % Left corner wedge 154 | l = l+1; 155 | first_wedge_endpoint_vert = round(2*floor(4*M_vert)/(2*nbangles_perquad) + 1); 156 | length_corner_wedge = floor(4*M_vert) - floor(M_vert) + ceil(first_wedge_endpoint_vert/4); 157 | Y_corner = 1:length_corner_wedge; 158 | [XX,YY] = meshgrid(1:(2*floor(4*M_horiz)+1),Y_corner); 159 | width_wedge = wedge_endpoints(2) + wedge_endpoints(1) - 1; 160 | slope_wedge = (floor(4*M_horiz) + 1 - wedge_endpoints(1))/floor(4*M_vert); 161 | left_line = round(2 - wedge_endpoints(1) + slope_wedge*(Y_corner - 1)); 162 | % integers 163 | [wrapped_data, wrapped_XX, wrapped_YY] = deal(zeros(length_corner_wedge,width_wedge)); 164 | first_row = floor(4*M_vert)+2-ceil((length_corner_wedge+1)/2)+... 165 | mod(length_corner_wedge+1,2)*(quadrant-2 == mod(quadrant-2,2)); 166 | first_col = floor(4*M_horiz)+2-ceil((width_wedge+1)/2)+... 167 | mod(width_wedge+1,2)*(quadrant-3 == mod(quadrant-3,2)); 168 | % Coordinates of the top-left corner of the wedge wrapped 169 | % around the origin. Some subtleties when the wedge is 170 | % even-sized because of the forthcoming 90 degrees rotation 171 | for row = Y_corner 172 | cols = left_line(row) + mod((0:(width_wedge-1))-(left_line(row)-first_col),width_wedge); 173 | admissible_cols = round(1/2*(cols+1+abs(cols-1))); 174 | new_row = 1 + mod(row - first_row, length_corner_wedge); 175 | 176 | wrapped_data(new_row,:) = Xhi(row,admissible_cols) .* (cols > 0); 177 | wrapped_XX(new_row,:) = XX(row,admissible_cols); 178 | wrapped_YY(new_row,:) = YY(row,admissible_cols); 179 | end; 180 | 181 | slope_wedge_right = (floor(4*M_horiz)+1 - wedge_midpoints(1))/floor(4*M_vert); 182 | mid_line_right = wedge_midpoints(1) + slope_wedge_right*(wrapped_YY - 1); 183 | % not integers in general 184 | coord_right = 1/2 + floor(4*M_vert)/(wedge_endpoints(2) - wedge_endpoints(1)) * ... 185 | (wrapped_XX - mid_line_right)./(floor(4*M_vert)+1 - wrapped_YY); 186 | C2 = 1/(1/(2*(floor(4*M_horiz))/(wedge_endpoints(1) - 1) - 1) + 1/(2*(floor(4*M_vert))/(first_wedge_endpoint_vert - 1) - 1)); 187 | C1 = C2 / (2*(floor(4*M_vert))/(first_wedge_endpoint_vert - 1) - 1); 188 | wrapped_XX((wrapped_XX - 1)/floor(4*M_horiz) + (wrapped_YY-1)/floor(4*M_vert) == 2) = ... 189 | wrapped_XX((wrapped_XX - 1)/floor(4*M_horiz) + (wrapped_YY-1)/floor(4*M_vert) == 2) + 1; 190 | coord_corner = C1 + C2 * ((wrapped_XX - 1)/(floor(4*M_horiz)) - (wrapped_YY - 1)/(floor(4*M_vert))) ./ ... 191 | (2-((wrapped_XX - 1)/(floor(4*M_horiz)) + (wrapped_YY - 1)/(floor(4*M_vert)))); 192 | wl_left = fdct_wrapping_window(coord_corner); 193 | [wl_right,wr_right] = fdct_wrapping_window(coord_right); 194 | wrapped_data = wrapped_data .* (wl_left .* wr_right); 195 | 196 | switch is_real 197 | case 0 198 | wrapped_data = rot90(wrapped_data,-(quadrant-1)); 199 | C{j}{l} = fftshift(ifft2(ifftshift(wrapped_data)))*sqrt(prod(size(wrapped_data))); 200 | case 1 201 | wrapped_data = rot90(wrapped_data,-(quadrant-1)); 202 | x = fftshift(ifft2(ifftshift(wrapped_data)))*sqrt(prod(size(wrapped_data))); 203 | C{j}{l} = sqrt(2)*real(x); 204 | C{j}{l+nbangles(j)/2} = sqrt(2)*imag(x); 205 | end; 206 | 207 | % Regular wedges 208 | length_wedge = floor(4*M_vert) - floor(M_vert); 209 | Y = 1:length_wedge; 210 | first_row = floor(4*M_vert)+2-ceil((length_wedge+1)/2)+... 211 | mod(length_wedge+1,2)*(quadrant-2 == mod(quadrant-2,2)); 212 | for subl = 2:(nbangles_perquad-1); 213 | l = l+1; 214 | width_wedge = wedge_endpoints(subl+1) - wedge_endpoints(subl-1) + 1; 215 | slope_wedge = ((floor(4*M_horiz)+1) - wedge_endpoints(subl))/floor(4*M_vert); 216 | left_line = round(wedge_endpoints(subl-1) + slope_wedge*(Y - 1)); 217 | [wrapped_data, wrapped_XX, wrapped_YY] = deal(zeros(length_wedge,width_wedge)); 218 | first_col = floor(4*M_horiz)+2-ceil((width_wedge+1)/2)+... 219 | mod(width_wedge+1,2)*(quadrant-3 == mod(quadrant-3,2)); 220 | for row = Y 221 | cols = left_line(row) + mod((0:(width_wedge-1))-(left_line(row)-first_col),width_wedge); 222 | new_row = 1 + mod(row - first_row, length_wedge); 223 | wrapped_data(new_row,:) = Xhi(row,cols); 224 | wrapped_XX(new_row,:) = XX(row,cols); 225 | wrapped_YY(new_row,:) = YY(row,cols); 226 | end; 227 | slope_wedge_left = ((floor(4*M_horiz)+1) - wedge_midpoints(subl-1))/floor(4*M_vert); 228 | mid_line_left = wedge_midpoints(subl-1) + slope_wedge_left*(wrapped_YY - 1); 229 | coord_left = 1/2 + floor(4*M_vert)/(wedge_endpoints(subl) - wedge_endpoints(subl-1)) * ... 230 | (wrapped_XX - mid_line_left)./(floor(4*M_vert)+1 - wrapped_YY); 231 | slope_wedge_right = ((floor(4*M_horiz)+1) - wedge_midpoints(subl))/floor(4*M_vert); 232 | mid_line_right = wedge_midpoints(subl) + slope_wedge_right*(wrapped_YY - 1); 233 | coord_right = 1/2 + floor(4*M_vert)/(wedge_endpoints(subl+1) - wedge_endpoints(subl)) * ... 234 | (wrapped_XX - mid_line_right)./(floor(4*M_vert)+1 - wrapped_YY); 235 | wl_left = fdct_wrapping_window(coord_left); 236 | [wl_right,wr_right] = fdct_wrapping_window(coord_right); 237 | wrapped_data = wrapped_data .* (wl_left .* wr_right); 238 | switch is_real 239 | case 0 240 | wrapped_data = rot90(wrapped_data,-(quadrant-1)); 241 | C{j}{l} = fftshift(ifft2(ifftshift(wrapped_data)))*sqrt(prod(size(wrapped_data))); 242 | case 1 243 | wrapped_data = rot90(wrapped_data,-(quadrant-1)); 244 | x = fftshift(ifft2(ifftshift(wrapped_data)))*sqrt(prod(size(wrapped_data))); 245 | C{j}{l} = sqrt(2)*real(x); 246 | C{j}{l+nbangles(j)/2} = sqrt(2)*imag(x); 247 | end; 248 | end; 249 | 250 | % Right corner wedge 251 | l = l+1; 252 | width_wedge = 4*floor(4*M_horiz) + 3 - wedge_endpoints(end) - wedge_endpoints(end-1); 253 | slope_wedge = ((floor(4*M_horiz)+1) - wedge_endpoints(end))/floor(4*M_vert); 254 | left_line = round(wedge_endpoints(end-1) + slope_wedge*(Y_corner - 1)); 255 | [wrapped_data, wrapped_XX, wrapped_YY] = deal(zeros(length_corner_wedge,width_wedge)); 256 | first_row = floor(4*M_vert)+2-ceil((length_corner_wedge+1)/2)+... 257 | mod(length_corner_wedge+1,2)*(quadrant-2 == mod(quadrant-2,2)); 258 | first_col = floor(4*M_horiz)+2-ceil((width_wedge+1)/2)+... 259 | mod(width_wedge+1,2)*(quadrant-3 == mod(quadrant-3,2)); 260 | for row = Y_corner 261 | cols = left_line(row) + mod((0:(width_wedge-1))-(left_line(row)-first_col),width_wedge); 262 | admissible_cols = round(1/2*(cols+2*floor(4*M_horiz)+1-abs(cols-(2*floor(4*M_horiz)+1)))); 263 | new_row = 1 + mod(row - first_row, length_corner_wedge); 264 | wrapped_data(new_row,:) = Xhi(row,admissible_cols) .* (cols <= (2*floor(4*M_horiz)+1)); 265 | wrapped_XX(new_row,:) = XX(row,admissible_cols); 266 | wrapped_YY(new_row,:) = YY(row,admissible_cols); 267 | end; 268 | slope_wedge_left = ((floor(4*M_horiz)+1) - wedge_midpoints(end))/floor(4*M_vert); 269 | mid_line_left = wedge_midpoints(end) + slope_wedge_left*(wrapped_YY - 1); 270 | coord_left = 1/2 + floor(4*M_vert)/(wedge_endpoints(end) - wedge_endpoints(end-1)) * ... 271 | (wrapped_XX - mid_line_left)./(floor(4*M_vert) + 1 - wrapped_YY); 272 | C2 = -1/(2*(floor(4*M_horiz))/(wedge_endpoints(end) - 1) - 1 + 1/(2*(floor(4*M_vert))/(first_wedge_endpoint_vert - 1) - 1)); 273 | C1 = -C2 * (2*(floor(4*M_horiz))/(wedge_endpoints(end) - 1) - 1); 274 | wrapped_XX((wrapped_XX - 1)/floor(4*M_horiz) == (wrapped_YY - 1)/floor(4*M_vert)) = ... 275 | wrapped_XX((wrapped_XX - 1)/floor(4*M_horiz) == (wrapped_YY - 1)/floor(4*M_vert)) - 1; 276 | coord_corner = C1 + C2 * (2-((wrapped_XX - 1)/(floor(4*M_horiz)) + (wrapped_YY - 1)/(floor(4*M_vert)))) ./ ... 277 | ((wrapped_XX - 1)/(floor(4*M_horiz)) - (wrapped_YY - 1)/(floor(4*M_vert))); 278 | wl_left = fdct_wrapping_window(coord_left); 279 | [wl_right,wr_right] = fdct_wrapping_window(coord_corner); 280 | 281 | wrapped_data = wrapped_data .* (wl_left .* wr_right); 282 | switch is_real 283 | case 0 284 | wrapped_data = rot90(wrapped_data,-(quadrant-1)); 285 | C{j}{l} = fftshift(ifft2(ifftshift(wrapped_data)))*sqrt(prod(size(wrapped_data))); 286 | case 1 287 | wrapped_data = rot90(wrapped_data,-(quadrant-1)); 288 | x = fftshift(ifft2(ifftshift(wrapped_data)))*sqrt(prod(size(wrapped_data))); 289 | C{j}{l} = sqrt(2)*real(x); 290 | C{j}{l+nbangles(j)/2} = sqrt(2)*imag(x); 291 | end; 292 | 293 | if quadrant < nbquadrants, Xhi = rot90(Xhi); end; 294 | end; 295 | end; 296 | 297 | % Coarsest wavelet level 298 | C{1}{1} = fftshift(ifft2(ifftshift(Xlow)))*sqrt(prod(size(Xlow))); 299 | if is_real == 1, 300 | C{1}{1} = real(C{1}{1}); 301 | end; 302 | -------------------------------------------------------------------------------- /diffcurve/fdct2d/ifdct_wrapping.m: -------------------------------------------------------------------------------- 1 | function x = ifdct_wrapping(C, is_real, M, N) 2 | 3 | % ifdct_wrapping.m - Inverse Fast Discrete Curvelet Transform via wedge wrapping - Version 1.0 4 | % This is in fact the adjoint, also the pseudo-inverse 5 | % 6 | % Inputs 7 | % C Cell array containing curvelet coefficients (see 8 | % description in fdct_wrapping.m) 9 | % is_real As used in fdct_wrapping.m 10 | % M, N Size of the image to be recovered (not necessary if finest 11 | % = 2) 12 | % 13 | % Outputs 14 | % x M-by-N matrix 15 | % 16 | % See also fdct_wrapping.m 17 | % 18 | % By Laurent Demanet, 2004 19 | 20 | % Initialization 21 | nbscales = length(C); 22 | nbangles_coarse = length(C{2}); 23 | nbangles = [1, nbangles_coarse .* 2.^(ceil((nbscales-(nbscales:-1:2))/2))]; 24 | if length(C{end}) == 1, finest = 2; else finest = 1; end; 25 | if finest == 2, nbangles(nbscales) = 1; end; 26 | if nargin < 2, is_real = 0; end; 27 | if nargin < 4, 28 | if finest == 1, error('Syntax: IFCT_wrapping(C,M,N) where the matrix to be recovered is M-by-N'); end; 29 | [N1,N2] = size(C{end}{1}); 30 | else 31 | N1 = M; 32 | N2 = N; 33 | end; 34 | 35 | M1 = N1/3; 36 | M2 = N2/3; 37 | 38 | if finest == 1; 39 | 40 | bigN1 = 2*floor(2*M1)+1; 41 | bigN2 = 2*floor(2*M2)+1; 42 | X = zeros(bigN1,bigN2); 43 | 44 | % Initialization: preparing the lowpass filter at finest scale 45 | window_length_1 = floor(2*M1) - floor(M1) - 1 - (mod(N1,3)==0); 46 | window_length_2 = floor(2*M2) - floor(M2) - 1 - (mod(N2,3)==0); 47 | coord_1 = 0:(1/window_length_1):1; 48 | coord_2 = 0:(1/window_length_2):1; 49 | [wl_1,wr_1] = fdct_wrapping_window(coord_1); 50 | [wl_2,wr_2] = fdct_wrapping_window(coord_2); 51 | lowpass_1 = [wl_1, ones(1,2*floor(M1)+1), wr_1]; 52 | if mod(N1,3)==0, lowpass_1 = [0, lowpass_1, 0]; end; 53 | lowpass_2 = [wl_2, ones(1,2*floor(M2)+1), wr_2]; 54 | if mod(N2,3)==0, lowpass_2 = [0, lowpass_2, 0]; end; 55 | lowpass = lowpass_1'*lowpass_2; 56 | 57 | scales = nbscales:-1:2; 58 | 59 | else 60 | 61 | M1 = M1/2; 62 | M2 = M2/2; 63 | 64 | bigN1 = 2*floor(2*M1)+1; 65 | bigN2 = 2*floor(2*M2)+1; 66 | X = zeros(bigN1,bigN2); 67 | 68 | window_length_1 = floor(2*M1) - floor(M1) - 1; 69 | window_length_2 = floor(2*M2) - floor(M2) - 1; 70 | coord_1 = 0:(1/window_length_1):1; 71 | coord_2 = 0:(1/window_length_2):1; 72 | [wl_1,wr_1] = fdct_wrapping_window(coord_1); 73 | [wl_2,wr_2] = fdct_wrapping_window(coord_2); 74 | lowpass_1 = [wl_1, ones(1,2*floor(M1)+1), wr_1]; 75 | lowpass_2 = [wl_2, ones(1,2*floor(M2)+1), wr_2]; 76 | lowpass = lowpass_1'*lowpass_2; 77 | hipass_finest = sqrt(1 - lowpass.^2); 78 | 79 | scales = (nbscales-1):-1:2; 80 | 81 | end; 82 | 83 | % Loop: pyramidal reconstruction 84 | 85 | Xj_topleft_1 = 1; 86 | Xj_topleft_2 = 1; 87 | for j = scales, 88 | 89 | M1 = M1/2; 90 | M2 = M2/2; 91 | window_length_1 = floor(2*M1) - floor(M1) - 1; 92 | window_length_2 = floor(2*M2) - floor(M2) - 1; 93 | coord_1 = 0:(1/window_length_1):1; 94 | coord_2 = 0:(1/window_length_2):1; 95 | [wl_1,wr_1] = fdct_wrapping_window(coord_1); 96 | [wl_2,wr_2] = fdct_wrapping_window(coord_2); 97 | lowpass_1 = [wl_1, ones(1,2*floor(M1)+1), wr_1]; 98 | lowpass_2 = [wl_2, ones(1,2*floor(M2)+1), wr_2]; 99 | lowpass_next = lowpass_1'*lowpass_2; 100 | hipass = sqrt(1 - lowpass_next.^2); 101 | Xj = zeros(2*floor(4*M1)+1,2*floor(4*M2)+1); 102 | 103 | % Loop: angles 104 | l = 0; 105 | nbquadrants = 2 + 2*(~is_real); 106 | nbangles_perquad = nbangles(j)/4; 107 | for quadrant = 1:nbquadrants 108 | 109 | M_horiz = M2 * (mod(quadrant,2)==1) + M1 * (mod(quadrant,2)==0); 110 | M_vert = M1 * (mod(quadrant,2)==1) + M2 * (mod(quadrant,2)==0); 111 | if mod(nbangles_perquad,2), 112 | wedge_ticks_left = round((0:(1/(2*nbangles_perquad)):.5)*2*floor(4*M_horiz) + 1); 113 | wedge_ticks_right = 2*floor(4*M_horiz) + 2 - wedge_ticks_left; 114 | wedge_ticks = [wedge_ticks_left, wedge_ticks_right(end:-1:1)]; 115 | else 116 | wedge_ticks_left = round((0:(1/(2*nbangles_perquad)):.5)*2*floor(4*M_horiz) + 1); 117 | wedge_ticks_right = 2*floor(4*M_horiz) + 2 - wedge_ticks_left; 118 | wedge_ticks = [wedge_ticks_left, wedge_ticks_right((end-1):-1:1)]; 119 | end; 120 | wedge_endpoints = wedge_ticks(2:2:(end-1)); % integers 121 | wedge_midpoints = (wedge_endpoints(1:(end-1)) + wedge_endpoints(2:end))/2; 122 | 123 | % Left corner wedge 124 | 125 | l = l+1; 126 | first_wedge_endpoint_vert = round(2*floor(4*M_vert)/(2*nbangles_perquad) + 1); 127 | length_corner_wedge = floor(4*M_vert) - floor(M_vert) + ceil(first_wedge_endpoint_vert/4); 128 | Y_corner = 1:length_corner_wedge; 129 | [XX,YY] = meshgrid(1:(2*floor(4*M_horiz)+1),Y_corner); 130 | width_wedge = wedge_endpoints(2) + wedge_endpoints(1) - 1; 131 | slope_wedge = (floor(4*M_horiz) + 1 - wedge_endpoints(1))/floor(4*M_vert); 132 | left_line = round(2 - wedge_endpoints(1) + slope_wedge*(Y_corner - 1)); 133 | [wrapped_XX, wrapped_YY] = deal(zeros(length_corner_wedge,width_wedge)); 134 | first_row = floor(4*M_vert)+2-ceil((length_corner_wedge+1)/2)+... 135 | mod(length_corner_wedge+1,2)*(quadrant-2 == mod(quadrant-2,2)); 136 | first_col = floor(4*M_horiz)+2-ceil((width_wedge+1)/2)+... 137 | mod(width_wedge+1,2)*(quadrant-3 == mod(quadrant-3,2)); 138 | 139 | for row = Y_corner 140 | cols = left_line(row) + mod((0:(width_wedge-1))-(left_line(row)-first_col),width_wedge); 141 | new_row = 1 + mod(row - first_row, length_corner_wedge); 142 | admissible_cols = round(1/2*(cols+1+abs(cols-1))); 143 | wrapped_XX(new_row,:) = XX(row,admissible_cols); 144 | wrapped_YY(new_row,:) = YY(row,admissible_cols); 145 | end; 146 | 147 | slope_wedge_right = (floor(4*M_horiz)+1 - wedge_midpoints(1))/floor(4*M_vert); 148 | mid_line_right = wedge_midpoints(1) + slope_wedge_right*(wrapped_YY - 1); 149 | % not integers 150 | % in general 151 | coord_right = 1/2 + floor(4*M_vert)/(wedge_endpoints(2) - wedge_endpoints(1)) * ... 152 | (wrapped_XX - mid_line_right)./(floor(4*M_vert)+1 - wrapped_YY); 153 | C2 = 1/(1/(2*(floor(4*M_horiz))/(wedge_endpoints(1) - 1) - 1) + 1/(2*(floor(4*M_vert))/(first_wedge_endpoint_vert - 1) - 1)); 154 | C1 = C2 / (2*(floor(4*M_vert))/(first_wedge_endpoint_vert - 1) - 1); 155 | wrapped_XX((wrapped_XX - 1)/floor(4*M_horiz) + (wrapped_YY-1)/floor(4*M_vert) == 2) = ... 156 | wrapped_XX((wrapped_XX - 1)/floor(4*M_horiz) + (wrapped_YY-1)/floor(4*M_vert) == 2) + 1; 157 | coord_corner = C1 + C2 * ((wrapped_XX - 1)/(floor(4*M_horiz)) - (wrapped_YY - 1)/(floor(4*M_vert))) ./ ... 158 | (2-((wrapped_XX - 1)/(floor(4*M_horiz)) + (wrapped_YY - 1)/(floor(4*M_vert)))); 159 | wl_left = fdct_wrapping_window(coord_corner); 160 | [wl_right,wr_right] = fdct_wrapping_window(coord_right); 161 | switch is_real 162 | case 0 163 | wrapped_data = fftshift(fft2(ifftshift(C{j}{l})))/sqrt(prod(size(C{j}{l}))); 164 | wrapped_data = rot90(wrapped_data,(quadrant-1)); 165 | case 1 166 | x = C{j}{l} + sqrt(-1)*C{j}{l+nbangles(j)/2}; 167 | wrapped_data = fftshift(fft2(ifftshift(x)))/sqrt(prod(size(x)))/sqrt(2); 168 | wrapped_data = rot90(wrapped_data,(quadrant-1)); 169 | end; 170 | wrapped_data = wrapped_data .* (wl_left .* wr_right); 171 | 172 | % Unwrapping data 173 | for row = Y_corner 174 | cols = left_line(row) + mod((0:(width_wedge-1))-(left_line(row)-first_col),width_wedge); 175 | admissible_cols = round(1/2*(cols+1+abs(cols-1))); 176 | new_row = 1 + mod(row - first_row, length_corner_wedge); 177 | Xj(row,admissible_cols) = Xj(row,admissible_cols) + wrapped_data(new_row,:); 178 | % We use the following property: in an assignment 179 | % A(B) = C where B and C are vectors, if 180 | % some value x repeats in B, then the 181 | % last occurrence of x is the one 182 | % corresponding to the eventual assignment. 183 | end; 184 | 185 | % Regular wedges 186 | length_wedge = floor(4*M_vert) - floor(M_vert); 187 | Y = 1:length_wedge; 188 | first_row = floor(4*M_vert)+2-ceil((length_wedge+1)/2)+... 189 | mod(length_wedge+1,2)*(quadrant-2 == mod(quadrant-2,2)); 190 | for subl = 2:(nbangles_perquad-1); 191 | l = l+1; 192 | width_wedge = wedge_endpoints(subl+1) - wedge_endpoints(subl-1) + 1; 193 | slope_wedge = ((floor(4*M_horiz)+1) - wedge_endpoints(subl))/floor(4*M_vert); 194 | left_line = round(wedge_endpoints(subl-1) + slope_wedge*(Y - 1)); 195 | [wrapped_XX, wrapped_YY] = deal(zeros(length_wedge,width_wedge)); 196 | first_col = floor(4*M_horiz)+2-ceil((width_wedge+1)/2)+... 197 | mod(width_wedge+1,2)*(quadrant-3 == mod(quadrant-3,2)); 198 | for row = Y 199 | cols = left_line(row) + mod((0:(width_wedge-1))-(left_line(row)-first_col),width_wedge); 200 | new_row = 1 + mod(row - first_row, length_wedge); 201 | wrapped_XX(new_row,:) = XX(row,cols); 202 | wrapped_YY(new_row,:) = YY(row,cols); 203 | end; 204 | slope_wedge_left = ((floor(4*M_horiz)+1) - wedge_midpoints(subl-1))/floor(4*M_vert); 205 | mid_line_left = wedge_midpoints(subl-1) + slope_wedge_left*(wrapped_YY - 1); 206 | coord_left = 1/2 + floor(4*M_vert)/(wedge_endpoints(subl) - wedge_endpoints(subl-1)) * ... 207 | (wrapped_XX - mid_line_left)./(floor(4*M_vert)+1 - wrapped_YY); 208 | slope_wedge_right = ((floor(4*M_horiz)+1) - wedge_midpoints(subl))/floor(4*M_vert); 209 | mid_line_right = wedge_midpoints(subl) + slope_wedge_right*(wrapped_YY - 1); 210 | coord_right = 1/2 + floor(4*M_vert)/(wedge_endpoints(subl+1) - wedge_endpoints(subl)) * ... 211 | (wrapped_XX - mid_line_right)./(floor(4*M_vert)+1 - wrapped_YY); 212 | wl_left = fdct_wrapping_window(coord_left); 213 | [wl_right,wr_right] = fdct_wrapping_window(coord_right); 214 | switch is_real 215 | case 0 216 | wrapped_data = fftshift(fft2(ifftshift(C{j}{l})))/sqrt(prod(size(C{j}{l}))); 217 | wrapped_data = rot90(wrapped_data,(quadrant-1)); 218 | case 1 219 | x = C{j}{l} + sqrt(-1)*C{j}{l+nbangles(j)/2}; 220 | wrapped_data = fftshift(fft2(ifftshift(x)))/sqrt(prod(size(x)))/sqrt(2); 221 | wrapped_data = rot90(wrapped_data,(quadrant-1)); 222 | end; 223 | wrapped_data = wrapped_data .* (wl_left .* wr_right); 224 | 225 | % Unwrapping data 226 | for row = Y 227 | cols = left_line(row) + mod((0:(width_wedge-1))-(left_line(row)-first_col),width_wedge); 228 | new_row = 1 + mod(row - first_row, length_wedge); 229 | Xj(row,cols) = Xj(row,cols) + wrapped_data(new_row,:); 230 | end; 231 | 232 | end; % for subl 233 | 234 | % Right corner wedge 235 | l = l+1; 236 | width_wedge = 4*floor(4*M_horiz) + 3 - wedge_endpoints(end) - wedge_endpoints(end-1); 237 | slope_wedge = ((floor(4*M_horiz)+1) - wedge_endpoints(end))/floor(4*M_vert); 238 | left_line = round(wedge_endpoints(end-1) + slope_wedge*(Y_corner - 1)); 239 | [wrapped_XX, wrapped_YY] = deal(zeros(length_corner_wedge,width_wedge)); 240 | first_row = floor(4*M_vert)+2-ceil((length_corner_wedge+1)/2)+... 241 | mod(length_corner_wedge+1,2)*(quadrant-2 == mod(quadrant-2,2)); 242 | first_col = floor(4*M_horiz)+2-ceil((width_wedge+1)/2)+... 243 | mod(width_wedge+1,2)*(quadrant-3 == mod(quadrant-3,2)); 244 | 245 | for row = Y_corner 246 | cols = left_line(row) + mod((0:(width_wedge-1))-(left_line(row)-first_col),width_wedge); 247 | admissible_cols = round(1/2*(cols+2*floor(4*M_horiz)+1-abs(cols-(2*floor(4*M_horiz)+1)))); 248 | new_row = 1 + mod(row - first_row, length_corner_wedge); 249 | wrapped_XX(new_row,:) = XX(row,admissible_cols); 250 | wrapped_YY(new_row,:) = YY(row,admissible_cols); 251 | end; 252 | YY = Y_corner'*ones(1,width_wedge); 253 | slope_wedge_left = ((floor(4*M_horiz)+1) - wedge_midpoints(end))/floor(4*M_vert); 254 | mid_line_left = wedge_midpoints(end) + slope_wedge_left*(wrapped_YY - 1); 255 | coord_left = 1/2 + floor(4*M_vert)/(wedge_endpoints(end) - wedge_endpoints(end-1)) * ... 256 | (wrapped_XX - mid_line_left)./(floor(4*M_vert)+1 - wrapped_YY); 257 | C2 = -1/(2*(floor(4*M_horiz))/(wedge_endpoints(end) - 1) - 1 + 1/(2*(floor(4*M_vert))/(first_wedge_endpoint_vert - 1) - 1)); 258 | C1 = -C2 * (2*(floor(4*M_horiz))/(wedge_endpoints(end) - 1) - 1); 259 | wrapped_XX((wrapped_XX - 1)/floor(4*M_horiz) == (wrapped_YY-1)/floor(4*M_vert)) = ... 260 | wrapped_XX((wrapped_XX - 1)/floor(4*M_horiz) == (wrapped_YY-1)/floor(4*M_vert)) - 1; 261 | coord_corner = C1 + C2 * (2-((wrapped_XX - 1)/(floor(4*M_horiz)) + (wrapped_YY - 1)/(floor(4*M_vert)))) ./ ... 262 | ((wrapped_XX - 1)/(floor(4*M_horiz)) - (wrapped_YY - 1)/(floor(4*M_vert))); 263 | wl_left = fdct_wrapping_window(coord_left); 264 | [wl_right,wr_right] = fdct_wrapping_window(coord_corner); 265 | switch is_real 266 | case 0 267 | wrapped_data = fftshift(fft2(ifftshift(C{j}{l})))/sqrt(prod(size(C{j}{l}))); 268 | wrapped_data = rot90(wrapped_data,(quadrant-1)); 269 | case 1 270 | x = C{j}{l} + sqrt(-1)*C{j}{l+nbangles(j)/2}; 271 | wrapped_data = fftshift(fft2(ifftshift(x)))/sqrt(prod(size(x)))/sqrt(2); 272 | wrapped_data = rot90(wrapped_data,(quadrant-1)); 273 | end; 274 | wrapped_data = wrapped_data .* (wl_left .* wr_right); 275 | 276 | % Unwrapping data 277 | for row = Y_corner 278 | cols = left_line(row) + mod((0:(width_wedge-1))-(left_line(row)-first_col),width_wedge); 279 | admissible_cols = round(1/2*(cols+2*floor(4*M_horiz)+1-abs(cols-(2*floor(4*M_horiz)+1)))); 280 | new_row = 1 + mod(row - first_row, length_corner_wedge); 281 | Xj(row,fliplr(admissible_cols)) = Xj(row,fliplr(admissible_cols)) + wrapped_data(new_row,end:-1:1); 282 | % We use the following property: in an assignment 283 | % A(B) = C where B and C are vectors, if 284 | % some value x repeats in B, then the 285 | % last occurrence of x is the one 286 | % corresponding to the eventual assignment. 287 | end; 288 | 289 | Xj = rot90(Xj); 290 | 291 | end; % for quadrant 292 | 293 | Xj = Xj .* lowpass; 294 | Xj_index1 = ((-floor(2*M1)):floor(2*M1)) + floor(4*M1) + 1; 295 | Xj_index2 = ((-floor(2*M2)):floor(2*M2)) + floor(4*M2) + 1; 296 | Xj(Xj_index1, Xj_index2) = Xj(Xj_index1, Xj_index2) .* hipass; 297 | 298 | loc_1 = Xj_topleft_1 + (0:(2*floor(4*M1))); 299 | loc_2 = Xj_topleft_2 + (0:(2*floor(4*M2))); 300 | X(loc_1,loc_2) = X(loc_1,loc_2) + Xj; 301 | 302 | % Preparing for loop reentry or exit 303 | 304 | Xj_topleft_1 = Xj_topleft_1 + floor(4*M1) - floor(2*M1); 305 | Xj_topleft_2 = Xj_topleft_2 + floor(4*M2) - floor(2*M2); 306 | 307 | lowpass = lowpass_next; 308 | 309 | end; % for j 310 | 311 | if is_real 312 | Y = X; 313 | X = rot90(X,2); 314 | X = X + conj(Y); 315 | end 316 | 317 | % Coarsest wavelet level 318 | M1 = M1/2; 319 | M2 = M2/2; 320 | Xj = fftshift(fft2(ifftshift(C{1}{1})))/sqrt(prod(size(C{1}{1}))); 321 | loc_1 = Xj_topleft_1 + (0:(2*floor(4*M1))); 322 | loc_2 = Xj_topleft_2 + (0:(2*floor(4*M2))); 323 | X(loc_1,loc_2) = X(loc_1,loc_2) + Xj .* lowpass; 324 | 325 | % Finest level 326 | M1 = N1/3; 327 | M2 = N2/3; 328 | if finest == 1, 329 | 330 | % Folding back onto N1-by-N2 matrix 331 | shift_1 = floor(2*M1)-floor(N1/2); 332 | shift_2 = floor(2*M2)-floor(N2/2); 333 | Y = X(:,(1:N2)+shift_2); 334 | Y(:,N2-shift_2+(1:shift_2)) = Y(:,N2-shift_2+(1:shift_2)) + X(:,1:shift_2); 335 | Y(:,1:shift_2) = Y(:,1:shift_2) + X(:,N2+shift_2+(1:shift_2)); 336 | X = Y((1:N1)+shift_1,:); 337 | X(N1-shift_1+(1:shift_1),:) = X(N1-shift_1+(1:shift_1),:) + Y(1:shift_1,:); 338 | X(1:shift_1,:) = X(1:shift_1,:) + Y(N1+shift_1+(1:shift_1),:); 339 | 340 | else 341 | 342 | % Extension to a N1-by-N2 matrix 343 | Y = fftshift(fft2(ifftshift(C{nbscales}{1})))/sqrt(prod(size(C{nbscales}{1}))); 344 | X_topleft_1 = ceil((N1+1)/2) - floor(M1); 345 | X_topleft_2 = ceil((N2+1)/2) - floor(M2); 346 | loc_1 = X_topleft_1 + (0:(2*floor(M1))); 347 | loc_2 = X_topleft_2 + (0:(2*floor(M2))); 348 | Y(loc_1,loc_2) = Y(loc_1,loc_2) .* hipass_finest + X; 349 | X = Y; 350 | 351 | end; 352 | 353 | x = fftshift(ifft2(ifftshift(X)))*sqrt(prod(size(X))); 354 | if is_real, x = real(x); end; 355 | --------------------------------------------------------------------------------