def test_readDicom(): dicomDir = os.path.join(os.path.dirname(__file__), 'test_input') dicomFile = os.path.join(dicomDir, test_dicomFile) dicomImg1 = rd.readDicomFromFile(dicomFile) vol1 = rd.parseDicomVolume(dicomImg1, 64) assert vol1 is not None with open(dicomFile, 'rb') as fp: data = fp.read() dicomImg2 = rd.readDicomFromBuffer(data) vol2 = rd.parseDicomVolume(dicomImg2, 64) assert vol2 is not None assert (vol1 == vol2).all() fileInterface = FileInterface() fileInterface.initWatch(dicomDir, '*.dcm', 0) dicomImg3 = rd.readRetryDicomFromFileInterface(fileInterface, dicomFile) vol3 = rd.parseDicomVolume(dicomImg3, 64) assert vol3 is not None assert (vol1 == vol3).all() # read in a truncated file, should fail and return None. trucatedDicomFile = os.path.join(dicomDir, test_dicomTruncFile) dicomImg4 = rd.readRetryDicomFromFileInterface(fileInterface, trucatedDicomFile) assert dicomImg4 is None # Test convert to nifti niftiObject = dicomreaders.mosaic_to_nii(dicomImg3) assert niftiObject is not None fileInterface.fileWatcher.__del__() fileInterface.fileWatcher = None
def getAxesForTransform(startingDicomFile, cfg): """ Load one example file """ nifti_object = nib.load(cfg.ref_BOLD) target_orientation = nib.aff2axcodes(nifti_object.affine) dicom_object = getLocalDicomData(cfg, startingDicomFile) dicom_object = dicomreaders.mosaic_to_nii(dicom_object) dicom_orientation = nib.aff2axcodes(dicom_object.affine) return target_orientation, dicom_orientation # from here you can save and load it so getTransform is hard coded --you only need to run this once
def dicom2nii(filename): dicomObject = dicom.read_file(filename) niftiObject = dicomreaders.mosaic_to_nii(dicomObject) print(nib.aff2axcodes(niftiObject.affine)) splitList=filename.split('/') fullNiftiFilename='/'+os.path.join(*splitList[0:-1] , splitList[-1].split('.')[0]+'.nii.gz') print('fullNiftiFilename=',fullNiftiFilename) niftiObject.to_filename(fullNiftiFilename) return fullNiftiFilename
def saveAsNiftiImage(dicomDataObject, fullNiftiFilename, cfg, reference): niftiObject = dicomreaders.mosaic_to_nii(dicomDataObject) temp_data = niftiObject.get_fdata() output_image_correct = nib.orientations.apply_orientation( temp_data, cfg.axesTransform) correct_object = new_img_like(reference, output_image_correct, copy_header=True) correct_object.to_filename(fullNiftiFilename) return fullNiftiFilename
def test_readDicom(): dicomImg1 = imgHandler.readDicomFromFile(dicomFile) vol1 = imgHandler.parseDicomVolume(dicomImg1, 64) assert vol1 is not None with open(dicomFile, 'rb') as fp: data = fp.read() dicomImg2 = imgHandler.readDicomFromBuffer(data) vol2 = imgHandler.parseDicomVolume(dicomImg2, 64) assert vol2 is not None assert (vol1 == vol2).all() # if dataInterface is not initialized with allowedDirs or allowedFileTypes it should fail dataInterface = DataInterface() with pytest.raises(ValidationError): dataInterface.initWatch(dicomDir, '*.dcm', 0) # Now allow all dirs and file types dataInterface = DataInterface(allowedDirs=['*'], allowedFileTypes=['*']) dataInterface.initWatch(dicomDir, '*.dcm', 0) dicomImg3 = imgHandler.readRetryDicomFromDataInterface( dataInterface, dicomFile) vol3 = imgHandler.parseDicomVolume(dicomImg3, 64) assert vol3 is not None assert (vol1 == vol3).all() # read in a truncated file, should fail and return None. trucatedDicomFile = os.path.join(dicomDir, test_dicomTruncFile) dicomImg4 = imgHandler.readRetryDicomFromDataInterface( dataInterface, trucatedDicomFile) assert dicomImg4 is None # Test convert to nifti niftiObject = dicomreaders.mosaic_to_nii(dicomImg3) assert niftiObject is not None dataInterface.fileWatcher.__del__() dataInterface.fileWatcher = None # Test anonymization of sensitive patient fields def countUnanonymizedSensitiveAttrs(dicomImg): sensitiveAttrs = 0 for attr in imgHandler.attributesToAnonymize: if hasattr(dicomImg, attr) and getattr(dicomImg, attr) != "": sensitiveAttrs += 1 return sensitiveAttrs dicomImg5 = imgHandler.readDicomFromFile(dicomFile) assert countUnanonymizedSensitiveAttrs(dicomImg5) >= 1 imgHandler.anonymizeDicom(dicomImg5) assert countUnanonymizedSensitiveAttrs(dicomImg5) == 0
def saveAsNiftiImage(dicomDataObject,expected_dicom_name,cfg): A = time.time() nameToSaveNifti = expected_dicom_name.split('.')[0] + '.nii.gz' tempNiftiDir = os.path.join(cfg.codeDir, 'tmp/convertedNiftis/') if not os.path.exists(tempNiftiDir): command = 'mkdir -pv {0}'.format(tempNiftiDir) call(command,shell=True) fullNiftiFilename = os.path.join(tempNiftiDir,nameToSaveNifti) niftiObject = dicomreaders.mosaic_to_nii(dicomDataObject) temp_data = niftiObject.get_fdata() rounded_temp_data = np.round(temp_data) output_image_correct = nib.orientations.apply_orientation(temp_data,cfg.axesTransform) correct_object = new_img_like(cfg.ref_BOLD,output_image_correct,copy_header=True) correct_object.to_filename(fullNiftiFilename) B = time.time() print(B-A)
def dicom2nii(filename): dicomObject = dicom.read_file(filename) niftiObject = dicomreaders.mosaic_to_nii(dicomObject) temp_data = niftiObject.get_data() output_image_correct = nib.orientations.apply_orientation( temp_data, ornt_transform) correct_object = new_img_like(templateFunctionalVolume, output_image_correct, copy_header=True) # print(nib.aff2axcodes(niftiObject.affine)) splitList = filename.split('/') # fullNiftiFilename='/'+os.path.join(*splitList[0:-1] , splitList[-1].split('.')[0]+'.nii.gz') fullNiftiFilename = os.path.join(tmp_folder, splitList[-1].split('.')[0] + '.nii.gz') print('fullNiftiFilename=', fullNiftiFilename) correct_object.to_filename(fullNiftiFilename) return fullNiftiFilename
def processMosaicFile(self, mosaic_dcm_fname): """ Process a given mosaic dicom file This method will read the dicom mosaic file. Convert to a nifti object that will provide the 3D voxel array for this mosaic. Reorder to RAS+, and then send to the pynealSocket Parameters ---------- mosaic_dcm_fname : string full path to the dicom mosaic file that you want to process """ ### Figure out the volume index for this mosaic by reading # the field from the file name itself mosaicFile_root, mosaicFile_name = os.path.split(mosaic_dcm_fname) volIdx = int( Siemens_mosaicVolumeNumberField.search(mosaicFile_name).group( 0)) - 1 self.logger.info('Volume {} processing'.format(volIdx)) ### Parse the mosaic image into a 3D volume # we use the nibabel mosaic_to_nii() method which does a lot of the # heavy-lifting of extracting slices, arranging in a 3D array, and # grabbing the affine dcm = pydicom.dcmread(mosaic_dcm_fname) # create dicom object thisVol = dicomreaders.mosaic_to_nii(dcm) # convert to nifti # convert to RAS+ thisVol_RAS = nib.as_closest_canonical(thisVol) # get the data as a contiguous array (required for ZMQ) thisVol_RAS_data = np.ascontiguousarray(thisVol_RAS.get_fdata()) ### Create a header with metadata info volHeader = { 'volIdx': volIdx, 'dtype': str(thisVol_RAS_data.dtype), 'shape': thisVol_RAS_data.shape, 'affine': json.dumps(thisVol_RAS.affine.tolist()), 'TR': str(dcm.RepetitionTime / 1000) } ### Send the voxel array and header to the pynealSocket self.sendVolToPynealSocket(volHeader, thisVol_RAS_data)
def saveAsNiftiImage(dicomDataObject, fullNiftiFilename, cfg, reference): """ This function takes in a dicom data object written in bytes, what you expect the dicom file to be called (we will use the same name format for the nifti file), and the config file while will have (1) the axes transformation for the dicom file and (2) the header information from a reference scan. Used externally. """ niftiObject = dicomreaders.mosaic_to_nii(dicomDataObject) temp_data = niftiObject.get_fdata() output_image_correct = nib.orientations.apply_orientation( temp_data, cfg.axesTransform) correct_object = new_img_like(reference, output_image_correct, copy_header=True) correct_object.to_filename(fullNiftiFilename) return fullNiftiFilename
def getAxesForTransform(startingDicomFile, cfg): """ This function takes a single dicom file (which can be the first file) and the config file to obtain the target_orientation (in nifti space) and the dicom_orientation (in the original space). NOTE: You only need to run this function once to obtain the target and dicom orientations. You can save and load these variables so that 'getTransform()' is hard coded. Used externally. """ #Load one example file nifti_object = nib.load(cfg.ref_BOLD) target_orientation = nib.aff2axcodes(nifti_object.affine) dicom_object = readDicomFromFile(startingDicomFile) dicom_object = dicomreaders.mosaic_to_nii(dicom_object) dicom_orientation = nib.aff2axcodes(dicom_object.affine) return target_orientation, dicom_orientation
def doRuns(cfg, fileInterface, projectComm): """ This function is called by 'main()' below. Here, we use the 'fileInterface' to read in dicoms (presumably from the scanner, but here it's from a folder with previously collected dicom files), doing some sort of analysis in the cloud, and then sending the info to the web browser. INPUT: [1] cfg (configuration file with important variables) [2] fileInterface (this will allow a script from the cloud to access files from the stimulus computer, which receives dicom files directly from the Siemens console computer) [3] projectComm (communication pipe to talk with projectInterface) OUTPUT: None. This is the main function that is called when you run 'sample.py'. Here, you will set up an important argument parser (mostly provided by the toml configuration file), initiate the class fileInterface, and then call the function 'doRuns' to actually start doing the experiment. """ # variables we'll use throughout scanNum = cfg.scanNum[0] runNum = cfg.runNum[0] # before we get ahead of ourselves, we need to make sure that the necessary file # types are allowed (meaning, we are able to read them in)... in this example, # at the very least we need to have access to dicom and txt file types. # use the function 'allowedFileTypes' in 'fileClient.py' to check this! # INPUT: None # OUTPUT: # [1] allowedFileTypes (list of allowed file types) allowedFileTypes = fileInterface.allowedFileTypes() print( "" "-----------------------------------------------------------------------------\n" "Before continuing, we need to make sure that dicoms are allowed. To verify\n" "this, use the 'allowedFileTypes'.\n" "Allowed file types: %s" % allowedFileTypes) # obtain the path for the directory where the subject's dicoms live if cfg.isSynthetic: cfg.dicomDir = cfg.imgDir else: subj_imgDir = "{}.{}.{}".format(cfg.datestr, cfg.subjectName, cfg.subjectName) cfg.dicomDir = os.path.join(cfg.imgDir, subj_imgDir) print( "Location of the subject's dicoms: \n%s\n" % cfg.dicomDir, "-----------------------------------------------------------------------------" ) # initialize a watch for the entire dicom folder (it doesn't look for a # specific dicom) using the function 'initWatch' in 'fileClient.py' # INPUT: # [1] cfg.dicomDir (where the subject's dicom files live) # [2] cfg.dicomNamePattern (the naming pattern of dicom files) # [3] cfg.minExpectedDicomSize (a check on size to make sure we don't # accidentally grab a dicom before it's fully acquired) print("• initalize a watch for the dicoms using 'initWatch'") fileInterface.initWatch(cfg.dicomDir, cfg.dicomNamePattern, cfg.minExpectedDicomSize) # we will use the function 'sendResultToWeb' in 'projectUtils.py' whenever we # want to send values to the web browser so that they can be plotted in the # --Data Plots-- tab # INPUT: # [1] projectComm (the communication pipe) # [2] runNum (not to be confused with the scan number) # [3] this_TR (timepoint of interest) # [4] value (value you want to send over to the web browser) # ** the inputs MUST be python integers; it won't work if it's a numpy int # # here, we are clearing an already existing plot print("• clear any pre-existing plot using 'sendResultToWeb'") projUtils.sendResultToWeb(projectComm, runNum, None, None) print( "" "-----------------------------------------------------------------------------\n" "In this sample project, we will retrieve the dicom file for a given TR and\n" "then convert the dicom file to a nifti object. **IMPORTANT: In this sample\n" "we won't care about the exact location of voxel data (we're only going to\n" "indiscriminately get the average activation value for all voxels). This\n" "actually isn't something you want to actually do but we'll go through the\n" "to get the data in the appropriate nifti format in the 'advanced sample\n" "project.** We are doing things in this way because it is the simplest way\n" "we can highlight the functionality of rt-cloud, which is the purpose of\n" "this sample project.\n" ".............................................................................\n" "NOTE: We will use the function 'readRetryDicomFromFileInterface' to retrieve\n" "specific dicom files from the subject's dicom folder. This function calls\n" "'fileInterface.watchFile' to look for the next dicom from the scanner.\n" "Since we're using previously collected dicom data, this is functionality is\n" "not particularly relevant for this sample project but it is very important\n" "when running real-time experiments.\n" "-----------------------------------------------------------------------------\n" ) num_total_TRs = 10 # number of TRs to use for example 1 if cfg.isSynthetic: num_total_TRs = cfg.numSynthetic all_avg_activations = np.zeros((num_total_TRs, 1)) for this_TR in np.arange(num_total_TRs): # declare variables that are needed to use 'readRetryDicomFromFileInterface' timeout_file = 5 # small number because of demo, can increase for real-time # use 'getDicomFileName' from 'readDicom.py' to obtain the filename structure # of the dicom data you want to get... which is useful considering how # complicated these filenames can be! # INPUT: # [1] cfg (config parameters) # [2] scanNum (scan number) # [3] fileNum (TR number, which will reference the correct file) # OUTPUT: # [1] fullFileName (the filename of the dicom that should be grabbed) fileName = getDicomFileName(cfg, scanNum, this_TR) # use 'readRetryDicomFromFileInterface' in 'readDicom.py' to wait for dicom # files to come in (by using 'watchFile' in 'fileClient.py') and then # reading the dicom file once it receives it detected having received it # INPUT: # [1] fileInterface (this will allow a script from the cloud to access files # from the stimulus computer that receives dicoms from the Siemens # console computer) # [2] filename (for the dicom file we're watching for and want to load) # [3] timeout (time spent waiting for a file before timing out) # OUTPUT: # [1] dicomData (with class 'pydicom.dataset.FileDataset') print("• use 'readRetryDicomFromFileInterface' to read dicom file for", "TR %d, %s" % (this_TR, fileName)) dicomData = readRetryDicomFromFileInterface(fileInterface, fileName, timeout_file) if cfg.isSynthetic: niftiObject = convertDicomImgToNifti(dicomData) else: # use 'dicomreaders.mosaic_to_nii' to convert the dicom data into a nifti # object. additional steps need to be taken to get the nifti object in # the correct orientation, but we will ignore those steps here. refer to # the 'advanced sample project' for more info about that print("| convert dicom data into a nifti object") niftiObject = dicomreaders.mosaic_to_nii(dicomData) # take the average of all the activation values avg_niftiData = np.mean(niftiObject.get_data()) # avg_niftiData = np.round(avg_niftiData,decimals=2) print("| average activation value for TR %d is %f" % (this_TR, avg_niftiData)) max_niftiData = np.amax(niftiObject.get_data()) print("| max activation value for TR %d is %d" % (this_TR, max_niftiData)) # use 'sendResultToWeb' from 'projectUtils.py' to send the result to the # web browser to be plotted in the --Data Plots-- tab. print("| send result to the web, plotted in the 'Data Plots' tab") projUtils.sendResultToWeb(projectComm, runNum, int(this_TR), float(avg_niftiData)) # save the activations value info into a vector that can be saved later all_avg_activations[this_TR] = avg_niftiData # create the full path filename of where we want to save the activation values vector # we're going to save things as .txt and .mat files output_textFilename = '/tmp/cloud_directory/tmp/avg_activations.txt' output_matFilename = os.path.join( '/tmp/cloud_directory/tmp/avg_activations.mat') # use 'putTextFile' from 'fileClient.py' to save the .txt file # INPUT: # [1] filename (full path!) # [2] data (that you want to write into the file) print( "" "-----------------------------------------------------------------------------\n" "• save activation value as a text file to tmp folder") fileInterface.putTextFile(output_textFilename, str(all_avg_activations)) # use sio.save mat from scipy to save the matlab file print("• save activation value as a matlab file to tmp folder") sio.savemat(output_matFilename, {'value': all_avg_activations}) print( "" "-----------------------------------------------------------------------------\n" "REAL-TIME EXPERIMENT COMPLETE!") return
def doRuns(cfg, dataInterface, subjInterface, webInterface): """ This function is called by 'main()' below. Here, we use the 'dataInterface' to read in dicoms (presumably from the scanner, but here it's from a folder with previously collected dicom files), doing some sort of analysis in the cloud, and then sending the info to the web browser. INPUT: [1] cfg - configuration file with important variables) [2] dataInterface - this will allow this script runnin in the cloud to access files from the stimulus computer, which receives dicom files directly from the MRI Scanner console [3] subjInterface - this allows sending feedback (e.g. classification results) to a subjectService running on the presentation computer to provide feedback to the subject (and optionally get their response). [4] webInterface - this allows updating information on the experimenter webpage. For example to plot data points, or update status messages. OUTPUT: None. """ # variables we'll use throughout scanNum = cfg.scanNum[0] runNum = cfg.runNum[0] print(f"Doing run {runNum}, scan {scanNum}") """ Before we get ahead of ourselves, we need to make sure that the necessary file types are allowed (meaning, we are able to read them in)... in this example, at the very least we need to have access to dicom and txt file types. use the function 'allowedFileTypes' in 'fileClient.py' to check this! If allowedTypes doesn't include the file types we need to use then the file service (scannerDataService) running at the control room computer will need to be restarted with the correct list of allowed types provided. INPUT: None OUTPUT: [1] allowedFileTypes (list of allowed file types) """ allowedFileTypes = dataInterface.getAllowedFileTypes() if verbose: print("" "-----------------------------------------------------------------------------\n" "Before continuing, we need to make sure that dicoms are allowed. To verify\n" "this, use the 'allowedFileTypes'.\n" "Allowed file types: %s" %allowedFileTypes) # obtain the path for the directory where the subject's dicoms live if cfg.isSynthetic: cfg.dicomDir = cfg.imgDir else: subj_imgDir = "{}.{}.{}".format(cfg.datestr, cfg.subjectName, cfg.subjectName) cfg.dicomDir = os.path.join(cfg.imgDir, subj_imgDir) if verbose: print("Location of the subject's dicoms: \n" + cfg.dicomDir + "\n" "-----------------------------------------------------------------------------") # If a dicomNamePattern is supplied in the config file, such as # "001_{SCAN:06d}_{TR:06d}.dcm", then call stringPartialFormat() to # set the SCAN number for the series of Dicoms we will be streaming. dicomScanNamePattern = stringPartialFormat(cfg.dicomNamePattern, 'SCAN', scanNum) """ There are several ways to receive Dicom data from the control room computer: 1. Using `initWatch()` and 'watchFile()` commands of dataInterface or the helper function `readRetryDicomFromDataInterface()` which calls watchFile() internally. 2. Using the streaming functions with `initScannerStream()` and `getImageData(stream)` which are also part of the dataInterface. """ if useInitWatch is True: """ Initialize a watch for the entire dicom folder using the function 'initWatch' of the dataInterface. (Later we will use watchFile() to look for a specific dicom) INPUT: [1] cfg.dicomDir (where the subject's dicom files live) [2] cfg.dicomNamePattern (the naming pattern of dicom files) [3] cfg.minExpectedDicomSize (a check on size to make sure we don't accidentally grab a dicom before it's fully acquired) """ if verbose: print("• initalize a watch for the dicoms using 'initWatch'") dataInterface.initWatch(cfg.dicomDir, dicomScanNamePattern, cfg.minExpectedDicomSize) else: # use Stream functions """ Initialize a Dicom stream by indicating the directory and dicom file pattern that will be streamed. INPUTs to initScannerStream(): [1] cfg.dicomDir (where the subject's dicom files live) [2] dicomScanNamePattern (the naming pattern of dicom files) [3] cfg.minExpectedDicomSize (a check on size to make sure we don't accidentally grab a dicom before it's fully acquired) """ streamId = dataInterface.initScannerStream(cfg.dicomDir, dicomScanNamePattern, cfg.minExpectedDicomSize) """ We will use the function plotDataPoint in webInterface whenever we want to send values to the web browser so that they can be plotted in the --Data Plots-- tab. However at the start of a run we will want to clear the plot, and we can use clearRunPlot(runId), or clearAllPlots() also in the webInterface object. """ if verbose: print("• clear any pre-existing plot for this run using 'clearRunPlot(runNum)'") webInterface.clearRunPlot(runNum) if verbose: print("" "-----------------------------------------------------------------------------\n" "In this sample project, we will retrieve the dicom file for a given TR and\n" "then convert the dicom file to a nifti object. **IMPORTANT: In this sample\n" "we won't care about the exact location of voxel data (we're only going to\n" "indiscriminately get the average activation value for all voxels). This\n" "actually isn't something you want to actually do but we'll go through the\n" "to get the data in the appropriate nifti format in the advanced sample\n" "project (amygActivation).** We are doing things in this way because it is the simplest way\n" "we can highlight the functionality of rt-cloud, which is the purpose of\n" "this sample project.\n" ".............................................................................\n" "NOTE: We will use the function readRetryDicomFromDataInterface() to retrieve\n" "specific dicom files from the subject's dicom folder. This function calls\n" "'dataInterface.watchFile' to look for the next dicom from the scanner.\n" "Since we're using previously collected dicom data, this functionality is\n" "not particularly relevant for this sample project but it is very important\n" "when running real-time experiments.\n" "-----------------------------------------------------------------------------\n") num_total_TRs = 10 # number of TRs to use for example 1 if cfg.isSynthetic: num_total_TRs = cfg.numSynthetic all_avg_activations = np.zeros((num_total_TRs, 1)) for this_TR in np.arange(num_total_TRs): # declare variables that are needed to use in get data requests timeout_file = 5 # small number because of demo, can increase for real-time dicomFilename = dicomScanNamePattern.format(TR=this_TR) if useInitWatch is True: """ Use 'readRetryDicomFromDataInterface' in 'imageHandling.py' to wait for dicom files to be written by the scanner (uses 'watchFile' internally) and then reading the dicom file once it is available. INPUT: [1] dataInterface (allows a cloud script to access files from the control room computer) [2] filename (the dicom file we're watching for and want to load) [3] timeout (time spent waiting for a file before timing out) OUTPUT: [1] dicomData (with class 'pydicom.dataset.FileDataset') """ print(f'Processing TR {this_TR}') if verbose: print("• use 'readRetryDicomFromDataInterface' to read dicom file for", "TR %d, %s" %(this_TR, dicomFilename)) dicomData = readRetryDicomFromDataInterface(dataInterface, dicomFilename, timeout_file) else: # use Stream functions """ Use dataInterface.getImageData(streamId) to query a stream, waiting for a dicom file to be written by the scanner and then reading the dicom file once it is available. INPUT: [1] dataInterface (allows a cloud script to access files from the control room computer) [2] streamId - from initScannerStream() called above [3] TR number - the image volume number to retrieve [3] timeout (time spent waiting for a file before timing out) OUTPUT: [1] dicomData (with class 'pydicom.dataset.FileDataset') """ print(f'Processing TR {this_TR}') if verbose: print("• use dataInterface.getImageData() to read dicom file for" "TR %d, %s" %(this_TR, dicomFilename)) dicomData = dataInterface.getImageData(streamId, int(this_TR), timeout_file) if dicomData is None: print('Error: getImageData returned None') return dicomData.convert_pixel_data() if cfg.isSynthetic: niftiObject = convertDicomImgToNifti(dicomData) else: # use 'dicomreaders.mosaic_to_nii' to convert the dicom data into a nifti # object. additional steps need to be taken to get the nifti object in # the correct orientation, but we will ignore those steps here. refer to # the advanced sample project (amygActivation) for more info about that if verbose: print("| convert dicom data into a nifti object") niftiObject = dicomreaders.mosaic_to_nii(dicomData) # take the average of all the activation values avg_niftiData = np.mean(niftiObject.get_fdata()) # avg_niftiData = np.round(avg_niftiData,decimals=2) print("| average activation value for TR %d is %f" %(this_TR, avg_niftiData)) max_niftiData = np.amax(niftiObject.get_fdata()) if verbose: print("| max activation value for TR %d is %d" %(this_TR, max_niftiData)) """ INPUT: [1] projectComm (the communication pipe) [2] runNum (not to be confused with the scan number) [3] this_TR (timepoint of interest) [4] value (value you want to send over to the web browser) ** the inputs MUST be python integers; it won't work if it's a numpy int here, we are clearing an already existing plot """ # Now we will send the result to be used to provide feedback for the subject. # Using subjInterface.setResult() will send the classification result to a # remote subjectService that can use the value to update the display on # the presentation computer. if verbose: print("| send result to the presentation computer for provide subject feedback") subjInterface.setResult(runNum, int(this_TR), float(avg_niftiData)) # Finally we will use use webInterface.plotDataPoint() to send the result # to the web browser to be plotted in the --Data Plots-- tab. # Each run will have its own data plot, the x-axis will the the TR vol # number and the y-axis will be the classification value (float). # IMPORTANT ** the inputs MUST be python integers or python floats; # it won't work if it's a numpy int or numpy float ** if verbose: print("| send result to the web, plotted in the 'Data Plots' tab") webInterface.plotDataPoint(runNum, int(this_TR), float(avg_niftiData)) # save the activations value info into a vector that can be saved later all_avg_activations[this_TR] = avg_niftiData # create the full path filename of where we want to save the activation values vector. # we're going to save things as .txt and .mat files output_textFilename = '/tmp/cloud_directory/tmp/avg_activations.txt' output_matFilename = os.path.join('/tmp/cloud_directory/tmp/avg_activations.mat') # use 'putFile' from the dataInterface to save the .txt file # INPUT: # [1] filename (full path!) # [2] data (that you want to write into the file) if verbose: print("" "-----------------------------------------------------------------------------\n" "• save activation value as a text file to tmp folder") dataInterface.putFile(output_textFilename, str(all_avg_activations)) # use sio.save mat from scipy to save the matlab file if verbose: print("• save activation value as a matlab file to tmp folder") sio.savemat(output_matFilename, {'value':all_avg_activations}) if verbose: print("" "-----------------------------------------------------------------------------\n" "REAL-TIME EXPERIMENT COMPLETE!") return
# Check for new file every 1ms while not os.path.exists(nextFullFilename): time.sleep(0.001) # Maybe useful to have a timestamp print(time.time()) # Give a little time for the file to be fully transferred time.sleep(0.01) if verbose: print('File size is {0} bytes'.format( os.path.getsize(nextFullFilename))) # read in, convert to nifti, analyze, send file back dicomObject = dicom.read_file(nextFullFilename) niftiObject = dicomreaders.mosaic_to_nii(dicomObject) dat = niftiObject.get_data() print(dat.shape) currentImage = currentImage + 1 if verbose: print('Send') linuxSocket.send(feedbackData) except (KeyboardInterrupt): print('Close') linuxSocket.close()
def buildFunc(self, dicomFiles): """ Build a 4D functional image from list of dicom files Given a list of dicomFile paths, build a 4d functional image. For Siemens scanners, each dicom file is assumed to represent a mosaic image comprised of mulitple slices. This tool will split apart the mosaic images, and construct a 4D nifti object. The 4D nifti object contain a voxel array ordered like RAS+ as well the affine transformation to map between vox and mm space Parameters ---------- dicomFiles : list list containing the file names (file names ONLY, no path) of all dicom mosaic images to be used in constructing the final nifti image """ imageMatrix = None affine = None TR = None # make dicomFiles store the full path dicomFiles = [join(self.seriesDir, f) for f in dicomFiles] ### Loop over all dicom mosaic files nVols = len(dicomFiles) for mosaic_dcm_fname in dicomFiles: ### Parse the mosaic image into a 3D volume # we use the nibabel mosaic_to_nii() method which does a lot of the # heavy-lifting of extracting slices, arranging in a 3D array, and # grabbing the affine dcm = pydicom.dcmread(mosaic_dcm_fname) # create dicom object # for mosaic files, the instanceNumber tag will correspond to the # volume number (using a 1-based indexing, so subtract by 1) volIdx = dcm.InstanceNumber - 1 # convert the dicom object to nii thisVol = dicomreaders.mosaic_to_nii(dcm) # convert to RAS+ thisVol_RAS = nib.as_closest_canonical(thisVol) if TR is None: TR = dcm.RepetitionTime / 1000 # construct the imageMatrix if it hasn't been made yet if imageMatrix is None: imageMatrix = np.zeros(shape=(thisVol_RAS.shape[0], thisVol_RAS.shape[1], thisVol_RAS.shape[2], nVols), dtype=np.uint16) # construct the affine if it isn't made yet if affine is None: affine = thisVol_RAS.affine # Add this data to the image matrix imageMatrix[:, :, :, volIdx] = thisVol_RAS.get_fdata() ### Build a Nifti object funcImage = nib.Nifti1Image(imageMatrix, affine=affine) pixDims = np.array(funcImage.header.get_zooms()) pixDims[3] = TR funcImage.header.set_zooms(pixDims) return funcImage