diff --git a/paper_figures/fig0_graphical_abstract.pdf b/paper_figures/fig0_graphical_abstract.pdf index 109ff296..61a7d0ca 100644 Binary files a/paper_figures/fig0_graphical_abstract.pdf and b/paper_figures/fig0_graphical_abstract.pdf differ diff --git a/paper_figures/fig0_graphical_abstract.png b/paper_figures/fig0_graphical_abstract.png index 6ac72a54..35e41dc0 100644 Binary files a/paper_figures/fig0_graphical_abstract.png and b/paper_figures/fig0_graphical_abstract.png differ diff --git a/paper_figures/fig1_interface.pdf b/paper_figures/fig1_interface.pdf index 877b4bdf..605266e0 100644 Binary files a/paper_figures/fig1_interface.pdf and b/paper_figures/fig1_interface.pdf differ diff --git a/paper_figures/fig1_interface.png b/paper_figures/fig1_interface.png index 68f6ecea..a1cc131a 100644 Binary files a/paper_figures/fig1_interface.png and b/paper_figures/fig1_interface.png differ diff --git a/paper_figures/fig2_learning_cycle.pdf b/paper_figures/fig2_learning_cycle.pdf index e2d51987..5b057276 100644 Binary files a/paper_figures/fig2_learning_cycle.pdf and b/paper_figures/fig2_learning_cycle.pdf differ diff --git a/paper_figures/fig3_landmarks.pdf b/paper_figures/fig3_landmarks.pdf index 3be2b482..744daf5a 100644 Binary files a/paper_figures/fig3_landmarks.pdf and b/paper_figures/fig3_landmarks.pdf differ diff --git a/paper_figures/fig4_qc_catch.pdf b/paper_figures/fig4_qc_catch.pdf index 86a7f26b..46ad6b46 100644 Binary files a/paper_figures/fig4_qc_catch.pdf and b/paper_figures/fig4_qc_catch.pdf differ diff --git a/paper_figures/fig5_difficulty.pdf b/paper_figures/fig5_difficulty.pdf index 3d4b47e5..be2a1a3f 100644 Binary files a/paper_figures/fig5_difficulty.pdf and b/paper_figures/fig5_difficulty.pdf differ diff --git a/paper_figures/fig5_difficulty.png b/paper_figures/fig5_difficulty.png index 22477b10..aa6cfa90 100644 Binary files a/paper_figures/fig5_difficulty.png and b/paper_figures/fig5_difficulty.png differ diff --git a/paper_figures/fig6_two_difficulties.pdf b/paper_figures/fig6_two_difficulties.pdf index 9989988e..7574b31b 100644 Binary files a/paper_figures/fig6_two_difficulties.pdf and b/paper_figures/fig6_two_difficulties.pdf differ diff --git a/paper_figures/ga_crop.png b/paper_figures/ga_crop.png index dc387e72..84605593 100644 Binary files a/paper_figures/ga_crop.png and b/paper_figures/ga_crop.png differ diff --git a/paper_figures/ss_learn_hero.png b/paper_figures/ss_learn_hero.png index b569aa57..c11a6075 100644 Binary files a/paper_figures/ss_learn_hero.png and b/paper_figures/ss_learn_hero.png differ diff --git a/scripts/make_figures.py b/scripts/make_figures.py index 45dd7506..8c03678f 100644 --- a/scripts/make_figures.py +++ b/scripts/make_figures.py @@ -365,10 +365,10 @@ def fig5_difficulty(): for thr, lab in ((0.5, ""), (1.0, ""), (2.0, "")): axA.axvline(thr, color="#c8c8c8", ls=":", lw=0.9, zorder=0) axA.set_yticks(y) - axA.set_yticklabels([a for a, _ in items], fontsize=5.6) + axA.set_yticklabels([a for a, _ in items], fontsize=8) axA.set_ylim(-0.7, len(items) - 0.3) axA.set_xlim(0, 2.6) - axA.set_xlabel("Trained-rater localization error, AFLE (mm)", fontsize=8.5) + axA.set_xlabel("Trained-rater localization error, AFLE (mm)", fontsize=11) axA.plot([], [], "o", color="#555", ms=5.5, label="median") axA.plot( [], [], "x", color="#555", ms=4.5, mew=1.1, label="mean (heavy tail)" @@ -377,9 +377,9 @@ def fig5_difficulty(): [], [], "-", color="#999", lw=2.6, alpha=0.6, label="IQR (p25–p75)" ) axA.legend( - fontsize=6.3, loc="lower right", frameon=False, handletextpad=0.4 + fontsize=9, loc="lower right", frameon=False, handletextpad=0.4 ) - panel_letter(axA, "A", "A 3.9× difficulty spectrum across 32 landmarks") + panel_letter(axA, "A", "") # Panel B — the same 1.2 mm placement, two verdicts axB = fig.add_subplot(gs[0, 1]) @@ -417,40 +417,13 @@ def fig5_difficulty(): markeredgecolor="white", markeredgewidth=0.8, ) - xs, ys = _pctile_curve(s) - pct = float(np.interp(probe, xs, ys)) # error rank (drives the verdict) - p = round(100 - pct) # precision pct shown (higher = better) - suf = ("th" if 11 <= p % 100 <= 13 - else {1: "st", 2: "nd", 3: "rd"}.get(p % 10, "th")) - verdict = ( - f"within range\n(≈{p}{suf} pct, near median)" - if pct < 75 - else f"outside expert range\n(≈{p}{suf} pct)" - ) - axB.annotate( - f"{a}: {verdict}", - (probe, yi + 0.22), - fontsize=7.2, - ha="center", - va="bottom", - color=c, - fontweight="bold", - ) axB.axvline(probe, color="#111111", ls="--", lw=1.3, zorder=5) - axB.annotate( - "one placement:\n1.2 mm from target", - (probe, -0.62), - fontsize=7.4, - ha="center", - va="top", - color="#111111", - ) axB.set_yticks([0, 1]) - axB.set_yticklabels(["LIGO\n(hard)", "AC\n(easy)"], fontsize=7.5) + axB.set_yticklabels(["LIGO\n(hard)", "AC\n(easy)"], fontsize=10.5) axB.set_ylim(-1.05, 1.7) axB.set_xlim(0, 4.0) - axB.set_xlabel("Trained-rater AFLE distribution (mm)", fontsize=8.5) - panel_letter(axB, "B", "The same 1.2 mm — two verdicts") + axB.set_xlabel("Trained-rater AFLE distribution (mm)", fontsize=11) + panel_letter(axB, "B", "") # Panel C — the mm→percentile calibration the platform applies axC = fig.add_subplot(gs[0, 2]) @@ -480,17 +453,16 @@ def fig5_difficulty(): axC.axhline(50, color="#dddddd", lw=0.8, zorder=0) axC.set_xlim(0, 4) axC.set_ylim(0, 100) - axC.set_xlabel("Placement error (mm)", fontsize=8.5) - axC.set_ylabel("Precision percentile (vs trained raters)", fontsize=8.5) + axC.set_xlabel("Placement error (mm)", fontsize=11) + axC.set_ylabel("Precision percentile (vs trained raters)", fontsize=11) axC.legend( - fontsize=7, + fontsize=9, loc="lower right", frameon=False, title="landmark", - title_fontsize=7, + title_fontsize=9, ) - axC.annotate("1.2 mm", (1.2, 4), fontsize=6.6, color="#111111", ha="left") - panel_letter(axC, "C", "One error, calibrated per landmark") + panel_letter(axC, "C", "") _save(fig, "fig5_difficulty") @@ -872,23 +844,6 @@ def fig1_interface(): color="black", zorder=6, ) - legend = ( - "\u2460 Multiplanar MRI viewer (NiiVue) " - "\u2461 Rater-calibrated result: error + percentile vs raters\n" - "\u2462 Difficulty-aware, anatomy-first AI tutor " - "\u2463 Bring-your-own-key + active model " - "\u2464 32-landmark progress + export" - ) - fig.text( - 0.5, - 0.05, - legend, - ha="center", - va="center", - fontsize=10.5, - color="#222222", - linespacing=1.9, - ) _save(fig, "fig1_interface") @@ -1017,9 +972,11 @@ def fig0_graphical_abstract(): figure set still regenerates from ``python make_figures.py``. """ import runpy + from pathlib import Path as _P - runpy.run_path("make_ga_assets.py", run_name="__main__") - runpy.run_path("make_graphical_abstract.py", run_name="__main__") + here = _P(__file__).resolve().parent + runpy.run_path(str(here / "make_ga_assets.py"), run_name="__main__") + runpy.run_path(str(here / "make_graphical_abstract.py"), run_name="__main__") if __name__ == "__main__": diff --git a/scripts/make_graphical_abstract.py b/scripts/make_graphical_abstract.py index 7aaa0b01..79d5dd89 100644 --- a/scripts/make_graphical_abstract.py +++ b/scripts/make_graphical_abstract.py @@ -527,50 +527,6 @@ def ic_file(cx, cy, s, color): ) -def ic_chat(cx, cy, s, color): - w, h = s * 1.7, s * 1.25 - rrect( - cx - w / 2, - cy - h / 2 + 0.12, - w, - h, - fc="white", - ec=color, - lw=1.6, - r=0.14, - z=7, - ) - ax.add_patch( - Polygon( - [ - (cx - s * 0.2, cy - h / 2 + 0.18), - (cx - s * 0.55, cy - h / 2 - 0.14), - (cx + s * 0.12, cy - h / 2 + 0.18), - ], - closed=True, - fc="white", - ec=color, - lw=1.6, - zorder=7, - joinstyle="round", - ) - ) - ax.add_patch( - Rectangle( - (cx - s * 0.5, cy - h / 2 + 0.14), - s * 0.7, - 0.1, - fc="white", - ec="none", - zorder=7.15, - ) - ) - for dx in (-0.42, 0, 0.42): - ax.add_patch( - Circle((cx + dx * s, cy + 0.14), s * 0.11, fc=color, zorder=7.2) - ) - - def ic_gear(cx, cy, s, color): n, outer, inner = 8, s, s * 0.66 pts = [ @@ -1159,40 +1115,8 @@ def data_row(y, nb, count, label, color): ) # ══════════════════════════════════════════════════════════════════════════════ -# THE METRIC — a slim shared header spanning both product cards -# (the "same mm, two verdicts" proof lives inside the cards themselves) +# (metric band removed) # ══════════════════════════════════════════════════════════════════════════════ -_thesis = "One Euclidean error — two uses" -_ty = BR_y0 + 0.40 -txt(SEAM, _ty, _thesis, size=11.5, weight="bold", color=SLATE, ha="center") -txt( - SEAM, - BR_y0 + 0.17, - "the same millimetre, always read against how hard the landmark is", - size=8.2, - color=MUT, - ha="center", - style="italic", -) -# quiet colour cue mapping the shared metric onto each card, no heavy arrows -txt( - M + 0.30, - BR_y0 + 0.13, - "taught in context", - size=7.8, - color=LEARN_C, - weight="bold", - ha="left", -) -txt( - XR - 0.30, - BR_y0 + 0.13, - "audited at scale", - size=7.8, - color=VALID_C, - weight="bold", - ha="right", -) # ══════════════════════════════════════════════════════════════════════════════ # 03 — LEARN (left card) @@ -1200,9 +1124,8 @@ def data_row(y, nb, count, label, color): LX0 = M LX1 = M + CARDW card("LEARN", LX0, P_y0, CARDW, H_PROD, LEARN_C) -ic_chat(LX0 + 0.42, P_y1 - 0.38, 0.18, LEARN_C) section( - LX0 + 0.78, + LX0 + 0.42, P_y1 - 0.38, "02", LEARN_C, @@ -1345,9 +1268,8 @@ def data_row(y, nb, count, label, color): VX0 = M + CARDW + GAP VX1 = XR card("VALIDATE", VX0, P_y0, CARDW, H_PROD, VALID_C) -ic_gear(VX0 + 0.42, P_y1 - 0.38, 0.17, VALID_C) section( - VX0 + 0.78, + VX0 + 0.42, P_y1 - 0.38, "03", VALID_C,