1010# 3d meshing libraries
1111import pyvista as pv
1212import trimesh
13- import point_cloud_utils as pcu
13+ import pymeshfix
1414
1515from bioio import BioImage
1616
@@ -82,9 +82,6 @@ def nuclei_localization(
8282 print (f"Mesh for timepoint { timepoint } not found." )
8383 continue
8484
85- if timepoint > 2 :
86- break
87-
8885 if align_segmentation :
8986 alignment_matrix = alignment .parse_rotation_matrix_from_string (df ['Dual Camera Alignment Matrix Value' ].values [0 ])
9087 else :
@@ -158,30 +155,13 @@ def localize_for_timepoint(
158155 seg = seg .transpose (2 , 1 , 0 )
159156 scale = 2.88 / 0.271
160157
161- # Calculate roof height to enclose all nuclei in the imaging volume
162- # The roof must be above the maximum possible scaled Z coordinate
163- max_z_slices = seg .shape [2 ] # Number of Z slices in imaging volume
164- max_scaled_z = max_z_slices * scale # Maximum Z after scaling to isotropic
165-
166158 vert , faces = mesh .points , mesh .faces .reshape (mesh .n_faces , 4 )[:,1 :]
167- vert_up = np .zeros_like (vert )
168- np .copyto (vert_up , vert )
169- # Place roof above the maximum scaled Z coordinate of the imaging volume
170- # This ensures all nuclei (including those at high Z) are enclosed
171- roof_height = max (max (vert [:,2 ]), max_scaled_z ) * 1.05 # 5% margin above max
172- vert_up [:, 2 ] = roof_height
173- face_up = np .zeros_like (faces )
174- np .copyto (face_up , faces )
175-
176- mesh = trimesh .Trimesh (vertices = vert , faces = faces )
177- roof = trimesh .Trimesh (vertices = vert_up , faces = face_up )
178- mesh_conc = trimesh .util .concatenate (mesh , roof )
179-
180- vert , faces = mesh_conc .vertices , mesh_conc .faces
181-
182- vw , fw = pcu .make_mesh_watertight (vert , faces , 10000 )
183159
184- mesh = trimesh .Trimesh (vertices = vw , faces = fw )
160+ # Use PyMeshFix to fill holes and create watertight mesh
161+ # This preserves geometry better than artificial roof concatenation
162+ meshfix = pymeshfix .MeshFix (vert , faces )
163+ meshfix .repair (verbose = False )
164+ mesh = trimesh .Trimesh (vertices = meshfix .v , faces = meshfix .f )
185165
186166 # initialize ray caster (for checking if a point is inside the mesh)
187167 rayCaster = trimesh .ray .ray_triangle .RayMeshIntersector (mesh )
@@ -232,7 +212,7 @@ def run_nuclei_localization(
232212 ):
233213 '''
234214 This is the main function to localize nuclei inside a 3D mesh.
235-
215+
236216 Parameters
237217 ----------
238218 manifest_path: str
@@ -245,10 +225,25 @@ def run_nuclei_localization(
245225 Flag to enable alignment of the segmentation using the barcode of the movie.
246226 Default is True.
247227 '''
228+ # Filter to specific Data IDs for analysis
229+ ANALYSIS_DATA_IDS = [
230+ '3500005548_43' , '3500005548_46' , '3500005548_48' ,
231+ '3500005824_35' , '3500005824_36' , '3500005824_37' , '3500005824_38' ,
232+ '3500005828_43' , '3500005828_45' , '3500005828_46' , '3500005828_67' , '3500005828_70' ,
233+ '3500006256_19' , '3500006256_21' ,
234+ '3500007081_8' ,
235+ '3500007213_38' ,
236+ '3500007247_5' ,
237+ '3500007432_52' , '3500007432_57' , '3500007432_63' ,
238+ ]
239+
248240 df_cond = df_manifest [
249241 [gene in ['HIST1H2BJ' , 'EOMES|TBR2' ] for gene in df_manifest ['Gene' ].values ]
250242 ].dropna (subset = ['CollagenIV Segmentation Probability URL' ])
251243
244+ # Filter to only the specified Data IDs
245+ df_cond = df_cond [df_cond ['Data ID' ].isin (ANALYSIS_DATA_IDS )]
246+
252247 print (f"Processing { len (df_cond )} movies with CollagenIV segmentations." )
253248
254249 for data_id in tqdm (pd .unique (df_cond ['Data ID' ]), desc = "Movies" ):
0 commit comments