From one image of a drug-treated cell, can we tell which of two futures it will take, and when that became decided?
In one line: 6,676 HeLa cells tracked for 6 h under a staurosporine dose series, each with a known fate (S-BIAD2515, CC0); a fate-conditioned flow-matching model written from scratch that generates both futures of the same cell; and a measured answer to "when": from a single frame, fate is not readable before about 2 h and largely readable by 4 h.
The same starting cells take two fates. Left: a cell that survives; right: a cell that condenses its nucleus and dies. Frames every 30 min, AnnexinV at 6 h.
| Question | Answer | Where |
|---|---|---|
| When is fate readable from one frame? | Within 19.5 nM, leave-one-field-out AUROC 0.51 at 0 min, 0.60 at 2 h, 0.78 at 4 h, 0.84 at 6 h | docs/results.md, fig 2 |
| Do frozen foundation encoders see it earlier? | No. Eleven handcrafted nuclear measurements match or beat frozen DINOv2 at every informative dose; V-JEPA 2 on frames 1 to 8 is 0.045 worse than one frame of handcrafted features (95% CI -0.067 to -0.021) | results/tables/vjepa_vs_frame.csv |
| Does the generated future follow the right cell? | For 72% of cells that die with a condensed nucleus, the model's 6 h prediction is closer to the real cell than its own 0 h frame is (95% CI 54 to 80%); per-cell nucleus-size predictions correlate with the real future as well as copying does (Spearman 0.50) and better for AnnexinV-only dying cells (0.48 against 0.23) | docs/results.md |
| Does more compute help? | Eight times the training steps or twice the resolution moves the readout scores by less than 0.1; the images do look more real (DINOv2 distance to a random real cell 0.7 to 1.4 against 3.0 to 5.4). Beyond that nothing moves: 8 more hours, a VGG16 perceptual term, four times the parameters or three times the cells all give the same scores, and from the same noise the wide models draw nearly the same image as the small one (pixel difference 0.03 against 0.16 between two samples) | fig 11, fig 12b, docs/results.md |
| Is the predicted future one trajectory? | Not frame by frame: independent draws make the nucleus jump 150 to 190 px between hours (real cells 100 to 120) and change direction at random. A video model that draws the 3 to 6 h clip jointly (temporal attention after every UNet stage) matches real cells on both (98 to 105 px; 0.74 reversals for condensing cells against 0.65 real). Started from the trained frame model, it also matches the frame model on every per-frame score, so the two are had together | fig 15, docs/results.md |
| What is still wrong | A linear probe tells generated from real with AUROC above 0.99; generated dying cytoplasm turns green where real dying cells end red and blue; the generated future is not recognisable as this cell's in DINOv2 space | fig 13 |
Dose matters: both fates occur in one band, 9.8 to 39 nM. Static2Dynamic's ChromaLive model (Boyer et al., 2026) was trained at 78 and 156 nM, where nearly all cells die.
| dose (nM) | 0 to 2.4 | 4.9 | 9.8 | 19.5 | 39.1 | 78 | 156 |
|---|---|---|---|---|---|---|---|
| fraction apoptotic at 6 h | 0.11 to 0.17 | 0.19 | 0.19 | 0.31 | 0.74 | 0.91 | 0.92 |
One well is about 1.5 GB and runs on a laptop; all 30 wells are 45 GB.
uv sync
scripts/00_download_subset.sh C-06 # one well of S-BIAD2515 (CC0); no argument = all 30 wells
scripts/run_all.sh # segmentation, tracking, fate labels, single-frame baselinesThen the models (Apple M5 Pro, MPS; a GPU is faster):
uv run scripts/09_train_flow.py --minutes 45 # fate-conditioned flow model, then scripts 10 to 15 to evaluate
uv run scripts/13_train_ctx.py --minutes 60 # same model given an earlier frame of the cell
uv run pytestOn a GPU: uv run scripts/export_kaggle.py <user> writes the 2,735 cells the models use (1.7 GB);
kaggle/train_kaggle.py trains from it on Kaggle.
- You have live-cell movies with a terminal fate stain and want a tracked single-cell dataset
with fate labels (
src/static2fate/fields.py,tracking,labels). - You want a tested baseline for "how early is fate readable", including frozen DINOv2 and V-JEPA 2 against handcrafted features.
- You want a small conditional flow-matching model (
src/static2fate/flow.py, 9.4 M parameters, 45 min on a laptop) to adapt to your own crops, with the evaluation scripts (energy score, identity rank, per-cell skill) that go with it.
Not a general cell tracker and not a production model: one plate, one cell line, one drug.
- Nuclear segmentation, tracking over 13 frames and registration of the terminal AnnexinV image for all 30 wells (119 fields): 6,676 cells followed from start to end with a fate label.
- Fate label: AnnexinV above the control 90th percentile inside a cell mask, or a condensed nucleus (area halved, Hoechst doubled). Replicate wells agree within a few points.
- Single-cell crops (13 frames x 3 channels x 128 px) and single-frame fate baselines.
- Two flow-matching models (fate-conditioned; fate plus an earlier frame), their Kaggle runs at 8x the steps, 2x the resolution and 2x the width, and the paired and SDEdit-style variants that did not help.
- A video model (
src/static2fate/video.py) that draws the 3 to 6 h course of a cell as one clip, started from the frame model, with the temporal consistency measurements that motivate it (scripts/30_temporal.py). - An autoencoder for the 128 px crops (
src/static2fate/autoencoder.py, factor 4, four latent channels, L1 + perceptual + KL, optional PatchGAN) and a reconstruction check on held-out cells (scripts/32_reconstruct.py) that bounds what generation in its latent can reach; training runs on Kaggle (kaggle/ae/).
- One plate; the label fixes a 10% positive rate in controls.
- Tracking keeps a quarter to half of cells, fewer in dense regions, and almost none that divide, so every model is trained on cells that did not divide during the movie.
- At most 2.1% of "dying" labels may be mitotic cells caught rounding up.
- Two deposited files are truncated and the deposited terminal images need field matching
(handled in
src/static2fate/fields.py).
Every experiment, table and figure, in the order it was run, is in docs/results.md. Dated notes, decisions and corrections are in notes/notebook.md.
@software{gerami2026static2fate,
author = {Gerami, Matin},
title = {static2fate: single-cell fate from live-cell imaging},
year = {2026},
version = {0.1.0},
url = {https://github.com/MMatinGerami/static2fate}
}Data: S-BIAD2515, Lippincott et al., Way lab, CC0 (BioImage Archive).
MIT
