stevafernandes commited on
Commit
812b6ee
·
verified ·
1 Parent(s): 5d31c67

Deploy CellQuant

Browse files
.gitattributes CHANGED
@@ -1,2 +1 @@
1
  *.pt filter=lfs diff=lfs merge=lfs -text
2
- examples/F100_crop_512.png filter=lfs diff=lfs merge=lfs -text
 
1
  *.pt filter=lfs diff=lfs merge=lfs -text
 
README.md CHANGED
@@ -32,10 +32,13 @@ Every nucleus receives exactly one of the four *Cell* labels.
32
 
33
  * **Model** – a UNet with an ImageNet-pretrained ResNet34 encoder (`cellquant/model.py`) that
34
  predicts, per pixel: nucleus interior / border / background, P(antibody +), P(nucleus has
35
- punctum) and punctum in-nucleus / external / background. A full-resolution refinement stage
36
- keeps 5-pixel puncta.
37
- * **Instances** – a marker-controlled watershed on the interior/border maps separates touching
38
- nuclei; puncta are connected components (`cellquant/postprocess.py`).
 
 
 
39
  * **Labels** – antibody status is the mean probability inside each nucleus; a nucleus is
40
  "Nostril" when a punctum classified "in nucleus" lies inside it (the annotation protocol;
41
  the network's own nucleus-level head is available as an alternative in *Advanced*);
@@ -43,7 +46,7 @@ Every nucleus receives exactly one of the four *Cell* labels.
43
  * **Training data** – nine 1024×1024 RGB images with 1,569 nucleus outlines and 1,057
44
  punctum outlines, parsed directly from the CVAT zip exports (`cellquant/annotations.py`).
45
 
46
- ## Validation (see `reports/SUMMARY.md` and `reports/cv/cv_report.md`)
47
 
48
  Leave-one-image-out cross-validation: nine models, each tested on an image it never saw, and
49
  each image tested 23 times (repeat runs, TTA settings, 8 rotations/flips, 6 photometric
@@ -51,17 +54,21 @@ perturbations, rule variants). Micro-averaged over the nine held-out images:
51
 
52
  | Measure | Precision | Recall | F1 |
53
  |---|---:|---:|---:|
54
- | Nucleus detection, IoU ≥ 0.5 | 77.5% | 78.4% | 77.9% |
55
- | Nucleus detection, centroid inside the annotated nucleus | 87.0% | 88.0% | 87.5% |
56
- | Punctum detection (centroid within 6 px) | 77.3% | 82.8% | 79.9% |
57
-
58
- Among matched nuclei the antibody +/− call is right for 94% and the Nostril attribute for
59
- 96%; puncta are classified in-nucleus vs external correctly 93% of the time. Scores vary by
60
- at most ±2 F1 points across the 14 rotated/perturbed runs of each image, and repeated runs are
61
- bit-identical. The weak spots are antibody-negative nuclei (F1 32%; only 102 examples, 62 of
62
- them in one image), the five-example "Cell, Nostril, Antibody −" class, and clusters of touching
63
- nuclei that are sometimes merged. The shipped `weights/cellquant.pt` was trained on all nine
64
- images with the same recipe.
 
 
 
 
65
 
66
  ## Using the Space
67
 
@@ -104,7 +111,7 @@ Retrain when more annotated images are available (put the TIFs and the CVAT zips
104
 
105
  ```bash
106
  python train.py --data-dir /path/to/folder --out weights/cellquant.pt --iters 3000
107
- python evaluate.py cv --data-dir /path/to/folder --runs-dir runs/cv --iters 3000
108
  ```
109
 
110
  Training uses Apple-silicon MPS, CUDA or CPU automatically.
 
32
 
33
  * **Model** – a UNet with an ImageNet-pretrained ResNet34 encoder (`cellquant/model.py`) that
34
  predicts, per pixel: nucleus interior / border / background, P(antibody +), P(nucleus has
35
+ punctum), punctum in-nucleus / external / background, and the normalised distance to the
36
+ nucleus outline (1 at the centre of every nucleus, 0 at its outline). A full-resolution
37
+ refinement stage keeps 5-pixel puncta.
38
+ * **Instances** – nucleus foreground comes from the class map; markers (distance > 0.5) and the
39
+ watershed landscape come from the predicted distance map, which has one peak per nucleus and
40
+ a valley between touching ones; puncta are connected components (`cellquant/postprocess.py`).
41
+ Checkpoints without the distance channel fall back to a watershed on the interior map.
42
  * **Labels** – antibody status is the mean probability inside each nucleus; a nucleus is
43
  "Nostril" when a punctum classified "in nucleus" lies inside it (the annotation protocol;
44
  the network's own nucleus-level head is available as an alternative in *Advanced*);
 
46
  * **Training data** – nine 1024×1024 RGB images with 1,569 nucleus outlines and 1,057
47
  punctum outlines, parsed directly from the CVAT zip exports (`cellquant/annotations.py`).
48
 
49
+ ## Validation (see `reports/SUMMARY.md`, `reports/cv_v2/cv_report.md` and `reports/cv_v2/comparison.md`)
50
 
51
  Leave-one-image-out cross-validation: nine models, each tested on an image it never saw, and
52
  each image tested 23 times (repeat runs, TTA settings, 8 rotations/flips, 6 photometric
 
54
 
55
  | Measure | Precision | Recall | F1 |
56
  |---|---:|---:|---:|
57
+ | Nucleus detection, IoU ≥ 0.5 | 75.8% | 85.5% | 80.4% |
58
+ | Nucleus detection, centroid inside the annotated nucleus | 83.7% | 94.5% | 88.8% |
59
+ | Punctum detection (centroid within 6 px) | 76.4% | 84.8% | 80.4% |
60
+
61
+ Among matched nuclei the antibody +/− call is right for 93.5% and the Nostril attribute for
62
+ 95.6%; puncta are classified in-nucleus vs external correctly 93.4% of the time. Scores vary by
63
+ at most ±3 F1 points across the 14 rotated/perturbed runs of each image, and repeated runs are
64
+ bit-identical. The weak spots are antibody-negative nuclei (F1 38%; only 110 examples, 62 of
65
+ them in one image, and the class-balanced loss trades their precision for recall), the five-example
66
+ "Cell, Nostril, Antibody −" class, dense clusters of touching nuclei (still the main source of misses,
67
+ although the distance-map watershed of this version recovers many that the first version merged), and
68
+ dim nuclei the annotator skipped, which the model tends to count (about 13 % more nuclei than annotated).
69
+ The previous version (v1: 2-px border, watershed on the interior map, unweighted antibody loss) scored
70
+ 77.9% nucleus F1, 87.5% by the centroid rule and 79.9% puncta F1 on the same protocol; `reports/cv/`
71
+ keeps its report. The shipped `weights/cellquant.pt` was trained on all nine images with the v2 recipe.
72
 
73
  ## Using the Space
74
 
 
111
 
112
  ```bash
113
  python train.py --data-dir /path/to/folder --out weights/cellquant.pt --iters 3000
114
+ python evaluate.py cv --data-dir /path/to/folder --runs-dir runs/cv_v2 --iters 3000
115
  ```
116
 
117
  Training uses Apple-silicon MPS, CUDA or CPU automatically.
cellquant/__init__.py CHANGED
@@ -3,4 +3,4 @@
3
  Trained on CVAT polygon annotations of 1024x1024 RGB images (blue = DAPI nuclei,
4
  green = antibody stain such as Iba1 or Tmem119, red = puncta).
5
  """
6
- __version__ = "1.0.0"
 
3
  Trained on CVAT polygon annotations of 1024x1024 RGB images (blue = DAPI nuclei,
4
  green = antibody stain such as Iba1 or Tmem119, red = puncta).
5
  """
6
+ __version__ = "1.1.0"
cellquant/data.py CHANGED
@@ -6,6 +6,9 @@ Targets built from a :class:`RasterAnnotation` (all (H, W)):
6
  cellnos float 1 where nucleus has a punctum ("Nostril"); valid on nucleus px
7
  cellmask float 1 on nucleus pixels (loss mask for the two attribute heads)
8
  nos3 int64 0 bg, 1 punctum in nucleus, 2 punctum external
 
 
 
9
  """
10
  from __future__ import annotations
11
 
@@ -19,7 +22,35 @@ from skimage.segmentation import find_boundaries
19
 
20
  from .annotations import RasterAnnotation
21
 
22
- BORDER_WIDTH = 2 # pixels of the inner border ring
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
 
25
  def make_targets(R: RasterAnnotation) -> dict[str, np.ndarray]:
@@ -43,6 +74,7 @@ def make_targets(R: RasterAnnotation) -> dict[str, np.ndarray]:
43
  "cellnos": cn,
44
  "cellmask": fg.astype(np.float32),
45
  "nos3": nos3,
 
46
  }
47
 
48
 
@@ -60,6 +92,7 @@ class AugConfig:
60
  p_blur: float = 0.3
61
  p_channel_shift: float = 0.5 # per-channel additive offset
62
  offset_max: float = 0.03
 
63
 
64
 
65
  class PatchSampler:
@@ -67,33 +100,53 @@ class PatchSampler:
67
 
68
  Geometric: random scale (zoom), random crop, random dihedral transform.
69
  Photometric: per-channel gain, gamma, additive offset, Gaussian noise, blur.
70
- Label maps use nearest-neighbour resampling.
 
 
 
 
 
71
  """
72
 
73
  def __init__(self, images: list[np.ndarray], targets: list[dict[str, np.ndarray]],
74
- cfg: AugConfig | None = None, seed: int = 0):
75
  assert len(images) == len(targets) and len(images) > 0
76
  self.images = images
77
  self.targets = targets
78
  self.cfg = cfg or AugConfig()
79
  self.rng = np.random.default_rng(seed)
 
 
 
 
80
 
81
  def _one(self) -> tuple[np.ndarray, dict[str, np.ndarray]]:
82
  cfg, rng = self.cfg, self.rng
83
- i = int(rng.integers(len(self.images)))
 
 
 
 
 
84
  img, tg = self.images[i], self.targets[i]
85
  H, W = img.shape[:2]
86
  s = float(rng.uniform(cfg.scale_min, cfg.scale_max)) # zoom factor applied to the crop
87
  src = int(round(cfg.crop / s)) # source window size
88
  src = max(32, min(src, H, W))
89
- y0 = int(rng.integers(0, H - src + 1))
90
- x0 = int(rng.integers(0, W - src + 1))
 
 
 
 
 
 
91
  patch = img[y0:y0 + src, x0:x0 + src].astype(np.float32) / 255.0
92
  tpatch = {k: v[y0:y0 + src, x0:x0 + src] for k, v in tg.items()}
93
  if src != cfg.crop:
94
  zoom = cfg.crop / src
95
  patch = ndi.zoom(patch, (zoom, zoom, 1), order=1)
96
- tpatch = {k: ndi.zoom(v, (zoom, zoom), order=0) for k, v in tpatch.items()}
97
  patch = patch[:cfg.crop, :cfg.crop]
98
  tpatch = {k: v[:cfg.crop, :cfg.crop] for k, v in tpatch.items()}
99
  if patch.shape[0] != cfg.crop or patch.shape[1] != cfg.crop: # rounding guard
@@ -145,7 +198,8 @@ class Prefetcher:
145
  fully determined by (seed, workers) regardless of thread scheduling.
146
  """
147
 
148
- def __init__(self, images, targets, cfg: AugConfig, batch: int, seed: int = 0, workers: int = 3, depth: int = 6):
 
149
  import queue
150
  import threading
151
  workers = max(1, int(workers))
@@ -156,7 +210,7 @@ class Prefetcher:
156
  self.qs: list["queue.Queue"] = [queue.Queue(maxsize=max(1, depth // workers)) for _ in range(workers)]
157
  self._threads = []
158
  for w in range(workers):
159
- s = PatchSampler(images, targets, cfg, seed=seed * 1000 + w)
160
  th = threading.Thread(target=self._work, args=(s, self.qs[w]), daemon=True)
161
  th.start()
162
  self._threads.append(th)
 
6
  cellnos float 1 where nucleus has a punctum ("Nostril"); valid on nucleus px
7
  cellmask float 1 on nucleus pixels (loss mask for the two attribute heads)
8
  nos3 int64 0 bg, 1 punctum in nucleus, 2 punctum external
9
+ dist float distance to the nucleus outline divided by the nucleus' maximum
10
+ (1 at the centre, ~0 at the outline and between touching nuclei,
11
+ 0 outside); regression target of the distance channel
12
  """
13
  from __future__ import annotations
14
 
 
22
 
23
  from .annotations import RasterAnnotation
24
 
25
+ BORDER_WIDTH = 3 # pixels of the inner border ring
26
+
27
+
28
+ def distance_target(inst: np.ndarray) -> np.ndarray:
29
+ """Per-instance Euclidean distance to the nearest pixel outside the instance
30
+ (other instances and the image edge count as outside), divided by the
31
+ instance's maximum so that every nucleus peaks at exactly 1."""
32
+ out = np.zeros(inst.shape, dtype=np.float32)
33
+ for i, sl in enumerate(ndi.find_objects(inst), start=1):
34
+ if sl is None:
35
+ continue
36
+ m = inst[sl] == i
37
+ d = ndi.distance_transform_edt(np.pad(m, 1))[1:-1, 1:-1]
38
+ mx = float(d.max())
39
+ if mx > 0:
40
+ region = out[sl]
41
+ region[m] = (d / mx)[m]
42
+ return out
43
+
44
+
45
+ def negative_nucleus_points(R: RasterAnnotation) -> np.ndarray:
46
+ """(N, 2) array of (y, x) centroids of the antibody-negative nuclei, used to
47
+ oversample crops that contain the rare class."""
48
+ neg = np.flatnonzero(~R.cell_antibody())
49
+ neg = neg[neg > 0]
50
+ if neg.size == 0:
51
+ return np.zeros((0, 2), dtype=np.float64)
52
+ ones = np.ones_like(R.cell_inst, dtype=np.float32)
53
+ return np.array(ndi.center_of_mass(ones, R.cell_inst, neg), dtype=np.float64).reshape(-1, 2)
54
 
55
 
56
  def make_targets(R: RasterAnnotation) -> dict[str, np.ndarray]:
 
74
  "cellnos": cn,
75
  "cellmask": fg.astype(np.float32),
76
  "nos3": nos3,
77
+ "dist": distance_target(inst),
78
  }
79
 
80
 
 
92
  p_blur: float = 0.3
93
  p_channel_shift: float = 0.5 # per-channel additive offset
94
  offset_max: float = 0.03
95
+ p_focus: float = 0.25 # share of crops forced to contain an antibody-negative nucleus
96
 
97
 
98
  class PatchSampler:
 
100
 
101
  Geometric: random scale (zoom), random crop, random dihedral transform.
102
  Photometric: per-channel gain, gamma, additive offset, Gaussian noise, blur.
103
+ Label maps use nearest-neighbour resampling (the distance map is bilinear).
104
+
105
+ ``focus``: optional per-image (N, 2) arrays of (y, x) points; a share
106
+ ``cfg.p_focus`` of the crops is placed so that it contains one such point,
107
+ choosing the image in proportion to its number of points (oversampling of
108
+ the antibody-negative nuclei).
109
  """
110
 
111
  def __init__(self, images: list[np.ndarray], targets: list[dict[str, np.ndarray]],
112
+ cfg: AugConfig | None = None, seed: int = 0, focus: list[np.ndarray] | None = None):
113
  assert len(images) == len(targets) and len(images) > 0
114
  self.images = images
115
  self.targets = targets
116
  self.cfg = cfg or AugConfig()
117
  self.rng = np.random.default_rng(seed)
118
+ self.focus = focus
119
+ self._focus_images = [i for i, f in enumerate(focus or []) if f is not None and len(f)]
120
+ n = np.array([len(focus[i]) for i in self._focus_images], dtype=np.float64)
121
+ self._focus_p = n / n.sum() if n.size else n
122
 
123
  def _one(self) -> tuple[np.ndarray, dict[str, np.ndarray]]:
124
  cfg, rng = self.cfg, self.rng
125
+ point = None
126
+ if self._focus_images and rng.random() < cfg.p_focus:
127
+ i = self._focus_images[int(rng.choice(len(self._focus_images), p=self._focus_p))]
128
+ point = self.focus[i][int(rng.integers(len(self.focus[i])))]
129
+ else:
130
+ i = int(rng.integers(len(self.images)))
131
  img, tg = self.images[i], self.targets[i]
132
  H, W = img.shape[:2]
133
  s = float(rng.uniform(cfg.scale_min, cfg.scale_max)) # zoom factor applied to the crop
134
  src = int(round(cfg.crop / s)) # source window size
135
  src = max(32, min(src, H, W))
136
+ if point is None:
137
+ y0 = int(rng.integers(0, H - src + 1))
138
+ x0 = int(rng.integers(0, W - src + 1))
139
+ else: # any window that contains the point
140
+ py = min(H - 1, max(0, int(round(point[0]))))
141
+ px = min(W - 1, max(0, int(round(point[1]))))
142
+ y0 = int(rng.integers(max(0, py - src + 1), min(H - src, py) + 1))
143
+ x0 = int(rng.integers(max(0, px - src + 1), min(W - src, px) + 1))
144
  patch = img[y0:y0 + src, x0:x0 + src].astype(np.float32) / 255.0
145
  tpatch = {k: v[y0:y0 + src, x0:x0 + src] for k, v in tg.items()}
146
  if src != cfg.crop:
147
  zoom = cfg.crop / src
148
  patch = ndi.zoom(patch, (zoom, zoom, 1), order=1)
149
+ tpatch = {k: ndi.zoom(v, (zoom, zoom), order=1 if k == "dist" else 0) for k, v in tpatch.items()}
150
  patch = patch[:cfg.crop, :cfg.crop]
151
  tpatch = {k: v[:cfg.crop, :cfg.crop] for k, v in tpatch.items()}
152
  if patch.shape[0] != cfg.crop or patch.shape[1] != cfg.crop: # rounding guard
 
198
  fully determined by (seed, workers) regardless of thread scheduling.
199
  """
200
 
201
+ def __init__(self, images, targets, cfg: AugConfig, batch: int, seed: int = 0, workers: int = 3, depth: int = 6,
202
+ focus: list[np.ndarray] | None = None):
203
  import queue
204
  import threading
205
  workers = max(1, int(workers))
 
210
  self.qs: list["queue.Queue"] = [queue.Queue(maxsize=max(1, depth // workers)) for _ in range(workers)]
211
  self._threads = []
212
  for w in range(workers):
213
+ s = PatchSampler(images, targets, cfg, seed=seed * 1000 + w, focus=focus)
214
  th = threading.Thread(target=self._work, args=(s, self.qs[w]), daemon=True)
215
  th.start()
216
  self._threads.append(th)
cellquant/infer.py CHANGED
@@ -5,7 +5,7 @@ import numpy as np
5
  import torch
6
 
7
  from .annotations import to_rgb_uint8
8
- from .model import CellUNet, load_checkpoint, pad_to_multiple
9
  from .postprocess import PostConfig, Result, analyze, logits_to_probs
10
 
11
 
@@ -49,16 +49,25 @@ class Predictor:
49
  assert tta in (1, 4, 8)
50
  self.tta = tta
51
  self.tile, self.overlap, self.max_whole = tile, overlap, max_whole
 
 
 
 
 
 
52
 
53
  @torch.no_grad()
54
  def _probs_tensor(self, x: torch.Tensor) -> torch.Tensor:
55
- """x: (1, 3, H, W) float in [0,1] on device, H, W multiples of 32 -> (8-ch probabilities) (1, 8, H, W)."""
56
  transforms = DIHEDRAL[:1] if self.tta == 1 else ([(0, False), (2, False), (0, True), (2, True)] if self.tta == 4 else DIHEDRAL)
57
  acc = None
58
  for k, f in transforms:
59
  out = self.model(_fwd_t(x, k, f))
60
  out = _inv_t(out, k, f)
61
- p = torch.cat([out[:, 0:3].softmax(1), out[:, 3:4].sigmoid(), out[:, 4:5].sigmoid(), out[:, 5:8].softmax(1)], dim=1)
 
 
 
62
  acc = p if acc is None else acc + p
63
  return acc / len(transforms)
64
 
@@ -72,14 +81,17 @@ class Predictor:
72
  p = self._probs_tensor(xp.to(self.device))[0, :, :H, :W].cpu().numpy()
73
  else:
74
  p = self._tiled(x)[:, :H, :W]
75
- return {"cell": p[0:3], "antibody": p[3], "cellnos": p[4], "nos": p[5:8]}
 
 
 
76
 
77
  def _tiled(self, x: torch.Tensor) -> np.ndarray:
78
  """Overlapping tiles blended with a linear ramp (no visible seams)."""
79
  _, _, H, W = x.shape
80
  t, o = self.tile, self.overlap
81
  step = t - o
82
- acc = np.zeros((8, H, W), np.float32)
83
  wsum = np.zeros((1, H, W), np.float32)
84
  ramp = np.minimum(np.arange(t) + 1, np.arange(t)[::-1] + 1).astype(np.float32)
85
  ramp = np.minimum(ramp, o) / o
 
5
  import torch
6
 
7
  from .annotations import to_rgb_uint8
8
+ from .model import DIST_CH, CellUNet, load_checkpoint, pad_to_multiple
9
  from .postprocess import PostConfig, Result, analyze, logits_to_probs
10
 
11
 
 
49
  assert tta in (1, 4, 8)
50
  self.tta = tta
51
  self.tile, self.overlap, self.max_whole = tile, overlap, max_whole
52
+ self.n_ch = int(self.model.head.out_channels)
53
+
54
+ @property
55
+ def has_dist(self) -> bool:
56
+ """True when the model predicts the distance channel (checkpoints trained after v1)."""
57
+ return self.n_ch > DIST_CH
58
 
59
  @torch.no_grad()
60
  def _probs_tensor(self, x: torch.Tensor) -> torch.Tensor:
61
+ """x: (1, 3, H, W) float in [0,1] on device, H, W multiples of 32 -> probabilities (1, 8 or 9, H, W)."""
62
  transforms = DIHEDRAL[:1] if self.tta == 1 else ([(0, False), (2, False), (0, True), (2, True)] if self.tta == 4 else DIHEDRAL)
63
  acc = None
64
  for k, f in transforms:
65
  out = self.model(_fwd_t(x, k, f))
66
  out = _inv_t(out, k, f)
67
+ parts = [out[:, 0:3].softmax(1), out[:, 3:4].sigmoid(), out[:, 4:5].sigmoid(), out[:, 5:8].softmax(1)]
68
+ if self.has_dist:
69
+ parts.append(out[:, DIST_CH:DIST_CH + 1].clamp(0.0, 1.0))
70
+ p = torch.cat(parts, dim=1)
71
  acc = p if acc is None else acc + p
72
  return acc / len(transforms)
73
 
 
81
  p = self._probs_tensor(xp.to(self.device))[0, :, :H, :W].cpu().numpy()
82
  else:
83
  p = self._tiled(x)[:, :H, :W]
84
+ probs = {"cell": p[0:3], "antibody": p[3], "cellnos": p[4], "nos": p[5:8]}
85
+ if self.has_dist:
86
+ probs["dist"] = p[DIST_CH]
87
+ return probs
88
 
89
  def _tiled(self, x: torch.Tensor) -> np.ndarray:
90
  """Overlapping tiles blended with a linear ramp (no visible seams)."""
91
  _, _, H, W = x.shape
92
  t, o = self.tile, self.overlap
93
  step = t - o
94
+ acc = np.zeros((self.n_ch, H, W), np.float32)
95
  wsum = np.zeros((1, H, W), np.float32)
96
  ramp = np.minimum(np.arange(t) + 1, np.arange(t)[::-1] + 1).astype(np.float32)
97
  ramp = np.minimum(ramp, o) / o
cellquant/losses.py CHANGED
@@ -4,7 +4,17 @@ from __future__ import annotations
4
  import torch
5
  import torch.nn.functional as F
6
 
7
- from .model import ANTIBODY_CH, CELLNOS_CH, CELL_SLICE, NOS_SLICE
 
 
 
 
 
 
 
 
 
 
8
 
9
 
10
  def soft_dice(probs: torch.Tensor, target_onehot: torch.Tensor, eps: float = 1.0) -> torch.Tensor:
@@ -15,19 +25,31 @@ def soft_dice(probs: torch.Tensor, target_onehot: torch.Tensor, eps: float = 1.0
15
  return (1 - (2 * inter + eps) / (denom + eps)).mean()
16
 
17
 
18
- def masked_bce(logit: torch.Tensor, target: torch.Tensor, mask: torch.Tensor) -> torch.Tensor:
 
 
 
19
  l = F.binary_cross_entropy_with_logits(logit, target, reduction="none")
20
- return (l * mask).sum() / mask.sum().clamp_min(1.0)
 
 
 
 
 
 
 
21
 
22
 
23
- def multitask_loss(out: torch.Tensor, t: dict[str, torch.Tensor]) -> tuple[torch.Tensor, dict[str, float]]:
 
 
24
  dev = out.device
25
  cell_logits = out[:, CELL_SLICE]
26
  nos_logits = out[:, NOS_SLICE]
27
  cell3 = t["cell3"].to(dev)
28
  nos3 = t["nos3"].to(dev)
29
- w_cell = torch.tensor([1.0, 1.0, 2.0], device=dev)
30
- w_nos = torch.tensor([1.0, 4.0, 4.0], device=dev)
31
  ce_cell = F.cross_entropy(cell_logits, cell3, weight=w_cell)
32
  ce_nos = F.cross_entropy(nos_logits, nos3, weight=w_nos)
33
  p_cell = cell_logits.softmax(1)
@@ -37,12 +59,17 @@ def multitask_loss(out: torch.Tensor, t: dict[str, torch.Tensor]) -> tuple[torch
37
  dice_cell = soft_dice(p_cell[:, 1:], oh_cell[:, 1:])
38
  dice_nos = soft_dice(p_nos[:, 1:], oh_nos[:, 1:])
39
  mask = t["cellmask"].to(dev)
40
- bce_ab = masked_bce(out[:, ANTIBODY_CH], t["antibody"].to(dev), mask)
41
  bce_cn = masked_bce(out[:, CELLNOS_CH], t["cellnos"].to(dev), mask)
42
  total = ce_cell + dice_cell + ce_nos + dice_nos + bce_ab + bce_cn
43
  parts = {
44
  "ce_cell": ce_cell.item(), "dice_cell": dice_cell.item(),
45
  "ce_nos": ce_nos.item(), "dice_nos": dice_nos.item(),
46
- "bce_ab": bce_ab.item(), "bce_cn": bce_cn.item(), "total": total.item(),
47
  }
 
 
 
 
 
48
  return total, parts
 
4
  import torch
5
  import torch.nn.functional as F
6
 
7
+ from .model import ANTIBODY_CH, CELLNOS_CH, CELL_SLICE, DIST_CH, NOS_SLICE
8
+
9
+ # Class weights of the nucleus map (background / interior / border). The border ring
10
+ # is 3 px wide (data.BORDER_WIDTH) and separates touching nuclei, so it gets the
11
+ # largest weight.
12
+ W_CELL = (1.0, 1.0, 3.0)
13
+ W_NOS = (1.0, 4.0, 4.0)
14
+ # Weight of the distance-regression term and of background pixels inside it
15
+ # (nucleus pixels have weight 1; the watershed only ever looks inside nuclei).
16
+ LAMBDA_DIST = 5.0
17
+ DIST_BG_WEIGHT = 0.1
18
 
19
 
20
  def soft_dice(probs: torch.Tensor, target_onehot: torch.Tensor, eps: float = 1.0) -> torch.Tensor:
 
25
  return (1 - (2 * inter + eps) / (denom + eps)).mean()
26
 
27
 
28
+ def masked_bce(logit: torch.Tensor, target: torch.Tensor, mask: torch.Tensor, neg_weight: float = 1.0) -> torch.Tensor:
29
+ """BCE averaged over ``mask`` pixels. ``neg_weight`` > 1 up-weights pixels whose
30
+ target is 0 (class balancing for a rare negative class); the result is normalised
31
+ by the summed weights so its magnitude does not depend on the weight."""
32
  l = F.binary_cross_entropy_with_logits(logit, target, reduction="none")
33
+ w = mask if neg_weight == 1.0 else mask * torch.where(target > 0.5, 1.0, float(neg_weight))
34
+ return (l * w).sum() / w.sum().clamp_min(1.0)
35
+
36
+
37
+ def distance_loss(pred: torch.Tensor, target: torch.Tensor, cellmask: torch.Tensor) -> torch.Tensor:
38
+ """Weighted mean squared error between the predicted and the target distance map."""
39
+ w = cellmask + DIST_BG_WEIGHT * (1.0 - cellmask)
40
+ return ((pred - target) ** 2 * w).sum() / w.sum().clamp_min(1.0)
41
 
42
 
43
+ def multitask_loss(out: torch.Tensor, t: dict[str, torch.Tensor], ab_neg_weight: float = 1.0) -> tuple[torch.Tensor, dict[str, float]]:
44
+ """``out``: (B, 8 or 9, H, W) network output; ``t``: targets from data.make_targets.
45
+ ``ab_neg_weight`` balances the antibody head towards antibody-negative pixels."""
46
  dev = out.device
47
  cell_logits = out[:, CELL_SLICE]
48
  nos_logits = out[:, NOS_SLICE]
49
  cell3 = t["cell3"].to(dev)
50
  nos3 = t["nos3"].to(dev)
51
+ w_cell = torch.tensor(W_CELL, device=dev)
52
+ w_nos = torch.tensor(W_NOS, device=dev)
53
  ce_cell = F.cross_entropy(cell_logits, cell3, weight=w_cell)
54
  ce_nos = F.cross_entropy(nos_logits, nos3, weight=w_nos)
55
  p_cell = cell_logits.softmax(1)
 
59
  dice_cell = soft_dice(p_cell[:, 1:], oh_cell[:, 1:])
60
  dice_nos = soft_dice(p_nos[:, 1:], oh_nos[:, 1:])
61
  mask = t["cellmask"].to(dev)
62
+ bce_ab = masked_bce(out[:, ANTIBODY_CH], t["antibody"].to(dev), mask, neg_weight=ab_neg_weight)
63
  bce_cn = masked_bce(out[:, CELLNOS_CH], t["cellnos"].to(dev), mask)
64
  total = ce_cell + dice_cell + ce_nos + dice_nos + bce_ab + bce_cn
65
  parts = {
66
  "ce_cell": ce_cell.item(), "dice_cell": dice_cell.item(),
67
  "ce_nos": ce_nos.item(), "dice_nos": dice_nos.item(),
68
+ "bce_ab": bce_ab.item(), "bce_cn": bce_cn.item(),
69
  }
70
+ if out.shape[1] > DIST_CH and "dist" in t:
71
+ mse_dist = distance_loss(out[:, DIST_CH], t["dist"].to(dev), mask)
72
+ total = total + LAMBDA_DIST * mse_dist
73
+ parts["mse_dist"] = mse_dist.item()
74
+ parts["total"] = total.item()
75
  return total, parts
cellquant/model.py CHANGED
@@ -5,6 +5,13 @@ Output channels (all at full input resolution):
5
  3 antibody logit : P(antibody positive) (meaningful on nucleus pixels)
6
  4 nostril-attr logit: P(nucleus contains punctum) (meaningful on nucleus pixels)
7
  5..7 punctum classes : background / in nucleus / external (softmax)
 
 
 
 
 
 
 
8
  """
9
  from __future__ import annotations
10
 
@@ -12,11 +19,13 @@ import torch
12
  import torch.nn as nn
13
  import torch.nn.functional as F
14
 
15
- N_OUT = 8
16
  CELL_SLICE = slice(0, 3)
17
  ANTIBODY_CH = 3
18
  CELLNOS_CH = 4
19
  NOS_SLICE = slice(5, 8)
 
 
20
 
21
  # ImageNet statistics; inputs are RGB in [0, 1].
22
  MEAN = (0.485, 0.456, 0.406)
 
5
  3 antibody logit : P(antibody positive) (meaningful on nucleus pixels)
6
  4 nostril-attr logit: P(nucleus contains punctum) (meaningful on nucleus pixels)
7
  5..7 punctum classes : background / in nucleus / external (softmax)
8
+ 8 distance : normalised distance to the nucleus outline (regression,
9
+ 1 at the centre of each nucleus, 0 at its outline and
10
+ outside); the watershed landscape in postprocess.py
11
+
12
+ Checkpoints written before channel 8 existed carry ``n_out = 8``; load_checkpoint
13
+ rebuilds them without the distance head and post-processing falls back to the
14
+ interior map.
15
  """
16
  from __future__ import annotations
17
 
 
19
  import torch.nn as nn
20
  import torch.nn.functional as F
21
 
22
+ N_OUT = 9
23
  CELL_SLICE = slice(0, 3)
24
  ANTIBODY_CH = 3
25
  CELLNOS_CH = 4
26
  NOS_SLICE = slice(5, 8)
27
+ DIST_CH = 8
28
+ N_OUT_LEGACY = 8 # checkpoints without the distance channel
29
 
30
  # ImageNet statistics; inputs are RGB in [0, 1].
31
  MEAN = (0.485, 0.456, 0.406)
cellquant/postprocess.py CHANGED
@@ -8,13 +8,14 @@ from scipy import ndimage as ndi
8
  from skimage.segmentation import find_boundaries, watershed
9
 
10
  from .labels import ALL_LABELS, LABEL_COLORS, cell_label, nostril_label
11
- from .model import ANTIBODY_CH, CELLNOS_CH, CELL_SLICE, NOS_SLICE
12
 
13
 
14
  @dataclass
15
  class PostConfig:
16
  cell_fg_thr: float = 0.5 # P(interior) + P(border) > thr -> nucleus foreground
17
- marker_thr: float = 0.5 # P(interior) > thr -> watershed markers
 
18
  min_marker_area: int = 12 # px
19
  min_cell_area: int = 50 # px (smallest traced nucleus in the training set is 35 px)
20
  nos_fg_thr: float = 0.5 # 1 - P(background) > thr -> punctum foreground
@@ -27,33 +28,43 @@ class PostConfig:
27
 
28
 
29
  def logits_to_probs(logits: np.ndarray) -> dict[str, np.ndarray]:
30
- """logits: (8, H, W) float -> dict of probabilities."""
 
31
  def softmax(z):
32
  z = z - z.max(axis=0, keepdims=True)
33
  e = np.exp(z)
34
  return e / e.sum(axis=0, keepdims=True)
35
  def sigmoid(z):
36
  return 1.0 / (1.0 + np.exp(-z))
37
- return {
38
  "cell": softmax(logits[CELL_SLICE].astype(np.float32)),
39
  "antibody": sigmoid(logits[ANTIBODY_CH].astype(np.float32)),
40
  "cellnos": sigmoid(logits[CELLNOS_CH].astype(np.float32)),
41
  "nos": softmax(logits[NOS_SLICE].astype(np.float32)),
42
  }
 
 
 
 
43
 
 
 
44
 
45
- def segment_cells(p_cell: np.ndarray, cfg: PostConfig) -> np.ndarray:
46
- """p_cell: (3, H, W) softmax (bg, interior, border) -> int32 instance map."""
 
 
47
  p_int, p_bor = p_cell[1], p_cell[2]
48
  fg = (p_int + p_bor) > cfg.cell_fg_thr
49
- markers, n = ndi.label(p_int > cfg.marker_thr)
 
50
  if n:
51
  sizes = np.bincount(markers.ravel())
52
  small = sizes < cfg.min_marker_area
53
  small[0] = False
54
  markers[small[markers]] = 0
55
  markers, n = ndi.label(markers > 0)
56
- inst = watershed(-p_int, markers, mask=fg) if n else np.zeros(fg.shape, np.int32)
57
  inst = inst.astype(np.int32)
58
  # foreground components that received no marker become instances of their own
59
  orphan, m = ndi.label(fg & (inst == 0))
@@ -129,7 +140,7 @@ class Result:
129
 
130
  def analyze(probs: dict[str, np.ndarray], cfg: PostConfig | None = None) -> Result:
131
  cfg = cfg or PostConfig()
132
- cell_inst = segment_cells(probs["cell"], cfg)
133
  nos_inst = segment_nostrils(probs["nos"], cfg)
134
  n_cells = int(cell_inst.max())
135
  n_nos = int(nos_inst.max())
 
8
  from skimage.segmentation import find_boundaries, watershed
9
 
10
  from .labels import ALL_LABELS, LABEL_COLORS, cell_label, nostril_label
11
+ from .model import ANTIBODY_CH, CELLNOS_CH, CELL_SLICE, DIST_CH, NOS_SLICE
12
 
13
 
14
  @dataclass
15
  class PostConfig:
16
  cell_fg_thr: float = 0.5 # P(interior) + P(border) > thr -> nucleus foreground
17
+ marker_thr: float = 0.5 # predicted distance (or, for models without the distance channel,
18
+ # P(interior)) > thr -> watershed markers
19
  min_marker_area: int = 12 # px
20
  min_cell_area: int = 50 # px (smallest traced nucleus in the training set is 35 px)
21
  nos_fg_thr: float = 0.5 # 1 - P(background) > thr -> punctum foreground
 
28
 
29
 
30
  def logits_to_probs(logits: np.ndarray) -> dict[str, np.ndarray]:
31
+ """logits: (8 or 9, H, W) float -> dict of probabilities; the optional 9th channel
32
+ (normalised distance to the nucleus outline) is clipped to [0, 1] as "dist"."""
33
  def softmax(z):
34
  z = z - z.max(axis=0, keepdims=True)
35
  e = np.exp(z)
36
  return e / e.sum(axis=0, keepdims=True)
37
  def sigmoid(z):
38
  return 1.0 / (1.0 + np.exp(-z))
39
+ out = {
40
  "cell": softmax(logits[CELL_SLICE].astype(np.float32)),
41
  "antibody": sigmoid(logits[ANTIBODY_CH].astype(np.float32)),
42
  "cellnos": sigmoid(logits[CELLNOS_CH].astype(np.float32)),
43
  "nos": softmax(logits[NOS_SLICE].astype(np.float32)),
44
  }
45
+ if logits.shape[0] > DIST_CH:
46
+ out["dist"] = np.clip(logits[DIST_CH].astype(np.float32), 0.0, 1.0)
47
+ return out
48
+
49
 
50
+ def segment_cells(p_cell: np.ndarray, cfg: PostConfig, dist: np.ndarray | None = None) -> np.ndarray:
51
+ """p_cell: (3, H, W) softmax (bg, interior, border) -> int32 instance map.
52
 
53
+ Foreground always comes from the class map. Markers and the watershed
54
+ landscape come from ``dist`` (the predicted normalised distance to the
55
+ outline, which has one peak per nucleus and a valley between touching ones)
56
+ when the model provides it, otherwise from P(interior)."""
57
  p_int, p_bor = p_cell[1], p_cell[2]
58
  fg = (p_int + p_bor) > cfg.cell_fg_thr
59
+ land = p_int if dist is None else dist
60
+ markers, n = ndi.label((land > cfg.marker_thr) & fg)
61
  if n:
62
  sizes = np.bincount(markers.ravel())
63
  small = sizes < cfg.min_marker_area
64
  small[0] = False
65
  markers[small[markers]] = 0
66
  markers, n = ndi.label(markers > 0)
67
+ inst = watershed(-land, markers, mask=fg) if n else np.zeros(fg.shape, np.int32)
68
  inst = inst.astype(np.int32)
69
  # foreground components that received no marker become instances of their own
70
  orphan, m = ndi.label(fg & (inst == 0))
 
140
 
141
  def analyze(probs: dict[str, np.ndarray], cfg: PostConfig | None = None) -> Result:
142
  cfg = cfg or PostConfig()
143
+ cell_inst = segment_cells(probs["cell"], cfg, probs.get("dist"))
144
  nos_inst = segment_nostrils(probs["nos"], cfg)
145
  n_cells = int(cell_inst.max())
146
  n_nos = int(nos_inst.max())
reports/SUMMARY.md CHANGED
@@ -3,70 +3,103 @@
3
  Data: 9 annotated 1024×1024 images, 1569 nucleus outlines and 1057 punctum outlines.
4
  Protocol: leave-one-image-out cross-validation (one model per image, 3000 iterations each, held-out image never seen in training), then a final model trained on all images with the same recipe. Each held-out image was tested 23 times: 3 identical runs (determinism), no-TTA / 4× / 8× test-time augmentation, 8 rotations and flips, 6 photometric perturbations, and 6 post-processing rule variants. The only choice made on held-out data is the rule that gives a nucleus the Nostril attribute: of three candidate rules, 'contains an in-nucleus punctum' (the annotation protocol) scored best and is the default; see the rule-variant table in cv_report.md.
5
 
 
 
6
  ## Headline numbers (micro-averaged over all held-out images, no TTA)
7
 
8
  | Measure | Precision | Recall | F1 |
9
  |---|---:|---:|---:|
10
- | Nucleus detection, IoU ≥ 0.5 | 77.5% | 78.4% | 77.9% |
11
- | Nucleus detection, IoU ≥ 0.3 | 85.6% | 86.7% | 86.2% |
12
- | Nucleus detection, centroid inside GT nucleus | 87.0% | 88.0% | 87.5% |
13
- | Punctum detection (centroid ≤ 6 px) | 77.3% | 82.8% | 79.9% |
14
- | Nucleus detection, IoU ≥ 0.5, 8× TTA | 78.1% | 78.9% | 78.5% |
15
 
16
  Per-class F1 (a true positive needs a matched object **and** the right label):
17
 
18
  | Label | GT | Pred | F1 |
19
  |---|---:|---:|---:|
20
- | Cell, Antibody + | 1061 | 1179 | 72.5% |
21
- | Cell, Antibody - | 105 | 39 | 31.9% |
22
- | Cell, Nostril, Antibody + | 398 | 370 | 71.6% |
23
  | Cell, Nostril, Antibody - | 5 | 0 | 0.0% |
24
- | Nostril in Nucleus | 525 | 529 | 78.9% |
25
- | Nostril External of Nucleus | 532 | 603 | 70.3% |
26
 
27
  ## Per held-out image
28
 
29
  | Image | Nuclei F1 | Antibody acc | Nostril-attr acc | Puncta F1 | Nuclei F1 over 14 perturbed runs (mean ± sd) | Deterministic |
30
  |---|---:|---:|---:|---:|---|---|
31
- | C100 | 81.2% | 99.3% | 98.6% | 74.4% | 82.5 ± 1.0 | yes |
32
- | C102 | 90.6% | 100.0% | 90.8% | 76.3% | 89.9 ± 1.8 | yes |
33
- | C110 | 78.5% | 100.0% | 91.9% | 84.3% | 77.5 ± 1.1 | yes |
34
- | C112 | 75.0% | 96.6% | 97.4% | 75.8% | 73.6 ± 1.5 | yes |
35
- | F100 | 75.1% | 97.8% | 97.1% | 85.2% | 72.2 ± 1.3 | yes |
36
- | F110 | 72.0% | 96.0% | 94.9% | 78.7% | 71.7 ± 1.9 | yes |
37
- | F113 | 65.4% | 100.0% | 97.1% | 85.2% | 65.6 ± 2.1 | yes |
38
- | F114 | 78.8% | 93.3% | 94.4% | 67.5% | 77.8 ± 1.7 | yes |
39
- | F99 | 84.4% | 77.5% | 99.5% | 71.4% | 85.3 ± 1.2 | yes |
40
 
41
  ## Final model (trained on all images) – fit on its own training images, NOT a generalisation estimate
42
 
43
  | Image | Nuclei F1 | Attribute acc | Puncta F1 |
44
  |---|---:|---:|---:|
45
- | C100 | 86.7% | 98.7% | 82.1% |
46
- | C102 | 93.3% | 94.4% | 84.6% |
47
- | C110 | 88.6% | 95.7% | 85.0% |
48
- | C112 | 83.8% | 99.2% | 86.2% |
49
- | F100 | 87.2% | 96.2% | 90.7% |
50
- | F110 | 91.7% | 94.8% | 87.2% |
51
- | F113 | 83.7% | 97.7% | 87.1% |
52
- | F114 | 88.3% | 93.4% | 83.1% |
53
- | F99 | 89.7% | 91.2% | 75.7% |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
54
 
55
- ## Antibody − threshold (held-out matched nuclei: 1230, of which 87 negative)
56
 
57
- Default threshold 0.5. Best Antibody − F1 in the sweep: 52.3% at threshold 0.85 (precision 62.9%, recall 44.8%); the full curve is in cv_report.md. The default was kept because the class is dominated by a single image (F99 holds 62 of the 102 negatives).
58
 
59
  ## What limits the scores
60
 
61
- * **Touching nuclei are sometimes merged** into one outline (worst on F113); this is the main source of IoU-0.5 misses. The marker/threshold sweep (`runs/cv/postprocess_sweep.json`) is flat, so it is a network-border issue, not a post-processing one.
62
- * **Annotation completeness**: most unmatched predictions overlap an annotated nucleus (outline disagreement) and the rest are dimmer, smaller objects the annotator skipped; many faint red dots were likewise not annotated, which caps external-punctum precision.
63
- * **Antibody − is rare** (102 of 1569 nuclei, 62 in one image), and **Cell, Nostril, Antibody −** has five examples; their scores are not reliable.
64
  * Scores are from nine images of one experiment (35-day spinal cord, Iba1/TMEM119). New staining batches or magnifications need a check against a few annotated images.
65
 
66
  ## Reproduce
67
 
68
  ```bash
69
- python evaluate.py cv --data-dir <folder with TIFs and CVAT zips> --runs-dir runs/cv --iters 3000
70
  python evaluate.py final --data-dir <folder> --weights weights/cellquant.pt
71
- python scripts/build_summary.py
 
72
  ```
 
3
  Data: 9 annotated 1024×1024 images, 1569 nucleus outlines and 1057 punctum outlines.
4
  Protocol: leave-one-image-out cross-validation (one model per image, 3000 iterations each, held-out image never seen in training), then a final model trained on all images with the same recipe. Each held-out image was tested 23 times: 3 identical runs (determinism), no-TTA / 4× / 8× test-time augmentation, 8 rotations and flips, 6 photometric perturbations, and 6 post-processing rule variants. The only choice made on held-out data is the rule that gives a nucleus the Nostril attribute: of three candidate rules, 'contains an in-nucleus punctum' (the annotation protocol) scored best and is the default; see the rule-variant table in cv_report.md.
5
 
6
+ Recipe (v2, shipped): ResNet34-UNet with a full-resolution stem; targets are a 3-px inner border ring (class weight 3), a per-nucleus normalised distance-to-outline map that the watershed uses as its landscape (markers = distance > 0.5), an antibody head whose loss up-weights antibody-negative pixels by sqrt(positive / negative pixels) (4.5 on the full set), and 25 % of training crops centred on an antibody-negative nucleus. v1 (reports/cv/) used a 2-px ring, the interior map as watershed landscape and an unweighted antibody loss; the change table below compares the two under the same protocol.
7
+
8
  ## Headline numbers (micro-averaged over all held-out images, no TTA)
9
 
10
  | Measure | Precision | Recall | F1 |
11
  |---|---:|---:|---:|
12
+ | Nucleus detection, IoU ≥ 0.5 | 75.8% | 85.5% | 80.4% |
13
+ | Nucleus detection, IoU ≥ 0.3 | 82.7% | 93.3% | 87.7% |
14
+ | Nucleus detection, centroid inside GT nucleus | 83.7% | 94.5% | 88.8% |
15
+ | Punctum detection (centroid ≤ 6 px) | 76.4% | 84.8% | 80.4% |
16
+ | Nucleus detection, IoU ≥ 0.5, 8× TTA | 75.9% | 85.7% | 80.5% |
17
 
18
  Per-class F1 (a true positive needs a matched object **and** the right label):
19
 
20
  | Label | GT | Pred | F1 |
21
  |---|---:|---:|---:|
22
+ | Cell, Antibody + | 1061 | 1292 | 74.4% |
23
+ | Cell, Antibody - | 105 | 91 | 37.8% |
24
+ | Cell, Nostril, Antibody + | 398 | 388 | 72.8% |
25
  | Cell, Nostril, Antibody - | 5 | 0 | 0.0% |
26
+ | Nostril in Nucleus | 525 | 532 | 78.7% |
27
+ | Nostril External of Nucleus | 532 | 641 | 71.8% |
28
 
29
  ## Per held-out image
30
 
31
  | Image | Nuclei F1 | Antibody acc | Nostril-attr acc | Puncta F1 | Nuclei F1 over 14 perturbed runs (mean ± sd) | Deterministic |
32
  |---|---:|---:|---:|---:|---|---|
33
+ | C100 | 84.4% | 96.9% | 98.1% | 71.4% | 85.5 ± 1.5 | yes |
34
+ | C102 | 86.4% | 100.0% | 89.5% | 78.9% | 86.6 ± 1.2 | yes |
35
+ | C110 | 77.4% | 98.7% | 94.2% | 83.8% | 78.4 ± 1.5 | yes |
36
+ | C112 | 71.6% | 95.0% | 97.5% | 76.9% | 71.0 ± 1.4 | yes |
37
+ | F100 | 82.9% | 98.1% | 94.4% | 85.7% | 78.8 ± 2.8 | yes |
38
+ | F110 | 77.0% | 97.2% | 90.8% | 82.9% | 71.7 ± 2.5 | yes |
39
+ | F113 | 69.9% | 100.0% | 97.4% | 83.3% | 70.6 ± 1.5 | yes |
40
+ | F114 | 82.4% | 90.1% | 94.1% | 63.5% | 81.8 ± 0.8 | yes |
41
+ | F99 | 87.5% | 79.3% | 99.6% | 74.3% | 87.8 ± 0.7 | yes |
42
 
43
  ## Final model (trained on all images) – fit on its own training images, NOT a generalisation estimate
44
 
45
  | Image | Nuclei F1 | Attribute acc | Puncta F1 |
46
  |---|---:|---:|---:|
47
+ | C100 | 93.0% | 98.3% | 80.0% |
48
+ | C102 | 91.5% | 94.5% | 78.8% |
49
+ | C110 | 87.0% | 96.4% | 83.9% |
50
+ | C112 | 80.0% | 99.2% | 82.0% |
51
+ | F100 | 88.7% | 94.8% | 90.3% |
52
+ | F110 | 89.7% | 97.5% | 88.6% |
53
+ | F113 | 84.8% | 97.9% | 92.1% |
54
+ | F114 | 90.8% | 95.1% | 82.5% |
55
+ | F99 | 91.5% | 97.1% | 90.9% |
56
+
57
+ ## Change from v1 (same protocol, same nine held-out images; `reports/cv_v2/comparison.md` has the full table)
58
+
59
+ | Measure | v1 | v2 | Δ (points) |
60
+ |---|---:|---:|---:|
61
+ | Nucleus F1, IoU ≥ 0.5 | 77.9% | 80.4% | +2.4 |
62
+ | Nucleus precision, IoU ≥ 0.5 | 77.5% | 75.8% | -1.7 |
63
+ | Nucleus recall, IoU ≥ 0.5 | 78.4% | 85.5% | +7.1 |
64
+ | Nucleus F1, centroid rule | 87.5% | 88.8% | +1.3 |
65
+ | Nucleus F1, IoU ≥ 0.5, 8× TTA | 78.5% | 80.5% | +2.0 |
66
+ | Puncta F1 | 79.9% | 80.4% | +0.4 |
67
+ | F1 [Cell, Antibody +] | 72.5% | 74.4% | +1.9 |
68
+ | F1 [Cell, Antibody -] | 31.9% | 37.8% | +5.8 |
69
+ | F1 [Cell, Nostril, Antibody +] | 71.6% | 72.8% | +1.2 |
70
+ | F1 [Cell, Nostril, Antibody -] | 0.0% | 0.0% | +0.0 |
71
+ | F1 [Nostril in Nucleus] | 78.9% | 78.7% | -0.2 |
72
+ | F1 [Nostril External of Nucleus] | 70.3% | 71.8% | +1.5 |
73
+ | Attribute accuracy, both (each model's own matched nuclei) | 90.2% | 89.3% | -1.0 |
74
+ | Punctum type accuracy (matched puncta) | 93.1% | 93.4% | +0.3 |
75
+ | Nuclei F1, C100 held out | 81.2% | 84.4% | +3.2 |
76
+ | Nuclei F1, C102 held out | 90.6% | 86.4% | -4.2 |
77
+ | Nuclei F1, C110 held out | 78.5% | 77.4% | -1.1 |
78
+ | Nuclei F1, C112 held out | 75.0% | 71.6% | -3.4 |
79
+ | Nuclei F1, F100 held out | 75.1% | 82.9% | +7.8 |
80
+ | Nuclei F1, F110 held out | 72.0% | 77.0% | +5.0 |
81
+ | Nuclei F1, F113 held out | 65.4% | 69.9% | +4.5 |
82
+ | Nuclei F1, F114 held out | 78.8% | 82.4% | +3.7 |
83
+ | Nuclei F1, F99 held out | 84.4% | 87.5% | +3.2 |
84
+
85
+ v2 misses far fewer nuclei (339 → 227 unmatched annotated nuclei, mostly touching nuclei that v1 merged) at the cost of more unmatched predictions (358 → 429): it predicts 1771 nuclei for 1569 annotated ones (v1: 1588). The extra predictions are dim objects the annotator skipped and outline/split disagreements in roughly the proportion shown in the FP table of cv_report.md. On the nuclei matched by both models the attribute accuracy differs by less than one point (see comparison.md), which is within the sampling error of ~1200 nuclei; the antibody-negative class gains recall (26 % → 38 % on matched nuclei) and loses precision (74 % → 59 %).
86
 
87
+ ## Antibody − threshold (held-out matched nuclei: 1342, of which 98 negative)
88
 
89
+ Default threshold 0.5. Best Antibody − F1 in the sweep: 50.8% at threshold 0.90 (precision 51.6%, recall 50.0%); the full curve is in cv_report.md. The default was kept because the class is dominated by a single image (F99 holds 62 of the 110 negatives).
90
 
91
  ## What limits the scores
92
 
93
+ * **Touching nuclei** are still the main source of IoU-0.5 misses (worst on F113, the densest image). The distance-map watershed of v2 recovers many clusters that v1 merged (v1 marker/threshold sweeps in `reports/cv/postprocess_sweep.json` had shown post-processing alone could not), but some clusters are still merged and a few nuclei are now split in two.
94
+ * **Annotation completeness**: most unmatched predictions overlap an annotated nucleus (outline disagreement) and the rest are dimmer, smaller objects the annotator skipped; many faint red dots were likewise not annotated, which caps external-punctum precision. v2 detects more of these dim objects than v1, so it over-counts nuclei by about 13 % on the annotated images.
95
+ * **Antibody − is rare** (110 of 1569 nuclei, 62 in one image), and **Cell, Nostril, Antibody −** has five examples; their scores are not reliable. The class-balanced loss of v2 trades antibody-negative precision for recall.
96
  * Scores are from nine images of one experiment (35-day spinal cord, Iba1/TMEM119). New staining batches or magnifications need a check against a few annotated images.
97
 
98
  ## Reproduce
99
 
100
  ```bash
101
+ python evaluate.py cv --data-dir <folder with TIFs and CVAT zips> --runs-dir runs/cv_v2 --iters 3000
102
  python evaluate.py final --data-dir <folder> --weights weights/cellquant.pt
103
+ python scripts/compare_cv.py reports/cv/cv_results.json runs/cv_v2/cv_results.json --out reports/cv_v2/comparison.md
104
+ python scripts/build_summary.py --cv-dir runs/cv_v2
105
  ```
reports/cv_v2/comparison.md ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Cross-validation comparison: v1 vs v2
2
+
3
+ `reports/cv/cv_results.json` vs `runs/cv_v2/cv_results.json`; micro-averaged over the held-out images (base run, no TTA unless stated).
4
+
5
+ | Measure | v1 | v2 | Δ (points) |
6
+ |---|---:|---:|---:|
7
+ | Nucleus F1, IoU ≥ 0.5 | 77.9% | 80.4% | +2.4 |
8
+ | Nucleus precision, IoU ≥ 0.5 | 77.5% | 75.8% | -1.7 |
9
+ | Nucleus recall, IoU ≥ 0.5 | 78.4% | 85.5% | +7.1 |
10
+ | Nucleus F1, IoU ≥ 0.3 | 86.2% | 87.7% | +1.5 |
11
+ | Nucleus F1, centroid rule | 87.5% | 88.8% | +1.3 |
12
+ | Nucleus F1, IoU ≥ 0.5, 8× TTA | 78.5% | 80.5% | +2.0 |
13
+ | Puncta F1 | 79.9% | 80.4% | +0.4 |
14
+ | Puncta F1, 8× TTA | 81.4% | 81.8% | +0.5 |
15
+ | Attribute accuracy (both, matched nuclei) | 90.2% | 89.3% | -1.0 |
16
+ | Punctum type accuracy (matched puncta) | 93.1% | 93.4% | +0.3 |
17
+ | F1 [Cell, Antibody +] | 72.5% | 74.4% | +1.9 |
18
+ | F1 [Cell, Antibody -] | 31.9% | 37.8% | +5.8 |
19
+ | F1 [Cell, Nostril, Antibody +] | 71.6% | 72.8% | +1.2 |
20
+ | F1 [Cell, Nostril, Antibody -] | 0.0% | 0.0% | +0.0 |
21
+ | F1 [Nostril in Nucleus] | 78.9% | 78.7% | -0.2 |
22
+ | F1 [Nostril External of Nucleus] | 70.3% | 71.8% | +1.5 |
23
+ | Precision [Cell, Antibody -] | 59.0% | 40.7% | -18.3 |
24
+ | Recall [Cell, Antibody -] | 21.9% | 35.2% | +13.3 |
25
+
26
+ ## Per held-out image
27
+
28
+ | Image | Nuclei F1 v1 | Nuclei F1 v2 | Δ | Centroid F1 v1 | Centroid F1 v2 | Antibody acc v1 | Antibody acc v2 | Puncta F1 v1 | Puncta F1 v2 |
29
+ |---|---:|---:|---:|---:|---:|---:|---:|---:|---:|
30
+ | C100 | 81.2% | 84.4% | +3.2 | 89.5% | 91.3% | 99.3% | 96.9% | 74.4% | 71.4% |
31
+ | C102 | 90.6% | 86.4% | -4.2 | 92.7% | 90.5% | 100.0% | 100.0% | 76.3% | 78.9% |
32
+ | C110 | 78.5% | 77.4% | -1.1 | 88.1% | 88.3% | 100.0% | 98.7% | 84.3% | 83.8% |
33
+ | C112 | 75.0% | 71.6% | -3.4 | 87.2% | 83.4% | 96.6% | 95.0% | 75.8% | 76.9% |
34
+ | F100 | 75.1% | 82.9% | +7.8 | 84.9% | 89.6% | 97.8% | 98.1% | 85.2% | 85.7% |
35
+ | F110 | 72.0% | 77.0% | +5.0 | 85.1% | 86.9% | 96.0% | 97.2% | 78.7% | 82.9% |
36
+ | F113 | 65.4% | 69.9% | +4.5 | 83.3% | 86.1% | 100.0% | 100.0% | 85.2% | 83.3% |
37
+ | F114 | 78.8% | 82.4% | +3.7 | 84.5% | 89.4% | 93.3% | 90.1% | 67.5% | 63.5% |
38
+ | F99 | 84.4% | 87.5% | +3.2 | 92.3% | 91.7% | 77.5% | 79.3% | 71.4% | 74.3% |
39
+
40
+ ## Robustness (nuclei F1 over the 14 rotated / perturbed runs per image)
41
+
42
+ | Image | v1 mean ± sd | v2 mean ± sd |
43
+ |---|---|---|
44
+ | C100 | 82.5 ± 1.0 | 85.5 ± 1.5 |
45
+ | C102 | 89.9 ± 1.8 | 86.6 ± 1.2 |
46
+ | C110 | 77.5 ± 1.1 | 78.4 ± 1.5 |
47
+ | C112 | 73.6 ± 1.5 | 71.0 ± 1.4 |
48
+ | F100 | 72.2 ± 1.3 | 78.8 ± 2.8 |
49
+ | F110 | 71.7 ± 1.9 | 71.7 ± 2.5 |
50
+ | F113 | 65.6 ± 2.1 | 70.6 ± 1.5 |
51
+ | F114 | 77.8 ± 1.7 | 81.8 ± 0.8 |
52
+ | F99 | 85.3 ± 1.2 | 87.8 ± 0.7 |
53
+
54
+ ## Antibody-negative class
55
+
56
+ | | v1 | v2 |
57
+ |---|---:|---:|
58
+ | Cell, Antibody -: GT / predicted / TP | 105 / 39 / 23 | 105 / 91 / 37 |
59
+ | Cell, Nostril, Antibody -: GT / predicted / TP | 5 / 0 / 0 | 5 / 0 / 0 |
60
+ | Antibody − F1 among matched nuclei at the default threshold 0.5 | 39.0% (P 74.2%, R 26.4%) | 46.0% (P 58.7%, R 37.8%) |
61
+ | Best threshold in the sweep (not used) | 52.3% at 0.85 | 50.8% at 0.90 |
62
+
63
+ ## Attribute accuracy on the 1194 held-out nuclei matched by both v1 and v2 (IoU >= 0.5, base run)
64
+
65
+ | | v1 | v2 |
66
+ |---|---:|---:|
67
+ | Antibody +/− correct | 94.1% | 93.8% |
68
+ | Nostril attribute correct | 96.1% | 95.7% |
69
+ | Both correct | 90.2% | 89.5% |
70
+ | Antibody − on this set (v1): TP / FP / FN, precision, recall, F1 | 23 / 8 / 63, 74.2%, 26.7%, 39.3% | |
71
+ | Antibody − on this set (v2): TP / FP / FN, precision, recall, F1 | 34 / 22 / 52, 60.7%, 39.5%, 47.9% | |
72
+
73
+ | Image | matched by both | v1 antibody acc | v2 antibody acc | v1 Nostril acc | v2 Nostril acc |
74
+ |---|---:|---:|---:|---:|---:|
75
+ | C100 | 144 | 99.3% | 97.2% | 98.6% | 97.9% |
76
+ | C102 | 83 | 100.0% | 100.0% | 90.4% | 89.2% |
77
+ | C110 | 144 | 100.0% | 98.6% | 91.7% | 93.8% |
78
+ | C112 | 113 | 96.5% | 94.7% | 98.2% | 97.3% |
79
+ | F100 | 133 | 98.5% | 97.7% | 97.0% | 95.5% |
80
+ | F110 | 94 | 95.7% | 98.9% | 94.7% | 91.5% |
81
+ | F113 | 99 | 100.0% | 100.0% | 97.0% | 97.0% |
82
+ | F114 | 176 | 93.2% | 91.5% | 94.9% | 94.9% |
83
+ | F99 | 208 | 76.9% | 79.3% | 99.5% | 99.5% |
84
+
85
+ ## Counts summed over the nine held-out images (and summed absolute per-image count error)
86
+
87
+ | Label | GT | v1 | v2 | abs error v1 | abs error v2 |
88
+ |---|---:|---:|---:|---:|---:|
89
+ | Cell, Antibody + | 1061 | 1179 | 1292 | 180 | 239 |
90
+ | Cell, Antibody - | 105 | 39 | 91 | 74 | 62 |
91
+ | Cell, Nostril, Antibody + | 398 | 370 | 388 | 56 | 60 |
92
+ | Cell, Nostril, Antibody - | 5 | 0 | 0 | 5 | 5 |
93
+ | Nostril in Nucleus | 525 | 529 | 532 | 60 | 65 |
94
+ | Nostril External of Nucleus | 532 | 603 | 641 | 187 | 205 |
reports/cv_v2/cv_report.md ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Leave-one-image-out cross-validation
2
+
3
+ 9 folds over 9 annotated images (1569 nucleus and 1057 punctum outlines); each image scored by a model whose checkpoint records that image as held out. Iterations per fold: 3000.
4
+
5
+ ## Micro-averaged per-class scores (base run, no TTA)
6
+
7
+ | Label | GT | Pred | TP | FP | FN | Precision | Recall | F1 |
8
+ |---|---:|---:|---:|---:|---:|---:|---:|---:|
9
+ | Cell, Antibody + | 1061 | 1292 | 875 | 417 | 186 | 67.7% | 82.5% | 74.4% |
10
+ | Cell, Antibody - | 105 | 91 | 37 | 54 | 68 | 40.7% | 35.2% | 37.8% |
11
+ | Cell, Nostril, Antibody + | 398 | 388 | 286 | 102 | 112 | 73.7% | 71.9% | 72.8% |
12
+ | Cell, Nostril, Antibody - | 5 | 0 | 0 | 0 | 5 | 0.0% | 0.0% | 0.0% |
13
+ | Nostril in Nucleus | 525 | 532 | 416 | 116 | 109 | 78.2% | 79.2% | 78.7% |
14
+ | Nostril External of Nucleus | 532 | 641 | 421 | 220 | 111 | 65.7% | 79.1% | 71.8% |
15
+
16
+ Class-agnostic detection (IoU >= 0.5 for nuclei; centroid distance for puncta):
17
+
18
+ - base: nuclei P 75.8% R 85.5% F1 80.4% (IoU≥0.5); F1 87.7% (IoU≥0.3); F1 88.8% (centroid-in-nucleus) | puncta P 76.4% R 84.8% F1 80.4%
19
+ - tta4: nuclei P 75.8% R 85.6% F1 80.4% (IoU≥0.5); F1 87.5% (IoU≥0.3); F1 89.0% (centroid-in-nucleus) | puncta P 77.8% R 85.7% F1 81.6%
20
+ - tta8: nuclei P 75.9% R 85.7% F1 80.5% (IoU≥0.5); F1 87.7% (IoU≥0.3); F1 89.4% (centroid-in-nucleus) | puncta P 78.5% R 85.4% F1 81.8%
21
+
22
+ ## Per-image results (each image held out of training)
23
+
24
+ | Image | Nuclei F1 (IoU≥0.5) | F1 (IoU≥0.3) | F1 (centroid) | mean IoU | Antibody acc | Nostril-attr acc | Puncta F1 | Puncta type acc | Deterministic |
25
+ |---|---:|---:|---:|---:|---:|---:|---:|---:|---|
26
+ | C100 | 84.4% | 91.3% | 91.3% | 0.784 | 96.9% | 98.1% | 71.4% | 100.0% | yes |
27
+ | C102 | 86.4% | 90.5% | 90.5% | 0.811 | 100.0% | 89.5% | 78.9% | 90.8% | yes |
28
+ | C110 | 77.4% | 85.4% | 88.3% | 0.747 | 98.7% | 94.2% | 83.8% | 93.9% | yes |
29
+ | C112 | 71.6% | 81.1% | 83.4% | 0.723 | 95.0% | 97.5% | 76.9% | 92.0% | yes |
30
+ | F100 | 82.9% | 89.6% | 89.6% | 0.742 | 98.1% | 94.4% | 85.7% | 97.7% | yes |
31
+ | F110 | 77.0% | 82.0% | 86.9% | 0.776 | 97.2% | 90.8% | 82.9% | 90.2% | yes |
32
+ | F113 | 69.9% | 84.9% | 86.1% | 0.717 | 100.0% | 97.4% | 83.3% | 88.0% | yes |
33
+ | F114 | 82.4% | 89.4% | 89.4% | 0.794 | 90.1% | 94.1% | 63.5% | 95.0% | yes |
34
+ | F99 | 87.5% | 91.7% | 91.7% | 0.791 | 79.3% | 99.6% | 74.3% | 92.3% | yes |
35
+
36
+ ## Repeated tests per image (8 rotations/flips + 6 photometric perturbations = 14 runs)
37
+
38
+ | Image | Nuclei F1 mean ± sd [min, max] | Attr acc mean ± sd | Puncta F1 mean ± sd [min, max] |
39
+ |---|---|---|---|
40
+ | C100 | 85.5 ± 1.5 [83.2, 89.6] | 96.7 ± 1.1 | 72.1 ± 2.7 [68.2, 78.0] |
41
+ | C102 | 86.6 ± 1.2 [84.6, 88.6] | 90.3 ± 2.1 | 78.8 ± 1.7 [75.7, 81.5] |
42
+ | C110 | 78.4 ± 1.5 [75.5, 80.7] | 92.1 ± 1.1 | 84.2 ± 0.7 [82.7, 85.4] |
43
+ | C112 | 71.0 ± 1.4 [68.1, 73.5] | 93.9 ± 1.6 | 75.3 ± 1.7 [72.5, 78.1] |
44
+ | F100 | 78.8 ± 2.8 [72.8, 82.9] | 92.3 ± 2.6 | 84.5 ± 2.1 [78.7, 87.1] |
45
+ | F110 | 71.7 ± 2.5 [66.7, 77.0] | 89.9 ± 2.4 | 81.8 ± 3.9 [73.6, 84.7] |
46
+ | F113 | 70.6 ± 1.5 [67.7, 73.4] | 95.6 ± 1.6 | 81.7 ± 3.9 [72.7, 87.1] |
47
+ | F114 | 81.8 ± 0.8 [80.2, 82.8] | 86.3 ± 1.6 | 66.4 ± 3.1 [62.6, 72.8] |
48
+ | F99 | 87.8 ± 0.7 [86.3, 89.2] | 77.4 ± 1.7 | 70.8 ± 11.0 [53.3, 85.0] |
49
+
50
+ ## Why nuclei F1 is below 100 %: unmatched predictions and misses (base run)
51
+
52
+ | Image | FP total | FP overlapping a GT nucleus | FP with no GT overlap | FP / matched median blue | FP / matched median area | FN total | FN covered by a prediction |
53
+ |---|---:|---:|---:|---|---|---:|---:|
54
+ | C100 | 41 | 31 | 10 | 44 / 79 | 247 / 624 | 18 | 16 |
55
+ | C102 | 22 | 7 | 15 | 22 / 53 | 371 / 803 | 5 | 4 |
56
+ | C110 | 63 | 38 | 25 | 17 / 42 | 371 / 628 | 28 | 23 |
57
+ | C112 | 71 | 50 | 21 | 21 / 26 | 400 / 701 | 25 | 23 |
58
+ | F100 | 40 | 22 | 18 | 15 / 34 | 574 / 790 | 26 | 24 |
59
+ | F110 | 46 | 39 | 7 | 43 / 52 | 387 / 704 | 19 | 11 |
60
+ | F113 | 53 | 32 | 21 | 9 / 22 | 594 / 757 | 47 | 45 |
61
+ | F114 | 46 | 29 | 17 | 71 / 89 | 514 / 632 | 40 | 37 |
62
+ | F99 | 47 | 38 | 9 | 44 / 53 | 446 / 596 | 19 | 15 |
63
+
64
+ ## Counts per image (ground truth / predicted, base run)
65
+
66
+ | Image | Cell, Antibody + | Cell, Antibody - | Cell, Nostril, Antibody + | Cell, Nostril, Antibody - | Nostril in Nucleus | Nostril External of Nucleus |
67
+ |---|---|---|---|---|---|---|
68
+ | C100 | 165 / 177 | 0 / 13 | 13 / 11 | 0 / 0 | 14 / 15 | 6 / 7 |
69
+ | C102 | 41 / 63 | 0 / 1 | 50 / 44 | 0 / 0 | 60 / 66 | 74 / 104 |
70
+ | C110 | 61 / 91 | 1 / 3 | 122 / 125 | 0 / 0 | 169 / 176 | 207 / 267 |
71
+ | C112 | 124 / 164 | 5 / 3 | 17 / 25 | 0 / 0 | 23 / 29 | 2 / 11 |
72
+ | F100 | 87 / 105 | 6 / 14 | 93 / 81 | 0 / 0 | 118 / 119 | 44 / 27 |
73
+ | F110 | 87 / 125 | 7 / 7 | 33 / 23 | 1 / 0 | 57 / 35 | 137 / 116 |
74
+ | F113 | 138 / 148 | 3 / 2 | 22 / 19 | 0 / 0 | 26 / 21 | 8 / 5 |
75
+ | F114 | 175 / 171 | 21 / 21 | 42 / 56 | 4 / 0 | 52 / 67 | 38 / 95 |
76
+ | F99 | 183 / 248 | 62 / 27 | 6 / 4 | 0 / 0 | 6 / 4 | 16 / 9 |
77
+
78
+ ## Post-processing rule variants (micro F1 across held-out images; accuracies pooled over matched objects)
79
+
80
+ | Variant | Cell, Antibody + | Cell, Antibody - | Cell, Nostril, Antibody + | Cell, Nostril, Antibody - | Nostril in Nucleus | Nostril External of Nucleus | attr acc (matched nuclei) | puncta type acc (matched puncta) |
81
+ |---|---|---|---|---|---|---|---|---|
82
+ | rule_nos=head_cell=head | 74.4% | 37.8% | 68.8% | 0.0% | 78.7% | 71.8% | 88.0% | 93.4% |
83
+ | rule_nos=head_cell=contains | 74.4% | 37.8% | 72.8% | 0.0% | 78.7% | 71.8% | 89.3% | 93.4% |
84
+ | rule_nos=head_cell=either | 74.5% | 37.8% | 69.1% | 0.0% | 78.7% | 71.8% | 88.2% | 93.4% |
85
+ | rule_nos=geometric_cell=head | 74.4% | 37.8% | 68.8% | 0.0% | 79.0% | 71.6% | 88.0% | 93.4% |
86
+ | rule_nos=geometric_cell=contains | 74.4% | 37.8% | 72.9% | 0.0% | 79.0% | 71.6% | 89.3% | 93.4% |
87
+ | rule_nos=geometric_cell=either | 74.5% | 37.8% | 69.2% | 0.0% | 79.0% | 71.6% | 88.2% | 93.4% |
88
+
89
+ ## Antibody threshold sweep on the 1342 matched held-out nuclei (98 antibody-negative)
90
+
91
+ | threshold | Antibody − precision | Antibody − recall | Antibody − F1 | overall accuracy |
92
+ |---:|---:|---:|---:|---:|
93
+ | 0.30 | 60.7% | 34.7% | 44.2% | 93.6% |
94
+ | 0.35 | 61.4% | 35.7% | 45.2% | 93.7% |
95
+ | 0.40 | 61.7% | 37.8% | 46.8% | 93.7% |
96
+ | 0.45 | 59.7% | 37.8% | 46.2% | 93.6% |
97
+ | 0.50 | 58.7% | 37.8% | 46.0% | 93.5% |
98
+ | 0.55 | 58.5% | 38.8% | 46.6% | 93.5% |
99
+ | 0.60 | 57.6% | 38.8% | 46.3% | 93.4% |
100
+ | 0.65 | 56.5% | 39.8% | 46.7% | 93.4% |
101
+ | 0.70 | 54.8% | 40.8% | 46.8% | 93.2% |
102
+ | 0.75 | 51.9% | 41.8% | 46.3% | 92.9% |
103
+ | 0.80 | 51.9% | 42.9% | 46.9% | 92.9% |
104
+ | 0.85 | 52.8% | 48.0% | 50.3% | 93.1% |
105
+ | 0.90 | 51.6% | 50.0% | 50.8% | 92.9% |
reports/cv_v2/cv_results.json ADDED
The diff for this file is too large to render. See raw diff
 
reports/cv_v2/run_all.sh ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Train the nine v2 folds (two at a time), then score every held-out image.
3
+ cd "$(dirname "$0")/../.."
4
+ Q=runs/cv_v2/train_queue.sh
5
+ bash "$Q" C100 C102 C110 C112 F100 F110 F113 F114 F99 > runs/cv_v2/queue_A.log 2>&1 &
6
+ bash "$Q" F99 F114 F113 F110 F100 C112 C110 C102 C100 > runs/cv_v2/queue_B.log 2>&1 &
7
+ wait
8
+ echo "=== training done $(date) ==="
9
+ python3 -I evaluate.py cv --data-dir /Users/slf20757/Desktop/Peyton --runs-dir runs/cv_v2 --iters 3000 --device mps > runs/cv_v2/eval.log 2>&1
10
+ echo "=== eval done $(date) exit $? ==="
11
+ echo ALL_DONE
reports/cv_v2/train_queue.sh ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Lock-based fold queue for the v2 recipe: several instances may run concurrently with different orders.
3
+ cd "$(dirname "$0")/../.."
4
+ for K in "$@"; do
5
+ [ -f "runs/cv_v2/fold_$K.pt" ] && { echo "skip $K (done)"; continue; }
6
+ mkdir "runs/cv_v2/fold_$K.lock" 2>/dev/null || { echo "skip $K (claimed)"; continue; }
7
+ echo "=== fold $K start $(date) ==="
8
+ python3 -I train.py --data-dir /Users/slf20757/Desktop/Peyton --holdout "$K" --iters 3000 --val-every 500 --out "runs/cv_v2/fold_$K.pt" --device mps > "runs/cv_v2/train_$K.log" 2>&1 || echo "FOLD $K FAILED"
9
+ echo "=== fold $K done $(date) ==="
10
+ done
11
+ echo QUEUE_DONE
reports/final/final_fit.json CHANGED
@@ -2,56 +2,56 @@
2
  "C100": {
3
  "cells": {
4
  "detection": {
5
- "tp": 153,
6
- "fp": 22,
7
- "fn": 25,
8
- "precision": 0.8742857142857143,
9
- "recall": 0.8595505617977528,
10
- "f1": 0.8668555240793201,
11
- "mean_iou_matched": 0.7974392864551416,
12
  "iou30": {
13
- "tp": 165,
14
- "fp": 10,
15
- "fn": 13,
16
- "precision": 0.9428571428571428,
17
- "recall": 0.9269662921348315,
18
- "f1": 0.934844192634561
19
  },
20
  "centroid": {
21
- "tp": 165,
22
- "fp": 10,
23
- "fn": 13,
24
- "precision": 0.9428571428571428,
25
- "recall": 0.9269662921348315,
26
- "f1": 0.934844192634561
27
  }
28
  },
29
  "attribute_accuracy": {
30
- "antibody": 0.9934640522875817,
31
- "nostril": 0.9934640522875817,
32
- "both": 0.9869281045751634,
33
- "n_matched": 153
34
  },
35
  "per_class": {
36
  "Cell, Antibody +": {
37
- "tp": 139,
38
- "fp": 22,
39
- "fn": 26,
40
- "precision": 0.8633540372670807,
41
- "recall": 0.8424242424242424,
42
- "f1": 0.852760736196319,
43
  "gt_count": 165,
44
- "pred_count": 161
45
  },
46
  "Cell, Antibody -": {
47
  "tp": 0,
48
- "fp": 2,
49
  "fn": 0,
50
  "precision": 0.0,
51
  "recall": 0.0,
52
  "f1": 0.0,
53
  "gt_count": 0,
54
- "pred_count": 2
55
  },
56
  "Cell, Nostril, Antibody +": {
57
  "tp": 12,
@@ -76,8 +76,8 @@
76
  },
77
  "confusion": {
78
  "Cell, Antibody +": {
79
- "Cell, Antibody +": 139,
80
- "Cell, Antibody -": 1,
81
  "Cell, Nostril, Antibody +": 0,
82
  "Cell, Nostril, Antibody -": 0
83
  },
@@ -101,29 +101,29 @@
101
  }
102
  },
103
  "n_gt": 178,
104
- "n_pred": 175
105
  },
106
  "nostrils": {
107
  "detection": {
108
  "tp": 16,
109
- "fp": 3,
110
  "fn": 4,
111
- "precision": 0.8421052631578947,
112
  "recall": 0.8,
113
- "f1": 0.8205128205128205,
114
- "mean_dist_matched": 1.157546253191996
115
  },
116
  "type_accuracy": 1.0,
117
  "per_class": {
118
  "Nostril in Nucleus": {
119
  "tp": 12,
120
- "fp": 0,
121
  "fn": 2,
122
- "precision": 1.0,
123
  "recall": 0.8571428571428571,
124
- "f1": 0.923076923076923,
125
  "gt_count": 14,
126
- "pred_count": 12
127
  },
128
  "Nostril External of Nucleus": {
129
  "tp": 4,
@@ -137,19 +137,19 @@
137
  }
138
  },
139
  "n_gt": 20,
140
- "n_pred": 19
141
  },
142
  "counts": {
143
  "Cell, Antibody +": {
144
  "gt": 165,
145
- "pred": 161,
146
- "abs_err": 4,
147
- "rel_err": 0.024242424242424242
148
  },
149
  "Cell, Antibody -": {
150
  "gt": 0,
151
- "pred": 2,
152
- "abs_err": 2,
153
  "rel_err": Infinity
154
  },
155
  "Cell, Nostril, Antibody +": {
@@ -166,9 +166,9 @@
166
  },
167
  "Nostril in Nucleus": {
168
  "gt": 14,
169
- "pred": 12,
170
- "abs_err": 2,
171
- "rel_err": 0.14285714285714285
172
  },
173
  "Nostril External of Nucleus": {
174
  "gt": 6,
@@ -180,90 +180,90 @@
180
  "fp_analysis": null,
181
  "pairs": null,
182
  "summary": {
183
- "cell_det_f1": 0.8668555240793201,
184
- "cell_det_precision": 0.8742857142857143,
185
- "cell_det_recall": 0.8595505617977528,
186
- "cell_mean_iou": 0.7974392864551416,
187
- "cell_det_f1_iou30": 0.934844192634561,
188
- "cell_det_f1_centroid": 0.934844192634561,
189
- "cell_attr_antibody": 0.9934640522875817,
190
- "cell_attr_nostril": 0.9934640522875817,
191
- "cell_attr_both": 0.9869281045751634,
192
- "nos_det_f1": 0.8205128205128205,
193
- "nos_det_precision": 0.8421052631578947,
194
  "nos_det_recall": 0.8,
195
  "nos_type_acc": 1.0,
196
- "f1[Cell, Antibody +]": 0.852760736196319,
197
  "f1[Cell, Antibody -]": 0.0,
198
  "f1[Cell, Nostril, Antibody +]": 0.9600000000000001,
199
  "f1[Cell, Nostril, Antibody -]": 0.0,
200
- "f1[Nostril in Nucleus]": 0.923076923076923,
201
  "f1[Nostril External of Nucleus]": 0.6153846153846153
202
  }
203
  },
204
  "C102": {
205
  "cells": {
206
  "detection": {
207
- "tp": 90,
208
- "fp": 12,
209
- "fn": 1,
210
- "precision": 0.8823529411764706,
211
- "recall": 0.989010989010989,
212
- "f1": 0.9326424870466321,
213
- "mean_iou_matched": 0.8122454665114849,
214
  "iou30": {
215
- "tp": 90,
216
- "fp": 12,
217
- "fn": 1,
218
- "precision": 0.8823529411764706,
219
- "recall": 0.989010989010989,
220
- "f1": 0.9326424870466321
221
  },
222
  "centroid": {
223
- "tp": 90,
224
- "fp": 12,
225
- "fn": 1,
226
- "precision": 0.8823529411764706,
227
- "recall": 0.989010989010989,
228
- "f1": 0.9326424870466321
229
  }
230
  },
231
  "attribute_accuracy": {
232
  "antibody": 1.0,
233
- "nostril": 0.9444444444444444,
234
- "both": 0.9444444444444444,
235
- "n_matched": 90
236
  },
237
  "per_class": {
238
  "Cell, Antibody +": {
239
  "tp": 41,
240
- "fp": 15,
241
  "fn": 0,
242
- "precision": 0.7321428571428571,
243
  "recall": 1.0,
244
- "f1": 0.8453608247422679,
245
  "gt_count": 41,
246
- "pred_count": 56
247
  },
248
  "Cell, Antibody -": {
249
  "tp": 0,
250
- "fp": 0,
251
  "fn": 0,
252
  "precision": 0.0,
253
  "recall": 0.0,
254
  "f1": 0.0,
255
  "gt_count": 0,
256
- "pred_count": 0
257
  },
258
  "Cell, Nostril, Antibody +": {
259
- "tp": 44,
260
- "fp": 2,
261
- "fn": 6,
262
- "precision": 0.9565217391304348,
263
- "recall": 0.88,
264
- "f1": 0.9166666666666666,
265
  "gt_count": 50,
266
- "pred_count": 46
267
  },
268
  "Cell, Nostril, Antibody -": {
269
  "tp": 0,
@@ -292,7 +292,7 @@
292
  "Cell, Nostril, Antibody +": {
293
  "Cell, Antibody +": 5,
294
  "Cell, Antibody -": 0,
295
- "Cell, Nostril, Antibody +": 44,
296
  "Cell, Nostril, Antibody -": 0
297
  },
298
  "Cell, Nostril, Antibody -": {
@@ -303,62 +303,62 @@
303
  }
304
  },
305
  "n_gt": 91,
306
- "n_pred": 102
307
  },
308
  "nostrils": {
309
  "detection": {
310
- "tp": 121,
311
- "fp": 31,
312
- "fn": 13,
313
- "precision": 0.7960526315789473,
314
- "recall": 0.9029850746268657,
315
- "f1": 0.8461538461538461,
316
- "mean_dist_matched": 1.0261222516695998
317
- },
318
- "type_accuracy": 1.0,
319
  "per_class": {
320
  "Nostril in Nucleus": {
321
  "tp": 54,
322
- "fp": 8,
323
  "fn": 6,
324
- "precision": 0.8709677419354839,
325
  "recall": 0.9,
326
- "f1": 0.8852459016393444,
327
  "gt_count": 60,
328
- "pred_count": 62
329
  },
330
  "Nostril External of Nucleus": {
331
- "tp": 67,
332
- "fp": 23,
333
- "fn": 7,
334
- "precision": 0.7444444444444445,
335
- "recall": 0.9054054054054054,
336
- "f1": 0.8170731707317074,
337
  "gt_count": 74,
338
- "pred_count": 90
339
  }
340
  },
341
  "n_gt": 134,
342
- "n_pred": 152
343
  },
344
  "counts": {
345
  "Cell, Antibody +": {
346
  "gt": 41,
347
- "pred": 56,
348
- "abs_err": 15,
349
- "rel_err": 0.36585365853658536
350
  },
351
  "Cell, Antibody -": {
352
  "gt": 0,
353
- "pred": 0,
354
- "abs_err": 0,
355
- "rel_err": 0.0
356
  },
357
  "Cell, Nostril, Antibody +": {
358
  "gt": 50,
359
- "pred": 46,
360
- "abs_err": 4,
361
- "rel_err": 0.08
362
  },
363
  "Cell, Nostril, Antibody -": {
364
  "gt": 0,
@@ -368,104 +368,104 @@
368
  },
369
  "Nostril in Nucleus": {
370
  "gt": 60,
371
- "pred": 62,
372
- "abs_err": 2,
373
- "rel_err": 0.03333333333333333
374
  },
375
  "Nostril External of Nucleus": {
376
  "gt": 74,
377
- "pred": 90,
378
- "abs_err": 16,
379
- "rel_err": 0.21621621621621623
380
  }
381
  },
382
  "fp_analysis": null,
383
  "pairs": null,
384
  "summary": {
385
- "cell_det_f1": 0.9326424870466321,
386
- "cell_det_precision": 0.8823529411764706,
387
- "cell_det_recall": 0.989010989010989,
388
- "cell_mean_iou": 0.8122454665114849,
389
- "cell_det_f1_iou30": 0.9326424870466321,
390
- "cell_det_f1_centroid": 0.9326424870466321,
391
  "cell_attr_antibody": 1.0,
392
- "cell_attr_nostril": 0.9444444444444444,
393
- "cell_attr_both": 0.9444444444444444,
394
- "nos_det_f1": 0.8461538461538461,
395
- "nos_det_precision": 0.7960526315789473,
396
- "nos_det_recall": 0.9029850746268657,
397
- "nos_type_acc": 1.0,
398
- "f1[Cell, Antibody +]": 0.8453608247422679,
399
  "f1[Cell, Antibody -]": 0.0,
400
- "f1[Cell, Nostril, Antibody +]": 0.9166666666666666,
401
  "f1[Cell, Nostril, Antibody -]": 0.0,
402
- "f1[Nostril in Nucleus]": 0.8852459016393444,
403
- "f1[Nostril External of Nucleus]": 0.8170731707317074
404
  }
405
  },
406
  "C110": {
407
  "cells": {
408
  "detection": {
409
- "tp": 164,
410
- "fp": 22,
411
- "fn": 20,
412
- "precision": 0.8817204301075269,
413
- "recall": 0.8913043478260869,
414
- "f1": 0.8864864864864864,
415
- "mean_iou_matched": 0.800839882168248,
416
  "iou30": {
417
- "tp": 173,
418
- "fp": 13,
419
- "fn": 11,
420
- "precision": 0.9301075268817204,
421
- "recall": 0.9402173913043478,
422
- "f1": 0.9351351351351351
423
  },
424
  "centroid": {
425
- "tp": 172,
426
- "fp": 14,
427
- "fn": 12,
428
- "precision": 0.9247311827956989,
429
- "recall": 0.9347826086956522,
430
- "f1": 0.9297297297297297
431
  }
432
  },
433
  "attribute_accuracy": {
434
- "antibody": 1.0,
435
- "nostril": 0.9573170731707317,
436
- "both": 0.9573170731707317,
437
- "n_matched": 164
438
  },
439
  "per_class": {
440
  "Cell, Antibody +": {
441
- "tp": 54,
442
- "fp": 19,
443
- "fn": 7,
444
- "precision": 0.7397260273972602,
445
- "recall": 0.8852459016393442,
446
- "f1": 0.8059701492537313,
447
  "gt_count": 61,
448
- "pred_count": 73
449
  },
450
  "Cell, Antibody -": {
451
  "tp": 1,
452
- "fp": 0,
453
  "fn": 0,
454
- "precision": 1.0,
455
  "recall": 1.0,
456
- "f1": 1.0,
457
  "gt_count": 1,
458
- "pred_count": 1
459
  },
460
  "Cell, Nostril, Antibody +": {
461
- "tp": 102,
462
- "fp": 10,
463
- "fn": 20,
464
- "precision": 0.9107142857142857,
465
- "recall": 0.8360655737704918,
466
- "f1": 0.8717948717948717,
467
  "gt_count": 122,
468
- "pred_count": 112
469
  },
470
  "Cell, Nostril, Antibody -": {
471
  "tp": 0,
@@ -480,9 +480,9 @@
480
  },
481
  "confusion": {
482
  "Cell, Antibody +": {
483
- "Cell, Antibody +": 54,
484
- "Cell, Antibody -": 0,
485
- "Cell, Nostril, Antibody +": 3,
486
  "Cell, Nostril, Antibody -": 0
487
  },
488
  "Cell, Antibody -": {
@@ -492,9 +492,9 @@
492
  "Cell, Nostril, Antibody -": 0
493
  },
494
  "Cell, Nostril, Antibody +": {
495
- "Cell, Antibody +": 4,
496
  "Cell, Antibody -": 0,
497
- "Cell, Nostril, Antibody +": 102,
498
  "Cell, Nostril, Antibody -": 0
499
  },
500
  "Cell, Nostril, Antibody -": {
@@ -505,62 +505,62 @@
505
  }
506
  },
507
  "n_gt": 184,
508
- "n_pred": 186
509
  },
510
  "nostrils": {
511
  "detection": {
512
  "tp": 347,
513
- "fp": 93,
514
  "fn": 29,
515
- "precision": 0.7886363636363637,
516
  "recall": 0.9228723404255319,
517
- "f1": 0.8504901960784313,
518
- "mean_dist_matched": 0.8862671910950328
519
  },
520
- "type_accuracy": 0.9798270893371758,
521
  "per_class": {
522
  "Nostril in Nucleus": {
523
- "tp": 148,
524
- "fp": 34,
525
- "fn": 21,
526
- "precision": 0.8131868131868132,
527
- "recall": 0.8757396449704142,
528
- "f1": 0.8433048433048432,
529
  "gt_count": 169,
530
- "pred_count": 182
531
  },
532
  "Nostril External of Nucleus": {
533
- "tp": 192,
534
- "fp": 66,
535
- "fn": 15,
536
- "precision": 0.7441860465116279,
537
- "recall": 0.927536231884058,
538
- "f1": 0.8258064516129032,
539
  "gt_count": 207,
540
- "pred_count": 258
541
  }
542
  },
543
  "n_gt": 376,
544
- "n_pred": 440
545
  },
546
  "counts": {
547
  "Cell, Antibody +": {
548
  "gt": 61,
549
- "pred": 73,
550
- "abs_err": 12,
551
- "rel_err": 0.19672131147540983
552
  },
553
  "Cell, Antibody -": {
554
  "gt": 1,
555
- "pred": 1,
556
- "abs_err": 0,
557
- "rel_err": 0.0
558
  },
559
  "Cell, Nostril, Antibody +": {
560
  "gt": 122,
561
- "pred": 112,
562
- "abs_err": 10,
563
- "rel_err": 0.08196721311475409
564
  },
565
  "Cell, Nostril, Antibody -": {
566
  "gt": 0,
@@ -570,104 +570,104 @@
570
  },
571
  "Nostril in Nucleus": {
572
  "gt": 169,
573
- "pred": 182,
574
- "abs_err": 13,
575
- "rel_err": 0.07692307692307693
576
  },
577
  "Nostril External of Nucleus": {
578
  "gt": 207,
579
- "pred": 258,
580
- "abs_err": 51,
581
- "rel_err": 0.2463768115942029
582
  }
583
  },
584
  "fp_analysis": null,
585
  "pairs": null,
586
  "summary": {
587
- "cell_det_f1": 0.8864864864864864,
588
- "cell_det_precision": 0.8817204301075269,
589
- "cell_det_recall": 0.8913043478260869,
590
- "cell_mean_iou": 0.800839882168248,
591
- "cell_det_f1_iou30": 0.9351351351351351,
592
- "cell_det_f1_centroid": 0.9297297297297297,
593
- "cell_attr_antibody": 1.0,
594
- "cell_attr_nostril": 0.9573170731707317,
595
- "cell_attr_both": 0.9573170731707317,
596
- "nos_det_f1": 0.8504901960784313,
597
- "nos_det_precision": 0.7886363636363637,
598
  "nos_det_recall": 0.9228723404255319,
599
- "nos_type_acc": 0.9798270893371758,
600
- "f1[Cell, Antibody +]": 0.8059701492537313,
601
- "f1[Cell, Antibody -]": 1.0,
602
- "f1[Cell, Nostril, Antibody +]": 0.8717948717948717,
603
  "f1[Cell, Nostril, Antibody -]": 0.0,
604
- "f1[Nostril in Nucleus]": 0.8433048433048432,
605
- "f1[Nostril External of Nucleus]": 0.8258064516129032
606
  }
607
  },
608
  "C112": {
609
  "cells": {
610
  "detection": {
611
- "tp": 124,
612
- "fp": 26,
613
- "fn": 22,
614
- "precision": 0.8266666666666667,
615
- "recall": 0.8493150684931506,
616
- "f1": 0.8378378378378378,
617
- "mean_iou_matched": 0.7526341825198912,
618
  "iou30": {
619
- "tp": 134,
620
- "fp": 16,
621
- "fn": 12,
622
- "precision": 0.8933333333333333,
623
- "recall": 0.9178082191780822,
624
- "f1": 0.9054054054054055
625
  },
626
  "centroid": {
627
- "tp": 133,
628
- "fp": 17,
629
- "fn": 13,
630
- "precision": 0.8866666666666667,
631
- "recall": 0.910958904109589,
632
- "f1": 0.8986486486486487
633
  }
634
  },
635
  "attribute_accuracy": {
636
- "antibody": 0.9919354838709677,
637
- "nostril": 1.0,
638
- "both": 0.9919354838709677,
639
- "n_matched": 124
640
  },
641
  "per_class": {
642
  "Cell, Antibody +": {
643
- "tp": 104,
644
- "fp": 22,
645
- "fn": 20,
646
- "precision": 0.8253968253968254,
647
- "recall": 0.8387096774193549,
648
- "f1": 0.832,
649
  "gt_count": 124,
650
- "pred_count": 126
651
  },
652
  "Cell, Antibody -": {
653
- "tp": 4,
654
  "fp": 1,
655
- "fn": 1,
656
- "precision": 0.8,
657
- "recall": 0.8,
658
- "f1": 0.8000000000000002,
659
  "gt_count": 5,
660
- "pred_count": 5
661
  },
662
  "Cell, Nostril, Antibody +": {
663
  "tp": 15,
664
- "fp": 4,
665
  "fn": 2,
666
- "precision": 0.7894736842105263,
667
  "recall": 0.8823529411764706,
668
- "f1": 0.8333333333333333,
669
  "gt_count": 17,
670
- "pred_count": 19
671
  },
672
  "Cell, Nostril, Antibody -": {
673
  "tp": 0,
@@ -682,14 +682,14 @@
682
  },
683
  "confusion": {
684
  "Cell, Antibody +": {
685
- "Cell, Antibody +": 104,
686
  "Cell, Antibody -": 0,
687
- "Cell, Nostril, Antibody +": 0,
688
  "Cell, Nostril, Antibody -": 0
689
  },
690
  "Cell, Antibody -": {
691
- "Cell, Antibody +": 1,
692
- "Cell, Antibody -": 4,
693
  "Cell, Nostril, Antibody +": 0,
694
  "Cell, Nostril, Antibody -": 0
695
  },
@@ -707,62 +707,62 @@
707
  }
708
  },
709
  "n_gt": 146,
710
- "n_pred": 150
711
  },
712
  "nostrils": {
713
  "detection": {
714
  "tp": 25,
715
- "fp": 8,
716
  "fn": 0,
717
- "precision": 0.7575757575757576,
718
  "recall": 1.0,
719
- "f1": 0.8620689655172413,
720
- "mean_dist_matched": 0.934525036066851
721
  },
722
  "type_accuracy": 1.0,
723
  "per_class": {
724
  "Nostril in Nucleus": {
725
  "tp": 23,
726
- "fp": 2,
727
  "fn": 0,
728
- "precision": 0.92,
729
  "recall": 1.0,
730
- "f1": 0.9583333333333334,
731
  "gt_count": 23,
732
- "pred_count": 25
733
  },
734
  "Nostril External of Nucleus": {
735
  "tp": 2,
736
- "fp": 6,
737
  "fn": 0,
738
- "precision": 0.25,
739
  "recall": 1.0,
740
- "f1": 0.4,
741
  "gt_count": 2,
742
- "pred_count": 8
743
  }
744
  },
745
  "n_gt": 25,
746
- "n_pred": 33
747
  },
748
  "counts": {
749
  "Cell, Antibody +": {
750
  "gt": 124,
751
- "pred": 126,
752
- "abs_err": 2,
753
- "rel_err": 0.016129032258064516
754
  },
755
  "Cell, Antibody -": {
756
  "gt": 5,
757
- "pred": 5,
758
- "abs_err": 0,
759
- "rel_err": 0.0
760
  },
761
  "Cell, Nostril, Antibody +": {
762
  "gt": 17,
763
- "pred": 19,
764
- "abs_err": 2,
765
- "rel_err": 0.11764705882352941
766
  },
767
  "Cell, Nostril, Antibody -": {
768
  "gt": 0,
@@ -772,104 +772,104 @@
772
  },
773
  "Nostril in Nucleus": {
774
  "gt": 23,
775
- "pred": 25,
776
- "abs_err": 2,
777
- "rel_err": 0.08695652173913043
778
  },
779
  "Nostril External of Nucleus": {
780
  "gt": 2,
781
- "pred": 8,
782
- "abs_err": 6,
783
- "rel_err": 3.0
784
  }
785
  },
786
  "fp_analysis": null,
787
  "pairs": null,
788
  "summary": {
789
- "cell_det_f1": 0.8378378378378378,
790
- "cell_det_precision": 0.8266666666666667,
791
- "cell_det_recall": 0.8493150684931506,
792
- "cell_mean_iou": 0.7526341825198912,
793
- "cell_det_f1_iou30": 0.9054054054054055,
794
- "cell_det_f1_centroid": 0.8986486486486487,
795
- "cell_attr_antibody": 0.9919354838709677,
796
- "cell_attr_nostril": 1.0,
797
- "cell_attr_both": 0.9919354838709677,
798
- "nos_det_f1": 0.8620689655172413,
799
- "nos_det_precision": 0.7575757575757576,
800
  "nos_det_recall": 1.0,
801
  "nos_type_acc": 1.0,
802
- "f1[Cell, Antibody +]": 0.832,
803
- "f1[Cell, Antibody -]": 0.8000000000000002,
804
- "f1[Cell, Nostril, Antibody +]": 0.8333333333333333,
805
  "f1[Cell, Nostril, Antibody -]": 0.0,
806
- "f1[Nostril in Nucleus]": 0.9583333333333334,
807
- "f1[Nostril External of Nucleus]": 0.4
808
  }
809
  },
810
  "F100": {
811
  "cells": {
812
  "detection": {
813
- "tp": 160,
814
- "fp": 21,
815
- "fn": 26,
816
- "precision": 0.8839779005524862,
817
- "recall": 0.8602150537634409,
818
- "f1": 0.871934604904632,
819
- "mean_iou_matched": 0.8049454720324757,
820
  "iou30": {
821
- "tp": 169,
822
- "fp": 12,
823
- "fn": 17,
824
- "precision": 0.9337016574585635,
825
- "recall": 0.9086021505376344,
826
- "f1": 0.9209809264305177
827
  },
828
  "centroid": {
829
- "tp": 168,
830
- "fp": 13,
831
- "fn": 18,
832
- "precision": 0.9281767955801105,
833
- "recall": 0.9032258064516129,
834
- "f1": 0.9155313351498637
835
  }
836
  },
837
  "attribute_accuracy": {
838
- "antibody": 0.98125,
839
- "nostril": 0.98125,
840
- "both": 0.9625,
841
- "n_matched": 160
842
  },
843
  "per_class": {
844
  "Cell, Antibody +": {
845
- "tp": 79,
846
- "fp": 17,
847
- "fn": 8,
848
- "precision": 0.8229166666666666,
849
- "recall": 0.9080459770114943,
850
- "f1": 0.8633879781420765,
851
  "gt_count": 87,
852
- "pred_count": 96
853
  },
854
  "Cell, Antibody -": {
855
- "tp": 4,
856
- "fp": 2,
857
- "fn": 2,
858
- "precision": 0.6666666666666666,
859
- "recall": 0.6666666666666666,
860
- "f1": 0.6666666666666666,
861
  "gt_count": 6,
862
- "pred_count": 6
863
  },
864
  "Cell, Nostril, Antibody +": {
865
- "tp": 71,
866
- "fp": 8,
867
- "fn": 22,
868
- "precision": 0.8987341772151899,
869
- "recall": 0.7634408602150538,
870
- "f1": 0.8255813953488371,
871
  "gt_count": 93,
872
- "pred_count": 79
873
  },
874
  "Cell, Nostril, Antibody -": {
875
  "tp": 0,
@@ -884,21 +884,21 @@
884
  },
885
  "confusion": {
886
  "Cell, Antibody +": {
887
- "Cell, Antibody +": 79,
888
  "Cell, Antibody -": 1,
889
- "Cell, Nostril, Antibody +": 0,
890
  "Cell, Nostril, Antibody -": 0
891
  },
892
  "Cell, Antibody -": {
893
- "Cell, Antibody +": 2,
894
- "Cell, Antibody -": 4,
895
  "Cell, Nostril, Antibody +": 0,
896
  "Cell, Nostril, Antibody -": 0
897
  },
898
  "Cell, Nostril, Antibody +": {
899
- "Cell, Antibody +": 3,
900
  "Cell, Antibody -": 0,
901
- "Cell, Nostril, Antibody +": 71,
902
  "Cell, Nostril, Antibody -": 0
903
  },
904
  "Cell, Nostril, Antibody -": {
@@ -909,62 +909,62 @@
909
  }
910
  },
911
  "n_gt": 186,
912
- "n_pred": 181
913
  },
914
  "nostrils": {
915
  "detection": {
916
- "tp": 142,
917
- "fp": 9,
918
- "fn": 20,
919
- "precision": 0.9403973509933775,
920
- "recall": 0.8765432098765432,
921
- "f1": 0.9073482428115016,
922
- "mean_dist_matched": 1.2296582069626907
923
- },
924
- "type_accuracy": 0.9929577464788732,
925
  "per_class": {
926
  "Nostril in Nucleus": {
927
- "tp": 102,
928
- "fp": 8,
929
- "fn": 16,
930
- "precision": 0.9272727272727272,
931
- "recall": 0.864406779661017,
932
- "f1": 0.8947368421052632,
933
  "gt_count": 118,
934
- "pred_count": 110
935
  },
936
  "Nostril External of Nucleus": {
937
- "tp": 39,
938
- "fp": 2,
939
- "fn": 5,
940
- "precision": 0.9512195121951219,
941
- "recall": 0.8863636363636364,
942
- "f1": 0.9176470588235294,
943
  "gt_count": 44,
944
  "pred_count": 41
945
  }
946
  },
947
  "n_gt": 162,
948
- "n_pred": 151
949
  },
950
  "counts": {
951
  "Cell, Antibody +": {
952
  "gt": 87,
953
- "pred": 96,
954
- "abs_err": 9,
955
- "rel_err": 0.10344827586206896
956
  },
957
  "Cell, Antibody -": {
958
  "gt": 6,
959
- "pred": 6,
960
- "abs_err": 0,
961
- "rel_err": 0.0
962
  },
963
  "Cell, Nostril, Antibody +": {
964
  "gt": 93,
965
- "pred": 79,
966
- "abs_err": 14,
967
- "rel_err": 0.15053763440860216
968
  },
969
  "Cell, Nostril, Antibody -": {
970
  "gt": 0,
@@ -974,9 +974,9 @@
974
  },
975
  "Nostril in Nucleus": {
976
  "gt": 118,
977
- "pred": 110,
978
- "abs_err": 8,
979
- "rel_err": 0.06779661016949153
980
  },
981
  "Nostril External of Nucleus": {
982
  "gt": 44,
@@ -988,90 +988,90 @@
988
  "fp_analysis": null,
989
  "pairs": null,
990
  "summary": {
991
- "cell_det_f1": 0.871934604904632,
992
- "cell_det_precision": 0.8839779005524862,
993
- "cell_det_recall": 0.8602150537634409,
994
- "cell_mean_iou": 0.8049454720324757,
995
- "cell_det_f1_iou30": 0.9209809264305177,
996
- "cell_det_f1_centroid": 0.9155313351498637,
997
- "cell_attr_antibody": 0.98125,
998
- "cell_attr_nostril": 0.98125,
999
- "cell_attr_both": 0.9625,
1000
- "nos_det_f1": 0.9073482428115016,
1001
- "nos_det_precision": 0.9403973509933775,
1002
- "nos_det_recall": 0.8765432098765432,
1003
- "nos_type_acc": 0.9929577464788732,
1004
- "f1[Cell, Antibody +]": 0.8633879781420765,
1005
- "f1[Cell, Antibody -]": 0.6666666666666666,
1006
- "f1[Cell, Nostril, Antibody +]": 0.8255813953488371,
1007
  "f1[Cell, Nostril, Antibody -]": 0.0,
1008
- "f1[Nostril in Nucleus]": 0.8947368421052632,
1009
- "f1[Nostril External of Nucleus]": 0.9176470588235294
1010
  }
1011
  },
1012
  "F110": {
1013
  "cells": {
1014
  "detection": {
1015
- "tp": 116,
1016
- "fp": 9,
1017
- "fn": 12,
1018
- "precision": 0.928,
1019
- "recall": 0.90625,
1020
- "f1": 0.9169960474308302,
1021
- "mean_iou_matched": 0.8161455695675424,
1022
  "iou30": {
1023
- "tp": 120,
1024
- "fp": 5,
1025
- "fn": 8,
1026
- "precision": 0.96,
1027
- "recall": 0.9375,
1028
- "f1": 0.9486166007905138
1029
  },
1030
  "centroid": {
1031
- "tp": 121,
1032
- "fp": 4,
1033
- "fn": 7,
1034
- "precision": 0.968,
1035
- "recall": 0.9453125,
1036
- "f1": 0.9565217391304348
1037
  }
1038
  },
1039
  "attribute_accuracy": {
1040
- "antibody": 0.9827586206896551,
1041
- "nostril": 0.9655172413793104,
1042
- "both": 0.9482758620689655,
1043
- "n_matched": 116
1044
  },
1045
  "per_class": {
1046
  "Cell, Antibody +": {
1047
- "tp": 82,
1048
- "fp": 10,
1049
- "fn": 5,
1050
- "precision": 0.8913043478260869,
1051
- "recall": 0.9425287356321839,
1052
- "f1": 0.9162011173184358,
1053
  "gt_count": 87,
1054
- "pred_count": 92
1055
  },
1056
  "Cell, Antibody -": {
1057
- "tp": 5,
1058
- "fp": 0,
1059
- "fn": 2,
1060
- "precision": 1.0,
1061
- "recall": 0.7142857142857143,
1062
- "f1": 0.8333333333333333,
1063
  "gt_count": 7,
1064
- "pred_count": 5
1065
  },
1066
  "Cell, Nostril, Antibody +": {
1067
- "tp": 23,
1068
  "fp": 5,
1069
- "fn": 10,
1070
- "precision": 0.8214285714285714,
1071
- "recall": 0.696969696969697,
1072
- "f1": 0.7540983606557378,
1073
  "gt_count": 33,
1074
- "pred_count": 28
1075
  },
1076
  "Cell, Nostril, Antibody -": {
1077
  "tp": 0,
@@ -1086,21 +1086,21 @@
1086
  },
1087
  "confusion": {
1088
  "Cell, Antibody +": {
1089
- "Cell, Antibody +": 82,
1090
  "Cell, Antibody -": 0,
1091
  "Cell, Nostril, Antibody +": 0,
1092
  "Cell, Nostril, Antibody -": 0
1093
  },
1094
  "Cell, Antibody -": {
1095
- "Cell, Antibody +": 2,
1096
- "Cell, Antibody -": 5,
1097
  "Cell, Nostril, Antibody +": 0,
1098
  "Cell, Nostril, Antibody -": 0
1099
  },
1100
  "Cell, Nostril, Antibody +": {
1101
- "Cell, Antibody +": 4,
1102
  "Cell, Antibody -": 0,
1103
- "Cell, Nostril, Antibody +": 23,
1104
  "Cell, Nostril, Antibody -": 0
1105
  },
1106
  "Cell, Nostril, Antibody -": {
@@ -1111,62 +1111,62 @@
1111
  }
1112
  },
1113
  "n_gt": 128,
1114
- "n_pred": 125
1115
  },
1116
  "nostrils": {
1117
  "detection": {
1118
- "tp": 157,
1119
- "fp": 9,
1120
- "fn": 37,
1121
- "precision": 0.9457831325301205,
1122
- "recall": 0.8092783505154639,
1123
- "f1": 0.8722222222222222,
1124
- "mean_dist_matched": 0.7899268230101212
1125
- },
1126
- "type_accuracy": 0.9936305732484076,
1127
  "per_class": {
1128
  "Nostril in Nucleus": {
1129
- "tp": 48,
1130
- "fp": 4,
1131
- "fn": 9,
1132
- "precision": 0.9230769230769231,
1133
- "recall": 0.8421052631578947,
1134
- "f1": 0.8807339449541285,
1135
  "gt_count": 57,
1136
- "pred_count": 52
1137
  },
1138
  "Nostril External of Nucleus": {
1139
- "tp": 108,
1140
- "fp": 6,
1141
- "fn": 29,
1142
- "precision": 0.9473684210526315,
1143
- "recall": 0.7883211678832117,
1144
- "f1": 0.8605577689243028,
1145
  "gt_count": 137,
1146
- "pred_count": 114
1147
  }
1148
  },
1149
  "n_gt": 194,
1150
- "n_pred": 166
1151
  },
1152
  "counts": {
1153
  "Cell, Antibody +": {
1154
  "gt": 87,
1155
- "pred": 92,
1156
- "abs_err": 5,
1157
- "rel_err": 0.05747126436781609
1158
  },
1159
  "Cell, Antibody -": {
1160
  "gt": 7,
1161
- "pred": 5,
1162
- "abs_err": 2,
1163
- "rel_err": 0.2857142857142857
1164
  },
1165
  "Cell, Nostril, Antibody +": {
1166
  "gt": 33,
1167
- "pred": 28,
1168
- "abs_err": 5,
1169
- "rel_err": 0.15151515151515152
1170
  },
1171
  "Cell, Nostril, Antibody -": {
1172
  "gt": 1,
@@ -1176,104 +1176,104 @@
1176
  },
1177
  "Nostril in Nucleus": {
1178
  "gt": 57,
1179
- "pred": 52,
1180
- "abs_err": 5,
1181
- "rel_err": 0.08771929824561403
1182
  },
1183
  "Nostril External of Nucleus": {
1184
  "gt": 137,
1185
- "pred": 114,
1186
- "abs_err": 23,
1187
- "rel_err": 0.1678832116788321
1188
  }
1189
  },
1190
  "fp_analysis": null,
1191
  "pairs": null,
1192
  "summary": {
1193
- "cell_det_f1": 0.9169960474308302,
1194
- "cell_det_precision": 0.928,
1195
- "cell_det_recall": 0.90625,
1196
- "cell_mean_iou": 0.8161455695675424,
1197
- "cell_det_f1_iou30": 0.9486166007905138,
1198
- "cell_det_f1_centroid": 0.9565217391304348,
1199
- "cell_attr_antibody": 0.9827586206896551,
1200
- "cell_attr_nostril": 0.9655172413793104,
1201
- "cell_attr_both": 0.9482758620689655,
1202
- "nos_det_f1": 0.8722222222222222,
1203
- "nos_det_precision": 0.9457831325301205,
1204
- "nos_det_recall": 0.8092783505154639,
1205
- "nos_type_acc": 0.9936305732484076,
1206
- "f1[Cell, Antibody +]": 0.9162011173184358,
1207
- "f1[Cell, Antibody -]": 0.8333333333333333,
1208
- "f1[Cell, Nostril, Antibody +]": 0.7540983606557378,
1209
  "f1[Cell, Nostril, Antibody -]": 0.0,
1210
- "f1[Nostril in Nucleus]": 0.8807339449541285,
1211
- "f1[Nostril External of Nucleus]": 0.8605577689243028
1212
  }
1213
  },
1214
  "F113": {
1215
  "cells": {
1216
  "detection": {
1217
- "tp": 131,
1218
- "fp": 19,
1219
- "fn": 32,
1220
- "precision": 0.8733333333333333,
1221
- "recall": 0.803680981595092,
1222
- "f1": 0.8370607028753994,
1223
- "mean_iou_matched": 0.7799368783421308,
1224
  "iou30": {
1225
- "tp": 146,
1226
- "fp": 4,
1227
- "fn": 17,
1228
- "precision": 0.9733333333333334,
1229
- "recall": 0.8957055214723927,
1230
- "f1": 0.9329073482428116
1231
  },
1232
  "centroid": {
1233
- "tp": 144,
1234
- "fp": 6,
1235
- "fn": 19,
1236
- "precision": 0.96,
1237
- "recall": 0.8834355828220859,
1238
- "f1": 0.9201277955271565
1239
  }
1240
  },
1241
  "attribute_accuracy": {
1242
  "antibody": 1.0,
1243
- "nostril": 0.9770992366412213,
1244
- "both": 0.9770992366412213,
1245
- "n_matched": 131
1246
  },
1247
  "per_class": {
1248
  "Cell, Antibody +": {
1249
- "tp": 108,
1250
- "fp": 21,
1251
- "fn": 30,
1252
- "precision": 0.8372093023255814,
1253
- "recall": 0.782608695652174,
1254
- "f1": 0.8089887640449438,
1255
  "gt_count": 138,
1256
- "pred_count": 129
1257
  },
1258
  "Cell, Antibody -": {
1259
  "tp": 2,
1260
- "fp": 0,
1261
  "fn": 1,
1262
- "precision": 1.0,
1263
  "recall": 0.6666666666666666,
1264
- "f1": 0.8,
1265
  "gt_count": 3,
1266
- "pred_count": 2
1267
  },
1268
  "Cell, Nostril, Antibody +": {
1269
  "tp": 18,
1270
- "fp": 1,
1271
  "fn": 4,
1272
- "precision": 0.9473684210526315,
1273
  "recall": 0.8181818181818182,
1274
- "f1": 0.8780487804878049,
1275
  "gt_count": 22,
1276
- "pred_count": 19
1277
  },
1278
  "Cell, Nostril, Antibody -": {
1279
  "tp": 0,
@@ -1288,7 +1288,7 @@
1288
  },
1289
  "confusion": {
1290
  "Cell, Antibody +": {
1291
- "Cell, Antibody +": 108,
1292
  "Cell, Antibody -": 0,
1293
  "Cell, Nostril, Antibody +": 0,
1294
  "Cell, Nostril, Antibody -": 0
@@ -1313,62 +1313,62 @@
1313
  }
1314
  },
1315
  "n_gt": 163,
1316
- "n_pred": 150
1317
  },
1318
  "nostrils": {
1319
  "detection": {
1320
- "tp": 27,
1321
- "fp": 1,
1322
- "fn": 7,
1323
- "precision": 0.9642857142857143,
1324
- "recall": 0.7941176470588235,
1325
- "f1": 0.8709677419354839,
1326
- "mean_dist_matched": 0.8145690279889126
1327
- },
1328
- "type_accuracy": 0.9259259259259259,
1329
  "per_class": {
1330
  "Nostril in Nucleus": {
1331
- "tp": 21,
1332
- "fp": 2,
1333
- "fn": 5,
1334
- "precision": 0.9130434782608695,
1335
- "recall": 0.8076923076923077,
1336
- "f1": 0.8571428571428572,
1337
  "gt_count": 26,
1338
  "pred_count": 23
1339
  },
1340
  "Nostril External of Nucleus": {
1341
- "tp": 4,
1342
  "fp": 1,
1343
- "fn": 4,
1344
- "precision": 0.8,
1345
- "recall": 0.5,
1346
- "f1": 0.6153846153846154,
1347
  "gt_count": 8,
1348
- "pred_count": 5
1349
  }
1350
  },
1351
  "n_gt": 34,
1352
- "n_pred": 28
1353
  },
1354
  "counts": {
1355
  "Cell, Antibody +": {
1356
  "gt": 138,
1357
- "pred": 129,
1358
- "abs_err": 9,
1359
- "rel_err": 0.06521739130434782
1360
  },
1361
  "Cell, Antibody -": {
1362
  "gt": 3,
1363
- "pred": 2,
1364
- "abs_err": 1,
1365
- "rel_err": 0.3333333333333333
1366
  },
1367
  "Cell, Nostril, Antibody +": {
1368
  "gt": 22,
1369
- "pred": 19,
1370
- "abs_err": 3,
1371
- "rel_err": 0.13636363636363635
1372
  },
1373
  "Cell, Nostril, Antibody -": {
1374
  "gt": 0,
@@ -1384,298 +1384,298 @@
1384
  },
1385
  "Nostril External of Nucleus": {
1386
  "gt": 8,
1387
- "pred": 5,
1388
- "abs_err": 3,
1389
- "rel_err": 0.375
1390
  }
1391
  },
1392
  "fp_analysis": null,
1393
  "pairs": null,
1394
  "summary": {
1395
- "cell_det_f1": 0.8370607028753994,
1396
- "cell_det_precision": 0.8733333333333333,
1397
- "cell_det_recall": 0.803680981595092,
1398
- "cell_mean_iou": 0.7799368783421308,
1399
- "cell_det_f1_iou30": 0.9329073482428116,
1400
- "cell_det_f1_centroid": 0.9201277955271565,
1401
  "cell_attr_antibody": 1.0,
1402
- "cell_attr_nostril": 0.9770992366412213,
1403
- "cell_attr_both": 0.9770992366412213,
1404
- "nos_det_f1": 0.8709677419354839,
1405
- "nos_det_precision": 0.9642857142857143,
1406
- "nos_det_recall": 0.7941176470588235,
1407
- "nos_type_acc": 0.9259259259259259,
1408
- "f1[Cell, Antibody +]": 0.8089887640449438,
1409
- "f1[Cell, Antibody -]": 0.8,
1410
- "f1[Cell, Nostril, Antibody +]": 0.8780487804878049,
1411
  "f1[Cell, Nostril, Antibody -]": 0.0,
1412
- "f1[Nostril in Nucleus]": 0.8571428571428572,
1413
- "f1[Nostril External of Nucleus]": 0.6153846153846154
1414
  }
1415
  },
1416
  "F114": {
1417
  "cells": {
1418
  "detection": {
1419
- "tp": 211,
1420
- "fp": 25,
1421
- "fn": 31,
1422
- "precision": 0.8940677966101694,
1423
- "recall": 0.871900826446281,
1424
- "f1": 0.8828451882845189,
1425
- "mean_iou_matched": 0.8168843572713155,
1426
  "iou30": {
1427
- "tp": 222,
1428
- "fp": 14,
1429
- "fn": 20,
1430
- "precision": 0.940677966101695,
1431
- "recall": 0.9173553719008265,
1432
- "f1": 0.9288702928870294
1433
  },
1434
  "centroid": {
1435
- "tp": 221,
1436
- "fp": 15,
1437
- "fn": 21,
1438
- "precision": 0.9364406779661016,
1439
- "recall": 0.9132231404958677,
1440
- "f1": 0.9246861924686192
1441
  }
1442
  },
1443
  "attribute_accuracy": {
1444
- "antibody": 0.95260663507109,
1445
- "nostril": 0.981042654028436,
1446
- "both": 0.933649289099526,
1447
- "n_matched": 211
1448
  },
1449
  "per_class": {
1450
  "Cell, Antibody +": {
1451
- "tp": 148,
1452
- "fp": 28,
1453
- "fn": 27,
1454
- "precision": 0.8409090909090909,
1455
- "recall": 0.8457142857142858,
1456
- "f1": 0.8433048433048433,
1457
  "gt_count": 175,
1458
- "pred_count": 176
1459
  },
1460
  "Cell, Antibody -": {
1461
- "tp": 12,
1462
- "fp": 2,
1463
- "fn": 9,
1464
- "precision": 0.8571428571428571,
1465
- "recall": 0.5714285714285714,
1466
- "f1": 0.6857142857142857,
1467
  "gt_count": 21,
1468
- "pred_count": 14
1469
  },
1470
  "Cell, Nostril, Antibody +": {
1471
- "tp": 37,
1472
- "fp": 9,
1473
- "fn": 5,
1474
- "precision": 0.8043478260869565,
1475
- "recall": 0.8809523809523809,
1476
- "f1": 0.8409090909090908,
1477
  "gt_count": 42,
1478
- "pred_count": 46
1479
  },
1480
  "Cell, Nostril, Antibody -": {
1481
- "tp": 0,
1482
  "fp": 0,
1483
- "fn": 4,
1484
- "precision": 0.0,
1485
- "recall": 0.0,
1486
- "f1": 0.0,
1487
  "gt_count": 4,
1488
- "pred_count": 0
1489
  }
1490
  },
1491
  "confusion": {
1492
  "Cell, Antibody +": {
1493
- "Cell, Antibody +": 148,
1494
- "Cell, Antibody -": 1,
1495
- "Cell, Nostril, Antibody +": 4,
1496
  "Cell, Nostril, Antibody -": 0
1497
  },
1498
  "Cell, Antibody -": {
1499
- "Cell, Antibody +": 9,
1500
- "Cell, Antibody -": 12,
1501
  "Cell, Nostril, Antibody +": 0,
1502
  "Cell, Nostril, Antibody -": 0
1503
  },
1504
  "Cell, Nostril, Antibody +": {
1505
- "Cell, Antibody +": 0,
1506
  "Cell, Antibody -": 0,
1507
- "Cell, Nostril, Antibody +": 37,
1508
  "Cell, Nostril, Antibody -": 0
1509
  },
1510
  "Cell, Nostril, Antibody -": {
1511
  "Cell, Antibody +": 0,
1512
- "Cell, Antibody -": 0,
1513
  "Cell, Nostril, Antibody +": 0,
1514
- "Cell, Nostril, Antibody -": 0
1515
  }
1516
  },
1517
  "n_gt": 242,
1518
- "n_pred": 236
1519
  },
1520
  "nostrils": {
1521
  "detection": {
1522
- "tp": 81,
1523
- "fp": 24,
1524
- "fn": 9,
1525
- "precision": 0.7714285714285715,
1526
- "recall": 0.9,
1527
- "f1": 0.8307692307692307,
1528
- "mean_dist_matched": 0.9417483459046142
1529
  },
1530
  "type_accuracy": 1.0,
1531
  "per_class": {
1532
  "Nostril in Nucleus": {
1533
- "tp": 46,
1534
- "fp": 8,
1535
- "fn": 6,
1536
- "precision": 0.8518518518518519,
1537
- "recall": 0.8846153846153846,
1538
- "f1": 0.8679245283018868,
1539
  "gt_count": 52,
1540
- "pred_count": 54
1541
  },
1542
  "Nostril External of Nucleus": {
1543
- "tp": 35,
1544
- "fp": 16,
1545
- "fn": 3,
1546
- "precision": 0.6862745098039216,
1547
- "recall": 0.9210526315789473,
1548
- "f1": 0.7865168539325842,
1549
  "gt_count": 38,
1550
- "pred_count": 51
1551
  }
1552
  },
1553
  "n_gt": 90,
1554
- "n_pred": 105
1555
  },
1556
  "counts": {
1557
  "Cell, Antibody +": {
1558
  "gt": 175,
1559
- "pred": 176,
1560
- "abs_err": 1,
1561
- "rel_err": 0.005714285714285714
1562
  },
1563
  "Cell, Antibody -": {
1564
  "gt": 21,
1565
- "pred": 14,
1566
- "abs_err": 7,
1567
- "rel_err": 0.3333333333333333
1568
  },
1569
  "Cell, Nostril, Antibody +": {
1570
  "gt": 42,
1571
- "pred": 46,
1572
- "abs_err": 4,
1573
- "rel_err": 0.09523809523809523
1574
  },
1575
  "Cell, Nostril, Antibody -": {
1576
  "gt": 4,
1577
- "pred": 0,
1578
- "abs_err": 4,
1579
- "rel_err": 1.0
1580
  },
1581
  "Nostril in Nucleus": {
1582
  "gt": 52,
1583
- "pred": 54,
1584
- "abs_err": 2,
1585
- "rel_err": 0.038461538461538464
1586
  },
1587
  "Nostril External of Nucleus": {
1588
  "gt": 38,
1589
- "pred": 51,
1590
- "abs_err": 13,
1591
- "rel_err": 0.34210526315789475
1592
  }
1593
  },
1594
  "fp_analysis": null,
1595
  "pairs": null,
1596
  "summary": {
1597
- "cell_det_f1": 0.8828451882845189,
1598
- "cell_det_precision": 0.8940677966101694,
1599
- "cell_det_recall": 0.871900826446281,
1600
- "cell_mean_iou": 0.8168843572713155,
1601
- "cell_det_f1_iou30": 0.9288702928870294,
1602
- "cell_det_f1_centroid": 0.9246861924686192,
1603
- "cell_attr_antibody": 0.95260663507109,
1604
- "cell_attr_nostril": 0.981042654028436,
1605
- "cell_attr_both": 0.933649289099526,
1606
- "nos_det_f1": 0.8307692307692307,
1607
- "nos_det_precision": 0.7714285714285715,
1608
- "nos_det_recall": 0.9,
1609
  "nos_type_acc": 1.0,
1610
- "f1[Cell, Antibody +]": 0.8433048433048433,
1611
- "f1[Cell, Antibody -]": 0.6857142857142857,
1612
- "f1[Cell, Nostril, Antibody +]": 0.8409090909090908,
1613
- "f1[Cell, Nostril, Antibody -]": 0.0,
1614
- "f1[Nostril in Nucleus]": 0.8679245283018868,
1615
- "f1[Nostril External of Nucleus]": 0.7865168539325842
1616
  }
1617
  },
1618
  "F99": {
1619
  "cells": {
1620
  "detection": {
1621
- "tp": 227,
1622
- "fp": 28,
1623
- "fn": 24,
1624
- "precision": 0.8901960784313725,
1625
- "recall": 0.9043824701195219,
1626
- "f1": 0.8972332015810275,
1627
- "mean_iou_matched": 0.8280746396024866,
1628
  "iou30": {
1629
- "tp": 238,
1630
- "fp": 17,
1631
- "fn": 13,
1632
- "precision": 0.9333333333333333,
1633
- "recall": 0.9482071713147411,
1634
- "f1": 0.9407114624505929
1635
  },
1636
  "centroid": {
1637
- "tp": 238,
1638
- "fp": 17,
1639
- "fn": 13,
1640
- "precision": 0.9333333333333333,
1641
- "recall": 0.9482071713147411,
1642
- "f1": 0.9407114624505929
1643
  }
1644
  },
1645
  "attribute_accuracy": {
1646
- "antibody": 0.920704845814978,
1647
- "nostril": 0.9911894273127754,
1648
- "both": 0.9118942731277533,
1649
- "n_matched": 227
1650
  },
1651
  "per_class": {
1652
  "Cell, Antibody +": {
1653
- "tp": 162,
1654
- "fp": 41,
1655
- "fn": 21,
1656
- "precision": 0.7980295566502463,
1657
- "recall": 0.8852459016393442,
1658
- "f1": 0.839378238341969,
1659
  "gt_count": 183,
1660
- "pred_count": 203
1661
  },
1662
  "Cell, Antibody -": {
1663
- "tp": 42,
1664
- "fp": 6,
1665
- "fn": 20,
1666
- "precision": 0.875,
1667
- "recall": 0.6774193548387096,
1668
- "f1": 0.7636363636363636,
1669
  "gt_count": 62,
1670
- "pred_count": 48
1671
  },
1672
  "Cell, Nostril, Antibody +": {
1673
- "tp": 3,
1674
- "fp": 1,
1675
- "fn": 3,
1676
- "precision": 0.75,
1677
- "recall": 0.5,
1678
- "f1": 0.6,
1679
  "gt_count": 6,
1680
  "pred_count": 4
1681
  },
@@ -1692,21 +1692,21 @@
1692
  },
1693
  "confusion": {
1694
  "Cell, Antibody +": {
1695
- "Cell, Antibody +": 162,
1696
  "Cell, Antibody -": 4,
1697
  "Cell, Nostril, Antibody +": 0,
1698
  "Cell, Nostril, Antibody -": 0
1699
  },
1700
  "Cell, Antibody -": {
1701
- "Cell, Antibody +": 14,
1702
- "Cell, Antibody -": 42,
1703
  "Cell, Nostril, Antibody +": 0,
1704
  "Cell, Nostril, Antibody -": 0
1705
  },
1706
  "Cell, Nostril, Antibody +": {
1707
  "Cell, Antibody +": 2,
1708
  "Cell, Antibody -": 0,
1709
- "Cell, Nostril, Antibody +": 3,
1710
  "Cell, Nostril, Antibody -": 0
1711
  },
1712
  "Cell, Nostril, Antibody -": {
@@ -1717,17 +1717,17 @@
1717
  }
1718
  },
1719
  "n_gt": 251,
1720
- "n_pred": 255
1721
  },
1722
  "nostrils": {
1723
  "detection": {
1724
- "tp": 14,
1725
- "fp": 1,
1726
- "fn": 8,
1727
- "precision": 0.9333333333333333,
1728
- "recall": 0.6363636363636364,
1729
- "f1": 0.7567567567567568,
1730
- "mean_dist_matched": 0.6631874114708707
1731
  },
1732
  "type_accuracy": 1.0,
1733
  "per_class": {
@@ -1742,31 +1742,31 @@
1742
  "pred_count": 4
1743
  },
1744
  "Nostril External of Nucleus": {
1745
- "tp": 10,
1746
- "fp": 1,
1747
- "fn": 6,
1748
- "precision": 0.9090909090909091,
1749
- "recall": 0.625,
1750
- "f1": 0.7407407407407406,
1751
  "gt_count": 16,
1752
- "pred_count": 11
1753
  }
1754
  },
1755
  "n_gt": 22,
1756
- "n_pred": 15
1757
  },
1758
  "counts": {
1759
  "Cell, Antibody +": {
1760
  "gt": 183,
1761
- "pred": 203,
1762
- "abs_err": 20,
1763
- "rel_err": 0.1092896174863388
1764
  },
1765
  "Cell, Antibody -": {
1766
  "gt": 62,
1767
- "pred": 48,
1768
- "abs_err": 14,
1769
- "rel_err": 0.22580645161290322
1770
  },
1771
  "Cell, Nostril, Antibody +": {
1772
  "gt": 6,
@@ -1788,33 +1788,33 @@
1788
  },
1789
  "Nostril External of Nucleus": {
1790
  "gt": 16,
1791
- "pred": 11,
1792
- "abs_err": 5,
1793
- "rel_err": 0.3125
1794
  }
1795
  },
1796
  "fp_analysis": null,
1797
  "pairs": null,
1798
  "summary": {
1799
- "cell_det_f1": 0.8972332015810275,
1800
- "cell_det_precision": 0.8901960784313725,
1801
- "cell_det_recall": 0.9043824701195219,
1802
- "cell_mean_iou": 0.8280746396024866,
1803
- "cell_det_f1_iou30": 0.9407114624505929,
1804
- "cell_det_f1_centroid": 0.9407114624505929,
1805
- "cell_attr_antibody": 0.920704845814978,
1806
- "cell_attr_nostril": 0.9911894273127754,
1807
- "cell_attr_both": 0.9118942731277533,
1808
- "nos_det_f1": 0.7567567567567568,
1809
- "nos_det_precision": 0.9333333333333333,
1810
- "nos_det_recall": 0.6363636363636364,
1811
  "nos_type_acc": 1.0,
1812
- "f1[Cell, Antibody +]": 0.839378238341969,
1813
- "f1[Cell, Antibody -]": 0.7636363636363636,
1814
- "f1[Cell, Nostril, Antibody +]": 0.6,
1815
  "f1[Cell, Nostril, Antibody -]": 0.0,
1816
  "f1[Nostril in Nucleus]": 0.8,
1817
- "f1[Nostril External of Nucleus]": 0.7407407407407406
1818
  }
1819
  }
1820
  }
 
2
  "C100": {
3
  "cells": {
4
  "detection": {
5
+ "tp": 173,
6
+ "fp": 21,
7
+ "fn": 5,
8
+ "precision": 0.8917525773195877,
9
+ "recall": 0.9719101123595506,
10
+ "f1": 0.9301075268817205,
11
+ "mean_iou_matched": 0.795722646954622,
12
  "iou30": {
13
+ "tp": 177,
14
+ "fp": 17,
15
+ "fn": 1,
16
+ "precision": 0.9123711340206185,
17
+ "recall": 0.9943820224719101,
18
+ "f1": 0.9516129032258065
19
  },
20
  "centroid": {
21
+ "tp": 178,
22
+ "fp": 16,
23
+ "fn": 0,
24
+ "precision": 0.9175257731958762,
25
+ "recall": 1.0,
26
+ "f1": 0.956989247311828
27
  }
28
  },
29
  "attribute_accuracy": {
30
+ "antibody": 0.9884393063583815,
31
+ "nostril": 0.9942196531791907,
32
+ "both": 0.9826589595375722,
33
+ "n_matched": 173
34
  },
35
  "per_class": {
36
  "Cell, Antibody +": {
37
+ "tp": 158,
38
+ "fp": 18,
39
+ "fn": 7,
40
+ "precision": 0.8977272727272727,
41
+ "recall": 0.9575757575757575,
42
+ "f1": 0.9266862170087976,
43
  "gt_count": 165,
44
+ "pred_count": 176
45
  },
46
  "Cell, Antibody -": {
47
  "tp": 0,
48
+ "fp": 6,
49
  "fn": 0,
50
  "precision": 0.0,
51
  "recall": 0.0,
52
  "f1": 0.0,
53
  "gt_count": 0,
54
+ "pred_count": 6
55
  },
56
  "Cell, Nostril, Antibody +": {
57
  "tp": 12,
 
76
  },
77
  "confusion": {
78
  "Cell, Antibody +": {
79
+ "Cell, Antibody +": 158,
80
+ "Cell, Antibody -": 2,
81
  "Cell, Nostril, Antibody +": 0,
82
  "Cell, Nostril, Antibody -": 0
83
  },
 
101
  }
102
  },
103
  "n_gt": 178,
104
+ "n_pred": 194
105
  },
106
  "nostrils": {
107
  "detection": {
108
  "tp": 16,
109
+ "fp": 4,
110
  "fn": 4,
111
+ "precision": 0.8,
112
  "recall": 0.8,
113
+ "f1": 0.8000000000000002,
114
+ "mean_dist_matched": 1.1705934762183428
115
  },
116
  "type_accuracy": 1.0,
117
  "per_class": {
118
  "Nostril in Nucleus": {
119
  "tp": 12,
120
+ "fp": 1,
121
  "fn": 2,
122
+ "precision": 0.9230769230769231,
123
  "recall": 0.8571428571428571,
124
+ "f1": 0.888888888888889,
125
  "gt_count": 14,
126
+ "pred_count": 13
127
  },
128
  "Nostril External of Nucleus": {
129
  "tp": 4,
 
137
  }
138
  },
139
  "n_gt": 20,
140
+ "n_pred": 20
141
  },
142
  "counts": {
143
  "Cell, Antibody +": {
144
  "gt": 165,
145
+ "pred": 176,
146
+ "abs_err": 11,
147
+ "rel_err": 0.06666666666666667
148
  },
149
  "Cell, Antibody -": {
150
  "gt": 0,
151
+ "pred": 6,
152
+ "abs_err": 6,
153
  "rel_err": Infinity
154
  },
155
  "Cell, Nostril, Antibody +": {
 
166
  },
167
  "Nostril in Nucleus": {
168
  "gt": 14,
169
+ "pred": 13,
170
+ "abs_err": 1,
171
+ "rel_err": 0.07142857142857142
172
  },
173
  "Nostril External of Nucleus": {
174
  "gt": 6,
 
180
  "fp_analysis": null,
181
  "pairs": null,
182
  "summary": {
183
+ "cell_det_f1": 0.9301075268817205,
184
+ "cell_det_precision": 0.8917525773195877,
185
+ "cell_det_recall": 0.9719101123595506,
186
+ "cell_mean_iou": 0.795722646954622,
187
+ "cell_det_f1_iou30": 0.9516129032258065,
188
+ "cell_det_f1_centroid": 0.956989247311828,
189
+ "cell_attr_antibody": 0.9884393063583815,
190
+ "cell_attr_nostril": 0.9942196531791907,
191
+ "cell_attr_both": 0.9826589595375722,
192
+ "nos_det_f1": 0.8000000000000002,
193
+ "nos_det_precision": 0.8,
194
  "nos_det_recall": 0.8,
195
  "nos_type_acc": 1.0,
196
+ "f1[Cell, Antibody +]": 0.9266862170087976,
197
  "f1[Cell, Antibody -]": 0.0,
198
  "f1[Cell, Nostril, Antibody +]": 0.9600000000000001,
199
  "f1[Cell, Nostril, Antibody -]": 0.0,
200
+ "f1[Nostril in Nucleus]": 0.888888888888889,
201
  "f1[Nostril External of Nucleus]": 0.6153846153846153
202
  }
203
  },
204
  "C102": {
205
  "cells": {
206
  "detection": {
207
+ "tp": 91,
208
+ "fp": 17,
209
+ "fn": 0,
210
+ "precision": 0.8425925925925926,
211
+ "recall": 1.0,
212
+ "f1": 0.914572864321608,
213
+ "mean_iou_matched": 0.8036863197012898,
214
  "iou30": {
215
+ "tp": 91,
216
+ "fp": 17,
217
+ "fn": 0,
218
+ "precision": 0.8425925925925926,
219
+ "recall": 1.0,
220
+ "f1": 0.914572864321608
221
  },
222
  "centroid": {
223
+ "tp": 91,
224
+ "fp": 17,
225
+ "fn": 0,
226
+ "precision": 0.8425925925925926,
227
+ "recall": 1.0,
228
+ "f1": 0.914572864321608
229
  }
230
  },
231
  "attribute_accuracy": {
232
  "antibody": 1.0,
233
+ "nostril": 0.945054945054945,
234
+ "both": 0.945054945054945,
235
+ "n_matched": 91
236
  },
237
  "per_class": {
238
  "Cell, Antibody +": {
239
  "tp": 41,
240
+ "fp": 17,
241
  "fn": 0,
242
+ "precision": 0.7068965517241379,
243
  "recall": 1.0,
244
+ "f1": 0.8282828282828283,
245
  "gt_count": 41,
246
+ "pred_count": 58
247
  },
248
  "Cell, Antibody -": {
249
  "tp": 0,
250
+ "fp": 1,
251
  "fn": 0,
252
  "precision": 0.0,
253
  "recall": 0.0,
254
  "f1": 0.0,
255
  "gt_count": 0,
256
+ "pred_count": 1
257
  },
258
  "Cell, Nostril, Antibody +": {
259
+ "tp": 45,
260
+ "fp": 4,
261
+ "fn": 5,
262
+ "precision": 0.9183673469387755,
263
+ "recall": 0.9,
264
+ "f1": 0.9090909090909091,
265
  "gt_count": 50,
266
+ "pred_count": 49
267
  },
268
  "Cell, Nostril, Antibody -": {
269
  "tp": 0,
 
292
  "Cell, Nostril, Antibody +": {
293
  "Cell, Antibody +": 5,
294
  "Cell, Antibody -": 0,
295
+ "Cell, Nostril, Antibody +": 45,
296
  "Cell, Nostril, Antibody -": 0
297
  },
298
  "Cell, Nostril, Antibody -": {
 
303
  }
304
  },
305
  "n_gt": 91,
306
+ "n_pred": 108
307
  },
308
  "nostrils": {
309
  "detection": {
310
+ "tp": 123,
311
+ "fp": 55,
312
+ "fn": 11,
313
+ "precision": 0.6910112359550562,
314
+ "recall": 0.917910447761194,
315
+ "f1": 0.7884615384615384,
316
+ "mean_dist_matched": 1.022617546328301
317
+ },
318
+ "type_accuracy": 0.991869918699187,
319
  "per_class": {
320
  "Nostril in Nucleus": {
321
  "tp": 54,
322
+ "fp": 13,
323
  "fn": 6,
324
+ "precision": 0.8059701492537313,
325
  "recall": 0.9,
326
+ "f1": 0.8503937007874016,
327
  "gt_count": 60,
328
+ "pred_count": 67
329
  },
330
  "Nostril External of Nucleus": {
331
+ "tp": 68,
332
+ "fp": 43,
333
+ "fn": 6,
334
+ "precision": 0.6126126126126126,
335
+ "recall": 0.918918918918919,
336
+ "f1": 0.7351351351351352,
337
  "gt_count": 74,
338
+ "pred_count": 111
339
  }
340
  },
341
  "n_gt": 134,
342
+ "n_pred": 178
343
  },
344
  "counts": {
345
  "Cell, Antibody +": {
346
  "gt": 41,
347
+ "pred": 58,
348
+ "abs_err": 17,
349
+ "rel_err": 0.4146341463414634
350
  },
351
  "Cell, Antibody -": {
352
  "gt": 0,
353
+ "pred": 1,
354
+ "abs_err": 1,
355
+ "rel_err": Infinity
356
  },
357
  "Cell, Nostril, Antibody +": {
358
  "gt": 50,
359
+ "pred": 49,
360
+ "abs_err": 1,
361
+ "rel_err": 0.02
362
  },
363
  "Cell, Nostril, Antibody -": {
364
  "gt": 0,
 
368
  },
369
  "Nostril in Nucleus": {
370
  "gt": 60,
371
+ "pred": 67,
372
+ "abs_err": 7,
373
+ "rel_err": 0.11666666666666667
374
  },
375
  "Nostril External of Nucleus": {
376
  "gt": 74,
377
+ "pred": 111,
378
+ "abs_err": 37,
379
+ "rel_err": 0.5
380
  }
381
  },
382
  "fp_analysis": null,
383
  "pairs": null,
384
  "summary": {
385
+ "cell_det_f1": 0.914572864321608,
386
+ "cell_det_precision": 0.8425925925925926,
387
+ "cell_det_recall": 1.0,
388
+ "cell_mean_iou": 0.8036863197012898,
389
+ "cell_det_f1_iou30": 0.914572864321608,
390
+ "cell_det_f1_centroid": 0.914572864321608,
391
  "cell_attr_antibody": 1.0,
392
+ "cell_attr_nostril": 0.945054945054945,
393
+ "cell_attr_both": 0.945054945054945,
394
+ "nos_det_f1": 0.7884615384615384,
395
+ "nos_det_precision": 0.6910112359550562,
396
+ "nos_det_recall": 0.917910447761194,
397
+ "nos_type_acc": 0.991869918699187,
398
+ "f1[Cell, Antibody +]": 0.8282828282828283,
399
  "f1[Cell, Antibody -]": 0.0,
400
+ "f1[Cell, Nostril, Antibody +]": 0.9090909090909091,
401
  "f1[Cell, Nostril, Antibody -]": 0.0,
402
+ "f1[Nostril in Nucleus]": 0.8503937007874016,
403
+ "f1[Nostril External of Nucleus]": 0.7351351351351352
404
  }
405
  },
406
  "C110": {
407
  "cells": {
408
  "detection": {
409
+ "tp": 168,
410
+ "fp": 34,
411
+ "fn": 16,
412
+ "precision": 0.8316831683168316,
413
+ "recall": 0.9130434782608695,
414
+ "f1": 0.8704663212435233,
415
+ "mean_iou_matched": 0.7832369871445182,
416
  "iou30": {
417
+ "tp": 178,
418
+ "fp": 24,
419
+ "fn": 6,
420
+ "precision": 0.8811881188118812,
421
+ "recall": 0.967391304347826,
422
+ "f1": 0.922279792746114
423
  },
424
  "centroid": {
425
+ "tp": 180,
426
+ "fp": 22,
427
+ "fn": 4,
428
+ "precision": 0.8910891089108911,
429
+ "recall": 0.9782608695652174,
430
+ "f1": 0.9326424870466321
431
  }
432
  },
433
  "attribute_accuracy": {
434
+ "antibody": 0.9940476190476191,
435
+ "nostril": 0.9702380952380952,
436
+ "both": 0.9642857142857143,
437
+ "n_matched": 168
438
  },
439
  "per_class": {
440
  "Cell, Antibody +": {
441
+ "tp": 52,
442
+ "fp": 23,
443
+ "fn": 9,
444
+ "precision": 0.6933333333333334,
445
+ "recall": 0.8524590163934426,
446
+ "f1": 0.7647058823529411,
447
  "gt_count": 61,
448
+ "pred_count": 75
449
  },
450
  "Cell, Antibody -": {
451
  "tp": 1,
452
+ "fp": 1,
453
  "fn": 0,
454
+ "precision": 0.5,
455
  "recall": 1.0,
456
+ "f1": 0.6666666666666666,
457
  "gt_count": 1,
458
+ "pred_count": 2
459
  },
460
  "Cell, Nostril, Antibody +": {
461
+ "tp": 109,
462
+ "fp": 16,
463
+ "fn": 13,
464
+ "precision": 0.872,
465
+ "recall": 0.8934426229508197,
466
+ "f1": 0.8825910931174089,
467
  "gt_count": 122,
468
+ "pred_count": 125
469
  },
470
  "Cell, Nostril, Antibody -": {
471
  "tp": 0,
 
480
  },
481
  "confusion": {
482
  "Cell, Antibody +": {
483
+ "Cell, Antibody +": 52,
484
+ "Cell, Antibody -": 1,
485
+ "Cell, Nostril, Antibody +": 2,
486
  "Cell, Nostril, Antibody -": 0
487
  },
488
  "Cell, Antibody -": {
 
492
  "Cell, Nostril, Antibody -": 0
493
  },
494
  "Cell, Nostril, Antibody +": {
495
+ "Cell, Antibody +": 3,
496
  "Cell, Antibody -": 0,
497
+ "Cell, Nostril, Antibody +": 109,
498
  "Cell, Nostril, Antibody -": 0
499
  },
500
  "Cell, Nostril, Antibody -": {
 
505
  }
506
  },
507
  "n_gt": 184,
508
+ "n_pred": 202
509
  },
510
  "nostrils": {
511
  "detection": {
512
  "tp": 347,
513
+ "fp": 104,
514
  "fn": 29,
515
+ "precision": 0.7694013303769401,
516
  "recall": 0.9228723404255319,
517
+ "f1": 0.8391777509068925,
518
+ "mean_dist_matched": 0.9012807114492273
519
  },
520
+ "type_accuracy": 0.9740634005763689,
521
  "per_class": {
522
  "Nostril in Nucleus": {
523
+ "tp": 149,
524
+ "fp": 40,
525
+ "fn": 20,
526
+ "precision": 0.7883597883597884,
527
+ "recall": 0.8816568047337278,
528
+ "f1": 0.8324022346368716,
529
  "gt_count": 169,
530
+ "pred_count": 189
531
  },
532
  "Nostril External of Nucleus": {
533
+ "tp": 189,
534
+ "fp": 73,
535
+ "fn": 18,
536
+ "precision": 0.7213740458015268,
537
+ "recall": 0.9130434782608695,
538
+ "f1": 0.8059701492537313,
539
  "gt_count": 207,
540
+ "pred_count": 262
541
  }
542
  },
543
  "n_gt": 376,
544
+ "n_pred": 451
545
  },
546
  "counts": {
547
  "Cell, Antibody +": {
548
  "gt": 61,
549
+ "pred": 75,
550
+ "abs_err": 14,
551
+ "rel_err": 0.22950819672131148
552
  },
553
  "Cell, Antibody -": {
554
  "gt": 1,
555
+ "pred": 2,
556
+ "abs_err": 1,
557
+ "rel_err": 1.0
558
  },
559
  "Cell, Nostril, Antibody +": {
560
  "gt": 122,
561
+ "pred": 125,
562
+ "abs_err": 3,
563
+ "rel_err": 0.02459016393442623
564
  },
565
  "Cell, Nostril, Antibody -": {
566
  "gt": 0,
 
570
  },
571
  "Nostril in Nucleus": {
572
  "gt": 169,
573
+ "pred": 189,
574
+ "abs_err": 20,
575
+ "rel_err": 0.11834319526627218
576
  },
577
  "Nostril External of Nucleus": {
578
  "gt": 207,
579
+ "pred": 262,
580
+ "abs_err": 55,
581
+ "rel_err": 0.26570048309178745
582
  }
583
  },
584
  "fp_analysis": null,
585
  "pairs": null,
586
  "summary": {
587
+ "cell_det_f1": 0.8704663212435233,
588
+ "cell_det_precision": 0.8316831683168316,
589
+ "cell_det_recall": 0.9130434782608695,
590
+ "cell_mean_iou": 0.7832369871445182,
591
+ "cell_det_f1_iou30": 0.922279792746114,
592
+ "cell_det_f1_centroid": 0.9326424870466321,
593
+ "cell_attr_antibody": 0.9940476190476191,
594
+ "cell_attr_nostril": 0.9702380952380952,
595
+ "cell_attr_both": 0.9642857142857143,
596
+ "nos_det_f1": 0.8391777509068925,
597
+ "nos_det_precision": 0.7694013303769401,
598
  "nos_det_recall": 0.9228723404255319,
599
+ "nos_type_acc": 0.9740634005763689,
600
+ "f1[Cell, Antibody +]": 0.7647058823529411,
601
+ "f1[Cell, Antibody -]": 0.6666666666666666,
602
+ "f1[Cell, Nostril, Antibody +]": 0.8825910931174089,
603
  "f1[Cell, Nostril, Antibody -]": 0.0,
604
+ "f1[Nostril in Nucleus]": 0.8324022346368716,
605
+ "f1[Nostril External of Nucleus]": 0.8059701492537313
606
  }
607
  },
608
  "C112": {
609
  "cells": {
610
  "detection": {
611
+ "tp": 130,
612
+ "fp": 49,
613
+ "fn": 16,
614
+ "precision": 0.7262569832402235,
615
+ "recall": 0.8904109589041096,
616
+ "f1": 0.8,
617
+ "mean_iou_matched": 0.7378347860378052,
618
  "iou30": {
619
+ "tp": 141,
620
+ "fp": 38,
621
+ "fn": 5,
622
+ "precision": 0.7877094972067039,
623
+ "recall": 0.9657534246575342,
624
+ "f1": 0.8676923076923078
625
  },
626
  "centroid": {
627
+ "tp": 143,
628
+ "fp": 36,
629
+ "fn": 3,
630
+ "precision": 0.7988826815642458,
631
+ "recall": 0.9794520547945206,
632
+ "f1": 0.88
633
  }
634
  },
635
  "attribute_accuracy": {
636
+ "antibody": 1.0,
637
+ "nostril": 0.9923076923076923,
638
+ "both": 0.9923076923076923,
639
+ "n_matched": 130
640
  },
641
  "per_class": {
642
  "Cell, Antibody +": {
643
+ "tp": 109,
644
+ "fp": 43,
645
+ "fn": 15,
646
+ "precision": 0.7171052631578947,
647
+ "recall": 0.8790322580645161,
648
+ "f1": 0.7898550724637682,
649
  "gt_count": 124,
650
+ "pred_count": 152
651
  },
652
  "Cell, Antibody -": {
653
+ "tp": 5,
654
  "fp": 1,
655
+ "fn": 0,
656
+ "precision": 0.8333333333333334,
657
+ "recall": 1.0,
658
+ "f1": 0.9090909090909091,
659
  "gt_count": 5,
660
+ "pred_count": 6
661
  },
662
  "Cell, Nostril, Antibody +": {
663
  "tp": 15,
664
+ "fp": 6,
665
  "fn": 2,
666
+ "precision": 0.7142857142857143,
667
  "recall": 0.8823529411764706,
668
+ "f1": 0.7894736842105262,
669
  "gt_count": 17,
670
+ "pred_count": 21
671
  },
672
  "Cell, Nostril, Antibody -": {
673
  "tp": 0,
 
682
  },
683
  "confusion": {
684
  "Cell, Antibody +": {
685
+ "Cell, Antibody +": 109,
686
  "Cell, Antibody -": 0,
687
+ "Cell, Nostril, Antibody +": 1,
688
  "Cell, Nostril, Antibody -": 0
689
  },
690
  "Cell, Antibody -": {
691
+ "Cell, Antibody +": 0,
692
+ "Cell, Antibody -": 5,
693
  "Cell, Nostril, Antibody +": 0,
694
  "Cell, Nostril, Antibody -": 0
695
  },
 
707
  }
708
  },
709
  "n_gt": 146,
710
+ "n_pred": 179
711
  },
712
  "nostrils": {
713
  "detection": {
714
  "tp": 25,
715
+ "fp": 11,
716
  "fn": 0,
717
+ "precision": 0.6944444444444444,
718
  "recall": 1.0,
719
+ "f1": 0.819672131147541,
720
+ "mean_dist_matched": 1.0636624208995131
721
  },
722
  "type_accuracy": 1.0,
723
  "per_class": {
724
  "Nostril in Nucleus": {
725
  "tp": 23,
726
+ "fp": 4,
727
  "fn": 0,
728
+ "precision": 0.8518518518518519,
729
  "recall": 1.0,
730
+ "f1": 0.92,
731
  "gt_count": 23,
732
+ "pred_count": 27
733
  },
734
  "Nostril External of Nucleus": {
735
  "tp": 2,
736
+ "fp": 7,
737
  "fn": 0,
738
+ "precision": 0.2222222222222222,
739
  "recall": 1.0,
740
+ "f1": 0.3636363636363636,
741
  "gt_count": 2,
742
+ "pred_count": 9
743
  }
744
  },
745
  "n_gt": 25,
746
+ "n_pred": 36
747
  },
748
  "counts": {
749
  "Cell, Antibody +": {
750
  "gt": 124,
751
+ "pred": 152,
752
+ "abs_err": 28,
753
+ "rel_err": 0.22580645161290322
754
  },
755
  "Cell, Antibody -": {
756
  "gt": 5,
757
+ "pred": 6,
758
+ "abs_err": 1,
759
+ "rel_err": 0.2
760
  },
761
  "Cell, Nostril, Antibody +": {
762
  "gt": 17,
763
+ "pred": 21,
764
+ "abs_err": 4,
765
+ "rel_err": 0.23529411764705882
766
  },
767
  "Cell, Nostril, Antibody -": {
768
  "gt": 0,
 
772
  },
773
  "Nostril in Nucleus": {
774
  "gt": 23,
775
+ "pred": 27,
776
+ "abs_err": 4,
777
+ "rel_err": 0.17391304347826086
778
  },
779
  "Nostril External of Nucleus": {
780
  "gt": 2,
781
+ "pred": 9,
782
+ "abs_err": 7,
783
+ "rel_err": 3.5
784
  }
785
  },
786
  "fp_analysis": null,
787
  "pairs": null,
788
  "summary": {
789
+ "cell_det_f1": 0.8,
790
+ "cell_det_precision": 0.7262569832402235,
791
+ "cell_det_recall": 0.8904109589041096,
792
+ "cell_mean_iou": 0.7378347860378052,
793
+ "cell_det_f1_iou30": 0.8676923076923078,
794
+ "cell_det_f1_centroid": 0.88,
795
+ "cell_attr_antibody": 1.0,
796
+ "cell_attr_nostril": 0.9923076923076923,
797
+ "cell_attr_both": 0.9923076923076923,
798
+ "nos_det_f1": 0.819672131147541,
799
+ "nos_det_precision": 0.6944444444444444,
800
  "nos_det_recall": 1.0,
801
  "nos_type_acc": 1.0,
802
+ "f1[Cell, Antibody +]": 0.7898550724637682,
803
+ "f1[Cell, Antibody -]": 0.9090909090909091,
804
+ "f1[Cell, Nostril, Antibody +]": 0.7894736842105262,
805
  "f1[Cell, Nostril, Antibody -]": 0.0,
806
+ "f1[Nostril in Nucleus]": 0.92,
807
+ "f1[Nostril External of Nucleus]": 0.3636363636363636
808
  }
809
  },
810
  "F100": {
811
  "cells": {
812
  "detection": {
813
+ "tp": 172,
814
+ "fp": 30,
815
+ "fn": 14,
816
+ "precision": 0.8514851485148515,
817
+ "recall": 0.9247311827956989,
818
+ "f1": 0.8865979381443299,
819
+ "mean_iou_matched": 0.7905866698793166,
820
  "iou30": {
821
+ "tp": 182,
822
+ "fp": 20,
823
+ "fn": 4,
824
+ "precision": 0.900990099009901,
825
+ "recall": 0.978494623655914,
826
+ "f1": 0.9381443298969072
827
  },
828
  "centroid": {
829
+ "tp": 180,
830
+ "fp": 22,
831
+ "fn": 6,
832
+ "precision": 0.8910891089108911,
833
+ "recall": 0.967741935483871,
834
+ "f1": 0.9278350515463919
835
  }
836
  },
837
  "attribute_accuracy": {
838
+ "antibody": 0.9941860465116279,
839
+ "nostril": 0.9534883720930233,
840
+ "both": 0.9476744186046512,
841
+ "n_matched": 172
842
  },
843
  "per_class": {
844
  "Cell, Antibody +": {
845
+ "tp": 80,
846
+ "fp": 26,
847
+ "fn": 7,
848
+ "precision": 0.7547169811320755,
849
+ "recall": 0.9195402298850575,
850
+ "f1": 0.8290155440414508,
851
  "gt_count": 87,
852
+ "pred_count": 106
853
  },
854
  "Cell, Antibody -": {
855
+ "tp": 5,
856
+ "fp": 6,
857
+ "fn": 1,
858
+ "precision": 0.45454545454545453,
859
+ "recall": 0.8333333333333334,
860
+ "f1": 0.5882352941176471,
861
  "gt_count": 6,
862
+ "pred_count": 11
863
  },
864
  "Cell, Nostril, Antibody +": {
865
+ "tp": 78,
866
+ "fp": 7,
867
+ "fn": 15,
868
+ "precision": 0.9176470588235294,
869
+ "recall": 0.8387096774193549,
870
+ "f1": 0.8764044943820225,
871
  "gt_count": 93,
872
+ "pred_count": 85
873
  },
874
  "Cell, Nostril, Antibody -": {
875
  "tp": 0,
 
884
  },
885
  "confusion": {
886
  "Cell, Antibody +": {
887
+ "Cell, Antibody +": 80,
888
  "Cell, Antibody -": 1,
889
+ "Cell, Nostril, Antibody +": 1,
890
  "Cell, Nostril, Antibody -": 0
891
  },
892
  "Cell, Antibody -": {
893
+ "Cell, Antibody +": 0,
894
+ "Cell, Antibody -": 5,
895
  "Cell, Nostril, Antibody +": 0,
896
  "Cell, Nostril, Antibody -": 0
897
  },
898
  "Cell, Nostril, Antibody +": {
899
+ "Cell, Antibody +": 7,
900
  "Cell, Antibody -": 0,
901
+ "Cell, Nostril, Antibody +": 78,
902
  "Cell, Nostril, Antibody -": 0
903
  },
904
  "Cell, Nostril, Antibody -": {
 
909
  }
910
  },
911
  "n_gt": 186,
912
+ "n_pred": 202
913
  },
914
  "nostrils": {
915
  "detection": {
916
+ "tp": 144,
917
+ "fp": 13,
918
+ "fn": 18,
919
+ "precision": 0.9171974522292994,
920
+ "recall": 0.8888888888888888,
921
+ "f1": 0.90282131661442,
922
+ "mean_dist_matched": 1.0960012345310384
923
+ },
924
+ "type_accuracy": 0.9930555555555556,
925
  "per_class": {
926
  "Nostril in Nucleus": {
927
+ "tp": 103,
928
+ "fp": 13,
929
+ "fn": 15,
930
+ "precision": 0.8879310344827587,
931
+ "recall": 0.8728813559322034,
932
+ "f1": 0.8803418803418803,
933
  "gt_count": 118,
934
+ "pred_count": 116
935
  },
936
  "Nostril External of Nucleus": {
937
+ "tp": 40,
938
+ "fp": 1,
939
+ "fn": 4,
940
+ "precision": 0.975609756097561,
941
+ "recall": 0.9090909090909091,
942
+ "f1": 0.9411764705882352,
943
  "gt_count": 44,
944
  "pred_count": 41
945
  }
946
  },
947
  "n_gt": 162,
948
+ "n_pred": 157
949
  },
950
  "counts": {
951
  "Cell, Antibody +": {
952
  "gt": 87,
953
+ "pred": 106,
954
+ "abs_err": 19,
955
+ "rel_err": 0.21839080459770116
956
  },
957
  "Cell, Antibody -": {
958
  "gt": 6,
959
+ "pred": 11,
960
+ "abs_err": 5,
961
+ "rel_err": 0.8333333333333334
962
  },
963
  "Cell, Nostril, Antibody +": {
964
  "gt": 93,
965
+ "pred": 85,
966
+ "abs_err": 8,
967
+ "rel_err": 0.08602150537634409
968
  },
969
  "Cell, Nostril, Antibody -": {
970
  "gt": 0,
 
974
  },
975
  "Nostril in Nucleus": {
976
  "gt": 118,
977
+ "pred": 116,
978
+ "abs_err": 2,
979
+ "rel_err": 0.01694915254237288
980
  },
981
  "Nostril External of Nucleus": {
982
  "gt": 44,
 
988
  "fp_analysis": null,
989
  "pairs": null,
990
  "summary": {
991
+ "cell_det_f1": 0.8865979381443299,
992
+ "cell_det_precision": 0.8514851485148515,
993
+ "cell_det_recall": 0.9247311827956989,
994
+ "cell_mean_iou": 0.7905866698793166,
995
+ "cell_det_f1_iou30": 0.9381443298969072,
996
+ "cell_det_f1_centroid": 0.9278350515463919,
997
+ "cell_attr_antibody": 0.9941860465116279,
998
+ "cell_attr_nostril": 0.9534883720930233,
999
+ "cell_attr_both": 0.9476744186046512,
1000
+ "nos_det_f1": 0.90282131661442,
1001
+ "nos_det_precision": 0.9171974522292994,
1002
+ "nos_det_recall": 0.8888888888888888,
1003
+ "nos_type_acc": 0.9930555555555556,
1004
+ "f1[Cell, Antibody +]": 0.8290155440414508,
1005
+ "f1[Cell, Antibody -]": 0.5882352941176471,
1006
+ "f1[Cell, Nostril, Antibody +]": 0.8764044943820225,
1007
  "f1[Cell, Nostril, Antibody -]": 0.0,
1008
+ "f1[Nostril in Nucleus]": 0.8803418803418803,
1009
+ "f1[Nostril External of Nucleus]": 0.9411764705882352
1010
  }
1011
  },
1012
  "F110": {
1013
  "cells": {
1014
  "detection": {
1015
+ "tp": 122,
1016
+ "fp": 22,
1017
+ "fn": 6,
1018
+ "precision": 0.8472222222222222,
1019
+ "recall": 0.953125,
1020
+ "f1": 0.8970588235294118,
1021
+ "mean_iou_matched": 0.7899681957823826,
1022
  "iou30": {
1023
+ "tp": 125,
1024
+ "fp": 19,
1025
+ "fn": 3,
1026
+ "precision": 0.8680555555555556,
1027
+ "recall": 0.9765625,
1028
+ "f1": 0.9191176470588235
1029
  },
1030
  "centroid": {
1031
+ "tp": 126,
1032
+ "fp": 18,
1033
+ "fn": 2,
1034
+ "precision": 0.875,
1035
+ "recall": 0.984375,
1036
+ "f1": 0.9264705882352942
1037
  }
1038
  },
1039
  "attribute_accuracy": {
1040
+ "antibody": 1.0,
1041
+ "nostril": 0.9754098360655737,
1042
+ "both": 0.9754098360655737,
1043
+ "n_matched": 122
1044
  },
1045
  "per_class": {
1046
  "Cell, Antibody +": {
1047
+ "tp": 85,
1048
+ "fp": 15,
1049
+ "fn": 2,
1050
+ "precision": 0.85,
1051
+ "recall": 0.9770114942528736,
1052
+ "f1": 0.9090909090909092,
1053
  "gt_count": 87,
1054
+ "pred_count": 100
1055
  },
1056
  "Cell, Antibody -": {
1057
+ "tp": 6,
1058
+ "fp": 5,
1059
+ "fn": 1,
1060
+ "precision": 0.5454545454545454,
1061
+ "recall": 0.8571428571428571,
1062
+ "f1": 0.6666666666666665,
1063
  "gt_count": 7,
1064
+ "pred_count": 11
1065
  },
1066
  "Cell, Nostril, Antibody +": {
1067
+ "tp": 28,
1068
  "fp": 5,
1069
+ "fn": 5,
1070
+ "precision": 0.8484848484848485,
1071
+ "recall": 0.8484848484848485,
1072
+ "f1": 0.8484848484848486,
1073
  "gt_count": 33,
1074
+ "pred_count": 33
1075
  },
1076
  "Cell, Nostril, Antibody -": {
1077
  "tp": 0,
 
1086
  },
1087
  "confusion": {
1088
  "Cell, Antibody +": {
1089
+ "Cell, Antibody +": 85,
1090
  "Cell, Antibody -": 0,
1091
  "Cell, Nostril, Antibody +": 0,
1092
  "Cell, Nostril, Antibody -": 0
1093
  },
1094
  "Cell, Antibody -": {
1095
+ "Cell, Antibody +": 0,
1096
+ "Cell, Antibody -": 6,
1097
  "Cell, Nostril, Antibody +": 0,
1098
  "Cell, Nostril, Antibody -": 0
1099
  },
1100
  "Cell, Nostril, Antibody +": {
1101
+ "Cell, Antibody +": 3,
1102
  "Cell, Antibody -": 0,
1103
+ "Cell, Nostril, Antibody +": 28,
1104
  "Cell, Nostril, Antibody -": 0
1105
  },
1106
  "Cell, Nostril, Antibody -": {
 
1111
  }
1112
  },
1113
  "n_gt": 128,
1114
+ "n_pred": 144
1115
  },
1116
  "nostrils": {
1117
  "detection": {
1118
+ "tp": 164,
1119
+ "fp": 12,
1120
+ "fn": 30,
1121
+ "precision": 0.9318181818181818,
1122
+ "recall": 0.845360824742268,
1123
+ "f1": 0.8864864864864865,
1124
+ "mean_dist_matched": 0.8436248674412936
1125
+ },
1126
+ "type_accuracy": 0.9878048780487805,
1127
  "per_class": {
1128
  "Nostril in Nucleus": {
1129
+ "tp": 46,
1130
+ "fp": 5,
1131
+ "fn": 11,
1132
+ "precision": 0.9019607843137255,
1133
+ "recall": 0.8070175438596491,
1134
+ "f1": 0.8518518518518519,
1135
  "gt_count": 57,
1136
+ "pred_count": 51
1137
  },
1138
  "Nostril External of Nucleus": {
1139
+ "tp": 116,
1140
+ "fp": 9,
1141
+ "fn": 21,
1142
+ "precision": 0.928,
1143
+ "recall": 0.8467153284671532,
1144
+ "f1": 0.8854961832061069,
1145
  "gt_count": 137,
1146
+ "pred_count": 125
1147
  }
1148
  },
1149
  "n_gt": 194,
1150
+ "n_pred": 176
1151
  },
1152
  "counts": {
1153
  "Cell, Antibody +": {
1154
  "gt": 87,
1155
+ "pred": 100,
1156
+ "abs_err": 13,
1157
+ "rel_err": 0.14942528735632185
1158
  },
1159
  "Cell, Antibody -": {
1160
  "gt": 7,
1161
+ "pred": 11,
1162
+ "abs_err": 4,
1163
+ "rel_err": 0.5714285714285714
1164
  },
1165
  "Cell, Nostril, Antibody +": {
1166
  "gt": 33,
1167
+ "pred": 33,
1168
+ "abs_err": 0,
1169
+ "rel_err": 0.0
1170
  },
1171
  "Cell, Nostril, Antibody -": {
1172
  "gt": 1,
 
1176
  },
1177
  "Nostril in Nucleus": {
1178
  "gt": 57,
1179
+ "pred": 51,
1180
+ "abs_err": 6,
1181
+ "rel_err": 0.10526315789473684
1182
  },
1183
  "Nostril External of Nucleus": {
1184
  "gt": 137,
1185
+ "pred": 125,
1186
+ "abs_err": 12,
1187
+ "rel_err": 0.08759124087591241
1188
  }
1189
  },
1190
  "fp_analysis": null,
1191
  "pairs": null,
1192
  "summary": {
1193
+ "cell_det_f1": 0.8970588235294118,
1194
+ "cell_det_precision": 0.8472222222222222,
1195
+ "cell_det_recall": 0.953125,
1196
+ "cell_mean_iou": 0.7899681957823826,
1197
+ "cell_det_f1_iou30": 0.9191176470588235,
1198
+ "cell_det_f1_centroid": 0.9264705882352942,
1199
+ "cell_attr_antibody": 1.0,
1200
+ "cell_attr_nostril": 0.9754098360655737,
1201
+ "cell_attr_both": 0.9754098360655737,
1202
+ "nos_det_f1": 0.8864864864864865,
1203
+ "nos_det_precision": 0.9318181818181818,
1204
+ "nos_det_recall": 0.845360824742268,
1205
+ "nos_type_acc": 0.9878048780487805,
1206
+ "f1[Cell, Antibody +]": 0.9090909090909092,
1207
+ "f1[Cell, Antibody -]": 0.6666666666666665,
1208
+ "f1[Cell, Nostril, Antibody +]": 0.8484848484848486,
1209
  "f1[Cell, Nostril, Antibody -]": 0.0,
1210
+ "f1[Nostril in Nucleus]": 0.8518518518518519,
1211
+ "f1[Nostril External of Nucleus]": 0.8854961832061069
1212
  }
1213
  },
1214
  "F113": {
1215
  "cells": {
1216
  "detection": {
1217
+ "tp": 140,
1218
+ "fp": 27,
1219
+ "fn": 23,
1220
+ "precision": 0.8383233532934131,
1221
+ "recall": 0.8588957055214724,
1222
+ "f1": 0.8484848484848485,
1223
+ "mean_iou_matched": 0.7555449040647428,
1224
  "iou30": {
1225
+ "tp": 155,
1226
+ "fp": 12,
1227
+ "fn": 8,
1228
+ "precision": 0.9281437125748503,
1229
+ "recall": 0.950920245398773,
1230
+ "f1": 0.9393939393939393
1231
  },
1232
  "centroid": {
1233
+ "tp": 153,
1234
+ "fp": 14,
1235
+ "fn": 10,
1236
+ "precision": 0.9161676646706587,
1237
+ "recall": 0.9386503067484663,
1238
+ "f1": 0.9272727272727274
1239
  }
1240
  },
1241
  "attribute_accuracy": {
1242
  "antibody": 1.0,
1243
+ "nostril": 0.9785714285714285,
1244
+ "both": 0.9785714285714285,
1245
+ "n_matched": 140
1246
  },
1247
  "per_class": {
1248
  "Cell, Antibody +": {
1249
+ "tp": 117,
1250
+ "fp": 26,
1251
+ "fn": 21,
1252
+ "precision": 0.8181818181818182,
1253
+ "recall": 0.8478260869565217,
1254
+ "f1": 0.8327402135231317,
1255
  "gt_count": 138,
1256
+ "pred_count": 143
1257
  },
1258
  "Cell, Antibody -": {
1259
  "tp": 2,
1260
+ "fp": 1,
1261
  "fn": 1,
1262
+ "precision": 0.6666666666666666,
1263
  "recall": 0.6666666666666666,
1264
+ "f1": 0.6666666666666666,
1265
  "gt_count": 3,
1266
+ "pred_count": 3
1267
  },
1268
  "Cell, Nostril, Antibody +": {
1269
  "tp": 18,
1270
+ "fp": 3,
1271
  "fn": 4,
1272
+ "precision": 0.8571428571428571,
1273
  "recall": 0.8181818181818182,
1274
+ "f1": 0.8372093023255814,
1275
  "gt_count": 22,
1276
+ "pred_count": 21
1277
  },
1278
  "Cell, Nostril, Antibody -": {
1279
  "tp": 0,
 
1288
  },
1289
  "confusion": {
1290
  "Cell, Antibody +": {
1291
+ "Cell, Antibody +": 117,
1292
  "Cell, Antibody -": 0,
1293
  "Cell, Nostril, Antibody +": 0,
1294
  "Cell, Nostril, Antibody -": 0
 
1313
  }
1314
  },
1315
  "n_gt": 163,
1316
+ "n_pred": 167
1317
  },
1318
  "nostrils": {
1319
  "detection": {
1320
+ "tp": 29,
1321
+ "fp": 0,
1322
+ "fn": 5,
1323
+ "precision": 1.0,
1324
+ "recall": 0.8529411764705882,
1325
+ "f1": 0.9206349206349206,
1326
+ "mean_dist_matched": 0.9723494301213084
1327
+ },
1328
+ "type_accuracy": 0.9310344827586207,
1329
  "per_class": {
1330
  "Nostril in Nucleus": {
1331
+ "tp": 22,
1332
+ "fp": 1,
1333
+ "fn": 4,
1334
+ "precision": 0.9565217391304348,
1335
+ "recall": 0.8461538461538461,
1336
+ "f1": 0.8979591836734695,
1337
  "gt_count": 26,
1338
  "pred_count": 23
1339
  },
1340
  "Nostril External of Nucleus": {
1341
+ "tp": 5,
1342
  "fp": 1,
1343
+ "fn": 3,
1344
+ "precision": 0.8333333333333334,
1345
+ "recall": 0.625,
1346
+ "f1": 0.7142857142857143,
1347
  "gt_count": 8,
1348
+ "pred_count": 6
1349
  }
1350
  },
1351
  "n_gt": 34,
1352
+ "n_pred": 29
1353
  },
1354
  "counts": {
1355
  "Cell, Antibody +": {
1356
  "gt": 138,
1357
+ "pred": 143,
1358
+ "abs_err": 5,
1359
+ "rel_err": 0.036231884057971016
1360
  },
1361
  "Cell, Antibody -": {
1362
  "gt": 3,
1363
+ "pred": 3,
1364
+ "abs_err": 0,
1365
+ "rel_err": 0.0
1366
  },
1367
  "Cell, Nostril, Antibody +": {
1368
  "gt": 22,
1369
+ "pred": 21,
1370
+ "abs_err": 1,
1371
+ "rel_err": 0.045454545454545456
1372
  },
1373
  "Cell, Nostril, Antibody -": {
1374
  "gt": 0,
 
1384
  },
1385
  "Nostril External of Nucleus": {
1386
  "gt": 8,
1387
+ "pred": 6,
1388
+ "abs_err": 2,
1389
+ "rel_err": 0.25
1390
  }
1391
  },
1392
  "fp_analysis": null,
1393
  "pairs": null,
1394
  "summary": {
1395
+ "cell_det_f1": 0.8484848484848485,
1396
+ "cell_det_precision": 0.8383233532934131,
1397
+ "cell_det_recall": 0.8588957055214724,
1398
+ "cell_mean_iou": 0.7555449040647428,
1399
+ "cell_det_f1_iou30": 0.9393939393939393,
1400
+ "cell_det_f1_centroid": 0.9272727272727274,
1401
  "cell_attr_antibody": 1.0,
1402
+ "cell_attr_nostril": 0.9785714285714285,
1403
+ "cell_attr_both": 0.9785714285714285,
1404
+ "nos_det_f1": 0.9206349206349206,
1405
+ "nos_det_precision": 1.0,
1406
+ "nos_det_recall": 0.8529411764705882,
1407
+ "nos_type_acc": 0.9310344827586207,
1408
+ "f1[Cell, Antibody +]": 0.8327402135231317,
1409
+ "f1[Cell, Antibody -]": 0.6666666666666666,
1410
+ "f1[Cell, Nostril, Antibody +]": 0.8372093023255814,
1411
  "f1[Cell, Nostril, Antibody -]": 0.0,
1412
+ "f1[Nostril in Nucleus]": 0.8979591836734695,
1413
+ "f1[Nostril External of Nucleus]": 0.7142857142857143
1414
  }
1415
  },
1416
  "F114": {
1417
  "cells": {
1418
  "detection": {
1419
+ "tp": 226,
1420
+ "fp": 30,
1421
+ "fn": 16,
1422
+ "precision": 0.8828125,
1423
+ "recall": 0.9338842975206612,
1424
+ "f1": 0.9076305220883534,
1425
+ "mean_iou_matched": 0.8136301605298976,
1426
  "iou30": {
1427
+ "tp": 236,
1428
+ "fp": 20,
1429
+ "fn": 6,
1430
+ "precision": 0.921875,
1431
+ "recall": 0.9752066115702479,
1432
+ "f1": 0.9477911646586346
1433
  },
1434
  "centroid": {
1435
+ "tp": 236,
1436
+ "fp": 20,
1437
+ "fn": 6,
1438
+ "precision": 0.921875,
1439
+ "recall": 0.9752066115702479,
1440
+ "f1": 0.9477911646586346
1441
  }
1442
  },
1443
  "attribute_accuracy": {
1444
+ "antibody": 0.9911504424778761,
1445
+ "nostril": 0.9601769911504425,
1446
+ "both": 0.9513274336283186,
1447
+ "n_matched": 226
1448
  },
1449
  "per_class": {
1450
  "Cell, Antibody +": {
1451
+ "tp": 157,
1452
+ "fp": 20,
1453
+ "fn": 18,
1454
+ "precision": 0.8870056497175142,
1455
+ "recall": 0.8971428571428571,
1456
+ "f1": 0.8920454545454545,
1457
  "gt_count": 175,
1458
+ "pred_count": 177
1459
  },
1460
  "Cell, Antibody -": {
1461
+ "tp": 21,
1462
+ "fp": 9,
1463
+ "fn": 0,
1464
+ "precision": 0.7,
1465
+ "recall": 1.0,
1466
+ "f1": 0.8235294117647058,
1467
  "gt_count": 21,
1468
+ "pred_count": 30
1469
  },
1470
  "Cell, Nostril, Antibody +": {
1471
+ "tp": 36,
1472
+ "fp": 12,
1473
+ "fn": 6,
1474
+ "precision": 0.75,
1475
+ "recall": 0.8571428571428571,
1476
+ "f1": 0.7999999999999999,
1477
  "gt_count": 42,
1478
+ "pred_count": 48
1479
  },
1480
  "Cell, Nostril, Antibody -": {
1481
+ "tp": 1,
1482
  "fp": 0,
1483
+ "fn": 3,
1484
+ "precision": 1.0,
1485
+ "recall": 0.25,
1486
+ "f1": 0.4,
1487
  "gt_count": 4,
1488
+ "pred_count": 1
1489
  }
1490
  },
1491
  "confusion": {
1492
  "Cell, Antibody +": {
1493
+ "Cell, Antibody +": 157,
1494
+ "Cell, Antibody -": 2,
1495
+ "Cell, Nostril, Antibody +": 5,
1496
  "Cell, Nostril, Antibody -": 0
1497
  },
1498
  "Cell, Antibody -": {
1499
+ "Cell, Antibody +": 0,
1500
+ "Cell, Antibody -": 21,
1501
  "Cell, Nostril, Antibody +": 0,
1502
  "Cell, Nostril, Antibody -": 0
1503
  },
1504
  "Cell, Nostril, Antibody +": {
1505
+ "Cell, Antibody +": 1,
1506
  "Cell, Antibody -": 0,
1507
+ "Cell, Nostril, Antibody +": 36,
1508
  "Cell, Nostril, Antibody -": 0
1509
  },
1510
  "Cell, Nostril, Antibody -": {
1511
  "Cell, Antibody +": 0,
1512
+ "Cell, Antibody -": 3,
1513
  "Cell, Nostril, Antibody +": 0,
1514
+ "Cell, Nostril, Antibody -": 1
1515
  }
1516
  },
1517
  "n_gt": 242,
1518
+ "n_pred": 256
1519
  },
1520
  "nostrils": {
1521
  "detection": {
1522
+ "tp": 85,
1523
+ "fp": 31,
1524
+ "fn": 5,
1525
+ "precision": 0.7327586206896551,
1526
+ "recall": 0.9444444444444444,
1527
+ "f1": 0.8252427184466019,
1528
+ "mean_dist_matched": 1.0045528563892454
1529
  },
1530
  "type_accuracy": 1.0,
1531
  "per_class": {
1532
  "Nostril in Nucleus": {
1533
+ "tp": 48,
1534
+ "fp": 11,
1535
+ "fn": 4,
1536
+ "precision": 0.8135593220338984,
1537
+ "recall": 0.9230769230769231,
1538
+ "f1": 0.8648648648648649,
1539
  "gt_count": 52,
1540
+ "pred_count": 59
1541
  },
1542
  "Nostril External of Nucleus": {
1543
+ "tp": 37,
1544
+ "fp": 20,
1545
+ "fn": 1,
1546
+ "precision": 0.6491228070175439,
1547
+ "recall": 0.9736842105263158,
1548
+ "f1": 0.7789473684210527,
1549
  "gt_count": 38,
1550
+ "pred_count": 57
1551
  }
1552
  },
1553
  "n_gt": 90,
1554
+ "n_pred": 116
1555
  },
1556
  "counts": {
1557
  "Cell, Antibody +": {
1558
  "gt": 175,
1559
+ "pred": 177,
1560
+ "abs_err": 2,
1561
+ "rel_err": 0.011428571428571429
1562
  },
1563
  "Cell, Antibody -": {
1564
  "gt": 21,
1565
+ "pred": 30,
1566
+ "abs_err": 9,
1567
+ "rel_err": 0.42857142857142855
1568
  },
1569
  "Cell, Nostril, Antibody +": {
1570
  "gt": 42,
1571
+ "pred": 48,
1572
+ "abs_err": 6,
1573
+ "rel_err": 0.14285714285714285
1574
  },
1575
  "Cell, Nostril, Antibody -": {
1576
  "gt": 4,
1577
+ "pred": 1,
1578
+ "abs_err": 3,
1579
+ "rel_err": 0.75
1580
  },
1581
  "Nostril in Nucleus": {
1582
  "gt": 52,
1583
+ "pred": 59,
1584
+ "abs_err": 7,
1585
+ "rel_err": 0.1346153846153846
1586
  },
1587
  "Nostril External of Nucleus": {
1588
  "gt": 38,
1589
+ "pred": 57,
1590
+ "abs_err": 19,
1591
+ "rel_err": 0.5
1592
  }
1593
  },
1594
  "fp_analysis": null,
1595
  "pairs": null,
1596
  "summary": {
1597
+ "cell_det_f1": 0.9076305220883534,
1598
+ "cell_det_precision": 0.8828125,
1599
+ "cell_det_recall": 0.9338842975206612,
1600
+ "cell_mean_iou": 0.8136301605298976,
1601
+ "cell_det_f1_iou30": 0.9477911646586346,
1602
+ "cell_det_f1_centroid": 0.9477911646586346,
1603
+ "cell_attr_antibody": 0.9911504424778761,
1604
+ "cell_attr_nostril": 0.9601769911504425,
1605
+ "cell_attr_both": 0.9513274336283186,
1606
+ "nos_det_f1": 0.8252427184466019,
1607
+ "nos_det_precision": 0.7327586206896551,
1608
+ "nos_det_recall": 0.9444444444444444,
1609
  "nos_type_acc": 1.0,
1610
+ "f1[Cell, Antibody +]": 0.8920454545454545,
1611
+ "f1[Cell, Antibody -]": 0.8235294117647058,
1612
+ "f1[Cell, Nostril, Antibody +]": 0.7999999999999999,
1613
+ "f1[Cell, Nostril, Antibody -]": 0.4,
1614
+ "f1[Nostril in Nucleus]": 0.8648648648648649,
1615
+ "f1[Nostril External of Nucleus]": 0.7789473684210527
1616
  }
1617
  },
1618
  "F99": {
1619
  "cells": {
1620
  "detection": {
1621
+ "tp": 241,
1622
+ "fp": 35,
1623
+ "fn": 10,
1624
+ "precision": 0.8731884057971014,
1625
+ "recall": 0.9601593625498008,
1626
+ "f1": 0.9146110056925996,
1627
+ "mean_iou_matched": 0.8180266624759959,
1628
  "iou30": {
1629
+ "tp": 247,
1630
+ "fp": 29,
1631
+ "fn": 4,
1632
+ "precision": 0.894927536231884,
1633
+ "recall": 0.9840637450199203,
1634
+ "f1": 0.937381404174573
1635
  },
1636
  "centroid": {
1637
+ "tp": 246,
1638
+ "fp": 30,
1639
+ "fn": 5,
1640
+ "precision": 0.8913043478260869,
1641
+ "recall": 0.9800796812749004,
1642
+ "f1": 0.9335863377609108
1643
  }
1644
  },
1645
  "attribute_accuracy": {
1646
+ "antibody": 0.979253112033195,
1647
+ "nostril": 0.991701244813278,
1648
+ "both": 0.970954356846473,
1649
+ "n_matched": 241
1650
  },
1651
  "per_class": {
1652
  "Cell, Antibody +": {
1653
+ "tp": 173,
1654
+ "fp": 26,
1655
+ "fn": 10,
1656
+ "precision": 0.8693467336683417,
1657
+ "recall": 0.9453551912568307,
1658
+ "f1": 0.9057591623036649,
1659
  "gt_count": 183,
1660
+ "pred_count": 199
1661
  },
1662
  "Cell, Antibody -": {
1663
+ "tp": 57,
1664
+ "fp": 16,
1665
+ "fn": 5,
1666
+ "precision": 0.7808219178082192,
1667
+ "recall": 0.9193548387096774,
1668
+ "f1": 0.8444444444444444,
1669
  "gt_count": 62,
1670
+ "pred_count": 73
1671
  },
1672
  "Cell, Nostril, Antibody +": {
1673
+ "tp": 4,
1674
+ "fp": 0,
1675
+ "fn": 2,
1676
+ "precision": 1.0,
1677
+ "recall": 0.6666666666666666,
1678
+ "f1": 0.8,
1679
  "gt_count": 6,
1680
  "pred_count": 4
1681
  },
 
1692
  },
1693
  "confusion": {
1694
  "Cell, Antibody +": {
1695
+ "Cell, Antibody +": 173,
1696
  "Cell, Antibody -": 4,
1697
  "Cell, Nostril, Antibody +": 0,
1698
  "Cell, Nostril, Antibody -": 0
1699
  },
1700
  "Cell, Antibody -": {
1701
+ "Cell, Antibody +": 1,
1702
+ "Cell, Antibody -": 57,
1703
  "Cell, Nostril, Antibody +": 0,
1704
  "Cell, Nostril, Antibody -": 0
1705
  },
1706
  "Cell, Nostril, Antibody +": {
1707
  "Cell, Antibody +": 2,
1708
  "Cell, Antibody -": 0,
1709
+ "Cell, Nostril, Antibody +": 4,
1710
  "Cell, Nostril, Antibody -": 0
1711
  },
1712
  "Cell, Nostril, Antibody -": {
 
1717
  }
1718
  },
1719
  "n_gt": 251,
1720
+ "n_pred": 276
1721
  },
1722
  "nostrils": {
1723
  "detection": {
1724
+ "tp": 20,
1725
+ "fp": 2,
1726
+ "fn": 2,
1727
+ "precision": 0.9090909090909091,
1728
+ "recall": 0.9090909090909091,
1729
+ "f1": 0.9090909090909091,
1730
+ "mean_dist_matched": 0.8019220998015626
1731
  },
1732
  "type_accuracy": 1.0,
1733
  "per_class": {
 
1742
  "pred_count": 4
1743
  },
1744
  "Nostril External of Nucleus": {
1745
+ "tp": 16,
1746
+ "fp": 2,
1747
+ "fn": 0,
1748
+ "precision": 0.8888888888888888,
1749
+ "recall": 1.0,
1750
+ "f1": 0.9411764705882353,
1751
  "gt_count": 16,
1752
+ "pred_count": 18
1753
  }
1754
  },
1755
  "n_gt": 22,
1756
+ "n_pred": 22
1757
  },
1758
  "counts": {
1759
  "Cell, Antibody +": {
1760
  "gt": 183,
1761
+ "pred": 199,
1762
+ "abs_err": 16,
1763
+ "rel_err": 0.08743169398907104
1764
  },
1765
  "Cell, Antibody -": {
1766
  "gt": 62,
1767
+ "pred": 73,
1768
+ "abs_err": 11,
1769
+ "rel_err": 0.1774193548387097
1770
  },
1771
  "Cell, Nostril, Antibody +": {
1772
  "gt": 6,
 
1788
  },
1789
  "Nostril External of Nucleus": {
1790
  "gt": 16,
1791
+ "pred": 18,
1792
+ "abs_err": 2,
1793
+ "rel_err": 0.125
1794
  }
1795
  },
1796
  "fp_analysis": null,
1797
  "pairs": null,
1798
  "summary": {
1799
+ "cell_det_f1": 0.9146110056925996,
1800
+ "cell_det_precision": 0.8731884057971014,
1801
+ "cell_det_recall": 0.9601593625498008,
1802
+ "cell_mean_iou": 0.8180266624759959,
1803
+ "cell_det_f1_iou30": 0.937381404174573,
1804
+ "cell_det_f1_centroid": 0.9335863377609108,
1805
+ "cell_attr_antibody": 0.979253112033195,
1806
+ "cell_attr_nostril": 0.991701244813278,
1807
+ "cell_attr_both": 0.970954356846473,
1808
+ "nos_det_f1": 0.9090909090909091,
1809
+ "nos_det_precision": 0.9090909090909091,
1810
+ "nos_det_recall": 0.9090909090909091,
1811
  "nos_type_acc": 1.0,
1812
+ "f1[Cell, Antibody +]": 0.9057591623036649,
1813
+ "f1[Cell, Antibody -]": 0.8444444444444444,
1814
+ "f1[Cell, Nostril, Antibody +]": 0.8,
1815
  "f1[Cell, Nostril, Antibody -]": 0.0,
1816
  "f1[Nostril in Nucleus]": 0.8,
1817
+ "f1[Nostril External of Nucleus]": 0.9411764705882353
1818
  }
1819
  }
1820
  }
scripts/build_summary.py CHANGED
@@ -1,7 +1,11 @@
1
  #!/usr/bin/env python3
2
- """Write reports/SUMMARY.md from runs/cv/cv_results.json and reports/final/final_fit.json."""
 
 
 
3
  from __future__ import annotations
4
 
 
5
  import json
6
  import os
7
  import sys
@@ -15,8 +19,14 @@ def pct(x: float) -> str:
15
  return f"{100 * x:.1f}%"
16
 
17
 
18
- def main():
19
- cv = json.load(open(os.path.join(ROOT, "runs", "cv", "cv_results.json")))
 
 
 
 
 
 
20
  agg = cv["aggregate"]
21
  per = agg["per_image"]
22
  fit = json.load(open(os.path.join(ROOT, "reports", "final", "final_fit.json")))
@@ -32,6 +42,11 @@ def main():
32
  "(determinism), no-TTA / 4× / 8× test-time augmentation, 8 rotations and flips, 6 photometric perturbations, and 6 post-processing rule variants. "
33
  "The only choice made on held-out data is the rule that gives a nucleus the Nostril attribute: of three candidate rules, "
34
  "'contains an in-nucleus punctum' (the annotation protocol) scored best and is the default; see the rule-variant table in cv_report.md.", "",
 
 
 
 
 
35
  "## Headline numbers (micro-averaged over all held-out images, no TTA)", "",
36
  "| Measure | Precision | Recall | F1 |", "|---|---:|---:|---:|",
37
  f"| Nucleus detection, IoU ≥ 0.5 | {pct(det['cells']['precision'])} | {pct(det['cells']['recall'])} | {pct(det['cells']['f1'])} |",
@@ -57,21 +72,52 @@ def main():
57
  if "summary" in v:
58
  s = v["summary"]
59
  L.append(f"| {k} | {pct(s['cell_det_f1'])} | {pct(s['cell_attr_both'])} | {pct(s['nos_det_f1'])} |")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  sw = agg.get("antibody_threshold_sweep")
61
  if sw:
62
  best = max(sw["sweep"], key=lambda r: r["neg_f1"])
63
  L += ["", f"## Antibody − threshold (held-out matched nuclei: {sw['n_matched']}, of which {sw['n_negative']} negative)", "",
64
  f"Default threshold 0.5. Best Antibody − F1 in the sweep: {pct(best['neg_f1'])} at threshold {best['thr']:.2f} "
65
  f"(precision {pct(best['neg_precision'])}, recall {pct(best['neg_recall'])}); the full curve is in cv_report.md. "
66
- "The default was kept because the class is dominated by a single image (F99 holds 62 of the 102 negatives)."]
67
  L += ["", "## What limits the scores", "",
68
- "* **Touching nuclei are sometimes merged** into one outline (worst on F113); this is the main source of IoU-0.5 misses. The marker/threshold sweep (`runs/cv/postprocess_sweep.json`) is flat, so it is a network-border issue, not a post-processing one.",
69
- "* **Annotation completeness**: most unmatched predictions overlap an annotated nucleus (outline disagreement) and the rest are dimmer, smaller objects the annotator skipped; many faint red dots were likewise not annotated, which caps external-punctum precision.",
70
- "* **Antibody − is rare** (102 of 1569 nuclei, 62 in one image), and **Cell, Nostril, Antibody −** has five examples; their scores are not reliable.",
71
  "* Scores are from nine images of one experiment (35-day spinal cord, Iba1/TMEM119). New staining batches or magnifications need a check against a few annotated images.",
72
  "", "## Reproduce", "",
73
- "```bash", "python evaluate.py cv --data-dir <folder with TIFs and CVAT zips> --runs-dir runs/cv --iters 3000",
74
- "python evaluate.py final --data-dir <folder> --weights weights/cellquant.pt", "python scripts/build_summary.py", "```", ""]
 
 
75
  out = os.path.join(ROOT, "reports", "SUMMARY.md")
76
  with open(out, "w") as f:
77
  f.write("\n".join(L))
 
1
  #!/usr/bin/env python3
2
+ """Write reports/SUMMARY.md from a cross-validation run and reports/final/final_fit.json.
3
+
4
+ python scripts/build_summary.py [--cv-dir runs/cv_v2] [--recipe v2]
5
+ """
6
  from __future__ import annotations
7
 
8
+ import argparse
9
  import json
10
  import os
11
  import sys
 
19
  return f"{100 * x:.1f}%"
20
 
21
 
22
+ def main(argv=None):
23
+ ap = argparse.ArgumentParser()
24
+ ap.add_argument("--cv-dir", default="runs/cv", help="folder holding cv_results.json of the shipped recipe")
25
+ ap.add_argument("--compare", default="reports/cv/cv_results.json",
26
+ help="cv_results.json of the previous recipe for the change table ('' = none)")
27
+ a = ap.parse_args(argv)
28
+ cv = json.load(open(os.path.join(ROOT, a.cv_dir, "cv_results.json")))
29
+ prev = json.load(open(os.path.join(ROOT, a.compare)))["aggregate"] if a.compare and os.path.exists(os.path.join(ROOT, a.compare)) else None
30
  agg = cv["aggregate"]
31
  per = agg["per_image"]
32
  fit = json.load(open(os.path.join(ROOT, "reports", "final", "final_fit.json")))
 
42
  "(determinism), no-TTA / 4× / 8× test-time augmentation, 8 rotations and flips, 6 photometric perturbations, and 6 post-processing rule variants. "
43
  "The only choice made on held-out data is the rule that gives a nucleus the Nostril attribute: of three candidate rules, "
44
  "'contains an in-nucleus punctum' (the annotation protocol) scored best and is the default; see the rule-variant table in cv_report.md.", "",
45
+ "Recipe (v2, shipped): ResNet34-UNet with a full-resolution stem; targets are a 3-px inner border ring (class weight 3), "
46
+ "a per-nucleus normalised distance-to-outline map that the watershed uses as its landscape (markers = distance > 0.5), "
47
+ "an antibody head whose loss up-weights antibody-negative pixels by sqrt(positive / negative pixels) (4.5 on the full set), "
48
+ "and 25 % of training crops centred on an antibody-negative nucleus. v1 (reports/cv/) used a 2-px ring, the interior map as "
49
+ "watershed landscape and an unweighted antibody loss; the change table below compares the two under the same protocol.", "",
50
  "## Headline numbers (micro-averaged over all held-out images, no TTA)", "",
51
  "| Measure | Precision | Recall | F1 |", "|---|---:|---:|---:|",
52
  f"| Nucleus detection, IoU ≥ 0.5 | {pct(det['cells']['precision'])} | {pct(det['cells']['recall'])} | {pct(det['cells']['f1'])} |",
 
72
  if "summary" in v:
73
  s = v["summary"]
74
  L.append(f"| {k} | {pct(s['cell_det_f1'])} | {pct(s['cell_attr_both'])} | {pct(s['nos_det_f1'])} |")
75
+ if prev:
76
+ pd, pd8 = prev["detection_base"], prev["detection_tta8"]
77
+ L += ["", "## Change from v1 (same protocol, same nine held-out images; `reports/cv_v2/comparison.md` has the full table)", "",
78
+ "| Measure | v1 | v2 | Δ (points) |", "|---|---:|---:|---:|"]
79
+ def row(name, x, y):
80
+ L.append(f"| {name} | {pct(x)} | {pct(y)} | {100 * (y - x):+.1f} |")
81
+ row("Nucleus F1, IoU ≥ 0.5", pd["cells"]["f1"], det["cells"]["f1"])
82
+ row("Nucleus precision, IoU ≥ 0.5", pd["cells"]["precision"], det["cells"]["precision"])
83
+ row("Nucleus recall, IoU ≥ 0.5", pd["cells"]["recall"], det["cells"]["recall"])
84
+ row("Nucleus F1, centroid rule", pd["cells_centroid"]["f1"], det["cells_centroid"]["f1"])
85
+ row("Nucleus F1, IoU ≥ 0.5, 8× TTA", pd8["cells"]["f1"], det8["cells"]["f1"])
86
+ row("Puncta F1", pd["nostrils"]["f1"], det["nostrils"]["f1"])
87
+ for lab in ALL_LABELS:
88
+ row(f"F1 [{lab}]", prev["micro_base"][lab]["f1"], agg["micro_base"][lab]["f1"])
89
+ pr, cr = prev["rule_variants"]["rule_nos=head_cell=contains"], agg["rule_variants"]["rule_nos=head_cell=contains"]
90
+ row("Attribute accuracy, both (each model's own matched nuclei)", pr["pooled_cell_attr_both"], cr["pooled_cell_attr_both"])
91
+ row("Punctum type accuracy (matched puncta)", pr["pooled_nos_type_acc"], cr["pooled_nos_type_acc"])
92
+ for k in sorted(per):
93
+ if k in prev["per_image"]:
94
+ row(f"Nuclei F1, {k} held out", prev["per_image"][k]["base"]["cell_det_f1"], per[k]["base"]["cell_det_f1"])
95
+ n_pred_v1 = pd["cells"]["tp"] + pd["cells"]["fp"]
96
+ n_pred_v2 = det["cells"]["tp"] + det["cells"]["fp"]
97
+ n_gt = det["cells"]["tp"] + det["cells"]["fn"]
98
+ L += ["", f"v2 misses far fewer nuclei ({pd['cells']['fn']} → {det['cells']['fn']} unmatched annotated nuclei, mostly touching nuclei that v1 "
99
+ f"merged) at the cost of more unmatched predictions ({pd['cells']['fp']} → {det['cells']['fp']}): it predicts {n_pred_v2} nuclei "
100
+ f"for {n_gt} annotated ones (v1: {n_pred_v1}). The extra predictions are dim objects the annotator skipped and outline/split "
101
+ "disagreements in roughly the proportion shown in the FP table of cv_report.md. On the nuclei matched by both models the "
102
+ "attribute accuracy differs by less than one point (see comparison.md), which is within the sampling error of ~1200 nuclei; "
103
+ "the antibody-negative class gains recall (26 % → 38 % on matched nuclei) and loses precision (74 % → 59 %)."]
104
  sw = agg.get("antibody_threshold_sweep")
105
  if sw:
106
  best = max(sw["sweep"], key=lambda r: r["neg_f1"])
107
  L += ["", f"## Antibody − threshold (held-out matched nuclei: {sw['n_matched']}, of which {sw['n_negative']} negative)", "",
108
  f"Default threshold 0.5. Best Antibody − F1 in the sweep: {pct(best['neg_f1'])} at threshold {best['thr']:.2f} "
109
  f"(precision {pct(best['neg_precision'])}, recall {pct(best['neg_recall'])}); the full curve is in cv_report.md. "
110
+ f"The default was kept because the class is dominated by a single image (F99 holds 62 of the {tot['Cell, Antibody -'] + tot['Cell, Nostril, Antibody -']} negatives)."]
111
  L += ["", "## What limits the scores", "",
112
+ "* **Touching nuclei** are still the main source of IoU-0.5 misses (worst on F113, the densest image). The distance-map watershed of v2 recovers many clusters that v1 merged (v1 marker/threshold sweeps in `reports/cv/postprocess_sweep.json` had shown post-processing alone could not), but some clusters are still merged and a few nuclei are now split in two.",
113
+ "* **Annotation completeness**: most unmatched predictions overlap an annotated nucleus (outline disagreement) and the rest are dimmer, smaller objects the annotator skipped; many faint red dots were likewise not annotated, which caps external-punctum precision. v2 detects more of these dim objects than v1, so it over-counts nuclei by about 13 % on the annotated images.",
114
+ f"* **Antibody − is rare** ({tot['Cell, Antibody -'] + tot['Cell, Nostril, Antibody -']} of {n_nuc} nuclei, 62 in one image), and **Cell, Nostril, Antibody −** has five examples; their scores are not reliable. The class-balanced loss of v2 trades antibody-negative precision for recall.",
115
  "* Scores are from nine images of one experiment (35-day spinal cord, Iba1/TMEM119). New staining batches or magnifications need a check against a few annotated images.",
116
  "", "## Reproduce", "",
117
+ "```bash", "python evaluate.py cv --data-dir <folder with TIFs and CVAT zips> --runs-dir runs/cv_v2 --iters 3000",
118
+ "python evaluate.py final --data-dir <folder> --weights weights/cellquant.pt",
119
+ "python scripts/compare_cv.py reports/cv/cv_results.json runs/cv_v2/cv_results.json --out reports/cv_v2/comparison.md",
120
+ "python scripts/build_summary.py --cv-dir runs/cv_v2", "```", ""]
121
  out = os.path.join(ROOT, "reports", "SUMMARY.md")
122
  with open(out, "w") as f:
123
  f.write("\n".join(L))
scripts/compare_attributes.py ADDED
@@ -0,0 +1,113 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Like-for-like attribute comparison of two cross-validation runs.
3
+
4
+ python scripts/compare_attributes.py --data-dir DATA --runs-a <folder with fold_*.pt> --runs-b <folder> [--device mps]
5
+
6
+ Pooled attribute accuracy in cv_report.md is computed over the nuclei each model
7
+ matched (IoU >= 0.5), so a model that matches more nuclei is scored on a harder
8
+ set. This script predicts every held-out image with both fold checkpoints and
9
+ reports the antibody / Nostril attribute accuracy on the nuclei matched by BOTH,
10
+ plus the per-label count errors. It makes no choice; it only reads out numbers.
11
+ """
12
+ from __future__ import annotations
13
+
14
+ import argparse
15
+ import json
16
+ import os
17
+ import sys
18
+
19
+ import numpy as np
20
+
21
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
22
+ from cellquant.annotations import discover, load_image, rasterize # noqa: E402
23
+ from cellquant.infer import Predictor # noqa: E402
24
+ from cellquant.labels import ALL_LABELS, cell_attributes # noqa: E402
25
+ from cellquant.metrics import match_by_iou # noqa: E402
26
+ from cellquant.postprocess import PostConfig # noqa: E402
27
+
28
+
29
+ def main(argv=None):
30
+ ap = argparse.ArgumentParser()
31
+ ap.add_argument("--data-dir", required=True)
32
+ ap.add_argument("--runs-a", required=True)
33
+ ap.add_argument("--runs-b", required=True)
34
+ ap.add_argument("--names", default="v1,v2")
35
+ ap.add_argument("--device", default="auto")
36
+ ap.add_argument("--out", default="")
37
+ a = ap.parse_args(argv)
38
+ na, nb = a.names.split(",")
39
+ samples, _ = discover(a.data_dir, verbose=False)
40
+ tot = {na: {"ab": 0, "nos": 0, "both": 0}, nb: {"ab": 0, "nos": 0, "both": 0}, "n": 0}
41
+ neg = {na: {"tp": 0, "fp": 0, "fn": 0}, nb: {"tp": 0, "fp": 0, "fn": 0}}
42
+ counts = {lab: {"gt": 0, na: 0, nb: 0, f"abs_{na}": 0, f"abs_{nb}": 0} for lab in ALL_LABELS}
43
+ rows = []
44
+ for s in samples:
45
+ img = load_image(s.image_path)
46
+ R = rasterize(s.annotation, img.shape[0], img.shape[1])
47
+ preds = {}
48
+ for name, d in ((na, a.runs_a), (nb, a.runs_b)):
49
+ ck = os.path.join(d, f"fold_{s.key}.pt")
50
+ res, _ = Predictor(ck, device=a.device, tta=1).predict(img, PostConfig())
51
+ preds[name] = res
52
+ for lab in ALL_LABELS:
53
+ counts[lab][name] += res.counts[lab]
54
+ counts[lab][f"abs_{name}"] += abs(res.counts[lab] - s.annotation.count(lab))
55
+ for lab in ALL_LABELS:
56
+ counts[lab]["gt"] += s.annotation.count(lab)
57
+ m = {name: {g: p for g, p, _ in match_by_iou(R.cell_inst, preds[name].cell_inst, 0.5)} for name in preds}
58
+ common = sorted(set(m[na]) & set(m[nb]))
59
+ row = {"image": s.key, "n_common": len(common), f"n_{na}": len(m[na]), f"n_{nb}": len(m[nb])}
60
+ for name in (na, nb):
61
+ ab = ns = both = 0
62
+ for g in common:
63
+ ga, gn = cell_attributes(R.cell_labels[g])
64
+ pa, pn = cell_attributes(preds[name].cell_labels[m[name][g]])
65
+ ab += ga == pa
66
+ ns += gn == pn
67
+ both += (ga == pa) and (gn == pn)
68
+ if not ga and not pa:
69
+ neg[name]["tp"] += 1
70
+ elif ga and not pa:
71
+ neg[name]["fp"] += 1
72
+ elif not ga and pa:
73
+ neg[name]["fn"] += 1
74
+ row[f"ab_{name}"] = ab / max(1, len(common))
75
+ row[f"nos_{name}"] = ns / max(1, len(common))
76
+ row[f"both_{name}"] = both / max(1, len(common))
77
+ tot[name]["ab"] += ab
78
+ tot[name]["nos"] += ns
79
+ tot[name]["both"] += both
80
+ tot["n"] += len(common)
81
+ rows.append(row)
82
+ print(f"[{s.key}] common {len(common)}: antibody acc {na} {row[f'ab_{na}']:.3f} {nb} {row[f'ab_{nb}']:.3f} | "
83
+ f"nostril acc {row[f'nos_{na}']:.3f} {row[f'nos_{nb}']:.3f} | both {row[f'both_{na}']:.3f} {row[f'both_{nb}']:.3f}", flush=True)
84
+ n = max(1, tot["n"])
85
+ L = [f"## Attribute accuracy on the {tot['n']} held-out nuclei matched by both {na} and {nb} (IoU >= 0.5, base run)", "",
86
+ f"| | {na} | {nb} |", "|---|---:|---:|"]
87
+ for k, label in (("ab", "Antibody +/− correct"), ("nos", "Nostril attribute correct"), ("both", "Both correct")):
88
+ L.append(f"| {label} | {100*tot[na][k]/n:.1f}% | {100*tot[nb][k]/n:.1f}% |")
89
+ for name in (na, nb):
90
+ t = neg[name]
91
+ p = t["tp"] / max(1, t["tp"] + t["fp"])
92
+ r = t["tp"] / max(1, t["tp"] + t["fn"])
93
+ f = 2 * p * r / max(1e-9, p + r)
94
+ L.append(f"| Antibody − on this set ({name}): TP / FP / FN, precision, recall, F1 | {t['tp']} / {t['fp']} / {t['fn']}, {100*p:.1f}%, {100*r:.1f}%, {100*f:.1f}% | |")
95
+ L += ["", f"| Image | matched by both | {na} antibody acc | {nb} antibody acc | {na} Nostril acc | {nb} Nostril acc |", "|---|---:|---:|---:|---:|---:|"]
96
+ for r in rows:
97
+ L.append(f"| {r['image']} | {r['n_common']} | {100*r[f'ab_{na}']:.1f}% | {100*r[f'ab_{nb}']:.1f}% | {100*r[f'nos_{na}']:.1f}% | {100*r[f'nos_{nb}']:.1f}% |")
98
+ L += ["", "## Counts summed over the nine held-out images (and summed absolute per-image count error)", "",
99
+ f"| Label | GT | {na} | {nb} | abs error {na} | abs error {nb} |", "|---|---:|---:|---:|---:|---:|"]
100
+ for lab in ALL_LABELS:
101
+ c = counts[lab]
102
+ L.append(f"| {lab} | {c['gt']} | {c[na]} | {c[nb]} | {c[f'abs_{na}']} | {c[f'abs_{nb}']} |")
103
+ L.append("")
104
+ text = "\n".join(L)
105
+ print(text)
106
+ if a.out:
107
+ with open(a.out, "a") as f:
108
+ f.write("\n" + text)
109
+ print("appended to", a.out)
110
+
111
+
112
+ if __name__ == "__main__":
113
+ main()
scripts/compare_cv.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Compare two cross-validation runs (cv_results.json files) on the measures that
3
+ decide whether a new training recipe replaces the shipped weights.
4
+
5
+ python scripts/compare_cv.py reports/cv/cv_results.json runs/cv_v2/cv_results.json [--out reports/cv_v2/comparison.md]
6
+
7
+ Nothing here looks at the images or the predictions; it only reads the two
8
+ aggregates, so it cannot tune anything on the held-out data.
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import argparse
13
+ import json
14
+ import os
15
+ import sys
16
+
17
+ sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
18
+ from cellquant.labels import ALL_LABELS # noqa: E402
19
+
20
+
21
+ def pct(x: float) -> str:
22
+ return f"{100 * x:.1f}%"
23
+
24
+
25
+ def delta(a: float, b: float) -> str:
26
+ d = 100 * (b - a)
27
+ return f"{d:+.1f}"
28
+
29
+
30
+ def headline_rows(agg: dict) -> dict[str, float]:
31
+ det, det8 = agg["detection_base"], agg["detection_tta8"]
32
+ rows = {
33
+ "Nucleus F1, IoU ≥ 0.5": det["cells"]["f1"],
34
+ "Nucleus precision, IoU ≥ 0.5": det["cells"]["precision"],
35
+ "Nucleus recall, IoU ≥ 0.5": det["cells"]["recall"],
36
+ "Nucleus F1, IoU ≥ 0.3": det["cells_iou30"]["f1"],
37
+ "Nucleus F1, centroid rule": det["cells_centroid"]["f1"],
38
+ "Nucleus F1, IoU ≥ 0.5, 8× TTA": det8["cells"]["f1"],
39
+ "Puncta F1": det["nostrils"]["f1"],
40
+ "Puncta F1, 8× TTA": det8["nostrils"]["f1"],
41
+ }
42
+ # attribute accuracies pooled over matched nuclei (weights = matched count per image)
43
+ rule = agg["rule_variants"]["rule_nos=head_cell=contains"]
44
+ rows["Attribute accuracy (both, matched nuclei)"] = rule["pooled_cell_attr_both"]
45
+ rows["Punctum type accuracy (matched puncta)"] = rule["pooled_nos_type_acc"]
46
+ for lab in ALL_LABELS:
47
+ rows[f"F1 [{lab}]"] = agg["micro_base"][lab]["f1"]
48
+ for lab in ("Cell, Antibody -",):
49
+ r = agg["micro_base"][lab]
50
+ rows[f"Precision [{lab}]"] = r["precision"]
51
+ rows[f"Recall [{lab}]"] = r["recall"]
52
+ return rows
53
+
54
+
55
+ def main(argv=None):
56
+ ap = argparse.ArgumentParser()
57
+ ap.add_argument("old")
58
+ ap.add_argument("new")
59
+ ap.add_argument("--names", default="v1,v2")
60
+ ap.add_argument("--out", default="")
61
+ a = ap.parse_args(argv)
62
+ A = json.load(open(a.old))["aggregate"]
63
+ B = json.load(open(a.new))["aggregate"]
64
+ na, nb = a.names.split(",")
65
+ L = [f"# Cross-validation comparison: {na} vs {nb}", "",
66
+ f"`{a.old}` vs `{a.new}`; micro-averaged over the held-out images (base run, no TTA unless stated).", "",
67
+ f"| Measure | {na} | {nb} | Δ (points) |", "|---|---:|---:|---:|"]
68
+ ra, rb = headline_rows(A), headline_rows(B)
69
+ for k in ra:
70
+ L.append(f"| {k} | {pct(ra[k])} | {pct(rb[k])} | {delta(ra[k], rb[k])} |")
71
+ L += ["", "## Per held-out image", "",
72
+ f"| Image | Nuclei F1 {na} | Nuclei F1 {nb} | Δ | Centroid F1 {na} | Centroid F1 {nb} | Antibody acc {na} | Antibody acc {nb} | Puncta F1 {na} | Puncta F1 {nb} |",
73
+ "|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|"]
74
+ for k in sorted(set(A["per_image"]) | set(B["per_image"])):
75
+ sa, sb = A["per_image"].get(k, {}).get("base"), B["per_image"].get(k, {}).get("base")
76
+ if not sa or not sb:
77
+ continue
78
+ L.append(f"| {k} | {pct(sa['cell_det_f1'])} | {pct(sb['cell_det_f1'])} | {delta(sa['cell_det_f1'], sb['cell_det_f1'])} | "
79
+ f"{pct(sa['cell_det_f1_centroid'])} | {pct(sb['cell_det_f1_centroid'])} | {pct(sa['cell_attr_antibody'])} | {pct(sb['cell_attr_antibody'])} | "
80
+ f"{pct(sa['nos_det_f1'])} | {pct(sb['nos_det_f1'])} |")
81
+ L += ["", "## Robustness (nuclei F1 over the 14 rotated / perturbed runs per image)", "",
82
+ f"| Image | {na} mean ± sd | {nb} mean ± sd |", "|---|---|---|"]
83
+ for k in sorted(set(A["per_image"]) & set(B["per_image"])):
84
+ ra_, rb_ = A["per_image"][k]["robustness"]["cell_det_f1"], B["per_image"][k]["robustness"]["cell_det_f1"]
85
+ L.append(f"| {k} | {100*ra_['mean']:.1f} ± {100*ra_['std']:.1f} | {100*rb_['mean']:.1f} ± {100*rb_['std']:.1f} |")
86
+ L += ["", "## Antibody-negative class", "",
87
+ f"| | {na} | {nb} |", "|---|---:|---:|"]
88
+ for lab in ("Cell, Antibody -", "Cell, Nostril, Antibody -"):
89
+ x, y = A["micro_base"][lab], B["micro_base"][lab]
90
+ L.append(f"| {lab}: GT / predicted / TP | {x['gt_count']} / {x['pred_count']} / {x['tp']} | {y['gt_count']} / {y['pred_count']} / {y['tp']} |")
91
+ sa, sb = A.get("antibody_threshold_sweep"), B.get("antibody_threshold_sweep")
92
+ if sa and sb:
93
+ ba = max(sa["sweep"], key=lambda r: r["neg_f1"])
94
+ bb = max(sb["sweep"], key=lambda r: r["neg_f1"])
95
+ at = [r for r in sa["sweep"] if abs(r["thr"] - 0.5) < 1e-6][0]
96
+ bt = [r for r in sb["sweep"] if abs(r["thr"] - 0.5) < 1e-6][0]
97
+ L.append(f"| Antibody − F1 among matched nuclei at the default threshold 0.5 | {pct(at['neg_f1'])} (P {pct(at['neg_precision'])}, R {pct(at['neg_recall'])}) | {pct(bt['neg_f1'])} (P {pct(bt['neg_precision'])}, R {pct(bt['neg_recall'])}) |")
98
+ L.append(f"| Best threshold in the sweep (not used) | {pct(ba['neg_f1'])} at {ba['thr']:.2f} | {pct(bb['neg_f1'])} at {bb['thr']:.2f} |")
99
+ L.append("")
100
+ text = "\n".join(L)
101
+ print(text)
102
+ if a.out:
103
+ os.makedirs(os.path.dirname(a.out), exist_ok=True)
104
+ with open(a.out, "w") as f:
105
+ f.write(text)
106
+ print("wrote", a.out)
107
+
108
+
109
+ if __name__ == "__main__":
110
+ main()
tests/test_data.py CHANGED
@@ -19,8 +19,9 @@ def test_make_targets_layout():
19
  T = make_targets(_raster())
20
  assert set(np.unique(T["cell3"])) == {0, 1, 2}
21
  assert ((T["cell3"] > 0) == (T["cellmask"] > 0)).all()
22
- # border ring is 2 px wide: a 20x20 square has 20*20 - 16*16 = 144 border px
23
- assert int((T["cell3"][10:30, 10:30] == 2).sum()) == 144
 
24
  assert T["antibody"][15, 15] == 1 and T["antibody"][40, 40] == 0
25
  assert T["cellnos"][15, 15] == 1 and T["cellnos"][40, 40] == 0
26
  assert T["nos3"][21, 21] == 1 and T["nos3"][5, 50] == 2 and T["nos3"][0, 0] == 0
 
19
  T = make_targets(_raster())
20
  assert set(np.unique(T["cell3"])) == {0, 1, 2}
21
  assert ((T["cell3"] > 0) == (T["cellmask"] > 0)).all()
22
+ # border ring is 3 px wide: a 20x20 square has 20*20 - 14*14 = 204 border px
23
+ assert int((T["cell3"][10:30, 10:30] == 2).sum()) == 204
24
+ assert T["dist"].shape == T["cell3"].shape and T["dist"].dtype == np.float32
25
  assert T["antibody"][15, 15] == 1 and T["antibody"][40, 40] == 0
26
  assert T["cellnos"][15, 15] == 1 and T["cellnos"][40, 40] == 0
27
  assert T["nos3"][21, 21] == 1 and T["nos3"][5, 50] == 2 and T["nos3"][0, 0] == 0
tests/test_v2_recipe.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tests for the v2 recipe: distance channel, 3-px border, balanced antibody loss,
2
+ oversampling of antibody-negative nuclei, and backward compatibility with 8-channel
3
+ checkpoints."""
4
+ import numpy as np
5
+ import torch
6
+
7
+ from cellquant.annotations import RasterAnnotation
8
+ from cellquant.data import AugConfig, PatchSampler, Prefetcher, distance_target, make_targets, negative_nucleus_points
9
+ from cellquant.infer import Predictor
10
+ from cellquant.losses import LAMBDA_DIST, masked_bce, multitask_loss
11
+ from cellquant.model import DIST_CH, N_OUT, N_OUT_LEGACY, CellUNet, load_checkpoint, save_checkpoint
12
+ from cellquant.postprocess import PostConfig, analyze, logits_to_probs, segment_cells
13
+
14
+
15
+ def _disc(h, w, cy, cx, r):
16
+ yy, xx = np.mgrid[:h, :w]
17
+ return (yy - cy) ** 2 + (xx - cx) ** 2 <= r * r
18
+
19
+
20
+ def test_distance_target_peaks_at_one_and_dips_between_touching_nuclei():
21
+ inst = np.zeros((40, 60), np.int32)
22
+ inst[10:30, 10:30] = 1
23
+ inst[10:30, 30:50] = 2 # shares a full edge with instance 1
24
+ d = distance_target(inst)
25
+ assert d.dtype == np.float32 and d.min() == 0 and (d[inst == 0] == 0).all()
26
+ assert np.isclose(d[inst == 1].max(), 1.0) and np.isclose(d[inst == 2].max(), 1.0)
27
+ # the contact line is as low as the outer outline: 1 px / 10 px
28
+ assert np.isclose(d[20, 29], 0.1) and np.isclose(d[20, 30], 0.1) and np.isclose(d[20, 10], 0.1)
29
+ assert d[20, 19] > 0.9 and d[20, 40] > 0.9
30
+ # the image edge counts as outside
31
+ edge = np.zeros((20, 20), np.int32)
32
+ edge[0:10, 0:10] = 1
33
+ de = distance_target(edge)
34
+ assert np.isclose(de[0, 0], 0.2) and np.isclose(de[4, 4], 1.0) and np.isclose(de[9, 9], 0.2)
35
+
36
+
37
+ def test_distance_watershed_splits_what_the_interior_map_merges():
38
+ h = w = 96
39
+ d1, d2 = _disc(h, w, 48, 30, 14), _disc(h, w, 48, 56, 14)
40
+ fg = d1 | d2
41
+ cell = np.zeros((3, h, w), np.float32)
42
+ cell[1][fg] = 0.9 # one solid interior blob, no border between the discs
43
+ cell[0] = 1 - cell[1]
44
+ inst_plain = segment_cells(cell, PostConfig())
45
+ assert inst_plain.max() == 1 # merged without the distance map
46
+ gt = np.zeros((h, w), np.int32)
47
+ gt[d1] = 1
48
+ gt[d2 & ~d1] = 2
49
+ dist = distance_target(gt)
50
+ inst_dist = segment_cells(cell, PostConfig(), dist)
51
+ assert inst_dist.max() == 2
52
+ areas = np.bincount(inst_dist.ravel())[1:]
53
+ assert all(450 <= a <= 750 for a in areas)
54
+ # analyze() picks the distance map up from the probability dict
55
+ probs = {"cell": cell, "antibody": np.full((h, w), 0.9, np.float32), "cellnos": np.zeros((h, w), np.float32),
56
+ "nos": np.stack([np.ones((h, w)), np.zeros((h, w)), np.zeros((h, w))]).astype(np.float32), "dist": dist}
57
+ assert len(analyze(probs, PostConfig()).cells) == 2
58
+ del probs["dist"]
59
+ assert len(analyze(probs, PostConfig()).cells) == 1
60
+
61
+
62
+ def test_logits_to_probs_with_and_without_distance_channel():
63
+ rng = np.random.default_rng(0)
64
+ p8 = logits_to_probs(rng.normal(size=(8, 16, 16)).astype(np.float32))
65
+ assert "dist" not in p8
66
+ p9 = logits_to_probs(rng.normal(size=(9, 16, 16)).astype(np.float32) * 3)
67
+ assert p9["dist"].shape == (16, 16) and p9["dist"].min() >= 0 and p9["dist"].max() <= 1
68
+ assert np.allclose(p9["cell"].sum(0), 1, atol=1e-5)
69
+
70
+
71
+ def test_predictor_old_and_new_checkpoints(tmp_path):
72
+ img = (np.random.default_rng(0).random((70, 90, 3)) * 80).astype(np.uint8)
73
+ old = CellUNet("resnet34", pretrained=False, n_out=N_OUT_LEGACY).eval()
74
+ save_checkpoint(old, str(tmp_path / "old.pt"))
75
+ m_old, meta = load_checkpoint(str(tmp_path / "old.pt"))
76
+ assert meta["n_out"] == N_OUT_LEGACY and m_old.head.out_channels == N_OUT_LEGACY
77
+ p_old = Predictor(str(tmp_path / "old.pt"), device="cpu")
78
+ pr = p_old.probs(img)
79
+ assert not p_old.has_dist and "dist" not in pr and pr["cell"].shape == (3, 70, 90)
80
+ new = CellUNet("resnet34", pretrained=False).eval()
81
+ assert new.head.out_channels == N_OUT == DIST_CH + 1
82
+ p_new = Predictor(new, device="cpu")
83
+ pr = p_new.probs(img)
84
+ assert p_new.has_dist and pr["dist"].shape == (70, 90) and pr["dist"].min() >= 0 and pr["dist"].max() <= 1
85
+ # tiled path and TTA keep the channel
86
+ tiled = Predictor(new, device="cpu", tile=64, overlap=16, max_whole=32).probs(img)
87
+ assert tiled["dist"].shape == (70, 90)
88
+ assert Predictor(new, device="cpu", tta=4).probs(img)["dist"].shape == (70, 90)
89
+ # a whole prediction runs end to end on the new layout
90
+ res, probs = p_new.predict(img, PostConfig())
91
+ assert "dist" in probs and isinstance(res.cells, list)
92
+
93
+
94
+ def test_loss_has_distance_term_and_negative_weight_balances_antibody():
95
+ rng = np.random.default_rng(0)
96
+ B, H, W = 2, 32, 32
97
+ inst = np.zeros((H, W), np.int32)
98
+ inst[4:14, 4:14] = 1
99
+ inst[18:28, 18:28] = 2
100
+ R = RasterAnnotation(inst, ["", "Cell, Antibody +", "Cell, Antibody -"], np.zeros((H, W), np.int32), [""])
101
+ T = make_targets(R)
102
+ t = {k: torch.from_numpy(np.stack([v, v])) for k, v in T.items()}
103
+ out9 = torch.from_numpy(rng.normal(size=(B, 9, H, W)).astype(np.float32))
104
+ total9, parts9 = multitask_loss(out9, t)
105
+ assert "mse_dist" in parts9 and parts9["mse_dist"] > 0
106
+ assert np.isclose(parts9["total"], sum(v for k, v in parts9.items() if k not in ("total", "mse_dist")) + LAMBDA_DIST * parts9["mse_dist"], atol=1e-4)
107
+ out8 = out9[:, :8]
108
+ _, parts8 = multitask_loss(out8, t)
109
+ assert "mse_dist" not in parts8
110
+ # a perfect distance prediction removes the term
111
+ perfect = out9.clone()
112
+ perfect[:, DIST_CH] = t["dist"]
113
+ assert multitask_loss(perfect, t)[1]["mse_dist"] < 1e-6
114
+ # the antibody head is confidently positive everywhere: up-weighting negatives raises the loss
115
+ logit = torch.full((B, H, W), 3.0)
116
+ mask = t["cellmask"]
117
+ l1 = masked_bce(logit, t["antibody"], mask, neg_weight=1.0)
118
+ l5 = masked_bce(logit, t["antibody"], mask, neg_weight=5.0)
119
+ assert l5 > l1
120
+ # ... and leaves an all-positive batch unchanged
121
+ assert torch.isclose(masked_bce(logit, torch.ones_like(logit), mask, neg_weight=5.0),
122
+ masked_bce(logit, torch.ones_like(logit), mask, neg_weight=1.0))
123
+
124
+
125
+ def test_focus_sampling_puts_a_negative_nucleus_in_every_crop():
126
+ H = W = 128
127
+ inst = np.zeros((H, W), np.int32)
128
+ inst[100:110, 10:20] = 1 # antibody-negative nucleus in one corner
129
+ inst[20:30, 90:100] = 2 # antibody-positive one in the opposite corner
130
+ R = RasterAnnotation(inst, ["", "Cell, Antibody -", "Cell, Antibody +"], np.zeros((H, W), np.int32), [""])
131
+ pts = negative_nucleus_points(R)
132
+ assert pts.shape == (1, 2) and np.allclose(pts[0], (104.5, 14.5))
133
+ assert negative_nucleus_points(RasterAnnotation(inst, ["", "Cell, Antibody +", "Cell, Antibody +"], np.zeros((H, W), np.int32), [""])).shape == (0, 2)
134
+ img = (np.random.default_rng(0).random((H, W, 3)) * 255).astype(np.uint8)
135
+ T = make_targets(R)
136
+ cfg = AugConfig(crop=32, p_focus=1.0)
137
+ _, t = PatchSampler([img], [T], cfg, seed=1, focus=[pts]).batch(16)
138
+ neg = (t["antibody"] < 0.5) & (t["cellmask"] > 0)
139
+ assert all(neg[i].any() for i in range(16))
140
+ # without focus points (or p_focus = 0) crops are placed at random, so the 10x10 nucleus is often missed
141
+ _, t0 = PatchSampler([img], [T], AugConfig(crop=32, p_focus=0.0), seed=1, focus=[pts]).batch(16)
142
+ neg0 = (t0["antibody"] < 0.5) & (t0["cellmask"] > 0)
143
+ assert sum(neg0[i].any() for i in range(16)) < 16
144
+ # the prefetcher passes the points through and stays deterministic
145
+ def stream(seed):
146
+ pf = Prefetcher([img], [T], cfg, batch=2, seed=seed, workers=2, depth=4, focus=[pts])
147
+ out = [pf.next()[1]["antibody"].numpy().copy() for _ in range(4)]
148
+ pf.close()
149
+ return out
150
+ a, b = stream(2), stream(2)
151
+ assert all(np.array_equal(x, y) for x, y in zip(a, b))
152
+ assert all(((x < 0.5) & (x >= 0)).any() for x in a) # cannot see cellmask here, but focus crops always contain the nucleus
train.py CHANGED
@@ -22,7 +22,7 @@ import torch
22
 
23
  sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
24
  from cellquant.annotations import discover, load_image, rasterize # noqa: E402
25
- from cellquant.data import AugConfig, Prefetcher, make_targets # noqa: E402
26
  from cellquant.infer import Predictor, pick_device # noqa: E402
27
  from cellquant.losses import multitask_loss # noqa: E402
28
  from cellquant.metrics import evaluate_cells, evaluate_nostrils # noqa: E402
@@ -48,9 +48,24 @@ def parse_args(argv=None):
48
  ap.add_argument("--log-every", type=int, default=50)
49
  ap.add_argument("--val-every", type=int, default=500)
50
  ap.add_argument("--workers", type=int, default=3, help="augmentation threads")
 
 
 
 
51
  return ap.parse_args(argv)
52
 
53
 
 
 
 
 
 
 
 
 
 
 
 
54
  def lr_at(it: int, total: int, base: float, warmup: int) -> float:
55
  if it < warmup:
56
  return base * (it + 1) / warmup
@@ -85,13 +100,18 @@ def main(argv=None):
85
  if a.holdout and not held:
86
  raise SystemExit(f"holdout {a.holdout!r} not among {[s.key for s in samples]}")
87
  print(f"device={device} train={[s.key for s in train]} holdout={[s.key for s in held]}")
88
- images, targets = [], []
89
  for s in train:
90
  img = load_image(s.image_path)
91
  R = rasterize(s.annotation, img.shape[0], img.shape[1])
92
  images.append(img)
93
  targets.append(make_targets(R))
94
- sampler = Prefetcher(images, targets, AugConfig(crop=a.crop), batch=a.batch, seed=a.seed, workers=a.workers)
 
 
 
 
 
95
  held_img = held_R = None
96
  if held:
97
  held_img = load_image(held[0].image_path)
@@ -109,7 +129,7 @@ def main(argv=None):
109
  model.train()
110
  x, t = sampler.next()
111
  out = model(x.to(device))
112
- loss, parts = multitask_loss(out, t)
113
  opt.zero_grad(set_to_none=True)
114
  loss.backward()
115
  torch.nn.utils.clip_grad_norm_(model.parameters(), 5.0)
@@ -125,7 +145,7 @@ def main(argv=None):
125
  history["train"].append(avg)
126
  print(f"it {it+1:5d} loss {avg['total']:.3f} cell {avg['ce_cell']:.3f}/{avg['dice_cell']:.3f} "
127
  f"nos {avg['ce_nos']:.3f}/{avg['dice_nos']:.3f} ab {avg['bce_ab']:.3f} cn {avg['bce_cn']:.3f} "
128
- f"lr {lr:.2e} {avg['elapsed_s']:.0f}s", flush=True)
129
  if held and ((it + 1) % a.val_every == 0 or it + 1 == a.iters):
130
  model.eval()
131
  v = quick_val(model, held_img, held_R, device)
@@ -137,7 +157,7 @@ def main(argv=None):
137
  model.eval().cpu()
138
  os.makedirs(os.path.dirname(os.path.abspath(a.out)), exist_ok=True)
139
  save_checkpoint(model, a.out, {
140
- "config": vars(a),
141
  "train_keys": [s.key for s in train],
142
  "holdout": a.holdout,
143
  "history": history,
 
22
 
23
  sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
24
  from cellquant.annotations import discover, load_image, rasterize # noqa: E402
25
+ from cellquant.data import AugConfig, Prefetcher, make_targets, negative_nucleus_points # noqa: E402
26
  from cellquant.infer import Predictor, pick_device # noqa: E402
27
  from cellquant.losses import multitask_loss # noqa: E402
28
  from cellquant.metrics import evaluate_cells, evaluate_nostrils # noqa: E402
 
48
  ap.add_argument("--log-every", type=int, default=50)
49
  ap.add_argument("--val-every", type=int, default=500)
50
  ap.add_argument("--workers", type=int, default=3, help="augmentation threads")
51
+ ap.add_argument("--p-focus", type=float, default=AugConfig.p_focus,
52
+ help="share of crops forced to contain an antibody-negative nucleus (0 = off)")
53
+ ap.add_argument("--ab-neg-weight", default="auto",
54
+ help="loss weight of antibody-negative nucleus pixels: 'auto' = sqrt(pos px / neg px) clipped to [1, 5], or a number")
55
  return ap.parse_args(argv)
56
 
57
 
58
+ def antibody_negative_weight(targets: list[dict], spec: str = "auto") -> float:
59
+ """Class-balancing weight for the antibody head (see losses.masked_bce)."""
60
+ if spec != "auto":
61
+ return float(spec)
62
+ pos = sum(float(((t["antibody"] > 0.5) & (t["cellmask"] > 0)).sum()) for t in targets)
63
+ neg = sum(float(((t["antibody"] < 0.5) & (t["cellmask"] > 0)).sum()) for t in targets)
64
+ if neg == 0:
65
+ return 1.0
66
+ return float(np.clip(np.sqrt(pos / neg), 1.0, 5.0))
67
+
68
+
69
  def lr_at(it: int, total: int, base: float, warmup: int) -> float:
70
  if it < warmup:
71
  return base * (it + 1) / warmup
 
100
  if a.holdout and not held:
101
  raise SystemExit(f"holdout {a.holdout!r} not among {[s.key for s in samples]}")
102
  print(f"device={device} train={[s.key for s in train]} holdout={[s.key for s in held]}")
103
+ images, targets, focus = [], [], []
104
  for s in train:
105
  img = load_image(s.image_path)
106
  R = rasterize(s.annotation, img.shape[0], img.shape[1])
107
  images.append(img)
108
  targets.append(make_targets(R))
109
+ focus.append(negative_nucleus_points(R))
110
+ ab_neg_weight = antibody_negative_weight(targets, a.ab_neg_weight)
111
+ print(f"antibody-negative nuclei in training set: {sum(len(f) for f in focus)} "
112
+ f"(loss weight {ab_neg_weight:.2f}, focus share {a.p_focus:.2f})")
113
+ sampler = Prefetcher(images, targets, AugConfig(crop=a.crop, p_focus=a.p_focus), batch=a.batch, seed=a.seed,
114
+ workers=a.workers, focus=focus if a.p_focus > 0 else None)
115
  held_img = held_R = None
116
  if held:
117
  held_img = load_image(held[0].image_path)
 
129
  model.train()
130
  x, t = sampler.next()
131
  out = model(x.to(device))
132
+ loss, parts = multitask_loss(out, t, ab_neg_weight=ab_neg_weight)
133
  opt.zero_grad(set_to_none=True)
134
  loss.backward()
135
  torch.nn.utils.clip_grad_norm_(model.parameters(), 5.0)
 
145
  history["train"].append(avg)
146
  print(f"it {it+1:5d} loss {avg['total']:.3f} cell {avg['ce_cell']:.3f}/{avg['dice_cell']:.3f} "
147
  f"nos {avg['ce_nos']:.3f}/{avg['dice_nos']:.3f} ab {avg['bce_ab']:.3f} cn {avg['bce_cn']:.3f} "
148
+ f"dist {avg.get('mse_dist', 0.0):.4f} lr {lr:.2e} {avg['elapsed_s']:.0f}s", flush=True)
149
  if held and ((it + 1) % a.val_every == 0 or it + 1 == a.iters):
150
  model.eval()
151
  v = quick_val(model, held_img, held_R, device)
 
157
  model.eval().cpu()
158
  os.makedirs(os.path.dirname(os.path.abspath(a.out)), exist_ok=True)
159
  save_checkpoint(model, a.out, {
160
+ "config": {**vars(a), "ab_neg_weight_used": ab_neg_weight},
161
  "train_keys": [s.key for s in train],
162
  "holdout": a.holdout,
163
  "history": history,
weights/cellquant.pt CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:b6d252d9f4c03aedcf717649cc766fb47a503acc7cca8135b8d43bc684838959
3
- size 98290507
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0a9ea444de463739cab0de9b17061fb9931fa338d8975c984c23cc826fc35042
3
+ size 98292471
weights/cellquant_history.json CHANGED
@@ -1,724 +1,784 @@
1
  {
2
  "train": [
3
  {
4
- "ce_cell": 0.9070329773426056,
5
- "dice_cell": 0.8730328452587127,
6
- "ce_nos": 1.0830040109157562,
7
- "dice_nos": 0.9898272252082825,
8
- "bce_ab": 0.524188295006752,
9
- "bce_cn": 0.6183760815858841,
10
- "total": 4.9954613590240475,
 
11
  "iter": 50,
12
  "lr": 0.00015,
13
- "elapsed_s": 59.566773891448975
14
  },
15
  {
16
- "ce_cell": 0.7300815117359162,
17
- "dice_cell": 0.8212458372116089,
18
- "ce_nos": 0.7789127671718598,
19
- "dice_nos": 0.9821323096752167,
20
- "bce_ab": 0.2914739960432053,
21
- "bce_cn": 0.3936499571800232,
22
- "total": 3.9974963092803955,
 
23
  "iter": 100,
24
  "lr": 0.0003,
25
- "elapsed_s": 109.73091292381287
26
  },
27
  {
28
- "ce_cell": 0.5734736859798432,
29
- "dice_cell": 0.7798113775253296,
30
- "ce_nos": 0.574396123290062,
31
- "dice_nos": 0.9748807692527771,
32
- "bce_ab": 0.23322827845811844,
33
- "bce_cn": 0.33575250566005704,
34
- "total": 3.4715427684783937,
 
35
  "iter": 150,
36
  "lr": 0.00029979294712266676,
37
- "elapsed_s": 160.73545289039612
38
  },
39
  {
40
- "ce_cell": 0.44446682393550874,
41
- "dice_cell": 0.7253605389595031,
42
- "ce_nos": 0.4329249274730682,
43
- "dice_nos": 0.9701189231872559,
44
- "bce_ab": 0.20956841707229615,
45
- "bce_cn": 0.32494749456644056,
46
- "total": 3.107387113571167,
 
47
  "iter": 200,
48
  "lr": 0.0002991554114956718,
49
- "elapsed_s": 211.49982213974
50
  },
51
  {
52
- "ce_cell": 0.35687846958637237,
53
- "dice_cell": 0.6721045792102813,
54
- "ce_nos": 0.32840390503406525,
55
- "dice_nos": 0.9639902281761169,
56
- "bce_ab": 0.17408027544617652,
57
- "bce_cn": 0.3147788044810295,
58
- "total": 2.810236248970032,
 
59
  "iter": 250,
60
  "lr": 0.00029808917686451123,
61
- "elapsed_s": 255.0301797389984
62
  },
63
  {
64
- "ce_cell": 0.30268543839454654,
65
- "dice_cell": 0.6246984755992889,
66
- "ce_nos": 0.24472756624221803,
67
- "dice_nos": 0.9488238263130188,
68
- "bce_ab": 0.1679477596282959,
69
- "bce_cn": 0.264477858543396,
70
- "total": 2.5533609533309938,
 
71
  "iter": 300,
72
  "lr": 0.00029659737067906796,
73
- "elapsed_s": 305.7051999568939
74
  },
75
  {
76
- "ce_cell": 0.2692413151264191,
77
- "dice_cell": 0.5903848171234131,
78
- "ce_nos": 0.18806805282831193,
79
- "dice_nos": 0.9169448506832123,
80
- "bce_ab": 0.1362369504570961,
81
- "bce_cn": 0.2647552129626274,
82
- "total": 2.365631227493286,
 
83
  "iter": 350,
84
  "lr": 0.00029468436866391876,
85
- "elapsed_s": 356.85323691368103
86
  },
87
  {
88
- "ce_cell": 0.24572547733783723,
89
- "dice_cell": 0.5541446590423584,
90
- "ce_nos": 0.14375351637601852,
91
- "dice_nos": 0.8989665198326111,
92
- "bce_ab": 0.1442735657095909,
93
- "bce_cn": 0.25428884506225585,
94
- "total": 2.241152620315552,
 
95
  "iter": 400,
96
  "lr": 0.0002923557819835801,
97
- "elapsed_s": 408.5768549442291
98
  },
99
  {
100
- "ce_cell": 0.23625801086425782,
101
- "dice_cell": 0.5364066225290298,
102
- "ce_nos": 0.11115477636456489,
103
- "dice_nos": 0.87745032787323,
104
- "bce_ab": 0.1183611673116684,
105
- "bce_cn": 0.22456467717885972,
106
- "total": 2.1041955590248107,
 
107
  "iter": 450,
108
  "lr": 0.00028961844078399557,
109
- "elapsed_s": 459.0741550922394
110
  },
111
  {
112
- "ce_cell": 0.21822448045015336,
113
- "dice_cell": 0.5127934575080871,
114
- "ce_nos": 0.0872543377429247,
115
- "dice_nos": 0.8334435498714448,
116
- "bce_ab": 0.1045101822167635,
117
- "bce_cn": 0.2290149673819542,
118
- "total": 1.9852409744262696,
 
119
  "iter": 500,
120
  "lr": 0.0002864803741585413,
121
- "elapsed_s": 509.7258110046387
122
  },
123
  {
124
- "ce_cell": 0.21543064922094346,
125
- "dice_cell": 0.49275926291942596,
126
- "ce_nos": 0.07010223433375358,
127
- "dice_nos": 0.795907906293869,
128
- "bce_ab": 0.10423918575048446,
129
- "bce_cn": 0.21024613380432128,
130
- "total": 1.8886853814125062,
 
131
  "iter": 550,
132
  "lr": 0.0002829507865973116,
133
- "elapsed_s": 560.3340859413147
134
  },
135
  {
136
- "ce_cell": 0.21174423784017563,
137
- "dice_cell": 0.4891924291849136,
138
- "ce_nos": 0.06100128136575222,
139
- "dice_nos": 0.7548426115512847,
140
- "bce_ab": 0.09695948652923107,
141
- "bce_cn": 0.20541400745511054,
142
- "total": 1.8191540384292602,
 
143
  "iter": 600,
144
  "lr": 0.0002790400309887635,
145
- "elapsed_s": 610.7946240901947
146
  },
147
  {
148
- "ce_cell": 0.20390785247087478,
149
- "dice_cell": 0.4746441453695297,
150
- "ce_nos": 0.04720834020525217,
151
- "dice_nos": 0.709533805847168,
152
- "bce_ab": 0.09941868580877782,
153
- "bce_cn": 0.19302350237965585,
154
- "total": 1.727736325263977,
 
155
  "iter": 650,
156
  "lr": 0.0002747595782529114,
157
- "elapsed_s": 660.7677178382874
158
  },
159
  {
160
- "ce_cell": 0.2039007693529129,
161
- "dice_cell": 0.46679477393627167,
162
- "ce_nos": 0.048075792603194716,
163
- "dice_nos": 0.6227267402410507,
164
- "bce_ab": 0.08658930584788323,
165
- "bce_cn": 0.18947327509522438,
166
- "total": 1.617560646533966,
 
167
  "iter": 700,
168
  "lr": 0.0002701219836951418,
169
- "elapsed_s": 711.0392649173737
170
  },
171
  {
172
- "ce_cell": 0.200768101811409,
173
- "dice_cell": 0.45868332326412203,
174
- "ce_nos": 0.03979761004447937,
175
- "dice_nos": 0.5989265680313111,
176
- "bce_ab": 0.07302961058914662,
177
- "bce_cn": 0.1712094095349312,
178
- "total": 1.5424146223068238,
 
179
  "iter": 750,
180
  "lr": 0.0002651408501793399,
181
- "elapsed_s": 760.5930259227753
182
  },
183
  {
184
- "ce_cell": 0.19177152156829835,
185
- "dice_cell": 0.4472357839345932,
186
- "ce_nos": 0.03094205627217889,
187
- "dice_nos": 0.5603820198774337,
188
- "bce_ab": 0.08034474328160286,
189
- "bce_cn": 0.14997318997979164,
190
- "total": 1.4606493067741395,
 
191
  "iter": 800,
192
  "lr": 0.0002598307882283455,
193
- "elapsed_s": 810.9303019046783
194
  },
195
  {
196
- "ce_cell": 0.19938127607107162,
197
- "dice_cell": 0.44567417323589326,
198
- "ce_nos": 0.04125866884365678,
199
- "dice_nos": 0.4803364199399948,
200
- "bce_ab": 0.07717311266809702,
201
- "bce_cn": 0.1591106753051281,
202
- "total": 1.4029343223571777,
 
203
  "iter": 850,
204
  "lr": 0.00025420737316877263,
205
- "elapsed_s": 857.9806337356567
206
  },
207
  {
208
- "ce_cell": 0.1933874309062958,
209
- "dice_cell": 0.4387037259340286,
210
- "ce_nos": 0.036275244057178496,
211
- "dice_nos": 0.48662609457969663,
212
- "bce_ab": 0.06898861289024354,
213
- "bce_cn": 0.15267845779657363,
214
- "total": 1.3766595673561097,
 
215
  "iter": 900,
216
  "lr": 0.00024828709944589296,
217
- "elapsed_s": 908.7008759975433
218
  },
219
  {
220
- "ce_cell": 0.19506525486707688,
221
- "dice_cell": 0.4412110126018524,
222
- "ce_nos": 0.035951650235801935,
223
- "dice_nos": 0.43454607129096984,
224
- "bce_ab": 0.06932192835956812,
225
- "bce_cn": 0.14733665704727172,
226
- "total": 1.32343257188797,
 
227
  "iter": 950,
228
  "lr": 0.00024208733224258537,
229
- "elapsed_s": 959.4800848960876
230
  },
231
  {
232
- "ce_cell": 0.1945480227470398,
233
- "dice_cell": 0.43224972307682036,
234
- "ce_nos": 0.0335944464802742,
235
- "dice_nos": 0.40378140449523925,
236
- "bce_ab": 0.06522910814732313,
237
- "bce_cn": 0.13741043150424959,
238
- "total": 1.2668131160736085,
 
239
  "iter": 1000,
240
  "lr": 0.0002356262565442632,
241
- "elapsed_s": 1009.7932658195496
242
  },
243
  {
244
- "ce_cell": 0.18734178960323333,
245
- "dice_cell": 0.4267615270614624,
246
- "ce_nos": 0.036363077107816937,
247
- "dice_nos": 0.4053969383239746,
248
- "bce_ab": 0.0643582896143198,
249
- "bce_cn": 0.12727220386266708,
250
- "total": 1.2474938321113587,
 
251
  "iter": 1050,
252
  "lr": 0.0002289228237991784,
253
- "elapsed_s": 1059.9658198356628
254
  },
255
  {
256
- "ce_cell": 0.18910773277282714,
257
- "dice_cell": 0.42362256467342374,
258
- "ce_nos": 0.0346462229359895,
259
- "dice_nos": 0.3729756760597229,
260
- "bce_ab": 0.06174593850970268,
261
- "bce_cn": 0.13085844799876212,
262
- "total": 1.2129565787315368,
 
263
  "iter": 1100,
264
  "lr": 0.0002219966963305587,
265
- "elapsed_s": 1110.0916857719421
266
  },
267
  {
268
- "ce_cell": 0.1822684535384178,
269
- "dice_cell": 0.4143069595098495,
270
- "ce_nos": 0.03322938298806548,
271
- "dice_nos": 0.3389162081480026,
272
- "bce_ab": 0.05988596722483635,
273
- "bce_cn": 0.11884681917726994,
274
- "total": 1.1474537920951844,
 
275
  "iter": 1150,
276
  "lr": 0.00021486818966362585,
277
- "elapsed_s": 1160.7585599422455
278
  },
279
  {
280
- "ce_cell": 0.18991790741682052,
281
- "dice_cell": 0.41873910784721374,
282
- "ce_nos": 0.036337544079869985,
283
- "dice_nos": 0.34970403611660006,
284
- "bce_ab": 0.05970212183892727,
285
- "bce_cn": 0.1334019273519516,
286
- "total": 1.1878026413917542,
 
287
  "iter": 1200,
288
  "lr": 0.00020755821293665946,
289
- "elapsed_s": 1211.4072179794312
290
  },
291
  {
292
- "ce_cell": 0.17697676301002502,
293
- "dice_cell": 0.40928772032260896,
294
- "ce_nos": 0.03304771558381617,
295
- "dice_nos": 0.3399574244022369,
296
- "bce_ab": 0.04348419822752476,
297
- "bce_cn": 0.1121597220748663,
298
- "total": 1.1149135494232179,
 
299
  "iter": 1250,
300
  "lr": 0.00020008820757089248,
301
- "elapsed_s": 1262.1290378570557
302
  },
303
  {
304
- "ce_cell": 0.17598221957683563,
305
- "dice_cell": 0.40642625391483306,
306
- "ce_nos": 0.033407045025378466,
307
- "dice_nos": 0.310367751121521,
308
- "bce_ab": 0.0478444230183959,
309
- "bce_cn": 0.09985558047890664,
310
- "total": 1.073883285522461,
 
311
  "iter": 1300,
312
  "lr": 0.0001924800843791277,
313
- "elapsed_s": 1311.735403060913
314
  },
315
  {
316
- "ce_cell": 0.18522560983896255,
317
- "dice_cell": 0.403508597612381,
318
- "ce_nos": 0.036090935450047254,
319
- "dice_nos": 0.3127438998222351,
320
- "bce_ab": 0.04713532615453005,
321
- "bce_cn": 0.11126945935189723,
322
- "total": 1.0959738206863403,
 
323
  "iter": 1350,
324
  "lr": 0.00018475615929754922,
325
- "elapsed_s": 1362.2759177684784
326
  },
327
  {
328
- "ce_cell": 0.18232185065746306,
329
- "dice_cell": 0.40336430847644805,
330
- "ce_nos": 0.0384017390338704,
331
- "dice_nos": 0.30273705661296846,
332
- "bce_ab": 0.04570051861926913,
333
- "bce_cn": 0.11299202486872673,
334
- "total": 1.0855175077915191,
 
335
  "iter": 1400,
336
  "lr": 0.0001769390879292361,
337
- "elapsed_s": 1412.6486489772797
338
  },
339
  {
340
- "ce_cell": 0.18443019971251487,
341
- "dice_cell": 0.41006422102451323,
342
- "ce_nos": 0.038777465261518956,
343
- "dice_nos": 0.293556307554245,
344
- "bce_ab": 0.040375117510557175,
345
- "bce_cn": 0.0971392347663641,
346
- "total": 1.0643425357341767,
 
347
  "iter": 1450,
348
  "lr": 0.00016905179909137546,
349
- "elapsed_s": 1462.6795949935913
350
  },
351
  {
352
- "ce_cell": 0.17869871109724045,
353
- "dice_cell": 0.40553332567214967,
354
- "ce_nos": 0.03795233623124659,
355
- "dice_nos": 0.2916260594129562,
356
- "bce_ab": 0.04099308693781495,
357
- "bce_cn": 0.0865385101735592,
358
- "total": 1.0413420403003693,
 
359
  "iter": 1500,
360
  "lr": 0.00016111742756109098,
361
- "elapsed_s": 1512.4294548034668
362
  },
363
  {
364
- "ce_cell": 0.1764592757821083,
365
- "dice_cell": 0.3958728986978531,
366
- "ce_nos": 0.035169387739151715,
367
- "dice_nos": 0.27471464276313784,
368
- "bce_ab": 0.035233155209571126,
369
- "bce_cn": 0.08008314736187458,
370
- "total": 0.9975325107574463,
 
371
  "iter": 1550,
372
  "lr": 0.00015315924621715527,
373
- "elapsed_s": 1562.1074118614197
374
  },
375
  {
376
- "ce_cell": 0.17475848585367204,
377
- "dice_cell": 0.3929943698644638,
378
- "ce_nos": 0.03353961421176791,
379
- "dice_nos": 0.3009433788061142,
380
- "bce_ab": 0.03854949928820133,
381
- "bce_cn": 0.08339719414710998,
382
- "total": 1.0241825413703918,
 
383
  "iter": 1600,
384
  "lr": 0.00014520059777662684,
385
- "elapsed_s": 1612.9562838077545
386
  },
387
  {
388
- "ce_cell": 0.18072757810354234,
389
- "dice_cell": 0.39913213849067686,
390
- "ce_nos": 0.039328129449859264,
391
- "dice_nos": 0.2857843828201294,
392
- "bce_ab": 0.03886710917577148,
393
- "bce_cn": 0.09502572849392892,
394
- "total": 1.0388650584220886,
 
395
  "iter": 1650,
396
  "lr": 0.00013726482632663912,
397
- "elapsed_s": 1663.1288919448853
398
  },
399
  {
400
- "ce_cell": 0.1759936460852623,
401
- "dice_cell": 0.39650719165802,
402
- "ce_nos": 0.032182310023345054,
403
- "dice_nos": 0.2933516526222229,
404
- "bce_ab": 0.03653882872313261,
405
- "bce_cn": 0.08788134880363942,
406
- "total": 1.0224549758434296,
 
407
  "iter": 1700,
408
  "lr": 0.00012937520885217038,
409
- "elapsed_s": 1712.6260061264038
410
  },
411
  {
412
- "ce_cell": 0.17498762905597687,
413
- "dice_cell": 0.38849363803863524,
414
- "ce_nos": 0.03151271438226104,
415
- "dice_nos": 0.27745749831199645,
416
- "bce_ab": 0.038630726784467696,
417
- "bce_cn": 0.08467736341059208,
418
- "total": 0.995759572982788,
 
419
  "iter": 1750,
420
  "lr": 0.0001215548869606366,
421
- "elapsed_s": 1763.162414073944
422
  },
423
  {
424
- "ce_cell": 0.16943098440766335,
425
- "dice_cell": 0.38974578082561495,
426
- "ce_nos": 0.030594841404818,
427
- "dice_nos": 0.2775198197364807,
428
- "bce_ab": 0.03063900684937835,
429
- "bce_cn": 0.07809612043201923,
430
- "total": 0.9760265576839448,
 
431
  "iter": 1800,
432
  "lr": 0.00011382679900356957,
433
- "elapsed_s": 1812.9093608856201
434
  },
435
  {
436
- "ce_cell": 0.17392412394285203,
437
- "dice_cell": 0.39087122797966,
438
- "ce_nos": 0.03287358325906098,
439
- "dice_nos": 0.27309031009674073,
440
- "bce_ab": 0.03879237989895046,
441
- "bce_cn": 0.07375255063176155,
442
- "total": 0.9833041846752166,
 
443
  "iter": 1850,
444
  "lr": 0.0001062136127944796,
445
- "elapsed_s": 1862.8471250534058
446
  },
447
  {
448
- "ce_cell": 0.1721265098452568,
449
- "dice_cell": 0.3918482345342636,
450
- "ce_nos": 0.03052111998666078,
451
- "dice_nos": 0.26571854174137116,
452
- "bce_ab": 0.028863372541964055,
453
- "bce_cn": 0.07009109571576118,
454
- "total": 0.9591688740253449,
 
455
  "iter": 1900,
456
  "lr": 9.873765912025276e-05,
457
- "elapsed_s": 1913.0803048610687
458
  },
459
  {
460
- "ce_cell": 0.17321533888578414,
461
- "dice_cell": 0.3879319250583649,
462
- "ce_nos": 0.03197065497748554,
463
- "dice_nos": 0.2754987716674805,
464
- "bce_ab": 0.03000295827165246,
465
- "bce_cn": 0.0681322829797864,
466
- "total": 0.966751925945282,
 
467
  "iter": 1950,
468
  "lr": 9.142086624110557e-05,
469
- "elapsed_s": 1962.4520688056946
470
  },
471
  {
472
- "ce_cell": 0.16933307886123658,
473
- "dice_cell": 0.3823584771156311,
474
- "ce_nos": 0.03379744639620185,
475
- "dice_nos": 0.2684609520435333,
476
- "bce_ab": 0.03405721362680197,
477
- "bce_cn": 0.07046777069568634,
478
- "total": 0.9584749519824982,
 
479
  "iter": 2000,
480
  "lr": 8.428469557121958e-05,
481
- "elapsed_s": 2011.636960029602
482
  },
483
  {
484
- "ce_cell": 0.17340840995311738,
485
- "dice_cell": 0.3878352814912796,
486
- "ce_nos": 0.03318483327515423,
487
- "dice_nos": 0.2580646830797195,
488
- "bce_ab": 0.029467624574899674,
489
- "bce_cn": 0.07082617726176978,
490
- "total": 0.9527869963645935,
 
491
  "iter": 2050,
492
  "lr": 7.735007872871564e-05,
493
- "elapsed_s": 2061.974331855774
494
  },
495
  {
496
- "ce_cell": 0.17320298582315444,
497
- "dice_cell": 0.3853736054897308,
498
- "ce_nos": 0.028748751198872925,
499
- "dice_nos": 0.26544315218925474,
500
- "bce_ab": 0.033497555926442144,
501
- "bce_cn": 0.06046421557664871,
502
- "total": 0.9467302620410919,
 
503
  "iter": 2100,
504
  "lr": 7.063735613961179e-05,
505
- "elapsed_s": 2112.548271894455
506
  },
507
  {
508
- "ce_cell": 0.1658191041648388,
509
- "dice_cell": 0.3796718597412109,
510
- "ce_nos": 0.03187917632050812,
511
- "dice_nos": 0.26365615785121915,
512
- "bce_ab": 0.030561734307557344,
513
- "bce_cn": 0.05981527805328369,
514
- "total": 0.9314033150672912,
 
515
  "iter": 2150,
516
  "lr": 6.416621737584896e-05,
517
- "elapsed_s": 2162.667312860489
518
  },
519
  {
520
- "ce_cell": 0.1701272311806679,
521
- "dice_cell": 0.37863297820091246,
522
- "ce_nos": 0.027262243572622538,
523
- "dice_nos": 0.26668123960494994,
524
- "bce_ab": 0.04351606283336878,
525
- "bce_cn": 0.06579859033226967,
526
- "total": 0.9520183408260345,
 
527
  "iter": 2200,
528
  "lr": 5.795564340238374e-05,
529
- "elapsed_s": 2213.4271240234375
530
  },
531
  {
532
- "ce_cell": 0.1703978794813156,
533
- "dice_cell": 0.38433976292610167,
534
- "ce_nos": 0.03619127416983247,
535
- "dice_nos": 0.24586948692798616,
536
- "bce_ab": 0.02381640898063779,
537
- "bce_cn": 0.06208158168941736,
538
- "total": 0.922696396112442,
 
539
  "iter": 2250,
540
  "lr": 5.202385090274833e-05,
541
- "elapsed_s": 2263.827299118042
542
  },
543
  {
544
- "ce_cell": 0.16755644142627715,
545
- "dice_cell": 0.3778986144065857,
546
- "ce_nos": 0.03260435258969665,
547
- "dice_nos": 0.25375066459178924,
548
- "bce_ab": 0.029958658767864108,
549
- "bce_cn": 0.05888926636427641,
550
- "total": 0.9206579995155334,
 
551
  "iter": 2300,
552
  "lr": 4.638823884637875e-05,
553
- "elapsed_s": 2313.582683801651
554
  },
555
  {
556
- "ce_cell": 0.1692182320356369,
557
- "dice_cell": 0.3797942841053009,
558
- "ce_nos": 0.031096484493464232,
559
- "dice_nos": 0.25585519909858706,
560
- "bce_ab": 0.025209803096950055,
561
- "bce_cn": 0.06410345062613487,
562
- "total": 0.925277452468872,
 
563
  "iter": 2350,
564
  "lr": 4.106533745443955e-05,
565
- "elapsed_s": 2366.780472755432
566
  },
567
  {
568
- "ce_cell": 0.1647430169582367,
569
- "dice_cell": 0.37565741121768953,
570
- "ce_nos": 0.029058436192572118,
571
- "dice_nos": 0.2536770069599152,
572
- "bce_ab": 0.026647686092182994,
573
- "bce_cn": 0.05636850781738758,
574
- "total": 0.9061520624160767,
 
575
  "iter": 2400,
576
  "lr": 3.607075971383718e-05,
577
- "elapsed_s": 2420.7595388889313
578
  },
579
  {
580
- "ce_cell": 0.1587481266260147,
581
- "dice_cell": 0.37386213421821596,
582
- "ce_nos": 0.027811604756861924,
583
- "dice_nos": 0.2525290942192078,
584
- "bce_ab": 0.027055934872478246,
585
- "bce_cn": 0.05388368744403124,
586
- "total": 0.8938905835151673,
 
587
  "iter": 2450,
588
  "lr": 3.1419155581639394e-05,
589
- "elapsed_s": 2472.9100120067596
590
  },
591
  {
592
- "ce_cell": 0.16981444805860518,
593
- "dice_cell": 0.3800900149345398,
594
- "ce_nos": 0.02935561618767679,
595
- "dice_nos": 0.2700779044628143,
596
- "bce_ab": 0.026528375521302223,
597
- "bce_cn": 0.05692515041679144,
598
- "total": 0.932791496515274,
 
599
  "iter": 2500,
600
  "lr": 2.7124169014226923e-05,
601
- "elapsed_s": 2525.758083820343
602
  },
603
  {
604
- "ce_cell": 0.16598297476768495,
605
- "dice_cell": 0.37718391716480254,
606
- "ce_nos": 0.030217354064807297,
607
- "dice_nos": 0.2526162987947464,
608
- "bce_ab": 0.027398303812369705,
609
- "bce_cn": 0.05613136574625969,
610
- "total": 0.9095302104949952,
 
611
  "iter": 2550,
612
  "lr": 2.319839794721846e-05,
613
- "elapsed_s": 2578.604254961014
614
  },
615
  {
616
- "ce_cell": 0.16501545295119285,
617
- "dice_cell": 0.37883709609508515,
618
- "ce_nos": 0.029584614327177405,
619
- "dice_nos": 0.24774192690849303,
620
- "bce_ab": 0.022402014611288906,
621
- "bce_cn": 0.057471454478800296,
622
- "total": 0.9010525631904602,
 
623
  "iter": 2600,
624
  "lr": 1.9653357343555055e-05,
625
- "elapsed_s": 2630.874783039093
626
  },
627
  {
628
- "ce_cell": 0.16610188841819762,
629
- "dice_cell": 0.3750225841999054,
630
- "ce_nos": 0.03136977587360889,
631
- "dice_nos": 0.24075563967227936,
632
- "bce_ab": 0.023018423346802593,
633
- "bce_cn": 0.054641781151294706,
634
- "total": 0.8909100878238678,
 
635
  "iter": 2650,
636
  "lr": 1.6499445418129828e-05,
637
- "elapsed_s": 2683.160297870636
638
  },
639
  {
640
- "ce_cell": 0.16699187859892844,
641
- "dice_cell": 0.37973670542240145,
642
- "ce_nos": 0.031007613111287354,
643
- "dice_nos": 0.25119431257247926,
644
- "bce_ab": 0.02384809180162847,
645
- "bce_cn": 0.05100016433745622,
646
- "total": 0.9037787771224975,
 
647
  "iter": 2700,
648
  "lr": 1.3745913138031944e-05,
649
- "elapsed_s": 2734.7909269332886
650
  },
651
  {
652
- "ce_cell": 0.1654239809513092,
653
- "dice_cell": 0.373527038693428,
654
- "ce_nos": 0.026881940369494258,
655
- "dice_nos": 0.25084660708904266,
656
- "bce_ab": 0.03065326821990311,
657
- "bce_cn": 0.05875104777514935,
658
- "total": 0.9060838842391967,
 
659
  "iter": 2750,
660
  "lr": 1.1400837087866401e-05,
661
- "elapsed_s": 2785.7226259708405
662
  },
663
  {
664
- "ce_cell": 0.164136121571064,
665
- "dice_cell": 0.37796851992607117,
666
- "ce_nos": 0.03165758168324828,
667
- "dice_nos": 0.2464037835597992,
668
- "bce_ab": 0.02406527513638139,
669
- "bce_cn": 0.05659090880304575,
670
- "total": 0.9008221983909607,
 
671
  "iter": 2800,
672
  "lr": 9.471095779739621e-06,
673
- "elapsed_s": 2837.11731505394
674
  },
675
  {
676
- "ce_cell": 0.16365474253892898,
677
- "dice_cell": 0.3711686569452286,
678
- "ce_nos": 0.03501185901463032,
679
- "dice_nos": 0.26115318775177004,
680
- "bce_ab": 0.020862439805641772,
681
- "bce_cn": 0.06006271541118622,
682
- "total": 0.9119135963916779,
 
683
  "iter": 2850,
684
  "lr": 7.96234947739845e-06,
685
- "elapsed_s": 2888.5047039985657
686
  },
687
  {
688
- "ce_cell": 0.1650400999188423,
689
- "dice_cell": 0.37380745112895963,
690
- "ce_nos": 0.027719355495646594,
691
- "dice_nos": 0.24648674428462983,
692
- "bce_ab": 0.030483954632654787,
693
- "bce_cn": 0.05518790137022734,
694
- "total": 0.898725507259369,
 
695
  "iter": 2900,
696
  "lr": 6.8790235937012375e-06,
697
- "elapsed_s": 2939.583750963211
698
  },
699
  {
700
- "ce_cell": 0.1652056121826172,
701
- "dice_cell": 0.37707124650478363,
702
- "ce_nos": 0.027215274684131145,
703
- "dice_nos": 0.24094445288181304,
704
- "bce_ab": 0.02379746310412884,
705
- "bce_cn": 0.05311750005930662,
706
- "total": 0.8873515450954437,
 
707
  "iter": 2950,
708
  "lr": 6.224295710119515e-06,
709
- "elapsed_s": 2990.7978727817535
710
  },
711
  {
712
- "ce_cell": 0.15959388136863709,
713
- "dice_cell": 0.3714492267370224,
714
- "ce_nos": 0.030986426128074528,
715
- "dice_nos": 0.2347310698032379,
716
- "bce_ab": 0.018123713415116073,
717
- "bce_cn": 0.05247432488948107,
718
- "total": 0.8673586428165436,
 
719
  "iter": 3000,
720
  "lr": 6.000086256343946e-06,
721
- "elapsed_s": 3042.6654930114746
722
  }
723
  ],
724
  "val": []
 
1
  {
2
  "train": [
3
  {
4
+ "ce_cell": 0.9777789008617401,
5
+ "dice_cell": 0.8740915787220002,
6
+ "ce_nos": 1.036865803003311,
7
+ "dice_nos": 0.9926403152942658,
8
+ "bce_ab": 0.6191497492790222,
9
+ "bce_cn": 0.6784366118907929,
10
+ "mse_dist": 0.17519462607800962,
11
+ "total": 6.05493613243103,
12
  "iter": 50,
13
  "lr": 0.00015,
14
+ "elapsed_s": 26.069042921066284
15
  },
16
  {
17
+ "ce_cell": 0.7761036133766175,
18
+ "dice_cell": 0.8330331027507782,
19
+ "ce_nos": 0.7418340992927551,
20
+ "dice_nos": 0.9909472858905792,
21
+ "bce_ab": 0.4780848079919815,
22
+ "bce_cn": 0.4427151954174042,
23
+ "mse_dist": 0.028857542239129542,
24
+ "total": 4.407005853652954,
25
  "iter": 100,
26
  "lr": 0.0003,
27
+ "elapsed_s": 51.948261976242065
28
  },
29
  {
30
+ "ce_cell": 0.6085621345043183,
31
+ "dice_cell": 0.77956205368042,
32
+ "ce_nos": 0.5155557030439377,
33
+ "dice_nos": 0.9848698270320893,
34
+ "bce_ab": 0.38647628366947173,
35
+ "bce_cn": 0.3627579927444458,
36
+ "mse_dist": 0.022874937355518342,
37
+ "total": 3.752158718109131,
38
  "iter": 150,
39
  "lr": 0.00029979294712266676,
40
+ "elapsed_s": 77.92088508605957
41
  },
42
  {
43
+ "ce_cell": 0.4677648037672043,
44
+ "dice_cell": 0.7135179710388183,
45
+ "ce_nos": 0.38082618355751036,
46
+ "dice_nos": 0.9798667204380035,
47
+ "bce_ab": 0.3422890931367874,
48
+ "bce_cn": 0.3326674288511276,
49
+ "mse_dist": 0.021435761135071518,
50
+ "total": 3.324111022949219,
51
  "iter": 200,
52
  "lr": 0.0002991554114956718,
53
+ "elapsed_s": 103.77014684677124
54
  },
55
  {
56
+ "ce_cell": 0.38509724378585813,
57
+ "dice_cell": 0.653343700170517,
58
+ "ce_nos": 0.28423633813858035,
59
+ "dice_nos": 0.9686614489555359,
60
+ "bce_ab": 0.2873850876092911,
61
+ "bce_cn": 0.313295716047287,
62
+ "mse_dist": 0.02068787667900324,
63
+ "total": 2.995458931922913,
64
  "iter": 250,
65
  "lr": 0.00029808917686451123,
66
+ "elapsed_s": 129.53510093688965
67
  },
68
  {
69
+ "ce_cell": 0.33268820941448213,
70
+ "dice_cell": 0.5951491236686707,
71
+ "ce_nos": 0.21769435673952103,
72
+ "dice_nos": 0.9576204478740692,
73
+ "bce_ab": 0.2672469413280487,
74
+ "bce_cn": 0.2786091101169586,
75
+ "mse_dist": 0.020070483386516572,
76
+ "total": 2.7493606185913086,
77
  "iter": 300,
78
  "lr": 0.00029659737067906796,
79
+ "elapsed_s": 155.210942029953
80
  },
81
  {
82
+ "ce_cell": 0.2987843030691147,
83
+ "dice_cell": 0.5483147490024567,
84
+ "ce_nos": 0.1640540537238121,
85
+ "dice_nos": 0.9450577974319458,
86
+ "bce_ab": 0.24849256694316865,
87
+ "bce_cn": 0.26394254505634307,
88
+ "mse_dist": 0.018844751380383967,
89
+ "total": 2.5628697538375853,
90
  "iter": 350,
91
  "lr": 0.00029468436866391876,
92
+ "elapsed_s": 180.83402609825134
93
  },
94
  {
95
+ "ce_cell": 0.2770148387551308,
96
+ "dice_cell": 0.517680995464325,
97
+ "ce_nos": 0.13154282703995704,
98
+ "dice_nos": 0.9315742325782775,
99
+ "bce_ab": 0.22211319893598558,
100
+ "bce_cn": 0.26095826357603075,
101
+ "mse_dist": 0.018346485309302808,
102
+ "total": 2.4326167488098145,
103
  "iter": 400,
104
  "lr": 0.0002923557819835801,
105
+ "elapsed_s": 206.35285687446594
106
  },
107
  {
108
+ "ce_cell": 0.26904626816511157,
109
+ "dice_cell": 0.4866592735052109,
110
+ "ce_nos": 0.10414468422532082,
111
+ "dice_nos": 0.8954603922367096,
112
+ "bce_ab": 0.21155467212200166,
113
+ "bce_cn": 0.23948282122612,
114
+ "mse_dist": 0.018231311477720738,
115
+ "total": 2.2975046682357787,
116
  "iter": 450,
117
  "lr": 0.00028961844078399557,
118
+ "elapsed_s": 231.1181778907776
119
  },
120
  {
121
+ "ce_cell": 0.2626450803875923,
122
+ "dice_cell": 0.4800322437286377,
123
+ "ce_nos": 0.08362166196107865,
124
+ "dice_nos": 0.8580216205120087,
125
+ "bce_ab": 0.184051346629858,
126
+ "bce_cn": 0.24237413197755814,
127
+ "mse_dist": 0.018717802539467813,
128
+ "total": 2.204335107803345,
129
  "iter": 500,
130
  "lr": 0.0002864803741585413,
131
+ "elapsed_s": 256.2003290653229
132
  },
133
  {
134
+ "ce_cell": 0.2437168264389038,
135
+ "dice_cell": 0.44185430943965914,
136
+ "ce_nos": 0.06530485145747661,
137
+ "dice_nos": 0.8218306469917297,
138
+ "bce_ab": 0.17754501536488532,
139
+ "bce_cn": 0.22136381536722183,
140
+ "mse_dist": 0.01670815333724022,
141
+ "total": 2.0551562237739565,
142
  "iter": 550,
143
  "lr": 0.0002829507865973116,
144
+ "elapsed_s": 281.3221549987793
145
  },
146
  {
147
+ "ce_cell": 0.244499791264534,
148
+ "dice_cell": 0.43603491365909575,
149
+ "ce_nos": 0.056205639988183974,
150
+ "dice_nos": 0.764169396162033,
151
+ "bce_ab": 0.16857516303658485,
152
+ "bce_cn": 0.22340300798416138,
153
+ "mse_dist": 0.016940609198063613,
154
+ "total": 1.9775909566879273,
155
  "iter": 600,
156
  "lr": 0.0002790400309887635,
157
+ "elapsed_s": 306.70035195350647
158
  },
159
  {
160
+ "ce_cell": 0.2412768453359604,
161
+ "dice_cell": 0.42379125833511355,
162
+ "ce_nos": 0.045140089876949785,
163
+ "dice_nos": 0.6991792964935303,
164
+ "bce_ab": 0.14149282865226268,
165
+ "bce_cn": 0.1941063818335533,
166
+ "mse_dist": 0.0163105876930058,
167
+ "total": 1.8265396285057067,
168
  "iter": 650,
169
  "lr": 0.0002747595782529114,
170
+ "elapsed_s": 331.7218248844147
171
  },
172
  {
173
+ "ce_cell": 0.23859565168619157,
174
+ "dice_cell": 0.416628275513649,
175
+ "ce_nos": 0.03758059218525887,
176
+ "dice_nos": 0.6752322578430175,
177
+ "bce_ab": 0.14357838369905948,
178
+ "bce_cn": 0.19971576437354088,
179
+ "mse_dist": 0.016762549821287394,
180
+ "total": 1.7951436424255371,
181
  "iter": 700,
182
  "lr": 0.0002701219836951418,
183
+ "elapsed_s": 357.79720091819763
184
  },
185
  {
186
+ "ce_cell": 0.23537835448980332,
187
+ "dice_cell": 0.407176011800766,
188
+ "ce_nos": 0.037268193271011114,
189
+ "dice_nos": 0.5960061675310135,
190
+ "bce_ab": 0.13629250541329385,
191
+ "bce_cn": 0.19288790062069894,
192
+ "mse_dist": 0.016352202873677016,
193
+ "total": 1.6867701506614685,
194
  "iter": 750,
195
  "lr": 0.0002651408501793399,
196
+ "elapsed_s": 383.76512479782104
197
  },
198
  {
199
+ "ce_cell": 0.22146987557411194,
200
+ "dice_cell": 0.3943329083919525,
201
+ "ce_nos": 0.03001030534505844,
202
+ "dice_nos": 0.5577754044532776,
203
+ "bce_ab": 0.12049378417432308,
204
+ "bce_cn": 0.17776169538497924,
205
+ "mse_dist": 0.015104550141841174,
206
+ "total": 1.5773667359352113,
207
  "iter": 800,
208
  "lr": 0.0002598307882283455,
209
+ "elapsed_s": 409.39320611953735
210
  },
211
  {
212
+ "ce_cell": 0.2323348864912987,
213
+ "dice_cell": 0.40128552079200747,
214
+ "ce_nos": 0.033332938868552445,
215
+ "dice_nos": 0.5035274916887283,
216
+ "bce_ab": 0.10335498113185167,
217
+ "bce_cn": 0.17933884173631667,
218
+ "mse_dist": 0.015931979529559614,
219
+ "total": 1.5328345370292664,
220
  "iter": 850,
221
  "lr": 0.00025420737316877263,
222
+ "elapsed_s": 435.14874291419983
223
  },
224
  {
225
+ "ce_cell": 0.2182929402589798,
226
+ "dice_cell": 0.3786925607919693,
227
+ "ce_nos": 0.027653501816093922,
228
+ "dice_nos": 0.4839075034856796,
229
+ "bce_ab": 0.11399138215929269,
230
+ "bce_cn": 0.14645383700728418,
231
+ "mse_dist": 0.014609130527824164,
232
+ "total": 1.442037374973297,
233
  "iter": 900,
234
  "lr": 0.00024828709944589296,
235
+ "elapsed_s": 460.9140498638153
236
  },
237
  {
238
+ "ce_cell": 0.22527328491210938,
239
+ "dice_cell": 0.38350287079811096,
240
+ "ce_nos": 0.03021520377136767,
241
+ "dice_nos": 0.4358292263746262,
242
+ "bce_ab": 0.09423262968659402,
243
+ "bce_cn": 0.16419889584183692,
244
+ "mse_dist": 0.015031665433198213,
245
+ "total": 1.408410460948944,
246
  "iter": 950,
247
  "lr": 0.00024208733224258537,
248
+ "elapsed_s": 486.0358588695526
249
  },
250
  {
251
+ "ce_cell": 0.22444447070360185,
252
+ "dice_cell": 0.38110810697078706,
253
+ "ce_nos": 0.03478069136850536,
254
+ "dice_nos": 0.41253172159194945,
255
+ "bce_ab": 0.09447835937142372,
256
+ "bce_cn": 0.1629086424410343,
257
+ "mse_dist": 0.01506655540317297,
258
+ "total": 1.3855847644805908,
259
  "iter": 1000,
260
  "lr": 0.0002356262565442632,
261
+ "elapsed_s": 511.45353078842163
262
  },
263
  {
264
+ "ce_cell": 0.22289368331432344,
265
+ "dice_cell": 0.37346395969390866,
266
+ "ce_nos": 0.03566695312969387,
267
+ "dice_nos": 0.38205474078655244,
268
+ "bce_ab": 0.09222890179604291,
269
+ "bce_cn": 0.15356166876852512,
270
+ "mse_dist": 0.0148412006162107,
271
+ "total": 1.3340759134292604,
272
  "iter": 1050,
273
  "lr": 0.0002289228237991784,
274
+ "elapsed_s": 536.3748590946198
275
  },
276
  {
277
+ "ce_cell": 0.21837765246629715,
278
+ "dice_cell": 0.36276875555515287,
279
+ "ce_nos": 0.0334074149094522,
280
+ "dice_nos": 0.37752662658691405,
281
+ "bce_ab": 0.09045730914920569,
282
+ "bce_cn": 0.1526588149368763,
283
+ "mse_dist": 0.014266370516270398,
284
+ "total": 1.3065284299850464,
285
  "iter": 1100,
286
  "lr": 0.0002219966963305587,
287
+ "elapsed_s": 561.2481529712677
288
  },
289
  {
290
+ "ce_cell": 0.22264640301465988,
291
+ "dice_cell": 0.3701382452249527,
292
+ "ce_nos": 0.03397220085840672,
293
+ "dice_nos": 0.37801525712013245,
294
+ "bce_ab": 0.08047693535685539,
295
+ "bce_cn": 0.1416425648331642,
296
+ "mse_dist": 0.014651054423302412,
297
+ "total": 1.3001468753814698,
298
  "iter": 1150,
299
  "lr": 0.00021486818966362585,
300
+ "elapsed_s": 586.2862780094147
301
  },
302
  {
303
+ "ce_cell": 0.21802948534488678,
304
+ "dice_cell": 0.36143216729164124,
305
+ "ce_nos": 0.03354042544960976,
306
+ "dice_nos": 0.35177252113819124,
307
+ "bce_ab": 0.09707722380757332,
308
+ "bce_cn": 0.13814265102148057,
309
+ "mse_dist": 0.014300682041794062,
310
+ "total": 1.2714978766441345,
311
  "iter": 1200,
312
  "lr": 0.00020755821293665946,
313
+ "elapsed_s": 611.6254408359528
314
  },
315
  {
316
+ "ce_cell": 0.2118623086810112,
317
+ "dice_cell": 0.3594000792503357,
318
+ "ce_nos": 0.028813177780248226,
319
+ "dice_nos": 0.3532944941520691,
320
+ "bce_ab": 0.08322387758642436,
321
+ "bce_cn": 0.12183172032237052,
322
+ "mse_dist": 0.014152146968990564,
323
+ "total": 1.229186396598816,
324
  "iter": 1250,
325
  "lr": 0.00020008820757089248,
326
+ "elapsed_s": 636.6162917613983
327
  },
328
  {
329
+ "ce_cell": 0.21404922842979432,
330
+ "dice_cell": 0.3589194703102112,
331
+ "ce_nos": 0.03624305865727365,
332
+ "dice_nos": 0.32102711260318756,
333
+ "bce_ab": 0.08243383184075355,
334
+ "bce_cn": 0.12266159057617188,
335
+ "mse_dist": 0.013961246944963932,
336
+ "total": 1.2051405358314513,
337
  "iter": 1300,
338
  "lr": 0.0001924800843791277,
339
+ "elapsed_s": 661.7505779266357
340
  },
341
  {
342
+ "ce_cell": 0.2120766496658325,
343
+ "dice_cell": 0.3596819120645523,
344
+ "ce_nos": 0.033906866414472464,
345
+ "dice_nos": 0.3169685709476471,
346
+ "bce_ab": 0.05813073594123125,
347
+ "bce_cn": 0.11684628672897816,
348
+ "mse_dist": 0.01383887032046914,
349
+ "total": 1.1668053698539733,
350
  "iter": 1350,
351
  "lr": 0.00018475615929754922,
352
+ "elapsed_s": 686.766040802002
353
  },
354
  {
355
+ "ce_cell": 0.21282824724912644,
356
+ "dice_cell": 0.35991409242153166,
357
+ "ce_nos": 0.03106280479580164,
358
+ "dice_nos": 0.3374244701862335,
359
+ "bce_ab": 0.06968416810035706,
360
+ "bce_cn": 0.12222521275281906,
361
+ "mse_dist": 0.013985643181949853,
362
+ "total": 1.2030672073364257,
363
  "iter": 1400,
364
  "lr": 0.0001769390879292361,
365
+ "elapsed_s": 711.7308628559113
366
  },
367
  {
368
+ "ce_cell": 0.20805930137634276,
369
+ "dice_cell": 0.34907511532306673,
370
+ "ce_nos": 0.03580653930082917,
371
+ "dice_nos": 0.32342082798480987,
372
+ "bce_ab": 0.0811778188869357,
373
+ "bce_cn": 0.11206604793667793,
374
+ "mse_dist": 0.013074529767036438,
375
+ "total": 1.174978291988373,
376
  "iter": 1450,
377
  "lr": 0.00016905179909137546,
378
+ "elapsed_s": 736.551778793335
379
  },
380
  {
381
+ "ce_cell": 0.2046449926495552,
382
+ "dice_cell": 0.3410522907972336,
383
+ "ce_nos": 0.02621271908748895,
384
+ "dice_nos": 0.31531415462493895,
385
+ "bce_ab": 0.07398473702371121,
386
+ "bce_cn": 0.09817121870815754,
387
+ "mse_dist": 0.012903542686253787,
388
+ "total": 1.1238978099822998,
389
  "iter": 1500,
390
  "lr": 0.00016111742756109098,
391
+ "elapsed_s": 761.4780099391937
392
  },
393
  {
394
+ "ce_cell": 0.20931495547294618,
395
+ "dice_cell": 0.35134742259979246,
396
+ "ce_nos": 0.037548264022916555,
397
+ "dice_nos": 0.2945420718193054,
398
+ "bce_ab": 0.060994742810726164,
399
+ "bce_cn": 0.11476682797074318,
400
+ "mse_dist": 0.013118454609066249,
401
+ "total": 1.134106569290161,
402
  "iter": 1550,
403
  "lr": 0.00015315924621715527,
404
+ "elapsed_s": 786.305233001709
405
  },
406
  {
407
+ "ce_cell": 0.20947466552257538,
408
+ "dice_cell": 0.34759899854660037,
409
+ "ce_nos": 0.03775742853060365,
410
+ "dice_nos": 0.2938369768857956,
411
+ "bce_ab": 0.05887895548716188,
412
+ "bce_cn": 0.1020959459990263,
413
+ "mse_dist": 0.013232378214597702,
414
+ "total": 1.1158048474788667,
415
  "iter": 1600,
416
  "lr": 0.00014520059777662684,
417
+ "elapsed_s": 811.053496837616
418
  },
419
  {
420
+ "ce_cell": 0.20545023560523987,
421
+ "dice_cell": 0.34604851961135863,
422
+ "ce_nos": 0.03138292274437845,
423
+ "dice_nos": 0.3009911799430847,
424
+ "bce_ab": 0.051527918446809054,
425
+ "bce_cn": 0.09074965998530388,
426
+ "mse_dist": 0.012758474349975586,
427
+ "total": 1.0899428081512452,
428
  "iter": 1650,
429
  "lr": 0.00013726482632663912,
430
+ "elapsed_s": 835.8836717605591
431
  },
432
  {
433
+ "ce_cell": 0.2035794249176979,
434
+ "dice_cell": 0.3380417960882187,
435
+ "ce_nos": 0.03119728611782193,
436
+ "dice_nos": 0.29003060042858125,
437
+ "bce_ab": 0.055323713328689336,
438
+ "bce_cn": 0.0933622520416975,
439
+ "mse_dist": 0.012566495817154646,
440
+ "total": 1.0743675696849824,
441
  "iter": 1700,
442
  "lr": 0.00012937520885217038,
443
+ "elapsed_s": 860.7524697780609
444
  },
445
  {
446
+ "ce_cell": 0.2061012852191925,
447
+ "dice_cell": 0.34122552573680875,
448
+ "ce_nos": 0.03216092755552381,
449
+ "dice_nos": 0.2873716199398041,
450
+ "bce_ab": 0.042616604026407004,
451
+ "bce_cn": 0.09780772387981415,
452
+ "mse_dist": 0.013027021698653697,
453
+ "total": 1.072418793439865,
454
  "iter": 1750,
455
  "lr": 0.0001215548869606366,
456
+ "elapsed_s": 885.5479848384857
457
  },
458
  {
459
+ "ce_cell": 0.2040314009785652,
460
+ "dice_cell": 0.34347267031669615,
461
+ "ce_nos": 0.031174597078934312,
462
+ "dice_nos": 0.2863872331380844,
463
+ "bce_ab": 0.05184935016557574,
464
+ "bce_cn": 0.08530131049454212,
465
+ "mse_dist": 0.012656427212059498,
466
+ "total": 1.0654987025260925,
467
  "iter": 1800,
468
  "lr": 0.00011382679900356957,
469
+ "elapsed_s": 910.4776349067688
470
  },
471
  {
472
+ "ce_cell": 0.19866832792758943,
473
+ "dice_cell": 0.3420099478960037,
474
+ "ce_nos": 0.03141806117258966,
475
+ "dice_nos": 0.27803765118122103,
476
+ "bce_ab": 0.045098451543599365,
477
+ "bce_cn": 0.09952721245586872,
478
+ "mse_dist": 0.012453611139208079,
479
+ "total": 1.0570277082920074,
480
  "iter": 1850,
481
  "lr": 0.0001062136127944796,
482
+ "elapsed_s": 935.2265169620514
483
  },
484
  {
485
+ "ce_cell": 0.19916877180337905,
486
+ "dice_cell": 0.34010264158248904,
487
+ "ce_nos": 0.030681413793936373,
488
+ "dice_nos": 0.268166041970253,
489
+ "bce_ab": 0.04972605316899717,
490
+ "bce_cn": 0.08704228073358536,
491
+ "mse_dist": 0.012498341277241707,
492
+ "total": 1.0373789024353028,
493
  "iter": 1900,
494
  "lr": 9.873765912025276e-05,
495
+ "elapsed_s": 960.0961599349976
496
  },
497
  {
498
+ "ce_cell": 0.19386377602815627,
499
+ "dice_cell": 0.334270116686821,
500
+ "ce_nos": 0.02582207920961082,
501
+ "dice_nos": 0.2805849975347519,
502
+ "bce_ab": 0.05429890226572752,
503
+ "bce_cn": 0.07757438875734807,
504
+ "mse_dist": 0.012007349245250225,
505
+ "total": 1.026451007127762,
506
  "iter": 1950,
507
  "lr": 9.142086624110557e-05,
508
+ "elapsed_s": 985.0865857601166
509
  },
510
  {
511
+ "ce_cell": 0.2070612996816635,
512
+ "dice_cell": 0.3450821244716644,
513
+ "ce_nos": 0.03970785442739725,
514
+ "dice_nos": 0.27347362697124483,
515
+ "bce_ab": 0.04065307324752212,
516
+ "bce_cn": 0.09133856296539307,
517
+ "mse_dist": 0.012714939899742603,
518
+ "total": 1.0608912444114684,
519
  "iter": 2000,
520
  "lr": 8.428469557121958e-05,
521
+ "elapsed_s": 1010.4481308460236
522
  },
523
  {
524
+ "ce_cell": 0.20134998142719268,
525
+ "dice_cell": 0.33819403886795046,
526
+ "ce_nos": 0.030352213024161755,
527
+ "dice_nos": 0.27864284574985504,
528
+ "bce_ab": 0.049128259383142,
529
+ "bce_cn": 0.08518408581614495,
530
+ "mse_dist": 0.012358254436403513,
531
+ "total": 1.044642698764801,
532
  "iter": 2050,
533
  "lr": 7.735007872871564e-05,
534
+ "elapsed_s": 1035.885332107544
535
  },
536
  {
537
+ "ce_cell": 0.19757086873054505,
538
+ "dice_cell": 0.33871914267539976,
539
+ "ce_nos": 0.02972847773693502,
540
+ "dice_nos": 0.2694511997699738,
541
+ "bce_ab": 0.04641149152070284,
542
+ "bce_cn": 0.07878688346594571,
543
+ "mse_dist": 0.012009835205972194,
544
+ "total": 1.0207172453403472,
545
  "iter": 2100,
546
  "lr": 7.063735613961179e-05,
547
+ "elapsed_s": 1061.2731919288635
548
  },
549
  {
550
+ "ce_cell": 0.1988272961974144,
551
+ "dice_cell": 0.33160640120506285,
552
+ "ce_nos": 0.03366862212307751,
553
+ "dice_nos": 0.2641986829042435,
554
+ "bce_ab": 0.04372629556804895,
555
+ "bce_cn": 0.08536963656544685,
556
+ "mse_dist": 0.011948191411793232,
557
+ "total": 1.0171378910541535,
558
  "iter": 2150,
559
  "lr": 6.416621737584896e-05,
560
+ "elapsed_s": 1086.707243680954
561
  },
562
  {
563
+ "ce_cell": 0.19380151480436325,
564
+ "dice_cell": 0.32752876222133637,
565
+ "ce_nos": 0.03283986233174801,
566
+ "dice_nos": 0.26049777925014495,
567
+ "bce_ab": 0.041361887510865926,
568
+ "bce_cn": 0.07517353564500809,
569
+ "mse_dist": 0.011463552955538034,
570
+ "total": 0.9885211074352265,
571
  "iter": 2200,
572
  "lr": 5.795564340238374e-05,
573
+ "elapsed_s": 1112.0149159431458
574
  },
575
  {
576
+ "ce_cell": 0.19721020132303238,
577
+ "dice_cell": 0.33173409938812254,
578
+ "ce_nos": 0.02758968804962933,
579
+ "dice_nos": 0.27361033380031585,
580
+ "bce_ab": 0.04173741614446044,
581
+ "bce_cn": 0.07369629118591547,
582
+ "mse_dist": 0.01197859773412347,
583
+ "total": 1.0054710137844085,
584
  "iter": 2250,
585
  "lr": 5.202385090274833e-05,
586
+ "elapsed_s": 1137.4904730319977
587
  },
588
  {
589
+ "ce_cell": 0.18801554799079895,
590
+ "dice_cell": 0.32500296354293823,
591
+ "ce_nos": 0.026653141435235738,
592
+ "dice_nos": 0.2547152751684189,
593
+ "bce_ab": 0.03614282112568617,
594
+ "bce_cn": 0.07250596221536398,
595
+ "mse_dist": 0.011296509951353072,
596
+ "total": 0.9595182621479035,
597
  "iter": 2300,
598
  "lr": 4.638823884637875e-05,
599
+ "elapsed_s": 1162.9490859508514
600
  },
601
  {
602
+ "ce_cell": 0.19445410251617432,
603
+ "dice_cell": 0.3265311974287033,
604
+ "ce_nos": 0.02529556589666754,
605
+ "dice_nos": 0.26673332929611204,
606
+ "bce_ab": 0.05173649054020643,
607
+ "bce_cn": 0.07536047860980034,
608
+ "mse_dist": 0.011767324544489383,
609
+ "total": 0.9989477896690369,
610
  "iter": 2350,
611
  "lr": 4.106533745443955e-05,
612
+ "elapsed_s": 1188.4017899036407
613
  },
614
  {
615
+ "ce_cell": 0.19469613671302796,
616
+ "dice_cell": 0.33241518437862394,
617
+ "ce_nos": 0.033744015344418586,
618
+ "dice_nos": 0.24858344793319703,
619
+ "bce_ab": 0.03829080337658525,
620
+ "bce_cn": 0.07345383018255233,
621
+ "mse_dist": 0.011775377187877894,
622
+ "total": 0.980060316324234,
623
  "iter": 2400,
624
  "lr": 3.607075971383718e-05,
625
+ "elapsed_s": 1214.008955001831
626
  },
627
  {
628
+ "ce_cell": 0.19300242602825166,
629
+ "dice_cell": 0.32622973263263705,
630
+ "ce_nos": 0.023967032334767283,
631
+ "dice_nos": 0.2765471911430359,
632
+ "bce_ab": 0.04265340869314969,
633
+ "bce_cn": 0.07023378118872642,
634
+ "mse_dist": 0.01163030793890357,
635
+ "total": 0.9907851052284241,
636
  "iter": 2450,
637
  "lr": 3.1419155581639394e-05,
638
+ "elapsed_s": 1238.943860054016
639
  },
640
  {
641
+ "ce_cell": 0.19708714127540589,
642
+ "dice_cell": 0.3298483777046204,
643
+ "ce_nos": 0.035931861903518436,
644
+ "dice_nos": 0.2631306803226471,
645
+ "bce_ab": 0.03939212892204523,
646
+ "bce_cn": 0.07550869181752205,
647
+ "mse_dist": 0.011621671505272388,
648
+ "total": 0.99900723695755,
649
  "iter": 2500,
650
  "lr": 2.7124169014226923e-05,
651
+ "elapsed_s": 1264.1307349205017
652
  },
653
  {
654
+ "ce_cell": 0.19627166271209717,
655
+ "dice_cell": 0.3276835995912552,
656
+ "ce_nos": 0.030831458177417517,
657
+ "dice_nos": 0.2647592234611511,
658
+ "bce_ab": 0.035610632617026565,
659
+ "bce_cn": 0.06968229006975889,
660
+ "mse_dist": 0.011490394324064256,
661
+ "total": 0.98229083776474,
662
  "iter": 2550,
663
  "lr": 2.319839794721846e-05,
664
+ "elapsed_s": 1288.9724678993225
665
  },
666
  {
667
+ "ce_cell": 0.19514285027980804,
668
+ "dice_cell": 0.3292509812116623,
669
+ "ce_nos": 0.02648238055408001,
670
+ "dice_nos": 0.26873668015003205,
671
+ "bce_ab": 0.03310712873935699,
672
+ "bce_cn": 0.06389056332409382,
673
+ "mse_dist": 0.01165549136698246,
674
+ "total": 0.9748880290985107,
675
  "iter": 2600,
676
  "lr": 1.9653357343555055e-05,
677
+ "elapsed_s": 1313.9768908023834
678
  },
679
  {
680
+ "ce_cell": 0.19508351147174835,
681
+ "dice_cell": 0.32727956473827363,
682
+ "ce_nos": 0.0280744384881109,
683
+ "dice_nos": 0.26588015496730805,
684
+ "bce_ab": 0.0365638392791152,
685
+ "bce_cn": 0.07343160055577755,
686
+ "mse_dist": 0.011491857282817364,
687
+ "total": 0.9837723982334137,
688
  "iter": 2650,
689
  "lr": 1.6499445418129828e-05,
690
+ "elapsed_s": 1338.650880098343
691
  },
692
  {
693
+ "ce_cell": 0.19099752217531205,
694
+ "dice_cell": 0.32075556099414826,
695
+ "ce_nos": 0.029300750745460392,
696
+ "dice_nos": 0.2554633593559265,
697
+ "bce_ab": 0.045620939303189514,
698
+ "bce_cn": 0.07410217750817537,
699
+ "mse_dist": 0.01112760055810213,
700
+ "total": 0.971878319978714,
701
  "iter": 2700,
702
  "lr": 1.3745913138031944e-05,
703
+ "elapsed_s": 1363.5123808383942
704
  },
705
  {
706
+ "ce_cell": 0.1883079969882965,
707
+ "dice_cell": 0.32481179118156434,
708
+ "ce_nos": 0.031208774596452712,
709
+ "dice_nos": 0.2581752932071686,
710
+ "bce_ab": 0.038093772381544114,
711
+ "bce_cn": 0.06765574529767036,
712
+ "mse_dist": 0.011228110697120429,
713
+ "total": 0.9643939423561096,
714
  "iter": 2750,
715
  "lr": 1.1400837087866401e-05,
716
+ "elapsed_s": 1388.352870941162
717
  },
718
  {
719
+ "ce_cell": 0.1916240319609642,
720
+ "dice_cell": 0.326035658121109,
721
+ "ce_nos": 0.023255940917879345,
722
+ "dice_nos": 0.2671393311023712,
723
+ "bce_ab": 0.03772587236016989,
724
+ "bce_cn": 0.0679256134480238,
725
+ "mse_dist": 0.011575959492474794,
726
+ "total": 0.9715862393379211,
727
  "iter": 2800,
728
  "lr": 9.471095779739621e-06,
729
+ "elapsed_s": 1413.569692850113
730
  },
731
  {
732
+ "ce_cell": 0.19167108833789825,
733
+ "dice_cell": 0.32803118288516997,
734
+ "ce_nos": 0.02982857631519437,
735
+ "dice_nos": 0.25197103917598723,
736
+ "bce_ab": 0.03527032459154725,
737
+ "bce_cn": 0.06827950268983841,
738
+ "mse_dist": 0.011356610022485256,
739
+ "total": 0.961834750175476,
740
  "iter": 2850,
741
  "lr": 7.96234947739845e-06,
742
+ "elapsed_s": 1438.4622449874878
743
  },
744
  {
745
+ "ce_cell": 0.19785686254501342,
746
+ "dice_cell": 0.3267842435836792,
747
+ "ce_nos": 0.03312531067058444,
748
+ "dice_nos": 0.2531671738624573,
749
+ "bce_ab": 0.036567215006798506,
750
+ "bce_cn": 0.06692319795489311,
751
+ "mse_dist": 0.011690724939107895,
752
+ "total": 0.9728776228427887,
753
  "iter": 2900,
754
  "lr": 6.8790235937012375e-06,
755
+ "elapsed_s": 1463.5729517936707
756
  },
757
  {
758
+ "ce_cell": 0.1914523333311081,
759
+ "dice_cell": 0.3220532989501953,
760
+ "ce_nos": 0.02655307489912957,
761
+ "dice_nos": 0.26563074231147765,
762
+ "bce_ab": 0.04149866354651749,
763
+ "bce_cn": 0.06330326247960329,
764
+ "mse_dist": 0.011330173863098026,
765
+ "total": 0.9671422529220581,
766
  "iter": 2950,
767
  "lr": 6.224295710119515e-06,
768
+ "elapsed_s": 1481.2963027954102
769
  },
770
  {
771
+ "ce_cell": 0.19723920345306398,
772
+ "dice_cell": 0.331863129734993,
773
+ "ce_nos": 0.031104682530276477,
774
+ "dice_nos": 0.254480841755867,
775
+ "bce_ab": 0.036050576698035,
776
+ "bce_cn": 0.070169439278543,
777
+ "mse_dist": 0.011816464867442845,
778
+ "total": 0.9799901854991913,
779
  "iter": 3000,
780
  "lr": 6.000086256343946e-06,
781
+ "elapsed_s": 1496.7248327732086
782
  }
783
  ],
784
  "val": []