Skip to content

Spot Count per Cell

This tutorial extends the Spot Count workflow to count spots on a per-cell basis. Three pipelines work together:

  1. Nucleus — segment nuclei from the DAPI channel.
  2. Cell area (optional) — segment the cell body, or use Voronoi approximation.
  3. EV Detection — detect spots, then link each spot to the cell it falls in.

Prerequisites

  • Multi-channel images with: (1) a nuclear stain (DAPI/Hoechst), and (2) a spot channel (Cy5 etc.).
  • Optionally: a cell-body stain (cell-fill marker) for more accurate cell boundaries.

Step 1: Define Classes

ClassPurpose
dapi@nucleusSegmented nuclei
cy5@spotDetected spots
cy5@spot-in-nucleusSpots that fall inside a nucleus
cy5@spot-outsideSpots outside any nucleus

Step 2: Nucleus Pipeline

Create a pipeline named Nucleus targeting the DAPI channel:

StepSettings
Rolling BallRadius: 20–50 (larger than nuclei)
Gaussian BlurKernel: 5
ThresholdManual or Otsu auto-threshold
Connected Components
WatershedTolerance: 0.3 (optional)
Extract ROIs
Classify ROIsTarget: dapi@nucleus; Min area: 500 px²; Min circularity: 0.3

Step 3: EV Detection Pipeline

Follow the Spot Count pipeline for the EV channel to produce cy5@spot objects.

After the Classify ROIs step in the EV pipeline, add a second Classify ROIs step:

SettingValue
Origin classcy5@spot
Target classcy5@spot-in-nucleus
Intersection conditionIntersects with dapi@nucleus (min intersection: 10%)

Objects that intersect a nucleus are moved to cy5@spot-in-nucleus. Spots that do not intersect remain as cy5@spot and can optionally be moved to cy5@spot-outside by an additional step.

Step 5: Results

In the results view:

  • The cy5@spot-in-nucleus Count per image gives total spots inside nuclei.
  • With the parent object ID relationship, you can query spots per nucleus in the DuckDB results file:
SELECT parent_object_id, COUNT(*) AS spots_per_nucleus
FROM objects
WHERE class = 'cy5@spot-in-nucleus'
GROUP BY parent_object_id;

Voronoi cell approximation (no cell stain)

If no cell-body stain is available, use Voronoi to approximate cell territories from nucleus centres:

SettingValue
Centersdapi@nucleus
Max radius30 µm (adjust to expected cell size)
Output classcell@voronoi
Exclude areas at edgesenabled
Exclude areas without centreenabled

Then use cell@voronoi instead of dapi@nucleus in the intersection step.