Skip to content

Commit c42e167

Browse files
committed
Add template script
1 parent 549f36b commit c42e167

1 file changed

Lines changed: 85 additions & 0 deletions

File tree

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
#@String(value = "<HTML>This script assembles a montage of small image neighborhoods centered on each<br>tip of a reconstruction. Works on both regular (e.g., TIFF) and pyramidal images<br>(e.g., OME-Zarr, N5).", visibility = "MESSAGE") msg
2+
#@File(required = false, label = "Reconstruction file (leave empty for demo)") treeFile
3+
#@File(required = false, label = "Image file (leave empty for demo)") imgFile
4+
# @int(label = "Window XY size (in pixels)", min = 4) windowXY
5+
# @int(label = "Window Z thickness (in pixels, 1=2D)", min = 1) windowZ
6+
#@SNTService sntService
7+
#@UIService uiService
8+
9+
10+
/**
11+
* Builds a montage of all the end-points (tips) of a reconstruction (Tree).
12+
*
13+
* Uses sc.fiji.snt.analysis.NodeCollector, which:
14+
* - operates on an ImgPlus, working directly with lazy, pyramid sources (OME-Zarr, N5) w/o loading the whole volume
15+
* - pads out-of-bounds crops with zeros, so the resulting montage forms a uniform grid
16+
* - defaults the channel/frame to the tip's owning Path (overridable)
17+
*
18+
* TF 20260609
19+
*/
20+
21+
import sc.fiji.snt.Tree
22+
import sc.fiji.snt.analysis.NodeCollector
23+
import sc.fiji.snt.util.ImgUtils
24+
25+
26+
// Abort if running an old version
27+
sntService.requireVersion("5.0.11")
28+
29+
def tree, imgPlus
30+
31+
// Load data
32+
if (treeFile && imgFile) {
33+
// Use SCIFIO to load ImgPlus (both regular and pyramid file formats supported)
34+
imgPlus = ImgUtils.open(imgFile.getAbsolutePath())
35+
tree = Tree.fromFile(treeFile.getAbsolutePath())
36+
} else {
37+
// No data specified: Run on demo dataset
38+
imgPlus = sntService.demoImgPlus("OP_1")
39+
tree = sntService.demoTree("OP_1")
40+
windowXY = 40
41+
windowZ = 1
42+
}
43+
if (!tree || !imgPlus) {
44+
uiService.showDialog("Invalid file paths or demo data not available.")
45+
return
46+
}
47+
48+
// Tree must know its source image so that tip coordinates can be unscaled against the right calibration
49+
tree.assignImage(imgPlus)
50+
51+
// If the window depth is a slab of multiple planes, we will project it (maximum intensity projection)
52+
def doMIP = (windowZ > 1) ? NodeCollector.Projection.MIP_Z : NodeCollector.Projection.NONE
53+
54+
// Now we can assemble the NodeCollector...
55+
def collector = NodeCollector.fromTips(imgPlus, tree)
56+
collector.setWindow(windowXY, windowZ)
57+
collector.setProjection(doMIP)
58+
59+
//... and retrieve the montage:
60+
def montage = collector.getMontage(
61+
-1, // no. of columns (-1=auto)
62+
-1, // no. of columns (-1=auto)
63+
1, // scaling factor for each tile in the montage
64+
2, // width of the border around each tile
65+
false) // label each tile?
66+
montage.show()
67+
68+
// Other things you can do with the same builder:
69+
//
70+
// // Branch-point montage
71+
// NodeCollector.fromBranchPoints(imgPlus, tree).setWindow(20, 1).getMontage().show()
72+
//
73+
// // Every 10th node along all paths
74+
// NodeCollector.fromNodes(imgPlus, tree, 10).setWindow(20, 1).getMontage().show()
75+
//
76+
// // Roots of a population of Trees
77+
// NodeCollector.fromRoots(imgPlus, listOfTrees).setWindow(20, 1).getMontage().show()
78+
//
79+
// // Arbitrary points (bookmarks, seeds, ...)
80+
// NodeCollector collector = new NodeCollector(imgPlus, myPointsCollection)
81+
// collector.setWindow(20, 5).setProjection(NodeCollector.Projection.MIP_Z)
82+
// collector.getStack().show() // just the stack, no montage
83+
//
84+
// // Lazy crops as RAIs (without actual materialization of the volume), e.g. for further imglib2 work:
85+
// def crops = NodeCollector.fromTips(imgPlus, tree).setWindow(20, 1).getCrops()

0 commit comments

Comments
 (0)