forked from 170010011/fr
71 lines
2.4 KiB
Python
71 lines
2.4 KiB
Python
|
import numpy as np
|
||
|
|
||
|
|
||
|
def _match_cumulative_cdf(source, template):
|
||
|
"""
|
||
|
Return modified source array so that the cumulative density function of
|
||
|
its values matches the cumulative density function of the template.
|
||
|
"""
|
||
|
src_values, src_unique_indices, src_counts = np.unique(source.ravel(),
|
||
|
return_inverse=True,
|
||
|
return_counts=True)
|
||
|
tmpl_values, tmpl_counts = np.unique(template.ravel(), return_counts=True)
|
||
|
|
||
|
# calculate normalized quantiles for each array
|
||
|
src_quantiles = np.cumsum(src_counts) / source.size
|
||
|
tmpl_quantiles = np.cumsum(tmpl_counts) / template.size
|
||
|
|
||
|
interp_a_values = np.interp(src_quantiles, tmpl_quantiles, tmpl_values)
|
||
|
return interp_a_values[src_unique_indices].reshape(source.shape)
|
||
|
|
||
|
|
||
|
def match_histograms(image, reference, *, multichannel=False):
|
||
|
"""Adjust an image so that its cumulative histogram matches that of another.
|
||
|
|
||
|
The adjustment is applied separately for each channel.
|
||
|
|
||
|
Parameters
|
||
|
----------
|
||
|
image : ndarray
|
||
|
Input image. Can be gray-scale or in color.
|
||
|
reference : ndarray
|
||
|
Image to match histogram of. Must have the same number of channels as
|
||
|
image.
|
||
|
multichannel : bool, optional
|
||
|
Apply the matching separately for each channel.
|
||
|
|
||
|
Returns
|
||
|
-------
|
||
|
matched : ndarray
|
||
|
Transformed input image.
|
||
|
|
||
|
Raises
|
||
|
------
|
||
|
ValueError
|
||
|
Thrown when the number of channels in the input image and the reference
|
||
|
differ.
|
||
|
|
||
|
References
|
||
|
----------
|
||
|
.. [1] http://paulbourke.net/miscellaneous/equalisation/
|
||
|
|
||
|
"""
|
||
|
if image.ndim != reference.ndim:
|
||
|
raise ValueError('Image and reference must have the same number '
|
||
|
'of channels.')
|
||
|
|
||
|
if multichannel:
|
||
|
if image.shape[-1] != reference.shape[-1]:
|
||
|
raise ValueError('Number of channels in the input image and '
|
||
|
'reference image must match!')
|
||
|
|
||
|
matched = np.empty(image.shape, dtype=image.dtype)
|
||
|
for channel in range(image.shape[-1]):
|
||
|
matched_channel = _match_cumulative_cdf(image[..., channel],
|
||
|
reference[..., channel])
|
||
|
matched[..., channel] = matched_channel
|
||
|
else:
|
||
|
matched = _match_cumulative_cdf(image, reference)
|
||
|
|
||
|
return matched
|