2011-07-06 07:47:59 +08:00
|
|
|
"""
|
|
|
|
|
=========================================
|
|
|
|
|
Image denoising using dictionary learning
|
|
|
|
|
=========================================
|
2011-08-24 16:40:14 +08:00
|
|
|
|
2011-07-06 07:47:59 +08:00
|
|
|
An example comparing the effect of reconstructing noisy fragments
|
2015-11-26 04:50:10 +08:00
|
|
|
of a raccoon face image using firstly online :ref:`DictionaryLearning` and
|
2013-07-22 21:48:44 +08:00
|
|
|
various transform methods.
|
2011-08-23 19:05:24 +08:00
|
|
|
|
2012-11-13 16:42:31 +08:00
|
|
|
The dictionary is fitted on the distorted left half of the image, and
|
|
|
|
|
subsequently used to reconstruct the right half. Note that even better
|
|
|
|
|
performance could be achieved by fitting to an undistorted (i.e.
|
|
|
|
|
noiseless) image, but here we start from the assumption that it is not
|
|
|
|
|
available.
|
2011-08-24 16:07:18 +08:00
|
|
|
|
2011-08-24 16:40:14 +08:00
|
|
|
A common practice for evaluating the results of image denoising is by looking
|
|
|
|
|
at the difference between the reconstruction and the original image. If the
|
2014-06-02 04:53:23 +08:00
|
|
|
reconstruction is perfect this will look like Gaussian noise.
|
2011-08-24 16:40:14 +08:00
|
|
|
|
|
|
|
|
It can be seen from the plots that the results of :ref:`omp` with two
|
2011-09-17 12:02:43 +08:00
|
|
|
non-zero coefficients is a bit less biased than when keeping only one
|
|
|
|
|
(the edges look less prominent). It is in addition closer from the ground
|
|
|
|
|
truth in Frobenius norm.
|
2011-08-24 16:40:14 +08:00
|
|
|
|
|
|
|
|
The result of :ref:`least_angle_regression` is much more strongly biased: the
|
|
|
|
|
difference is reminiscent of the local intensity value of the original image.
|
|
|
|
|
|
2011-08-25 06:57:02 +08:00
|
|
|
Thresholding is clearly not useful for denoising, but it is here to show that
|
|
|
|
|
it can produce a suggestive output with very high speed, and thus be useful
|
|
|
|
|
for other tasks such as object classification, where performance is not
|
|
|
|
|
necessarily related to visualisation.
|
|
|
|
|
|
2011-07-06 07:47:59 +08:00
|
|
|
"""
|
2013-02-01 22:04:03 +08:00
|
|
|
print(__doc__)
|
2011-07-06 07:47:59 +08:00
|
|
|
|
|
|
|
|
from time import time
|
|
|
|
|
|
2014-05-15 04:31:03 +08:00
|
|
|
import matplotlib.pyplot as plt
|
2011-07-06 07:47:59 +08:00
|
|
|
import numpy as np
|
2015-11-26 04:50:10 +08:00
|
|
|
import scipy as sp
|
2011-12-08 03:16:04 +08:00
|
|
|
|
2011-09-17 23:55:16 +08:00
|
|
|
from sklearn.decomposition import MiniBatchDictionaryLearning
|
2011-09-16 18:15:57 +08:00
|
|
|
from sklearn.feature_extraction.image import extract_patches_2d
|
|
|
|
|
from sklearn.feature_extraction.image import reconstruct_from_patches_2d
|
2015-11-26 04:50:10 +08:00
|
|
|
|
2011-07-06 07:47:59 +08:00
|
|
|
|
2017-06-03 05:36:33 +08:00
|
|
|
try: # SciPy >= 0.16 have face in misc
|
|
|
|
|
from scipy.misc import face
|
|
|
|
|
face = face(gray=True)
|
|
|
|
|
except ImportError:
|
2015-11-26 04:50:10 +08:00
|
|
|
face = sp.face(gray=True)
|
|
|
|
|
|
|
|
|
|
# Convert from uint8 representation with values between 0 and 255 to
|
|
|
|
|
# a floating point representation with values between 0 and 1.
|
2017-06-06 20:36:13 +08:00
|
|
|
face = face / 255.
|
2011-08-23 19:05:24 +08:00
|
|
|
|
|
|
|
|
# downsample for higher speed
|
2019-04-24 02:56:11 +08:00
|
|
|
face = face[::4, ::4] + face[1::4, ::4] + face[::4, 1::4] + face[1::4, 1::4]
|
2015-11-26 04:50:10 +08:00
|
|
|
face /= 4.0
|
|
|
|
|
height, width = face.shape
|
2011-08-23 01:41:15 +08:00
|
|
|
|
2011-08-23 19:05:24 +08:00
|
|
|
# Distort the right half of the image
|
2013-02-01 22:04:03 +08:00
|
|
|
print('Distorting image...')
|
2015-11-26 04:50:10 +08:00
|
|
|
distorted = face.copy()
|
|
|
|
|
distorted[:, width // 2:] += 0.075 * np.random.randn(height, width // 2)
|
2011-08-23 01:41:15 +08:00
|
|
|
|
2012-11-13 16:42:31 +08:00
|
|
|
# Extract all reference patches from the left half of the image
|
2013-02-01 22:04:03 +08:00
|
|
|
print('Extracting reference patches...')
|
2011-09-16 18:15:57 +08:00
|
|
|
t0 = time()
|
2011-09-09 10:43:13 +08:00
|
|
|
patch_size = (7, 7)
|
2015-11-26 04:50:10 +08:00
|
|
|
data = extract_patches_2d(distorted[:, :width // 2], patch_size)
|
2011-07-06 07:47:59 +08:00
|
|
|
data = data.reshape(data.shape[0], -1)
|
2011-09-09 10:43:13 +08:00
|
|
|
data -= np.mean(data, axis=0)
|
|
|
|
|
data /= np.std(data, axis=0)
|
2013-02-01 22:04:03 +08:00
|
|
|
print('done in %.2fs.' % (time() - t0))
|
2011-07-06 07:47:59 +08:00
|
|
|
|
2017-06-20 20:48:57 +08:00
|
|
|
# #############################################################################
|
2012-11-13 16:42:31 +08:00
|
|
|
# Learn the dictionary from reference patches
|
2011-09-16 18:15:57 +08:00
|
|
|
|
2013-02-01 22:04:03 +08:00
|
|
|
print('Learning the dictionary...')
|
2011-08-24 01:37:09 +08:00
|
|
|
t0 = time()
|
2012-11-03 19:35:12 +08:00
|
|
|
dico = MiniBatchDictionaryLearning(n_components=100, alpha=1, n_iter=500)
|
2011-07-06 07:47:59 +08:00
|
|
|
V = dico.fit(data).components_
|
2011-08-24 04:44:19 +08:00
|
|
|
dt = time() - t0
|
2013-02-01 22:04:03 +08:00
|
|
|
print('done in %.2fs.' % dt)
|
2011-08-24 04:44:19 +08:00
|
|
|
|
2014-05-15 04:31:03 +08:00
|
|
|
plt.figure(figsize=(4.2, 4))
|
2011-08-25 01:41:28 +08:00
|
|
|
for i, comp in enumerate(V[:100]):
|
2014-05-15 04:31:03 +08:00
|
|
|
plt.subplot(10, 10, i + 1)
|
|
|
|
|
plt.imshow(comp.reshape(patch_size), cmap=plt.cm.gray_r,
|
2014-05-15 10:35:13 +08:00
|
|
|
interpolation='nearest')
|
2014-05-15 04:31:03 +08:00
|
|
|
plt.xticks(())
|
|
|
|
|
plt.yticks(())
|
2015-11-26 04:50:10 +08:00
|
|
|
plt.suptitle('Dictionary learned from face patches\n' +
|
2014-05-15 10:35:13 +08:00
|
|
|
'Train time %.1fs on %d patches' % (dt, len(data)),
|
|
|
|
|
fontsize=16)
|
2014-05-15 04:31:03 +08:00
|
|
|
plt.subplots_adjust(0.08, 0.02, 0.92, 0.85, 0.08, 0.23)
|
2011-07-06 07:47:59 +08:00
|
|
|
|
2011-12-20 22:34:17 +08:00
|
|
|
|
2017-06-20 20:48:57 +08:00
|
|
|
# #############################################################################
|
2011-09-16 18:15:57 +08:00
|
|
|
# Display the distorted image
|
2011-08-24 17:01:21 +08:00
|
|
|
|
2011-08-24 05:59:36 +08:00
|
|
|
def show_with_diff(image, reference, title):
|
2011-08-24 17:01:21 +08:00
|
|
|
"""Helper function to display denoising"""
|
2014-05-15 04:31:03 +08:00
|
|
|
plt.figure(figsize=(5, 3.3))
|
|
|
|
|
plt.subplot(1, 2, 1)
|
|
|
|
|
plt.title('Image')
|
2015-11-26 04:50:10 +08:00
|
|
|
plt.imshow(image, vmin=0, vmax=1, cmap=plt.cm.gray,
|
|
|
|
|
interpolation='nearest')
|
2014-05-15 04:31:03 +08:00
|
|
|
plt.xticks(())
|
|
|
|
|
plt.yticks(())
|
|
|
|
|
plt.subplot(1, 2, 2)
|
2011-08-24 16:07:18 +08:00
|
|
|
difference = image - reference
|
|
|
|
|
|
2014-05-15 04:31:03 +08:00
|
|
|
plt.title('Difference (norm: %.2f)' % np.sqrt(np.sum(difference ** 2)))
|
|
|
|
|
plt.imshow(difference, vmin=-0.5, vmax=0.5, cmap=plt.cm.PuOr,
|
2014-05-15 10:35:13 +08:00
|
|
|
interpolation='nearest')
|
2014-05-15 04:31:03 +08:00
|
|
|
plt.xticks(())
|
|
|
|
|
plt.yticks(())
|
|
|
|
|
plt.suptitle(title, size=16)
|
|
|
|
|
plt.subplots_adjust(0.02, 0.02, 0.98, 0.79, 0.02, 0.2)
|
2011-08-24 05:59:36 +08:00
|
|
|
|
2015-11-26 04:50:10 +08:00
|
|
|
show_with_diff(distorted, face, 'Distorted image')
|
2011-08-22 05:07:17 +08:00
|
|
|
|
2017-06-20 20:48:57 +08:00
|
|
|
# #############################################################################
|
2011-08-23 19:05:24 +08:00
|
|
|
# Extract noisy patches and reconstruct them using the dictionary
|
2011-09-16 18:15:57 +08:00
|
|
|
|
2013-02-01 22:04:03 +08:00
|
|
|
print('Extracting noisy patches... ')
|
2011-09-16 18:15:57 +08:00
|
|
|
t0 = time()
|
2015-11-26 04:50:10 +08:00
|
|
|
data = extract_patches_2d(distorted[:, width // 2:], patch_size)
|
2011-09-09 10:43:13 +08:00
|
|
|
data = data.reshape(data.shape[0], -1)
|
|
|
|
|
intercept = np.mean(data, axis=0)
|
|
|
|
|
data -= intercept
|
2013-02-01 22:04:03 +08:00
|
|
|
print('done in %.2fs.' % (time() - t0))
|
2011-08-23 19:05:24 +08:00
|
|
|
|
|
|
|
|
transform_algorithms = [
|
2011-08-25 06:57:02 +08:00
|
|
|
('Orthogonal Matching Pursuit\n1 atom', 'omp',
|
2011-08-25 01:41:28 +08:00
|
|
|
{'transform_n_nonzero_coefs': 1}),
|
2011-08-25 06:57:02 +08:00
|
|
|
('Orthogonal Matching Pursuit\n2 atoms', 'omp',
|
2011-08-25 01:41:28 +08:00
|
|
|
{'transform_n_nonzero_coefs': 2}),
|
2011-09-17 12:02:43 +08:00
|
|
|
('Least-angle regression\n5 atoms', 'lars',
|
2012-12-25 20:16:05 +08:00
|
|
|
{'transform_n_nonzero_coefs': 5}),
|
2011-08-25 06:57:02 +08:00
|
|
|
('Thresholding\n alpha=0.1', 'threshold', {'transform_alpha': .1})]
|
2011-08-23 19:05:24 +08:00
|
|
|
|
2011-08-23 01:41:15 +08:00
|
|
|
reconstructions = {}
|
2011-08-25 01:41:28 +08:00
|
|
|
for title, transform_algorithm, kwargs in transform_algorithms:
|
2013-02-01 22:04:03 +08:00
|
|
|
print(title + '...')
|
2015-11-26 04:50:10 +08:00
|
|
|
reconstructions[title] = face.copy()
|
2011-08-22 05:07:17 +08:00
|
|
|
t0 = time()
|
2011-09-09 10:43:13 +08:00
|
|
|
dico.set_params(transform_algorithm=transform_algorithm, **kwargs)
|
2011-08-25 00:21:28 +08:00
|
|
|
code = dico.transform(data)
|
2011-08-25 06:57:02 +08:00
|
|
|
patches = np.dot(code, V)
|
|
|
|
|
|
|
|
|
|
patches += intercept
|
2011-08-22 05:54:13 +08:00
|
|
|
patches = patches.reshape(len(data), *patch_size)
|
2011-08-25 06:57:02 +08:00
|
|
|
if transform_algorithm == 'threshold':
|
|
|
|
|
patches -= patches.min()
|
|
|
|
|
patches /= patches.max()
|
2015-11-26 04:50:10 +08:00
|
|
|
reconstructions[title][:, width // 2:] = reconstruct_from_patches_2d(
|
|
|
|
|
patches, (height, width // 2))
|
2011-08-24 04:44:19 +08:00
|
|
|
dt = time() - t0
|
2013-02-01 22:04:03 +08:00
|
|
|
print('done in %.2fs.' % dt)
|
2015-11-26 04:50:10 +08:00
|
|
|
show_with_diff(reconstructions[title], face,
|
2011-08-24 05:59:36 +08:00
|
|
|
title + ' (time: %.1fs)' % dt)
|
2011-08-22 05:07:17 +08:00
|
|
|
|
2014-05-15 04:31:03 +08:00
|
|
|
plt.show()
|