@@ -389,6 +389,15 @@ class PrincipalComponents:
389389 A method to return a reduced set of principal components based
390390 on either a fixed number of components or a fraction of total
391391 variance.
392+
393+ `denoise`:
394+
395+ A callable function to denoise data using a reduced set of
396+ principal components.
397+
398+ `get_denoising_transform`:
399+
400+ A callable function that returns a function for denoising data.
392401 '''
393402 def __init__ (self , vals , vecs , stats ):
394403 from .transforms import LinearTransform
@@ -462,6 +471,73 @@ def reduce(self, N=0, **kwargs):
462471 raise Exception ('Must specify one of the following keywords:'
463472 '`num`, `eigs`, `fraction`.' )
464473
474+ def denoise (self , X , ** kwargs ):
475+ '''Returns a de-noised version of `X`.
476+
477+ Arguments:
478+
479+ `X` (np.ndarray):
480+
481+ Data to be de-noised. Can be a single pixel or an image.
482+
483+ Keyword Arguments (one of the following must be specified):
484+
485+ `num` (integer):
486+
487+ Number of eigenvalues/eigenvectors to use. The top `num`
488+ eigenvalues will be used.
489+
490+ `eigs` (list):
491+
492+ A list of indices of eigenvalues/eigenvectors to be used.
493+
494+ `fraction` (float):
495+
496+ The fraction of total image variance to retain. Eigenvalues
497+ will be included (starting from greatest to smallest) until
498+ `fraction` of total image variance is retained.
499+
500+ Returns denoised image data with same shape as `X`.
501+
502+ Note that calling this method is equivalent to calling the
503+ `get_denoising_transform` method with same keyword and applying the
504+ returned transform to `X`. If you only intend to denoise data with the
505+ same parameters multiple times, then it is more efficient to get the
506+ denoising transform and reuse it, rather than calling this method
507+ multilple times.
508+ '''
509+ f = self .get_denoising_transform (** kwargs )
510+ return f (X )
511+
512+ def get_denoising_transform (self , ** kwargs ):
513+ '''Returns a function for denoising image data.
514+
515+ Keyword Arguments (one of the following must be specified):
516+
517+ `num` (integer):
518+
519+ Number of eigenvalues/eigenvectors to use. The top `num`
520+ eigenvalues will be used.
521+
522+ `eigs` (list):
523+
524+ A list of indices of eigenvalues/eigenvectors to be used.
525+
526+ `fraction` (float):
527+
528+ The fraction of total image variance to retain. Eigenvalues
529+ will be included (starting from greatest to smallest) until
530+ `fraction` of total image variance is retained.
531+
532+ Returns a callable :class:`~spectral.algorithms.transforms.LinearTransform`
533+ object for denoising image data.
534+ '''
535+ from .transforms import LinearTransform
536+ V = self .reduce (self , ** kwargs ).eigenvectors
537+ f = LinearTransform (V .dot (V .T ), pre = - self .mean ,
538+ post = self .mean )
539+ return f
540+
465541
466542def principal_components (image ):
467543 '''
@@ -500,6 +576,15 @@ def principal_components(image):
500576 `reduce`:
501577
502578 A method to reduce the number of eigenvalues.
579+
580+ `denoise`:
581+
582+ A callable function to denoise data using a reduced set of
583+ principal components.
584+
585+ `get_denoising_transform`:
586+
587+ A callable function that returns a function for denoising data.
503588 '''
504589 from numpy import sqrt , sum
505590
@@ -674,7 +759,7 @@ class GaussianStats(object):
674759 `principal_components`:
675760
676761 The principal components of the data, based on mean and cov.
677- '''
762+ '''
678763
679764 def __init__ (self , mean = None , cov = None , nsamples = None , inv_cov = None ):
680765 self .cov = cov
@@ -698,7 +783,7 @@ def inv_cov(self):
698783 if self ._inv_cov is None :
699784 self ._inv_cov = np .linalg .inv (self ._cov )
700785 return self ._inv_cov
701-
786+
702787 def reset_derived_stats (self ):
703788 self ._cov = self ._inv_cov = None
704789 self ._sqrt_cov = self ._sqrt_inv_cov = self ._pcs = None
@@ -717,7 +802,7 @@ def sqrt_cov(self):
717802 pcs .eigenvectors ),
718803 symmetric = True )
719804 return self ._sqrt_cov
720-
805+
721806 @property
722807 def sqrt_inv_cov (self ):
723808 '''Property method returning matrix square root of inverse of cov.
@@ -732,7 +817,7 @@ def sqrt_inv_cov(self):
732817 symmetric = True ,
733818 inverse = True )
734819 return self ._sqrt_inv_cov
735-
820+
736821 @property
737822 def principal_components (self ):
738823 if self ._pcs is None :
@@ -884,7 +969,7 @@ def size(self):
884969 else :
885970 return np .sum (np .not_equal (self .mask , 0 ).ravel ())
886971
887-
972+
888973 def calc_stats (self ):
889974 '''
890975 Calculates statistics for the class.
@@ -1019,7 +1104,7 @@ def save(self, filename, calc_stats=False):
10191104 c .calc_stats ()
10201105 f = open (filename , 'wb' )
10211106 ids = sorted (self .classes .keys ())
1022- pickle .dump (self .classes [ids [0 ]].mask , f )
1107+ pickle .dump (self .classes [ids [0 ]].mask , f )
10231108 pickle .dump (len (self ), f )
10241109 for id in ids :
10251110 c = self .classes [id ]
@@ -1048,7 +1133,7 @@ def load(self, filename, image):
10481133 c .nbands = len (mean )
10491134 self .add_class (c )
10501135 f .close
1051-
1136+
10521137
10531138def create_training_classes (image , class_mask , calc_stats = False , indices = None ):
10541139 '''
@@ -1376,9 +1461,9 @@ def msam(data, members):
13761461 '''
13771462 # The modifications to the `spectral_angles` function were contributed by
13781463 # Christian Mielke.
1379-
1464+
13801465 import math
1381-
1466+
13821467 assert members .shape [1 ] == data .shape [2 ], \
13831468 'Matrix dimensions are not aligned.'
13841469
@@ -1443,7 +1528,7 @@ def noise_from_diffs(X, direction='lowerright'):
14431528 deltas = X [:, :- 1 , :] - X [:, 1 :, :]
14441529 else :
14451530 deltas = X [:- 1 , :, :] - X [1 :, :, :]
1446-
1531+
14471532 stats = calc_stats (deltas )
14481533 stats .cov /= 2.0
14491534 return stats
@@ -1668,7 +1753,7 @@ def mnf(signal, noise):
16681753 wstats = GaussianStats (mean = np .zeros_like (L ), cov = C )
16691754 napc = PrincipalComponents (L , V , wstats )
16701755 return MNFResult (signal , noise , napc )
1671-
1756+
16721757def ppi (X , niters , threshold = 0 , centered = False , start = None , display = 0 ,
16731758 ** imshow_kwargs ):
16741759 '''Returns pixel purity indices for an image.
@@ -1743,7 +1828,7 @@ def ppi(X, niters, threshold=0, centered=False, start=None, display=0,
17431828 from numbers import Integral
17441829 import spectral as spy
17451830 from spectral .algorithms .algorithms import calc_stats
1746-
1831+
17471832
17481833 if display is not None :
17491834 if not isinstance (display , Integral ) or isinstance (display , bool ) or \
@@ -1758,7 +1843,7 @@ def ppi(X, niters, threshold=0, centered=False, start=None, display=0,
17581843 shape = X .shape
17591844 X = X .reshape (- 1 , X .shape [- 1 ])
17601845 nbands = X .shape [- 1 ]
1761-
1846+
17621847 fig = None
17631848 updating = False
17641849
@@ -1814,7 +1899,7 @@ def ppi(X, niters, threshold=0, centered=False, start=None, display=0,
18141899 'values may be corrupt. Returning None'
18151900 spy ._status .write (msg )
18161901 return None
1817-
1902+
18181903 spy ._status .end_percentage ()
18191904
18201905 return counts .reshape (shape [:2 ])
0 commit comments