mirror of
https://github.com/wassname/scikit-image.git
synced 2026-06-27 18:25:32 +08:00
136 lines
3.9 KiB
Python
136 lines
3.9 KiB
Python
from __future__ import division
|
|
|
|
import numpy as np
|
|
from ..util.dtype import dtype_range
|
|
|
|
__all__ = ['compare_mse',
|
|
'compare_nrmse',
|
|
'compare_psnr',
|
|
]
|
|
|
|
|
|
def _assert_compatible(im1, im2):
|
|
"""Raise an error if the shape and dtype do not match."""
|
|
if not im1.dtype == im2.dtype:
|
|
raise ValueError('Input images must have the same dtype.')
|
|
if not im1.shape == im2.shape:
|
|
raise ValueError('Input images must have the same dimensions.')
|
|
return
|
|
|
|
|
|
def _as_floats(im1, im2):
|
|
"""Promote im1, im2 to nearest appropriate floating point precision."""
|
|
float_type = np.result_type(im1.dtype, im2.dtype, np.float32)
|
|
if im1.dtype != float_type:
|
|
im1 = im1.astype(float_type)
|
|
if im2.dtype != float_type:
|
|
im2 = im2.astype(float_type)
|
|
return im1, im2
|
|
|
|
|
|
def compare_mse(im1, im2):
|
|
"""Compute the mean-squared error between two images.
|
|
|
|
Parameters
|
|
----------
|
|
im1, im2 : ndarray
|
|
Image. Any dimensionality.
|
|
|
|
Returns
|
|
-------
|
|
mse : float
|
|
The mean-squared error (MSE) metric.
|
|
|
|
"""
|
|
_assert_compatible(im1, im2)
|
|
im1, im2 = _as_floats(im1, im2)
|
|
return np.mean(np.square(im1 - im2), dtype=np.float64)
|
|
|
|
|
|
def compare_nrmse(im_true, im_test, norm_type='Euclidean'):
|
|
"""Compute the normalized root mean-squared error (NRMSE) between two
|
|
images.
|
|
|
|
Parameters
|
|
----------
|
|
im_true : ndarray
|
|
Ground-truth image.
|
|
im_test : ndarray
|
|
Test image.
|
|
norm_type : {'Euclidean', 'min-max', 'mean'}
|
|
Controls the normalization method to use in the denominator of the
|
|
NRMSE. There is no standard method of normalization across the
|
|
literature [1]_. The methods available here are as follows:
|
|
|
|
- 'Euclidean' : normalize by the Euclidean norm of ``im_true``.
|
|
- 'min-max' : normalize by the intensity range of ``im_true``.
|
|
- 'mean' : normalize by the mean of ``im_true``.
|
|
|
|
Returns
|
|
-------
|
|
nrmse : float
|
|
The NRMSE metric.
|
|
|
|
References
|
|
----------
|
|
.. [1] https://en.wikipedia.org/wiki/Root-mean-square_deviation
|
|
|
|
"""
|
|
_assert_compatible(im_true, im_test)
|
|
im_true, im_test = _as_floats(im_true, im_test)
|
|
|
|
norm_type = norm_type.lower()
|
|
if norm_type == 'euclidean':
|
|
denom = np.sqrt(np.mean((im_true*im_true), dtype=np.float64))
|
|
elif norm_type == 'min-max':
|
|
denom = im_true.max() - im_true.min()
|
|
elif norm_type == 'mean':
|
|
denom = im_true.mean()
|
|
else:
|
|
raise ValueError("Unsupported norm_type")
|
|
return np.sqrt(compare_mse(im_true, im_test)) / denom
|
|
|
|
|
|
def compare_psnr(im_true, im_test, dynamic_range=None):
|
|
""" Compute the peak signal to noise ratio (PSNR) for an image.
|
|
|
|
Parameters
|
|
----------
|
|
im_true : ndarray
|
|
Ground-truth image.
|
|
im_test : ndarray
|
|
Test image.
|
|
dynamic_range : int
|
|
The dynamic range of the input image (distance between minimum and
|
|
maximum possible values). By default, this is estimated from the image
|
|
data-type.
|
|
|
|
Returns
|
|
-------
|
|
psnr : float
|
|
The PSNR metric.
|
|
|
|
References
|
|
----------
|
|
.. [1] https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio
|
|
|
|
"""
|
|
_assert_compatible(im_true, im_test)
|
|
if dynamic_range is None:
|
|
dmin, dmax = dtype_range[im_true.dtype.type]
|
|
true_min, true_max = np.min(im_true), np.max(im_true)
|
|
if true_max > dmax or true_min < dmin:
|
|
raise ValueError(
|
|
"im_true has intensity values outside the range expected for "
|
|
"its data type. Please manually specify the dynamic_range")
|
|
if true_min >= 0:
|
|
# most common case (255 for uint8, 1 for float)
|
|
dynamic_range = dmax
|
|
else:
|
|
dynamic_range = dmax - dmin
|
|
|
|
im_true, im_test = _as_floats(im_true, im_test)
|
|
|
|
err = compare_mse(im_true, im_test)
|
|
return 10 * np.log10((dynamic_range ** 2) / err)
|