mirror of
https://github.com/wassname/scikit-image.git
synced 2026-08-12 12:30:16 +08:00
248 lines
7.2 KiB
Python
248 lines
7.2 KiB
Python
import numpy as np
|
|
|
|
from skimage import img_as_float
|
|
from skimage.util.dtype import dtype_range
|
|
from skimage.color import rgb2gray
|
|
from skimage.util.dtype import convert
|
|
|
|
from _adapthist import _adapthist
|
|
|
|
__all__ = ['histogram', 'cumulative_distribution', 'equalize',
|
|
'rescale_intensity', 'adapthist']
|
|
|
|
|
|
def histogram(image, nbins=256):
|
|
"""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.
|
|
|
|
Parameters
|
|
----------
|
|
image : array
|
|
Input image.
|
|
nbins : int
|
|
Number of bins used to calculate histogram. This value is ignored for
|
|
integer arrays.
|
|
|
|
Returns
|
|
-------
|
|
hist : array
|
|
The values of the histogram.
|
|
bin_centers : array
|
|
The values at the center of the bins.
|
|
"""
|
|
|
|
# For integer types, histogramming with bincount is more efficient.
|
|
if np.issubdtype(image.dtype, np.integer):
|
|
offset = 0
|
|
if np.min(image) < 0:
|
|
offset = np.min(image)
|
|
hist = np.bincount(image.ravel() - offset)
|
|
bin_centers = np.arange(len(hist)) + offset
|
|
|
|
# clip histogram to start with a non-zero bin
|
|
idx = np.nonzero(hist)[0][0]
|
|
return hist[idx:], bin_centers[idx:]
|
|
else:
|
|
hist, bin_edges = np.histogram(image.flat, nbins)
|
|
bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2.
|
|
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
|
|
Number of bins for image histogram.
|
|
|
|
Returns
|
|
-------
|
|
img_cdf : array
|
|
Values of cumulative distribution function.
|
|
bin_centers : array
|
|
Centers of bins.
|
|
|
|
References
|
|
----------
|
|
.. [1] http://en.wikipedia.org/wiki/Cumulative_distribution_function
|
|
|
|
"""
|
|
hist, bin_centers = histogram(image, nbins)
|
|
img_cdf = hist.cumsum()
|
|
img_cdf = img_cdf / float(img_cdf[-1])
|
|
return img_cdf, bin_centers
|
|
|
|
|
|
def equalize(image, nbins=256):
|
|
"""Return image after histogram equalization.
|
|
|
|
Parameters
|
|
----------
|
|
image : array
|
|
Image array.
|
|
nbins : int
|
|
Number of bins for image histogram.
|
|
|
|
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/2009/06/histogram-equalization-with-python-and.html
|
|
.. [2] http://en.wikipedia.org/wiki/Histogram_equalization
|
|
|
|
"""
|
|
image = img_as_float(image)
|
|
cdf, bin_centers = cumulative_distribution(image, nbins)
|
|
out = np.interp(image.flat, bin_centers, cdf)
|
|
return out.reshape(image.shape)
|
|
|
|
|
|
def rescale_intensity(image, in_range=None, out_range=None):
|
|
"""Return image after stretching or shrinking its intensity levels.
|
|
|
|
The image intensities are uniformly rescaled such that the minimum and
|
|
maximum values given by `in_range` match those given by `out_range`.
|
|
|
|
Parameters
|
|
----------
|
|
image : array
|
|
Image array.
|
|
in_range : 2-tuple (float, float)
|
|
Min and max *allowed* intensity values of input image. If None, the
|
|
*allowed* min/max values are set to the *actual* min/max values in the
|
|
input image.
|
|
out_range : 2-tuple (float, float)
|
|
Min and max intensity values of output image. If None, use the min/max
|
|
intensities of the image data type. See `skimage.util.dtype` for
|
|
details.
|
|
|
|
Returns
|
|
-------
|
|
out : array
|
|
Image array after rescaling its intensity. This image is the same dtype
|
|
as the input image.
|
|
|
|
Examples
|
|
--------
|
|
By default, intensities are stretched to the limits allowed by the 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:
|
|
|
|
>>> image = np.array([-10, 0, 10], dtype=np.int8)
|
|
>>> rescale_intensity(image, out_range=(0, 127))
|
|
array([ 0, 63, 127], dtype=int8)
|
|
|
|
"""
|
|
dtype = image.dtype.type
|
|
|
|
if in_range is None:
|
|
imin = np.min(image)
|
|
imax = np.max(image)
|
|
else:
|
|
imin, imax = in_range
|
|
|
|
if out_range is None:
|
|
omin, omax = dtype_range[dtype]
|
|
if imin >= 0:
|
|
omin = 0
|
|
else:
|
|
omin, omax = out_range
|
|
|
|
image = np.clip(image, imin, imax)
|
|
|
|
image = (image - imin) / float(imax - imin)
|
|
return dtype(image * (omax - omin) + omin)
|
|
|
|
|
|
def adapthist(image, nx=8, ny=8, clip_limit=0.01, nbins=256, out_range='full'):
|
|
'''Contrast Limited Adaptive Histogram Equalization
|
|
|
|
Parameters
|
|
----------
|
|
image : array-like
|
|
original image
|
|
nx : int, optional
|
|
Tile regions in the X direction (2, 16)
|
|
ny : int, optional
|
|
Tile regions in the Y direction (2, 16)
|
|
clip_limit : float: optional
|
|
Normalized cliplimit (higher values give more contrast)
|
|
nbins : int, optional
|
|
Greybins for histogram ("dynamic range")
|
|
out_range : str, optional
|
|
Range of the output image data.
|
|
- 'original' - Use original image limits
|
|
- 'full' - Use full range of image data type
|
|
|
|
Returns
|
|
-------
|
|
out - np.ndarray :
|
|
equalized image - may be a different shape than the original
|
|
'''
|
|
in_type = image.dtype.type
|
|
if out_range == 'full':
|
|
out_range = None
|
|
else:
|
|
out_range = (image.min(), image.max())
|
|
# must be converted to 12 bit for CLAHE
|
|
image = skimage.img_as_uint(image)
|
|
MAX_VAL = 2 ** 12 - 1
|
|
image = rescale_intensity(image, out_range=(0, MAX_VAL))
|
|
# handle color images - CLAHE accepts scalar images only
|
|
args = [image.copy(), 0, MAX_VAL, nx, ny, nbins, clip_limit]
|
|
if image.ndim == 3:
|
|
image = image[:, :, :3]
|
|
for channel in range(3):
|
|
args[0] = image[:, :, channel]
|
|
out = _adapthist(*args)
|
|
image[:out.shape[0], :out.shape[1], channel] = out
|
|
else:
|
|
out = _adapthist(*args)
|
|
image[:out.shape[0], :out.shape[1]] = out
|
|
# restore to desired output type and output limits
|
|
image = rescale_intensity(image)
|
|
if in_type != np.uint16:
|
|
image = convert(image, in_type)
|
|
image = rescale_intensity(image, out_range=out_range)
|
|
return image
|