================================================================= Image reconstruction (Forward-Backward, Total Variation, L2-norm) ================================================================= This tutorial presents an image reconstruction problem solved by the Forward-Backward splitting algorithm. The convex optimization problem is the sum of a data fidelity term and a regularization term which expresses a prior on the smoothness of the solution, given by .. math:: \min\limits_x \tau \|g(x)-y\|_2^2 + \|x\|_\text{TV} where :math:`\|\cdot\|_\text{TV}` denotes the total variation, `y` are the measurements, `g` is a masking operator and :math:`\tau` expresses the trade-off between the two terms. Load an image and convert it to grayscale .. plot:: :context: reset >>> import matplotlib.image as mpimg >>> import numpy as np >>> try: ... im_original = mpimg.imread('tutorials/barbara.png') ... except: ... im_original = mpimg.imread('doc/tutorials/barbara.png') and generate a random masking matrix .. plot:: :context: >>> np.random.seed(14) # Reproducible results. >>> mask = np.random.uniform(size=im_original.shape) >>> mask = mask > 0.85 which masks 85% of the pixels. The masked image is given by .. plot:: :context: >>> g = lambda x: mask * x >>> im_masked = g(im_original) The prior objective to minimize is defined by .. math:: f_1(x) = \|x\|_\text{TV} which can be expressed by the toolbox TV-norm function object, instantiated with .. plot:: :context: >>> from pyunlocbox import functions >>> f1 = functions.norm_tv(maxit=50, dim=2) The fidelity objective to minimize is defined by .. math:: f_2(x) = \tau \|g(x)-y\|_2^2 which can be expressed by the toolbox L2-norm function object, instantiated with .. plot:: :context: >>> tau = 100 >>> f2 = functions.norm_l2(y=im_masked, A=g, lambda_=tau) .. note:: We set :math:`\tau` to a large value as we trust our measurements and want the solution to be close to them. For noisy measurements a lower value should be considered. The step size for optimal convergence is :math:`\frac{1}{\beta}` where :math:`\beta=2\tau` is the Lipschitz constant of the gradient of :math:`f_2` :cite:`beck2009FISTA`. The Forward-Backward splitting algorithm is instantiated with .. plot:: :context: >>> from pyunlocbox import solvers >>> solver = solvers.forward_backward(step=0.5/tau) and the problem solved with .. plot:: :context: >>> x0 = np.array(im_masked) # Make a copy to preserve im_masked. >>> ret = solvers.solve([f1, f2], x0, solver, maxit=100) Solution found after 78 iterations: objective function f(sol) = 6.723857e+03 stopping criterion: RTOL Let's display the results: .. plot:: :context: >>> import matplotlib.pyplot as plt >>> fig = plt.figure(figsize=(8, 2.5)) >>> ax1 = fig.add_subplot(1, 3, 1) >>> _ = ax1.imshow(im_original, cmap='gray') >>> _ = ax1.axis('off') >>> _ = ax1.set_title('Original image') >>> ax2 = fig.add_subplot(1, 3, 2) >>> _ = ax2.imshow(im_masked, cmap='gray') >>> _ = ax2.axis('off') >>> _ = ax2.set_title('Masked image') >>> ax3 = fig.add_subplot(1, 3, 3) >>> _ = ax3.imshow(ret['sol'], cmap='gray') >>> _ = ax3.axis('off') >>> _ = ax3.set_title('Reconstructed image') The above figure shows a good reconstruction which is both smooth (the TV prior) and close to the measurements (the L2 fidelity).