Beispiel #1
0
class ApplyTransform(QpWidget):
    """
    Widget for applying previously calculated transformations
    """
    def __init__(self, **kwargs):
        super(ApplyTransform, self).__init__(name="Apply Transform", icon="reg", 
                                             desc="Apply previously calculated transformations", 
                                             group="Registration", **kwargs)
        self.reg_methods = []
        for method in get_plugins("reg-methods"):
            try:
                self.reg_methods.append(method(self.ivm))
            except:
                traceback.print_exc()
                self.warn("Failed to create registration method: %s", method)

    def init_ui(self):
        layout = QtGui.QVBoxLayout()
        self.setLayout(layout)

        title = TitleWidget(self, help="reg")
        layout.addWidget(title)

        if not self.reg_methods:
            layout.addWidget(QtGui.QLabel("No registration methods found"))
            layout.addStretch(1)
            return

        self.options = OptionBox("General Options")
        self.options.add("Transform", TransformOption(self.ivm), key="transform")
        self.options.add("Apply to data", DataOption(self.ivm), key="data")
        self.options.add("Interpolation", ChoiceOption(["Nearest neighbour", "Linear", "Spline"], [0, 1, 3], default=1), key="interp-order")
        self.options.add("Output name", OutputNameOption(src_data=self.options.option("data"), suffix="_reg"), key="output-name")
        self.options.option("transform").sig_changed.connect(self._transform_changed)
        layout.addWidget(self.options)

        self.details = TransformDetails()
        layout.addWidget(self.details)

        layout.addWidget(RunButton(self))
        layout.addStretch(1)
        self._transform_changed()

    def processes(self):
        return {
            "ApplyTransform" : self.options.values(),
        }

    def activate(self):
        self._transform_changed()

    def _transform_changed(self):
        trans_name = self.options.option("transform").value
        transform = self.ivm.data.get(trans_name, None)
        if transform is None or "QpReg" not in transform.metadata:
            transform = self.ivm.extras.get(trans_name, None)

        if transform is not None and "QpReg" in transform.metadata:
            self.details.transform = transform
Beispiel #2
0
class SimMotionWidget(QpWidget):
    """
    Widget to simulate random motion on a 4D data set
    """
    def __init__(self, **kwargs):
        super(SimMotionWidget,
              self).__init__(name="Simulate motion",
                             icon="reg",
                             desc="Simulate random motion on a 4D data set",
                             group="Simulation",
                             **kwargs)

    def init_ui(self):
        vbox = QtGui.QVBoxLayout()
        self.setLayout(vbox)

        title = TitleWidget(self, title="Simulate Motion", help="sim_motion")
        vbox.addWidget(title)

        self.option_box = OptionBox("Options")
        data = self.option_box.add("Data set",
                                   DataOption(self.ivm,
                                              include_4d=True,
                                              include_3d=False),
                                   key="data")
        self.option_box.add("Motion standard deviation (mm)",
                            NumericOption(minval=0,
                                          maxval=5,
                                          default=1,
                                          decimals=2),
                            key="std")
        self.option_box.add("Padding (mm)",
                            NumericOption(minval=0,
                                          maxval=10,
                                          default=5,
                                          decimals=1),
                            key="padding",
                            checked=True)
        self.option_box.add("Output name",
                            OutputNameOption(src_data=data, suffix="_moving"),
                            key="output-name")
        vbox.addWidget(self.option_box)

        run_btn = QtGui.QPushButton('Run', self)
        run_btn.clicked.connect(self.run)
        vbox.addWidget(run_btn)

        vbox.addStretch(1)

    def batch_options(self):
        return "SimMotion", self.option_box.values()

    def run(self):
        options = self.batch_options()[1]
        process = SimMotionProcess(self.ivm)
        process.execute(options)
Beispiel #3
0
class VbOptions(OptionsWidget):
    def __init__(self, ivm, parent, acq_options):
        OptionsWidget.__init__(self, ivm, parent)
        self.acq_options = acq_options

        vbox = QtWidgets.QVBoxLayout()
        self.setLayout(vbox)

        cite = Citation(FAB_CITE_TITLE, FAB_CITE_AUTHOR, FAB_CITE_JOURNAL)
        vbox.addWidget(cite)

        self._optbox = OptionBox()
        self._optbox.add("<b>Model options</b>")
        self._optbox.add("Infer constant signal offset",
                         BoolOption(default=True),
                         key="infer-sig0")
        self._optbox.add("Infer delay",
                         BoolOption(default=True),
                         key="infer-delay")

        self._optbox.add("<b>Model fitting options</b>")
        self._optbox.add("Number of iterations",
                         NumericOption(minval=0,
                                       maxval=100,
                                       default=10,
                                       intonly=True),
                         key="max-iterations")
        #self._optbox.add("Spatial regularization", BoolOption(default=True), key="spatial")

        self._optbox.add("<b>Output options</b>")
        self._optbox.add("Output variance maps",
                         BoolOption(),
                         key="output-var")
        self._optbox.add("Output data name suffix",
                         TextOption(),
                         checked=True,
                         key="output-suffix")

        vbox.addWidget(self._optbox)
        vbox.addWidget(RunWidget(self, save_option=True))
        vbox.addStretch(1)

    def processes(self):
        opts = {}
        opts.update(self.acq_options.options())
        opts.update(self._optbox.values())

        self.debug("CvrPetCo2Vb options: %s", opts)
        processes = [
            {
                "CvrPetCo2Vb": opts
            },
        ]

        return processes
Beispiel #4
0
class AslDataModelView:
    def __init__(self, ivm):
        self.model = AslDataModel(ivm)
        self.gui = OptionBox()
        self.gui.add("Bolus duration",
                     NumericOption(minval=0, maxval=5, default=1.8),
                     key="tau")
        self.gui.add("Labelling",
                     ChoiceOption(["CASL/pCASL", "PASL"], [True, False],
                                  default=True),
                     key="casl")
        self.gui.add("PLDs",
                     NumberListOption([0.25, 0.5, 0.75, 1.0, 1.25, 1.5]),
                     key="plds")
        self.gui.add("Time per slice (ms)",
                     NumericOption(minval=0,
                                   maxval=1000,
                                   default=0,
                                   intonly=True),
                     key="slicedt")
        self.gui.add("Data format",
                     ChoiceOption(["Differenced data", "Label/Control pairs"],
                                  ["diff", "tc"]),
                     key="iaf")
        self.gui.add("Repeats",
                     NumericOption(minval=1,
                                   maxval=100,
                                   default=1,
                                   intonly=True),
                     key="repeats")
        self.gui.add("Group by",
                     ChoiceOption(["PLDs", "Repeats"], ["tis", "rpt"]),
                     key="ibf")
        self.gui.add("Inversion efficiency",
                     NumericOption(minval=0.5, maxval=1.0, default=0.85),
                     key="alpha")
        self.gui.add("M0",
                     NumericOption(minval=0, maxval=2000, default=1000),
                     key="m0")
        self.gui.add("TR (s)",
                     NumericOption(minval=0, maxval=10, default=4),
                     key="tr")
        self.gui.add("TE (ms)",
                     NumericOption(minval=0, maxval=1000, default=13),
                     key="te")
        self.gui.add("Tissue/arterial partition coefficient",
                     NumericOption(minval=0, maxval=1, default=0.9),
                     key="pct")
        #self.gui.add("Arterial component", BoolOption(), key="incart")
        self.gui.sig_changed.connect(self._update_options)
        self._update_options()

    def _update_options(self):
        self.model.options.update(self.gui.values())
class CheckerboardModelView:
    """
    View for CheckerboardModel
    """
    def __init__(self, ivm):
        self.model = CheckerboardModel(ivm)
        self.gui = OptionBox()
        self.gui.add("Number of voxels per patch (approx)", NumericOption(minval=1, maxval=1000, default=20, intonly=True), key="voxels-per-patch")
        self.gui.sig_changed.connect(self._update_options)
        
    def _update_options(self):
        self.model.options.update(self.gui.values())
class FastStructureModelView:
    """
    View for FastStructureModel
    """
    def __init__(self, ivm):
        self.model = FastStructureModel(ivm)
        self.gui = OptionBox()
        self.gui.add("Structural image (brain extracted)", DataOption(self.model._ivm, explicit=True), key="struc")
        self.gui.add("Image type", ChoiceOption(["T1 weighted", "T2 weighted", "Proton Density"], return_values=[1, 2, 3]), key="type")
        self.gui.sig_changed.connect(self._update_options)
        
    def _update_options(self):
        self.model.options.update(self.gui.values())
Beispiel #7
0
class DscDataModelView:
    def __init__(self, ivm):
        self.model = DscDataModel(ivm)
        self.gui = OptionBox()
        self.gui.add("Time between volumes (s)",
                     NumericOption(minval=0, maxval=5, default=1.0),
                     key="delt")
        self.gui.add("TE (s)",
                     NumericOption(minval=0, maxval=5, default=1.0),
                     key="te")
        self.gui.add("AIF", NumberListOption(), key="aif")
        self.gui.sig_changed.connect(self._update_options)
        self._update_options()

    def _update_options(self):
        self.model.options.update(self.gui.values())
Beispiel #8
0
class SpinEchoDataModelView:
    def __init__(self, ivm):
        self.model = SpinEchoDataModel(ivm)
        self.gui = OptionBox()
        self.gui.add("TR (s)",
                     NumericOption(minval=0, maxval=10, default=4.8),
                     key="tr")
        self.gui.add("TE (ms)",
                     NumericOption(minval=0, maxval=1000, default=0),
                     key="te")
        self.gui.add("M0",
                     NumericOption(minval=0, maxval=10000, default=1000),
                     key="m0")
        self.gui.sig_changed.connect(self._update_options)
        self._update_options()

    def _update_options(self):
        self.model.options.update(self.gui.values())
Beispiel #9
0
class FslWidget(QpWidget):
    """
    Widget providing interface to FSL program
    """
    def __init__(self, **kwargs):
        QpWidget.__init__(self, icon="fsl.png", group="FSL", **kwargs)
        self.prog = kwargs["prog"]

    def init_ui(self, run_box=True):
        self.vbox = QtGui.QVBoxLayout()
        self.setLayout(self.vbox)

        title = TitleWidget(self,
                            help="fsl",
                            subtitle="%s %s" % (self.description, __version__))
        self.vbox.addWidget(title)

        cite = Citation(*CITATIONS.get(self.prog, CITATIONS["fsl"]))
        self.vbox.addWidget(cite)

        self.options = OptionBox("%s options" % self.prog.upper())
        self.vbox.addWidget(self.options)

        if run_box:
            self.run_box = RunBox(self.get_process, self.get_options)
            self.vbox.addWidget(self.run_box)

        self.vbox.addStretch(1)

        fsldir = FslDirWidget()
        self.vbox.addWidget(fsldir)
        fsldir.sig_changed.connect(self._fsldir_changed)
        self._fsldir_changed(fsldir.fsldir)

    def _fsldir_changed(self, fsldir):
        self.options.setVisible(bool(fsldir))
        if hasattr(self, "run_box"):
            self.run_box.setVisible(bool(fsldir))

    def batch_options(self):
        return self.get_process().PROCESS_NAME, self.get_options()

    def get_options(self):
        return self.options.values()
