Skip to content

Repository files navigation

Retinal vessel segmentation and morphometry

Code for tracing blood vessels in fluorescence retina images and then measuring the shape of the network they form. Two pieces: a segmentation method built for the vessels a standard filter tends to drop (the faint ones, and the ones sitting a little out of focus), and a morphometry panel that turns the binary mask into network shape numbers.

Individual passes build vessel masks to limit background but also capture dim/slightly out of focus fields to recover an increased true vessel network.

The segmentation

Three passes

Pass 1, the bright tree. A white tophat with a disk wider than the widest vessel strips the smooth bright haze, so the faint mesh sits on a flat background instead of drowning in it. Then a multiscale Frangi map with a locally adaptive threshold for the fine mesh, plus a plain threshold on the flattened image to fill the thick vessels the ridge filter leaves hollow down the middle.

Pass 2, the faint mesh. A mild contrast lift before the ridge filter. Tubeness taken as the max of a Frangi and a Sato response, because Sato picks up faint tubes Frangi under-responds to. Then a permissive threshold gives a faint mask, keeping only the faint pixels whose connected component touches the confident network from pass 1.

Pass 3, the out-of-focus vessels. An out-of-focus vessel loses ridge contrast (the exact cue every vesselness filter keys on) even though its fluorescence is plainly still there. So this pass sharpens first to partly undo the blur, then widens the scale range, since out-of-focus vessels read wider and softer than the small scales respond to. The faint step here stays a shape cue, not a brightness one: in a single flat image an out-of-focus vessel and a patch of bright haze sit at the same brightness, so a brightness floor would just flood the gaps between capillaries. A tube is locally long with two edges; haze is a formless blob. The shape cue keeps one and rejects the other.

For a large stitched field with dark background around the tissue, segment --tissue runs a masked version of pass 3 — it finds the tissue first, then takes every threshold and brightness statistic inside the tissue only, so the dark surround never pulls the trace around.

The metrics

morphometry.py and morphometry2d.py take a binary vessel mask, and an optional tissue mask, and return a fixed panel of network descriptors: density and caliber, branch and endpoint counts, segment length and length in vessel-widths, tortuosity, the size and count of the enclosed avascular loops, bifurcation angle and its spread, lacunarity, and a couple of tissue-at-risk distances for how far tissue sits from the nearest vessel. Some are in pixels and only compare within one imaging setup; the dimensionless ratios carry across magnification. The metric code is pure geometry and knows nothing about what the image is of.

Running it

Python 3.9+.

pip install -r requirements.txt
python run.py demo

The demo makes a synthetic field, runs all three passes, and writes a comparison figure, an overlay, and the metrics table into an output folder. synthetic.py is there so the whole thing runs before you point it at your own data — it draws a branching tree plus a mesh, and it isn't meant to resemble a real retina.

The steps also run on their own:

python run.py generate --out output/synthetic.png
python run.py segment --input output/synthetic.png --version v3
python run.py measure --mask output/synthetic_v3_mask.png --out output/metrics.csv

Point segment at your own grayscale image to trace it. Add --tissue for a stitched image with a dark surround.

python run.py segment --input your_image.tif --version v3 --tissue

Files

  • segmentation.py: the three passes and the tissue-masked variant
  • morphometry.py, morphometry2d.py: the metric panels
  • synthetic.py: the demo image generator
  • run.py: the CLI

Calibration

The flatten radius, the scale ranges, and the local-threshold block size are set by eye for the images I work with, and they're the first knobs I reach for on a new image set. Calibrate the trace against a hand-traced region before reading any of the metrics quantitatively, so you know it matches what you'd draw by hand.

About

Segmentation of faint and out-of-focus vessels in fluorescence retina images, plus a vessel-network morphometry panel.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages