forked from gamorosino/fixSidecar
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdcm_convert.py
More file actions
159 lines (138 loc) · 8.67 KB
/
dcm_convert.py
File metadata and controls
159 lines (138 loc) · 8.67 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Oct 21 17:05:44 2024
#########################################################################################################################
#########################################################################################################################
################### ###################
################### Title: Dicom Convert and Sidecar fix ###################
################### ###################
################### Description: ###################
################### This script facilitates the conversion of DICOM files to NIfTI format ###################
################### and ensures the resulting JSON sidecar is compliant with BIDS (Brain Imaging ###################
################### Data Structure) metadata standards. ###################
################### ###################
################### Version: 0.2.0 ###################
################### ###################
################### Requirements: Python modules - nibabel, dipy ###################
################### External tool - dcm2niix ###################
################### ###################
################### Bash Version: Tested on GNU bash, version 4.3.48 ###################
################### ###################
################### Author: Gabriele Amorosino ###################
################### Contact: gabriele.amorosino@utexas.edu ###################
################### ###################
#########################################################################################################################
#########################################################################################################################
################### ###################
################### Update: Integrated DICOM to NIfTI conversion using dcm2niix ###################
################### Automated JSON sidecar updates to align with BIDS metadata standards ###################
################### Added advanced metadata calculations (SliceTiming, ReadoutTime, etc.) ###################
################### ###################
#########################################################################################################################
#########################################################################################################################
"""
import os
import shutil
import tempfile
import subprocess
import argparse
from update_json_sidecar import update_json_with_dicom_info
def convert_dicom_to_nifti(dicom_file: str, output_dir: str, tmp_dir: str = None):
"""
Converts DICOM to NIfTI format using dcm2niix and stores results in output_dir.
Parameters:
dicom_file (str): Path to the DICOM file or directory.
output_dir (str): Output directory for the NIfTI and JSON files.
tmp_dir (str): Optional. Directory for temporary storage. If not provided, uses a system temporary directory.
Returns:
Tuple[str, str]: Paths to the generated NIfTI and JSON files.
"""
# Ensure the output directory exists
os.makedirs(output_dir, exist_ok=True)
# Manage temporary directory
if tmp_dir is None:
tmp_dir = tempfile.mkdtemp()
cleanup_tmp = True
else:
tmp_dir = os.path.abspath(tmp_dir)
os.makedirs(tmp_dir, exist_ok=True)
cleanup_tmp = False
print(f"Using temporary directory: {tmp_dir}")
# Create a subdirectory for dcm2niix output
temp_output_dir = os.path.join(tmp_dir, "nifti_output")
os.makedirs(temp_output_dir, exist_ok=True)
try:
# Copy DICOM file(s) to temporary directory
dicom_basename = os.path.basename(dicom_file)
tmp_dicom_path = os.path.join(tmp_dir, dicom_basename)
if os.path.isdir(dicom_file):
shutil.copytree(dicom_file, tmp_dicom_path)
else:
shutil.copy(dicom_file, tmp_dicom_path)
# Run dcm2niix and output to temp_output_dir
subprocess.run(
["dcm2niix", "-o", temp_output_dir, "-z", "n", "-v", "y", "-f", "%p", tmp_dicom_path],
check=True
)
# Identify generated NIfTI and JSON files in temp_output_dir
nifti_files = [f for f in os.listdir(temp_output_dir) if f.endswith(".nii")]
json_files = [f for f in os.listdir(temp_output_dir) if f.endswith(".json")]
if not nifti_files or not json_files:
raise FileNotFoundError("NIfTI or JSON files were not generated in the temporary directory.")
nifti_file = os.path.join(temp_output_dir, nifti_files[0])
json_file = os.path.join(temp_output_dir, json_files[0])
# Move the files to the output directory
final_nifti_path = os.path.join(output_dir, os.path.basename(nifti_file))
final_json_path = os.path.join(output_dir, os.path.basename(json_file))
shutil.move(nifti_file, final_nifti_path)
shutil.move(json_file, final_json_path)
print(f"NIfTI file moved to: {final_nifti_path}")
print(f"JSON file moved to: {final_json_path}")
return final_nifti_path, final_json_path
finally:
# Clean up temporary directory if created by this function
if cleanup_tmp:
shutil.rmtree(tmp_dir, ignore_errors=True)
print(f"Temporary directory {tmp_dir} cleaned up.")
def main():
parser = argparse.ArgumentParser(
description="Convert DICOM to NIfTI and (optionally) update JSON sidecar."
)
parser.add_argument("dicom_file", help="Path to the DICOM file or directory.")
parser.add_argument("output_dir", help="Output directory for the NIfTI and JSON files.")
# ------------------------------------------------------------------------- #
# NEW FLAG: tell the script *not* to touch the JSON sidecar for fMRI data
parser.add_argument(
"--no-fmri",
help="Skip JSON-sidecar update (useful for structural or non-fMRI data).",
action="store_true",
)
# ------------------------------------------------------------------------- #
parser.add_argument("--exam-card", help="Path to the exam card file.", default=None)
parser.add_argument("--compute-slice-timing", help="Enable Slice Timing calculation.", action="store_true")
parser.add_argument("--compute-total-readout", help="Enable Total Readout Time calculation.", action="store_true")
parser.add_argument("--slice-order", help="Custom slice order as a list of lists.", default=None)
parser.add_argument("--tmp-dir", help="Optional temporary directory for processing.", default=None)
parser.add_argument("--scanner-type", help="Scanner type (default: 'Philips').", default="Philips")
parser.add_argument("--flip-phase-encoding-direction", help="Toggle the sign of the phase encoding direction.", action="store_true")
args = parser.parse_args()
# Step 1: DICOM → NIfTI
nifti_file, json_file = convert_dicom_to_nifti(
args.dicom_file, args.output_dir, tmp_dir=args.tmp_dir
)
# Step 2: update JSON sidecar unless the user asked not to
if not args.no_fmri:
update_json_with_dicom_info(
dicom_path=args.dicom_file,
json_path=json_file,
output_path=json_file,
exam_card_path=args.exam_card,
compute_slice_timing=args.compute_slice_timing,
user_slice_order=args.slice_order,
scanner_type=args.scanner_type,
calculate_total_readout=args.compute_total_readout,
flip_phase=args.flip_phase_encoding_direction,
)
if __name__ == "__main__":
main()