Beispiel #10
0
class AddNoiseWidget(QpWidget):
    """
    Add noise to data
    """
    def __init__(self, **kwargs):
        super(AddNoiseWidget,
              self).__init__(name="Add noise",
                             icon="noise",
                             desc="Add random noise to a data set",
                             group="Simulation",
                             **kwargs)

    def init_ui(self):
        vbox = QtGui.QVBoxLayout()
        self.setLayout(vbox)

        title = TitleWidget(self, title="Add Noise", help="noise")
        vbox.addWidget(title)

        self.option_box = OptionBox("Options")
        data = self.option_box.add("Data set",
                                   DataOption(self.ivm),
                                   key="data")
        self.option_box.add("Gaussian standard deviation",
                            NumericOption(minval=0, maxval=100, default=50),
                            key="std")
        self.option_box.add("Output name",
                            OutputNameOption(src_data=data, suffix="_noisy"),
                            key="output-name")
        vbox.addWidget(self.option_box)

        run_btn = QtGui.QPushButton('Run', self)
        run_btn.clicked.connect(self.run)
        vbox.addWidget(run_btn)

        vbox.addStretch(1)

    def batch_options(self):
        return "AddNoise", self.option_box.values()

    def run(self):
        options = self.batch_options()[1]
        process = AddNoiseProcess(self.ivm)
        process.execute(options)
Beispiel #11
0
class GlmOptions(OptionsWidget):
    def __init__(self, ivm, parent, acq_options):
        OptionsWidget.__init__(self, ivm, parent)
        self.acq_options = acq_options

        vbox = QtWidgets.QVBoxLayout()
        self.setLayout(vbox)

        self._optbox = OptionBox()
        self._optbox.add("<b>Model options</b>")
        self._optbox.add("Delay minimum (s)",
                         NumericOption(minval=-100, maxval=100, default=0),
                         key="delay-min")
        self._optbox.add("Delay maximum (s)",
                         NumericOption(minval=-100, maxval=100, default=0),
                         key="delay-max")
        self._optbox.add("Delay step (s)",
                         NumericOption(minval=-5, maxval=5, default=1),
                         key="delay-step")

        self._optbox.add("<b>Output options</b>")
        self._optbox.add("Output data name suffix",
                         TextOption(),
                         checked=True,
                         key="output-suffix")

        vbox.addWidget(self._optbox)
        vbox.addWidget(RunWidget(self, save_option=True))
        vbox.addStretch(1)

    def processes(self):
        opts = {}
        opts.update(self.acq_options.options())
        opts.update(self._optbox.values())
        self.debug("CvrPetCo2Glm options: %s", opts)
        processes = [
            {
                "CvrPetCo2Glm": opts
            },
        ]

        return processes
Beispiel #12
0
class AifWidget(QtGui.QWidget):
    """
    Widget allowing choice of AIF
    """
    def __init__(self, ivm):
        QtGui.QWidget.__init__(self)
        self.ivm = ivm

        vbox = QtGui.QVBoxLayout()
        self.setLayout(vbox)

        self.optbox = OptionBox()
        self.optbox.add(
            "AIF source",
            ChoiceOption(["Global sequence of values", "Voxelwise image"],
                         ["global", "voxelwise"]),
            key="aif_source")
        self.optbox.option("aif_source").sig_changed.connect(
            self._aif_source_changed)
        self.optbox.add("AIF", NumberListOption(), key="aif")
        self.optbox.add("AIF image", DataOption(self.ivm), key="suppdata")
        self.optbox.add("AIF type",
                        ChoiceOption(["DSC signal", "Concentration"],
                                     [False, True]),
                        key="aifconc")
        vbox.addWidget(self.optbox)
        vbox.addStretch()
        self._aif_source_changed()

    def options(self):
        """ :return: Dictionary of options selected for the AIF"""
        opts = self.optbox.values()
        opts.pop("aif_source")
        return opts

    def _aif_source_changed(self):
        global_aif = self.optbox.option("aif_source").value == "global"
        self.optbox.set_visible("aif", global_aif)
        self.optbox.set_visible("suppdata", not global_aif)
Beispiel #13
0
class SequenceOptions(QtGui.QWidget):
    """
    Widget containing options for the CEST sequence
    """

    sig_b0_changed = QtCore.Signal(float)

    def __init__(self, ivm=None):
        QtGui.QWidget.__init__(self)
        self._ivm = ivm

        vbox = QtGui.QVBoxLayout()
        self.setLayout(vbox)

        self.optbox = OptionBox()
        vbox.addWidget(self.optbox)

        self.optbox.add("CEST data", DataOption(self._ivm), key="data")
        self.optbox.add("ROI",
                        DataOption(self._ivm, rois=True, data=False),
                        key="mask")
        self.optbox.add("Frequency offsets", NumberListOption(), key="freqs")
        self.optbox.add("B0", ChoiceOption(B0_DEFAULTS), key="b0")
        self.optbox.add("Custom B0 (T)",
                        NumericOption(minval=0.0,
                                      maxval=15,
                                      default=3.0,
                                      decimals=3),
                        key="b0_custom")
        # FIXME multiple B1 values
        self.optbox.add("B1 (\u03bcT)",
                        NumericOption(minval=0.0,
                                      maxval=2,
                                      default=0.55,
                                      decimals=6),
                        key="b1")
        self.optbox.add(
            "Saturation",
            ChoiceOption(["Continuous Saturation", "Pulsed Saturation"],
                         ["continuous", "pulsed"]),
            key="sat")
        self.optbox.add("Saturation time (s)",
                        NumericOption(minval=0.0,
                                      maxval=5,
                                      default=2,
                                      decimals=2),
                        key="sat_time")
        self.optbox.add("Pulse Magnitudes",
                        NumberListOption(),
                        key="pulse_mag")
        self.optbox.add("Pulse Durations (s)",
                        NumberListOption(),
                        key="pulse_dur")
        self.optbox.add("Pulse Repeats",
                        NumberListOption(),
                        key="pulse_repeats")

        self.optbox.option("b0").sig_changed.connect(self._b0_changed)
        self.optbox.option("b0_custom").sig_changed.connect(self._b0_changed)
        self.optbox.option("sat").sig_changed.connect(self._sat_changed)

        self.warn_box = WarningBox()
        vbox.addWidget(self.warn_box)

        # B1 field
        #hbox = QtGui.QHBoxLayout()
        #self.unsat_cb = QtGui.QCheckBox("Unsaturated")
        #self.unsat_cb.stateChanged.connect(self.update_ui)
        #hbox.addWidget(self.unsat_cb)
        #self.unsat_combo = QtGui.QComboBox()
        #self.unsat_combo.addItem("first")
        #self.unsat_combo.addItem("last")
        #self.unsat_combo.addItem("first and last  ")
        #hbox.addWidget(self.unsat_combo)
        #hbox.addStretch(1)
        #grid.addLayout(hbox, 2, 2)

        vbox.addStretch(1)
        self._sat_changed()
        self._b0_changed()

    def _sat_changed(self):
        pulsed = self.optbox.option("sat").value == "pulsed"
        self.optbox.set_visible("pulse_mag", pulsed)
        self.optbox.set_visible("pulse_dur", pulsed)
        self.optbox.set_visible("pulse_repeats", pulsed)

    def _b0_changed(self):
        b0_sel = self.optbox.option("b0").value
        if b0_sel == "Custom":
            self.optbox.set_visible("b0_custom", True)
            b0 = self.optbox.option("b0_custom").value
        else:
            self.optbox.set_visible("b0_custom", False)
            b0 = float(b0_sel[:-1])
        self.sig_b0_changed.emit(b0)

    def _get_dataspec(self, options):
        dataspec = []
        freqs = options.pop("freqs")
        b1 = options.pop("b1") / 1e6
        if options["sat"] == "pulsed":
            repeats = options.pop("pulse_repeats")
        else:
            repeats = 1
        for idx, freq in enumerate(freqs):
            #if self.unsat_cb.isChecked():
            #    self.debug("Unsat", idx, self.unsat_combo.currentIndex())
            #    if idx == 0 and self.unsat_combo.currentIndex() in (0, 2):
            #        b1 = 0
            #    elif idx == len(freqs)-1 and self.unsat_combo.currentIndex() in (1, 2):
            #        b1 = 0
            dataspec.append([freq, b1, repeats])
        #self.debug(dataspec)
        return dataspec

    def _get_ptrain(self, options):
        ptrain = []
        if options.pop("sat") == "pulsed":
            pms = options.pop("pulse_mag")
            pds = options.pop("pulse_dur")
            if len(pms) != len(pds):
                raise QpException(
                    "Pulse magnitude and duration must contain the same number of values"
                )
            for pm, pd in zip(pms, pds):
                ptrain.append([pm, pd])
        else:
            ptrain.append([1, options.pop("sat_time")])
        #self.debug(ptrain)
        return ptrain

    def options(self):
        options = self.optbox.values()
        options["spec"] = self._get_dataspec(options)
        options["ptrain"] = self._get_ptrain(options)
        options.pop("b0")
        options.pop("b0_custom", None)
        return options
Beispiel #14
0
class FabberWidget(QpWidget):
    """
    Widget for running Fabber model fitting
    """
    def __init__(self, **kwargs):
        QpWidget.__init__(self, **kwargs)
        self._fabber_options = {
            "degree" : 2,
            "noise" : "white",
            "save-mean" : True,
            "save-model-fit" : True,
            "save-model-extras" : True,
        }
        self._fabber_params = []

    def init_ui(self):
        self.vbox = QtGui.QVBoxLayout()
        self.setLayout(self.vbox)

        title = TitleWidget(self, subtitle="Plugin %s" % __version__, help="fabber")
        self.vbox.addWidget(title)
        
        cite = Citation(FAB_CITE_TITLE, FAB_CITE_AUTHOR, FAB_CITE_JOURNAL)
        self.vbox.addWidget(cite)

        self.options = OptionBox("Options")
        self.options.sig_changed.connect(self._options_changed)
        self.vbox.addWidget(self.options)

        self.warn_box = WarningBox("")
        self.warn_box.setVisible(False)
        self.vbox.addWidget(self.warn_box)

    def _model_group_changed(self):
        models = FabberProcess.api().get_models(model_group=self._fabber_options.get("model-group", None))
        self.debug("Models: %s", models)
        self.options.option("model").setChoices(models)
      
    def _options_changed(self):
        self._fabber_options.update(self.options.values())
        if self._fabber_options.get("model-group", None) == "ALL":
           self._fabber_options["model-group"] = None

        self.debug("Options changed:\n%s", self._fabber_options)
        self._update_params()

    def _fix_data_params(self, api):
        """
        Given a set of Fabber options, replace those that should be data items with a Numpy array
        """
        options = dict(self._fabber_options)
        known_options = api.get_options(generic=True, model=options.get("model", None), method=options.get("method", None))[0]
        for key in options:
            if api.is_data_option(key, known_options):
                # Just provide a placeholder
                options[key] = np.zeros((1, 1, 1))
        return options

    def _update_params(self):
        from fabber import FabberException
        try:
            api = FabberProcess.api()
            options = self._fix_data_params(api)
            self._fabber_params = api.get_model_params(options)
            self.warn_box.setVisible(False)
        except FabberException as exc:
            self._fabber_params = []
            self.warn_box.text.setText("Invalid model options:\n\n%s" % str(exc))
            self.warn_box.setVisible(True)

    def _show_model_options(self):
        model = self._fabber_options["model"]
        dlg = OptionsDialog(self, ivm=self.ivm, rundata=self._fabber_options, desc_first=True)
        opts, desc = FabberProcess.api().get_options(model=model)
        self.debug("Model options: %s", opts)
        dlg.set_title("Forward Model: %s" % model, desc)
        dlg.set_options(opts)
        dlg.exec_()
        self._update_params()

    def _show_method_options(self):
        method = self._fabber_options["method"]
        dlg = OptionsDialog(self, ivm=self.ivm, rundata=self._fabber_options, desc_first=True)
        opts, desc = FabberProcess.api().get_options(method=method)
        # Ignore prior options which have their own dialog
        opts = [o for o in opts if "PSP_byname" not in o["name"] and o["name"] != "param-spatial-priors"]
        dlg.set_title("Inference method: %s" % method, desc)
        self.debug("Method options: %s", opts)
        dlg.set_options(opts)
        dlg.fit_width()
        dlg.exec_()
        
    def _show_general_options(self):
        dlg = OptionsDialog(self, ivm=self.ivm, rundata=self._fabber_options, desc_first=True)
        dlg.ignore("model", "method", "output", "data", "mask", "data<n>", "overwrite", "help",
                   "listmodels", "listmethods", "link-to-latest", "data-order", "dump-param-names",
                   "loadmodels")
        opts, _ = FabberProcess.api().get_options()
        dlg.set_options(opts)
        dlg.fit_width()
        dlg.exec_()
        
    def _show_prior_options(self):
        dlg = PriorsDialog(self, ivm=self.ivm, rundata=self._fabber_options)
        try:
            api = FabberProcess.api()
            options = self._fix_data_params(api)
            params = api.get_model_params(options)
        except Exception as exc:
            raise QpException("Unable to get list of model parameters\n\n%s\n\nModel options must be set before parameters can be listed" % str(exc))
        dlg.set_params(params)
        dlg.fit_width()
        dlg.exec_()

    def get_options(self):
        """ Return a copy of current Fabber options """
        return dict(self._fabber_options)

    def get_process(self):
        return FabberProcess(self.ivm)

    def batch_options(self):
        return "Fabber", self.get_options()
