@@ -47,33 +47,45 @@ def prompt_output_directory(input_dir):
47
47
return os .path .abspath (os .path .join (input_dir , answer .strip ("./" )))
48
48
49
49
50
- def dicom_to_niix (vol_dir : Path , out_dir : Path , merge_2d : bool = False ):
50
+ def dicom_to_niix (vol_dir : Path , out_dir : Path , merge_2d : bool = False , series_num : int = - 1 , is_single_file : bool = False ):
51
51
"""
52
52
For converting DICOM images to a (compresssed) 4d nifti image
53
53
"""
54
+
54
55
os .makedirs (out_dir , exist_ok = True )
55
56
57
+ cmd = [
58
+ "dcm2niix" ,
59
+ "-f" , "%s_%p" , # dcm2niix attempts to provide a sensible file naming scheme
60
+ "-o" , out_dir , # output directory
61
+ "-z" , "y" , #specifying compressed nii.gz file
62
+ "-m" , "y" if merge_2d else "n" , # Add merge option
63
+ "-s" , "y" if is_single_file else "n" ,
64
+ # https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage
65
+ # for further configuration for general usage see page above
66
+ ]
67
+ if series_num >= 0 :
68
+ cmd .extend (["-n" , str (series_num )])
69
+ cmd .append (str (vol_dir )) # input directory
70
+
56
71
try :
57
- res = subprocess .run (
58
- [
59
- "dcm2niix" ,
60
- "-f" , "%s_%p" , # dcm2niix attempts to provide a sensible file naming scheme
61
- "-o" , out_dir , # output directory
62
- "-z" , "y" , #specifying compressed nii.gz file
63
- "-m" , "y" if merge_2d else "n" , # Add merge option
64
- # https://www.nitrc.org/plugins/mwiki/index.php/dcm2nii:MainPage
65
- # for further configuration for general usage see page above
66
- vol_dir # input directory
67
- ],
68
- capture_output = True ,
69
- text = True ,
70
- check = True
71
- )
72
+ res = subprocess .run (cmd , capture_output = True , text = True , check = True )
72
73
73
74
nifti_files = list (Path (out_dir ).glob ("*.nii.gz" ))
74
- if len (nifti_files ) != 1 :
75
- raise RuntimeError ("Only one NIfTI (.nii.gz) should be in this output directory." )
76
75
76
+ if (is_single_file or merge_2d or series_num >= 0 ):
77
+ if len (nifti_files ) != 1 :
78
+ raise RuntimeError (
79
+ f"Expected a single .nii.gz output due to flags "
80
+ f"{ '-s' if is_single_file else '' } "
81
+ f"{ '-m' if merge_2d else '' } "
82
+ f"{ f'-n { series_num } ' if series_num >= 0 else '' } , "
83
+ f"but found { len (nifti_files )} files."
84
+ )
85
+ else :
86
+ if len (nifti_files ) < 1 :
87
+ raise RuntimeError ("No NIfTI (.nii.gz) files were generated." )
88
+
77
89
bval_files = list (out_dir .glob ("*.bval" ))
78
90
bvec_files = list (out_dir .glob ("*.bvec" ))
79
91
bval_path = str (bval_files [0 ]) if bval_files else None
@@ -106,29 +118,42 @@ def run_interactive():
106
118
if not add_more :
107
119
break
108
120
121
+ series_num = inquirer .prompt ([
122
+ inquirer .Text ("series" , message = "🔢 Enter series number to convert (-n <num>) [Leave blank for all]" , default = "" )
123
+ ])["series" ]
124
+ series_num = int (series_num ) if series_num .isdigit () else - 1
125
+
109
126
merge_answer = inquirer .prompt ([
110
127
inquirer .Confirm ("merge" , message = "🧩 Merge 2D slices into a single NIfTI (-m y)?" , default = True )
111
128
])
112
129
merge_2d = merge_answer ["merge" ]
113
130
131
+ single_file = inquirer .prompt ([
132
+ inquirer .Confirm ("single" , message = "📦 Force single file output (-s y)?" , default = False )
133
+ ])["single" ]
134
+
114
135
for in_dir , out_dir in zip (input_dirs , output_dirs ):
115
136
vol_dir = Path (in_dir )
116
137
out_path = Path (out_dir )
117
138
118
139
print (f"Converting:\n → Input: { vol_dir } \n → Output: { out_path } " )
119
140
try :
120
- nifti , bval , bvec = dicom_to_niix (vol_dir , out_path , merge_2d )
121
- print (f"✅ Created : { nifti } " )
141
+ nifti , bval , bvec = dicom_to_niix (vol_dir , out_path , merge_2d , series_num , single_file )
142
+ print (f" Conversion succeeded : { nifti } " )
122
143
except RuntimeError as err :
123
144
print (f"❌ Conversion failed: { err } " )
124
145
125
- def run_cli (input_path : str , output_path : str ):
146
+ def run_cli (input_path : str , output_path : str , ** kwargs ):
126
147
vol_dir = Path (input_path )
127
148
out_dir = Path (output_path )
128
149
150
+ merge_2d = kwargs .get ("merge_2d" , False )
151
+ series_num = kwargs .get ("series_number" , - 1 )
152
+ single_file = kwargs .get ("single_file" , False )
153
+
129
154
print (f" Converting:\n → Input: { vol_dir } \n → Output: { out_dir } " )
130
155
try :
131
- nifti , bval , bvec = dicom_to_niix (vol_dir , out_dir , merge_2d = False )
156
+ nifti , bval , bvec = dicom_to_niix (vol_dir , out_dir , merge_2d , series_num , single_file )
132
157
print (f" Created NIfTI: { nifti } " )
133
158
134
159
if bval and bvec :
@@ -148,14 +173,18 @@ def run_cli(input_path: str, output_path: str):
148
173
parser = argparse .ArgumentParser (description = "DICOM to NIfTI converter with optional IVIM processing" )
149
174
parser .add_argument ("input" , nargs = "?" , help = "Path to input DICOM directory" )
150
175
parser .add_argument ("output" , nargs = "?" , help = "Path to output directory for NIfTI files" )
176
+ parser .add_argument ("-n" , "--series-number" , type = int , default = - 1 , help = "Only convert this series number (-n <num>)" )
177
+ parser .add_argument ("-m" , "--merge-2d" , action = "store_true" , help = "Merge 2D slices (-m y)" )
178
+ parser .add_argument ("-s" , "--single-file" , action = "store_true" , help = "Enable single file mode (-s y)" )
151
179
parser .add_argument ("-pu" , "--prompt-user" , action = "store_true" , help = "Run in interactive mode" )
152
180
181
+
153
182
args = parser .parse_args ()
154
183
155
184
if args .prompt_user :
156
185
run_interactive ()
157
186
elif args .input and args .output :
158
- run_cli (args .input , args .output )
187
+ run_cli (args .input , args .output , ** vars ( args ) )
159
188
else :
160
189
print ("❗ You must provide input and output paths OR use --prompt-user for interactive mode." )
161
190
parser .print_help ()
0 commit comments