Source code for ssp.numpy.ssp

import numpy as np


[docs] def ssp(x1, x2, batched=False): """ Surface Similarity Parameter (SSP) without any fuzz, cf. https://doi.org/10.1007/s10665-016-9849-7. This implementation does not rely in any FFT call and thus complies with n-D data. Parameters ---------- x1 : ndarray First input array for comparison x2 : ndarray Second input array for comparison batched : bool, optional If this is set to `True`, the result is not reduced along the first dimension. This option allows to use the SSP for comparing batches of data. Returns ------- ssp : ndarray Surface Similarity Parameter between arrays `x1` and `x2`. Notes ----- The result is always within `[0, 1]` with - 0 indicating perfect agreement, and - 1 indicating perfect disagreement. `Perfect disagreement` in terms of SSP means either - a non-zero signal is compared against a zero signal, or - a signal is compared against the inverse itself, e.g., `ssp(x, -x)`. Examples -------- >>> from ssp.numpy import ssp >>> import numpy as np >>> np.random.seed(0) # for deterministic results >>> x1 = np.random.random((2, 32)) >>> x2 = np.random.random((2, 32)) >>> ssp(x1, x2) # some value between 0 and 1 np.float64(0.35119514129237195) >>> ssp(x1, x1) np.float64(0.0) >>> ssp(x1, -x1) np.float64(1.0) >>> ssp(x1, np.zeros_like(x1)) np.float64(1.0) Use `batched=True` to get a unique result for each signal in `x1` and `x2` >>> from ssp.numpy import ssp >>> import numpy as np >>> np.random.seed(0) # for deterministic results >>> x1 = np.random.random((2, 32)) >>> x2 = np.random.random((2, 32)) >>> ssp(x1, x2, batched=True) array([0.34864963, 0.35827101]) >>> ssp(x1, x1, batched=True) array([0., 0.]) >>> ssp(x1, -x1, batched=True) array([1., 1.]) >>> ssp(x1, np.zeros_like(x1), batched=True) array([1., 1.]) """ assert x1.shape == x2.shape, f"Shapes have to match, received {x1.shape} and {x2.shape}" if batched: # flatten inputs along last axis to keep batch-dimension, batch dimension is maintained x1 = np.reshape(x1, (x1.shape[0], -1)) x2 = np.reshape(x2, (x2.shape[0], -1)) else: # flatten inputs completely, result will be a float x1 = np.ravel(x1) x2 = np.ravel(x2) # get numerator and denuminator numerator = np.linalg.norm(x1 - x2, axis=-1) denum = np.linalg.norm(x1, axis=-1) + np.linalg.norm(x2, axis=-1) return np.divide( numerator, denum, where=denum != 0 )