Skip to content

Commit bab6b00

Browse files
authored
Merge pull request #2 from berenslab/update
v0.1.12
2 parents 81f4e65 + ceb9fa6 commit bab6b00

File tree

4 files changed

+143
-79
lines changed

4 files changed

+143
-79
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ output
9999
- switch the conformal map with `--mapping j1` (default `j2`: much faster, but slightly less accurate);
100100
- change the z-extends for the stratification profile, e.g.: `flatone SEG_ID --overwrite-profile --z-profile-extent -30 50`
101101
- change the soma detection threshold if the default failed, e.g.: `flatone SEG_ID --soma-threshold 90 --overwrite-skeleton --overwrite-profile`
102-
- change the initial soma guess (needed if there's no soma in the mesh), e.g.: `flatone SEG_ID --soma-init-guess-mode max --overwrite-skeleton --overwrite-profile`
102+
- change the initial soma guess (needed if there's no soma in the mesh), e.g.: `flatone SEG_ID --soma-init-guess z max --overwrite-skeleton --overwrite-profile`
103103

104104
You can also warp the mesh, but it's not in the default as it's a much slower process and not always needed to do. You can run `flatone SEGMENT_ID --warp-mesh` to warp the mesh (and the previous steps will not be recomputed unless you also provide any of the `--overwrite*` flags).
105105

flatone/cli.py

Lines changed: 129 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -179,29 +179,37 @@ def build_skeleton(
179179
seg_id: int,
180180
soma_threshold: float,
181181
soma_distance_factor: float,
182+
soma_init_guess_axis: str,
182183
soma_init_guess_mode: str,
183184
verbose: bool,
184185
overwrite: bool,
185186
) -> Path:
186-
"""Skeletonise mesh, save SWC & preview PNG; return SWC path."""
187-
skel_path = outdir / "skeleton.swc"
187+
"""Skeletonise mesh, save SWC, NPZ & preview PNG; return NPZ path."""
188+
swc_path = outdir / "skeleton.swc"
188189
npz_path = outdir / "skeleton.npz"
189190
preview = outdir / "skeleton.png"
190191

191-
if not overwrite and skel_path.exists() and preview.exists():
192+
if not overwrite and npz_path.exists() and preview.exists():
192193
if verbose:
193194
print(f"Skeleton for segment {seg_id} already exists at:")
194-
print(f" {skel_path}\n")
195-
return skel_path
195+
print(f" {swc_path}\n")
196+
print(f" {npz_path}\n")
197+
198+
return npz_path
196199

197200
if verbose:
198201
print("Skeletonising …")
199-
print(soma_threshold, soma_distance_factor, soma_init_guess_mode)
202+
print(
203+
soma_threshold,
204+
soma_distance_factor,
205+
f"init_guess=({soma_init_guess_axis}, {soma_init_guess_mode})",
206+
)
200207
mesh = sk.io.load_mesh(mesh_path) # nm
201208
skel = sk.skeletonize(
202209
mesh,
203210
soma_radius_percentile_threshold=soma_threshold,
204211
soma_radius_distance_factor=soma_distance_factor,
212+
soma_init_guess_axis=soma_init_guess_axis,
205213
soma_init_guess_mode=soma_init_guess_mode,
206214
verbose=verbose,
207215
id=seg_id,
@@ -220,15 +228,15 @@ def build_skeleton(
220228
plt.close(fig)
221229

222230
skel.convert_unit(target_unit="μm")
223-
skel.to_swc(skel_path)
231+
skel.to_swc(swc_path)
224232
skel.to_npz(npz_path)
225233
if verbose:
226234
print("Saved skeleton and plot to: ")
227-
print(f" {skel_path}")
235+
print(f" {swc_path}")
228236
print(f" {npz_path}")
229237
print(f" {preview}")
230238
print("\n")
231-
return skel_path
239+
return npz_path
232240

233241

234242
def warp_skeleton(
@@ -263,21 +271,35 @@ def warp_skeleton(
263271

264272
# w.on_sac_surface, w.off_sac_surface = _load_sac_surfaces()
265273

266-
w.skeleton = sk.io.load_swc(skel_path) # μm
274+
w.skeleton = sk.io.load_npz(skel_path) # μm
267275
w.mapping = mapping
268276
w.warp_skeleton(z_profile_extent=z_profile_extent)
269277

270278
w.warped_skeleton.to_swc(warped_swc) # μm
271279
w.warped_skeleton.to_npz(outdir / "skeleton_warped.npz")
272280
# 3-D warped view -------------------------------------------------------
273-
fig, ax = sk.plot3v(
274-
w.warped_skeleton,
275-
scale=1,
276-
unit="μm",
277-
color_by="ntype",
278-
skel_cmap="Set2",
279-
title=f"{seg_id} (warped)",
280-
)
281+
# If a warped mesh exists, plot it together with the warped skeleton
282+
warped_mesh_path = outdir / "mesh_warped.obj"
283+
if warped_mesh_path.exists():
284+
warped_mesh = sk.io.load_mesh(warped_mesh_path)
285+
fig, ax = sk.plot3v(
286+
w.warped_skeleton,
287+
warped_mesh,
288+
scale=(1, 1e-3),
289+
unit="μm",
290+
color_by="ntype",
291+
skel_cmap="Set2",
292+
title=f"{seg_id} (warped)",
293+
)
294+
else:
295+
fig, ax = sk.plot3v(
296+
w.warped_skeleton,
297+
scale=1,
298+
unit="μm",
299+
color_by="ntype",
300+
skel_cmap="Set2",
301+
title=f"{seg_id} (warped)",
302+
)
281303

282304
fig.savefig(warped_png, dpi=300, bbox_inches="tight")
283305
plt.close(fig)
@@ -409,25 +431,77 @@ def warp_mesh_and_save(
409431
mesh_path: Path,
410432
outdir: Path,
411433
mapping: dict,
434+
seg_id: str,
412435
verbose: bool,
413436
overwrite: bool,
414437
) -> None:
415-
"""Warp the *raw mesh* and save as OBJ."""
438+
"""
439+
Warp the *raw mesh* and save as OBJ.
440+
441+
If mesh_warped.obj already exists and overwrite=False, skip re-warping
442+
and just (re)generate the plot using the existing warped mesh.
443+
"""
416444
warped_path = outdir / "mesh_warped.obj"
417-
if warped_path.exists() and not overwrite:
418-
if verbose:
419-
print("Warped mesh already exists at:")
420-
print(f" {warped_path}\n")
421-
return
445+
out_png = outdir / "skeleton_warped.png"
446+
447+
to_warp = overwrite or not warped_path.exists()
422448

423449
if verbose:
424-
print("Warping raw mesh (may be slow) …")
425-
mesh = sk.io.load_mesh(mesh_path) # nm
426-
warped_mesh = warp_mesh_fn(mesh, mapping, mesh_vertices_scale=1e-3, verbose=verbose)
427-
warped_mesh.export(warped_path)
450+
if to_warp:
451+
print("Warping raw mesh (may be slow) …")
452+
else:
453+
print("Using existing warped mesh …")
454+
455+
if to_warp:
456+
mesh = sk.io.load_mesh(mesh_path) # nm
457+
warped_mesh = warp_mesh_fn(
458+
mesh, mapping, mesh_vertices_scale=1e-3, verbose=verbose
459+
)
460+
warped_mesh.export(warped_path)
461+
else:
462+
# Load the already-warped mesh from disk
463+
warped_mesh = sk.io.load_mesh(warped_path)
464+
465+
# Create the combined plot only if a warped skeleton exists.
466+
skel_npz_path = outdir / "skeleton_warped.npz"
467+
if skel_npz_path.exists():
468+
warped_skel = sk.io.load_npz(skel_npz_path)
469+
fig, ax = sk.plot3v(
470+
warped_skel,
471+
warped_mesh,
472+
scale=(1, 1e-3),
473+
unit="μm",
474+
title=f"{seg_id} (warped)",
475+
color_by="ntype",
476+
skel_cmap="Set2",
477+
)
478+
fig.savefig(out_png, dpi=300, bbox_inches="tight")
479+
plt.close(fig)
480+
else:
481+
if verbose:
482+
print(
483+
"Warped skeleton not found; skipping combined plot. "
484+
"(Will be included in warp_skeleton’s plot if available.)"
485+
)
486+
428487
if verbose:
429-
print("Saved warped mesh to:")
430-
print(f" {warped_path}\n")
488+
if to_warp and skel_npz_path.exists():
489+
print("Saved warped mesh and combined plot to:")
490+
print(f" {warped_path}")
491+
print(f" {out_png}\n")
492+
elif to_warp and not skel_npz_path.exists():
493+
print("Saved warped mesh to:")
494+
print(f" {warped_path}")
495+
print("No warped skeleton found; skipped combined plot.\n")
496+
elif (not to_warp) and skel_npz_path.exists():
497+
print("Reused warped mesh at:")
498+
print(f" {warped_path}")
499+
print("Saved combined plot to:")
500+
print(f" {out_png}\n")
501+
else:
502+
print("Reused warped mesh at:")
503+
print(f" {warped_path}")
504+
print("No warped skeleton found; skipped combined plot.\n")
431505

432506

433507
def _gather_all_segids(root_out: Path) -> list[int]:
@@ -574,19 +648,31 @@ def _build_pipeline_parser() -> argparse.ArgumentParser:
574648
p.add_argument(
575649
"--soma-threshold",
576650
type=float,
577-
help="(0-90)",
651+
help=(
652+
"Percentile (0-100) of the node radius distribution used to detect the "
653+
"soma; default: 99.9."
654+
),
578655
default=99.9,
579656
)
580657
p.add_argument(
581658
"--soma-distance-factor",
582659
type=int,
660+
help=(
661+
"Distance factor for soma detection: keeps candidate nodes within "
662+
"factor × R_max of the largest-radius node; higher values admit more "
663+
"distant candidates (default: 4)."
664+
),
583665
default=4,
584666
)
585667
p.add_argument(
586-
"--soma-init-guess-mode",
587-
type=str,
588-
help="choose the min or max node in Z axis as initial guess",
589-
default="min",
668+
"--soma-init-guess",
669+
nargs=2,
670+
metavar=("AXIS", "EXTREME"),
671+
default=("z", "min"),
672+
help=(
673+
"Initial guess for soma seeding: provide axis and extreme, e.g. "
674+
"'z min'. AXIS ∈ {x,y,z}; EXTREME ∈ {min,max}."
675+
),
590676
)
591677

592678
return p
@@ -681,12 +767,19 @@ def main() -> None:
681767
args.seg_id,
682768
args.soma_threshold,
683769
args.soma_distance_factor,
684-
args.soma_init_guess_mode,
770+
args.soma_init_guess[0],
771+
args.soma_init_guess[1],
685772
args.verbose,
686773
ow_skel,
687774
)
688775
mapping = _load_global_mapping(args.mapping)
689776

777+
# Optionally warp the mesh first so that warp_skeleton can overlay it
778+
if args.warp_mesh:
779+
warp_mesh_and_save(
780+
mesh_path, outdir, mapping, args.seg_id, args.verbose, ow_meshwarp
781+
)
782+
690783
warp_skeleton(
691784
skel_path,
692785
outdir,
@@ -697,9 +790,6 @@ def main() -> None:
697790
overwrite=ow_prof,
698791
)
699792

700-
if args.warp_mesh:
701-
warp_mesh_and_save(mesh_path, outdir, mapping, args.verbose, ow_meshwarp)
702-
703793

704794
if __name__ == "__main__":
705795
main()

pyproject.toml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "flatone"
7-
version = "0.1.11"
7+
version = "0.1.12"
88
description = "A command-line tool to flatten one Eyewire2 neuron automatically."
99
authors = []
1010
requires-python = ">=3.10.0"
1111
dependencies = [
1212
"caveclient>=7.8.0",
1313
"cloud-volume>=12.3.1",
14-
"pywarper[scikit-sparse]>=0.2.2",
15-
"skeliner[3d]>=0.1.14",
14+
"pywarper[scikit-sparse]>=0.2.3",
15+
"skeliner[3d]>=0.2.1",
1616
]
1717
readme = {file = "README.md", content-type = "text/markdown"}
1818

@@ -21,7 +21,6 @@ dev = [
2121
"ruff",
2222
"pytest",
2323
"twine",
24-
"maturin",
2524
"ipykernel>=6.29.5",
2625
]
2726

0 commit comments

Comments
 (0)