Source code for monailabel.datastore.utils.dicom

# Copyright (c) MONAI Consortium
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#     http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import logging
import os
import time
from concurrent.futures import ThreadPoolExecutor

from dicomweb_client import DICOMwebClient
from pydicom.dataset import Dataset
from pydicom.filereader import dcmread

from monailabel.utils.others.generic import md5_digest, run_command

logger = logging.getLogger(__name__)


[docs]def generate_key(patient_id: str, study_id: str, series_id: str): return md5_digest(f"{patient_id}+{study_id}+{series_id}")
[docs]def get_scu(query, output_dir, query_level="SERIES", host="127.0.0.1", port="4242", aet="MONAILABEL"): start = time.time() field = "StudyInstanceUID" if query_level == "STUDIES" else "SeriesInstanceUID" run_command( "python", [ "-m", "pynetdicom", "getscu", host, port, "-P", "-k", f"0008,0052={query_level}", "-k", f"{field}={query}", "-aet", aet, "-q", "-od", output_dir, ], ) logger.info(f"Time to run GET-SCU: {time.time() - start} (sec)")
[docs]def store_scu(input_file, host="127.0.0.1", port="4242", aet="MONAILABEL"): start = time.time() input_files = input_file if isinstance(input_file, list) else [input_file] for i in input_files: run_command("python", ["-m", "pynetdicom", "storescu", host, port, "-aet", aet, i]) logger.info(f"Time to run STORE-SCU: {time.time() - start} (sec)")
[docs]def dicom_web_download_series(study_id, series_id, save_dir, client: DICOMwebClient, frame_fetch=False): start = time.time() # Limitation for DICOMWeb Client as it needs StudyInstanceUID to fetch series if not study_id: meta = Dataset.from_json( [ series for series in client.search_for_series(search_filters={"SeriesInstanceUID": series_id}) if series["0020000E"]["Value"] == [series_id] ][0] ) study_id = str(meta["StudyInstanceUID"].value) os.makedirs(save_dir, exist_ok=True) if not frame_fetch: instances = client.retrieve_series(study_id, series_id) for instance in instances: instance_id = str(instance["SOPInstanceUID"].value) file_name = os.path.join(save_dir, f"{instance_id}.dcm") instance.save_as(file_name) else: # TODO:: This logic (combining meta+pixeldata) needs improvement def save_from_frame(m): d = Dataset.from_json(m) instance_id = str(d["SOPInstanceUID"].value) # Hack to merge Info + RawData d.is_little_endian = True d.is_implicit_VR = True d.PixelData = client.retrieve_instance_frames( study_instance_uid=study_id, series_instance_uid=series_id, sop_instance_uid=instance_id, frame_numbers=[1], )[0] file_name = os.path.join(save_dir, f"{instance_id}.dcm") logger.info(f"++ Saved {os.path.basename(file_name)}") d.save_as(file_name) meta_list = client.retrieve_series_metadata(study_id, series_id) logger.info(f"++ Saving DCM into: {save_dir}") with ThreadPoolExecutor(max_workers=2, thread_name_prefix="DICOMFetch") as executor: executor.map(save_from_frame, meta_list) logger.info(f"Time to download: {time.time() - start} (sec)")
[docs]def dicom_web_upload_dcm(input_file, client: DICOMwebClient): start = time.time() dataset = dcmread(input_file) result = client.store_instances([dataset]) url = "" for elm in result.iterall(): s = str(elm.value) logger.info(f"{s}") if "/series/" in s: url = s break series_id = url.split("/series/")[1].split("/")[0] if url else "" logger.info(f"Series Instance UID: {series_id}") logger.info(f"Time to upload: {time.time() - start} (sec)") return series_id
if __name__ == "__main__": import shutil from monailabel.datastore.dicom import DICOMwebClientX client = DICOMwebClientX( url="https://d1l7y4hjkxnyal.cloudfront.net", session=None, qido_url_prefix="output", wado_url_prefix="output", stow_url_prefix="output", ) study_id = "1.2.840.113654.2.55.68425808326883186792123057288612355322" series_id = "1.2.840.113654.2.55.257926562693607663865369179341285235858" save_dir = "/local/sachi/Data/dicom" shutil.rmtree(save_dir, ignore_errors=True) os.makedirs(save_dir, exist_ok=True) dicom_web_download_series(study_id, series_id, save_dir, client, True)