1+ #@ ImageJ ij
2+
3+ '''
4+ Note that this script requires a Python environment that includes StarDist and Cellpose
5+ StarDist currently only supports NumPy 1.x, which necessitates using TensorFlow 2.15 or earlier
6+ TensorFlow 2.15 itself requires python 3.11 or earlier
7+
8+ You can rebuild your Python environment by using:
9+ Edit > Options > Python…
10+
11+ The following configuration was used to develop this script:
12+
13+ --Conda dependencies--
14+ python=3.11
15+ numpy=1.26.4
16+
17+ --Pip dependencies--
18+ tensorflow==2.15
19+ cellpose==4.0.6
20+ stardist==0.9.0
21+ csbdeep==0.8.0
22+ '''
23+
24+ import sys
25+ import imagej .convert as convert
26+ import numpy as np
27+ import matplotlib .pyplot as plt
28+ from cellpose import models
29+ from csbdeep .utils import normalize
30+ from stardist .models import StarDist2D
31+ import scyjava as sj
32+
33+ def filter_index_image (narr :np .ndarray , min_size :int , max_size :int ):
34+ """
35+ Filter an index image's labels with a pixel size range.
36+ """
37+ unique = np .unique (narr )
38+ for label in unique :
39+ if label == 0 :
40+ # skip the background
41+ continue
42+
43+ # create a crop for each label
44+ bbox = get_bounding_box (np .where (narr == label ))
45+ bbox_crop = narr [bbox [0 ]:bbox [2 ] + 1 , bbox [1 ]:bbox [3 ] + 1 ].copy ()
46+ bbox_crop [bbox_crop != label ] = 0
47+
48+ # get the number of pixels in label
49+ bbox_crop = bbox_crop .astype (bool )
50+ label_size = np .sum (bbox_crop )
51+
52+ if not min_size <= label_size <= max_size :
53+ narr [narr == label ] = 0
54+
55+ return narr
56+
57+ def get_bounding_box (indices : np .ndarray ):
58+ """
59+ Get the bounding box coordinates from a the label indices.
60+ """
61+ # get min and max bounds of indices array
62+ min_row = np .min (indices [0 ])
63+ min_col = np .min (indices [1 ])
64+ max_row = np .max (indices [0 ])
65+ max_col = np .max (indices [1 ])
66+
67+ return (min_row , min_col , max_row , max_col )
68+
69+ # open image data and convert to Python from Java
70+ #TODO does this connection need to be closed?
71+ data = ij .io ().open ('https://media.imagej.net/pyimagej/3d/hela_a3g.tif' )
72+ xdata = ij .py .from_java (data )
73+
74+ # show the first channel
75+ ij .ui ().show ("nucleus" , ij .py .to_java (xdata [:, :, 0 ]))
76+
77+ # show the second channel
78+ ij .ui ().show ("cytoplasm" , ij .py .to_java (xdata [:, :, 1 ] * 125 ))
79+
80+ # run StarDist on nuclei channel
81+ model = StarDist2D .from_pretrained ('2D_versatile_fluo' )
82+ nuc_labels , _ = model .predict_instances (normalize (xdata [:, :, 0 ]))
83+
84+ # run Cellpose on cytoplasm (grayscale)
85+ model = models .CellposeModel (gpu = False , model_type = 'cyto' )
86+ ch = [0 , 0 ]
87+ cyto_labels = model .eval (xdata [:, :, 1 ].data , channels = ch , diameter = 72.1 )
88+
89+ # show the stardist results
90+ ij .ui ().show ("StarDist results" , ij .py .to_java (nuc_labels ))
91+ ij .IJ .run ("mpl-viridis" , "" );
92+
93+ # show the second channel
94+ ij .ui ().show ("Cellpose results" , ij .py .to_java (cyto_labels [0 ]))
95+ ij .IJ .run ("mpl-viridis" , "" );
96+
97+ # filter the stardist results and display
98+ filter_index_image (nuc_labels , 500 , 10000 )
99+ ij .ui ().show ("StarDist filtered" , ij .py .to_java (nuc_labels ))
100+ ij .IJ .run ("mpl-viridis" , "" );
101+
102+ # ensure ROI Manager exists
103+ rm = ij .RoiManager .getInstance ()
104+ if rm is None :
105+ ij .IJ .run ("ROI Manager..." )
106+ rm = ij .RoiManager .getInstance ()
107+
108+ # Reset the ROI manager
109+ rm .reset ()
110+
111+ # convert to ImgLib2 ROI in a ROITree
112+ nuc_roi_tree = convert .index_img_to_roi_tree (ij , nuc_labels )
113+ cyto_roi_tree = convert .index_img_to_roi_tree (ij , cyto_labels [0 ])
114+
115+ # print the contents of the ROITree (nuclear ROIs)
116+ len (nuc_roi_tree .children ())
117+ for i in range (len (nuc_roi_tree .children ())):
118+ print (nuc_roi_tree .children ().get (i ).data ())
119+
120+ # display the input data, select channel 2 and enhance the contrast
121+ data_title = "hela_a3g.tif"
122+ ij .ui ().show (data_title , data )
123+ imp = ij .WindowManager .getImage (data_title )
124+ imp .setC (2 )
125+ ij .IJ .run (imp , "Enhance Contrast" , "saturated=0.35" )
126+
127+ # convert a single ImgLib2 roi to a legacy ImageJ ROI with the ConvertService.
128+ imglib_polygon_roi = nuc_roi_tree .children ().get (0 ).data ()
129+ ij_polygon_roi = ij .convert ().convert (imglib_polygon_roi , sj .jimport ('ij.gui.PolygonRoi' ))
130+ print (type (ij_polygon_roi ))
131+
132+ # convert index images to ImageJ ROI in RoiManager
133+ #TODO any way to color the selections? We can use Colors... but it appears to be global and the last one run wins
134+ #ij.IJ.run(imp, "Colors...", "foreground=blue background=black selection=red");
135+ convert .index_img_to_roi_manager (ij , nuc_labels )
136+ convert .index_img_to_roi_manager (ij , cyto_labels [0 ])
137+
138+ #TODO this pops an unnecessary display at the end but if I don't make it the last line the ROIs don't show
139+ rm .moveRoisToOverlay (imp )
140+ rm .runCommand (imp , "Show All" )
0 commit comments