Beispiel #15
0
class SimData(FabberWidget):
    """
    Widget which uses Fabber models to generate simulated data
    """
    def __init__(self, **kwargs):
        super(SimData, self).__init__(name="Simulated Fabber Data", icon="fabber", 
                                      desc="Generate test data sets from Fabber models", 
                                      group="Simulation", **kwargs)
        self._param_test_values = {}

    def init_ui(self):
        FabberWidget.init_ui(self)
        
        self.param_values_box = OptionBox("Parameter values")
        self.param_values_box.sig_changed.connect(self._param_values_changed)
        self.vbox.addWidget(self.param_values_box)

        run_btn = QtGui.QPushButton('Generate test data', self)
        run_btn.clicked.connect(self._run)
        self.vbox.addWidget(run_btn)
        
        self.vbox.addStretch(1)

        model_opts_btn = QtGui.QPushButton('Model Options')
        model_opts_btn.clicked.connect(self._show_model_options)

        self.options.add("Model group", ChoiceOption(), key="model-group")
        self.options.add("Model", ChoiceOption(), model_opts_btn, key="model")
        self.options.add("Number of volumes (time points)", NumericOption(intonly=True, minval=1, maxval=100, default=10), key="num-vols")
        self.options.add("Voxels per patch (approx)", NumericOption(intonly=True, minval=1, maxval=10000, default=1000), key="num-voxels")
        self.options.add("Noise (Gaussian std.dev)", NumericOption(intonly=True, minval=0, maxval=1000, default=0), key="noise")
        self.options.add("Output data name", OutputNameOption(initial="fabber_test_data"), key="output-name")
        self.options.add("Output noise-free data", BoolOption(), key="save-clean")
        self.options.add("Output parameter ROIs", BoolOption(), key="save-rois")
        self.options.option("model-group").sig_changed.connect(self._model_group_changed)

        model_groups = ["ALL"]
        for group in FabberProcess.api().get_model_groups():
            model_groups.append(group.upper())
        self.options.option("model-group").setChoices(model_groups)
        self.options.option("model-group").value = "ALL"
        self._model_group_changed()

        self.options.option("model").value = "poly"
        self._options_changed()

        # Start with something sensible for the polynomial model
        self._param_test_values = {"c0" : [-100, 0, 100], "c1" : [-10, 0, 10], "c2" : [-1, 0, 1]}
        self._update_params()
 
    def _update_params(self):
        FabberWidget._update_params(self)
        self.param_values_box.clear()
        for param in self._fabber_params:
            current_values = self._param_test_values.get(param, [1.0])
            self.param_values_box.add(param, NumberListOption(initial=current_values))
            self._param_test_values[param] = current_values

        # Remove references to parameters which no longer exist
        for param in list(self._param_test_values.keys()):
            if param not in self._fabber_params:
                del self._param_test_values[param]

    def _param_values_changed(self):
        self._param_test_values = self.param_values_box.values()
        num_variable = len([1 for v in self._param_test_values.values() if len(v) > 1])
        if num_variable > 3:
            self.warn("Cannot have more than 3 varying parameters")
        
    def get_options(self):
        """ Return a copy of current Fabber options and parameter test values """
        options = dict(self._fabber_options)
        options["param-test-values"] = self._param_test_values
        return options
        
    def _run(self):
        process = self.get_process()
        options = self.get_options()
        process.run(options)
        
    def get_process(self):
        return FabberTestDataProcess(self.ivm)
  
    def batch_options(self):
        return "FabberTestData", self.get_options()
Beispiel #16
0
class PcaWidget(QpWidget):
    """
    PCA widget
    """
    def __init__(self, **kwargs):
        super(PcaWidget, self).__init__(name="PCA",
                                        icon="pca",
                                        desc="PCA reduction",
                                        group="Processing",
                                        **kwargs)

    def init_ui(self):
        vbox = QtGui.QVBoxLayout()
        self.setLayout(vbox)

        title = TitleWidget(
            self,
            title="PCA reduction",
            subtitle="Principal Component Analysis for 4D data")
        vbox.addWidget(title)

        self._options = OptionBox("Options")
        self._options.add("Data",
                          DataOption(self.ivm, include_3d=False),
                          key="data")
        self._options.add("ROI",
                          DataOption(self.ivm, data=False, rois=True),
                          key="roi")
        self._options.add("Number of components",
                          NumericOption(minval=1, intonly=True, default=4),
                          key="n-components")
        self._options.add("Output name",
                          OutputNameOption(
                              src_data=self._options.option("data"),
                              suffix="_pca"),
                          key="output-name")
        self._options.option("data").sig_changed.connect(self._data_changed)
        vbox.addWidget(self._options)

        self._run = RunWidget(self)
        self._run.sig_postrun.connect(self._postrun)
        vbox.addWidget(self._run)

        self.plot = Plot(qpo=None, parent=self, title="PCA modes")

        self.variance_model = QtGui.QStandardItemModel()
        variance_table = QtGui.QTableView()
        variance_table.verticalHeader().hide()
        variance_table.setModel(self.variance_model)

        tabs = QtGui.QTabWidget()
        tabs.addTab(self.plot, "PCA modes")
        tabs.addTab(variance_table, "Explained variance")
        tabs.setCurrentWidget(self.plot)
        vbox.addWidget(tabs)

        vbox.addStretch(1)
        self._data_changed()

    def processes(self):
        return {"PCA": self._options.values()}

    def _data_changed(self):
        self._run.setEnabled(
            self._options.option("data").value in self.ivm.data)

    def _postrun(self):
        self._update_plot()
        self._update_table()

    def _update_plot(self):
        self.plot.clear()
        extra = self.ivm.extras.get(
            self._options.option("output-name").value + "_modes", None)
        if extra is not None:
            arr = np.array(extra.arr)
            for idx in range(arr.shape[1] - 1):
                self.plot.add_line(arr[:, idx], name="Mode %i" % idx)
            self.plot.add_line(arr[:, -1],
                               name="Mean",
                               line_col=(255, 0, 0),
                               line_width=3.0)

    def _update_table(self):
        self.variance_model.clear()
        extra = self.ivm.extras.get(
            self._options.option("output-name").value + "_variance", None)
        if extra is not None:
            self.debug(str(extra))
            for idx, header in enumerate(extra.col_headers):
                self.variance_model.setHorizontalHeaderItem(
                    idx, QtGui.QStandardItem(header))

            for idx, variance in enumerate(extra.arr):
                self.variance_model.setItem(
                    idx, 0, QtGui.QStandardItem(str(variance[0])))
                self.variance_model.setItem(
                    idx, 1, QtGui.QStandardItem(sf(variance[1])))
                self.variance_model.setItem(
                    idx, 2, QtGui.QStandardItem(sf(variance[2])))
Beispiel #17
0
class OrientDataWidget(QpWidget):
    """
    Widget that lets you tweak the orientation of data
    """
    def __init__(self, **kwargs):
        super(OrientDataWidget,
              self).__init__(name="Orient Data",
                             icon="inspect.png",
                             desc="Manipulate data orientation",
                             group="Utilities",
                             **kwargs)
        self._transform_cache = {}
        self.ivm.sig_all_data.connect(self._all_data_changed)

    def init_ui(self):
        vbox = QtGui.QVBoxLayout()
        self.setLayout(vbox)

        title = TitleWidget(self)
        vbox.addWidget(title)

        hbox = QtGui.QHBoxLayout()
        self.options = OptionBox("Re-orient data")
        data = self.options.add("Data item", DataOption(self.ivm), key="data")
        data.sig_changed.connect(self._data_changed)
        self.trans, self.rot = {}, {}
        self.options.add("Translation")
        for axis, label in {2: "axial", 0: "sagittal", 1: "coronal"}.items():
            trans = self.options.add("  %s (mm)" % label.title(),
                                     NumericOption(minval=-100,
                                                   maxval=100,
                                                   default=0),
                                     key="trans-%s" % label)
            trans.sig_changed.connect(self._translate(axis, label))
            self.trans[axis] = trans
        self.options.add("Rotation")
        for axis, label in {2: "axial", 0: "sagittal", 1: "coronal"}.items():
            rot = self.options.add("  %s (degrees)" % label.title(),
                                   NumericOption(minval=-180,
                                                 maxval=180,
                                                 default=0),
                                   key="rot-%s" % label)
            rot.sig_changed.connect(self._rotate(axis, label))
            self.rot[axis] = rot
        hbox.addWidget(self.options)
        vbox.addLayout(hbox)

        self.gridview = GridView(self.ivm, self.ivl)
        vbox.addWidget(self.gridview)

        hbox = QtGui.QHBoxLayout()
        reset_btn = QtGui.QPushButton("Reset to original")
        reset_btn.clicked.connect(self._reset)
        hbox.addWidget(reset_btn)
        hbox.addStretch(1)
        vbox.addLayout(hbox)

        vbox.addStretch(1)

    def activate(self):
        self._data_changed()

    def _all_data_changed(self, data):
        for name in list(self._transform_cache.keys()):
            if name not in data:
                del self._transform_cache[name]

    def _data_changed(self):
        name = self.options.values()["data"]
        qpdata = self.ivm.data.get(name, self.ivm.rois.get(name, None))
        self.gridview.set_data(qpdata)
        if qpdata is not None:
            if name not in self._transform_cache:
                self._transform_cache[name] = ([0, 0, 0], [0, 0, 0])
            translation, rotations = self._transform_cache[name]
            for axis in range(3):
                self.trans[axis].value = translation[axis]
                self.rot[axis].value = rotations[axis]
            self._set()

    def _translate(self, axis, label):
        def _trans():
            name = self.gridview.data.name
            trans = self.options.values()["trans-%s" % label]
            self._transform_cache[name][0][axis] = trans
            self._set()

        return _trans

    def _rotate(self, axis, label):
        def _rot():
            name = self.gridview.data.name
            angle = self.options.values()["rot-%s" % label]
            if axis == 1: angle = -angle
            self._transform_cache[name][1][axis] = angle
            self._set()

        return _rot

    def _reset(self):
        name = self.gridview.data.name
        del self._transform_cache[name]
        self._data_changed()

    def _set(self):
        name = self.gridview.data.name
        affine = self.gridview.data.grid.affine_orig
        grid_centre = [float(dim) / 2 for dim in self.gridview.data.grid.shape]
        world_centre = np.dot(affine[:3, :3], grid_centre)
        self.debug("Initial affine\n%s", affine)
        translation, rotations = self._transform_cache[name]

        R = np.identity(3)
        for axis in range(3):
            angle = rotations[axis]
            rot3d = self._rotmtx_3d(axis, angle)
            affine[:3, :3] = np.dot(rot3d, affine[:3, :3])
            R = np.dot(rot3d, R)

        origin_offset = world_centre - np.dot(R, world_centre)
        origin_offset += translation
        self.debug("Origin offset\n%s", origin_offset)
        affine[:3, 3] += origin_offset

        self.debug("Final affine\n%s", affine)
        self.gridview.data.grid.affine = affine
        self.gridview.update()
        if self.gridview.data == self.ivm.main:
            self.ivm.sig_main_data.emit(self.ivm.main)
        if self.gridview.data.view.visible == Visibility.SHOW or self.gridview.data == self.ivm.main:
            self.ivl.redraw()

    def _rotmtx_3d(self, axis, angle):
        # FIXME this is not quite right when rotating in a plane where
        # the basis vectors have different lengths
        c, s = math.cos(math.radians(angle)), math.sin(math.radians(angle))
        rot2d = np.array([[c, -s], [s, c]])
        rot3d = np.identity(3)
        if axis == 0:
            rot3d[1:, 1:] = rot2d
        elif axis == 1:
            rot3d[0, 0] = rot2d[0, 0]
            rot3d[0, 2] = rot2d[0, 1]
            rot3d[2, 0] = rot2d[1, 0]
            rot3d[2, 2] = rot2d[1, 1]
        elif axis == 2:
            rot3d[:2, :2] = rot2d
        self.debug("3d rotation matrix: %i %f", axis, angle)
        self.debug("\n%s", rot3d)
        return rot3d
