TrackMate CSV Format¶
TrackMate is an ImageJ/Fiji plugin for
single-particle tracking in microscopy images. It exports tracking results as a
set of CSV files. sleap-io reads these exports and converts spot detections into
PredictedCentroid objects with track assignments.
File Structure¶
TrackMate exports three CSV files per video, sharing a common prefix:
| File | Description | Required |
|---|---|---|
*_spots.csv |
Individual spot detections with coordinates, quality score, and track assignment | Yes |
*_edges.csv |
Frame-to-frame linkages with assignment cost | No (provides tracking_score) |
*_tracks.csv |
Track-level summary statistics | No (not used) |
All CSV files have 4 header rows (field names, descriptions, abbreviations, units) followed by data rows.
Auto-Detection¶
sleap-io automatically detects TrackMate CSV files by checking for the column
signature LABEL, ID, TRACK_ID, QUALITY, POSITION_X, POSITION_Y in the first
row. When loading a spots file:
- The sibling
*_edges.csvis auto-detected (by replacing_spotswith_edgesin the filename) and used to populatetracking_scoreon each centroid. - A sibling
.tif/.tiffvideo file is auto-detected (by stripping_spotsfrom the stem) and associated with the loaded data.
Data Mapping¶
| TrackMate field | sleap-io field |
|---|---|
POSITION_X, POSITION_Y |
PredictedCentroid.x, .y |
POSITION_Z |
PredictedCentroid.z (None if 0.0) |
QUALITY |
PredictedCentroid.score |
LINK_COST (edges) |
PredictedCentroid.tracking_score |
TRACK_ID |
Track (named Track_<id>) |
LABEL |
PredictedCentroid.name |
FRAME |
LabeledFrame.frame_idx |
Reading¶
import sleap_io as sio
# Load from spots CSV (edges and video auto-detected)
labels = sio.load_trackmate("experiment_spots.csv")
# Auto-detection via load_file
labels = sio.load_file("experiment_spots.csv")
# With explicit video path
labels = sio.load_trackmate("experiment_spots.csv", video="experiment.tif")
CLI¶
TrackMate CSV files can be converted to other formats via the sio convert
command. The format is auto-detected from CSV content:
# Auto-detected from CSV content
sio convert experiment_spots.csv -o experiment.slp
# Explicit format
sio convert experiment_spots.csv -o experiment.slp --from trackmate
# Convert to NWB
sio convert experiment_spots.csv -o experiment.nwb --from trackmate
API¶
sleap_io.io.main.load_trackmate(filename, video=None, **kwargs)
¶
Read TrackMate CSV exports and return a Labels object.
Loads a TrackMate *_spots.csv file and optionally the corresponding
*_edges.csv (auto-detected if present). Spot detections are imported
as PredictedCentroid objects.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
filename
|
str
|
Path to the TrackMate spots CSV file. |
required |
video
|
Video | str | None
|
Video to associate with centroids. Can be a |
None
|
**kwargs
|
Additional arguments passed to |
required |
Returns:
| Type | Description |
|---|---|
Labels
|
Parsed labels as a |
Source code in sleap_io/io/main.py
def load_trackmate(
filename: str,
video: "Video | str | None" = None,
**kwargs,
) -> Labels:
"""Read TrackMate CSV exports and return a ``Labels`` object.
Loads a TrackMate ``*_spots.csv`` file and optionally the corresponding
``*_edges.csv`` (auto-detected if present). Spot detections are imported
as ``PredictedCentroid`` objects.
Args:
filename: Path to the TrackMate spots CSV file.
video: Video to associate with centroids. Can be a ``Video`` object,
a string path to a video file, or ``None`` (auto-detects a
sibling ``.tif`` file).
**kwargs: Additional arguments passed to ``read_trackmate_csv``.
Returns:
Parsed labels as a ``Labels`` instance with centroids.
"""
from sleap_io.io import trackmate
return trackmate.read_trackmate_csv(filename, video=video, **kwargs)
sleap_io.io.trackmate.read_trackmate_csv(spots_path, edges_path=None, video=None)
¶
Load TrackMate CSV exports into a Labels object.
The spots CSV is required. The edges CSV is optional but provides
per-link tracking_score (from TrackMate's LINK_COST).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
spots_path
|
str | Path
|
Path to the |
required |
edges_path
|
str | Path | None
|
Path to the |
None
|
video
|
Video | str | Path | None
|
Video to associate with centroids. Can be a |
None
|
Returns:
| Type | Description |
|---|---|
Labels
|
A |
Raises:
| Type | Description |
|---|---|
FileNotFoundError
|
If the spots CSV does not exist. |
ValueError
|
If the spots CSV does not have the expected TrackMate column signature. |
Source code in sleap_io/io/trackmate.py
def read_trackmate_csv(
spots_path: str | Path,
edges_path: str | Path | None = None,
video: "Video | str | Path | None" = None,
) -> "Labels":
"""Load TrackMate CSV exports into a ``Labels`` object.
The spots CSV is required. The edges CSV is optional but provides
per-link ``tracking_score`` (from TrackMate's ``LINK_COST``).
Args:
spots_path: Path to the ``*_spots.csv`` file.
edges_path: Path to the ``*_edges.csv`` file. If ``None``, attempts
to auto-detect a sibling ``_edges.csv`` alongside the spots file.
video: Video to associate with centroids. Can be a ``Video`` object,
a string/path to a video file, or ``None`` (auto-detects a
sibling ``.tif`` file).
Returns:
A ``Labels`` object with ``centroids``, ``tracks``, and optionally
``videos`` populated.
Raises:
FileNotFoundError: If the spots CSV does not exist.
ValueError: If the spots CSV does not have the expected TrackMate
column signature.
"""
from sleap_io.model.centroid import PredictedCentroid
from sleap_io.model.instance import Track
from sleap_io.model.labeled_frame import LabeledFrame
from sleap_io.model.labels import Labels
from sleap_io.model.video import Video as VideoClass
spots_path = Path(spots_path)
if not spots_path.exists():
raise FileNotFoundError(f"Spots CSV not found: {spots_path}")
# --- Auto-detect sibling files ---
if edges_path is not None:
edges_path = Path(edges_path)
else:
edges_path = _find_sibling(spots_path, "_edges")
video_obj: VideoClass | None = None
if video is not None:
if isinstance(video, (str, Path)):
video_obj = VideoClass(filename=str(video))
else:
video_obj = video
else:
tif_path = _find_sibling(spots_path, ".tif")
if tif_path is not None:
video_obj = VideoClass(filename=str(tif_path), open_backend=False)
# --- Parse edges (if available) ---
target_to_cost: dict[int, float] = {}
if edges_path is not None and edges_path.exists():
target_to_cost = _parse_edges(edges_path)
# --- Parse spots CSV ---
with open(spots_path, newline="") as f:
reader = csv.reader(f)
# Read header row to find column indices.
header = next(reader)
if tuple(header[: len(_SPOTS_SIGNATURE)]) != _SPOTS_SIGNATURE:
raise ValueError(
f"Not a TrackMate spots CSV. Expected columns starting with "
f"{_SPOTS_SIGNATURE}, got {tuple(header[:6])}."
)
col = {name: header.index(name) for name in header}
# Skip remaining header rows.
for _ in range(_HEADER_ROWS - 1):
next(reader, None)
# First pass: collect data rows and unique track IDs.
rows: list[list[str]] = []
track_ids: set[int] = set()
for row in reader:
if not row:
continue
rows.append(row)
tid = row[col["TRACK_ID"]]
if tid:
track_ids.add(int(tid))
# --- Build Track objects ---
track_map: dict[int, Track] = {}
for tid in sorted(track_ids):
track_map[tid] = Track(name=f"Track_{tid}")
tracks = list(track_map.values())
# --- Build PredictedCentroid objects ---
frame_centroids: dict[int, list[PredictedCentroid]] = {}
for row in rows:
spot_id = int(row[col["ID"]])
tid_str = row[col["TRACK_ID"]]
x = float(row[col["POSITION_X"]])
y = float(row[col["POSITION_Y"]])
z_val = float(row[col["POSITION_Z"]]) if "POSITION_Z" in col else 0.0
z = z_val if z_val != 0.0 else None
frame_idx = int(float(row[col["FRAME"]]))
score = float(row[col["QUALITY"]])
track = track_map.get(int(tid_str)) if tid_str else None
tracking_score = target_to_cost.get(spot_id)
label = row[col["LABEL"]] if "LABEL" in col else f"ID{spot_id}"
centroid = PredictedCentroid(
x=x,
y=y,
z=z,
track=track,
tracking_score=tracking_score,
score=score,
name=label,
source="trackmate",
)
# Collect per-frame centroids for distribution to LabeledFrames
frame_centroids.setdefault(frame_idx, []).append(centroid)
# --- Assemble Labels ---
videos = [video_obj] if video_obj is not None else []
labeled_frames = []
for fidx, cents in sorted(frame_centroids.items()):
lf = LabeledFrame(video=video_obj, frame_idx=fidx)
lf.centroids.extend(cents)
labeled_frames.append(lf)
labels = Labels(videos=videos, tracks=tracks, labeled_frames=labeled_frames)
labels.provenance["filename"] = str(spots_path)
return labels
sleap_io.io.trackmate.is_trackmate_file(path)
¶
Check if a CSV file is a TrackMate spots export.
Reads the first line and checks for the TrackMate column signature.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str | Path
|
Path to a CSV file. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in sleap_io/io/trackmate.py
def is_trackmate_file(path: str | Path) -> bool:
"""Check if a CSV file is a TrackMate spots export.
Reads the first line and checks for the TrackMate column signature.
Args:
path: Path to a CSV file.
Returns:
``True`` if the file looks like a TrackMate spots CSV.
"""
try:
with open(path, newline="") as f:
first_line = f.readline().strip()
cols = first_line.split(",")
return tuple(cols[: len(_SPOTS_SIGNATURE)]) == _SPOTS_SIGNATURE
except Exception:
return False