update
This commit is contained in:
849
.CondaPkg/env/Lib/site-packages/skimage/exposure/exposure.py
vendored
Normal file
849
.CondaPkg/env/Lib/site-packages/skimage/exposure/exposure.py
vendored
Normal file
@@ -0,0 +1,849 @@
|
||||
import numpy as np
|
||||
|
||||
from ..util.dtype import dtype_range, dtype_limits
|
||||
from .._shared import utils
|
||||
|
||||
|
||||
__all__ = [
|
||||
'histogram',
|
||||
'cumulative_distribution',
|
||||
'equalize_hist',
|
||||
'rescale_intensity',
|
||||
'adjust_gamma',
|
||||
'adjust_log',
|
||||
'adjust_sigmoid',
|
||||
]
|
||||
|
||||
|
||||
DTYPE_RANGE = dtype_range.copy()
|
||||
DTYPE_RANGE.update((d.__name__, limits) for d, limits in dtype_range.items())
|
||||
DTYPE_RANGE.update(
|
||||
{
|
||||
'uint10': (0, 2**10 - 1),
|
||||
'uint12': (0, 2**12 - 1),
|
||||
'uint14': (0, 2**14 - 1),
|
||||
'bool': dtype_range[bool],
|
||||
'float': dtype_range[np.float64],
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _offset_array(arr, low_boundary, high_boundary):
|
||||
"""Offset the array to get the lowest value at 0 if negative."""
|
||||
if low_boundary < 0:
|
||||
offset = low_boundary
|
||||
dyn_range = high_boundary - low_boundary
|
||||
# get smallest dtype that can hold both minimum and offset maximum
|
||||
offset_dtype = np.promote_types(
|
||||
np.min_scalar_type(dyn_range), np.min_scalar_type(low_boundary)
|
||||
)
|
||||
if arr.dtype != offset_dtype:
|
||||
# prevent overflow errors when offsetting
|
||||
arr = arr.astype(offset_dtype)
|
||||
arr = arr - offset
|
||||
return arr
|
||||
|
||||
|
||||
def _bincount_histogram_centers(image, source_range):
|
||||
"""Compute bin centers for bincount-based histogram."""
|
||||
if source_range not in ['image', 'dtype']:
|
||||
raise ValueError(f'Incorrect value for `source_range` argument: {source_range}')
|
||||
if source_range == 'image':
|
||||
image_min = int(image.min().astype(np.int64))
|
||||
image_max = int(image.max().astype(np.int64))
|
||||
elif source_range == 'dtype':
|
||||
image_min, image_max = dtype_limits(image, clip_negative=False)
|
||||
bin_centers = np.arange(image_min, image_max + 1)
|
||||
return bin_centers
|
||||
|
||||
|
||||
def _bincount_histogram(image, source_range, bin_centers=None):
|
||||
"""
|
||||
Efficient histogram calculation for an image of integers.
|
||||
|
||||
This function is significantly more efficient than np.histogram but
|
||||
works only on images of integers. It is based on np.bincount.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : array
|
||||
Input image.
|
||||
source_range : string
|
||||
'image' determines the range from the input image.
|
||||
'dtype' determines the range from the expected range of the images
|
||||
of that data type.
|
||||
|
||||
Returns
|
||||
-------
|
||||
hist : array
|
||||
The values of the histogram.
|
||||
bin_centers : array
|
||||
The values at the center of the bins.
|
||||
"""
|
||||
if bin_centers is None:
|
||||
bin_centers = _bincount_histogram_centers(image, source_range)
|
||||
image_min, image_max = bin_centers[0], bin_centers[-1]
|
||||
image = _offset_array(image, image_min, image_max)
|
||||
hist = np.bincount(image.ravel(), minlength=image_max - min(image_min, 0) + 1)
|
||||
if source_range == 'image':
|
||||
idx = max(image_min, 0)
|
||||
hist = hist[idx:]
|
||||
return hist, bin_centers
|
||||
|
||||
|
||||
def _get_outer_edges(image, hist_range):
|
||||
"""Determine the outer bin edges to use for `numpy.histogram`.
|
||||
|
||||
These are obtained from either the image or hist_range.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : ndarray
|
||||
Image for which the histogram is to be computed.
|
||||
hist_range: 2-tuple of int or None
|
||||
Range of values covered by the histogram bins. If None, the minimum
|
||||
and maximum values of `image` are used.
|
||||
|
||||
Returns
|
||||
-------
|
||||
first_edge, last_edge : int
|
||||
The range spanned by the histogram bins.
|
||||
|
||||
Notes
|
||||
-----
|
||||
This function is adapted from ``np.lib.histograms._get_outer_edges``.
|
||||
"""
|
||||
if hist_range is not None:
|
||||
first_edge, last_edge = hist_range
|
||||
if first_edge > last_edge:
|
||||
raise ValueError("max must be larger than min in hist_range parameter.")
|
||||
if not (np.isfinite(first_edge) and np.isfinite(last_edge)):
|
||||
raise ValueError(
|
||||
f'supplied hist_range of [{first_edge}, {last_edge}] is ' f'not finite'
|
||||
)
|
||||
elif image.size == 0:
|
||||
# handle empty arrays. Can't determine hist_range, so use 0-1.
|
||||
first_edge, last_edge = 0, 1
|
||||
else:
|
||||
first_edge, last_edge = image.min(), image.max()
|
||||
if not (np.isfinite(first_edge) and np.isfinite(last_edge)):
|
||||
raise ValueError(
|
||||
f'autodetected hist_range of [{first_edge}, {last_edge}] is '
|
||||
f'not finite'
|
||||
)
|
||||
|
||||
# expand empty hist_range to avoid divide by zero
|
||||
if first_edge == last_edge:
|
||||
first_edge = first_edge - 0.5
|
||||
last_edge = last_edge + 0.5
|
||||
|
||||
return first_edge, last_edge
|
||||
|
||||
|
||||
def _get_bin_edges(image, nbins, hist_range):
|
||||
"""Computes histogram bins for use with `numpy.histogram`.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : ndarray
|
||||
Image for which the histogram is to be computed.
|
||||
nbins : int
|
||||
The number of bins.
|
||||
hist_range: 2-tuple of int
|
||||
Range of values covered by the histogram bins.
|
||||
|
||||
Returns
|
||||
-------
|
||||
bin_edges : ndarray
|
||||
The histogram bin edges.
|
||||
|
||||
Notes
|
||||
-----
|
||||
This function is a simplified version of
|
||||
``np.lib.histograms._get_bin_edges`` that only supports uniform bins.
|
||||
"""
|
||||
first_edge, last_edge = _get_outer_edges(image, hist_range)
|
||||
# numpy/gh-10322 means that type resolution rules are dependent on array
|
||||
# shapes. To avoid this causing problems, we pick a type now and stick
|
||||
# with it throughout.
|
||||
bin_type = np.result_type(first_edge, last_edge, image)
|
||||
if np.issubdtype(bin_type, np.integer):
|
||||
bin_type = np.result_type(bin_type, float)
|
||||
|
||||
# compute bin edges
|
||||
bin_edges = np.linspace(
|
||||
first_edge, last_edge, nbins + 1, endpoint=True, dtype=bin_type
|
||||
)
|
||||
return bin_edges
|
||||
|
||||
|
||||
def _get_numpy_hist_range(image, source_range):
|
||||
if source_range == 'image':
|
||||
hist_range = None
|
||||
elif source_range == 'dtype':
|
||||
hist_range = dtype_limits(image, clip_negative=False)
|
||||
else:
|
||||
raise ValueError(f'Incorrect value for `source_range` argument: {source_range}')
|
||||
return hist_range
|
||||
|
||||
|
||||
@utils.channel_as_last_axis(multichannel_output=False)
|
||||
def histogram(
|
||||
image, nbins=256, source_range='image', normalize=False, *, channel_axis=None
|
||||
):
|
||||
"""Return histogram of image.
|
||||
|
||||
Unlike `numpy.histogram`, this function returns the centers of bins and
|
||||
does not rebin integer arrays. For integer arrays, each integer value has
|
||||
its own bin, which improves speed and intensity-resolution.
|
||||
|
||||
If `channel_axis` is not set, the histogram is computed on the flattened
|
||||
image. For color or multichannel images, set ``channel_axis`` to use a
|
||||
common binning for all channels. Alternatively, one may apply the function
|
||||
separately on each channel to obtain a histogram for each color channel
|
||||
with separate binning.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : array
|
||||
Input image.
|
||||
nbins : int, optional
|
||||
Number of bins used to calculate histogram. This value is ignored for
|
||||
integer arrays.
|
||||
source_range : string, optional
|
||||
'image' (default) determines the range from the input image.
|
||||
'dtype' determines the range from the expected range of the images
|
||||
of that data type.
|
||||
normalize : bool, optional
|
||||
If True, normalize the histogram by the sum of its values.
|
||||
channel_axis : int or None, optional
|
||||
If None, the image is assumed to be a grayscale (single channel) image.
|
||||
Otherwise, this parameter indicates which axis of the array corresponds
|
||||
to channels.
|
||||
|
||||
Returns
|
||||
-------
|
||||
hist : array
|
||||
The values of the histogram. When ``channel_axis`` is not None, hist
|
||||
will be a 2D array where the first axis corresponds to channels.
|
||||
bin_centers : array
|
||||
The values at the center of the bins.
|
||||
|
||||
See Also
|
||||
--------
|
||||
cumulative_distribution
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data, exposure, img_as_float
|
||||
>>> image = img_as_float(data.camera())
|
||||
>>> np.histogram(image, bins=2)
|
||||
(array([ 93585, 168559]), array([0. , 0.5, 1. ]))
|
||||
>>> exposure.histogram(image, nbins=2)
|
||||
(array([ 93585, 168559]), array([0.25, 0.75]))
|
||||
"""
|
||||
sh = image.shape
|
||||
if len(sh) == 3 and sh[-1] < 4 and channel_axis is None:
|
||||
utils.warn(
|
||||
'This might be a color image. The histogram will be '
|
||||
'computed on the flattened image. You can instead '
|
||||
'apply this function to each color channel, or set '
|
||||
'channel_axis.'
|
||||
)
|
||||
|
||||
if channel_axis is not None:
|
||||
channels = sh[-1]
|
||||
hist = []
|
||||
|
||||
# compute bins based on the raveled array
|
||||
if np.issubdtype(image.dtype, np.integer):
|
||||
# here bins corresponds to the bin centers
|
||||
bins = _bincount_histogram_centers(image, source_range)
|
||||
else:
|
||||
# determine the bin edges for np.histogram
|
||||
hist_range = _get_numpy_hist_range(image, source_range)
|
||||
bins = _get_bin_edges(image, nbins, hist_range)
|
||||
|
||||
for chan in range(channels):
|
||||
h, bc = _histogram(image[..., chan], bins, source_range, normalize)
|
||||
hist.append(h)
|
||||
# Convert to numpy arrays
|
||||
bin_centers = np.asarray(bc)
|
||||
hist = np.stack(hist, axis=0)
|
||||
else:
|
||||
hist, bin_centers = _histogram(image, nbins, source_range, normalize)
|
||||
|
||||
return hist, bin_centers
|
||||
|
||||
|
||||
def _histogram(image, bins, source_range, normalize):
|
||||
"""
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : ndarray
|
||||
Image for which the histogram is to be computed.
|
||||
bins : int or ndarray
|
||||
The number of histogram bins. For images with integer dtype, an array
|
||||
containing the bin centers can also be provided. For images with
|
||||
floating point dtype, this can be an array of bin_edges for use by
|
||||
``np.histogram``.
|
||||
source_range : string, optional
|
||||
'image' (default) determines the range from the input image.
|
||||
'dtype' determines the range from the expected range of the images
|
||||
of that data type.
|
||||
normalize : bool, optional
|
||||
If True, normalize the histogram by the sum of its values.
|
||||
"""
|
||||
|
||||
image = image.flatten()
|
||||
# For integer types, histogramming with bincount is more efficient.
|
||||
if np.issubdtype(image.dtype, np.integer):
|
||||
bin_centers = bins if isinstance(bins, np.ndarray) else None
|
||||
hist, bin_centers = _bincount_histogram(image, source_range, bin_centers)
|
||||
else:
|
||||
hist_range = _get_numpy_hist_range(image, source_range)
|
||||
hist, bin_edges = np.histogram(image, bins=bins, range=hist_range)
|
||||
bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2.0
|
||||
|
||||
if normalize:
|
||||
hist = hist / np.sum(hist)
|
||||
return hist, bin_centers
|
||||
|
||||
|
||||
def cumulative_distribution(image, nbins=256):
|
||||
"""Return cumulative distribution function (cdf) for the given image.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : array
|
||||
Image array.
|
||||
nbins : int, optional
|
||||
Number of bins for image histogram.
|
||||
|
||||
Returns
|
||||
-------
|
||||
img_cdf : array
|
||||
Values of cumulative distribution function.
|
||||
bin_centers : array
|
||||
Centers of bins.
|
||||
|
||||
See Also
|
||||
--------
|
||||
histogram
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Cumulative_distribution_function
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data, exposure, img_as_float
|
||||
>>> image = img_as_float(data.camera())
|
||||
>>> hi = exposure.histogram(image)
|
||||
>>> cdf = exposure.cumulative_distribution(image)
|
||||
>>> all(cdf[0] == np.cumsum(hi[0])/float(image.size))
|
||||
True
|
||||
"""
|
||||
hist, bin_centers = histogram(image, nbins)
|
||||
img_cdf = hist.cumsum()
|
||||
img_cdf = img_cdf / float(img_cdf[-1])
|
||||
|
||||
# cast img_cdf to single precision for float32 or float16 inputs
|
||||
cdf_dtype = utils._supported_float_type(image.dtype)
|
||||
img_cdf = img_cdf.astype(cdf_dtype, copy=False)
|
||||
|
||||
return img_cdf, bin_centers
|
||||
|
||||
|
||||
def equalize_hist(image, nbins=256, mask=None):
|
||||
"""Return image after histogram equalization.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : array
|
||||
Image array.
|
||||
nbins : int, optional
|
||||
Number of bins for image histogram. Note: this argument is
|
||||
ignored for integer images, for which each integer is its own
|
||||
bin.
|
||||
mask : ndarray of bools or 0s and 1s, optional
|
||||
Array of same shape as `image`. Only points at which mask == True
|
||||
are used for the equalization, which is applied to the whole image.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : float array
|
||||
Image array after histogram equalization.
|
||||
|
||||
Notes
|
||||
-----
|
||||
This function is adapted from [1]_ with the author's permission.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] http://www.janeriksolem.net/histogram-equalization-with-python-and.html
|
||||
.. [2] https://en.wikipedia.org/wiki/Histogram_equalization
|
||||
|
||||
"""
|
||||
if mask is not None:
|
||||
mask = np.array(mask, dtype=bool)
|
||||
cdf, bin_centers = cumulative_distribution(image[mask], nbins)
|
||||
else:
|
||||
cdf, bin_centers = cumulative_distribution(image, nbins)
|
||||
out = np.interp(image.flat, bin_centers, cdf)
|
||||
out = out.reshape(image.shape)
|
||||
# Unfortunately, np.interp currently always promotes to float64, so we
|
||||
# have to cast back to single precision when float32 output is desired
|
||||
return out.astype(utils._supported_float_type(image.dtype), copy=False)
|
||||
|
||||
|
||||
def intensity_range(image, range_values='image', clip_negative=False):
|
||||
"""Return image intensity range (min, max) based on desired value type.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : array
|
||||
Input image.
|
||||
range_values : str or 2-tuple, optional
|
||||
The image intensity range is configured by this parameter.
|
||||
The possible values for this parameter are enumerated below.
|
||||
|
||||
'image'
|
||||
Return image min/max as the range.
|
||||
'dtype'
|
||||
Return min/max of the image's dtype as the range.
|
||||
dtype-name
|
||||
Return intensity range based on desired `dtype`. Must be valid key
|
||||
in `DTYPE_RANGE`. Note: `image` is ignored for this range type.
|
||||
2-tuple
|
||||
Return `range_values` as min/max intensities. Note that there's no
|
||||
reason to use this function if you just want to specify the
|
||||
intensity range explicitly. This option is included for functions
|
||||
that use `intensity_range` to support all desired range types.
|
||||
|
||||
clip_negative : bool, optional
|
||||
If True, clip the negative range (i.e. return 0 for min intensity)
|
||||
even if the image dtype allows negative values.
|
||||
"""
|
||||
if range_values == 'dtype':
|
||||
range_values = image.dtype.type
|
||||
|
||||
if range_values == 'image':
|
||||
i_min = np.min(image)
|
||||
i_max = np.max(image)
|
||||
elif range_values in DTYPE_RANGE:
|
||||
i_min, i_max = DTYPE_RANGE[range_values]
|
||||
if clip_negative:
|
||||
i_min = 0
|
||||
else:
|
||||
i_min, i_max = range_values
|
||||
return i_min, i_max
|
||||
|
||||
|
||||
def _output_dtype(dtype_or_range, image_dtype):
|
||||
"""Determine the output dtype for rescale_intensity.
|
||||
|
||||
The dtype is determined according to the following rules:
|
||||
- if ``dtype_or_range`` is a dtype, that is the output dtype.
|
||||
- if ``dtype_or_range`` is a dtype string, that is the dtype used, unless
|
||||
it is not a NumPy data type (e.g. 'uint12' for 12-bit unsigned integers),
|
||||
in which case the data type that can contain it will be used
|
||||
(e.g. uint16 in this case).
|
||||
- if ``dtype_or_range`` is a pair of values, the output data type will be
|
||||
``_supported_float_type(image_dtype)``. This preserves float32 output for
|
||||
float32 inputs.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
dtype_or_range : type, string, or 2-tuple of int/float
|
||||
The desired range for the output, expressed as either a NumPy dtype or
|
||||
as a (min, max) pair of numbers.
|
||||
image_dtype : np.dtype
|
||||
The input image dtype.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out_dtype : type
|
||||
The data type appropriate for the desired output.
|
||||
"""
|
||||
if type(dtype_or_range) in [list, tuple, np.ndarray]:
|
||||
# pair of values: always return float.
|
||||
return utils._supported_float_type(image_dtype)
|
||||
if type(dtype_or_range) == type:
|
||||
# already a type: return it
|
||||
return dtype_or_range
|
||||
if dtype_or_range in DTYPE_RANGE:
|
||||
# string key in DTYPE_RANGE dictionary
|
||||
try:
|
||||
# if it's a canonical numpy dtype, convert
|
||||
return np.dtype(dtype_or_range).type
|
||||
except TypeError: # uint10, uint12, uint14
|
||||
# otherwise, return uint16
|
||||
return np.uint16
|
||||
else:
|
||||
raise ValueError(
|
||||
'Incorrect value for out_range, should be a valid image data '
|
||||
f'type or a pair of values, got {dtype_or_range}.'
|
||||
)
|
||||
|
||||
|
||||
def rescale_intensity(image, in_range='image', out_range='dtype'):
|
||||
"""Return image after stretching or shrinking its intensity levels.
|
||||
|
||||
The desired intensity range of the input and output, `in_range` and
|
||||
`out_range` respectively, are used to stretch or shrink the intensity range
|
||||
of the input image. See examples below.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : array
|
||||
Image array.
|
||||
in_range, out_range : str or 2-tuple, optional
|
||||
Min and max intensity values of input and output image.
|
||||
The possible values for this parameter are enumerated below.
|
||||
|
||||
'image'
|
||||
Use image min/max as the intensity range.
|
||||
'dtype'
|
||||
Use min/max of the image's dtype as the intensity range.
|
||||
dtype-name
|
||||
Use intensity range based on desired `dtype`. Must be valid key
|
||||
in `DTYPE_RANGE`.
|
||||
2-tuple
|
||||
Use `range_values` as explicit min/max intensities.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : array
|
||||
Image array after rescaling its intensity. This image is the same dtype
|
||||
as the input image.
|
||||
|
||||
Notes
|
||||
-----
|
||||
.. versionchanged:: 0.17
|
||||
The dtype of the output array has changed to match the input dtype, or
|
||||
float if the output range is specified by a pair of values.
|
||||
|
||||
See Also
|
||||
--------
|
||||
equalize_hist
|
||||
|
||||
Examples
|
||||
--------
|
||||
By default, the min/max intensities of the input image are stretched to
|
||||
the limits allowed by the image's dtype, since `in_range` defaults to
|
||||
'image' and `out_range` defaults to 'dtype':
|
||||
|
||||
>>> image = np.array([51, 102, 153], dtype=np.uint8)
|
||||
>>> rescale_intensity(image)
|
||||
array([ 0, 127, 255], dtype=uint8)
|
||||
|
||||
It's easy to accidentally convert an image dtype from uint8 to float:
|
||||
|
||||
>>> 1.0 * image
|
||||
array([ 51., 102., 153.])
|
||||
|
||||
Use `rescale_intensity` to rescale to the proper range for float dtypes:
|
||||
|
||||
>>> image_float = 1.0 * image
|
||||
>>> rescale_intensity(image_float)
|
||||
array([0. , 0.5, 1. ])
|
||||
|
||||
To maintain the low contrast of the original, use the `in_range` parameter:
|
||||
|
||||
>>> rescale_intensity(image_float, in_range=(0, 255))
|
||||
array([0.2, 0.4, 0.6])
|
||||
|
||||
If the min/max value of `in_range` is more/less than the min/max image
|
||||
intensity, then the intensity levels are clipped:
|
||||
|
||||
>>> rescale_intensity(image_float, in_range=(0, 102))
|
||||
array([0.5, 1. , 1. ])
|
||||
|
||||
If you have an image with signed integers but want to rescale the image to
|
||||
just the positive range, use the `out_range` parameter. In that case, the
|
||||
output dtype will be float:
|
||||
|
||||
>>> image = np.array([-10, 0, 10], dtype=np.int8)
|
||||
>>> rescale_intensity(image, out_range=(0, 127))
|
||||
array([ 0. , 63.5, 127. ])
|
||||
|
||||
To get the desired range with a specific dtype, use ``.astype()``:
|
||||
|
||||
>>> rescale_intensity(image, out_range=(0, 127)).astype(np.int8)
|
||||
array([ 0, 63, 127], dtype=int8)
|
||||
|
||||
If the input image is constant, the output will be clipped directly to the
|
||||
output range:
|
||||
>>> image = np.array([130, 130, 130], dtype=np.int32)
|
||||
>>> rescale_intensity(image, out_range=(0, 127)).astype(np.int32)
|
||||
array([127, 127, 127], dtype=int32)
|
||||
"""
|
||||
if out_range in ['dtype', 'image']:
|
||||
out_dtype = _output_dtype(image.dtype.type, image.dtype)
|
||||
else:
|
||||
out_dtype = _output_dtype(out_range, image.dtype)
|
||||
|
||||
imin, imax = map(float, intensity_range(image, in_range))
|
||||
omin, omax = map(
|
||||
float, intensity_range(image, out_range, clip_negative=(imin >= 0))
|
||||
)
|
||||
|
||||
if np.any(np.isnan([imin, imax, omin, omax])):
|
||||
utils.warn(
|
||||
"One or more intensity levels are NaN. Rescaling will broadcast "
|
||||
"NaN to the full image. Provide intensity levels yourself to "
|
||||
"avoid this. E.g. with np.nanmin(image), np.nanmax(image).",
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
image = np.clip(image, imin, imax)
|
||||
|
||||
if imin != imax:
|
||||
image = (image - imin) / (imax - imin)
|
||||
return (image * (omax - omin) + omin).astype(out_dtype)
|
||||
else:
|
||||
return np.clip(image, omin, omax).astype(out_dtype)
|
||||
|
||||
|
||||
def _assert_non_negative(image):
|
||||
if np.any(image < 0):
|
||||
raise ValueError(
|
||||
'Image Correction methods work correctly only on '
|
||||
'images with non-negative values. Use '
|
||||
'skimage.exposure.rescale_intensity.'
|
||||
)
|
||||
|
||||
|
||||
def _adjust_gamma_u8(image, gamma, gain):
|
||||
"""LUT based implementation of gamma adjustment."""
|
||||
lut = 255 * gain * (np.linspace(0, 1, 256) ** gamma)
|
||||
lut = np.minimum(np.rint(lut), 255).astype('uint8')
|
||||
return lut[image]
|
||||
|
||||
|
||||
def adjust_gamma(image, gamma=1, gain=1):
|
||||
"""Performs Gamma Correction on the input image.
|
||||
|
||||
Also known as Power Law Transform.
|
||||
This function transforms the input image pixelwise according to the
|
||||
equation ``O = I**gamma`` after scaling each pixel to the range 0 to 1.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : ndarray
|
||||
Input image.
|
||||
gamma : float, optional
|
||||
Non negative real number. Default value is 1.
|
||||
gain : float, optional
|
||||
The constant multiplier. Default value is 1.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray
|
||||
Gamma corrected output image.
|
||||
|
||||
See Also
|
||||
--------
|
||||
adjust_log
|
||||
|
||||
Notes
|
||||
-----
|
||||
For gamma greater than 1, the histogram will shift towards left and
|
||||
the output image will be darker than the input image.
|
||||
|
||||
For gamma less than 1, the histogram will shift towards right and
|
||||
the output image will be brighter than the input image.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://en.wikipedia.org/wiki/Gamma_correction
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> from skimage import data, exposure, img_as_float
|
||||
>>> image = img_as_float(data.moon())
|
||||
>>> gamma_corrected = exposure.adjust_gamma(image, 2)
|
||||
>>> # Output is darker for gamma > 1
|
||||
>>> image.mean() > gamma_corrected.mean()
|
||||
True
|
||||
"""
|
||||
if gamma < 0:
|
||||
raise ValueError("Gamma should be a non-negative real number.")
|
||||
|
||||
dtype = image.dtype.type
|
||||
|
||||
if dtype is np.uint8:
|
||||
out = _adjust_gamma_u8(image, gamma, gain)
|
||||
else:
|
||||
_assert_non_negative(image)
|
||||
|
||||
scale = float(dtype_limits(image, True)[1] - dtype_limits(image, True)[0])
|
||||
|
||||
out = (((image / scale) ** gamma) * scale * gain).astype(dtype)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
def adjust_log(image, gain=1, inv=False):
|
||||
"""Performs Logarithmic correction on the input image.
|
||||
|
||||
This function transforms the input image pixelwise according to the
|
||||
equation ``O = gain*log(1 + I)`` after scaling each pixel to the range
|
||||
0 to 1. For inverse logarithmic correction, the equation is
|
||||
``O = gain*(2**I - 1)``.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : ndarray
|
||||
Input image.
|
||||
gain : float, optional
|
||||
The constant multiplier. Default value is 1.
|
||||
inv : float, optional
|
||||
If True, it performs inverse logarithmic correction,
|
||||
else correction will be logarithmic. Defaults to False.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray
|
||||
Logarithm corrected output image.
|
||||
|
||||
See Also
|
||||
--------
|
||||
adjust_gamma
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] http://www.ece.ucsb.edu/Faculty/Manjunath/courses/ece178W03/EnhancePart1.pdf
|
||||
|
||||
"""
|
||||
_assert_non_negative(image)
|
||||
dtype = image.dtype.type
|
||||
scale = float(dtype_limits(image, True)[1] - dtype_limits(image, True)[0])
|
||||
|
||||
if inv:
|
||||
out = (2 ** (image / scale) - 1) * scale * gain
|
||||
return dtype(out)
|
||||
|
||||
out = np.log2(1 + image / scale) * scale * gain
|
||||
return out.astype(dtype)
|
||||
|
||||
|
||||
def adjust_sigmoid(image, cutoff=0.5, gain=10, inv=False):
|
||||
"""Performs Sigmoid Correction on the input image.
|
||||
|
||||
Also known as Contrast Adjustment.
|
||||
This function transforms the input image pixelwise according to the
|
||||
equation ``O = 1/(1 + exp*(gain*(cutoff - I)))`` after scaling each pixel
|
||||
to the range 0 to 1.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : ndarray
|
||||
Input image.
|
||||
cutoff : float, optional
|
||||
Cutoff of the sigmoid function that shifts the characteristic curve
|
||||
in horizontal direction. Default value is 0.5.
|
||||
gain : float, optional
|
||||
The constant multiplier in exponential's power of sigmoid function.
|
||||
Default value is 10.
|
||||
inv : bool, optional
|
||||
If True, returns the negative sigmoid correction. Defaults to False.
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : ndarray
|
||||
Sigmoid corrected output image.
|
||||
|
||||
See Also
|
||||
--------
|
||||
adjust_gamma
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] Gustav J. Braun, "Image Lightness Rescaling Using Sigmoidal Contrast
|
||||
Enhancement Functions",
|
||||
http://markfairchild.org/PDFs/PAP07.pdf
|
||||
|
||||
"""
|
||||
_assert_non_negative(image)
|
||||
dtype = image.dtype.type
|
||||
scale = float(dtype_limits(image, True)[1] - dtype_limits(image, True)[0])
|
||||
|
||||
if inv:
|
||||
out = (1 - 1 / (1 + np.exp(gain * (cutoff - image / scale)))) * scale
|
||||
return dtype(out)
|
||||
|
||||
out = (1 / (1 + np.exp(gain * (cutoff - image / scale)))) * scale
|
||||
return out.astype(dtype)
|
||||
|
||||
|
||||
def is_low_contrast(
|
||||
image,
|
||||
fraction_threshold=0.05,
|
||||
lower_percentile=1,
|
||||
upper_percentile=99,
|
||||
method='linear',
|
||||
):
|
||||
"""Determine if an image is low contrast.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
image : array-like
|
||||
The image under test.
|
||||
fraction_threshold : float, optional
|
||||
The low contrast fraction threshold. An image is considered low-
|
||||
contrast when its range of brightness spans less than this
|
||||
fraction of its data type's full range. [1]_
|
||||
lower_percentile : float, optional
|
||||
Disregard values below this percentile when computing image contrast.
|
||||
upper_percentile : float, optional
|
||||
Disregard values above this percentile when computing image contrast.
|
||||
method : str, optional
|
||||
The contrast determination method. Right now the only available
|
||||
option is "linear".
|
||||
|
||||
Returns
|
||||
-------
|
||||
out : bool
|
||||
True when the image is determined to be low contrast.
|
||||
|
||||
Notes
|
||||
-----
|
||||
For boolean images, this function returns False only if all values are
|
||||
the same (the method, threshold, and percentile arguments are ignored).
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] https://scikit-image.org/docs/dev/user_guide/data_types.html
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> image = np.linspace(0, 0.04, 100)
|
||||
>>> is_low_contrast(image)
|
||||
True
|
||||
>>> image[-1] = 1
|
||||
>>> is_low_contrast(image)
|
||||
True
|
||||
>>> is_low_contrast(image, upper_percentile=100)
|
||||
False
|
||||
"""
|
||||
image = np.asanyarray(image)
|
||||
|
||||
if image.dtype == bool:
|
||||
return not ((image.max() == 1) and (image.min() == 0))
|
||||
|
||||
if image.ndim == 3:
|
||||
from ..color import rgb2gray, rgba2rgb # avoid circular import
|
||||
|
||||
if image.shape[2] == 4:
|
||||
image = rgba2rgb(image)
|
||||
if image.shape[2] == 3:
|
||||
image = rgb2gray(image)
|
||||
|
||||
dlimits = dtype_limits(image, clip_negative=False)
|
||||
limits = np.percentile(image, [lower_percentile, upper_percentile])
|
||||
ratio = (limits[1] - limits[0]) / (dlimits[1] - dlimits[0])
|
||||
|
||||
return ratio < fraction_threshold
|
||||
Reference in New Issue
Block a user