From 918122be1702341e25a120e32e2c54d08c449142 Mon Sep 17 00:00:00 2001 From: leoca Date: Tue, 15 Sep 2026 13:37:17 +0200 Subject: [PATCH] Python: match the MATLAB MSSIM formula in Measure_Quality The MSSIM branch used K1*d**2 and K2*d**2 in the denominators of the luminance and contrast terms, and (K2*d**2)/2 in the structure term, while the numerators of the same expressions already use (K1*d)**2 and (K2*d)**2. MSSIM.m squares the whole product in every one of those six places. K2 was also 0.02 here against 0.03 in MSSIM.m, and the standard deviations came from np.std, which normalises by N, while delta on the next line and MATLAB's std both normalise by N-1. Squaring only K*d leaves the stabilisation term 1/K1 = 100 times too large in l; in c and s, with the K2 difference on top, 0.02*d**2 is about 22 times the (0.03*d)**2 of MSSIM.m. So l, c and s never reach 1: on master, MSSIM(x, x) * N is 0.79 for uniform noise and 0.57 for a sparse phantom instead of 1, and the ceiling depends on the image, so values are not comparable across datasets. Fixes CERN/TIGRE#782 --- Python/tigre/utilities/Measure_Quality.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Python/tigre/utilities/Measure_Quality.py b/Python/tigre/utilities/Measure_Quality.py index 5580ff38..196a04a6 100644 --- a/Python/tigre/utilities/Measure_Quality.py +++ b/Python/tigre/utilities/Measure_Quality.py @@ -64,22 +64,24 @@ def Measure_Quality(res_prev, res, QualMeasOpts): # noqa: N803 K1 = 0.01 # K1 is a small constant <<1 d = np.max(res_prev) - np.min(res_prev) # dynamic range of the pixel values l = ((2 * mean_res * mean_res_p) + (K1 * d) ** 2) / ( - (mean_res_p ** 2) + (mean_res ** 2) + K1 * d ** 2 + (mean_res_p ** 2) + (mean_res ** 2) + (K1 * d) ** 2 ) # Contrast comparison - K2 = 0.02 - sres_p = res_prev.std() - sres = res.std() + K2 = 0.03 + sres_p = res_prev.std(ddof=1) + sres = res.std(ddof=1) - c = ((2 * sres_p * sres) + (K2 * d) ** 2) / ((sres_p ** 2) + (sres ** 2) + K2 * d ** 2) + c = ((2 * sres_p * sres) + (K2 * d) ** 2) / ( + (sres_p ** 2) + (sres ** 2) + (K2 * d) ** 2 + ) # Structure comparison diffres_p = res_prev - mean_res_p diffres = res - mean_res delta = (1 / (N - 1)) * np.sum(diffres_p * diffres) - s = (delta + (((K2 * d) ** 2)) / 2) / ((sres_p * sres) + ((K2 * d ** 2) / 2)) + s = (delta + (((K2 * d) ** 2)) / 2) / ((sres_p * sres) + (((K2 * d) ** 2) / 2)) values.append((1 / N) * l * c * s) if "UQI" == qual: