Beispiel #1
0
    def __init__(self, *args, **kwargs):
        """
        Constructor. 

        Can take same arguments as the fsl.data.image.Image constructor
        but also supports keyword ``param_names`` argument. This should
        be a list of parameter names which matches the size of the MVN
        """
        self.param_names = kwargs.get("param_names", None)
        Image.__init__(self, *args, **kwargs)
        self.nparams = self._get_num_params()
        if self.param_names is None:
            self.param_names = ["Parameter %i" % idx for idx in range(self.nparams)]
        elif len(self.param_names) != self.nparams:
            raise ValueError("MVN contains %i parameters, but %i parameter names were provided")
Beispiel #2
0
    def __init__(self, image, name=None, **kwargs):
        if image is None:
            raise ValueError(
                "No image data (filename, Nibabel object or Numpy array)")

        # This is sort-of a bug in nibabel or fslpy - it passes the kwargs to the
        # nibabel.load function which does not expect extra keyword arguments. So we will extract
        # those that fslpy/nibabel might expect and only pass these
        img_kwargs = ("header", "xform", "loadData", "calcRange", "indexed",
                      "threaded", "dataSource")
        img_args = dict([(k, v) for k, v in kwargs.items() if k in img_kwargs])

        if kwargs.pop("calib_first_vol", False):
            # First volume is an M0 calibration image - stip it off and initialize the image with the
            # remaining data
            temp_img = Image(image, name="temp", **img_args)
            img_args.pop("header", None)
            self.calib = Image(temp_img.data[..., 0],
                               name="calib",
                               header=temp_img.header,
                               **img_args)
            Image.__init__(self,
                           temp_img.data[..., 1:],
                           name=name,
                           header=temp_img.header,
                           **img_args)
        else:
            self.calib = None
            Image.__init__(self, image, name=name, **img_args)

        order = kwargs.pop("order", None)
        iaf = kwargs.pop("iaf", None)
        ibf = kwargs.pop("ibf", None)
        ntis = kwargs.pop("ntis", None)
        nplds = kwargs.pop("nplds", None)
        tis = kwargs.pop("tis", None)
        tes = kwargs.pop("tes", None)
        plds = kwargs.pop("plds", None)
        rpts = kwargs.pop("rpts", kwargs.pop("nrpts", None))
        phases = kwargs.pop("phases", None)
        nphases = kwargs.pop("nphases", None)
        nenc = kwargs.pop("nenc", kwargs.pop("ntc", None))

        if self.ndim not in (3, 4):
            raise ValueError("3D or 4D data expected")

        # Check for multi-TE data
        #
        # Note that for the moment we assume that multi-TEs are applied at every TI/PLD.
        # Variable repeats/taus are only per-TI. Default is one TE with value 0
        #
        # Sets the attributes tes, ntes
        if tes is not None:
            if isinstance(tes, six.string_types):
                tes = [float(te) for te in tes.split(",")]
            ntes = len(tes)
            self.setMeta("tes", tes)
            self.setMeta("ntes", len(tes))
        else:
            self.setMeta("tes", [
                0,
            ])
            self.setMeta("ntes", 1)

        # Determine the data ordering
        #
        # Sets the metadata: iaf (str), order (str)
        iaf, order, ibf_guessed = data_order(iaf, ibf, order, self.ntes > 1)
        self.setMeta("order", order)
        self.setMeta("iaf", iaf.lower())

        # Determine the number of labelling images present. This may be label/control
        # pairs, a set of multiple phases, vessel encoding cycles or already differenced data
        #
        # Sets the metadata: ntc (int), phases (list or None)
        if self.iaf in ("tc", "ct"):
            ntc = 2
        elif self.iaf == "mp":
            if phases is None and nphases is None:
                raise ValueError(
                    "Multiphase data specified but number of phases not given")
            elif phases is not None:
                if nphases is not None and nphases != len(phases):
                    raise ValueError(
                        "Number of phases is not consistent with length of phases list"
                    )
            else:
                phases = [pidx * 360 / nphases for pidx in range(nphases)]

            if isinstance(phases, six.string_types):
                phases = [float(ph) for ph in phases.split(",")]
            phases = phases
            ntc = len(phases)
        elif self.iaf in ("ve", "vediff"):
            self.setMeta("nenc", nenc)
            if nenc is None:
                raise ValueError(
                    "Vessel encoded data specified but number of encoding cycles not given"
                )
            elif self.iaf == "ve":
                ntc = nenc
            elif nenc % 2 == 0:
                ntc = int(nenc / 2)
            else:
                raise ValueError(
                    "Subtracted vessel encoded data must have had an even number of encoding cycles"
                )
        else:
            ntc = 1
        self.setMeta("ntc", ntc)
        self.setMeta("phases", phases)

        # Determine the number and type of delay images present. These may be given as TIs or PLDs.
        #
        # Internally we always have TIs, and only have PLDs as well if they were provided. If PLDs
        # were provided, the TIs are derived by adding on the bolus duration
        #
        # Sets the attributes tis (list), ntis (int), have_plds (bool), plds (list)
        if tis is not None and plds is not None:
            raise ValueError("Cannot specify PLDs and TIs at the same time")

        have_plds = False
        if nplds is not None:
            ntis = nplds
            have_plds = True

        if plds is not None:
            tis = plds
            have_plds = True

        if ntis is None and tis is None:
            raise ValueError("Number of TIs/PLDs not specified")
        elif tis is not None:
            if isinstance(tis, six.string_types):
                tis = [float(ti) for ti in tis.split(",")]
            ntis = len(tis)
            if ntis is not None and len(tis) != ntis:
                raise ValueError(
                    "Number of TIs/PLDs specified as: %i, but a list of %i TIs/PLDs was given"
                    % (ntis, len(tis)))

        self.setMeta("ntis", int(ntis))
        self.setMeta("have_plds", have_plds)
        if have_plds:
            self.setMeta("plds", tis)
        else:
            self.setMeta("tis", tis)

        if ibf_guessed and len(self.tis) > 1:
            warnings.warn(
                "Data order was not specified for multi-TI data - assuming blocks of repeats"
            )

        # Determine the number of repeats (fixed or variable)
        #
        # Sets the attribute rpts (list, one per TI/PLD)
        nvols_norpts = self.ntc * self.ntis * self.ntes
        if rpts is None:
            # Calculate fixed number of repeats
            if self.nvols % nvols_norpts != 0:
                raise ValueError(
                    "Data contains %i volumes, inconsistent with %i TIs and %i labelling images at %i TEs"
                    % (self.nvols, self.ntis, self.ntc, self.ntes))
            rpts = [int(self.nvols / nvols_norpts)] * self.ntis
        else:
            if isinstance(rpts, six.string_types):
                rpts = [int(rpt) for rpt in rpts.split(",")]
            elif isinstance(rpts, (int, np.integer)):
                rpts = [
                    rpts,
                ]

            if len(rpts) == 1:
                rpts *= self.ntis
            elif len(rpts) != self.ntis:
                raise ValueError(
                    "%i TIs specified, inconsistent with %i variable repeats" %
                    (self.ntis, len(rpts)))

            if sum(rpts) * self.ntc * self.ntes != self.nvols:
                raise ValueError(
                    "Data contains %i volumes, inconsistent with %i labelling images at %i TEs and total of %i repeats"
                    % (self.nvols, self.ntc, self.ntes, sum(rpts)))
        self.setMeta("rpts", rpts)

        # Bolus durations should be a sequence same length as TIs/PLDs
        #
        # Sets the attributes taus (list, one per TI/PLD)
        taus = kwargs.pop("bolus", kwargs.pop("taus", kwargs.pop("tau", None)))
        if taus is None:
            taus = 1.8
        if isinstance(taus, six.string_types):
            taus = [float(tau) for tau in taus.split(",")]
        elif isinstance(taus, (float, int, np.integer)):
            taus = [
                float(taus),
            ] * self.ntis

        if len(taus) == 1:
            taus = taus * ntis
        if len(taus) != self.ntis:
            raise ValueError(
                "%i bolus durations specified, inconsistent with %i TIs/PLDs" %
                (len(taus), self.ntis))
        self.setMeta("taus", taus)

        # Labelling type. CASL/pCASL normally associated with PLDs but can pass TIs instead.
        # However we would not expect PLDs for a non-CASL aquisition so this generates a warning
        #
        # If PLDs were provided, TIs are derived by adding the bolus duration to the PLDs
        #
        # Sets the attributes casl (bool), updates tis (list)
        casl = kwargs.pop("casl", None)
        if casl is None:
            casl = self.have_plds
        if self.have_plds:
            if not casl:
                warnings.warn(
                    "PLDs specified but aquisition was not CASL/pCASL - will treat these as TIs"
                )
        self.setMeta("casl", casl)

        # Other acquisition parameters
        self.setMeta("slicedt", kwargs.pop("slicedt", 0))
        self.setMeta("sliceband", kwargs.pop("sliceband", None))
        self.setMeta("artsuff", kwargs.pop("artsupp", False))