/
simple_metrics.py
160 lines (128 loc) · 4.9 KB
/
simple_metrics.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
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)