Beispiel #18
0
class RegWidget(QpWidget):
    """
    Generic registration / motion correction widget 
    """
    def __init__(self, **kwargs):
        super(RegWidget,
              self).__init__(name="Registration",
                             icon="reg",
                             desc="Registration and Motion Correction",
                             group="Registration",
                             **kwargs)
        self.reg_methods = []
        for method in get_plugins("reg-methods"):
            try:
                self.reg_methods.append(method(self.ivm))
            except:
                traceback.print_exc()
                self.warn("Failed to create registration method: %s", method)

    def init_ui(self):
        layout = QtGui.QVBoxLayout()
        self.setLayout(layout)

        title = TitleWidget(self,
                            title="Registration and Motion Correction",
                            help="reg")
        layout.addWidget(title)

        if not self.reg_methods:
            layout.addWidget(QtGui.QLabel("No registration methods found"))
            layout.addStretch(1)
            return

        self.options = OptionBox("General Options")
        self.options.add("Mode",
                         ChoiceOption(["Registration", "Motion Correction"],
                                      ["reg", "moco"]),
                         key="mode")
        self.options.add(
            "Method",
            ChoiceOption([method.display_name for method in self.reg_methods],
                         self.reg_methods),
            key="method")
        self.options.add("Registration data", DataOption(self.ivm), key="reg")
        self.options.add("Reference data", DataOption(self.ivm), key="ref")
        self.options.add(
            "Reference volume",
            ChoiceOption(["Middle volume", "Mean volume", "Specified volume"],
                         ["median", "mean", "idx"]),
            key="ref-vol")
        self.options.add("Reference volume index",
                         NumericOption(intonly=True),
                         key="ref-idx")
        self.options.add(
            "Output space",
            ChoiceOption(["Reference", "Registration", "Transformed"],
                         ["ref", "reg", "trans"]),
            key="output-space")
        self.options.add("Output name",
                         OutputNameOption(src_data=self.options.option("reg"),
                                          suffix="_reg"),
                         key="output-name",
                         checked=True)
        self.options.add("Also apply transform to",
                         DataOption(self.ivm, multi=True),
                         key="add-reg")
        self.options.add("Save transformation",
                         TextOption(),
                         key="save-transform",
                         checked=True,
                         default=False)

        self.options.option("mode").sig_changed.connect(
            self._update_option_visibility)
        self.options.option("method").sig_changed.connect(self._method_changed)
        self.options.option("ref").sig_changed.connect(
            self._update_option_visibility)
        self.options.option("ref-vol").sig_changed.connect(
            self._update_option_visibility)
        self.options.option("reg").sig_changed.connect(
            self._update_option_visibility)
        layout.addWidget(self.options)

        # Create the options boxes for reg methods - only one visible at a time!
        self.opt_boxes = {}
        for method in self.reg_methods:
            hbox = QtGui.QHBoxLayout()
            opt_box = QtGui.QGroupBox()
            opt_box.setTitle(method.display_name)
            vbox = QtGui.QVBoxLayout()
            opt_box.setLayout(vbox)
            vbox.addWidget(method.interface())
            hbox.addWidget(opt_box)
            opt_box.setVisible(False)
            layout.addLayout(hbox)
            self.opt_boxes[method.name] = opt_box

        layout.addWidget(RunWidget(self))
        layout.addStretch(1)
        self._method_changed()

    def _method_changed(self):
        method = self.options.option("method").value
        for name, box in self.opt_boxes.items():
            box.setVisible(name == method.name)
        self.options.option("save-transform").value = "%s_trans" % method.name
        self._update_option_visibility()

    def _update_option_visibility(self):
        mode = self.options.option("mode").value
        regdata = self.ivm.data.get(self.options.option("reg").value, None)
        refdata = self.ivm.data.get(self.options.option("ref").value, None)
        refvol = self.options.option("ref-vol").value

        nvols_reg, nvols_ref = 1, 1
        if regdata is not None:
            nvols_reg = regdata.nvols

        if mode == "moco" and regdata is not None:
            nvols_ref = regdata.nvols
        elif mode == "reg" and refdata is not None:
            nvols_ref = refdata.nvols

        self.options.set_visible("ref", mode == "reg")
        self.options.set_visible("ref-vol", nvols_ref > 1)
        self.options.set_visible("ref-idx", nvols_ref > 1 and refvol == "idx")
        self.options.set_visible("add-reg", nvols_reg == 1 and mode == "reg")
        self.options.set_visible("output-space", mode == "reg")

        if nvols_ref > 1:
            self.options.option("ref-idx").setLimits(0, nvols_ref - 1)
            self.options.option("ref-idx").value = int(nvols_ref / 2)

    def processes(self):
        options = self.options.values()
        if options.get("ref-vol", None) == "idx":
            options["ref-vol"] = options.pop("ref-idx")

        method = options.pop("method")
        options["method"] = method.name
        options.update(method.options())

        return {
            "Reg": options,
        }
Beispiel #19
0
class DceDataModelView:
    def __init__(self, ivm):
        self.model = DceDataModel(ivm)
        self.gui = OptionBox()
        self.gui.add(
            "Model",
            ChoiceOption([
                "Standard Tofts model", "Extended Tofts model (ETM)",
                "2 Compartment exchange model",
                "Compartmental Tissue Update (CTU) model",
                "Adiabatic Approximation to Tissue Homogeneity (AATH) Model"
            ], ["dce_tofts", "dce_ETM", "dce_2CXM", "dce_CTU", "dce_AATH"]),
            key="model")
        self.gui.add("Contrast agent R1 relaxivity (l/mmol s)",
                     NumericOption(minval=0, maxval=10, default=3.7),
                     key="r1")
        self.gui.add("Flip angle (\N{DEGREE SIGN})",
                     NumericOption(minval=0, maxval=90, default=12),
                     key="fa")
        self.gui.add("TR (ms)",
                     NumericOption(minval=0, maxval=10, default=4.108),
                     key="tr")
        self.gui.add("Time between volumes (s)",
                     NumericOption(minval=0, maxval=30, default=12),
                     key="delt")
        self.gui.add("AIF",
                     ChoiceOption([
                         "Population (Orton 2008)", "Population (Parker)",
                         "Measured DCE signal", "Measured concentration curve"
                     ], ["orton", "parker", "signal", "conc"]),
                     key="aif")
        self.gui.add("Number of volumes",
                     NumericOption(minval=0,
                                   maxval=100,
                                   default=20,
                                   intonly=True),
                     key="nt")
        self.gui.add("Bolus injection time (s)",
                     NumericOption(minval=0, maxval=60, default=30),
                     key="tinj")
        self.gui.add("AIF data values",
                     NumberListOption([
                         0,
                     ]),
                     key="aif-data")
        self.gui.add("Arterial transit time (s)",
                     NumericOption(minval=0, maxval=1.0, default=0),
                     key="delay")
        self.gui.option("model").sig_changed.connect(self._model_changed)
        self.gui.option("aif").sig_changed.connect(self._aif_changed)
        self._aif_changed()
        self._model_changed()
        self.gui.sig_changed.connect(self._update_options)
        self._update_options()

    def _aif_changed(self):
        aif_source = self.gui.option("aif").value
        self.gui.set_visible("tinj", aif_source not in ("signal", "conc"))
        self.gui.set_visible("aif-data", aif_source in ("signal", "conc"))
        self.gui.set_visible("nt", aif_source not in ("signal", "conc"))

    def _model_changed(self):
        pass

    def _update_options(self):
        self.model.options.update(self.gui.values())
