This example demonstrates the use of class spline.SplineL1 for removing salt & pepper noise from a greyscale image using ℓ1-spline fitting.

from __future__ import print_function
from builtins import input
from builtins import range

import numpy as np

from sporco.admm import spline
from sporco import util
from sporco import metric
from sporco import plot

Load reference image.

img = util.ExampleImages().image('monarch.png', scaled=True,
                                 idxexp=np.s_[:,160:672], gray=True)

Construct test image corrupted by 20% salt & pepper noise.

imgn = util.spnoise(img, 0.2)

Set regularization parameter and options for ℓ1-spline solver. The regularization parameter used here has been manually selected for good performance.

lmbda = 5.0
opt = spline.SplineL1.Options({'Verbose': True, 'gEvalY': False})

Create solver object and solve, returning the the denoised image imgr.

b = spline.SplineL1(imgn, lmbda, opt)
imgr = b.solve()
Itn   Fnc       DFid      Reg       r         s         ρ
   0  4.14e+04  3.24e+04  1.80e+03  2.19e-01  3.80e+00  1.01e+01
   1  3.53e+04  3.36e+04  3.41e+02  1.15e-01  1.98e+00  1.01e+01
   2  3.89e+04  3.69e+04  4.08e+02  1.35e-01  3.57e-01  2.44e+00
   3  3.52e+04  3.36e+04  3.23e+02  1.05e-01  2.33e-01  1.50e+00
   4  3.32e+04  3.15e+04  3.25e+02  8.04e-02  1.21e-01  1.01e+00
   5  3.35e+04  3.19e+04  3.14e+02  6.72e-02  6.56e-02  8.20e-01
   6  3.22e+04  3.06e+04  3.16e+02  4.97e-02  5.36e-02  8.20e-01
   7  3.11e+04  2.95e+04  3.15e+02  3.69e-02  4.17e-02  8.20e-01
   8  3.09e+04  2.93e+04  3.15e+02  2.94e-02  2.84e-02  8.20e-01
   9  3.05e+04  2.90e+04  3.12e+02  2.31e-02  2.31e-02  8.20e-01
  10  3.02e+04  2.86e+04  3.14e+02  1.86e-02  1.82e-02  8.20e-01
  11  3.01e+04  2.85e+04  3.12e+02  1.56e-02  1.32e-02  8.20e-01
  12  2.99e+04  2.84e+04  3.13e+02  1.33e-02  1.06e-02  8.20e-01
  13  2.98e+04  2.83e+04  3.12e+02  1.14e-02  9.09e-03  9.16e-01
  14  2.98e+04  2.82e+04  3.12e+02  1.02e-02  7.41e-03  1.02e+00
  15  2.97e+04  2.82e+04  3.12e+02  9.12e-03  6.83e-03  1.20e+00
  16  2.97e+04  2.81e+04  3.12e+02  8.38e-03  6.08e-03  1.39e+00
  17  2.97e+04  2.81e+04  3.12e+02  7.72e-03  5.46e-03  1.63e+00
  18  2.96e+04  2.81e+04  3.12e+02  7.13e-03  5.33e-03  1.94e+00
  19  2.96e+04  2.80e+04  3.12e+02  6.57e-03  4.92e-03  2.24e+00
  20  2.96e+04  2.80e+04  3.12e+02  6.06e-03  4.84e-03  2.59e+00
  21  2.95e+04  2.80e+04  3.12e+02  5.55e-03  4.53e-03  2.90e+00
  22  2.95e+04  2.80e+04  3.13e+02  5.08e-03  4.33e-03  3.21e+00
  23  2.95e+04  2.79e+04  3.13e+02  4.66e-03  3.69e-03  3.21e+00
  24  2.95e+04  2.79e+04  3.14e+02  4.27e-03  3.71e-03  3.61e+00
  25  2.95e+04  2.79e+04  3.14e+02  3.92e-03  3.27e-03  3.61e+00
  26  2.95e+04  2.79e+04  3.15e+02  3.60e-03  2.92e-03  3.61e+00
  27  2.94e+04  2.79e+04  3.16e+02  3.31e-03  2.84e-03  4.01e+00
  28  2.94e+04  2.78e+04  3.16e+02  3.05e-03  2.60e-03  4.01e+00
  29  2.94e+04  2.78e+04  3.17e+02  2.81e-03  2.31e-03  4.01e+00
  30  2.94e+04  2.78e+04  3.18e+02  2.58e-03  2.29e-03  4.42e+00
  31  2.94e+04  2.78e+04  3.18e+02  2.38e-03  2.02e-03  4.42e+00
  32  2.94e+04  2.78e+04  3.19e+02  2.19e-03  1.83e-03  4.42e+00
  33  2.94e+04  2.78e+04  3.19e+02  2.03e-03  1.59e-03  4.42e+00
  34  2.94e+04  2.78e+04  3.20e+02  1.87e-03  1.61e-03  5.00e+00
  35  2.94e+04  2.78e+04  3.20e+02  1.72e-03  1.49e-03  5.00e+00
  36  2.94e+04  2.78e+04  3.21e+02  1.59e-03  1.30e-03  5.00e+00
  37  2.94e+04  2.78e+04  3.21e+02  1.46e-03  1.30e-03  5.53e+00
  38  2.94e+04  2.77e+04  3.22e+02  1.34e-03  1.18e-03  5.53e+00
  39  2.94e+04  2.77e+04  3.22e+02  1.24e-03  1.05e-03  5.53e+00
  40  2.93e+04  2.77e+04  3.22e+02  1.14e-03  9.51e-04  5.53e+00
  41  2.93e+04  2.77e+04  3.23e+02  1.05e-03  9.54e-04  6.06e+00
  42  2.93e+04  2.77e+04  3.23e+02  9.66e-04  8.27e-04  6.06e+00

Display solve time and denoising performance.

print("SplineL1 solve time: %5.2f s" % b.timer.elapsed('solve'))
print("Noisy image PSNR:    %5.2f dB" % metric.psnr(img, imgn))
print("Denoised image PSNR: %5.2f dB" % metric.psnr(img, imgr))
SplineL1 solve time:  1.80 s
Noisy image PSNR:    11.58 dB
Denoised image PSNR: 27.60 dB

Display reference, corrupted, and denoised images.

fig = plot.figure(figsize=(21, 7))
plot.subplot(1, 3, 1)
plot.imview(img, title='Reference', fig=fig)
plot.subplot(1, 3, 2)
plot.imview(imgn, title='Corrupted', fig=fig)
plot.subplot(1, 3, 3)
plot.imview(imgr, title=r'Restored ($\ell_1$-spline)', fig=fig)

Get iterations statistics from solver object and plot functional value, ADMM primary and dual residuals, and automatically adjusted ADMM penalty parameter against the iteration number.

its = b.getitstat()
fig = plot.figure(figsize=(20, 5))
plot.subplot(1, 3, 1)
plot.plot(its.ObjFun, xlbl='Iterations', ylbl='Functional', fig=fig)
plot.subplot(1, 3, 2)
plot.plot(np.vstack((its.PrimalRsdl, its.DualRsdl)).T,
          ptyp='semilogy', xlbl='Iterations', ylbl='Residual',
          lgnd=['Primal', 'Dual'], fig=fig)
plot.subplot(1, 3, 3)
plot.plot(its.Rho, xlbl='Iterations', ylbl='Penalty Parameter', fig=fig)