Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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
Ejemplo n.º 4
0
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
Ejemplo n.º 5
0
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
Ejemplo n.º 6
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)
Ejemplo n.º 7
0
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
Ejemplo n.º 8
0
    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)
Ejemplo n.º 9
0
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
Ejemplo n.º 10
0
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
Ejemplo n.º 11
0
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
Ejemplo n.º 12
0
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
Ejemplo n.º 13
0
        # 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()
Ejemplo n.º 14
0
    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