def start_process(self): """starts the process linked to the module selected; that is in case of dcm2nii it runs the extraction of nifti- files from the DICOM folder or in case of displayN4corr it displays all nifti files available in the folder""" input2process = [] [ input2process.append(self.mOutput.item(x).text()) for x in range(self.mOutput.count()) ] if not input2process: Output.msg_box( text="At least one folder with data must be selected!", title='No directory selected') elif len(input2process) != 0 and self.option_gui == 'dcm2niix': print('in total, {} folders were selected'.format( len(input2process))) preprocDCM2NII.PreprocessDCM(input2process) elif len( input2process) != 0 and self.option_gui == 'displayNiftiFiles': input_with_path = [] [ input_with_path.extend( glob.glob(self.working_dir + '/**/' + x, recursive=True)) for x in input2process ] Imaging.load_imageviewer( 'itk-snap', input_with_path ) # to-date, only itk-snap available. could be changed
def save_cfg_dicomdir(self): """Function intended to save the DICOM directory once button is pressed""" self.cfg['folders']['dicom'] = self.working_dir Configuration.save_config(self.cfg['folders']["rootdir"], self.cfg) Output.msg_box( text="Folder changed in the configuration file to {}".format( self.working_dir), title='Changed folder')
def start_converting(self): folderlist = [] [folderlist.append(self.mOutput.item(x).text()) for x in range(self.mOutput.count())] print('in total, {} folders were selected'.format(len(folderlist))) if not folderlist: Output.msg_box(text="At least one folder with DICOM data needed!", title="No directory selected") else: preprocDCM2NII.PreprocessDCM(folderlist)
def view_template(self): """this function opens a list dialog and enables selecting NIFTI files for e.g. check the content (identical function as in GUITabPreprocessANTs.py.""" if not self.selected_subj_Gen: Output.msg_box(text="No image selected. Please indicate image(s) to load.", title="No templates selected") return else: template_list = [FileOperations.return_full_filename(self.wdirTemplate, x) for x in self.selected_subj_Gen] Imaging.load_imageviewer('itk-snap', template_list) # to-date, only itk-snap available. could be changed
def wrapper_multiprocessing(self, fileIDs, subjects, modality): """To avoid redundancy between CT and MR registration to common space, this part is wrapped up here """ working_dir = self.cfg['folders']['nifti'] prefix = '{}2template'.format(modality) # Start multiprocessing framework start_multi = time.time() status = mp.Queue() processes = [ mp.Process(target=self.ANTsCoregisterMultiprocessing, args=(filename_fixed, filename_moving, no_subj, os.path.join(working_dir, no_subj), 'registration', prefix, status)) for filename_fixed, filename_moving, no_subj in fileIDs ] for p in processes: p.start() while any([p.is_alive() for p in processes]): while not status.empty(): filename_fixed, filename_moving, no_subj = status.get() print("\tRegistering {} (f) to {} (m), in ANTsPy\n".format( filename_fixed, os.path.split(filename_moving)[1], no_subj)) time.sleep(0.1) for p in processes: p.join() # Functions creating/updating pipeline log, which documents all steps along with settings for subjID in subjects: files_processed = [(os.path.split(file_moving)[1], os.path.split(file_fixed)[1]) for file_fixed, file_moving, subj_no in fileIDs if subj_no == subjID] log_text = "{} successfully registered (@{}) to \n{}, \n\n Mean Duration per subject: {:.2f} " \ "secs".format(files_processed[0][0], time.strftime("%Y%m%d-%H%M%S"), files_processed[0][1], (time.time() - start_multi) / len(subjects)) Output.logging_routine(text=Output.split_lines(log_text), cfg=self.cfg, subject=str(subjID), module='{}-Registration'.format(modality), opt=self.cfg['preprocess']['registration'], project="") print( '\nIn total, a list of {} subject(s) was processed; {} registration took {:.2f}secs. ' 'overall'.format(len(subjects), modality, time.time() - start_multi))
def run_n4Bias_corr(self): """wrapper to start the preprocessing, that is the GUI in which the different options for ANTs routines are displayed""" if not self.selected_subj_ANT: Output.msg_box(text="No folder selected. To proceed, please indicate what folder to process. " "(For this option, numerous folders are possible for batch processing)", title="No subject selected") else: msg = "Are you sure you want to de-bias all NIFTI-files in the following folder(s):\n\n" \ "{}".format(''.join('--> {}\n'.format(c) for c in self.selected_subj_ANT)) ret = QMessageBox.question(self, "Start Debiasing", msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if ret == QMessageBox.Yes: N4BiasCorr.BiasCorrection().N4BiasCorrection(subjects=self.selected_subj_ANT)
def save_leadModel(lead_models, intensityProfiles, skelSkalms, filename=''): if not filename: Output.msg_box(text='No filename for saving lead model provided', title='No filename provided') return with open(filename, "wb") as f: pickle.dump(lead_models, f) pickle.dump(intensityProfiles, f) pickle.dump(skelSkalms, f)
def redefineDefault(self): """redefines which template is set as default on the right list and in the cfg-file""" if not self.selected_subj_Gen: Output.msg_box(text="No template selected. Please indicate new default one.", title="No data selected") return elif len(self.selected_subj_Gen) > 1: Output.msg_box(text="Please select only one image as default.", title="Too many templates selected") return else: default_template = [FileOperations.return_full_filename(self.wdirTemplate, x) for x in self.selected_subj_Gen] self.cfg['folders']['default_template'] = default_template[0] Configuration.save_config(ROOTDIR, self.cfg) self.run_reload_files()
def run_RegisterMRI2template(self): """Wrapper to run the coregistration routines for the MRI (moving image) to MRI templates (fixed image) specified in the config file using ANTs routines""" if not self.selected_subj_ANT: Output.msg_box(text="No folder selected. To proceed, please indicate what folder to process. " "(For this option, numerous folders are possible for batch processing)", title="No subject selected") else: msg = "Are you sure you want to coregister the preoperative imaging in the following folders:\n\n" \ "{}".format(''.join(' -> {}\n'.format(c) for c in self.selected_subj_ANT)) ret = QMessageBox.question(self, 'MessageBox', msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if ret == QMessageBox.Yes: ANTspyRoutines.RegistrationANTs().CoregisterMRI2template(subjects=self.selected_subj_ANT)
def display_nifti_files(self): """this function enables to select NIFTI files and e.g. compare the results obtained at this step.""" if not self.selected_subj_ANT: Output.msg_box(text="No folder selected. Please select subject.", title="No subject selected") return elif len(self.selected_subj_ANT) > 1: Output.msg_box(text="Please select only one subject to avoid loading too many images", title="Too many subjects selected") return else: image_folder = os.path.join(self.cfg['folders']['nifti'], self.selected_subj_ANT[0]) self.SelectFiles = TwoListGUI(working_directory=image_folder, option_gui="displayNiftiFiles") self.SelectFiles.show()
def CoregisterMRI2template(self, subjects): """Co-Registration of preoperative MRI to specific template""" print('\nStarting Co-Registration for {} subject(s)'.format( len(subjects))) all_files = FileOperations.get_filelist_as_tuple( inputdir=self.cfg['folders']['nifti'], subjects=subjects) sequences = self.cfg['preprocess']['normalisation']['sequences'].split( sep=',') # sequences of interest template = glob.glob( os.path.join( ROOTDIR, 'ext', 'templates', self.cfg['preprocess']['normalisation']['template_image'] + '/*')) fileIDs = [] for idx, seqs in enumerate(sequences): file_template = [ x for x in template if re.search(r'\w+.({}).'.format(seqs), x, re.IGNORECASE) ] # corresponding template if not file_template: Output.msg_box( text= "No template found. Please ensure templates are installed at './ext/templates'", title="Template missing!" ) # TODO install templates by default! return regex_complete = '{}{}'.format( self.cfg['preprocess']['ANTsN4']['prefix'], seqs) files_subj = [ x for x in all_files if x[0].endswith('.nii') and 'run' not in x[0] and re.search(r'\w+(?!_).({}).'.format(regex_complete), x[0], re.IGNORECASE) ] fileIDs.extend( tuple([(file_template[0], file_id, subj) for file_id, subj in files_subj])) if not fileIDs: Output.msg_box( text="No bias-corrected MRI found. Please double-check", title="Preprocessed MRI missing") return self.wrapper_multiprocessing(fileIDs, subjects, 'MRI')
def check_for_complete_input(self, item_list, modalities = ['CT', 'MRI']): """this function ensures, that only those subjects are displayed where MRI and CT data is available in the correct folders (surname_nameMRI or surname_nameCT)""" available_subjects = set([re.split(r'CT|MRI', x)[0] for x in list(item_list)]) item_list_complete = [] [item_list_complete.append(subj) for subj in list(available_subjects) if all([os.path.isdir(os.path.join(self.dicomdir, subj + y)) for y in modalities])] if len(available_subjects) != len(item_list_complete): incomplete = list(set(available_subjects) - set(item_list_complete)) Output.msg_box(text="There is incomplete data or directories have unknown names. Please ensure the presence" " of two folders (surname_nameCT) and (surname_nameMRI) for:" "\n{}".format(''.join(' -> {}\n'.format(c) for c in incomplete)), title="Incomplete data") return set(item_list_complete)
def run_RegisterCT2MRI(self): """Wrapper to run the coregistration routines for the CT (moving image) to the T1-sequence of the MRI (fixed image) using ANTs routines""" if not self.selected_subj_ANT: Output.msg_box(text="No folder selected. To proceed, please indicate what folder to process. " "(For this option, numerous folders are possible for batch processing)", title="No subject selected") else: msg = "Are you sure you want to coregister the postoperative CT imaging in the following folders:\n\n" \ "{}".format(''.join(' -> {}\n'.format(c) for c in self.selected_subj_ANT)) ret = QMessageBox.question(self, 'MessageBox', msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if ret == QMessageBox.Yes: ANTspyRoutines.RegistrationANTs().CoregisterCT2MRI(subjects=self.selected_subj_ANT, input_folder=self.cfg['folders']['nifti'])
def RegisterCT2MRI(): text = "Runs registration of CT imaging (moving) and T1-sequence (fixed) in order to get them into same space. " \ "The default option (see cfg-file) constitutes three steps: a) Rigid, b) Affine registration and c) " \ "Symmetric image Normalisation (SyN). For details see 'SyNRA'-option at " \ "https://github.com/ANTsX/ANTsPy/blob/master/ants/registration/interface.py. Non-default means, that the " \ "command-line for running ANTsRegistration can be modified according to the file: " \ "cmdline_ANTsRegistration.txt in the .utils directory. Please make sure to include all terms within *...* " \ "into the text-file as they will be replaced." return Output.split_lines(text)
def run_ManualCorrection(self): """wrapper which starts the plotting routine for the detected lead which enables manual corrections""" if len(self.selected_subj_ANT) != 1: Output.msg_box(text="Please select one and only one subject", title="Subjects selected") return else: msg = "Are you sure you want to process the following subject:\n\n" \ "{}".format(''.join(' -> {}\n'.format(c) for c in self.selected_subj_ANT)) ret = QMessageBox.question(self, 'MessageBox', msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if ret == QMessageBox.Yes: plotElecModel.PlotRoutines(subject=self.selected_subj_ANT[0], inputfolder=os.path.join( self.niftidir, self.selected_subj_ANT[0]))
def RegisterMRI2template(): text = "Runs registration of MR-imaging (moving) to template sequences (fixed) as defined in the config file in " \ "order to get them into same space. The default option constitutes three steps: a) Rigid, " \ "b) Affine registration and c) Symmetric image Normalisation (SyN), although all options available in ANTsPy" \ "are possible (for details see https://github.com/ANTsX/ANTsPy/blob/master/ants/registration/interface.py. " \ "Non-default means, that the command-line for running ANTsRegistration can be modified according to " \ "the file: cmdline_ANTsRegistration.txt in the .utils directory. Please make sure to include all terms " \ "within *...* into the text-file as they will be replaced." return Output.split_lines(text)
def run_LeadDetectionPaCER(self): """wrapper to start lead detection with PaCER routines translated to python; original data can be found at: https://github.com/adhusch/PaCER/""" if len(self.selected_subj_ANT) > 1: Output.msg_box( text= "Please select only one subject, as multiprocessing for lead detection is not intended", title="Too many subjects selected") return else: msg = "Are you sure you want to process the following subject:\n\n" \ "{}".format(''.join(' -> {}\n'.format(c) for c in self.selected_subj_ANT)) ret = QMessageBox.question(self, 'MessageBox', msg, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if ret == QMessageBox.Yes: LeadDetectionRoutines.PaCER_script( subjects=self.selected_subj_ANT)
def VisualiseLeadDetection(self): """wrapper to start comparisons between pre- and post-processed images after N4BiasCorrection""" if not self.selected_subj_ANT: Output.msg_box( text= "No folder selected. To proceed, please select at least one.", title="No subject selected") return elif len(self.selected_subj_ANT) > 1: Output.msg_box(text="Please select only one subj.", title="Too many subjects selected") return else: image_folder = os.path.join(self.cfg['folders']['nifti'], self.selected_subj_ANT[0]) self.SelectFiles = TwoListGUI(working_directory=image_folder, option_gui='displayNiftiFiles') self.SelectFiles.show()
def get_default_lead(lead_data): """obtains default lead properties according to the model proposed in the PaCER algorithm @ ./template""" if lead_data[ 'model'] == 'Boston Vercise Directional': # load mat-file to proceed mat_filename = 'boston_vercise_directed.mat' lead_model = loadmat( os.path.join(ROOTDIR, 'ext', 'LeadDBS', mat_filename), 'r')['electrode'] default_positions = { x: np.hstack(vals) for x, vals in lead_model.items() if x.endswith('position') } default_coordinates = np.array(lead_model['coords_mm']) # in [mm] else: Output.msg_box(text="Lead type not yet implemented.", title="Lead type not implemented") return return lead_model, default_positions, default_coordinates
def openDetails(self): """opens details file which has additional information on subjects """ import subprocess fileName = os.path.join(self.cfg['folders']['nifti'], 'subjdetails.csv') if os.path.isfile(fileName): if sys.platform == 'linux': subprocess.Popen(['xdg-open', ''.join(fileName)]) else: os.system('open"%s"' % fileName) subprocess.run(['open', fileName], check=True) else: Output.msg_box( text="Subject details unavailable, creating empty csv-file!", title="Detail file not found") subprocess.call([ 'touch', '{}subjdetails.csv'.format(self.cfg['folders']['nifti']) ])
def CoregisterCT2MRI(self, subjects, input_folder, fixed_image='reg_run[0-9]_bc_t1'): """Co-registration of postoperative CT to preoperative MRI for further analyses in same space; before registration presence of registered MRI data is ensured to avoid redundancy""" print('\nStarting co-registration for {} subject(s)'.format( len(subjects))) allfiles = FileOperations.get_filelist_as_tuple(inputdir=input_folder, subjects=subjects) self.check_for_normalisation(subjects) regex_complete = ['CT_', '{}_'.format(fixed_image.upper())] included_sequences = [ x for x in list( filter(re.compile(r"^(?!~).*").match, regex_complete)) ] file_ID_CT, file_ID_MRI = ([] for _ in range(2)) [ file_ID_CT.append(x) for x in allfiles if 'run' not in x[0] and re.search( r'\w+{}.'.format(included_sequences[0]), x[0], re.IGNORECASE) and x[0].endswith('.nii') ] [ file_ID_MRI.append(x) for x in allfiles # for simplicity written in a second line as regexp is slightly different if re.search(r'\w+(?!_).({}).'.format(included_sequences[1]), x[0], re.IGNORECASE) and x[0].endswith('.nii') ] if not file_ID_MRI: Output.msg_box(text="Bias-corrected MRI not found!", title="Preprocessed MRI unavailable") return fileIDs = list(FileOperations.inner_join(file_ID_CT, file_ID_MRI)) self.wrapper_multiprocessing(fileIDs, subjects, 'CT')
def __init__(self, _folderlist): self.logfile = True self.cfg = Configuration.load_config(ROOTDIR) self.DCM2NIIX_ROOT = os.path.join(ROOTDIR, 'ext', 'dcm2niix') if not os.path.isdir(self.cfg['folders']['dicom']): Output.msg_box( text="Please indicate a correct folder in the main GUI", title="Wrong folder") else: self.inputdir = self.cfg['folders']['dicom'] subjlist = self.create_subjlist(_folderlist) if not os.path.isdir(self.DCM2NIIX_ROOT): Output.msg_box( text= "Extracting imaging data from DICOM-files not successful because of wrong " "folder for 'dcm2niix'.", title="Wrong folder!") return if not os.path.isdir(self.cfg['folders']['nifti']): print( "\nDirectory for output is invalid; assuming same base and creating folder named 'nifti' therein!" ) self.outdir = os.path.join( os.path.split(self.inputdir)[0], 'nifti') if not os.path.isdir(self.outdir): os.mkdir(self.outdir) else: self.outdir = self.cfg['folders']['nifti'] lastsubj = self.get_index_nifti_folders( self.outdir, prefix=self.cfg['folders']['prefix']) self.create_csv_subjlist(subjlist, int(lastsubj)) self.convert_dcm2nii(subjlist, last_idx=int(lastsubj))
def load_leadModel(inputdir, filename): """Function loading results from [preprocLeadCT.py] which emulates the PaCER toolbox""" if not inputdir: Output.msg_box( text="No input folder provided, please double-check!", title="Missing input folder") return elif not os.path.isfile(filename): Output.msg_box( text= "Models for electrode unavailable, please run detection first!", title="Models not available") return else: with open( filename, "rb" ) as model: # roughly ea_loadreconstruction in the LeadDBS script lead_models = pickle.load(model) intensityProfiles = pickle.load(model) skelSkalms = pickle.load(model) return lead_models, intensityProfiles, skelSkalms
def show_nifti_files(self): """this function opens a list dialog and enables selecting NIFTI files for e.g. check the content (identical function as in GUITabPreprocessANTs.py.""" if not self.selected_subj_Gen: Output.msg_box( text= "No folder selected. To proceed, please indicate what folder to process.", title="No subject selected") return elif len(self.selected_subj_Gen) > 1: Output.msg_box( text= "Please select only one folder to avoid excessive image load", title="Number of selected files") return else: image_folder = os.path.join(self.cfg['folders']['nifti'], self.selected_subj_Gen[0]) self.SelectFiles = TwoListGUI(working_directory=image_folder, option_gui='displayNiftiFiles') self.SelectFiles.show()
def get_settings_from_config(self): """function which enters the settings according to cfg variable which is loaded""" if self.cfg == "": print() Output.msg_box( title="Warning", text= "No default settings found, please double check the folder content. " "Continuing with same settings.") else: if self.cfg["preprocess"]["dcm2nii"]["BIDSsidecar"] == 'yes': self.rbtnBIDSy.setChecked(True) else: self.rbtnBIDSn.setChecked(True) if self.cfg["preprocess"]["dcm2nii"]["OutputCompression"] == 'yes': self.rbtnCompressiony.setChecked(True) else: self.rbtnCompressionn.setChecked(True) if self.cfg["preprocess"]["dcm2nii"]["Verbosity"] == 0: self.rbtnVerbosity0.setChecked(True) elif self.cfg["preprocess"]["dcm2nii"]["Verbosity"] == 1: self.rbtnVerbosity1.setChecked(True) else: self.rbtnVerbosity2.setChecked(True) self.lineEditFilename.setText( self.cfg["preprocess"]["dcm2nii"]["OutputFileStruct"]) self.lineEditIncludeFiles.setText( self.cfg["preprocess"]["dcm2nii"]["IncludeFiles"]) if self.cfg["preprocess"]["dcm2nii"]["ReorientCrop"] == 'yes': self.rbtnReorientCropy.setChecked(True) else: self.rbtnReorientCropn.setChecked(True)
def LabelFilenameDCM2NII(): text = "Please enter here the prefix/file information that should be included when saving NIFTI files. " \ "Characters such as '<', '>', ':', '/' ... MUST be avoided (default is %p_%s). " \ "From documentation:\n\t %a : antenna (coil) number inserted. For example, the output filename" \ " 'myName%a' would generate 'myName1', 'myName2', each for each coil. Note that most scans combine data " \ "from all coils and in these cases this option is ignored. For example, most scans which combine " \ "data from all coils would simply be called 'myName'\n\t%d :" \ "series description (0008,103E) inserted. For example, an echo-planar image " \ "converted with 'myName%d' would yield 'myNameEPI' \n\t%e : echo number " \ "inserted. For example, a sequence with two echo times converted with the output" \ "filename 'myName%e' will yield 'myName1' and 'myName2'. Note that most MRI" \ " sequences only use a single echo time, and in these cases you would only get " \ "'myName1'.\n\t%f : input folder name inserted. For example, the output " \ "filename'myName%f' combined with an input folder '/usr/Subj22' will result in " \ "the output file named'myNameSubj22.nii' \n\t%i : patient ID " \ "(DICOM tag 0010,0020) inserted. For example, the output filename 'myName%i' " \ " would convert an image where the patient ID is named 'ID123' to be " \ " 'myNameID123.nii' \n\t%m : manufacturer name For example, the output filename" \ " 'myName%m' would convert an image from a GE scanner to 'myNameGE.nii', while " \ " an image from Philips would be 'myNamePh.nii', whereas Siemens would be " \ " 'myNameSi.nii', otherwise the manufacturer is not available ('myNameNA.nii')." \ " (requires dcm2nii versions from 2015 or later). \n\t%n : subject name (DICOM" \ " tag 0010,0010) inserted. For example, the output filename 'myName%n' would " \ " convert an image from John Doe to 'myNameJohnDoe.nii'. This option works best" \ " if your participant names use only English letters, for other European " \ " languages you may find it makes some basic conversions ('Müller' will become " \ " 'Muller'). For non-European languages you will find this option unsatisfactory" \ ". Perhaps future versions can support DICOM tag 0008,0005. \n\t %p: protocol" \ " name (DICOM tag 0018,1030) inserted. For example, the output filename" \ " 'myName%p' would convert image where protocol is named T1 to be 'myNameT1.nii'" \ " \n\t%q: sequence name (DICOM tag 0018,1020) inserted.For example," \ " the output filename 'myName%q' would convert a Spin Echo sequence to be " \ "'myNameSE.nii' (new feature, in versions from 30Aug2015).\n\t%s : series (DICOM " \ "tag 0020,0011) inserted. For example, the output filename 'myName%s' would " \ "convert the second series to be 'myName2.nii'. If you want to zero-pad the " \ "series number, insert the number of digits desired (0..9). For example applying " \ "the filter 'm%s' when converting 11 series will create files that will cause" \ " problems for a simple alphabetical sort, e.g. 'm1.nii,m11.nii,m2.nii...m9.nii'." \ " In contrast specifying 'm%3s' will help sorting (e.g. 'm001.nii,m002.nii" \ "...m011.nii').\n\t%t : session date and time inserted (DICOM tags 0008,0021 and " \ "0008,0030). For example, the output filename 'myName%t' would convert an image " \ "where the session began at 1:23pm on 13 Jan 2014 as 'myName20140113132322.nii' " \ "\n\t%z : Sequence Name (0018,0024) inserted, so a T1 scan converted with " \ "'myName%z' might yield 'myNameT1'." return Output.split_lines(text)
def __init__(self, parent=None): super(QWidget, self).__init__(parent) # Load configuration files and general settings self.cfg = Configuration.load_config(ROOTDIR) if os.path.isdir(self.cfg['folders']['dicom']): self.dicomdir = self.cfg['folders']['dicom'] else: self.dicomdir = FileOperations.set_wdir_in_config(self.cfg, foldername='dicom', init=True) self.cfg['folders']['dicom'] = self.dicomdir self.cfg['folders']['rootdir'] = ROOTDIR Configuration.save_config(ROOTDIR, self.cfg) # Create general layout self.tot_layout = QVBoxLayout(self) self.mid_layout = QHBoxLayout(self) # ============================ Create upper of GUI, i.e. working directory ============================ self.folderboxDcm2nii = QGroupBox("Directory (DICOM-files)") self.HBoxUpperDcm2nii = QVBoxLayout(self.folderboxDcm2nii) self.label_dicomdir = QLabel('dicom DIR: {}'.format(self.dicomdir)) self.HBoxUpperDcm2nii.addWidget(self.label_dicomdir) self.btn_dicomdir = QPushButton('Change working \ndirectory') self.btn_dicomdir.setFixedSize(150, 40) self.btn_dicomdir.clicked.connect(self.change_dicomdir) self.btn_savedir = QPushButton('Save directory \nto config file') self.btn_savedir.setFixedSize(150, 40) self.btn_savedir.setToolTip(Output.split_lines(setToolTips.saveDirButton())) self.btn_savedir.clicked.connect(self.save_cfg) hlay_upper = QHBoxLayout() hlay_upper.addWidget(self.btn_dicomdir) hlay_upper.addWidget(self.btn_savedir) hlay_upper.addStretch(1) self.HBoxUpperDcm2nii.addLayout(hlay_upper) # ==================== Create Content for Lists, i.e. input/output ==================== self.listboxInputDcm2nii = QGroupBox('Available subjects in working directory') self.listboxInput = QVBoxLayout(self.listboxInputDcm2nii) self.mInput = QListWidget() self.listboxInput.addWidget(self.mInput) self.mButtonToAvailable = QPushButton("<<") self.mBtnMoveToAvailable = QPushButton(">") self.mBtnMoveToSelected = QPushButton("<") self.mButtonToSelected = QPushButton(">>") self.mBtnUp = QPushButton("Up") self.mBtnDown = QPushButton("Down") self.listboxOutputDcm2nii = QGroupBox('Subjects to process') self.listboxOutput = QVBoxLayout(self.listboxOutputDcm2nii) self.mOutput = QListWidget() self.listboxOutput.addWidget(self.mOutput) # First column on the left side vlay = QVBoxLayout() vlay.addStretch() vlay.addWidget(self.mBtnMoveToAvailable) vlay.addWidget(self.mBtnMoveToSelected) vlay.addStretch() vlay.addWidget(self.mButtonToAvailable) vlay.addWidget(self.mButtonToSelected) vlay.addStretch() # Second column on the right side vlay2 = QVBoxLayout() vlay2.addStretch() vlay2.addWidget(self.mBtnUp) vlay2.addWidget(self.mBtnDown) vlay2.addStretch() # ==================== Lower part of GUI, i.e. Preferences/Start estimation ==================== self.btn_preferences = QPushButton("Preferences") self.btn_preferences.clicked.connect(self.settings_show) self.btn_run_dcm2niix = QPushButton("Run dcm2niix") self.btn_run_dcm2niix.setToolTip(setToolTips.run_dcm2niix()) self.btn_run_dcm2niix.clicked.connect(self.start_converting) hlay_bottom = QHBoxLayout() hlay_bottom.addStretch(1) hlay_bottom.addWidget(self.btn_preferences) hlay_bottom.addWidget(self.btn_run_dcm2niix) hlay_bottom.addStretch() # ==================== Set all contents to general Layout ======================= self.mid_layout.addWidget(self.listboxInputDcm2nii) self.mid_layout.addLayout(vlay) self.mid_layout.addWidget(self.listboxOutputDcm2nii) self.mid_layout.addLayout(vlay2) self.tot_layout.addWidget(self.folderboxDcm2nii) self.tot_layout.addLayout(self.mid_layout) self.tot_layout.addLayout(hlay_bottom) try: self.mInput.clear() items = FileOperations.list_folders(inputdir=self.cfg['folders']['dicom'], prefix='', files2lookfor='') items = self.check_for_complete_input(list(items)) self.add_available_subj(items) except FileExistsError: print('{} without any valid files/folders, continuing ...'.format(self.dicomdir)) self.update_buttons_status() self.connections()
def save_cfg(self): """Function intended to save the DICOM directory once button is pressed""" self.cfg['folders']['dicom'] = self.dicomdir Configuration.save_config(self.cfg['folders']['rootdir'], self.cfg) Output.msg_box(text="Folder changed in configuration to {}".format(self.dicomdir), title="Changed folder")
def dcm2niix_multiprocessing(self, name_subj, no_subj, dcm2niix_bin, last_idx, total_subj, status): """function intended to provide multiprocessing approach to speed up extraction of DICOM data to nifti files""" modalities = ['CT', 'MRI'] if self.logfile: log_filename = os.path.join( ROOTDIR, 'logs', 'log_DCM2NII_' + str(no_subj + last_idx) + time.strftime("%Y%m%d-%H%M%S")) else: log_filename = os.devnull subj_outdir = os.path.join( self.outdir, self.cfg['folders']['prefix'] + str(no_subj + last_idx)) FileOperations.create_folder(subj_outdir) start_time_subject = time.time() keptfiles, deletedfiles = ([] for _ in range(2)) for mod in modalities: status.put((name_subj, mod, no_subj, total_subj)) input_folder_name = os.path.join(self.inputdir, name_subj + mod) # input_folder_files = [f.path for f in os.scandir(input_folder_name) # if (f.is_dir() and ('100' in f.path or 'DICOM' in f.path or '001' in f.path))] input_folder_files = [] [ input_folder_files.append(item) for item in os.listdir(input_folder_name) if (os.path.isdir(os.path.join(input_folder_name, item)) and ( '100' in item or 'DICOM' in item or '001' in item)) ] orig_stdout = sys.stdout sys.stdout = open(log_filename, 'w') for folder in input_folder_files: subprocess.call( [ dcm2niix_bin, '-a', 'y', # anonimisation of DICOM data '-b', self.cfg['preprocess']['dcm2nii']['BIDSsidecar'][0], '-z', self.cfg['preprocess']['dcm2nii']['OutputCompression'] [0], '-f', self.cfg['preprocess']['dcm2nii']['OutputFileStruct'], '-o', subj_outdir, '-w', str(self.cfg['preprocess']['dcm2nii'] ['NameConflicts']), '-v', str(self.cfg['preprocess']['dcm2nii']['Verbosity']), '-x', str(self.cfg['preprocess']['dcm2nii']['ReorientCrop']), folder ], stdout=sys.stdout, stderr=subprocess.STDOUT) sys.stdout.close() sys.stdout = orig_stdout files_kept, files_deleted = self.select_sequences(subj_outdir) keptfiles.extend(files_kept) deletedfiles.extend(files_deleted) # Functions creating/updating pipeline log, which document individually all steps along with settings log_text = "{} files successfully converted: {}, \n\nand {} deleted: {}.\nDuration: {:.2f} secs" \ .format(len(set(keptfiles)), '\n\t{}'.format('\n\t'.join(os.path.split(x)[1] for x in sorted(set(keptfiles)))), len(set(deletedfiles)), '\n\t{}'.format('\n\t'.join(os.path.split(x)[1] for x in sorted(set(deletedfiles)))), time.time() - start_time_subject) Output.logging_routine(text=Output.split_lines(log_text), cfg=self.cfg, subject=self.cfg['folders']['prefix'] + str(no_subj), module='dcm2nii', opt=self.cfg['preprocess']['dcm2nii'], project="")
def run_dcm2niix(): text = "Before running this script, please make sure that the prefrences are set properly. moreover it is " \ "recommended to name DICOM folders xxxCT and xxxMRI or one of both. cDBS will specifically look for these " \ "folders to convert DICOM data from." return Output.split_lines(text)