Beispiel #20
0
class AtlasDescription(QtGui.QWidget):
    """
    Displays atlas description
    """

    sig_selected = QtCore.Signal(object)

    def __init__(self, parent, registry):
        super(AtlasDescription, self).__init__(parent)
        self._registry = registry
        self.ivm = parent.ivm
        self._desc = None
        grid = QtGui.QGridLayout()
        self.setLayout(grid)

        grid.addWidget(QtGui.QLabel("Name"), 0, 0)
        self._name = QtGui.QLabel()
        grid.addWidget(self._name, 0, 1)
        grid.addWidget(QtGui.QLabel("Type"), 1, 0)
        self._type = QtGui.QLabel()
        grid.addWidget(self._type, 1, 1)
        grid.addWidget(QtGui.QLabel("Resolutions"), 2, 0)
        self._imgs = QtGui.QComboBox()
        grid.addWidget(self._imgs, 2, 1)

        self._label_table = QtGui.QTableView()
        self._label_model = QtGui.QStandardItemModel()
        self._label_table.setModel(self._label_model)
        self._label_table.setSelectionBehavior(
            QtGui.QAbstractItemView.SelectRows)
        self._label_table.setSelectionMode(
            QtGui.QAbstractItemView.SingleSelection)
        self._label_table.setEditTriggers(
            QtGui.QAbstractItemView.NoEditTriggers)
        self._label_table.selectionModel().selectionChanged.connect(
            self._region_changed)

        self._label_table.setStyleSheet(
            "font-size: 10px; alternate-background-color: #6c6c6c;")
        self._label_table.setShowGrid(False)
        self._label_table.setTextElideMode(QtCore.Qt.ElideLeft)
        self._label_table.setAlternatingRowColors(True)
        self._label_table.ensurePolished()
        fm = QtGui.QFontMetrics(self._label_table.font())
        self._label_table.verticalHeader().setVisible(False)
        self._label_table.verticalHeader().setSectionResizeMode(
            QtGui.QHeaderView.Fixed)
        self._label_table.verticalHeader().setDefaultSectionSize(fm.height() +
                                                                 2)

        grid.addWidget(self._label_table, 3, 0, 1, 2)
        grid.setRowStretch(3, 1)

        self._load_options = OptionBox()
        self._load_options.add("Regions",
                               ChoiceOption(["Selected region", "All regions"],
                                            ["sel", "all"]),
                               key="regions")
        self._load_options.add(
            "Load as",
            ChoiceOption(["New dataset", "Add to existing dataset"],
                         ["new", "add"]),
            key="add")
        self._load_options.add("Dataset name", TextOption("atlas"), key="name")
        self._load_options.add("Existing dataset",
                               DataOption(self.ivm),
                               key="data")
        self._load_options.option("regions").sig_changed.connect(
            self._load_regions_changed)
        self._load_options.option("add").sig_changed.connect(self._add_changed)
        grid.addWidget(self._load_options, 4, 0, 1, 2)

        hbox = QtGui.QHBoxLayout()
        btn = QtGui.QPushButton("Load")
        btn.clicked.connect(self._load)
        hbox.addWidget(btn)
        hbox.addStretch(1)
        grid.addLayout(hbox, 5, 0, 1, 2)
        self._add_changed()

    def _name_to_dataset_name(self, name):
        return name.replace(" ",
                            "_").replace("-", "_").replace(",", "").replace(
                                "(", "").replace(")", "").lower()

    def set_atlas(self, atlas_desc):
        self._desc = atlas_desc
        self._name.setText(atlas_desc.name)
        self._type.setText(atlas_desc.atlasType)
        self._load_options.option("name").value = self._name_to_dataset_name(
            atlas_desc.name)

        self._imgs.clear()
        for pixdim in atlas_desc.pixdims:
            pixdim_str = "%.2g mm x %.2g mm x %.2g mm" % pixdim
            self._imgs.addItem(pixdim_str, pixdim[0])

        self._label_model.clear()
        self._label_model.setColumnCount(2)
        self._label_model.setHorizontalHeaderLabels(["Index", "Name"])
        for label in atlas_desc.labels:
            index_item = QtGui.QStandardItem("%i" % label.index)
            name_item = QtGui.QStandardItem(label.name)
            self._label_model.appendRow([index_item, name_item])
        self._label_table.horizontalHeader().setResizeMode(
            0, QtGui.QHeaderView.ResizeToContents)
        self._label_table.horizontalHeader().setResizeMode(
            1, QtGui.QHeaderView.Stretch)
        self._load_options.option("regions").value = "all"

    def _load_regions_changed(self):
        if self._load_options.values()["regions"] == "sel":
            self._region_changed()
        else:
            self._load_options.option(
                "name").value = self._name_to_dataset_name(self._desc.name)

    def _region_changed(self):
        if self._load_options.values()["regions"] == "sel":
            indexes = self._label_table.selectionModel().selectedRows()
            if indexes:
                region_name = self._label_model.item(indexes[0].row(),
                                                     1).text()
                if region_name:
                    self._load_options.option(
                        "name").value = self._name_to_dataset_name(region_name)

    def _add_changed(self):
        add = self._load_options.values()["add"] == "add"
        self._load_options.set_visible("name", not add)
        self._load_options.set_visible("data", add)

    def _load(self):
        if self._desc is not None:
            res = self._imgs.itemData(self._imgs.currentIndex())
            atlas = self._registry.loadAtlas(self._desc.atlasID,
                                             loadSummary=False,
                                             resolution=res)
            is_roi = self._desc.atlasType == "label"

            new_name = self._load_options.option("name").value
            add_name = self._load_options.option("data").value
            add = self._load_options.option("add").value == "add"
            load_all = self._load_options.option("regions").value == "all"

            vol = None
            if not load_all:
                indexes = self._label_table.selectionModel().selectedRows()
                vol = int(self._label_model.item(indexes[0].row(), 0).text())
            new_data = fslimage_to_qpdata(atlas,
                                          vol=vol,
                                          name=new_name,
                                          roi=is_roi)

            if add and add_name in self.ivm.data:
                # User wants to add the region to an existing data set
                if load_all:
                    raise QpException(
                        "Cannot add data to existing data set when loading all regions"
                    )
                orig_data = self.ivm.data[add_name]
                if not orig_data.grid.matches(new_data.grid):
                    raise QpException(
                        "Can't add data to existing data set - grids do not match"
                    )
                if is_roi and not orig_data.roi:
                    raise QpException(
                        "Can't add data to existing data set - it is not an ROI"
                    )
                new_data = NumpyData(orig_data.raw() + new_data.raw(),
                                     grid=new_data.grid,
                                     name=add_name,
                                     roi=is_roi)

            self.ivm.add(new_data, make_current=True)
Beispiel #21
0
class DscOptionsWidget(QtGui.QWidget):
    """
    Widget allowing choice of DSC options
    """
    def __init__(self, ivm):
        QtGui.QWidget.__init__(self)
        self.ivm = ivm

        vbox = QtGui.QVBoxLayout()
        self.setLayout(vbox)

        self.optbox = OptionBox()

        self.optbox.add("DSC Data", DataOption(self.ivm), key="data")
        self.optbox.add("ROI",
                        DataOption(self.ivm, rois=True, data=False),
                        key="mask")
        self.optbox.add(
            "Model choice",
            ChoiceOption(["Standard", "Control point interpolation"],
                         ["dsc", "dsc_cpi"]),
            key="model")
        self.optbox.add("TE (s)",
                        NumericOption(minval=0, maxval=0.1, default=0.065),
                        key="te")
        self.optbox.add("Time interval between volumes (s)",
                        NumericOption(minval=0, maxval=10, default=1.5),
                        key="delt")
        self.optbox.add("Apply dispersion to AIF", BoolOption(), key="disp")
        self.optbox.add("Infer delay parameter",
                        BoolOption(default=True),
                        key="inferdelay")
        self.optbox.add("Infer arterial component",
                        BoolOption(),
                        key="inferart")
        self.optbox.add("Log transform on rCBF", BoolOption(), key="log-cbf")
        self.optbox.add("Output residue function",
                        BoolOption(),
                        key="save-model-extras")
        self.optbox.add("Spatial regularization",
                        ChoiceOption(("None", "Standard", "Full"),
                                     default="Standard"),
                        key="spatial")
        self.optbox.add("Output data suffix",
                        TextOption(),
                        checked=True,
                        key="output-suffix")
        self.optbox.option("model").sig_changed.connect(self._model_changed)

        vbox.addWidget(self.optbox)

        hbox = QtGui.QHBoxLayout()
        self.classic_options = OptionBox("Standard model")
        self.classic_options.add("Infer MTT",
                                 BoolOption(default=True),
                                 key="infermtt")
        self.classic_options.add("Infer lambda",
                                 BoolOption(default=True),
                                 key="inferlambda")
        hbox.addWidget(self.classic_options)
        hbox.addStretch(1)
        vbox.addLayout(hbox)

        hbox = QtGui.QHBoxLayout()
        self.cpi_options = OptionBox("CPI model")
        self.cpi_options.setVisible(False)
        self.cpi_options.add("Number of control points",
                             NumericOption(minval=3,
                                           maxval=20,
                                           default=5,
                                           intonly=True),
                             key="num-cps")
        self.cpi_options.add("Infer control point time position",
                             BoolOption(),
                             key="infer-cpt")
        hbox.addWidget(self.cpi_options)
        hbox.addStretch(1)
        vbox.addLayout(hbox)

        vbox.addStretch()

    def options(self):
        """ :return: Dictionary of options selected for the DSC analysis"""
        opts = self.optbox.values()
        if opts["model"] == "dsc":
            opts.update(self.classic_options.values())
        elif opts["model"] == "dsc_cpi":
            opts.update(self.cpi_options.values())

        spatial = opts.pop("spatial", "None")
        if spatial == "Standard":
            opts["method"] = "spatialvb"
            opts["param-spatial-priors"] = "MN+"
        elif spatial == "Full":
            opts["method"] = "spatialvb"
            opts["param-spatial-priors"] = "M+"

        if opts.pop("log-cbf", False):
            opts["PSP_byname1"] = "cbf"
            opts["PSP_byname1_mean"] = 0.1
            opts["PSP_byname1_prec"] = 1e-4
            opts["PSP_byname1_transform"] = "L"

        return opts

    def _model_changed(self):
        classic = self.optbox.option("model").value == "dsc"
        self.classic_options.setVisible(classic)
        self.cpi_options.setVisible(not classic)
Beispiel #22
0
class AcquisitionOptions(OptionsWidget):

    N_REGRESSORS = 3

    def __init__(self, ivm, parent):
        OptionsWidget.__init__(self, ivm, parent)

        vbox = QtWidgets.QVBoxLayout()
        self.setLayout(vbox)

        self._optbox = OptionBox()
        self._optbox.add("<b>Data</b>")
        self._optbox.add("BOLD timeseries data",
                         DataOption(self.ivm),
                         key="data")
        self._optbox.add("ROI",
                         DataOption(self.ivm, rois=True, data=False),
                         key="roi")

        #self._optbox.add("Physiological data (CO<sub>2</sub>/O<sub>2</sub>)", FileOption(plot_btn=True), key="phys-data")
        #self._optbox.add("Sampling frequency (Hz)", NumericOption(minval=0, maxval=1000, default=100, intonly=True), key="samp-rate")
        self._optbox.add("TR for MRI timeseries (s)",
                         NumericOption(minval=0, maxval=5, default=1.0),
                         key="tr")
        self._optbox.add("Baseline period (s)",
                         NumericOption(minval=0,
                                       maxval=200,
                                       default=60,
                                       intonly=True),
                         key="baseline")
        self._optbox.add("MRI timeseries alignment",
                         ChoiceOption(["Automatic", "Manual"]),
                         key="mri-align")
        self._optbox.option("mri-align").sig_changed.connect(
            self._align_changed)
        self._optbox.add("MRI timeseries start time (s)",
                         NumericOption(minval=0, maxval=1000, default=0),
                         key="data-start-time")
        vbox.addWidget(self._optbox)

        self._optbox_reg = OptionBox()
        self._optbox_reg.add("<b>Regressors</b>")
        for idx in range(self.N_REGRESSORS):
            self._optbox_reg.add("Regressor %i" % (idx + 1),
                                 ChoiceOption([
                                     "Unprocessed CO2", "Preprocessed pETCO2",
                                     "Ramp (linear drift)", "Custom"
                                 ], ["co2", "petco2", "ramp", "custom"]),
                                 checked=True,
                                 default=True,
                                 key="type_%i" % (idx + 1))
            self._optbox_reg.option("type_%i" % (idx + 1)).sig_changed.connect(
                self._regressor_changed)
            self._optbox_reg.add("Data (CO<sub>2</sub>/O<sub>2</sub>)",
                                 FileOption(plot_btn=True),
                                 key="data_%i" % (idx + 1))
            self._optbox_reg.add("Time resolution (s)",
                                 NumericOption(minval=0, maxval=10, default=1),
                                 key="tr_%i" % (idx + 1))

        vbox.addWidget(self._optbox_reg)

        vbox.addStretch(1)
        self._regressor_changed()
        self._align_changed()

    def _regressor_changed(self):
        for idx in range(self.N_REGRESSORS):
            opts = self._optbox_reg.values()
            extras_visible = "type_%i" % (
                idx + 1) in opts and opts["type_%i" % (idx + 1)] != "ramp"
            self._optbox_reg.set_visible("data_%i" % (idx + 1), extras_visible)
            self._optbox_reg.set_visible("tr_%i" % (idx + 1), extras_visible)

    def _add_regressor_options(self, opts):
        regressors = []
        regressor_types = []
        regressor_trs = []
        reg_opts = self._optbox_reg.values()
        for idx in range(self.N_REGRESSORS):
            regressor_type = reg_opts.get("type_%i" % (idx + 1), None)
            if regressor_type is not None:
                if regressor_type != "ramp":
                    regressor_types.append(regressor_type)
                    regressors.append(reg_opts["data_%i" % (idx + 1)])
                    regressor_trs.append(reg_opts["tr_%i" % (idx + 1)])
                else:
                    # FIXME can't mix file regressors with Numpy array, need to write to tmp file
                    regressor_types.append("custom")
                    regressor_trs.append(opts["tr"])
                    regressors.append(
                        np.linspace(0, 1, self.ivm.data[opts["data"]].nvols))
        opts["regressors"] = ",".join(regressors)
        opts["regressor_trs"] = ",".join(["%.3f" % v for v in regressor_trs])
        opts["regressor_types"] = ",".join(regressor_types)

    def _align_changed(self):
        self._optbox.set_visible(
            "data-start-time",
            self._optbox.option("mri-align").value == "Manual")

    def options(self):
        opts = self._optbox.values()
        opts.pop("mri-align", None)
        self._add_regressor_options(opts)
        return opts
