|
1 | 1 | #!/usr/bin/env python3 |
2 | 2 |
|
3 | 3 | import argparse |
| 4 | +import json |
4 | 5 | import shutil |
5 | 6 | import sys |
6 | 7 | from typing import Any |
|
12 | 13 | try: |
13 | 14 | from dcm_classifier.study_processing import ProcessOneDicomStudyToVolumesMappingBase |
14 | 15 | from dcm_classifier.image_type_inference import ImageTypeClassifierBase |
| 16 | + from dcm_classifier.dicom_config import required_DICOM_fields, optional_DICOM_fields |
15 | 17 | except Exception as e: |
16 | 18 | print(f"Missing module import {e}") |
17 | 19 | print( |
@@ -61,6 +63,13 @@ def main(): |
61 | 63 | default=None, |
62 | 64 | help="Path to the output the newly organized dicom data", |
63 | 65 | ) |
| 66 | + parser.add_argument( |
| 67 | + "-j", |
| 68 | + "--json", |
| 69 | + required=False, |
| 70 | + default=None, |
| 71 | + help="Path to the output json file", |
| 72 | + ) |
64 | 73 |
|
65 | 74 | args = parser.parse_args() |
66 | 75 |
|
@@ -88,45 +97,141 @@ def main(): |
88 | 97 | list_of_inputs: list[dict[str, Any]] = [] |
89 | 98 | list_of_probabilities: list[pd.DataFrame] = [] |
90 | 99 | list_of_dictionaries: list[dict[str, str]] = [] |
| 100 | + session_dictionary: dict[str, Any] = {} |
91 | 101 |
|
92 | 102 | for series_number, series in study.series_dictionary.items(): |
93 | 103 | for index, volume in enumerate(series.get_volume_list()): |
| 104 | + invalid_volume: bool = False |
94 | 105 | current_dict: dict[str, str] = ordered_dict() |
95 | 106 | current_dict["Series#"] = str(series_number) |
| 107 | + dict_entry_name: str | None = None |
| 108 | + dictionary = {} |
| 109 | + |
| 110 | + # Volume Index |
96 | 111 | try: |
97 | | - current_dict["Vol.#"] = str(volume.get_volume_index()) |
| 112 | + vol_index: str = str(volume.get_volume_index()) |
| 113 | + current_dict["Vol.#"] = vol_index |
| 114 | + dict_entry_name: str = f"{series_number}_{vol_index}" |
98 | 115 | except AttributeError: |
99 | 116 | current_dict["Vol.#"] = "None" |
| 117 | + |
| 118 | + # Volume Modality |
100 | 119 | try: |
101 | | - current_dict["Volume Modality"] = str(volume.get_volume_modality()) |
| 120 | + vol_modality: str = str(volume.get_volume_modality()) |
| 121 | + current_dict["Volume Modality"] = vol_modality |
| 122 | + if vol_modality != "INVALID": |
| 123 | + dictionary["VolumeModality"] = vol_modality |
| 124 | + else: |
| 125 | + invalid_volume = True |
102 | 126 | except AttributeError: |
103 | 127 | current_dict["Volume Modality"] = "None" |
| 128 | + dictionary = {} |
| 129 | + |
| 130 | + # Series Modality |
104 | 131 | try: |
105 | | - current_dict["Series Modality"] = str(series.get_series_modality()) |
| 132 | + series_modality: str = str(series.get_series_modality()) |
| 133 | + current_dict["Series Modality"] = series_modality |
| 134 | + dictionary["SeriesModality"] = series_modality if dictionary else {} |
106 | 135 | except AttributeError: |
107 | 136 | current_dict["Series Modality"] = "None" |
| 137 | + dictionary = {} |
| 138 | + |
| 139 | + # Acquisition Plane |
108 | 140 | try: |
109 | | - current_dict["Acq.Plane"] = str(volume.get_acquisition_plane()) |
| 141 | + acq_plane: str = str(volume.get_acquisition_plane()) |
| 142 | + current_dict["Acq.Plane"] = acq_plane |
| 143 | + dictionary["AcqPlane"] = acq_plane if dictionary else {} |
110 | 144 | except AttributeError: |
111 | 145 | current_dict["Acq.Plane"] = "None" |
| 146 | + dictionary = {} |
| 147 | + |
| 148 | + # Isotropic |
112 | 149 | try: |
113 | | - current_dict["Isotropic"] = str(volume.get_is_isotropic()) |
| 150 | + isotropic: str = str(volume.get_is_isotropic()) |
| 151 | + current_dict["Isotropic"] = isotropic |
| 152 | + dictionary["Isotropic"] = isotropic if dictionary else {} |
114 | 153 | except AttributeError: |
115 | 154 | current_dict["Isotropic"] = "None" |
| 155 | + dictionary["Isotropic"] = "None" |
| 156 | + |
| 157 | + # Modality Probabilities |
116 | 158 | vol_probabilities = volume.get_modality_probabilities() |
| 159 | + for col in vol_probabilities.columns: |
| 160 | + # current_dict[col] = str(vol_probabilities[col].values[0]) |
| 161 | + if "SeriesNumber" in col or "CODE" in col: |
| 162 | + continue |
| 163 | + dictionary[col] = ( |
| 164 | + str(vol_probabilities[col].values[0]) if dictionary else {} |
| 165 | + ) |
117 | 166 | print(vol_probabilities.to_string(index=False)) |
| 167 | + |
| 168 | + # Bvalue |
118 | 169 | try: |
119 | | - current_dict["Bvalue"] = str(volume.get_volume_bvalue()) |
| 170 | + bval = str(volume.get_volume_bvalue()) |
| 171 | + current_dict["Bvalue"] = bval |
| 172 | + dictionary["Bvalue"] = bval if dictionary else {} |
120 | 173 | except AttributeError: |
121 | 174 | current_dict["Bvalue"] = "None" |
| 175 | + dictionary["Bvalue"] = "None" |
| 176 | + |
| 177 | + # Series Description |
122 | 178 | try: |
123 | | - current_dict["SeriesDesc"] = volume.get_dicom_field_by_name( |
| 179 | + series_description: str = volume.get_dicom_field_by_name( |
124 | 180 | "SeriesDescription" |
125 | 181 | ) |
| 182 | + current_dict["SeriesDesc"] = series_description |
| 183 | + dictionary["SeriesDescription"] = ( |
| 184 | + series_description if dictionary else {} |
| 185 | + ) |
126 | 186 | except AttributeError: |
127 | 187 | current_dict["SeriesDesc"] = "None" |
| 188 | + dictionary["SeriesDescription"] = "None" |
| 189 | + |
| 190 | + # Contrast Agent |
| 191 | + try: |
| 192 | + contrast = volume.get_has_contrast() |
| 193 | + dictionary["Contrast"] = str(contrast) if dictionary else {} |
| 194 | + |
| 195 | + if contrast: |
| 196 | + contrast_agent = volume.get_contrast_agent() |
| 197 | + # current_dict["ContrastAgent"] = contrast_agent |
| 198 | + dictionary["ContrastAgent"] = contrast_agent if dictionary else {} |
| 199 | + else: |
| 200 | + # current_dict["Contrast"] = "None" |
| 201 | + dictionary["Contrast"] = "None" |
| 202 | + except AttributeError: |
| 203 | + # current_dict["Contrast"] = "None" |
| 204 | + dictionary["Contrast"] = "None" |
| 205 | + |
| 206 | + # Pixel Spacing |
| 207 | + try: |
| 208 | + pixel_spacing = volume.get_dicom_field_by_name("PixelSpacing") |
| 209 | + # current_dict["PixelSpacing"] = pixel_spacing |
| 210 | + dictionary["PixelSpacing_0"] = ( |
| 211 | + str(pixel_spacing[0]) if dictionary else {} |
| 212 | + ) |
| 213 | + dictionary["PixelSpacing_1"] = ( |
| 214 | + str(pixel_spacing[1]) if dictionary else {} |
| 215 | + ) |
| 216 | + except AttributeError: |
| 217 | + # current_dict["PixelSpacing"] = "None" |
| 218 | + dictionary["PixelSpacing_0"] = "None" |
| 219 | + dictionary["PixelSpacing_1"] = "None" |
| 220 | + |
| 221 | + for field in required_DICOM_fields: |
| 222 | + if field in ["PixelSpacing", "ImageType"]: |
| 223 | + continue |
| 224 | + try: |
| 225 | + value = volume.get_dicom_field_by_name(field) |
| 226 | + # current_dict[field] = str(value) |
| 227 | + dictionary[field] = str(value) if dictionary else {} |
| 228 | + except AttributeError: |
| 229 | + # current_dict[field] = "None" |
| 230 | + dictionary[field] = "None" |
| 231 | + |
128 | 232 | inputs_df: dict[str, Any] = volume.get_volume_dictionary() |
129 | 233 | current_dict["ImageType"] = str(inputs_df.get("ImageType", "Unknown")) |
| 234 | + dictionary["ImageType"] = str(inputs_df.get("ImageType", "Unknown")) |
130 | 235 | for unwanted in [ |
131 | 236 | "FileName", |
132 | 237 | "StudyInstanceUID", |
@@ -170,6 +275,17 @@ def main(): |
170 | 275 | print(f"Copying {dcm_file} to {output_file_path}") |
171 | 276 | shutil.copy(dcm_file, output_file_path, follow_symlinks=True) |
172 | 277 | # shutil.move(dcm_file, output_file_path) |
| 278 | + if dict_entry_name is not None: |
| 279 | + session_dictionary[dict_entry_name] = ( |
| 280 | + dictionary if not invalid_volume else {} # set to empty if invalid |
| 281 | + ) |
| 282 | + |
| 283 | + json_output_dict: dict[str, Any] = {str(args.session_directory): session_dictionary} |
| 284 | + |
| 285 | + # save the dictionary to a file |
| 286 | + if args.json is not None: |
| 287 | + with open(args.json, "w") as f: |
| 288 | + json.dump(json_output_dict, f, indent=4) |
173 | 289 |
|
174 | 290 | df: pd.DataFrame = pd.DataFrame(list_of_dictionaries) |
175 | 291 | df.sort_values(by=["Series#", "Vol.#"], inplace=True) |
|
0 commit comments