Prevent extra folder on dicom export

Hi there

Exporting a volume to DICOM from the Data module does not place all DICOM files inside of the selected folder, but creates an extra one on the same level.
Is there an option to prevent the extra folder?

1 Like

Exporting data to a clean folder ensures that there is no interference between DICOM exporters that are writing their outputs (potentially in parallel), the maximum number of files per folder is not exceeded (it can be a problem on file systems used for some removable media) and it also offers an easy way to determine which files are resulted from the same DICOM export operation.

It is certainly possible, but it would require developing alternative solutions for the issues mentioned above. If there are strong use cases / needed by many users then we could allocate Slicer core developers time to change the current design. What is your use case? Is the problem that you have each DICOM export result in a separate folder or that you don’t know how to get that folder?

I understand, thank you for your quick reply. In my case the exported DICOMs are read by a Python routine after the volume is processed with 3D Slicer. I create different folders for different volumes beforehand. Of course I could just select the deepest available folder in my routine, but I prefer a direct output to get a clean folder structure.

If you only need to export scalar volumes then you can use the CreateDICOMSeries CLI module as it is done here:

Alternatively, you could add an option to CreateDICOMSeries module to take a file prefix. The scalar volume DICOM plugin then could create all the files in the base folder with a randomly generated prefix to make sure there is no name clash:

1 Like

Late follow-up. In the end, I moved the exported files from the subdirectory created by 3DSlicer to the parent directory and removed the empty subdirectory.

import os
import shutil

import DICOMScalarVolumePlugin

def export_volume_to_DICOM(slicer: object, vol_name: str, dir_out: str):
    if len(vol_name) == 0:
        raise ValueError("invalid volume name")
    if len(dir_out) == 0:
        raise ValueError("invalid output directory")
    if not (len(os.listdir(dir_out)) == 0):
        raise ValueError(f'Directory not empty {dir_out}')

    # get patient and study name
    shNode = slicer.mrmlScene.GetSubjectHierarchyNode()
    volumeNode = slicer.util.getNode(vol_name)
    seriesItem = shNode.GetItemByDataNode(volumeNode)
    studyItem = shNode.GetItemParent(seriesItem)
    patientItem = shNode.GetItemParent(studyItem)
    dcm_patient_id = shNode.GetItemAttribute(patientItem, 'DICOM.PatientID')
    dcm_study_name = shNode.GetItemName(studyItem)
    # export to file
    exporter = DICOMScalarVolumePlugin.DICOMScalarVolumePluginClass()
    exportables = exporter.examineForExport(seriesItem)
    for exp in exportables:
        # set output folder
        exp.directory = dir_out
        # here we set DICOM PatientID and StudyID tags
        exp.setTag('PatientID', dcm_patient_id)
        exp.setTag('StudyID', dcm_study_name)

    exporter.export(exportables)

def move_files(source, dest):
    files = os.listdir(source)
    for file in files:
        file_name = os.path.join(source, file)
        shutil.move(file_name, dest)
    print(f"Moved files from {source} to {dest}")


def get_immediate_subdirectories(a_dir):
    ret = [name for name in os.listdir(a_dir)
           if os.path.isdir(os.path.join(a_dir, name))]
    return ret


def get_immediate_subdirectory(a_dir):
    ret = get_immediate_subdirectories(a_dir)
    if len(ret) != 1:
        raise ValueError(f'Must contain exactly one subdirectory {ret}')
    ret = ret[0]
    return ret


def move_files_from_immediate_subdir_to_parent(parent_dir):
    dir_sub = get_immediate_subdirectory(parent_dir)
    dir_sub = os.path.join(parent_dir, dir_sub)
    move_files(dir_sub, parent_dir)


def remove_immediate_subdir(parent_dir):
    dir_sub = get_immediate_subdirectory(parent_dir)
    dir_sub = os.path.join(parent_dir, dir_sub)
    os.removedirs(dir_sub)
    print(f'removed dir {dir_sub}')

# example from Jupyter managed 3DSlicer: clean up temporary directory right after file export
export_volume_to_DICOM(slicer, 'my_volume', dir_out)
move_files_from_immediate_subdir_to_parent(dir_out)
remove_immediate_subdir(dir_out)
1 Like