Beispiel #23
0
class AnalysisOptions(QtGui.QWidget):
    """
    Widget allowing model and output options to be changed
    """
    def __init__(self, ivm=None):
        QtGui.QWidget.__init__(self)
        self._ivm = ivm
        self._poolvals_edited = False

        vbox = QtGui.QVBoxLayout()
        self.setLayout(vbox)

        self.optbox = OptionBox()
        vbox.addWidget(self.optbox)

        self.optbox.add("<b>Output options</b>")
        self.optbox.add("CEST R*",
                        BoolOption(default=True),
                        key="save-model-extras")
        self.optbox.add("Parameter maps",
                        BoolOption(default=False),
                        key="save-mean")
        #self.optbox.add("Parameter variance", BoolOption(default=False), key="var")
        self.optbox.add("Model fit",
                        BoolOption(default=False),
                        key="save-model-fit")
        self.optbox.add("Prefix for output",
                        TextOption(),
                        checked=True,
                        key="output-prefix")

        self.optbox.add(" ")
        self.optbox.add("<b>Analysis options</b>")
        self.optbox.add("Spatial Regularization", BoolOption(), key="spatial")
        self.optbox.add("Allow uncertainty in T1/T2 values",
                        BoolOption(),
                        key="t12prior")
        self.optbox.add("Prior T1 map",
                        DataOption(self._ivm),
                        key="t1img",
                        checked=True)
        self.optbox.add("Prior T2 map",
                        DataOption(self._ivm),
                        key="t2img",
                        checked=True)
        self.optbox.add("Tissue PV map (GM+WM)",
                        DataOption(self._ivm),
                        key="pvimg",
                        checked=True)
        self.optbox.option("t12prior").sig_changed.connect(self._update_ui)
        self.optbox.add("Use steady state solution for MT bias reduction",
                        BoolOption(default=False),
                        key="new-ss")
        self.optbox.option("new-ss").sig_changed.connect(self._update_ui)
        self.optbox.add("TR (s)",
                        NumericOption(default=3.0,
                                      minval=0,
                                      maxval=5,
                                      digits=3,
                                      step=0.1),
                        key="tr")
        self.optbox.add("Excitation flip angle (\N{DEGREE SIGN})",
                        NumericOption(default=12.0,
                                      minval=0,
                                      maxval=25,
                                      digits=3,
                                      step=1.0),
                        key="fa")
        self.optbox.add(
            "MT pool Line shape",
            ChoiceOption(
                ["None", "Gaussian", "Lorentzian", "Super Lorentzian"],
                ["none", "gaussian", "lorentzian", "superlorentzian"]),
            key="lineshape")

        self.alexmt_cite = Citation(ALEXMT_CITE_TITLE, ALEXMT_CITE_AUTHOR,
                                    ALEXMT_CITE_JOURNAL)
        vbox.addWidget(self.alexmt_cite)

        vbox.addStretch(1)
        self._update_ui()

    def _update_ui(self):
        t12prior = self.optbox.option("t12prior").value
        self.optbox.set_visible("t1img", t12prior)
        self.optbox.set_visible("t2img", t12prior)
        newss = self.optbox.values().get("new-ss", False)
        self.optbox.set_visible("tr", newss)
        self.optbox.set_visible("fa", newss)
        self.optbox.set_visible("lineshape", newss)
        self.alexmt_cite.setVisible(newss)

    def set_pools(self, pools):
        self.optbox.set_visible("new-ss", "MT"
                                in [p.name for p in pools if p.enabled])
        self._update_ui()

    def options(self):
        options = self.optbox.values()

        if options.pop("spatial", False):
            options["method"] = "spatialvb"
            options["param-spatial-priors"] = "MN+"
        else:
            options["method"] = "vb"
            options.pop("param-spatial-priors", None)

        # The new MT model is automatically triggered when the TR and FA options are given
        options.pop("new-ss", None)

        prior_num = 1
        for idx in (1, 2):
            if "t%iimg" % idx in options:
                options["PSP_byname%i" % prior_num] = "T%ia" % idx
                options["PSP_byname%i_type" % prior_num] = "I"
                options["PSP_byname%i_image" % prior_num] = options.pop(
                    "t%iimg" % idx)
                prior_num += 1
        return options
Beispiel #24
0
class FlirtRegMethod(RegMethod):
    """
    FLIRT/MCFLIRT registration method
    """
    def __init__(self, ivm):
        RegMethod.__init__(self, "flirt", ivm, "FLIRT/MCFLIRT")
        self.options_widget = None
        self.cost_models = [
            "Mutual information", "Woods", "Correlation ratio",
            "Normalized correlation", "Normalized mutual information",
            "Least squares"
        ]
        self.cost_model_options = [
            "mutualinfo", "woods", "corratio", "normcorr", "normmi", "leastsq"
        ]

    @classmethod
    def apply_transform(cls, reg_data, transform, options, queue):
        """
        Apply a previously calculated transformation to a data set

        We are not actually using FSL applyxfm for this although it would be
        an alternative option for the reference space output option. Instead
        we perform a non-lossy affine transformation and then resample onto
        the reference or registration spaces as required.
        """
        log = "Performing non-lossy affine transformation\n"
        order = options.pop("interp-order", 1)
        affine = transform.voxel_to_world(reg_data.grid)
        grid = DataGrid(reg_data.grid.shape, affine)
        qpdata = NumpyData(reg_data.raw(), grid=grid, name=reg_data.name)

        output_space = options.pop("output-space", "ref")
        if output_space == "ref":
            qpdata = qpdata.resample(transform.ref_grid,
                                     suffix="",
                                     order=order)
            log += "Resampling onto reference grid\n"
        elif output_space == "reg":
            qpdata = qpdata.resample(transform.reg_grid,
                                     suffix="",
                                     order=order)
            log += "Resampling onto input grid\n"

        return qpdata, log

    @classmethod
    def reg_3d(cls, reg_data, ref_data, options, queue):
        """
        Static function for performing 3D registration
        """
        from fsl import wrappers as fsl
        reg = qpdata_to_fslimage(reg_data)
        ref = qpdata_to_fslimage(ref_data)

        set_environ(options)

        output_space = options.pop("output-space", "ref")
        interp = _interp(options.pop("interp-order", 1))
        twod = reg_data.grid.shape[2] == 1
        logstream = six.StringIO()
        flirt_output = fsl.flirt(reg,
                                 ref,
                                 interp=interp,
                                 out=fsl.LOAD,
                                 omat=fsl.LOAD,
                                 twod=twod,
                                 log={
                                     "cmd": logstream,
                                     "stdout": logstream,
                                     "stderr": logstream
                                 },
                                 **options)
        transform = FlirtTransform(ref_data.grid,
                                   flirt_output["omat"],
                                   name="flirt_xfm")

        if output_space == "ref":
            qpdata = fslimage_to_qpdata(flirt_output["out"], reg_data.name)
        elif output_space == "reg":
            qpdata = fslimage_to_qpdata(flirt_output["out"],
                                        reg_data.name).resample(reg_data.grid,
                                                                suffix="")
            qpdata.name = reg_data.name
        elif output_space == "trans":
            trans_affine = transform.voxel_to_world(reg_data.grid)
            trans_grid = DataGrid(reg_data.grid.shape, trans_affine)
            qpdata = NumpyData(reg_data.raw(),
                               grid=trans_grid,
                               name=reg_data.name)

        return qpdata, transform, logstream.getvalue()

    @classmethod
    def moco(cls, moco_data, ref, options, queue):
        """
        Motion correction
        
        We use MCFLIRT to implement this
        
        :param moco_data: A single 4D QpData instance containing data to motion correct.
        :param ref: Either 3D QpData containing reference data, or integer giving 
                    the volume index of ``moco_data`` to use
        :param options: Method options as dictionary
        :param queue: Queue object which method may put progress information on to. Progress 
                      should be given as a number between 0 and 1.
        
        :return Tuple of three items. 
        
                First, motion corrected data as 4D QpData in the same space as ``moco_data``
        
                Second, if options contains ``output-transform : True``, sequence of transformations
                found, one for each volume in ``reg_data``. Each is either an affine matrix transformation 
                or a sequence of 3 warp images, the same shape as ``regdata`` If ``output-transform`` 
                is not given, returns None instead.

                Third, log information from the registration as a string.
        """
        from fsl import wrappers as fsl
        if moco_data.ndim != 4:
            raise QpException("Cannot motion correct 3D data")

        set_environ(options)

        reg = qpdata_to_fslimage(moco_data)

        if isinstance(ref, int):
            options["refvol"] = ref
            ref_grid = moco_data.grid
        elif isinstance(ref, QpData):
            options["reffile"] = qpdata_to_fslimage(ref)
            ref_grid = ref.grid
        else:
            raise QpException("invalid reference object type: %s" % type(ref))

        interp = _interp(options.pop("interp-order", 1))  # FIXME ignored
        twod = moco_data.grid.shape[2] == 1
        logstream = six.StringIO()
        result = fsl.mcflirt(reg,
                             out=fsl.LOAD,
                             mats=fsl.LOAD,
                             twod=twod,
                             log={
                                 "cmd": logstream,
                                 "stdout": logstream,
                                 "stderr": logstream
                             },
                             **options)
        qpdata = fslimage_to_qpdata(result["out"], moco_data.name)
        transforms = [
            FlirtTransform(ref_grid,
                           result[os.path.join("out.mat", "MAT_%04i" % vol)])
            for vol in range(moco_data.nvols)
        ]

        return qpdata, transforms, logstream.getvalue()

    def interface(self, generic_options=None):
        """
        :return: QWidget containing registration options
        """
        if generic_options is None:
            generic_options = {}

        if self.options_widget is None:
            self.options_widget = QtGui.QWidget()
            vbox = QtGui.QVBoxLayout()
            self.options_widget.setLayout(vbox)

            cite = Citation(CITE_TITLE, CITE_AUTHOR, CITE_JOURNAL)
            vbox.addWidget(cite)

            self.optbox = OptionBox()
            self.optbox.add("Cost Model",
                            ChoiceOption(self.cost_models,
                                         self.cost_model_options,
                                         default="normcorr"),
                            key="cost")
            #self.optbox.add("Number of search stages", ChoiceOption([1, 2, 3, 4]), key="nstages")
            #self.optbox.option("stages").value = 2
            #self.optbox.add("Final stage interpolation", ChoiceOption(["None", "Sinc", "Spline", "Nearest neighbour"], ["", "sinc_final", "spline_final", "nn_final"]), key="final")
            #self.optbox.add("Field of view (mm)", NumericOption(minval1, maxval=100, default=20), key="fov")
            self.optbox.add("Number of bins",
                            NumericOption(intonly=True,
                                          minval=1,
                                          maxval=1000,
                                          default=256),
                            key="bins")
            self.optbox.add("Degrees of freedom",
                            ChoiceOption([6, 9, 12]),
                            key="dof")
            #self.optbox.add("Scaling", NumericOption(minval=0.1, maxval=10, default=6), key="scaling")
            #self.optbox.add("Smoothing in cost function", NumericOption(minval=0.1, maxval=10, default=1), key="smoothing")
            #self.optbox.add("Scaling factor for rotation\noptimization tolerances", NumericOption(minval=0.1, maxval=10, default=1), key="rotscale")
            #self.optbox.add("Search on gradient images", BoolOption, key="grad")

            vbox.addWidget(self.optbox)
        return self.options_widget

    def options(self):
        """
        :return: Dictionary of registration options selected
        """
        self.interface()
        opts = self.optbox.values()
        for env_copy in ["FSLOUTPUTTYPE", "FSLDIR", "FSLDEVDIR"]:
            if env_copy in os.environ:
                opts[env_copy] = os.environ[env_copy]
            else:
                self.debug("%s is not in environment" % env_copy)

        for key, value in opts.items():
            self.debug("%s: %s", key, value)
        return opts
