import numpy as np from ..util.dtype import dtype_range from .._shared.utils import warn, check_shape_equality __all__ = ['mean_squared_error', 'normalized_root_mse', 'peak_signal_noise_ratio', ] def _as_floats(image0, image1): """ Promote im1, im2 to nearest appropriate floating point precision. """ float_type = np.result_type(image0.dtype, image1.dtype, np.float32) image0 = np.asarray(image0, dtype=float_type) image1 = np.asarray(image1, dtype=float_type) return image0, image1 def mean_squared_error(image0, image1): """ Compute the mean-squared error between two images. Parameters ---------- image0, image1 : ndarray Images. Any dimensionality, must have same shape. Returns ------- mse : float The mean-squared error (MSE) metric. Notes ----- .. versionchanged:: 0.16 This function was renamed from ``skimage.measure.compare_mse`` to ``skimage.metrics.mean_squared_error``. """ check_shape_equality(image0, image1) image0, image1 = _as_floats(image0, image1) return np.mean((image0 - image1) ** 2, dtype=np.float64) def normalized_root_mse(image_true, image_test, *, normalization='euclidean'): """ Compute the normalized root mean-squared error (NRMSE) between two images. Parameters ---------- image_true : ndarray Ground-truth image, same shape as im_test. image_test : ndarray Test image. normalization : {'euclidean', 'min-max', 'mean'}, optional 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 averaged Euclidean norm of ``im_true``:: NRMSE = RMSE * sqrt(N) / || im_true || where || . || denotes the Frobenius norm and ``N = im_true.size``. This result is equivalent to:: NRMSE = || im_true - im_test || / || 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. Notes ----- .. versionchanged:: 0.16 This function was renamed from ``skimage.measure.compare_nrmse`` to ``skimage.metrics.normalized_root_mse``. References ---------- .. [1] https://en.wikipedia.org/wiki/Root-mean-square_deviation """ check_shape_equality(image_true, image_test) image_true, image_test = _as_floats(image_true, image_test) # Ensure that both 'Euclidean' and 'euclidean' match normalization = normalization.lower() if normalization == 'euclidean': denom = np.sqrt(np.mean((image_true * image_true), dtype=np.float64)) elif normalization == 'min-max': denom = image_true.max() - image_true.min() elif normalization == 'mean': denom = image_true.mean() else: raise ValueError("Unsupported norm_type") return np.sqrt(mean_squared_error(image_true, image_test)) / denom def peak_signal_noise_ratio(image_true, image_test, *, data_range=None): """ Compute the peak signal to noise ratio (PSNR) for an image. Parameters ---------- image_true : ndarray Ground-truth image, same shape as im_test. image_test : ndarray Test image. data_range : int, optional The data 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. Notes ----- .. versionchanged:: 0.16 This function was renamed from ``skimage.measure.compare_psnr`` to ``skimage.metrics.peak_signal_noise_ratio``. References ---------- .. [1] https://en.wikipedia.org/wiki/Peak_signal-to-noise_ratio """ check_shape_equality(image_true, image_test) if data_range is None: if image_true.dtype != image_test.dtype: warn("Inputs have mismatched dtype. Setting data_range based on " "im_true.", stacklevel=2) dmin, dmax = dtype_range[image_true.dtype.type] true_min, true_max = np.min(image_true), np.max(image_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 data_range") if true_min >= 0: # most common case (255 for uint8, 1 for float) data_range = dmax else: data_range = dmax - dmin image_true, image_test = _as_floats(image_true, image_test) err = mean_squared_error(image_true, image_test) return 10 * np.log10((data_range ** 2) / err)