2222from pathlib import Path
2323
2424
25+
26+
27+
2528def preprocess_single_subject (s , output_dir ):
2629 """Preprocess a single subject's images (for parallel processing)."""
2730 temp_dir = os .path .join (output_dir , "temp_3d_images" )
@@ -1188,50 +1191,36 @@ def create_gif_index_html(subjects, output_dir, size="compact"):
11881191 return index_file
11891192
11901193
1191- def main ():
1192- parser = argparse .ArgumentParser (
1193- description = "Generate static HTML comparisons of PET deface results using nilearn."
1194- )
1195- parser .add_argument (
1196- "--faced-dir" , required = True , help = "Directory for original (faced) dataset"
1197- )
1198- parser .add_argument (
1199- "--defaced-dir" , required = True , help = "Directory for defaced dataset"
1200- )
1201- parser .add_argument (
1202- "--output-dir" ,
1203- help = "Output directory for HTML files (default: {orig_folder}_{defaced_folder}_qa)" ,
1204- )
1205- parser .add_argument (
1206- "--open-browser" , action = "store_true" , help = "Open browser automatically"
1207- )
1208- parser .add_argument (
1209- "--n-jobs" ,
1210- type = int ,
1211- default = None ,
1212- help = "Number of parallel jobs (default: all cores)" ,
1213- )
1214- parser .add_argument (
1215- "--subject" ,
1216- type = str ,
1217- help = "Filter to specific subject (e.g., 'sub-01' or 'sub-01_ses-baseline')" ,
1218- )
1219-
1220- parser .add_argument (
1221- "--size" ,
1222- type = str ,
1223- choices = ["compact" , "full" ],
1224- default = "compact" ,
1225- help = "Image size: 'compact' for closer together or 'full' for entire page width" ,
1226- )
1227- args = parser .parse_args ()
1228-
1229- faced_dir = os .path .abspath (args .faced_dir )
1230- defaced_dir = os .path .abspath (args .defaced_dir )
1194+ def run_qa (
1195+ faced_dir ,
1196+ defaced_dir ,
1197+ output_dir = None ,
1198+ subject = None ,
1199+ n_jobs = None ,
1200+ size = "compact" ,
1201+ open_browser = False ,
1202+ ):
1203+ """
1204+ Run QA report generation programmatically.
1205+
1206+ Args:
1207+ faced_dir (str): Path to original (faced) dataset directory
1208+ defaced_dir (str): Path to defaced dataset directory
1209+ output_dir (str, optional): Output directory for HTML files
1210+ subject (str, optional): Filter to specific subject
1211+ n_jobs (int, optional): Number of parallel jobs
1212+ size (str): Image size - 'compact' or 'full'
1213+ open_browser (bool): Whether to open browser automatically
1214+
1215+ Returns:
1216+ dict: Information about generated files
1217+ """
1218+ faced_dir = os .path .abspath (faced_dir )
1219+ defaced_dir = os .path .abspath (defaced_dir )
12311220
12321221 # Create output directory name based on input directories
1233- if args . output_dir :
1234- output_dir = os .path .abspath (args . output_dir )
1222+ if output_dir :
1223+ output_dir = os .path .abspath (output_dir )
12351224 else :
12361225 orig_folder = os .path .basename (faced_dir )
12371226 defaced_folder = os .path .basename (defaced_dir )
@@ -1247,23 +1236,24 @@ def main():
12471236 print (f"Found { len (subjects )} subjects with matching files" )
12481237
12491238 # Filter to specific subject if requested
1250- if args . subject :
1239+ if subject :
12511240 original_count = len (subjects )
1252- subjects = [s for s in subjects if args . subject in s ["id" ]]
1241+ subjects = [s for s in subjects if subject in s ["id" ]]
12531242 print (
1254- f"Filtered to { len (subjects )} subjects matching '{ args . subject } ' (from { original_count } total)"
1243+ f"Filtered to { len (subjects )} subjects matching '{ subject } ' (from { original_count } total)"
12551244 )
12561245
12571246 if not subjects :
1258- print (f"No subjects found matching '{ args . subject } '" )
1247+ print (f"No subjects found matching '{ subject } '" )
12591248 print ("Available subjects:" )
12601249 all_subjects = build_subjects_from_datasets (faced_dir , defaced_dir )
12611250 for s in all_subjects :
12621251 print (f" - { s ['id' ]} " )
1263- exit ( 1 )
1252+ raise ValueError ( f"No subjects found matching ' { subject } '" )
12641253
12651254 # Set number of jobs for parallel processing
1266- n_jobs = args .n_jobs if args .n_jobs else mp .cpu_count ()
1255+ if n_jobs is None :
1256+ n_jobs = mp .cpu_count ()
12671257 print (f"Using { n_jobs } parallel processes" )
12681258
12691259 # Preprocess all images once (4D→3D conversion)
@@ -1281,7 +1271,7 @@ def main():
12811271 process_func = partial (
12821272 process_subject ,
12831273 output_dir = output_dir ,
1284- size = args . size ,
1274+ size = size ,
12851275 )
12861276
12871277 # Process all subjects in parallel
@@ -1295,9 +1285,9 @@ def main():
12951285
12961286 # Create both HTML files
12971287 side_by_side_file = create_side_by_side_index_html (
1298- preprocessed_subjects , output_dir , args . size
1288+ preprocessed_subjects , output_dir , size
12991289 )
1300- animated_file = create_gif_index_html (preprocessed_subjects , output_dir , args . size )
1290+ animated_file = create_gif_index_html (preprocessed_subjects , output_dir , size )
13011291
13021292 # Create a simple index that links to both
13031293 index_html = f"""
@@ -1379,13 +1369,13 @@ def main():
13791369 "--output-dir" ,
13801370 output_dir ,
13811371 "--size" ,
1382- args . size ,
1372+ size ,
13831373 ]
1384- if args . n_jobs :
1385- command_parts .extend (["--n-jobs" , str (args . n_jobs )])
1386- if args . subject :
1387- command_parts .extend (["--subject" , args . subject ])
1388- if args . open_browser :
1374+ if n_jobs :
1375+ command_parts .extend (["--n-jobs" , str (n_jobs )])
1376+ if subject :
1377+ command_parts .extend (["--subject" , subject ])
1378+ if open_browser :
13891379 command_parts .append ("--open-browser" )
13901380
13911381 command_str = " " .join (command_parts )
@@ -1404,13 +1394,72 @@ def main():
14041394 print (f"Saved command to: { command_file } " )
14051395
14061396 # Open browser if requested
1407- if args . open_browser :
1397+ if open_browser :
14081398 webbrowser .open (f"file://{ index_file } " )
14091399 print (f"Opened browser to: { index_file } " )
14101400
14111401 print (f"\n All files generated in: { output_dir } " )
14121402 print (f"Open index.html in your browser to view comparisons" )
14131403
1404+ return {
1405+ "output_dir" : output_dir ,
1406+ "side_by_side_file" : side_by_side_file ,
1407+ "animated_file" : animated_file ,
1408+ "index_file" : index_file ,
1409+ "command_file" : command_file ,
1410+ "subjects_processed" : len (successful ),
1411+ "total_subjects" : len (preprocessed_subjects ),
1412+ }
1413+
1414+
1415+ def main ():
1416+ parser = argparse .ArgumentParser (
1417+ description = "Generate static HTML comparisons of PET deface results using nilearn."
1418+ )
1419+ parser .add_argument (
1420+ "--faced-dir" , required = True , help = "Directory for original (faced) dataset"
1421+ )
1422+ parser .add_argument (
1423+ "--defaced-dir" , required = True , help = "Directory for defaced dataset"
1424+ )
1425+ parser .add_argument (
1426+ "--output-dir" ,
1427+ help = "Output directory for HTML files (default: {orig_folder}_{defaced_folder}_qa)" ,
1428+ )
1429+ parser .add_argument (
1430+ "--open-browser" , action = "store_true" , help = "Open browser automatically"
1431+ )
1432+ parser .add_argument (
1433+ "--n-jobs" ,
1434+ type = int ,
1435+ default = None ,
1436+ help = "Number of parallel jobs (default: all cores)" ,
1437+ )
1438+ parser .add_argument (
1439+ "--subject" ,
1440+ type = str ,
1441+ help = "Filter to specific subject (e.g., 'sub-01' or 'sub-01_ses-baseline')" ,
1442+ )
1443+
1444+ parser .add_argument (
1445+ "--size" ,
1446+ type = str ,
1447+ choices = ["compact" , "full" ],
1448+ default = "compact" ,
1449+ help = "Image size: 'compact' for closer together or 'full' for entire page width" ,
1450+ )
1451+ args = parser .parse_args ()
1452+
1453+ return run_qa (
1454+ faced_dir = args .faced_dir ,
1455+ defaced_dir = args .defaced_dir ,
1456+ output_dir = args .output_dir ,
1457+ subject = args .subject ,
1458+ n_jobs = args .n_jobs ,
1459+ size = args .size ,
1460+ open_browser = args .open_browser ,
1461+ )
1462+
14141463
14151464if __name__ == "__main__" :
14161465 main ()
0 commit comments