Source code for chambolle
# chambolle.py - Solve nonlinear TV-denoising using Chambolle's projection algorithm
#
# Author: Stefan Fuertinger [stefan.fuertinger@gmx.at]
# Created: August 22 2012
# Last modified: <2017-09-14 11:02:15>
from __future__ import division
import numpy as np
import scipy as sp
from difftools import fidop2d
##########################################################################################
[docs]def chambolle(ut, Dx=None, Dy=None, mu=1.0e-5, dt=0.25, itmax=10000, tol=1.0e-3):
"""
Solve the nonlinear TV-denoising problem using Chambolle's projection algorithm
Parameters
----------
ut : NumPy 2darray
Raw grey-scale image to denoise. Note that `ut` has to be square!
Dx : NumPy/SciPy matrix
Discrete derivative operator in direction `x` (forward differences are recommended).
Note that if `ut` is `N`-by-`N` then `Dx` has to be `N**2`-by-`N**2`!
Dy : NumPy/SciPy matrix
Discrete derivative operator in direction `y` (forward differences are recommended).
Note that if `ut` is `N`-by-`N` then `Dy` has to be `N**2`-by-`N**2`!
mu : non-negative float
Regularization parameter in the TV functional. Note that `mu >= 0` has to hold!
dt : positive float
Pseudo time step. Note that `dt` has to satisfy `0 < dt < 1`.
itmax : positive integer
Maximal number of Chambolle iterations. Note that `itmax` has to be > 0!
tol : positive float
Error tolerance in Chambolle iterations. Note that `tol` has to satisfy `0 < tol << 1`!
Returns
-------
u : NumPy 2darray
Denoised Chambolle-image. Has the same dimension as the input image `ut`.
p : NumPy 3darray
Dual variable ("Chambolle-edge-set"). If `ut` is `N`-by-`N` then `p` is `2`-by-`N`-by-`N`, i.e.
a tensor.
References
----------
.. [1] A. Chambolle. An algorithm for total variation minimization and applications.
Journal of Mathematical Imaging and Vision, 20(1-2):89-97, 2004.
"""
# Sanity checks
if type(ut).__name__ != "ndarray":
raise TypeError("ut has to be a (square) NumPy ndarray!")
else:
if len(ut.shape) > 2: raise ValueError("ut has to be 2-dimensional!")
N = ut.shape[0]
try: M = ut.shape[1]
except: raise ValueError("ut has to be square!")
if N!=M: raise ValueError("ut has to be square!")
if Dx != None:
if type(Dx).__name__.rfind("matrix") == -1:
raise TypeError("Dx has to be a SciPy/NumPy matrix!")
else:
NN = Dx.shape[0]
if NN != Dx.shape[1]: raise ValueError("Dx has to be a square matrix!")
if NN != N**2: raise ValueError("Dx has to be of dimension %s**2 = %s"%(repr(N),repr(N**2)))
if Dy != None:
if type(Dy).__name__.rfind("matrix") == -1:
raise TypeError("Dy has to be a SciPy/NumPy matrix!")
else:
NN = Dy.shape[0]
if NN != Dy.shape[1]: raise ValueError("Dy has to be a square matrix!")
if NN != N**2: raise ValueError("Dy has to be of dimension %s**2 = %s"%(repr(N),repr(N**2)))
try: mu = float(mu)
except: raise TypeError("mu has to be a non-negative scalar!")
if mu < 0: raise ValueError("mu has to be non-negative!")
try: dt = float(dt)
except: raise TypeError("dt has to be a positive scalar!")
if dt <= 0 or dt > 1: raise ValueError("dt has to be 0 < dt < 1!")
try: itmax = int(itmax)
except: raise TypeError("itmax has to be a positive scalar!")
if itmax <= 0: raise ValueError("itmax has to be > 0!")
try: tol = float(tol)
except: raise TypeError("tol has to be a positive scalar!")
if tol <= 0 or tol > 1: raise ValueError("tol has to be 0 < tol << 1!")
# Now start to actually do something: get squared image dimension
NN = N**2
# If code was called without operators generate them
if Dx == None or Dy == None:
Dx,Dy = fidop2d(N,'xy','f')
# Convert the image to a vector
ut = ut.flatten(1)
# Initialize necessary variables
ux = np.zeros((NN,))
uy = np.zeros((NN,))
gu = np.zeros((NN,))
dn = np.zeros((NN,))
dp = np.zeros((NN,))
du = np.zeros((NN,))
u = np.zeros((NN,))
p = np.zeros((2,N,N))
# Iteration parameters
relerr = 2*tol
res = 0
relres = 0
it = 0
ep1 = 1.0e-8
nt = np.linalg.norm(ut)
# Start Chambolle Iteration
while relerr > tol and it <= itmax:
# Update iteration counter
it += 1
# grad u
ux = Dx*u
uy = Dy*u
# |grad u|
gu = np.sqrt(ux**2 + uy**2)
# p = (1 + dt grad u)p / (1 + dt |grad u|)
dn = 1 + dt*gu
p[0,:] = (p[0,:] + dt*ux.reshape(N,N,order="F"))/dn.reshape(N,N,order="F")
p[1,:] = (p[1,:] + dt*uy.reshape(N,N,order="F"))/dn.reshape(N,N,order="F")
# div p
dp = -Dx.T*p[0,:].flatten(1) - Dy.T*p[1,:].flatten(1)
# residual
res = np.sum(np.abs(mu*(u - dp) - ut))
# u = ut + mu * div p
du = u.copy()
u = ut/mu + dp
du = u - du
# Relative error
relerr = np.linalg.norm(u + ep1)
relerr = np.linalg.norm(du)/relerr
relres = res/nt
# Print output to prompt
print "it = %s, |du|/|u|= %s, |res|/|ut| = %s"%(repr(it),repr(relerr),repr(relres))
# Modify stopping criterion
relerr = relres
# Convert u back to matrix and compute Chambolle image
u = u.reshape(N,N,order="F")
u = u*mu
return u, p