| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851 |
- 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 : {'image', 'dtype'}
- '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 : {'image', 'dtype'}, 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 : {'image', 'dtype'}, 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):
- """Perform gamma correction on the input image.
- Gamma correction is a power-law transform [1]_. This function
- transforms the input `image` pixel-wise according to the power law
- ``image**gamma`` after scaling each pixel to the range 0 to 1. Then
- it is rescaled to its original range and muliplied by `gain`.
- 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
- --------
- >>> import skimage as ski
- >>> image = ski.util.img_as_float(ski.data.moon())
- >>> gamma_corrected = ski.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)
- limits = dtype_limits(image, clip_negative=True)
- scale = float(limits[1] - limits[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
|