Skip to content

Repository files navigation

Angicart

Angicart is an efficient software for vascular network segmentation and analysis, designed and maintained by the Savage Lab at UCLA.

References

The original algorithm for segmentation is described in:

Newberry MG, Ennis DB, Savage VM (2015) Testing Foundations of Biological Scaling Theory Using Automated Measurements of Vascular Networks. PLoS Comput Biol 11(8): e1004455. https://doi.org/10.1371/journal.pcbi.1004455

Applications to PET–CT scan data from cancer patients are described in:

Brummer AB, Savage VM (2021) Cancer as a Model System for Testing Metabolic Scaling Theory. Front. Ecol. Evol. https://doi.org/10.3389/fevo.2021.691830

The method for quantifying curvature and tortuosity is described in:

Brummer AB, Hunt D, Savage VM (2021) Improving Blood Vessel Tortuosity Measurements via Highly Sampled Numerical Integration of the Frenet–Serret Equations. IEEE Trans. Med. Imaging https://doi.org/10.1109/TMI.2020.3025467


Quick installation

Note that the source code includes an optional GUI build using wxWidgets. For our installation instructions and executable files, we exclude the GUI for faster processing. If you want the GUI, you would have to (1) install wxWidgets, (2) set TRY_WX to 1 in the first line of source/minSurfTests.cpp and (3) rebuild.

Windows

A prebuilt x64 executable can be found in releases: https://github.com/Savage-Lab-UCLA/AngicartCpp/releases/tag/v1.0.0windowsmaclinux

Build from source (Visual Studio, not VS Code):

  1. Install Visual Studio 2022 Community (with C++ workload).
  2. Open this repository in Visual Studio.
  3. Set the top toolbar to x64-Release.
  4. In Solution Explorer, open angicarttest.sln.
  5. Right-click the solution → PropertiesConfiguration Properties (set Configuration to All configurations) → LinkerSystem → set Subsystem to Console (/SUBSYSTEM:CONSOLE).
  6. BuildBuild angicarttest.sln. You are now ready to run the executable or directly build and run from Visual Studio.

macOS

A prebuilt macOS executable can be found in releases: https://github.com/Savage-Lab-UCLA/AngicartCpp/releases/tag/v1.0.0windowsmaclinux

Build from source (Xcode):

  1. Install the Xcode command-line tools:
    xcode-select --install
  2. Open the folder on xcode. You are now ready to build and run from xcode.

We noticed that on some macOS devices, users encounter privacy/security warnings when trying to run the executable file. Please see other_resources/Angicart Notes for MacOS Users.pdf for a simple fix.

Linux

A prebuilt binary can be found in releases: https://github.com/Savage-Lab-UCLA/AngicartCpp/releases/tag/v1.0.0windowsmaclinux


Quick run on example images, inputs and outputs

Example input images are in sample_data/FITC-MCA0_N12_PI001_s1/. Each .png file is a 2D slice of the 3D volume; Angicart reconstructs the volume from these slices before analysis.

Command-line usage (10–13 arguments):

angicart.exe <image_dir> <im_start> <im_end> <output_base> <vox_x> <vox_y> <vox_z> <length_unit> <threshold> [thread_count] [max_connected_components] [save_nifti]
Argument Description
image_dir Directory containing the input slice images WITHOUT the last '/' (e.g. sample_data/FITC-MCA0_N12_PI001_s1).
im_start First slice index to include (inclusive).
im_end Last slice index to include (inclusive).
output_base Base path for output files (_vessels.png, .tsv, _withRoots.tsv, .dat, etc. are appended to this prefix; _vessels.nii.gz only if save_nifti=1).
vox_x Voxel size in the x direction (same units as length_unit).
vox_y Voxel size in the y direction (same units as length_unit).
vox_z Voxel size in the z direction (same units as length_unit).
length_unit Name of the length unit for voxel dimensions and output (e.g. um or mm).
threshold Normalized intensity threshold (0–1) to separate vascular from non-vascular voxels.
thread_count (Optional) Number of threads. If omitted, defaults to (CPU cores − 1).
max_connected_components (Optional) Maximum number of largest connected components to keep after thresholding. Default: 9. If fewer components exist, all are kept. Requires thread_count to also be provided (use 0 to keep the default thread count).
save_nifti (Optional) 0 or 1. If 1, also write output_base_vessels.nii.gz. Default: 0 (off). Requires thread_count and max_connected_components to also be provided.

