Skip to content

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.csv is auto-detected (by replacing _spots with _edges in the filename) and used to populate tracking_score on each centroid.
  • A sibling .tif / .tiff video file is auto-detected (by stripping _spots from 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 Video object, a string path to a video file, or None (auto-detects a sibling .tif file).

None
**kwargs

Additional arguments passed to read_trackmate_csv.

required

Returns:

Type Description
Labels

Parsed labels as a Labels instance with centroids.

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 *_spots.csv file.

required
edges_path str | Path | None

Path to the *_edges.csv file. If None, attempts to auto-detect a sibling _edges.csv alongside the spots file.

None
video Video | str | Path | None

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).

None

Returns:

Type Description
Labels

A Labels object with centroids, tracks, and optionally videos populated.

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

True if the file looks like a TrackMate spots CSV.

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