Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions medpy/metric/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@
from .binary import dc as dc
from .binary import hd as hd
from .binary import hd95 as hd95
from .binary import hd95_max as hd95_max
from .binary import jc as jc
from .binary import obj_asd as obj_asd
from .binary import obj_assd as obj_assd
Expand Down Expand Up @@ -177,6 +178,7 @@
"true_negative_rate",
"true_positive_rate",
"hd95",
"hd95_max",
"obj_asd",
"obj_assd",
"obj_fpr",
Expand Down
58 changes: 56 additions & 2 deletions medpy/metric/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,9 @@ def hd95(result, reference, voxelspacing=None, connectivity=1):
images. Compared to the Hausdorff Distance, this metric is slightly more stable to small outliers and is
commonly used in Biomedical Segmentation challenges.

This implementation computes the 95th percentile of the combined set of directed distances. See function 'hd95_max'
for an alternative implementation

Parameters
----------
result : array_like
Expand All @@ -399,12 +402,14 @@ def hd95(result, reference, voxelspacing=None, connectivity=1):
-------
hd : float
The symmetric Hausdorff Distance between the object(s) in ```result``` and the
object(s) in ```reference```. The distance unit is the same as for the spacing of
elements along each dimension, which is usually given in mm.
object(s) in ```reference``` (95th percentile of the combined set of directed distances).
The distance unit is the same as for the spacing of elements along each dimension,
which is usually given in mm.

See also
--------
:func:`hd`
:func:`hd95_max`

Notes
-----
Expand All @@ -416,6 +421,55 @@ def hd95(result, reference, voxelspacing=None, connectivity=1):
return hd95


def hd95_max(result, reference, voxelspacing=None, connectivity=1):
"""
Alternative 95th percentile Hausdorff Distance.

Computes the symmetric 95th percentile Hausdorff Distance by taking the maximum of the 95th percentiles of the
directed distances from result to reference and from reference to result separately.

This definition matches the common approach referenced in literature.

Parameters
----------
result : array_like
Input data containing objects. Can be any type but will be converted
into binary: background where 0, object everywhere else.
reference : array_like
Input data containing objects. Can be any type but will be converted
into binary: background where 0, object everywhere else.
voxelspacing : float or sequence of floats, optional
The voxelspacing in a distance unit i.e. spacing of elements
along each dimension. If a sequence, must be of length equal to
the input rank; if a single number, this is used for all axes. If
not specified, a grid spacing of unity is implied.
connectivity : int
The neighbourhood/connectivity considered when determining the surface
of the binary objects. This value is passed to
`scipy.ndimage.generate_binary_structure` and should usually be :math:`> 1`.
Note that the connectivity influences the result in the case of the Hausdorff distance.

Returns
-------
hd95 : float
The symmetric 95th percentile Hausdorff Distance (max of per-direction 95th percentiles).
The distance unit is the same as for the spacing of elements along each dimension,
which is usually given in mm.

See also
--------
:func:`hd`
:func:`hd95`

Notes
-----
This is a real metric. The binary images can therefore be supplied in any order.
"""
hd1 = __surface_distances(result, reference, voxelspacing, connectivity)
hd2 = __surface_distances(reference, result, voxelspacing, connectivity)
return max(numpy.percentile(hd1, 95), numpy.percentile(hd2, 95))


def assd(result, reference, voxelspacing=None, connectivity=1):
"""
Average symmetric surface distance.
Expand Down
22 changes: 21 additions & 1 deletion tests/metric_/binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import numpy as np

from medpy.metric import asd, assd, obj_asd, obj_assd
from medpy.metric import asd, assd, hd95, hd95_max, obj_asd, obj_assd

result_min = np.asarray([1, 0]).astype(bool)
reference_min = np.asarray([0, 1]).astype(bool)
Expand Down Expand Up @@ -75,3 +75,23 @@ def test_obj_assd_is_symetric():
assd_1 = obj_assd(result_sym, reference_sym)
assd_2 = obj_assd(reference_sym, result_sym)
assert assd_1 == assd_2


def test_hd95_identity():
assert hd95(result_min, result_min) == 0


def test_hd95_max_identity():
assert hd95_max(result_min, result_min) == 0


def test_hd95_symmetry():
val1 = hd95(result_sym, reference_sym)
val2 = hd95(reference_sym, result_sym)
assert np.isclose(val1, val2)


def test_hd95_max_symmetry():
val1 = hd95_max(result_sym, reference_sym)
val2 = hd95_max(reference_sym, result_sym)
assert np.isclose(val1, val2)
Loading