SharpMeow commited on
Commit
f0be2e8
·
verified ·
1 Parent(s): 0f3d4ae

Check against this Space's own fields where the open data has none; normals on the strong half; larger picture labels

Browse files
Files changed (2) hide show
  1. app.py +19 -10
  2. lasfield.py +67 -15
app.py CHANGED
@@ -200,8 +200,12 @@ def volume_text(vid):
200
  shape = v.get("shape_zyx") or ["?"] * 3
201
  f = 2 ** v["level"]
202
  pub = L.published_field(v)
203
- pub_line = ("The open data has villa's field for this scan, so each box is checked against it." if pub
204
- else "The open data has no lasagna field for this scan, so there is nothing to check against.")
 
 
 
 
205
  return (f"{' × '.join(map(str, shape))} voxels (z × y × x) at {v['voxel_um']:g} um. The model reads level "
206
  f"{v['level']} ({v['voxel_um'] * f:.2f} um), nearest to the 9.4 um it was trained at. {pub_line}")
207
 
@@ -233,12 +237,17 @@ def result_text(r):
233
 
234
  def fmt(v, unit=""):
235
  return "n/a" if v is None else f"{v}{unit}"
 
 
236
  lines.append(
237
- "\n**Check against the published field** (`" + a["published"] + "`): "
238
- f"cos r = {fmt(same['cos_r'])}, grad_mag r = {fmt(same['grad_mag_r'])}, normals {fmt(same['normal_median_deg'], '°')} "
239
- f"apart (median). Control, the published field {L.CONTROL_SHIFT} voxels away: cos r = "
240
- f"{fmt(ctl.get('cos_r'))}, normals {fmt(ctl.get('normal_median_deg'), '°')}. The same box should agree "
241
- "far better than the control.")
 
 
 
242
  elif a.get("note") or a.get("error"):
243
  lines.append(f"\nCheck: {a.get('note') or a.get('error')}")