Beispiel #25
0
class HistogramWidget(QpWidget):
    """
    Widget which displays data histograms
    """
    def __init__(self, **kwargs):
        super(HistogramWidget, self).__init__(name="Histogram", icon="hist",
                                              desc="Display histograms from data", group="Visualisation", **kwargs)
        self._updating = False

    def init_ui(self):
        vbox = QtGui.QVBoxLayout()
        self.setLayout(vbox)

        title = TitleWidget(self)
        vbox.addWidget(title)

        self.options = OptionBox("Options")
        self.options.add("Data", DataOption(self.ivm, multi=True), key="data")
        self.options.add("Within ROI", DataOption(self.ivm, data=False, rois=True, none_option=True), key="roi")
        self.options.add("All volumes", BoolOption(default=False), key="allvols")
        self.options.add("Y-axis scale", ChoiceOption(["Count", "Probability"]), key="yscale")
        self.options.add("Number of bins", NumericOption(minval=5, maxval=500, default=100, intonly=True), key="bins")
        self.options.add("Min value", NumericOption(minval=0, maxval=100, default=0), key="min")
        self.options.add("Max value", NumericOption(minval=0, maxval=500, default=100), key="max")
        self.options.option("yscale").sig_changed.connect(self._yscale_changed)
        self.options.option("min").sig_changed.connect(self._min_changed)
        self.options.option("min").sig_changed.connect(self._max_changed)
        vbox.addWidget(self.options)

        self.plot = Plot(qpo=None, parent=self, title="Data histogram", display_mode=False)
        self.plot.set_xlabel("Data value")
        self.plot.set_ylabel("Count")
        vbox.addWidget(self.plot)

        vbox.addStretch(1)

    def activate(self):
        self.ivm.sig_all_data.connect(self._data_changed)
        self.options.option("data").sig_changed.connect(self._data_changed)
        self.options.sig_changed.connect(self._update)
        self._data_changed()

    def deactivate(self):
        self.ivm.sig_all_data.disconnect(self._data_changed)
        self.options.option("data").sig_changed.disconnect(self._data_changed)
        self.options.sig_changed.disconnect(self._update)

    def processes(self):
        opts = self.options.values()
        if not opts.pop("allvols", False):
            opts["vol"] = self.ivl.focus()[3]

        return {
            "Histogram" : opts
        }

    def _yscale_changed(self):
        self.plot.set_ylabel(self.options.option("yscale").value)

    def _min_changed(self):
        minval = self.options.option("min").value
        self.options.option("max").setLimits(minval=minval)

    def _max_changed(self):
        maxval = self.options.option("max").value
        self.options.option("min").setLimits(maxval=maxval)

    def _data_changed(self):
        if self._updating: return
        self._updating = True
        try:
            data_names = self.options.option("data").value
            vol = None
            if self.options.option("allvols").value:
                vol = self.ivl.focus()[3]
            dmin, dmax, multivol = None, None, False
            for data_name in data_names:
                qpdata = self.ivm.data[data_name]
                multivol = multivol or qpdata.nvols > 1
                _dmin, _dmax = qpdata.range(vol=vol)
                if dmin is None or dmin > _dmin:
                    dmin = _dmin
                if dmax is None or dmax > dmax:
                    dmax = _dmax
            
            if dmin is not None and dmax is not None:
                self.options.option("min").value = dmin
                self.options.option("min").setLimits(dmin, dmax)
                self.options.option("max").value = dmax
                self.options.option("max").setLimits(dmin, dmax)
                
            self.options.set_visible("allvols", multivol)
            self._update()
        finally:
            self._updating = False

    def _update(self):
        opts = self.processes()["Histogram"]
        if opts["data"]:
            HistogramProcess(self.ivm).run(opts)
            self.plot.clear()
            histogram = self.ivm.extras.get("histogram", None)
            if histogram is not None:
                for idx, name in enumerate(histogram.col_headers[3:]):
                    xvalues = [row[2] for row in histogram.arr]
                    yvalues = [row[idx+3] for row in histogram.arr]
                    self.plot.add_line(yvalues, name=name, xvalues=xvalues)
Beispiel #26
0
class FabberDceWidget(QpWidget):
    """
    DCE modelling, using the Fabber process
    """
    def __init__(self, **kwargs):
        QpWidget.__init__(self,
                          name="Bayesian DCE",
                          icon="dce",
                          group="DCE-MRI",
                          desc="DCE model fitting using Bayesian inference",
                          **kwargs)

    def init_ui(self):
        vbox = QtGui.QVBoxLayout()
        self.setLayout(vbox)

        try:
            self.FabberProcess = get_plugins("processes", "FabberProcess")[0]
        except IndexError:
            self.FabberProcess = None

        if self.FabberProcess is None:
            vbox.addWidget(
                QtGui.QLabel(
                    "Fabber core library not found.\n\n You must install Fabber to use this widget"
                ))
            return

        title = TitleWidget(
            self,
            help="fabber-dsc",
            subtitle="DSC modelling using the Fabber process %s" % __version__)
        vbox.addWidget(title)

        cite = Citation(FAB_CITE_TITLE, FAB_CITE_AUTHOR, FAB_CITE_JOURNAL)
        vbox.addWidget(cite)

        self.input = OptionBox("Input data")
        self.input.add("DCE data",
                       DataOption(self.ivm, include_3d=False, include_4d=True),
                       key="data")
        self.input.add("ROI",
                       DataOption(self.ivm, data=False, rois=True),
                       key="roi",
                       checked=True)
        self.input.add("T1 map",
                       DataOption(self.ivm, include_3d=True, include_4d=False),
                       key="t1",
                       checked=True)
        self.input.option("t1").sig_changed.connect(self._t1_map_changed)
        vbox.addWidget(self.input)

        self.acquisition = OptionBox("Acquisition")
        self.acquisition.add("Contrast agent R1 relaxivity (l/mmol s)",
                             NumericOption(minval=0, maxval=10, default=3.7),
                             key="r1")
        self.acquisition.add("Flip angle (\N{DEGREE SIGN})",
                             NumericOption(minval=0, maxval=90, default=12),
                             key="fa")
        self.acquisition.add("TR (ms)",
                             NumericOption(minval=0, maxval=10, default=4.108),
                             key="tr")
        self.acquisition.add("Time between volumes (s)",
                             NumericOption(minval=0, maxval=30, default=12),
                             key="delt")
        vbox.addWidget(self.acquisition)

        self.model = OptionBox("Model options")
        self.model.add(
            "Model",
            ChoiceOption([
                "Standard Tofts model", "Extended Tofts model (ETM)",
                "2 Compartment exchange model",
                "Compartmental Tissue Update (CTU) model",
                "Adiabatic Approximation to Tissue Homogeneity (AATH) Model"
            ], ["dce_tofts", "dce_ETM", "dce_2CXM", "dce_CTU", "dce_AATH"]),
            key="model")
        self.model.add(
            "AIF",
            ChoiceOption([
                "Population (Orton 2008)", "Population (Parker)",
                "Measured DCE signal", "Measured concentration curve"
            ], ["orton", "parker", "signal", "conc"]),
            key="aif")
        self.model.add("Bolus injection time (s)",
                       NumericOption(minval=0, maxval=60, default=30),
                       key="tinj")
        self.model.add("AIF data values",
                       NumberListOption([
                           0,
                       ]),
                       key="aif-data")
        self.model.add("T1 (s)",
                       NumericOption(minval=0.0, maxval=5.0, default=1.0),
                       key="t10")
        self.model.add("Allow T1 to vary",
                       BoolOption(default=False),
                       key="infer-t10")
        self.model.add("Bolus arrival time (s)",
                       NumericOption(minval=0, maxval=2.0, default=0),
                       key="delay")
        self.model.add("Allow bolus arrival time to vary",
                       BoolOption(default=False),
                       key="infer-delay")
        self.model.add("Infer kep rather than ve",
                       BoolOption(default=False),
                       key="infer-kep")
        self.model.add("Infer flow", BoolOption(default=True), key="infer-fp")
        self.model.add("Infer permeability-surface area",
                       BoolOption(default=False),
                       key="infer-ps")
        self.model.add("Spatial regularization",
                       BoolOption(default=False),
                       key="spatial")
        self.model.option("model").sig_changed.connect(self._model_changed)
        self.model.option("aif").sig_changed.connect(self._aif_changed)
        vbox.addWidget(self.model)

        # Run button and progress
        vbox.addWidget(RunWidget(self, title="Run modelling"))
        vbox.addStretch(1)

        self._aif_changed()
        self._model_changed()

    def _t1_map_changed(self):
        self.model.set_visible("t10", "t1" not in self.input.values())

    def _aif_changed(self):
        aif_source = self.model.option("aif").value
        self.model.set_visible("tinj", aif_source not in ("signal", "conc"))
        self.model.set_visible("aif-data", aif_source in ("signal", "conc"))

    def _model_changed(self):
        self.model.set_visible(
            "infer-kep",
            self.model.option("model").value in ("dce_tofts", "dce_ETM"))
        self.model.set_visible("infer-fp",
                               self.model.option("model").value == "dce_AATH")
        self.model.set_visible("infer-ps",
                               self.model.option("model").value == "dce_AATH")

    def processes(self):
        options = {
            "model-group": "dce",
            "method": "vb",
            "noise": "white",
            "save-mean": True,
            "save-model-fit": True,
            "convergence": "trialmode",
            "max-trials": 20,
            "max-iterations": 50,
            "infer-sig0": True,
        }
        options.update(self.input.values())
        options.update(self.acquisition.values())
        options.update(self.model.values())

        # Extended Tofts model is the same model name but with inference of Vp
        if options["model"] == "dce_ETM":
            options["model"] = "dce_tofts"
            options["infer-vp"] = True

        # T1 map is an image prior
        if "t1" in options:
            options.update({
                "PSP_byname1": "t10",
                "PSP_byname1_type": "I",
                "PSP_byname1_image": options.pop("t1")
            })
            if not options["infer-t10"]:
                # To treat the image prior as ground truth need to put T10
                # into the model but give the image prior a high precision so
                # the parameter doesn't actually have any freedom to vary
                options["infer-t10"] = True
                options["PSP_byname1_prec"] = 1e6

        # Delay time to include injection time for population AIF
        if "tinj" in options:
            options["delay"] = options["delay"] + options.pop("tinj")

        # Times in minutes and TR in s
        options["delt"] = options["delt"] / 60
        options["delay"] = options["delay"] / 60
        options["tr"] = options["tr"] / 1000

        # Spatial mode
        if options.pop("spatial", False):
            options["method"] = "spatialvb"
            options["param-spatial-priors"] = "M+"

        return {"Fabber": options}