CRITICAL: threshold is the only hyperparameter that has to be tuned, usually for each tissue-dataset pair. Before running Angicart, it helps to explore different threshold values visually to identify an optimal normalized treshold value for identifying candidate vascular voxels. Depending on the type of imaging, you may have to perform some image preprocessing to make this thresholding possible. For example, there are many non-vascular artiacts in chest-CT scans that have to be cleaned to extract pulmonary vasculature with a thresholding approach. These steps may include segmenting the organ of interest and enhancing vessel structures (e.g. https://doi.org/10.3389/fevo.2021.691830).

Example run (from the repository root, or adjust paths):

angicart.exe sample_data/FITC-MCA0_N12_PI001_s1 0 61 sample_outputs/FITC-MCA0_N12_PI001_s1 0.412 0.412 0.492 um 0.2

Optional 11th argument: thread count (e.g. 8). Optional 12th argument: max connected components (e.g. 20). Optional 13th argument: save NIfTI mask (0 or 1). If omitted, thread count defaults based on your CPU, max connected components defaults to 9, and NIfTI saving is off.

All outputs use the path given as output_base with the following suffixes:

Output file Description
output_base.tsv Summary table per backbone: name, volume, length, radius from volume/length, observed mean radius per vessel, number of adjacent segments, and adjacency list.
output_base_withRoots.tsv Same as above but with a rooted tree: each segment has a parent and children instead of a flat adjacency list. The root vessel of each connected component is identified by finding the tip with the largest average radius (the root vessel is the row with no parent vessel).
output_base.dat Per-backbone tables: for each segment, vertebra index, voxel coordinates (x, y, z), and per-vertebra radii (r_solid, r_surf). Blocks separated by blank lines; each block has a header with the segment name (same as in the TSV). These are x y z coordinates in the 3D array, NOT physical space
output_base_vessels.png Sanity-check visualization of the binary vessel volume before backbone extraction.
output_base_vessels.nii.gz (Optional; only if save_nifti=1) Binary vessel mask (uint8, 0/1) in NIfTI format for downstream Python visualization (e.g. nibabel). Voxel spacing is taken from vox_x, vox_y, vox_z.
output_base_sphere_radii.tsv A column of sphere radii: these are the radius of the spheres utilizes in the sphere coarsening step. The runtime is often long when there are a few large spheres so this output may be helpful for trouble shooting.

Each segment is labeled ("name" column) such that the index of the backbone within its connected component appears before the decimal point, and the connected component label appears after the decimal point.

Detailed description of outputs

Vessel radius measures

Angicart reports two radius definitions. Both measure the distance from a voxel in the segment’s meat (voxels assigned to that vessel) to the nearest point on the backbone centerline, then average over voxels (weighted by fractional voxel occupancy).

Measure Description
r_solid Average distance-to-centerline over all meat voxels near that centerline point.
r_surf Average distance-to-centerline over surface meat voxels only (boundary voxels of the segmented vessel).

Per-vertebra values (r_solid, r_surf) are written in the .dat file. Segment-level summaries in the .tsv files use:

  • <r>_vl — radius inferred from segment volume and centerline length, as if the segment were a uniform cylinder: (\sqrt{(V/L)/\pi}).
  • <r>_obs — segment-wide average of r_surf (observed surface radius).

Segment names use the format backbone_index.connected_component_index (e.g. 0.01 = backbone 0 in connected component 1).

output_base.tsv

One row per vessel segment. Adjacent segments at branch points are listed explicitly.

Column Description
name Segment label (backbone_index.connected_component_index).
vol(cu.<units>) Physical volume of voxels assigned to this segment.
len(<units>) Centerline length along the backbone (after unsnarling).
<r>_vl(<units>) Volume–length radius: (\sqrt{(V/L)/\pi}).
<r>_obs(<units>) Segment-average surface radius (r_surf averaged over the whole segment).
num_adj Number of adjacent segments at branch points.
adj... Names of adjacent segments (one column per neighbor).

output_base_withRoots.tsv

Same segment metrics as .tsv, but connectivity is expressed as a rooted tree per connected component (cycles resolved arbitrarily with depth first search). The root is the tip with the largest <r>_obs in that component.

Column Description
name Segment label (backbone_index.connected_component_index).
vol(cu.<units>) Physical volume of voxels assigned to this segment.
len(<units>) Centerline length along the backbone (after unsnarling).
<r>_vl(<units>) Volume–length radius: (\sqrt{(V/L)/\pi}).
<r>_obs(<units>) Segment-average surface radius (r_surf averaged over the whole segment).
par Parent segment name, or N/A for the root.
num_child Number of child segments in the rooted tree.
children... Names of child segments (one column per child).

output_base.dat

Per-backbone tables of centerline geometry and local radius profiles. Each segment is a block: a # name header line, a column header, one row per centerline voxel (vertebra), then a blank line before the next segment.

Column Description
idx Index of the vertebra along the backbone (0 = one end).
x, y, z Voxel indices in the 3D array (NOT physical coordinates).
r_solid Per-vertebra r_solid (see table above) (these are in physical coordinates).
r_surf Per-vertebra r_surf (see table above). (these are in physical coordinates)

Algorithm details

Pipeline: intensity threshold → binary volume → sphere coarsening → adjacency and false-tip removal → critical vertebrae → backbone erosion → segmentation and tip extension → segregation by connected component.

1. Input and preprocessing

  • Load 2D slices into a 3D volume; threshold intensities to obtain a binary vessel mask.
  • Keep the largest connected components; fill interior holes.

2. Sphere coarsening (sphereCoarsen, critFrac ≈ 0.16)

  • Build disjoint spheres that roughly cover the vasculature. Spheres are grown in voxel space (not physical dimensions).
  • For each candidate seed: grow a sphere while the fraction of vascular voxels in the expanding shell stays above critFrac. Stop if the shell becomes too non-vascular.
  • If the growing sphere hits a voxel already in another sphere, reject the seed; otherwise the sphere is valid.
  • Valid sphere centers may be repositioned to the center of mass of their vascular region until the center and radius stabilize.
  • Continue until a specified number of consecutive candidate seeds fail.
  • Once spheres are determined, adjacency is computed: which intersphere regions connect which intrasphere (sphere) seeds.

3. Connectivity and false-tip removal

  • Intrasphere space: voxels within each sphere's radius. Intersphere space: voxels in the complement that form connections between spheres.
  • Remove false tips: drop spheres with only one connection and a singly connected intersphere component; drop any intersphere region touching fewer than two spheres (don't worry, tips will be extended later)

4. Critical vertebrae and raw backbones

  • Critical vertebrae = center of mass of each singly connected component at the interface between intrasphere and intersphere space. They define the tips for skeletonization.
  • Backbone erosion: from these tips, erode the vessel to obtain centerlines (ordered voxel sequences) farthest from the boundary. One raw backbone per sphere or connection.

5. Segmentation and tip extension

  • Order and stitch raw backbones using critical vertebrae; split at branch points so each segment has its own backbone. Remove empty backbones.
  • Assign vessel voxels to the nearest backbone (meat per segment). For tip segments, extend the backbone into its meat (erosion from current tip toward farthest voxel in the segment). Compute per-segment volume and average radius (and per-centerline voxel radius).

6. Output

  • Backbones are segregated by connected components. Results are written to the TSV, rooted TSV, and .dat files.

Contributors

This repository is currently being maintained by Kai Akamatsu ([email protected]) and Dr. Van Savage ([email protected]).

  • Dr. Van Savage
  • Jocelyn J. Shen
  • Mitchell Newberry
  • Dr. David Hunt
  • Dr. Alexander Brummer
  • Anderson Ju
  • Kai Akamatsu

Our software is tested across multiple devices thanks to talented undergraduate researchers:

  • Kaili Tam
  • David Zhang
  • Charissa Mak
  • Josh Mehdian

About

Efficient software to segment vascular networks from medical images.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages