3232 from mideface import Mideface
3333 from pet import WeightedAverage
3434 from utils import run_validator
35+ from qa import run_qa
3536except 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
@@ -297,7 +299,7 @@ def deface(args: Union[dict, argparse.Namespace]) -> None:
297299 write_out_dataset_description_json (args .bids_dir )
298300
299301 # remove temp outputs - this is commented out to enable easier testing for now
300- if str (os .getenv ("DEBUG " , "false" )).lower () != "true" :
302+ if str (os .getenv ("PETDEFACE_DEBUG " , "false" )).lower () != "true" :
301303 shutil .rmtree (os .path .join (output_dir , "petdeface_wf" ))
302304
303305 return {"subjects" : subjects }
@@ -671,21 +673,21 @@ def wrap_up_defacing(
671673 should_exclude = False
672674 for excluded_subject in participant_label_exclude :
673675 # Handle both cases: excluded_subject with or without 'sub-' prefix
674- if excluded_subject .startswith (' sub-' ):
676+ if excluded_subject .startswith (" sub-" ):
675677 subject_pattern = f"/{ excluded_subject } /"
676678 subject_pattern_underscore = f"/{ excluded_subject } _"
677679 else :
678680 subject_pattern = f"/sub-{ excluded_subject } /"
679681 subject_pattern_underscore = f"/sub-{ excluded_subject } _"
680-
682+
681683 if subject_pattern in entry or subject_pattern_underscore in entry :
682684 should_exclude = True
683685 break
684-
686+
685687 # Skip excluded subject files, but copy everything else (including dataset-level files)
686688 if should_exclude :
687689 continue
688-
690+
689691 copy_path = entry .replace (str (path_to_dataset ), str (final_destination ))
690692 pathlib .Path (copy_path ).parent .mkdir (
691693 parents = True , exist_ok = True , mode = 0o775
@@ -730,7 +732,7 @@ def wrap_up_defacing(
730732 desc = "defaced" ,
731733 return_type = "file" ,
732734 )
733- if str (os .getenv ("DEBUG " , "false" )).lower () != "true" :
735+ if str (os .getenv ("PETDEFAC_DEBUG " , "false" )).lower () != "true" :
734736 for extraneous in derivatives :
735737 os .remove (extraneous )
736738
@@ -741,15 +743,16 @@ def wrap_up_defacing(
741743 "placement must be one of ['adjacent', 'inplace', 'derivatives']"
742744 )
743745
744- # clean up any errantly leftover files with globe in destination folder
745- leftover_files = [
746- pathlib .Path (defaced_nii )
747- for defaced_nii in glob .glob (
748- f"{ final_destination } /**/*_defaced*.nii*" , recursive = True
749- )
750- ]
751- for leftover in leftover_files :
752- leftover .unlink ()
746+ if not os .getenv ("PETDEFACE_DEBUG" ):
747+ # clean up any errantly leftover files with glob in destination folder
748+ leftover_files = [
749+ pathlib .Path (defaced_nii )
750+ for defaced_nii in glob .glob (
751+ f"{ final_destination } /**/*_defaced*.nii*" , recursive = True
752+ )
753+ ]
754+ for leftover in leftover_files :
755+ leftover .unlink ()
753756
754757 print (f"completed copying dataset to { final_destination } " )
755758
@@ -770,7 +773,9 @@ def move_defaced_images(
770773 :param move_files: delete defaced images in "working" directory, e.g. move them to the destination dir instead of copying them there, defaults to False
771774 :type move_files: bool, optional
772775 """
773- # update paths in mapping dict
776+ # Create a new mapping with destination paths
777+ dest_mapping = {}
778+
774779 for defaced , raw in mapping_dict .items ():
775780 # get common path and replace with final_destination to get new path
776781 common_path = os .path .commonpath ([defaced .path , raw .path ])
@@ -791,15 +796,13 @@ def move_defaced_images(
791796 ]
792797 )
793798 )
794- mapping_dict [defaced ] = new_path
799+ dest_mapping [defaced ] = new_path
795800
796801 # copy defaced images to new location
797- for defaced , raw in mapping_dict .items ():
798- if pathlib .Path (raw ).exists () and pathlib .Path (defaced ).exists ():
799- shutil .copy (defaced .path , raw )
800- else :
801- pathlib .Path (raw ).parent .mkdir (parents = True , exist_ok = True )
802- shutil .copy (defaced .path , raw )
802+ for defaced , dest_path in dest_mapping .items ():
803+ if pathlib .Path (defaced ).exists ():
804+ pathlib .Path (dest_path ).parent .mkdir (parents = True , exist_ok = True )
805+ shutil .copy (defaced .path , dest_path )
803806
804807 if move_files :
805808 os .remove (defaced .path )
@@ -1056,6 +1059,12 @@ def cli():
10561059 required = False ,
10571060 default = [],
10581061 )
1062+ parser .add_argument (
1063+ "--open-browser" ,
1064+ action = "store_true" ,
1065+ default = False ,
1066+ help = "Open browser automatically after QA report generation" ,
1067+ )
10591068
10601069 arguments = parser .parse_args ()
10611070 return arguments
@@ -1256,6 +1265,45 @@ def main(): # noqa: max-complexity: 12
12561265 )
12571266 petdeface .run ()
12581267
1268+ # Generate QA reports if requested
1269+ print ("\n " + "=" * 60 )
1270+ print ("Generating QA reports..." )
1271+ print ("=" * 60 )
1272+
1273+ try :
1274+ # Determine the defaced directory based on placement
1275+ if args .placement == "adjacent" :
1276+ defaced_dir = args .bids_dir .parent / f"{ args .bids_dir .name } _defaced"
1277+ elif args .placement == "inplace" :
1278+ defaced_dir = args .bids_dir
1279+ elif args .placement == "derivatives" :
1280+ defaced_dir = args .bids_dir / "derivatives" / "petdeface"
1281+ else :
1282+ defaced_dir = (
1283+ args .output_dir
1284+ if args .output_dir
1285+ else args .bids_dir / "derivatives" / "petdeface"
1286+ )
1287+
1288+ # Run QA
1289+ qa_result = run_qa (
1290+ faced_dir = str (args .bids_dir ),
1291+ defaced_dir = str (defaced_dir ),
1292+ subject = (
1293+ " " .join (args .participant_label ) if args .participant_label else None
1294+ ),
1295+ open_browser = args .open_browser ,
1296+ )
1297+
1298+ print ("\n " + "=" * 60 )
1299+ print ("QA reports generated successfully!" )
1300+ print (f"Reports available at: { qa_result ['output_dir' ]} " )
1301+ print ("=" * 60 )
1302+
1303+ except Exception as e :
1304+ print (f"\n Error generating QA reports: { e } " )
1305+ print ("QA report generation failed, but defacing completed successfully." )
1306+
12591307
12601308if __name__ == "__main__" :
12611309 main ()
0 commit comments