sonocnn / examples /predict_demo.py
habib-analyst's picture
Upload sonocnn (mirror of github.com/habib-analyst/sonocnn)
179372e verified
Raw History Blame Contribute Delete
2.73 kB
"""SonoCNN demo: predict the bundled samples, a few held-out test images,
and — if the sibling ViewConsensus library is importable — an 8-view
consensus panel with a defer-to-human verdict on one test image.
This is a mechanics demo on a 28x28 benchmark model, not a clinical tool.
"""
import os
import sys
import numpy as np
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "src"))
HERE = os.path.dirname(os.path.abspath(__file__))
PKG = os.path.dirname(HERE)
from sonocnn import load_model, predict # noqa: E402
model = load_model(os.path.join(PKG, "model.safetensors"))
print("== bundled demo samples (256x256 ultrasound stills, resized to 28x28) ==")
for name in ("benign", "malignant"):
r = predict(model, os.path.join(PKG, "assets", "demo_samples", f"{name}.png"))
print(f"{name}.png -> predicted {r['label']} "
f"(confidence {r['confidence']:.1%}; benign {r['probabilities']['benign']:.3f}, "
f"malignant {r['probabilities']['malignant']:.3f})")
print("\n== held-out BreastMNIST test images ==")
d = np.load(os.path.join(PKG, "assets", "breastmnist.npz"))
x_test = d["test_images"]
y_test = d["test_labels"].ravel()
labels = ("benign", "malignant")
for cls in (0, 1):
idx = int(np.where(y_test == cls)[0][0])
r = predict(model, x_test[idx])
print(f"test[{idx}] true={labels[cls]} -> predicted {r['label']} "
f"(confidence {r['confidence']:.1%})")
print("\n== ViewConsensus panel on the malignant test image (optional) ==")
try:
sys.path.insert(0, os.path.join(PKG, "..", "..", "libraries", "consensuskit", "src"))
from viewconsensus import consensus_from_probabilities
import torch
from sonocnn.infer import TRAIN_MEAN, TRAIN_STD
img = x_test[int(np.where(y_test == 1)[0][0])].astype(np.float32) / 255.0
def shift(a, dy, dx):
return np.roll(np.roll(a, dy, axis=0), dx, axis=1)
views = [img, img[:, ::-1], img[::-1, :], shift(img, 0, 2), shift(img, 0, -2),
shift(img, 2, 0), shift(img, -2, 0), shift(img, 2, 2)[:, ::-1]]
batch = torch.from_numpy(
np.stack([((v - TRAIN_MEAN) / TRAIN_STD).astype(np.float32) for v in views]))[:, None]
with torch.no_grad():
probs = torch.softmax(model(batch), 1).numpy()
res = consensus_from_probabilities(probs, labels=list(labels))
verdict = getattr(res.verdict, "value", res.verdict)
print(f"consensus: {res.label} at {res.probability:.1%} "
f"(+/-{res.uncertainty:.1%} across 8 views) -> verdict: {verdict}, "
f"should_defer: {res.should_defer}")
except ImportError:
print("viewconsensus not importable here — install the sibling ViewConsensus "
"library to see the consensus panel.")