3131)
3232
3333
34- def find_sulcal_graphs (morphologist_dir ):
34+ def list_acquisitions (subject_dir ):
35+ """Return acquisition folder names that contain at least one sulcal graph.
36+
37+ Scans ``<subject_dir>/t1mri/`` and returns all subdirectory names whose
38+ subtree contains a ``.arg`` sulcal/folds graph file. These are the valid
39+ choices for ``--acquisition``.
40+
41+ Args:
42+ subject_dir: Path to a single subject folder.
43+
44+ Returns:
45+ Sorted list of acquisition folder names.
46+ """
47+ t1mri_dir = osp .join (subject_dir , "t1mri" )
48+ if not osp .isdir (t1mri_dir ):
49+ return []
50+ candidates = []
51+ for name in os .listdir (t1mri_dir ):
52+ acq_dir = osp .join (t1mri_dir , name )
53+ if not osp .isdir (acq_dir ):
54+ continue
55+ found = any (
56+ ("sulci" in g .lower () or "folds" in g .lower ())
57+ for g in glob .glob (osp .join (acq_dir , "**" , "*.arg" ), recursive = True )
58+ )
59+ if found :
60+ candidates .append (name )
61+ return sorted (candidates )
62+
63+
64+ def find_sulcal_graphs (morphologist_dir , subject = None , acquisition = None ):
3565 """Find sulcal graph files (.arg) in the Morphologist output directory.
3666
3767 Args:
3868 morphologist_dir: Path to derivatives/morphologist-6.0/ directory
69+ subject: Optional subject folder name to restrict the search (e.g.
70+ ``"sub_0001"``). When omitted the entire directory is searched.
71+ acquisition: Optional acquisition folder name to restrict the search
72+ (e.g. ``"wk30"``). When omitted all acquisitions are returned.
3973
4074 Returns:
4175 List of paths to .arg files
4276 """
43- pattern = osp .join (morphologist_dir , "**" , "*.arg" )
77+ root = osp .join (morphologist_dir , subject ) if subject else morphologist_dir
78+ pattern = osp .join (root , "**" , "*.arg" )
4479 graphs = glob .glob (pattern , recursive = True )
45- sulcal_graphs = [g for g in graphs if "sulci" in g .lower () or "folds" in g .lower ()]
46- return sulcal_graphs
80+ graphs = [g for g in graphs if "sulci" in g .lower () or "folds" in g .lower ()]
81+ if acquisition :
82+ graphs = [g for g in graphs
83+ if f"/{ acquisition } /" in g .replace ("\\ " , "/" )]
84+ return graphs
4785
4886
4987def find_white_mesh (graph_path ):
@@ -367,6 +405,14 @@ def generate_umap_snapshot(embeddings_dir, reference_data_dir, output_path,
367405 return snapshots
368406
369407
408+ def _detect_hemi (graph_path ):
409+ """Return 'right' or 'left' based on graph filename."""
410+ fname = osp .basename (graph_path ).lower ()
411+ if fname .startswith ("r" ) or "_r" in fname or "right" in fname :
412+ return "right"
413+ return "left"
414+
415+
370416class GenerateSnapshots (ScriptBuilder ):
371417 """Script for generating visualization snapshots."""
372418
@@ -377,6 +423,12 @@ def __init__(self):
377423 )
378424 (self
379425 .add_optional_argument ("--morphologist_dir" , "Path to Morphologist output directory" )
426+ .add_optional_argument ("--subject" ,
427+ "Subject folder name to visualize (e.g. sub_0001). "
428+ "When omitted the first subject found is used." )
429+ .add_optional_argument ("--acquisition" ,
430+ "Acquisition folder name to use (e.g. wk30, wk40). "
431+ "Required when a subject has multiple segmentations." )
380432 .add_optional_argument ("--embeddings_dir" , "Path to embeddings output directory" )
381433 .add_optional_argument ("--cortical_tiles_dir" , "Path to cortical tiles crops directory" )
382434 .add_argument ("--output_dir" , type = str , required = True , help = "Directory to save snapshot images" )
@@ -433,39 +485,63 @@ def run(self) -> int:
433485 def _run_sulcal (self , size ):
434486 """Generate sulcal graph snapshots."""
435487 snapshots = []
436- if self .args .morphologist_dir and osp .exists (self .args .morphologist_dir ):
437- print ("\n Generating sulcal graph snapshots..." )
438- graphs = find_sulcal_graphs (self .args .morphologist_dir )
439- print (f" Found { len (graphs )} sulcal graph(s)" )
440-
441- QUAT_LEFT = (0.5 , 0.5 , 0.5 , 0.5 )
442- QUAT_RIGHT = (0.5 , - 0.5 , - 0.5 , 0.5 )
443-
444- for graph_path in graphs [:2 ]:
445- fname = osp .basename (graph_path ).lower ()
446- if fname .startswith ("r" ) or "_r" in fname or "right" in fname :
447- hemi = "right"
448- quat = QUAT_RIGHT
449- else :
450- hemi = "left"
451- quat = QUAT_LEFT
452-
453- white_mesh = find_white_mesh (graph_path )
454- if white_mesh :
455- print (f" White mesh: { white_mesh } " )
456-
457- out = osp .join (self .args .output_dir , f"sulcal_graph_{ hemi } .png" )
458- try :
459- snap = generate_sulcal_graph_snapshot (
460- graph_path , out , size ,
461- view_quaternion = quat ,
462- mesh_path = white_mesh ,
488+ morphologist_dir = self .args .morphologist_dir
489+ if not morphologist_dir or not osp .exists (morphologist_dir ):
490+ if morphologist_dir :
491+ print (f"Morphologist directory not found: { morphologist_dir } " )
492+ return snapshots
493+
494+ print ("\n Generating sulcal graph snapshots..." )
495+ subject = getattr (self .args , "subject" , None )
496+ acquisition = getattr (self .args , "acquisition" , None )
497+
498+ graphs = find_sulcal_graphs (morphologist_dir , subject = subject ,
499+ acquisition = acquisition )
500+
501+ # Group by hemisphere — keep one graph per side
502+ by_hemi = {"left" : [], "right" : []}
503+ for g in graphs :
504+ by_hemi [_detect_hemi (g )].append (g )
505+
506+ # Warn if multiple acquisitions exist and user hasn't disambiguated
507+ if not acquisition :
508+ for hemi , hemi_graphs in by_hemi .items ():
509+ if len (hemi_graphs ) > 1 :
510+ subject_dir = (osp .join (morphologist_dir , subject )
511+ if subject else morphologist_dir )
512+ acqs = list_acquisitions (subject_dir )
513+ acq_list = ", " .join (acqs ) if acqs else "unknown"
514+ print (
515+ f" Warning: { len (hemi_graphs )} { hemi } graphs found "
516+ f"(acquisitions: { acq_list } ). "
517+ f"Using the first one. Re-run with --acquisition <name> "
518+ f"to select a specific one."
463519 )
464- snapshots .append (snap )
465- except Exception as e :
466- print (f" Error processing { graph_path } : { e } " )
467- elif self .args .morphologist_dir :
468- print (f"Morphologist directory not found: { self .args .morphologist_dir } " )
520+ break
521+
522+ QUAT = {"left" : (0.5 , 0.5 , 0.5 , 0.5 ), "right" : (0.5 , - 0.5 , - 0.5 , 0.5 )}
523+
524+ total = sum (len (v ) for v in by_hemi .values ())
525+ print (f" Found { total } sulcal graph(s)" )
526+
527+ for hemi , hemi_graphs in by_hemi .items ():
528+ if not hemi_graphs :
529+ continue
530+ graph_path = hemi_graphs [0 ]
531+ white_mesh = find_white_mesh (graph_path )
532+ if white_mesh :
533+ print (f" White mesh: { white_mesh } " )
534+ out = osp .join (self .args .output_dir , f"sulcal_graph_{ hemi } .png" )
535+ try :
536+ snap = generate_sulcal_graph_snapshot (
537+ graph_path , out , size ,
538+ view_quaternion = QUAT [hemi ],
539+ mesh_path = white_mesh ,
540+ )
541+ snapshots .append (snap )
542+ except Exception as e :
543+ print (f" Error processing { graph_path } : { e } " )
544+
469545 return snapshots
470546
471547 def _run_tiles (self , size ):
0 commit comments