Пример #1
0
    def explore_study(self):
        """
        Automatic filling of the advanced selections class attributes.
        It also checks if the given attributes are meaningful.
        :return:
        """

        if not os.path.isdir(self.pfo_study_bruker_input):
            raise IOError('Input folder does not exist.')
        if not os.path.isdir(self.pfo_study_nifti_output):
            raise IOError('Output folder does not exist.')
        if self.scans_list is None:
            self.scans_list = get_list_scans(self.pfo_study_bruker_input,
                                             print_structure=False)
            assert isinstance(self.scans_list, list)
            msg = 'No scans found, are you sure the input folder contains a Bruker study?'
            if not len(self.scans_list) > 0:
                raise IOError(msg)
        if self.study_name is None or self.study_name is '':
            _study_name = get_subject_name(
                self.pfo_study_bruker_input).replace(' ', '_')
            self.study_name = ''.join(e for e in _study_name if e.isalnum())
        if self.list_new_name_each_scan is None:
            list_new_name_each_scan = [
                self.study_name + '_' + ls for ls in self.scans_list
            ]
            self.list_new_name_each_scan = list_new_name_each_scan
            assert isinstance(self.list_new_name_each_scan, list)
        # if self.list_new_nifti_file_names is None:
        #     self.list_new_nifti_file_names = self.list_new_name_each_scan
        else:
            if not len(self.scans_list) == len(self.list_new_name_each_scan):
                msg = 'list_name_each_scan {0} does not have the same amount of scans in the ' \
                      'study: {1}'.format(self.list_new_name_each_scan, self.scans_list)
                raise IOError(msg)
Пример #2
0
    def show_study_structure(self):
        """
        Print to console the structure of the selected study.
        :return: [None] only print to console information.
        """
        if not os.path.isdir(self.pfo_study_bruker_input):
            raise IOError("Input folder does not exist.")

        print("Study folder structure: ")
        scans_list = get_list_scans(self.pfo_study_bruker_input)
        print("\n")
        print("List of scans: {}".format(scans_list))
        pfi_first_scan = os.path.join(self.pfo_study_bruker_input, scans_list[0])
        acqp = bruker_read_files("acqp", pfi_first_scan)
        print("Version: {}".format(acqp["ACQ_sw_version"][0]))
Пример #3
0
    def show_study_structure(self):
        """
        Print to console the structure of the selected study.
        :return: [None] only print to console information.
        """
        if not os.path.isdir(self.pfo_study_bruker_input):
            raise IOError('Input folder does not exist.')

        print('Study folder structure: ')
        scans_list = get_list_scans(self.pfo_study_bruker_input)
        print('\n')
        print('List of scans: {}'.format(scans_list))
        pfi_first_scan = os.path.join(self.pfo_study_bruker_input,
                                      scans_list[0])
        acqp = bruker_read_files('acqp', pfi_first_scan)
        print('Version: {}'.format(acqp['ACQ_sw_version'][0]))
