Skip to content

Commit 5e44278

Browse files
committed
always run qa, switched to less hacky invocation of executing qa
1 parent 53b77be commit 5e44278

File tree

2 files changed

+147
-117
lines changed

2 files changed

+147
-117
lines changed

petdeface/petdeface.py

Lines changed: 40 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,13 @@
3232
from mideface import Mideface
3333
from pet import WeightedAverage
3434
from utils import run_validator
35+
from qa import run_qa
3536
except ModuleNotFoundError:
3637
from .mideface import ApplyMideface
3738
from .mideface import Mideface
3839
from .pet import WeightedAverage
3940
from .utils import run_validator
41+
from .qa import run_qa
4042

4143

4244
# collect version from pyproject.toml
@@ -1057,17 +1059,11 @@ def cli():
10571059
required=False,
10581060
default=[],
10591061
)
1060-
parser.add_argument(
1061-
"--qa",
1062-
action="store_true",
1063-
default=False,
1064-
help="Generate QA reports after defacing is complete",
1065-
)
10661062
parser.add_argument(
10671063
"--open-browser",
10681064
action="store_true",
10691065
default=False,
1070-
help="Open browser automatically after QA report generation (requires --qa)",
1066+
help="Open browser automatically after QA report generation",
10711067
)
10721068

10731069
arguments = parser.parse_args()
@@ -1270,63 +1266,48 @@ def main(): # noqa: max-complexity: 12
12701266
petdeface.run()
12711267

12721268
# Generate QA reports if requested
1273-
if args.qa:
1274-
print("\n" + "=" * 60)
1275-
print("Generating QA reports...")
1276-
print("=" * 60)
1277-
1278-
try:
1279-
# Import qa module
1280-
from petdeface import qa
1281-
import sys
1282-
1283-
# Determine the defaced directory based on placement
1284-
if args.placement == "adjacent":
1285-
defaced_dir = args.bids_dir.parent / f"{args.bids_dir.name}_defaced"
1286-
elif args.placement == "inplace":
1287-
defaced_dir = args.bids_dir
1288-
elif args.placement == "derivatives":
1289-
defaced_dir = args.bids_dir / "derivatives" / "petdeface"
1290-
else:
1291-
defaced_dir = (
1292-
args.output_dir
1293-
if args.output_dir
1294-
else args.bids_dir / "derivatives" / "petdeface"
1295-
)
1296-
1297-
# Build QA arguments as sys.argv style
1298-
qa_argv = [
1299-
"qa.py", # Script name
1300-
"--faced-dir",
1301-
str(args.bids_dir),
1302-
"--defaced-dir",
1303-
str(defaced_dir),
1304-
]
1269+
print("\n" + "=" * 60)
1270+
print("Generating QA reports...")
1271+
print("=" * 60)
13051272

1306-
if args.open_browser:
1307-
qa_argv.append("--open-browser")
1308-
1309-
if args.participant_label:
1310-
qa_argv.extend(["--subject", " ".join(args.participant_label)])
1273+
try:
13111274

1312-
# Temporarily replace sys.argv and run QA
1313-
original_argv = sys.argv
1314-
sys.argv = qa_argv
1275+
# Determine the defaced directory based on placement
1276+
if args.placement == "adjacent":
1277+
defaced_dir = args.bids_dir.parent / f"{args.bids_dir.name}_defaced"
1278+
elif args.placement == "inplace":
1279+
defaced_dir = args.bids_dir
1280+
elif args.placement == "derivatives":
1281+
defaced_dir = args.bids_dir / "derivatives" / "petdeface"
1282+
else:
1283+
defaced_dir = (
1284+
args.output_dir
1285+
if args.output_dir
1286+
else args.bids_dir / "derivatives" / "petdeface"
1287+
)
13151288

1316-
try:
1317-
qa.main()
1318-
finally:
1319-
sys.argv = original_argv
1289+
# Run QA
1290+
qa_result = run_qa(
1291+
faced_dir=str(args.bids_dir),
1292+
defaced_dir=str(defaced_dir),
1293+
subject=(
1294+
" ".join(args.participant_label)
1295+
if args.participant_label
1296+
else None
1297+
),
1298+
open_browser=args.open_browser,
1299+
)
13201300

1321-
print("\n" + "=" * 60)
1322-
print("QA reports generated successfully!")
1323-
print("=" * 60)
1301+
print("\n" + "=" * 60)
1302+
print("QA reports generated successfully!")
1303+
print(f"Reports available at: {qa_result['output_dir']}")
1304+
print("=" * 60)
13241305

1325-
except Exception as e:
1326-
print(f"\nError generating QA reports: {e}")
1327-
print(
1328-
"QA report generation failed, but defacing completed successfully."
1329-
)
1306+
except Exception as e:
1307+
print(f"\nError generating QA reports: {e}")
1308+
print(
1309+
"QA report generation failed, but defacing completed successfully."
1310+
)
13301311

13311312

13321313
if __name__ == "__main__":

petdeface/qa.py

Lines changed: 107 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
from pathlib import Path
2323

2424

25+
26+
27+
2528
def 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"\nAll 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

14151464
if __name__ == "__main__":
14161465
main()

0 commit comments

Comments
 (0)