244
  lines.append("\nThe download holds villa's own output folder (open `villa/*.lasagna.json` with lasagna's fit), "
@@ -444,7 +453,7 @@ CSS = """
444
  .lf-est p { font-size:13.5px; color: var(--body-text-color-subdued); }
445
  .lf-point { flex-wrap: nowrap !important; }
446
  .lf-credit { text-align:center; font-size:13px; color: var(--body-text-color-subdued); padding:18px 0 6px; }
447
- .lf-credit a { color: var(--lf-accent); }
448
  .lf-howto img { border-radius:10px; border:1px solid var(--border-color-primary); max-width:100%; }
449
  @media (max-width: 640px) {
450
  .lf-head h1 { font-size:24px; }
@@ -573,11 +582,11 @@ def build_demo():
573
  example = MEDIA / "example-result.png"
574
  pic = gr.Image(str(example) if example.is_file() else None,
575
  label="Middle slices along z, y, x: CT · cos · grad_mag · normal",
576
- type="numpy", format="png", interactive=False)
577
  summary = gr.Markdown("Above: what a result looks like (the first example point). Your "
578
  "field replaces it. The first run after the Space wakes up also loads "
579
  "the model (about a minute).")
580
- dl = gr.File(label="Download: villa output folder, field.npz, receipt.json")
581
  with gr.Accordion("Receipt", open=False):
582
  receipt = gr.JSON()
583
  with gr.Accordion("About the model", open=False):
 
200
  shape = v.get("shape_zyx") or ["?"] * 3
201
  f = 2 ** v["level"]
202
  pub = L.published_field(v)
203
+ if pub and pub.get("source") == "lasagna-fields":
204
+ pub_line = "This Space has a whole-scan field for this scan, so each box is checked against it."
205
+ elif pub:
206
+ pub_line = "The open data has villa's field for this scan, so each box is checked against it."
207
+ else:
208
+ pub_line = "No lasagna field has been published for this scan, so there is nothing to check against."
209
  return (f"{' × '.join(map(str, shape))} voxels (z × y × x) at {v['voxel_um']:g} um. The model reads level "
210
  f"{v['level']} ({v['voxel_um'] * f:.2f} um), nearest to the 9.4 um it was trained at. {pub_line}")
211
 
 
237
 
238
  def fmt(v, unit=""):
239
  return "n/a" if v is None else f"{v}{unit}"
240
+ where = ("this Space's whole-scan field" if a.get("published_source") == "lasagna-fields"
241
+ else "the open data's field")
242
  lines.append(
243
+ f"\n**Check against {where}** (`" + a["published"] + "`): "
244
+ f"cos r = {fmt(same['cos_r'])}, grad_mag r = {fmt(same['grad_mag_r'])}, normals "
245
+ f"{fmt(same.get('normal_median_deg_strong'), '°')} apart where the layer signal is strong "
246
+ f"({fmt(same['normal_median_deg'], '°')} over every voxel; medians). Control, the same field "
247
+ f"{L.CONTROL_SHIFT} voxels away: cos r = {fmt(ctl.get('cos_r'))}, normals "
248
+ f"{fmt(ctl.get('normal_median_deg_strong'), '°')}. The same box should agree far better than the control.")
249
+ if a.get("normals_note"):
250
+ lines.append(f"\n{a['normals_note']}.")
251
  elif a.get("note") or a.get("error"):
252
  lines.append(f"\nCheck: {a.get('note') or a.get('error')}")
253
  lines.append("\nThe download holds villa's own output folder (open `villa/*.lasagna.json` with lasagna's fit), "
 
453
  .lf-est p { font-size:13.5px; color: var(--body-text-color-subdued); }
454
  .lf-point { flex-wrap: nowrap !important; }
455
  .lf-credit { text-align:center; font-size:13px; color: var(--body-text-color-subdued); padding:18px 0 6px; }
456
+ .lf-credit a { color: var(--lf-accent) !important; padding:0 !important; margin:0 !important; }
457
  .lf-howto img { border-radius:10px; border:1px solid var(--border-color-primary); max-width:100%; }
458
  @media (max-width: 640px) {
459
  .lf-head h1 { font-size:24px; }
 
582
  example = MEDIA / "example-result.png"
583
  pic = gr.Image(str(example) if example.is_file() else None,
584
  label="Middle slices along z, y, x: CT · cos · grad_mag · normal",
585
+ type="numpy", format="png", interactive=False, show_label=False)
586
  summary = gr.Markdown("Above: what a result looks like (the first example point). Your "
587
  "field replaces it. The first run after the Space wakes up also loads "
588
  "the model (about a minute).")
589
+ dl = gr.File(label="Download: villa output folder, field.npz, receipt.json", height=88)
590
  with gr.Accordion("Receipt", open=False):
591
  receipt = gr.JSON()
592
  with gr.Accordion("About the model", open=False):
lasfield.py CHANGED
@@ -155,6 +155,21 @@ def zarray(v, level):
155
 
156
 
157
  _PUBLISHED = {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
158
 
159
 
160
  def published_field(v):
@@ -174,10 +189,25 @@ def published_field(v):
174
  found = dict(found, note=f"published for {old['volume']}, which this volume replaces in the "
175
  "same voxel frame")
176
  break
 
 
177
  _PUBLISHED[key] = found
178
  return found
179
 
180
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  def _published_run(v):
182
  found = None
183
  try:
@@ -195,7 +225,8 @@ def _published_run(v):
195
  if js:
196
  manifest = get_json(f"{BUCKET}/{js[0]}")
197
  found = {"run": name, "url": f"{BUCKET}/{run}", "manifest": f"{BUCKET}/{js[0]}",
198
- "groups": manifest.get("groups"), "base_shape_zyx": manifest.get("base_shape_zyx")}
 
199
  except FieldError as exc:
200
  log(f"published field lookup for {v['volume']} failed: {exc}")
201
  return found
@@ -537,7 +568,9 @@ def _corr(a, b, mask):
537
 
538
  def compare(mine, theirs):
539
  """Agreement of two fields on the same grids: Pearson r of cos and grad_mag where either is non-zero, and the
540
- median angle between normals (sign ignored) where both are."""
 
 
541
  m_cos = (mine["cos"] > 0) | (theirs["cos"] > 0)
542
  m_oth = (mine["grad_mag"] > 0) | (theirs["grad_mag"] > 0)
543
  both = (mine["nx"] > 0) & (theirs["nx"] > 0)
@@ -546,8 +579,13 @@ def compare(mine, theirs):
546
  "voxels": int(m_cos.sum())}
547
  if both.sum() >= 100:
548
  a, b = decode_normal(mine["nx"], mine["ny"]), decode_normal(theirs["nx"], theirs["ny"])
549
- dot = np.abs((a * b).sum(axis=0))[both]
550
- out["normal_median_deg"] = round(float(np.degrees(np.arccos(np.clip(np.median(dot), 0, 1)))), 2)
 
 
 
 
 
551
  return out
552
 
553
 
@@ -570,6 +608,12 @@ def read_published(pub, boxes, shift=(0, 0, 0)):
570
  CONTROL_SHIFT = 48 # input-level voxels: the control reads the published field this far away in y and x
571
 
572
 
 
 
 
 
 
 
573
  def agreement(p, arrays, boxes):
574
  """How close this field is to the open data's own lasagna field for the scan, where one exists, with a control
575
  that must score lower: the published field read CONTROL_SHIFT input voxels away along y and x (a few papyrus
@@ -592,7 +636,12 @@ def agreement(p, arrays, boxes):
592
  return {"published": pub["run"], "error": f"{type(exc).__name__}: {exc}"[:300]}
593
  if same is None:
594
  return {"published": pub["run"], "note": "the published field uses other grids; not compared"}
595
- out = {"published": pub["run"], "published_url": pub["url"], "same_box": compare(arrays, same)}
 
 
 
 
 
596
  if shifted is not None:
597
  out["control_shifted"] = compare(arrays, shifted)
598
  out["control_note"] = (f"the published field {CONTROL_SHIFT} input voxels away in y and x: "
@@ -646,26 +695,29 @@ PANELS = ("CT", "cos", "grad_mag", "normal (x red, y green, z blue)")
646
 
647
  def compose(pictures, title=""):
648
  """The three rows (z, y, x middle slices) under one header naming the panels, each row named on its left, on a
649
- white card. Returns an RGB uint8 array."""
650
  from PIL import Image, ImageDraw, ImageFont
651
- try:
652
- font = ImageFont.load_default(size=14)
653
- except TypeError: # Pillow before 10.1
654
- font = ImageFont.load_default()
655
  rows = [_fit(r, pictures[0].shape[:2]) if r.shape != pictures[0].shape else r for r in pictures]
656
  h, w = rows[0].shape[:2]
 
 
 
 
 
 
657
  pw = (w - 18) // 4
658
- left, top, gap = 64, 30 + (26 if title else 0), 8
659
- canvas = Image.new("RGB", (left + w + 8, top + 3 * h + 2 * gap + 8), (255, 255, 255))
 
660
  d = ImageDraw.Draw(canvas)
661
  if title:
662
- d.text((left, 6), title, fill=(35, 32, 28), font=font)
663
  for i, name in enumerate(PANELS):
664
- d.text((left + i * (pw + 6) + 2, top - 20), name, fill=(93, 85, 75), font=font)
665
  for i, (r, name) in enumerate(zip(rows, ("z slice", "y slice", "x slice"))):
666
  y0 = top + i * (h + gap)
667
  canvas.paste(Image.fromarray(r), (left, y0))
668
- d.text((6, y0 + h // 2 - 8), name, fill=(93, 85, 75), font=font)
669
  return np.asarray(canvas)
670
 
671
 
 
155
 
156
 
157
  _PUBLISHED = {}
158
+ # the ten whole-scan fields this Space hosts, made with the same villa commit and checkpoint as Make a field: volume id
159
+ # to (sample, run folder) in the public bucket; the check uses them where the open data has no field
160
+ OWN_BUCKET = "https://huggingface.co/buckets/SharpMeow/lasagna-fields/resolve"
161
+ OWN_FIELDS = {
162
+ "20250521115057": ("PHerc0175A", "20250521115057-lasagna-de45f011"),
163
+ "20250521125822": ("PHerc0175B", "20250521125822-lasagna-de45f011"),
164
+ "20250521133212": ("PHerc0306B", "20250521133212-lasagna-de45f011"),
165
+ "20250521140913": ("PHerc0483A", "20250521140913-lasagna-de45f011"),
166
+ "20251124083638": ("PHerc0483B", "20251124083638-lasagna-de45f011"),
167
+ "20250521151210": ("PHerc0490A", "20250521151210-lasagna-de45f011"),
168
+ "20250521151215": ("PHerc0490B", "20250521151215-lasagna-de45f011"),
169
+ "20250821151531": ("PHerc0841", "20250821151531-lasagna-de45f011"),
170
+ "20250728152254": ("PHerc0846A", "20250728152254-lasagna-de45f011"),
171
+ "20250804142305": ("PHerc0846B", "20250804142305-lasagna-de45f011"),
172
+ }
173
 
174
 
175
  def published_field(v):
 
189
  found = dict(found, note=f"published for {old['volume']}, which this volume replaces in the "
190
  "same voxel frame")
191
  break
192
+ if found is None and key in OWN_FIELDS and v["level"] == 0:
193
+ found = _own_run(*OWN_FIELDS[key])
194
  _PUBLISHED[key] = found
195
  return found
196
 
197
 
198
+ def _own_run(sample, run):
199
+ """One of this Space's ten whole-scan fields, described like an open-data run."""
200
+ url = f"{OWN_BUCKET}/{sample}/representations/predictions/lasagna/{run}/"
201
+ try:
202
+ manifest = get_json(f"{url}{sample}.lasagna.json")
203
+ except FieldError as exc:
204
+ log(f"own field lookup for {sample} failed: {exc}")
205
+ return None
206
+ return {"run": run, "url": url, "manifest": f"{url}{sample}.lasagna.json", "groups": manifest.get("groups"),
207
+ "base_shape_zyx": manifest.get("base_shape_zyx"), "source": "lasagna-fields",
208
+ "note": "this Space's own whole-scan field, made with the same villa commit and checkpoint"}
209
+
210
+
211
  def _published_run(v):
212
  found = None
213
  try:
 
225
  if js:
226
  manifest = get_json(f"{BUCKET}/{js[0]}")
227
  found = {"run": name, "url": f"{BUCKET}/{run}", "manifest": f"{BUCKET}/{js[0]}",
228
+ "groups": manifest.get("groups"), "base_shape_zyx": manifest.get("base_shape_zyx"),
229
+ "source": "open-data"}
230
  except FieldError as exc:
231
  log(f"published field lookup for {v['volume']} failed: {exc}")
232
  return found
 
568
 
569
  def compare(mine, theirs):
570
  """Agreement of two fields on the same grids: Pearson r of cos and grad_mag where either is non-zero, and the
571
+ median angle between normals (sign ignored) where both are, over all those voxels and over the strong half (our
572
+ grad_mag at or above its median there). Where the layer signal is weak the normal is barely defined and tiny
573
+ numerical differences turn it, so the strong half is the fair test."""
574
  m_cos = (mine["cos"] > 0) | (theirs["cos"] > 0)
575
  m_oth = (mine["grad_mag"] > 0) | (theirs["grad_mag"] > 0)
576
  both = (mine["nx"] > 0) & (theirs["nx"] > 0)
 
579
  "voxels": int(m_cos.sum())}
580
  if both.sum() >= 100:
581
  a, b = decode_normal(mine["nx"], mine["ny"]), decode_normal(theirs["nx"], theirs["ny"])
582
+ dot = np.abs((a * b).sum(axis=0))
583
+ out["normal_median_deg"] = round(float(np.degrees(np.arccos(np.clip(np.median(dot[both]), 0, 1)))), 2)
584
+ g = mine["grad_mag"]
585
+ strong = both & (g >= np.median(g[both]))
586
+ if strong.sum() >= 100:
587
+ out["normal_median_deg_strong"] = round(
588
+ float(np.degrees(np.arccos(np.clip(np.median(dot[strong]), 0, 1)))), 2)
589
  return out
590
 
591
 
 
608
  CONTROL_SHIFT = 48 # input-level voxels: the control reads the published field this far away in y and x
609
 
610
 
611
+ OPEN_DATA_NORMALS = ("cos and grad_mag should match the open data's field exactly. Its normals can differ in "
612
+ "parts of a box: villa changed how lasagna estimates normals after the open data's fields "
613
+ "were made (April 2026, by their run names), and Make a field runs the later code, the same "
614
+ "as this Space's ten whole-scan fields, which it matches exactly")
615
+
616
+
617
  def agreement(p, arrays, boxes):
618
  """How close this field is to the open data's own lasagna field for the scan, where one exists, with a control
619
  that must score lower: the published field read CONTROL_SHIFT input voxels away along y and x (a few papyrus
 
636
  return {"published": pub["run"], "error": f"{type(exc).__name__}: {exc}"[:300]}
637
  if same is None:
638
  return {"published": pub["run"], "note": "the published field uses other grids; not compared"}
639
+ out = {"published": pub["run"], "published_url": pub["url"], "published_source": pub.get("source"),
640
+ "same_box": compare(arrays, same)}
641
+ if pub.get("note"):
642
+ out["published_note"] = pub["note"]
643
+ if pub.get("source") == "open-data":
644
+ out["normals_note"] = OPEN_DATA_NORMALS
645
  if shifted is not None:
646
  out["control_shifted"] = compare(arrays, shifted)
647
  out["control_note"] = (f"the published field {CONTROL_SHIFT} input voxels away in y and x: "
 
695
 
696
  def compose(pictures, title=""):
697
  """The three rows (z, y, x middle slices) under one header naming the panels, each row named on its left, on a
698
+ white card. Text is sized for the card to be shown about 700 px wide. Returns an RGB uint8 array."""
699
  from PIL import Image, ImageDraw, ImageFont
 
 
 
 
700
  rows = [_fit(r, pictures[0].shape[:2]) if r.shape != pictures[0].shape else r for r in pictures]
701
  h, w = rows[0].shape[:2]
702
+ k = max(1.0, w / 640)
703
+ try:
704
+ font = ImageFont.load_default(size=round(14 * k))
705
+ bold = ImageFont.load_default(size=round(16 * k))
706
+ except TypeError: # Pillow before 10.1
707
+ font = bold = ImageFont.load_default()
708
  pw = (w - 18) // 4
709
+ left, gap = round(66 * k), round(8 * k)
710
+ top = round(26 * k) + (round(30 * k) if title else 0)
711
+ canvas = Image.new("RGB", (left + w + gap, top + 3 * h + 3 * gap), (255, 255, 255))
712
  d = ImageDraw.Draw(canvas)
713
  if title:
714
+ d.text((left, round(6 * k)), title, fill=(35, 32, 28), font=bold)
715
  for i, name in enumerate(PANELS):
716
+ d.text((left + i * (pw + 6) + 2, top - round(21 * k)), name, fill=(93, 85, 75), font=font)
717
  for i, (r, name) in enumerate(zip(rows, ("z slice", "y slice", "x slice"))):
718
  y0 = top + i * (h + gap)
719
  canvas.paste(Image.fromarray(r), (left, y0))
720
+ d.text((round(6 * k), y0 + h // 2 - round(8 * k)), name, fill=(93, 85, 75), font=font)
721
  return np.asarray(canvas)
722
 
723