MeshMetrics
Official Python-based implementation of
MeshMetricsfrom MeshMetrics: A Precise Implementation of Distance-Based Image Segmentation Metrics, motivated by the implementation pitfalls identified in Understanding Implementation Pitfalls of Distance-Based Metrics for Image Segmentation and HDilemma: Are Open-Source Hausdorff Distance Implementations Equivalent?
About
MeshMetrics is a precise, mesh-based implementation of widely used distance-based metrics for evaluating image segmentation tasks. By leveraging mesh representations of segmentation, MeshMetrics ensures precision in distance and boundary element size calculations. For a detailed description and a comparison with other open-source tools supporting distance-based metric calculations, see our paper.
The library supports both 2D and 3D data and works seamlessly with multiple segmentation formats (numpy.ndarray, SimpleITK.Image, vtk.vtkPolyData, trimesh.Trimesh, and meshio.Mesh). It also allows mixing representations between reference and predicted segmentations - for example, one input can be a mask image (SimpleITK.Image), while the other is a surface mesh (vtk.vtkPolyData/trimesh.Trimesh/meshio.Mesh). See the Advanced usage section in examples.ipynb for more details.
Available distance-based metrics:
- Hausdorff distance (HD) with $p$-th percentile variants (HDp)
- Mean average surface distance (MASD)
- Average symmetric surface distance (ASSD)
- Normalized surface distance (NSD)
- Boundary intersection over union (BIoU)
For convenience, MeshMetrics also includes implementations of the Dice similarity coefficient (DSC) and intersection over union (IoU).
If you use MeshMetrics in your work, please cite:
Podobnik, G., & Vrtovec, T. (2025). MeshMetrics: A Precise Implementation of Distance-Based Image Segmentation Metrics. arXiv preprint arXiv:2509.05670.
Podobnik, G., & Vrtovec, T. (2025). Understanding Implementation Pitfalls of Distance-Based Metrics for Image Segmentation. arXiv preprint arXiv:2410.02630.
Installation
System Dependencies
This package requires libxrender1 to be installed on your system. Install it via:
sudo apt update && sudo apt install -y libxrender1
Install meshmetrics package
Install from PyPI with pip, or add it to your project with uv:
pip install pymeshmetrics
uv add pymeshmetrics
The package is published as pymeshmetrics and imported as meshmetrics:
import meshmetrics
Optional support for trimesh and meshio inputs is available via extras: pip install "pymeshmetrics[all]" (or [trimesh] / [meshio]).
To install the latest development version from GitHub:
pip install git+https://github.com/gasperpodobnik/meshmetrics.git
Development
Clone the repository and create the environment (including dev tools and all extras):
git clone https://github.com/gasperpodobnik/meshmetrics.git
cd meshmetrics
uv sync --all-extras
uv run pytest
Usage
Quick start
Compute all metrics for a pair of segmentations in one call:
import SimpleITK as sitk
from meshmetrics import compute_metrics
ref_sitk = sitk.ReadImage("data/example_3d_ref_mask.nii.gz")
pred_sitk = sitk.ReadImage("data/example_3d_pred_mask.nii.gz")
results = compute_metrics(ref_sitk, pred_sitk, taus=(2.0, 5.0))
# {'ref_is_empty': False, 'pred_is_empty': False, 'HD_100': ..., 'HD_95': ..., 'MASD': ..., 'ASSD': ...,
# 'NSD_2.0': ..., 'NSD_5.0': ..., 'BIoU_2.0': ..., 'BIoU_5.0': ..., 'DSC': ..., 'IoU': ...}
taus are the tolerances (in physical units) for NSD and BIoU; they are application-specific, so NSD and BIoU are only computed when taus are given. Use percentiles to choose the HD variants (default (100, 95)) and metrics to select a subset, e.g. metrics=["hd", "nsd"]. Inputs can be any of the supported types (see below); numpy arrays and pairs of meshes also need spacing.
Step-by-step
Simple usage example of MeshMetrics for 3D segmentation masks is shown below.
See examples.ipynb notebook for more examples.
from pathlib import Path
import SimpleITK as sitk
from meshmetrics import DistanceMetrics
data_dir = Path("data")
# read binary segmentation masks
ref_sitk = sitk.ReadImage(str(data_dir / "example_3d_ref_mask.nii.gz"))
pred_sitk = sitk.ReadImage(str(data_dir / "example_3d_pred_mask.nii.gz"))
# Set parameters
percentile = 95 # percentile for HD
tau = 2.0 # tolerance for NSD and BIoU
# Initialize distance metrics class and set inputs
dist_metrics = DistanceMetrics()
dist_metrics.set_input(ref=ref_sitk, pred=pred_sitk)
# store flags indicating empty masks
results = {
"ref_is_empty": dist_metrics.ref_is_empty,
"pred_is_empty": dist_metrics.pred_is_empty,
}
# Hausdorff distance (HD), by default, HD percentile is set to 100 (equivalent to HD)
results["HD_100"] = dist_metrics.hd()
# p-th percentile HD (HD_p)
results[f"HD_{percentile}"] = dist_metrics.hd(percentile=percentile)
# Mean average surface distance (MASD)
results["MASD"] = dist_metrics.masd()
# Average symmetric surface distance (ASSD)
results["ASSD"] = dist_metrics.assd()
# Normalized surface distance (NSD) with tau
results[f"NSD_{tau}"] = dist_metrics.nsd(tau=tau)
# Boundary intersection over union (BIoU) with tau
results[f"BIoU_{tau}"] = dist_metrics.biou(tau=tau)
# print metric values
units = {"HD": "mm", "MASD": "mm", "ASSD": "mm", "NSD": "%", "BIoU": "%"}
for k, v in results.items():
unit = units.get(k.split("_")[0], "")
f = 100.0 if unit == "%" else 1.0
print(f"{k}: {v*f:.2f} {unit}")
# ----------------------------------------
# If using `numpy.ndarray` representations, note that the spacing must be
# reordered when converting a `SimpleITK.Image` object to a `numpy.ndarray`
ref_np = sitk.GetArrayFromImage(ref_sitk).astype(bool)
pred_np = sitk.GetArrayFromImage(pred_sitk).astype(bool)
# spacing should resemble the order of numpy array axes
spacing = ref_sitk.GetSpacing()[::-1]
dist_metrics = DistanceMetrics()
dist_metrics.set_input(ref=ref_np, pred=pred_np, spacing=spacing)
# ... follow the same procedure as before
Metadata
Release files for pymeshmetrics 1.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| pymeshmetrics-1.0.0.tar.gz | 25.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| pymeshmetrics-1.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 50.6 kB
Release files / pymeshmetrics-1.0.0.tar.gz
| Download URL | pymeshmetrics-1.0.0.tar.gz |
|---|---|
| Size | 25.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
baa3141d1ab2f57c36d78665918b635d4fd3aa71a592eece831d0686a04ca21e
|
|
BLAKE2b-256 checksum How to use checksums |
cf5652cb9e549f4216db4517005f8a5481e32a5f1f52c5608e06b1c5fc385672
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 3, 2026.
Transparency logRelease files / pymeshmetrics-1.0.0-py3-none-any.whl
| Download URL | pymeshmetrics-1.0.0-py3-none-any.whl |
|---|---|
| Size | 25.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
038758ec622fc75dda6aa50392c3c44da8de233b2897806df61491762fa1434e
|
|
BLAKE2b-256 checksum How to use checksums |
254dd9f27e01a87db08df5d6ca62bf071b0d4915d08d637d9955cf6dae274f66
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 3, 2026.
Transparency log