Пример #4
0
def scan2struct(
    pfo_scan,
    correct_slope=True,
    correct_offset=True,
    sample_upside_down=False,
    nifti_version=1,
    qform_code=1,
    sform_code=2,
    get_acqp=False,
    get_method=False,
    get_reco=False,
    frame_body_as_frame_head=False,
    keep_same_det=True,
    consider_subject_position=False,
):
    """
    The core method of the converter has 2 parts.
    1) parsing the Bruker scan folder structure into an internal dictionary called struct.
    2) writing the information parsed in struct into folders.
    ----
    scan2struct is the first part of the bridge. Info required to fill nifti header are in the visu_pars file.
    The user may want to parse as well acqp, method (must when EpiDti) and reco parameter files.
    Data are parsed in the intermediate dictionary struct containing the final scan(s) converted in nibabel
    image, with additional infos.
    :param pfo_scan: path to folder containing the scan
    :param correct_slope: [True] if you want to correct the slope of the values.
    :param correct_offset: [True] if you want to correct the offset of the values.
    :param sample_upside_down: [False] if you want to have the sample rotated 180 around the Anterior-Posterior axis.
    :param nifti_version: [1] output nifti version can be version 1 or version 2 (see nibabel documentation)
    :param qform_code: [1] qform of the final nifti image
    :param sform_code: [2] sform of the final nifti image
    :param get_acqp: [False] if you want to parse the information in the acqp parameter file of the bruker raw data
    :param get_method: [False] if you want to parse the information in the method file. Forced to True when
    dealing with diffusion weighted images.
    :param get_reco: [False] if you want to parse the information in the reco parameter file.
    :param frame_body_as_frame_head: e.g. true if monkey, false if rat.
    :param keep_same_det: impose to have in the nifti affine matrix, the same determinat as in the bruker parameter.
    :param consider_subject_position : visu_pars SubjPosition can be 'Head_Prone' or 'Head_Supine'. While it may
    make sense in most cases to take this value into account, in some other it may not, as it is
    tuned to switch from radiological to neurological coordinate systems in a work-around.
    If the subject is Prone and the technician wants to have the coordinates
    in neurological he/she can consciously set the variable vc_subject_position to 'Head_Supine'.
    :return: output_data data structure containing the nibabel image(s) {nib_list, visu_pars_list, acqp, method, reco}
    """

    if not os.path.isdir(pfo_scan):
        raise IOError("Input folder does not exists.")

    # Get system endian_nes
    system_endian_nes = sys.byteorder

    # Get sub-scans series in the same experiment.
    list_sub_scans = get_list_scans(jph(pfo_scan, "pdata"))

    if not list_sub_scans:
        warn_msg = (
            "\nNo sub scan in the folder structure: \n{}. \nAre you sure the input folder contains a "
            "proper Bruker scan?\n".format(jph(pfo_scan, "pdata")))
        warnings.warn(warn_msg)
        return None

    nib_scans_list = []
    visu_pars_list = []

    for id_sub_scan in list_sub_scans:

        visu_pars = bruker_read_files("visu_pars",
                                      pfo_scan,
                                      sub_scan_num=id_sub_scan)

        if visu_pars == {}:
            warn_msg = (
                "\nNo 'visu_pars' data found here: \n{}. \nAre you sure the input folder contains a "
                "proper Bruker scan?\n".format(
                    jph(pfo_scan, "pdata", id_sub_scan)))
            warnings.warn(warn_msg)
            return None

        # In some cases we cannot deal with, VisuPars['VisuCoreSize'] can be a float. No conversion in this case.
        if not (isinstance(visu_pars["VisuCoreSize"], np.ndarray)
                or isinstance(visu_pars["VisuCoreSize"], list)):
            warn_msg = (
                "\nWarning, VisuCoreSize in VisuPars parameter file {} \n"
                "is not a list or a vector in. The study cannot be converted."
                " \n".format(jph(pfo_scan, "pdata", id_sub_scan)))
            warnings.warn(warn_msg)
            return None

        # Get data endian_nes - default big!!
        if visu_pars["VisuCoreByteOrder"] == "littleEndian":
            data_endian_ness = "little"
        elif visu_pars["VisuCoreByteOrder"] == "bigEndian":
            data_endian_ness = "big"
        else:
            data_endian_ness = "big"

        # Get datatype
        if visu_pars["VisuCoreWordType"] == "_32BIT_SGN_INT":
            dt = np.int32
        elif visu_pars["VisuCoreWordType"] == "_16BIT_SGN_INT":
            dt = np.int16
        elif visu_pars["VisuCoreWordType"] == "_8BIT_UNSGN_INT":
            dt = np.uint8
        elif visu_pars["VisuCoreWordType"] == "_32BIT_FLOAT":
            dt = np.float32
        else:
            raise IOError("Unknown data type for VisuPars VisuCoreWordType")

        # GET IMAGE VOLUME
        if os.path.exists(jph(pfo_scan, "pdata", id_sub_scan, "2dseq")):
            img_data_vol = np.copy(
                np.fromfile(jph(pfo_scan, "pdata", id_sub_scan, "2dseq"),
                            dtype=dt))
        else:
            warn_msg = (
                "\nNo '2dseq' data found here: \n{}. \nAre you sure the input folder contains a "
                "proper Bruker scan?\n".format(
                    jph(pfo_scan, "pdata", id_sub_scan)))
            warnings.warn(warn_msg)
            return None

        if not data_endian_ness == system_endian_nes:
            img_data_vol.byteswap(True)

        if "VisuAcqSequenceName" in visu_pars.keys():
            visu_pars_acq_sequence_name = visu_pars["VisuAcqSequenceName"]
        else:
            visu_pars_acq_sequence_name = ""

        is_dwi = "dtiepi" in visu_pars_acq_sequence_name.lower()

        if is_dwi:
            # Force to not correcting the slope, if true. Diffusion weighted images must be slope corrected before the
            # DTI analysis. They will be to heavy otherwise.
            correct_slope = False
            correct_offset = False
            # Force method to be parsed. Useful infos in this file to process the DWI.
            get_method = True

        # ------------------------------------------------------ #
        # ------ Generate the nifti image using visu_pars. ----- #
        # ------------------------------------------------------ #

        nib_im = nifti_getter(
            img_data_vol,
            visu_pars,
            correct_slope,
            correct_offset,
            sample_upside_down,
            nifti_version,
            qform_code,
            sform_code,
            frame_body_as_frame_head=frame_body_as_frame_head,
            keep_same_det=keep_same_det,
            consider_subject_position=consider_subject_position,
        )
        # ------------------------------------------------------ #
        # ------------------------------------------------------ #

        nib_scans_list.append(nib_im)
        visu_pars_list.append(visu_pars)

    # -- Get additional data

    # Get information from method, if it exists. Parse Method parameter and erase the dictionary if unwanted
    method = bruker_read_files("method", pfo_scan)

    if method == {}:
        print("Warning: No 'method' file to parse.")
    if "Method" in method.keys():
        acquisition_method = (method["Method"].replace("<", "").replace(
            ">", "").split(":")[-1])
    else:
        acquisition_method = ""

    if not get_method:
        method = {}

    # Get information from acqp, reco, if they exist.
    acqp = {}
    reco = {}

    if get_acqp:
        acqp = bruker_read_files("acqp", pfo_scan)
        if acqp == {}:
            print("Warning: No 'acqp' file to parse.")

    if get_reco:
        reco = bruker_read_files("reco", pfo_scan)
        if reco == {}:
            print("Warning: No 'method' file to parse.")

    # -- Return data structure
    struct_scan = {
        "nib_scans_list": nib_scans_list,
        "visu_pars_list": visu_pars_list,
        "acqp": acqp,
        "reco": reco,
        "method": method,
        "acquisition_method": acquisition_method,
    }

    return struct_scan