mirror of
https://github.com/wassname/scikit-image.git
synced 2026-06-28 19:01:23 +08:00
09876408fc
Fix some sphinx warnings Add documentation build to test Add documentation build to test Remove change in numpydoc Remove change in apigen Add makefile target for html and add to travis script Add a makefile target for html and add to travis script Fix more sphinx warnings
197 lines
7.4 KiB
Python
197 lines
7.4 KiB
Python
import numpy as np
|
|
from .dtype import img_as_float
|
|
|
|
|
|
__all__ = ['random_noise']
|
|
|
|
|
|
def random_noise(image, mode='gaussian', seed=None, clip=True, **kwargs):
|
|
"""
|
|
Function to add random noise of various types to a floating-point image.
|
|
|
|
Parameters
|
|
----------
|
|
image : ndarray
|
|
Input image data. Will be converted to float.
|
|
mode : str
|
|
One of the following strings, selecting the type of noise to add:
|
|
|
|
- 'gaussian' Gaussian-distributed additive noise.
|
|
- 'localvar' Gaussian-distributed additive noise, with specified
|
|
local variance at each point of `image`
|
|
- 'poisson' Poisson-distributed noise generated from the data.
|
|
- 'salt' Replaces random pixels with 1.
|
|
- 'pepper' Replaces random pixels with 0.
|
|
- 's&p' Replaces random pixels with 0 or 1.
|
|
- 'speckle' Multiplicative noise using out = image + n*image, where
|
|
n is uniform noise with specified mean & variance.
|
|
seed : int
|
|
If provided, this will set the random seed before generating noise,
|
|
for valid pseudo-random comparisons.
|
|
clip : bool
|
|
If True (default), the output will be clipped after noise applied
|
|
for modes `'speckle'`, `'poisson'`, and `'gaussian'`. This is
|
|
needed to maintain the proper image data range. If False, clipping
|
|
is not applied, and the output may extend beyond the range [-1, 1].
|
|
mean : float
|
|
Mean of random distribution. Used in 'gaussian' and 'speckle'.
|
|
Default : 0.
|
|
var : float
|
|
Variance of random distribution. Used in 'gaussian' and 'speckle'.
|
|
Note: variance = (standard deviation) ** 2. Default : 0.01
|
|
local_vars : ndarray
|
|
Array of positive floats, same shape as `image`, defining the local
|
|
variance at every image point. Used in 'localvar'.
|
|
amount : float
|
|
Proportion of image pixels to replace with noise on range [0, 1].
|
|
Used in 'salt', 'pepper', and 'salt & pepper'. Default : 0.05
|
|
salt_vs_pepper : float
|
|
Proportion of salt vs. pepper noise for 's&p' on range [0, 1].
|
|
Higher values represent more salt. Default : 0.5 (equal amounts)
|
|
|
|
Returns
|
|
-------
|
|
out : ndarray
|
|
Output floating-point image data on range [0, 1] or [-1, 1] if the
|
|
input `image` was unsigned or signed, respectively.
|
|
|
|
Notes
|
|
-----
|
|
Speckle, Poisson, Localvar, and Gaussian noise may generate noise outside
|
|
the valid image range. The default is to clip (not alias) these values,
|
|
but they may be preserved by setting `clip=False`. Note that in this case
|
|
the output may contain values outside the ranges [0, 1] or [-1, 1].
|
|
Use this option with care.
|
|
|
|
Because of the prevalence of exclusively positive floating-point images in
|
|
intermediate calculations, it is not possible to intuit if an input is
|
|
signed based on dtype alone. Instead, negative values are explicity
|
|
searched for. Only if found does this function assume signed input.
|
|
Unexpected results only occur in rare, poorly exposes cases (e.g. if all
|
|
values are above 50 percent gray in a signed `image`). In this event,
|
|
manually scaling the input to the positive domain will solve the problem.
|
|
|
|
The Poisson distribution is only defined for positive integers. To apply
|
|
this noise type, the number of unique values in the image is found and
|
|
the next round power of two is used to scale up the floating-point result,
|
|
after which it is scaled back down to the floating-point image range.
|
|
|
|
To generate Poisson noise against a signed image, the signed image is
|
|
temporarily converted to an unsigned image in the floating point domain,
|
|
Poisson noise is generated, then it is returned to the original range.
|
|
|
|
"""
|
|
mode = mode.lower()
|
|
|
|
# Detect if a signed image was input
|
|
if image.min() < 0:
|
|
low_clip = -1.
|
|
else:
|
|
low_clip = 0.
|
|
|
|
image = img_as_float(image)
|
|
if seed is not None:
|
|
np.random.seed(seed=seed)
|
|
|
|
allowedtypes = {
|
|
'gaussian': 'gaussian_values',
|
|
'localvar': 'localvar_values',
|
|
'poisson': 'poisson_values',
|
|
'salt': 'sp_values',
|
|
'pepper': 'sp_values',
|
|
's&p': 's&p_values',
|
|
'speckle': 'gaussian_values'}
|
|
|
|
kwdefaults = {
|
|
'mean': 0.,
|
|
'var': 0.01,
|
|
'amount': 0.05,
|
|
'salt_vs_pepper': 0.5,
|
|
'local_vars': np.zeros_like(image) + 0.01}
|
|
|
|
allowedkwargs = {
|
|
'gaussian_values': ['mean', 'var'],
|
|
'localvar_values': ['local_vars'],
|
|
'sp_values': ['amount'],
|
|
's&p_values': ['amount', 'salt_vs_pepper'],
|
|
'poisson_values': []}
|
|
|
|
for key in kwargs:
|
|
if key not in allowedkwargs[allowedtypes[mode]]:
|
|
raise ValueError('%s keyword not in allowed keywords %s' %
|
|
(key, allowedkwargs[allowedtypes[mode]]))
|
|
|
|
# Set kwarg defaults
|
|
for kw in allowedkwargs[allowedtypes[mode]]:
|
|
kwargs.setdefault(kw, kwdefaults[kw])
|
|
|
|
if mode == 'gaussian':
|
|
noise = np.random.normal(kwargs['mean'], kwargs['var'] ** 0.5,
|
|
image.shape)
|
|
out = image + noise
|
|
|
|
elif mode == 'localvar':
|
|
# Ensure local variance input is correct
|
|
if (kwargs['local_vars'] <= 0).any():
|
|
raise ValueError('All values of `local_vars` must be > 0.')
|
|
|
|
# Safe shortcut usage broadcasts kwargs['local_vars'] as a ufunc
|
|
out = image + np.random.normal(0, kwargs['local_vars'] ** 0.5)
|
|
|
|
elif mode == 'poisson':
|
|
# Determine unique values in image & calculate the next power of two
|
|
vals = len(np.unique(image))
|
|
vals = 2 ** np.ceil(np.log2(vals))
|
|
|
|
# Ensure image is exclusively positive
|
|
if low_clip == -1.:
|
|
old_max = image.max()
|
|
image = (image + 1.) / (old_max + 1.)
|
|
|
|
# Generating noise for each unique value in image.
|
|
out = np.random.poisson(image * vals) / float(vals)
|
|
|
|
# Return image to original range if input was signed
|
|
if low_clip == -1.:
|
|
out = out * (old_max + 1.) - 1.
|
|
|
|
elif mode == 'salt':
|
|
# Re-call function with mode='s&p' and p=1 (all salt noise)
|
|
out = random_noise(image, mode='s&p', seed=seed,
|
|
amount=kwargs['amount'], salt_vs_pepper=1.)
|
|
|
|
elif mode == 'pepper':
|
|
# Re-call function with mode='s&p' and p=1 (all pepper noise)
|
|
out = random_noise(image, mode='s&p', seed=seed,
|
|
amount=kwargs['amount'], salt_vs_pepper=0.)
|
|
|
|
elif mode == 's&p':
|
|
# This mode makes no effort to avoid repeat sampling. Thus, the
|
|
# exact number of replaced pixels is only approximate.
|
|
out = image.copy()
|
|
|
|
# Salt mode
|
|
num_salt = np.ceil(
|
|
kwargs['amount'] * image.size * kwargs['salt_vs_pepper'])
|
|
coords = [np.random.randint(0, i - 1, int(num_salt))
|
|
for i in image.shape]
|
|
out[coords] = 1
|
|
|
|
# Pepper mode
|
|
num_pepper = np.ceil(
|
|
kwargs['amount'] * image.size * (1. - kwargs['salt_vs_pepper']))
|
|
coords = [np.random.randint(0, i - 1, int(num_pepper))
|
|
for i in image.shape]
|
|
out[coords] = low_clip
|
|
|
|
elif mode == 'speckle':
|
|
noise = np.random.normal(kwargs['mean'], kwargs['var'] ** 0.5,
|
|
image.shape)
|
|
out = image + image * noise
|
|
|
|
# Clip back to original range, if necessary
|
|
if clip:
|
|
out = np.clip(out, low_clip, 1.0)
|
|
|
|
return out
|