Spaces:
Running
Running
File size: 4,985 Bytes
e4a7008 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 | """Build a MedCLIPSeg dataset for the neurofilament task (no manual masks needed).
For every CZI with an IMARIS _Statistics folder, writes:
data/Neurofilament/<split>_Folder/img/<name>.png (neurofilament MIP, RGB)
data/Neurofilament/<split>_Folder/label/<name>.png (IMARIS-reconstruction mask)
data/Neurofilament/Prompts_Folder/<split>_text.xlsx (Image, Ground Truth, Description)
The mask is the k-NN reconstruction of IMARIS's traced skeleton from its segment
coordinates (dilated) — the same approximate label used elsewhere. Splits are by
ANIMAL so validation/test are unseen animals. Then fine-tune the bundled model:
cd reference_medclipseg
python train.py --config-file configs/Neurofilament.yaml # needs a CUDA GPU
Honest note: this label is approximate (reconstructed, not IMARIS's exact voxel
mask), and the target is IMARIS's over-traced tangle — see README.
"""
import argparse, glob, os, sys
import numpy as np
import pandas as pd
from scipy import ndimage as ndi
from scipy.spatial import cKDTree
from skimage.transform import resize
from skimage.io import imsave
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import processing as P
ROOT = "/Users/slf20757/Desktop/Dr. Fernandes"
SIZE = 224
PROMPT = "a fluorescence microscopy image of neurofilament nerve fibers"
# animal -> split
VAL = {"C1"}
TEST = {"C2b"}
def seg_xyz(sd, stem):
def cols(name, nc):
r = []
for line in open(os.path.join(sd, f"{stem}_{name}.csv"), errors="ignore"):
p = line.split(",")
try:
r.append([float(p[i]) for i in range(nc)])
except (ValueError, IndexError):
pass
return np.array(r)
return cols("Segment_Position", 3)
def skel_mask(pos, voxel, shape_yx, k=3):
dz, dy, dx = voxel; ny, nx = shape_yx
col = pos[:, 0] / dx * (SIZE / nx); row = pos[:, 1] / dy * (SIZE / ny)
pts = np.stack([row, col], 1)
tree = cKDTree(pts); dist, idx = tree.query(pts, k=min(k + 1, len(pts)))
radius = 2.0 / dx * (SIZE / nx)
m = np.zeros((SIZE, SIZE), bool)
for i in range(len(pts)):
for j, d in zip(idx[i, 1:], dist[i, 1:]):
if d <= radius:
p0, p1 = pts[i], pts[j]
n = max(int(np.abs(p1 - p0).max()) + 1, 2)
t = np.linspace(0, 1, n)[:, None]; line = p0 + t * (p1 - p0)
m[np.clip(line[:, 0].astype(int), 0, SIZE - 1),
np.clip(line[:, 1].astype(int), 0, SIZE - 1)] = True
return ndi.binary_dilation(m, iterations=1)
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--out", default="data/Neurofilament")
ap.add_argument("--limit", type=int, default=0, help="cap images (for testing)")
args = ap.parse_args()
rows = {"Train": [], "Val": [], "Test": []}
n = 0
for csv in sorted(glob.glob(os.path.join(ROOT, "*", "*_Statistics",
"*_Filament_Length_(sum).csv"))):
sd = os.path.dirname(csv)
stem = os.path.basename(csv).replace("_Filament_Length_(sum).csv", "")
animal = os.path.relpath(csv, ROOT).split(os.sep)[0]
czi = os.path.join(ROOT, animal, stem.replace(" filament", "") + ".czi")
if not (os.path.exists(czi) and
os.path.exists(os.path.join(sd, f"{stem}_Segment_Position_X.csv"))):
continue
split = "Val" if animal in VAL else "Test" if animal in TEST else "Train"
img = P.load_image(czi); nf, _ = P.guess_channels(img)
vol = img.data[nf]; ny, nx = vol.shape[1], vol.shape[2]
mip = resize(P.channel_preview(vol), (SIZE, SIZE), preserve_range=True).astype(np.uint8)
mask = (skel_mask(seg_xyz(sd, stem), img.voxel, (ny, nx)) * 255).astype(np.uint8)
name = stem.replace(" ", "_") + ".png"
for sub in ("img", "label"):
os.makedirs(os.path.join(args.out, f"{split}_Folder", sub), exist_ok=True)
imsave(os.path.join(args.out, f"{split}_Folder", "img", name),
np.stack([mip] * 3, -1))
imsave(os.path.join(args.out, f"{split}_Folder", "label", name), mask)
rows[split].append({"Image": name, "Ground Truth": name, "Description": PROMPT})
n += 1
print(f"[{split}] {stem}: mask px={int((mask>0).sum())}", flush=True)
del img, vol
if args.limit and n >= args.limit:
break
os.makedirs(os.path.join(args.out, "Prompts_Folder"), exist_ok=True)
for split, r in rows.items():
if r:
pd.DataFrame(r).to_excel(
os.path.join(args.out, "Prompts_Folder", f"{split}_text.xlsx"),
index=False)
print(f"\nDone. Train={len(rows['Train'])} Val={len(rows['Val'])} "
f"Test={len(rows['Test'])} -> {args.out}")
print("Now fine-tune (GPU): cd reference_medclipseg && "
"python train.py --config-file configs/Neurofilament.yaml")
if __name__ == "__main__":
main()
|