Beispiel #27
0
class FnirtRegMethod(RegMethod):
    """
    FNIRT registration method
    """
    def __init__(self, ivm):
        RegMethod.__init__(self, "fnirt", ivm, display_name="FNIRT")
        self.options_widget = None

    @classmethod
    def apply_transform(cls, reg_data, transform, options, queue):
        """
        Apply a previously calculated transformation to a data set
        """
        output_space = options.pop("output-space", "ref")
        if output_space not in ("ref", "reg"):
            raise QpException(
                "FNIRT does not support output in transformed space")

        from fsl import wrappers as fsl
        reg = qpdata_to_fslimage(reg_data)
        trans = qpdata_to_fslimage(transform)

        # Applywarp generates an output for each volume of reference image
        # for some reason. So use just the first volume of the transform
        # as the reference space
        ref = qpdata_to_fslimage(transform.volume(0, qpdata=True))

        log = six.StringIO()
        order = options.pop("interp-order", 1)
        interp = _interp(order)
        apply_output = fsl.applywarp(reg,
                                     ref,
                                     interp=interp,
                                     paddingsize=1,
                                     super=True,
                                     superlevel="a",
                                     out=fsl.LOAD,
                                     log={
                                         "cmd": log,
                                         "stdout": log,
                                         "stderr": log
                                     },
                                     warp=trans,
                                     rel=True)
        qpdata = fslimage_to_qpdata(apply_output["out"], name=reg_data.name)

        if output_space == "ref":
            # Default is to output in reference space
            pass
        else:
            qpdata = qpdata.resample(reg_data.grid, suffix="", order=order)
            log += "Resampling onto input grid\n"

        return qpdata, log.getvalue()

    @classmethod
    def reg_3d(cls, reg_data, ref_data, options, queue):
        """
        Static function for performing 3D registration

        FIXME return jacobian as part of xform?
        """
        output_space = options.pop("output-space", "ref")
        if output_space not in ("ref", "reg"):
            raise QpException(
                "FNIRT does not support output in transformed space")

        from fsl import wrappers as fsl
        reg = qpdata_to_fslimage(reg_data)
        ref = qpdata_to_fslimage(ref_data)

        log = six.StringIO()
        fnirt_output = fsl.fnirt(reg,
                                 ref=ref,
                                 iout=fsl.LOAD,
                                 fout=fsl.LOAD,
                                 log={
                                     "cmd": log,
                                     "stdout": log,
                                     "stderr": log
                                 },
                                 **options)
        transform = fslimage_to_qpdata(fnirt_output["fout"], name="fnirt_warp")
        transform.metadata["QpReg"] = "FNIRT"

        if output_space == "ref":
            qpdata = fslimage_to_qpdata(fnirt_output["iout"],
                                        name=reg_data.name)
        else:
            qpdata = fslimage_to_qpdata(fnirt_output["iout"],
                                        name=reg_data.name).resample(
                                            reg_data.grid, suffix="")

        return qpdata, transform, log.getvalue()

    def interface(self, generic_options=None):
        if generic_options is None:
            generic_options = {}

        if self.options_widget is None:
            self.options_widget = QtGui.QWidget()
            vbox = QtGui.QVBoxLayout()
            self.options_widget.setLayout(vbox)

            cite = Citation(CITE_TITLE, CITE_AUTHOR, CITE_JOURNAL)
            vbox.addWidget(cite)

            self.optbox = OptionBox()
            self.optbox.add("Mask for registration data",
                            DataOption(self.ivm, rois=True, data=False),
                            key="inmask",
                            checked=True)
            self.optbox.add("Mask for reference data",
                            DataOption(self.ivm, rois=True, data=False),
                            key="refmask",
                            checked=True)
            self.optbox.add("Spline order",
                            ChoiceOption([2, 3]),
                            key="splineorder",
                            checked=True)
            self.optbox.add("Use pre-defined configuration",
                            ChoiceOption(
                                ["T1_2_MNI152_2mm", "FA_2_FMRIB58_1mm"]),
                            key="config",
                            checked=True)
            vbox.addWidget(self.optbox)

        return self.options_widget

    def options(self):
        self.interface()
        return self.optbox.values()
Beispiel #28
0
class DceWidget(QpWidget):
    """
    Widget for DCE Pharmacokinetic modelling
    """
    def __init__(self, **kwargs):
        super(DceWidget, self).__init__(name="DCE Modelling",
                                        desc="DCE kinetic modelling",
                                        icon="dce",
                                        group="DCE-MRI",
                                        **kwargs)

    def init_ui(self):
        vbox = QtGui.QVBoxLayout()
        self.setLayout(vbox)

        title = TitleWidget(self, help="pk", batch_btn=True, opts_btn=False)
        vbox.addWidget(title)

        self.input = OptionBox("Input data")
        self.input.add("DCE data",
                       DataOption(self.ivm, include_3d=False, include_4d=True),
                       key="data")
        self.input.add("ROI",
                       DataOption(self.ivm, data=False, rois=True),
                       key="roi")
        self.input.add("T1 map",
                       DataOption(self.ivm, include_3d=True, include_4d=False),
                       key="t1")
        vbox.addWidget(self.input)

        self.options = OptionBox("Options")
        self.options.add("Contrast agent R1 relaxivity (l/mmol s)",
                         NumericOption(minval=0, maxval=10, default=3.7),
                         key="r1")
        self.options.add("Contrast agent R2 relaxivity (l/mmol s)",
                         NumericOption(minval=0, maxval=10, default=4.8),
                         key="r2")
        self.options.add("Flip angle (\N{DEGREE SIGN})",
                         NumericOption(minval=0, maxval=90, default=12),
                         key="fa")
        self.options.add("TR (ms)",
                         NumericOption(minval=0, maxval=10, default=4.108),
                         key="tr")
        self.options.add("TE (ms)",
                         NumericOption(minval=0, maxval=10, default=1.832),
                         key="te")
        self.options.add("Time between volumes (s)",
                         NumericOption(minval=0, maxval=30, default=12),
                         key="dt")
        self.options.add("Estimated injection time (s)",
                         NumericOption(minval=0, maxval=60, default=30),
                         key="tinj")
        self.options.add("Ktrans/kep percentile threshold",
                         NumericOption(minval=0, maxval=100, default=100),
                         key="ve-thresh")
        self.options.add("Dose (mM/kg) - preclinical only",
                         NumericOption(minval=0, maxval=5, default=0.6),
                         key="dose",
                         visible=False)

        models = [
            "Clinical: Toft / OrtonAIF (3rd) with offset",
            "Clinical: Toft / OrtonAIF (3rd) no offset",
            "Preclinical: Toft / BiexpAIF (Heilmann)",
            "Preclinical: Ext Toft / BiexpAIF (Heilmann)",
        ]
        self.options.add("Pharmacokinetic model choice",
                         ChoiceOption(models, [1, 2, 3, 4]),
                         key="model")
        self.options.option("model").sig_changed.connect(self._aif_changed)
        vbox.addWidget(self.options)

        # Run button and progress
        vbox.addWidget(RunWidget(self, title="Run modelling"))
        vbox.addStretch(1)
        self._aif_changed()

    def _aif_changed(self):
        self.options.set_visible("dose",
                                 self.options.option("model").value in (2, 3))

    def processes(self):
        options = self.input.values()
        options.update(self.options.values())
        return {"PkModelling": options}
Beispiel #29
0
class ResampleDataWidget(QpWidget):
    """
    Widget that lets you resample data onto a different grid
    """
    def __init__(self, **kwargs):
        super(ResampleDataWidget,
              self).__init__(name="Resample Data",
                             icon="resample.png",
                             desc="Resample data onto a different grid",
                             group="Utilities",
                             **kwargs)

    def init_ui(self):
        vbox = QtGui.QVBoxLayout()
        self.setLayout(vbox)

        vbox.addWidget(TitleWidget(self))

        self.optbox = OptionBox("Resampling options")
        self.data = self.optbox.add("Data to resample",
                                    DataOption(self.ivm),
                                    key="data")
        self.resample_type = self.optbox.add(
            "Resampling method",
            ChoiceOption(
                ["On to grid from another data set", "Upsample", "Downsample"],
                ["data", "up", "down"]),
            key="type")
        self.grid_data = self.optbox.add("Use grid from",
                                         DataOption(self.ivm),
                                         key="grid")
        self.factor = self.optbox.add("Factor",
                                      NumericOption(default=2,
                                                    minval=2,
                                                    maxval=10,
                                                    intonly=True),
                                      key="factor")
        self.slicewise = self.optbox.add("2D only", BoolOption(), key="2d")
        self.order = self.optbox.add(
            "Interpolation",
            ChoiceOption(["Nearest neighbour", "Linear", "Quadratic", "Cubic"],
                         [0, 1, 2, 3],
                         default=1),
            key="order")
        self.output_name = self.optbox.add("Output name",
                                           OutputNameOption(src_data=self.data,
                                                            suffix="_res"),
                                           key="output-name")
        vbox.addWidget(self.optbox)
        self.resample_type.sig_changed.connect(self._resample_type_changed)

        self.run = RunButton("Resample", self._run)
        vbox.addWidget(self.run)
        vbox.addStretch(1)

        self._resample_type_changed()

    def _resample_type_changed(self):
        resample_type = self.resample_type.value
        self.optbox.set_visible("grid", resample_type == "data")
        self.optbox.set_visible("factor", resample_type != "data")
        self.optbox.set_visible("order", resample_type != "down")
        self.optbox.set_visible("2d", resample_type != "data")

    def batch_options(self):
        options = self.optbox.values()
        return "Resample", options

    def _run(self):
        _, options = self.batch_options()
        ResampleProcess(self.ivm).run(options)
Beispiel #30
0
class FabberVbOptions(OptionsWidget):
    def __init__(self, ivm, parent, acq_options):
        OptionsWidget.__init__(self, ivm, parent)
        self.acq_options = acq_options

        vbox = QtWidgets.QVBoxLayout()
        self.setLayout(vbox)

        cite = Citation(FAB_CITE_TITLE, FAB_CITE_AUTHOR, FAB_CITE_JOURNAL)
        vbox.addWidget(cite)

        self._optbox = OptionBox()
        self._optbox.add("<b>Model options</b>")
        self._optbox.add("Infer constant signal offset",
                         BoolOption(default=True),
                         key="infer-sig0")
        self._optbox.add("Infer delay",
                         BoolOption(default=True),
                         key="infer-delay")

        #self._optbox.add("<b>Model fitting options</b>")
        #self._optbox.add("Spatial regularization", BoolOption(default=True), key="spatial")
        self._optbox.add("<b>Output options</b>")
        self._optbox.add("Output data name suffix",
                         TextOption(),
                         checked=True,
                         key="output-suffix")

        vbox.addWidget(self._optbox)
        vbox.addWidget(RunWidget(self, save_option=True))
        vbox.addStretch(1)

    def processes(self):
        opts = {
            "model-group": "cvr",
            "model": "cvr_petco2",
            "save-mean": True,
            "save-model-fit": True,
            "noise": "white",
            "max-iterations": 10,
        }
        opts.update(self.acq_options.options())
        opts.update(self._optbox.values())

        # Fabber model requires the physiological data to be preprocessed
        from vaby.data import DataModel
        data_model = DataModel(opts["data"].raw(), mask=opts["mask"].raw())
        from vaby_models_cvr.petco2 import CvrPetCo2Model
        opts["phys_data"] = opts["phys-data"]  # FIXME hack
        model = CvrPetCo2Model(data_model, **opts)
        opts["phys-data"] = model.co2_mmHg
        opts.pop("phys_data")

        # Deal with the output suffix if specified
        suffix = opts.pop("output-suffix", "")
        if suffix and suffix[0] != "_":
            suffix = "_" + suffix
        opts["output-rename"] = {
            "mean_cvr": "cvr%s" % suffix,
            "mean_sig0": "sig0%s" % suffix,
            "mean_delay": "delay%s" % suffix,
            "modelfit": "modelfit%s" % suffix,
        }

        # In spatial mode use sig0 as regularization parameter
        if opts.pop("spatial", False):
            opts["method"] = "spatialvb"
            opts["param-spatial-priors"] = "M+"

        self.debug("Fabber CVR options: %s", opts)
        processes = [
            {
                "Fabber": opts
            },
        ]

        return processes