def test_vector_cuda(): # Rn inp = [1.0, 2.0, 3.0] x = odl.vector(inp, dtype='float32', impl='cuda') assert isinstance(x, CudaFnVector) assert x.dtype == np.dtype('float32') assert all_equal(x, inp) x = odl.vector([1.0, 2.0, float('inf')], dtype='float32', impl='cuda') assert x.dtype == np.dtype('float32') assert isinstance(x, CudaFnVector) x = odl.vector([1.0, 2.0, float('nan')], dtype='float32', impl='cuda') assert x.dtype == np.dtype('float32') assert isinstance(x, CudaFnVector) x = odl.vector([1, 2, 3], dtype='float32', impl='cuda') assert x.dtype == np.dtype('float32') assert isinstance(x, CudaFnVector)
def mean_absolute_error(data, ground_truth, mask=None, normalized=False, force_lower_is_better=True): r"""Return L1-distance between ``data`` and ``ground_truth``. See also `this Wikipedia article <https://en.wikipedia.org/wiki/Mean_absolute_error>`_. Parameters ---------- data : `Tensor` or `array-like` Input data to compare to the ground truth. If not a `Tensor`, an unweighted tensor space will be assumed. ground_truth : `array-like` Reference to which ``data`` should be compared. mask : `array-like`, optional If given, ``data * mask`` is compared to ``ground_truth * mask``. normalized : bool, optional If ``True``, the output values are mapped to the interval :math:`[0, 1]` (see `Notes` for details), otherwise return the original mean absolute error. force_lower_is_better : bool, optional If ``True``, it is ensured that lower values correspond to better matches. For the mean absolute error, this is already the case, and the flag is only present for compatibility to other figures of merit. Returns ------- mae : float FOM value, where a lower value means a better match. Notes ----- The FOM evaluates .. math:: \mathrm{MAE}(f, g) = \frac{\| f - g \|_1}{\| 1 \|_1}, where :math:`\| 1 \|_1` is the volume of the domain of definition of the functions. For :math:`\mathbb{R}^n` type spaces, this is equal to the number of elements :math:`n`. The normalized form is .. math:: \mathrm{MAE_N}(f, g) = \frac{\| f - g \|_1}{\| f \|_1 + \| g \|_1}. The normalized variant takes values in :math:`[0, 1]`. """ if not hasattr(data, 'space'): data = odl.vector(data) space = data.space ground_truth = space.element(ground_truth) l1_norm = odl.solvers.L1Norm(space) if mask is not None: data = data * mask ground_truth = ground_truth * mask diff = data - ground_truth fom = l1_norm(diff) if normalized: fom /= (l1_norm(data) + l1_norm(ground_truth)) else: fom /= l1_norm(space.one()) # Ignore `force_lower_is_better` since that's already the case return fom
def blurring(data, ground_truth, mask=None, normalized=False, smoothness_factor=None): r"""Return weighted L2 distance, emphasizing regions defined by ``mask``. .. note:: If the mask argument is omitted, this FOM is equivalent to the mean squared error. Parameters ---------- data : `Tensor` or `array-like` Input data to compare to the ground truth. If not a `Tensor`, an unweighted tensor space will be assumed. ground_truth : `array-like` Reference to which ``data`` should be compared. mask : `array-like`, optional Binary mask to define ROI in which FOM evaluation is performed. normalized : bool, optional Boolean flag to switch between unormalized and normalized FOM. smoothness_factor : float, optional Positive real number. Higher value gives smoother weighting. Returns ------- blur : float FOM value, where a lower value means a better match. See Also -------- false_structures mean_squared_error Notes ----- The FOM evaluates .. math:: \mathrm{BLUR}(f, g) = \|\alpha (f - g) \|_2^2, or, in normalized form .. math:: \mathrm{BLUR_N}(f, g) = \frac{\|\alpha(f - g)\|^2_2} {\|\alpha f\|^2_2 + \|\alpha g\|^2_2}. The weighting function :math:`\alpha` is given as .. math:: \alpha(x) = e^{-\frac{1}{k} \beta_m(x)}, where :math:`\beta_m(x)` is the Euclidian distance transform of a given binary mask :math:`m`, and :math:`k` positive real number that controls the smoothness of the weighting function :math:`\alpha`. The weighting gives higher values to structures in the region of interest defined by the mask. The normalized variant takes values in :math:`[0, 1]`. """ from scipy.ndimage.morphology import distance_transform_edt if not hasattr(data, 'space'): data = odl.vector(data) space = data.space ground_truth = space.element(ground_truth) if smoothness_factor is None: smoothness_factor = np.mean(data.shape) / 10 if mask is not None: mask = distance_transform_edt(1 - mask) mask = np.exp(-mask / smoothness_factor) return mean_squared_error(data, ground_truth, mask, normalized)
def standard_deviation_difference(data, ground_truth, mask=None, normalized=False, force_lower_is_better=True): r"""Return absolute diff in std between ``data`` and ``ground_truth``. Parameters ---------- data : `Tensor` or `array-like` Input data to compare to the ground truth. If not a `Tensor`, an unweighted tensor space will be assumed. ground_truth : `array-like` Reference to which ``data`` should be compared. mask : `array-like`, optional If given, ``data * mask`` is compared to ``ground_truth * mask``. normalized : bool, optional Boolean flag to switch between unormalized and normalized FOM. force_lower_is_better : bool, optional If ``True``, it is ensured that lower values correspond to better matches. For the standard deviation difference, this is already the case, and the flag is only present for compatibility to other figures of merit. Returns ------- sdd : float FOM value, where a lower value means a better match. Notes ----- The FOM evaluates .. math:: \mathrm{SDD}(f, g) = \Big| \| f - \overline{f} \|_2 - \| g - \overline{g} \|_2 \Big|, or, in normalized form .. math:: \mathrm{SDD_N}(f, g) = \frac{\Big| \| f - \overline{f} \|_2 - \| g - \overline{g} \|_2 \Big|} {\| f - \overline{f} \|_2 + \| g - \overline{g} \|_2}, where :math:`\overline{f}` is the mean value of :math:`f`, .. math:: \overline{f} = \frac{\langle f, 1\rangle}{\|1|_1}. The normalized variant takes values in :math:`[0, 1]`. """ if not hasattr(data, 'space'): data = odl.vector(data) space = data.space ground_truth = space.element(ground_truth) l1_norm = odl.solvers.L1Norm(space) l2_norm = odl.solvers.L2Norm(space) if mask is not None: data = data * mask ground_truth = ground_truth * mask # Volume of space vol = l1_norm(space.one()) data_mean = data.inner(space.one()) / vol ground_truth_mean = ground_truth.inner(space.one()) / vol deviation_data = l2_norm(data - data_mean) deviation_ground_truth = l2_norm(ground_truth - ground_truth_mean) fom = np.abs(deviation_data - deviation_ground_truth) if normalized: denom = deviation_data + deviation_ground_truth if denom == 0: fom = 0.0 else: fom /= denom return fom
def test_vector_numpy(): # Rn inp = [1.0, 2.0, 3.0] x = vector(inp) assert isinstance(x, odl.NumpyFnVector) assert x.dtype == np.dtype('float64') assert all_equal(x, inp) x = vector([1.0, 2.0, float('inf')]) assert x.dtype == np.dtype('float64') assert isinstance(x, odl.NumpyFnVector) x = vector([1.0, 2.0, float('nan')]) assert x.dtype == np.dtype('float64') assert isinstance(x, odl.NumpyFnVector) x = vector([1, 2, 3], dtype='float32') assert x.dtype == np.dtype('float32') assert isinstance(x, odl.NumpyFnVector) # Cn inp = [1 + 1j, 2, 3 - 2j] x = vector(inp) assert isinstance(x, odl.NumpyFnVector) assert x.dtype == np.dtype('complex128') assert all_equal(x, inp) x = vector([1, 2, 3], dtype='complex64') assert isinstance(x, odl.NumpyFnVector) # Fn inp = [1, 2, 3] x = vector(inp) assert isinstance(x, odl.NumpyNtuplesVector) assert x.dtype == np.dtype('int') assert all_equal(x, inp) # Ntuples inp = ['a', 'b', 'c'] x = vector(inp) assert isinstance(x, odl.NumpyNtuplesVector) assert np.issubdtype(x.dtype, basestring) assert all_equal(x, inp) x = vector([1, 2, 'inf']) # Becomes string type assert isinstance(x, odl.NumpyNtuplesVector) assert np.issubdtype(x.dtype, basestring) assert all_equal(x, ['1', '2', 'inf']) # Input not one-dimensional x = vector(5.0) # OK assert x.shape == (1,) with pytest.raises(ValueError): vector([[1, 0], [0, 1]])
def test_vector_cuda(): # Rn inp = [1.0, 2.0, 3.0] x = vector(inp, impl='cuda') assert isinstance(x, odl.CudaFnVector) assert x.dtype == np.dtype('float32') assert all_equal(x, inp) x = vector([1.0, 2.0, float('inf')], impl='cuda') assert isinstance(x, odl.CudaFnVector) assert x.dtype == np.dtype('float32') assert all_equal(x, [1.0, 2.0, float('inf')]) x = vector([1, 2, 3], dtype='float32', impl='cuda') assert isinstance(x, odl.CudaFnVector) assert all_equal(x, [1.0, 2.0, 3.0]) assert x.dtype == np.dtype('float32') # Cn, not yet supported inp = [1 + 1j, 2, 3 - 2j] with pytest.raises(NotImplementedError): vector(inp, impl='cuda') # Fn inp = [1, 2, 3] x = vector(inp, impl='cuda') assert isinstance(x, odl.CudaFnVector) assert all_equal(x, inp) # Ntuples inp = ['a', 'b', 'c'] # String types not supported with pytest.raises(TypeError): vector(inp, impl='cuda') # Input not one-dimensional x = vector(5.0) # OK assert x.shape == (1,) with pytest.raises(ValueError): vector([[1, 0], [0, 1]], impl='cuda')
def test_vector_numpy(): # Rn inp = [1.0, 2.0, 3.0] x = vector(inp) assert isinstance(x, odl.NumpyFnVector) assert x.dtype == np.dtype('float64') assert all_equal(x, inp) x = vector([1.0, 2.0, float('inf')]) assert x.dtype == np.dtype('float64') assert isinstance(x, odl.NumpyFnVector) x = vector([1.0, 2.0, float('nan')]) assert x.dtype == np.dtype('float64') assert isinstance(x, odl.NumpyFnVector) x = vector([1, 2, 3], dtype='float32') assert x.dtype == np.dtype('float32') assert isinstance(x, odl.NumpyFnVector) # Cn inp = [1 + 1j, 2, 3 - 2j] x = vector(inp) assert isinstance(x, odl.NumpyFnVector) assert x.dtype == np.dtype('complex128') assert all_equal(x, inp) x = vector([1, 2, 3], dtype='complex64') assert isinstance(x, odl.NumpyFnVector) # Fn inp = [1, 2, 3] x = vector(inp) assert isinstance(x, odl.NumpyNtuplesVector) assert x.dtype == np.dtype('int') assert all_equal(x, inp) # Ntuples inp = ['a', 'b', 'c'] x = vector(inp) assert isinstance(x, odl.NumpyNtuplesVector) assert np.issubdtype(x.dtype, basestring) assert all_equal(x, inp) x = vector([1, 2, 'inf']) # Becomes string type assert isinstance(x, odl.NumpyNtuplesVector) assert np.issubdtype(x.dtype, basestring) assert all_equal(x, ['1', '2', 'inf']) # Input not one-dimensional x = vector(5.0) # OK assert x.shape == (1, ) with pytest.raises(ValueError): vector([[1, 0], [0, 1]])
def test_vector_numpy(): # Rn inp = [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]] x = vector(inp) assert isinstance(x, NumpyTensor) assert x.dtype == np.dtype('float64') assert all_equal(x, inp) x = vector([1.0, 2.0, float('inf')]) assert x.dtype == np.dtype('float64') assert isinstance(x, NumpyTensor) x = vector([1.0, 2.0, float('nan')]) assert x.dtype == np.dtype('float64') assert isinstance(x, NumpyTensor) x = vector([1, 2, 3], dtype='float32') assert x.dtype == np.dtype('float32') assert isinstance(x, NumpyTensor) # Cn inp = [[1 + 1j, 2, 3 - 2j], [4 + 1j, 5, 6 - 1j]] x = vector(inp) assert isinstance(x, NumpyTensor) assert x.dtype == np.dtype('complex128') assert all_equal(x, inp) x = vector([1, 2, 3], dtype='complex64') assert isinstance(x, NumpyTensor) # Generic TensorSpace inp = [1, 2, 3] x = vector(inp) assert isinstance(x, NumpyTensor) assert x.dtype == np.dtype('int') assert all_equal(x, inp) inp = ['a', 'b', 'c'] x = vector(inp) assert isinstance(x, NumpyTensor) assert np.issubdtype(x.dtype, np.str_) assert all_equal(x, inp) x = vector([1, 2, 'inf']) # Becomes string type assert isinstance(x, NumpyTensor) assert np.issubdtype(x.dtype, np.str_) assert all_equal(x, ['1', '2', 'inf']) # Scalar or empty input x = vector(5.0) # becomes 1d, size 1 assert x.shape == (1,) x = vector([]) # becomes 1d, size 0 assert x.shape == (0,)