#!/usr/bin/env python # coding: utf-8 # Wavelet Block Thresholding # ========================== # # *Important:* Please read the [installation page](http://gpeyre.github.io/numerical-tours/installation_python/) for details about how to install the toolboxes. # $\newcommand{\dotp}[2]{\langle #1, #2 \rangle}$ # $\newcommand{\enscond}[2]{\lbrace #1, #2 \rbrace}$ # $\newcommand{\pd}[2]{ \frac{ \partial #1}{\partial #2} }$ # $\newcommand{\umin}[1]{\underset{#1}{\min}\;}$ # $\newcommand{\umax}[1]{\underset{#1}{\max}\;}$ # $\newcommand{\umin}[1]{\underset{#1}{\min}\;}$ # $\newcommand{\uargmin}[1]{\underset{#1}{argmin}\;}$ # $\newcommand{\norm}[1]{\|#1\|}$ # $\newcommand{\abs}[1]{\left|#1\right|}$ # $\newcommand{\choice}[1]{ \left\{ \begin{array}{l} #1 \end{array} \right. }$ # $\newcommand{\pa}[1]{\left(#1\right)}$ # $\newcommand{\diag}[1]{{diag}\left( #1 \right)}$ # $\newcommand{\qandq}{\quad\text{and}\quad}$ # $\newcommand{\qwhereq}{\quad\text{where}\quad}$ # $\newcommand{\qifq}{ \quad \text{if} \quad }$ # $\newcommand{\qarrq}{ \quad \Longrightarrow \quad }$ # $\newcommand{\ZZ}{\mathbb{Z}}$ # $\newcommand{\CC}{\mathbb{C}}$ # $\newcommand{\RR}{\mathbb{R}}$ # $\newcommand{\EE}{\mathbb{E}}$ # $\newcommand{\Zz}{\mathcal{Z}}$ # $\newcommand{\Ww}{\mathcal{W}}$ # $\newcommand{\Vv}{\mathcal{V}}$ # $\newcommand{\Nn}{\mathcal{N}}$ # $\newcommand{\NN}{\mathcal{N}}$ # $\newcommand{\Hh}{\mathcal{H}}$ # $\newcommand{\Bb}{\mathcal{B}}$ # $\newcommand{\Ee}{\mathcal{E}}$ # $\newcommand{\Cc}{\mathcal{C}}$ # $\newcommand{\Gg}{\mathcal{G}}$ # $\newcommand{\Ss}{\mathcal{S}}$ # $\newcommand{\Pp}{\mathcal{P}}$ # $\newcommand{\Ff}{\mathcal{F}}$ # $\newcommand{\Xx}{\mathcal{X}}$ # $\newcommand{\Mm}{\mathcal{M}}$ # $\newcommand{\Ii}{\mathcal{I}}$ # $\newcommand{\Dd}{\mathcal{D}}$ # $\newcommand{\Ll}{\mathcal{L}}$ # $\newcommand{\Tt}{\mathcal{T}}$ # $\newcommand{\si}{\sigma}$ # $\newcommand{\al}{\alpha}$ # $\newcommand{\la}{\lambda}$ # $\newcommand{\ga}{\gamma}$ # $\newcommand{\Ga}{\Gamma}$ # $\newcommand{\La}{\Lambda}$ # $\newcommand{\si}{\sigma}$ # $\newcommand{\Si}{\Sigma}$ # $\newcommand{\be}{\beta}$ # $\newcommand{\de}{\delta}$ # $\newcommand{\De}{\Delta}$ # $\newcommand{\phi}{\varphi}$ # $\newcommand{\th}{\theta}$ # $\newcommand{\om}{\omega}$ # $\newcommand{\Om}{\Omega}$ # This numerical tour presents block thresholding methods, # that makes use of the structure of wavelet coefficients of natural images to perform denoising. # Theoretical properties of block thresholding were investigated # in [CaiSilv](#biblio) [Cai99](#biblio) [HallKerkPic99](#biblio) # In[44]: from __future__ import division import numpy as np import scipy as scp import pylab as pyl import matplotlib.pyplot as plt from nt_toolbox.general import * from nt_toolbox.signal import * import warnings warnings.filterwarnings('ignore') get_ipython().run_line_magic('matplotlib', 'inline') get_ipython().run_line_magic('load_ext', 'autoreload') get_ipython().run_line_magic('autoreload', '2') # Generating a Noisy Image # ------------------------ # Here we use an additive Gaussian noise. # # # Size of the image of $N=n \times n$ pixels. # In[45]: n = 256 # First we load an image $f_0 \in \RR^N$. # In[46]: f0 = rescale(load_image("nt_toolbox/data/boat.bmp", n)) # Display it. # In[47]: plt.figure(figsize = (5,5)) imageplot(f0) # Noise level. # In[48]: sigma = .08 # Generate a noisy image $f=f_0+\epsilon$ where $\epsilon \sim # \Nn(0,\si^2\text{Id}_N)$. # In[49]: from numpy import random f = f0 + sigma*random.standard_normal((n,n)) # Display it. # In[50]: plt.figure(figsize = (5,5)) imageplot(clamp(f)) # Orthogonal Wavelet Thresholding # ------------------------------- # We first consider the traditional # wavelet thresholding method. # # # Parameters for the orthogonal wavelet transform. # In[51]: Jmin = 4 # Shortcuts for the foward and backward wavelet transforms. # In[52]: from nt_toolbox.perform_wavelet_transf import * wav = lambda f : perform_wavelet_transf(f, Jmin, +1) iwav = lambda fw : perform_wavelet_transf(fw, Jmin, -1) # Display the original set of noisy coefficients. # In[53]: plt.figure(figsize=(10,10)) plot_wavelet(wav(f), Jmin) plt.show() # Denoting $\Ww$ and $\Ww^*$ the forward and backward wavelet # transform, wavelet thresholding $\tilde f$ is defined as # # $$ \tilde f = \Ww^* \circ \theta_T \circ \Ww(f) $$ # # where $T>0$ is the threshold, that should be adapted to the noise # level. # # # The thresholding operator is applied component-wise # # $$ \th_T(x)_i = \psi_T(x_i) x_i $$ # # where $\psi_T$ is an atenuation fonction. In this tour, we use the James # Stein (JS) attenuation: # # $$ \psi_T(s) = \max\pa{ 0, 1-\frac{T^2}{s^2} } $$ # In[54]: psi = lambda s,T : np.maximum(1-T**2/np.maximum(abs(s)**2, 1e-9*np.ones(np.shape(s))), np.zeros(np.shape(s))) # Display the thresholding function $\th_T$. # In[55]: s = np.linspace(-3, 3, 1024) plt.plot(s, s*psi(s,1)) plt.plot(s, s, 'r--') plt.show() # Thresholding operator. # In[56]: theta = lambda x,T : psi(x, T)*x ThreshWav = lambda f,T : iwav(theta(wav(f), T)) # Test the thresholding. # In[57]: T = 1.5*sigma plt.figure(figsize=(5,5)) imageplot(clamp(ThreshWav(f, T))) # __Exercise 1__ # # Display the evolution of the denoising SNR when $T$ varies. # Store in $f_{Thresh}$ the optimal denoising result. # In[58]: run -i nt_solutions/denoisingwav_4_block/exo1 # In[59]: ## Insert your code here. # Display the optimal thresolding. # In[60]: plt.figure(figsize = (5,5)) imageplot(clamp(fThresh), "SNR = %.1f dB" %snr(f0, fThresh)) # Block Thresholding Operator # --------------------------- # A block thresholding operator of coefficients $x=(x_i)_{i=1}^P \in \RR^P$ is defined # using a partition $B$ into a set of blocks $b$ # # $$ \{1,\ldots,P\} = \bigcup_{b \in B} b. $$ # Its definition reads # # $$ \forall i \in b, \quad # \theta_T(x)_i = \psi_T\left( \norm{x_b}_2 \right) x_i $$ # # where $ x_b = (x_j)_{j \in B} \in \RR^{\abs{b}} $. # One thus thresholds the $\ell^2$ norm (the energy) of each block rather # than each coefficient independently. # # # For image-based thresholding, we use a partition in square blocks of # equal size $w \times w$. # # # The block size $w$. # In[61]: w = 4 n = 256 # Compute indexing of the blocks. # In[62]: [X,Y,dX,dY] = np.meshgrid(np.arange(1,n-w+2,w),np.arange(1,n-w+2,w),np.arange(0,w),np.arange(0,w)) I = (X + dX-1) + (Y + dY-1)*n for k in range(n//w): for l in range(n//w): I[k][l] = np.transpose(I[k][l]) # Block extraction operator. It returns the set $ \{x_b\}_{b \in B} $ # of block-partitioned coefficients. # In[63]: block = lambda x : np.ravel(x)[I] # Block reconstruction operator. # In[64]: def assign(M,I,H): M_temp = M np.ravel(M_temp)[I] = H return np.reshape(M_temp,(n,n)) iblock = lambda H : assign(np.zeros([n,n]), I, H) # Check that block extraction / reconstruction gives perfect # reconstruction. # In[65]: from numpy import linalg print("Should be 0:", linalg.norm(f - iblock(block(f)))) # Compute the average energy of each block, and duplicate. # In[66]: def energy(H): H_tmp = np.copy(H) for i in range(n//w): for j in range(n//w): H_tmp[i][j] = np.sqrt(np.mean(H_tmp[i][j]**2))#*np.ones([1,1]) return H_tmp # Block thresholding operator. # In[67]: Thresh = lambda H,T : psi(energy(H), T)*H ThreshBlock = lambda x,T : iblock(Thresh(block(x), T)) # __Exercise 2__ # # Test the effect of block thresholding on the image $f_0$ itself, for increasing value of $T$. # Of course directly thresholding the image has no interest, this is just # to vizualize the effect. # In[68]: run -i nt_solutions/denoisingwav_4_block/exo2 # In[69]: ## Insert your code here. # Orthogonal Wavelet Block Thresholding # ------------------------------------- # Wavelet coefficients of natural images are not independant one from each # other. One can thus improve the denoising results by thresholding block # of coefficients togethers. Block thresholding is only efficient when # used as a soft thresholder. Here we use a Stein soft thresholder. # # # Display the thresholded coefficients for a threshold value $T$ proportional to the noise level $\si$. # In[70]: T = 1.25*sigma plt.figure(figsize=(10,10)) plot_wavelet(ThreshBlock(wav(f), T), Jmin) plt.show() # Define the wavelet block thresholding operator. # In[71]: ThreshWav = lambda f, T: iwav(ThreshBlock(wav(f), T)) # Test the thresholding. # In[72]: plt.figure(figsize=(5,5)) imageplot(clamp(ThreshWav(f, T))) # __Exercise 3__ # # Display the evolution of the denoising SNR when $T$ varies. # Store the optimal denoising result in $f_{Block}$. # In[73]: run -i nt_solutions/denoisingwav_4_block/exo3 # In[74]: ## Insert your code here. # Display the result. # In[75]: plt.figure(figsize=(5,5)) imageplot(clamp(fBlock), "SNR = %.1f dB" %snr(f0, fBlock)) # Translation invariant Block Thresholding # ---------------------------------------- # Block thresholding can also be applied to a translation invariant wavelet # transform. It gives state of the art denoising results. # # # # Shortcuts for the foward and backward translation invariant wavelet transforms. # In[76]: wav = lambda f: perform_wavelet_transf(f, Jmin, + 1, ti=1) iwav = lambda fw: perform_wavelet_transf(fw, Jmin, -1, ti=1) # Foward wavelet transform. # In[77]: fw = wav(f) n = 256 #np.shape(fw)[0] # Compute indexing of the blocks. # In[78]: [X,J,Y,dX,dY] = np.meshgrid(np.arange(1,n-w+2,w),np.arange(1,np.shape(fw)[0]+1),np.arange(1,n-w+2,w),np.arange(0,w),np.arange(0,w)) I = (X + dX-1) + (Y + dY-1)*n + (J-1)*n**2 for k in range(n//w): for l in range(n//w): for m in range(np.shape(fw)[0]): I[m][k][l] = np.transpose(I[m][k][l]) # Forward and backward extraction operators. # In[79]: block = lambda x : np.ravel(x)[I] def assign(M,I,H): M_temp = M np.ravel(M_temp)[I] = H return np.reshape(M_temp,(np.shape(fw)[0],n,n)) iblock = lambda H : assign(np.zeros([np.shape(fw)[0],n,n]), I, H) # Compute the average energy of each block, and duplicate. # In[80]: def energy(H): H_tmp = np.copy(H) for i in range(n//w): for j in range(n//w): for k in range(np.shape(fw)[0]): H_tmp[k][i][j] = np.sqrt(np.mean(H_tmp[k][i][j]**2)) return H_tmp # Block thresholding operator. # In[81]: Thresh = lambda H,T : psi(energy(H), T)*H ThreshBlock = lambda x,T : iblock(Thresh(block(x), T)) # Define the wavelet block thresholding operator. # In[82]: ThreshWav = lambda f,T : iwav(ThreshBlock(wav(f), T)) # Test the thresholding. # In[ ]: T = 1.25*sigma plt.figure(figsize = (5,5)) imageplot(clamp(ThreshWav(f, T))) # __Exercise 4__ # # Display the evolution of the denoising SNR when $T$ varies. # Store the optimal denoising result in $f_{TI}$. # In[ ]: run -i nt_solutions/denoisingwav_4_block/exo4 # In[ ]: ## Insert your code here. # Display the result. # In[ ]: plt.figure(figsize = (5,5)) imageplot(clamp(fTI), "SNR = %.1f dB" %snr(f0, fTI)) # Bibliography # ------------ # # # # * [CaiSil01] T. Cai and B.W. Silverman, [Incorporating information on neighboring coefficients into wavelet estimation][1], Sankhya 63, 127-148, 2001. # * [Cai99] T. Cai, [Adaptive wavelet estimation: a block thresholding and oracle inequality approach][2], The Annals of Statistics 27, 898-924, 1999. # * [HallKerkPic99] P. Hall, G. Kerkyacharian and D. Picard, _On the minimax optimality of block thresholded wavelet estimator_, Statistica Sinica 9(1999), 33-49 # # [1]:http://sankhya.isical.ac.in/search/63b2/caifnl.html # [2]:http://dx.doi.org/10.1214/aos/1018031262