Esempio n. 1
0
    def run(self, options):
        exec_globals = {'np': np, 'scipy': scipy, 'ivm': self.ivm}

        # For general Numpy operations we will need a grid to put the
        # results back into. This is specified by the 'grid' option.
        # Note that all data is combined using their raw grids so these
        # must all match if the result is to work - that is the user's job!
        gridfrom = options.pop("grid", None)
        if gridfrom is None:
            grid = self.ivm.main.grid
        else:
            grid = self.ivm.data[gridfrom].grid

        for name, data in self.ivm.data.items():
            exec_globals[name] = data.raw()

        for name in list(options.keys()):
            proc = options.pop(name)
            if name in ("exec", "_"):
                for code in proc:
                    try:
                        exec(code, exec_globals)
                    except:
                        raise QpException(
                            "'%s' is not valid Python code (Reason: %s)" %
                            (code, sys.exc_info()[1]))
            else:
                try:
                    result = eval(proc, exec_globals)
                    self.ivm.add(result, grid=grid, name=name)
                except:
                    raise QpException(
                        "'%s' did not return valid data (Reason: %s)" %
                        (proc, sys.exc_info()[1]))
Esempio n. 2
0
def _run_reg(worker_id, queue, method_name, mode, reg_data, ref_data, options):
    """
    Generic registration function for asynchronous process
    """
    try:
        set_local_file_path()
        method = get_reg_method(method_name)
        if method is None:
            raise QpException("Unknown registration method: %s (known: %s)" %
                              (method_name, str(get_plugins("reg-methods"))))

        if not reg_data:
            raise QpException("No registration data")
        elif mode == "moco":
            return _reg_moco(worker_id, method, reg_data, ref_data, options,
                             queue)
        elif reg_data[0].ndim == 3:
            return _reg_3d(worker_id, method, reg_data, ref_data, options,
                           queue)
        else:
            return _reg_4d(worker_id, method, reg_data, ref_data, options,
                           queue)
    except:
        traceback.print_exc()
        return worker_id, False, sys.exc_info()[1]
Esempio n. 3
0
    def run(self, options):
        self.debug("Run")
        data = self.get_data(options)
        trans_name = options.pop("transform")
        output_name = options.pop("output-name", data.name + "_reg")

        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 None or "QpReg" not in transform.metadata:
            raise QpException("Transform not found: %s" % trans_name)

        method = get_reg_method(transform.metadata["QpReg"])
        if method is None:
            raise QpException("Registration method not found: %s" %
                              transform.metadata["QpReg"])

        # Fake queue - we ignore progress reports as this process is not asynchronous
        queue = multiprocessing.Queue()

        self.log("Applying transformation to data: %s" % data.name)
        registered, apply_log = method.apply_transform(data, transform,
                                                       dict(options), queue)
        registered = _normalize_output(data, registered, "_reg")
        self.log(apply_log)
        self.ivm.add(registered, name=output_name, make_current=True)
    def structure_maps(self):
        processes = get_plugins("processes", "FastProcess")
        if len(processes) != 1:
            raise QpException("Can't identify Fast process")

        struc = self.options.get("struc", None)
        if struc not in self._ivm.data:
            raise QpException("Structural image not loaded: %s" % struc)

        qpdata = self._ivm.data[struc]
        ivm = ImageVolumeManagement()
        ivm.add(qpdata)
        process = processes[0](ivm)
        fast_options = {
            "data": qpdata.name,
            "class": 3,
            "type": self.options["type"],
            "output-pve": True,
            "output-pveseg": False,
        }
        process.execute(fast_options)
        while process.status == Process.RUNNING:
            time.sleep(1)

        if process.status == Process.FAILED:
            raise process.exception

        # FIXME hack
        process._complete()

        return {
            "gm": ivm.data["%s_pve_1" % qpdata.name],
            "wm": ivm.data["%s_pve_2" % qpdata.name],
            "csf": ivm.data["%s_pve_0" % qpdata.name],
        }
Esempio n. 5
0
    def run(self, options):
        data = self.get_data(options)
        if data.ndim != 4: 
            raise QpException("Data must be 4D for DCE PK modelling")

        roi = self.get_roi(options, data.grid)
    
        self.suffix = options.pop('output-suffix', '')
        if self.suffix != "" and self.suffix[0] != "_": 
            self.suffix = "_" + self.suffix

        regressors = options.pop('regressors', None)
        regressor_trs = options.pop('regressor_trs', None)
        regressor_types = options.pop('regressor_types', None)
        if regressors is None or regressor_types is None or regressor_trs is None:
            raise QpException("Regressors, regressor type and regressor TR options must be given")
        if isinstance(regressors, str):
            regressors = regressors.split()
            new_regressors = []
            for r in regressors:
                if not os.path.isabs(r):
                    r = os.path.join(self.indir, r)
                new_regressors.append(r)
            regressors = ",".join(new_regressors)

        if isinstance(regressor_trs, str):
            try:
                regressor_trs = [float(v) for v in regressor_trs.split(",")]
            except ValueError:
                raise QpException("Regressor TRs should be comma separated list of numbers")
        elif isinstance(regressor_trs, (int, float)):
            regressor_trs = [regressor_trs]
        self.n_regressors = len(regressor_trs)

        tr = options.pop("tr", None)
        if tr is None:
            raise QpException("TR must be given")

        # Non-compulsary options
        baseline = options.pop("baseline", 60)
        data_start_time = options.pop("data-start-time", None)
        delay_min = options.pop("delay-min", 0)
        delay_max = options.pop("delay-max", 0)
        delay_step = options.pop("delay-step", 1)
        
        # Use smallest sub-array of the data which contains all unmasked voxels
        self.grid = data.grid
        self.bb_slices = roi.get_bounding_box()
        self.debug("Using bounding box: %s", self.bb_slices)
        data_bb = data.raw()[tuple(self.bb_slices)]
        mask_bb = roi.raw()[tuple(self.bb_slices)]
        #n_workers = data_bb.shape[0]
        n_workers = 1

        args = [data_bb, mask_bb, regressors, regressor_types, regressor_trs, tr, baseline, data_start_time, delay_min, delay_max, delay_step]
        self.voxels_done = [0] * n_workers
        self.total_voxels = np.count_nonzero(roi.raw())
        self.start_bg(args, n_workers=n_workers)
Esempio n. 6
0
    def reg_4d(cls, reg_data, ref_data, options, queue):
        """
        4D Registration

        The default implementation simply registers each volume of the data independently. However,
        implementations can supply their own more optimal implementation if appropriate

        :param reg_data: 4D QpData containing data to register.
        :param ref_data: 3D QpData containing reference data.
        :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, A QpData containing registered data

                Second, if options contains ``output-transform : True``, sequence of transformations
                found, one for each volume in ``reg_data``. Each is either a QpData object containing 
                a sequence of 3 warp images or an Extra object containing a transformation matrix
                If ``output-transform`` is not given or not supported, returns None instead.

                Third, log information from the registration as a string.
        """
        if reg_data.ndim != 4:
            raise QpException("reg_4d expected 4D data")
        
        if options.get("output-space", "ref") == "ref":
            output_space = ref_data
        else:
            output_space = reg_data
        out_data = np.zeros(list(output_space.grid.shape) + [reg_data.nvols])

        transforms = []
        log = "Default 4D registration using multiple 3d registrations\n"
        for vol in range(reg_data.shape[-1]):
            log += "Registering volume %i of %i\n" % (vol+1, reg_data.shape[-1])
            reg_vol = NumpyData(reg_data.volume(vol), grid=reg_data.grid, name="regvol")
            #self.debug("Vol %i of %i" % (vol+1, reg_data.shape[-1]))
            if vol == options.get("ignore-idx", -1):
                # Ignore this index (e.g. because it is the same as the ref volume)
                if options.get("output-space", "ref") != "reg":
                    raise QpException("Can't ignore an index unless the output space is the registration data")
                out_data[..., vol] = reg_vol.raw()
                transforms.append(None)
            else:
                #self.debug("Calling reg_3d", cls, cls.reg_3d)
                # We did not remove output-space from the options so regdata should
                # come back in the appropriate space
                regdata, transform, vol_log = cls.reg_3d(reg_vol, ref_data, options, queue)
                out_data[..., vol] = regdata.raw()
                transforms.append(transform)
                log += vol_log
            queue.put(float(vol)/reg_data.shape[-1])

        return NumpyData(out_data, grid=output_space.grid, name=reg_data.name), transforms, log
Esempio n. 7
0
    def run(self, options):
        data_model_name = options.pop("data-model", None)
        if data_model_name is None:
            raise QpException("Data model not specified")
        data_model = self._data_models.get(data_model_name, None)
        if data_model is None:
            raise QpException("Unknown data model: %s" % data_model_name)
        data_model = data_model(self.ivm)
        data_model_options = options.pop("data-model-options", {})
        data_model.options = data_model_options
        self.log("Created data model: %s\n" % data_model_name)

        struc_model_name = options.pop("struc-model", None)
        struc_model_options = options.pop("struc-model-options", {})
        if struc_model_name is None:
            raise QpException("Structure model not specified")
        struc_model = self._struc_models.get(struc_model_name, None)
        if struc_model is None:
            raise QpException("Unknown structure model: %s" % struc_model_name)
        struc_model = struc_model(self.ivm)
        struc_model.options = struc_model_options
        self.log("Created structure model: %s\n" % struc_model_name)

        param_values = options.pop("param-values", {})
        output_param_maps = options.pop("output-param-maps", False)
        self.log("Getting simulated data\n")
        ret = struc_model.get_simulated_data(
            data_model, param_values, output_param_maps=output_param_maps)
        if output_param_maps:
            sim_data, param_maps = ret
        else:
            sim_data, param_maps = ret, {}

        output_clean_name = options.pop("output-clean", "")
        if output_clean_name:
            self.ivm.add(sim_data.raw().copy(),
                         grid=sim_data.grid,
                         name=output_clean_name,
                         make_current=False)

        for param, qpdata in param_maps.items():
            self.ivm.add(qpdata, name=param, make_current=False)

        output_name = options.pop("output", "sim_data")
        self.ivm.add(sim_data, name=output_name, make_current=True)

        output_roi = output_name + "_roi"
        if sim_data.ndim > 3:
            roi_data = sim_data.raw()[..., 0]
        else:
            roi_data = sim_data.raw()
        roi = NumpyData(np.array(roi_data > 0, dtype=np.int),
                        grid=sim_data.grid,
                        roi=True,
                        name=output_roi)
        self.ivm.add(roi, make_current=False)
Esempio n. 8
0
    def run(self, options):
        data = self.get_data(options)
        roi = self.get_roi(options, data.grid)
        output_name = options.pop("output-name", "%s_pca" % data.name)
        norm_input = options.pop('norm-input', True)
        norm_type = options.pop('norm-type', "sigenh")
        norm_output = options.pop('norm-output', False)
        n_components = options.pop('n-components', 5)

        if data.ndim != 4:
            raise QpException("PCA reduction possible on 4D data only")
        elif data.nvols <= n_components:
            raise QpException(
                "Number of PCA components must be less than number of data volumes"
            )

        pca = PcaFeatReduce(n_components=n_components,
                            norm_input=norm_input,
                            norm_type=norm_type,
                            norm_modes=norm_output)

        feature_images = pca.get_training_features(data.raw(),
                                                   roi.raw(),
                                                   feature_volume=True)
        for comp_idx in range(n_components):
            name = "%s%i" % (output_name, comp_idx)
            self.ivm.add(feature_images[:, :, :, comp_idx],
                         grid=data.grid,
                         name=name,
                         make_current=(comp_idx == 0))

        #if options.pop("reduced-data", True):
        #    reduced = pca.pca.inverse_transform(feature_images)
        #    self.ivm.add(reduced, grid=data.grid, name=name + "_reduced")

        cumulative = 0
        var_rows = []
        for idx, variance in enumerate(pca.explained_variance()):
            cumulative += variance
            var_rows.append([idx, variance, cumulative])
        extra = MatrixExtra(
            output_name + "_variance",
            var_rows,
            col_headers=["PCA mode", "Explained variance", "Cumulative"])
        self.ivm.add_extra(extra.name, extra)

        modes = np.zeros((len(pca.mean()), len(pca.modes()) + 1))
        cols = []
        for idx, mode in enumerate(pca.modes()):
            modes[:, idx] = mode
            cols.append("Mode %i" % idx)
        modes[:, -1] = pca.mean()
        cols.append("Mean")
        extra = MatrixExtra(output_name + "_modes", modes, col_headers=cols)
        self.ivm.add_extra(extra.name, extra)
Esempio n. 9
0
 def roi(self, is_roi):
     if is_roi:
         if self.nvols != 1:
             raise QpException("This data set cannot be an ROI - it is 4D")
         else:
             rawdata = self.raw()
             if not np.all(np.equal(np.mod(rawdata, 1), 0)):
                 raise QpException(
                     "This data set cannot be an ROI - it does not contain integers"
                 )
     self._meta["roi"] = is_roi
Esempio n. 10
0
    def run(self, options):
        """ Generate test data from Fabber model """
        kwargs = {
            "patchsize":
            int(math.floor(options.pop("num-voxels", 1000)**(1. / 3) + 0.5)),
            "nt":
            options.pop("num-vols", 10),
            "noise":
            options.pop("noise", 0),
            "param_rois":
            options.pop("save-rois", False),
        }
        param_test_values = options.pop("param-test-values", None)
        output_name = options.pop("output-name", "fabber_test_data")
        grid_data_name = options.pop("grid", None)

        if not param_test_values:
            raise QpException("No test values given for model parameters")

        api = FabberProcess.api(options.pop("model-group", None))
        from fabber import generate_test_data
        test_data = generate_test_data(api, options, param_test_values,
                                       **kwargs)

        data = test_data["data"]
        self.debug("Data shape: %s", data.shape)

        if grid_data_name is None:
            grid = DataGrid(data.shape[:3], np.identity(4))
        else:
            grid_data = self.ivm.data.get(grid_data_name, None)
            if grid_data is None:
                raise QpException("Data not found for output grid: %s" %
                                  grid_data_name)
            grid = grid_data.grid

        self.ivm.add(data, name=output_name, grid=grid, make_current=True)

        clean_data = test_data.get("clean", None)
        if clean_data is not None:
            self.ivm.add(clean_data,
                         name="%s_clean" % output_name,
                         grid=grid,
                         make_current=False)

        for param, param_roi in test_data.get("param-rois", {}).items():
            self.ivm.add(param_roi,
                         name="%s_roi_%s" % (output_name, param),
                         grid=grid)
Esempio n. 11
0
    def get_data(self, options, multi=False):
        """ 
        Standard method to get the data object the process is to operate on 
        
        If no 'data' option is specified, go with main data if it exists
        If 'multi' then allow data to be a list of items

        :param options: Dictionary of options - ``data`` will be consumed if present
        :return: QpData instance
        """
        data_name = options.pop("data", None)
        if data_name is None:
            if self.ivm.main is None:
                raise QpException("No data loaded")
            data = self.ivm.main
        elif multi and isinstance(data_name, list):
            # Allow specifying a list of data volumes which are concatenated
            if not data_name:
                raise QpException("Empty list given for data")
            for name in data_name:
                if name not in self.ivm.data:
                    raise QpException("Data not found: %s" % name)

            multi_data = [self.ivm.data[name] for name in data_name]
            nvols = sum([d.nvols for d in multi_data])
            self.debug("Multivol: nvols=%i", nvols)
            grid = None
            num_vols = 0
            for data_item in multi_data:
                if grid is None:
                    grid = data_item.grid
                    data = np.zeros(list(grid.shape) + [
                        nvols,
                    ])
                data_item = data_item.resample(grid)
                if data_item.nvols == 1:
                    rawdata = np.expand_dims(data_item.raw(), 3)
                else:
                    rawdata = data_item.raw()
                data[..., num_vols:num_vols + data_item.nvols] = rawdata
                num_vols += data_item.nvols
            data = NumpyData(data, grid=grid, name="multi_data")
        else:
            if data_name in self.ivm.data:
                data = self.ivm.data[data_name]
            else:
                raise QpException("Data not found: %s" % data_name)
        return data
Esempio n. 12
0
    def run(self, options):
        data = self.get_data(options)
        if data.ndim != 4:
            raise QpException("Data must be 4D for DCE PK modelling")

        roi = self.get_roi(options, data.grid)

        self.suffix = options.pop('suffix', '')
        if self.suffix != "": self.suffix = "_" + self.suffix

        t1_name = options.pop("t1", "T10")
        if t1_name not in self.ivm.data:
            raise QpException("Could not find T1 map: %s" % t1_name)
        t1 = self.ivm.data[t1_name].resample(data.grid)

        R1 = options.pop('r1')
        R2 = options.pop('r2')
        DelT = options.pop('dt')
        InjT = options.pop('tinj')
        TR = options.pop('tr')
        TE = options.pop('te')
        FA = options.pop('fa')
        self.thresh = options.pop('ve-thresh')
        Dose = options.pop('dose', 0)
        model_choice = options.pop('model')

        # Baseline defaults to time points prior to injection
        baseline_tpts = int(1 + InjT / DelT)
        self.log("First %i time points used for baseline normalisation\n" %
                 baseline_tpts)
        baseline = np.mean(data.raw()[:, :, :, :baseline_tpts], axis=-1)

        self.grid = data.grid
        self.nvols = data.nvols
        self.roi = roi.raw()
        data_vec = data.raw()[self.roi > 0]
        t1_vec = t1.raw()[self.roi > 0]
        self.baseline = baseline[self.roi > 0]

        # Normalisation of the image - convert to signal enhancement
        data_vec = data_vec / (np.tile(np.expand_dims(self.baseline, axis=-1),
                                       (1, data.nvols)) + 0.001) - 1

        args = [
            data_vec, t1_vec, R1, R2, DelT, InjT, TR, TE, FA, Dose,
            model_choice
        ]
        self.start_bg(args)
    def resamp(self, qpdata):
        """
        Resample a map according to resampling options
        """
        resamp_options = dict(self.options.get("resampling", {}))
        if resamp_options:
            resamp_processes = get_plugins("processes", "ResampleProcess")
            if len(resamp_processes) != 1:
                raise QpException("Can't identify Resampling process")

            ivm = ImageVolumeManagement()
            ivm.add(qpdata)
            process = resamp_processes[0](ivm)
            resamp_options.update({
                "data": qpdata.name,
                "output-name": "output_res"
            })
            process.execute(resamp_options)
            while process.status == Process.RUNNING:
                time.sleep(1)

            if process.status == Process.FAILED:
                raise process.exception

            # FIXME hack
            process._complete()
            return ivm.data["output_res"]
        else:
            return qpdata
Esempio n. 14
0
    def get_projected_test_features(self,
                                    data,
                                    roi=None,
                                    smooth_timeseries=None,
                                    feature_volume=False):
        """
        Return features for each voxel from previously trained PCA modes

        :param data: 4D data set
        :param roi: Optional 3D ROI
        :param feature_volume: determines whether the features are returned as a list or an image

        :return: If ``feature_volume``, 4D array with the same 3d dimensions as data and 
                 4th dimension=number of PCA components.
                 Otherwise, 2D array whose first dimension is unmasked voxels and 2nd dimension
                 is the PCA components
        """
        data_inmask, roi = self._mask(data, roi, smooth_timeseries)

        #Projecting the data using training set PCA
        if data_inmask.shape[1] != self.pca.mean_.shape[0]:
            raise QpException(
                "Input data length does not match previous training data")

        reduced_data = self.pca.transform(data_inmask)

        # Scaling features
        if self.norm_modes:
            self.debug("Normalising PCA modes")
            reduced_data = norm.normalise(reduced_data, "indiv")

        if not feature_volume:
            return reduced_data

        return _flat_data_to_image(reduced_data, roi)
Esempio n. 15
0
    def get_rundata(self):
        rundata = {}
        rundata["model-group"] = "t1"
        rundata["save-mean"] = ""
        rundata["save-model-fit"] = ""
        rundata["noise"] = "white"
        rundata["max-iterations"] = "20"
        rundata["model"] = "vfa"
        rundata["tr"] = self.tr.spin.value() / 1000

        multivol = self.multivol_choice.combo.currentIndex() == 0
        if multivol and self.multivol_combo.currentText() in self.ivm.data:
            rundata["data"] = self.multivol_combo.currentText()
            fas = self.multivol_fas.values()
            nvols = self.ivm.data[self.multivol_combo.currentText()].nvols
            if nvols != len(fas):
                raise QpException(
                    "Number of flip angles must match the number of volumes in the selected data (%i)"
                    % nvols)
            for idx, fa in enumerate(fas):
                rundata["fa%i" % (idx + 1)] = fa
        else:
            rundata["data"] = []
            for r in range(self.singlevol_table.rowCount()):
                rundata["data"].append(self.singlevol_table.item(r, 0).text())
                rundata["fa%i" % (r + 1)] = float(
                    self.singlevol_table.item(r, 1).text())

        return rundata
Esempio n. 16
0
    def run(self, options):
        data = self.get_data(options)

        output_name = options.pop("output-name", "%s_noisy" % data.name)
        if "std" in options:
            std = float(options.pop("std"))
        elif "percent" in options:
            percent = float(options.pop("percent"))
            std = np.mean(data.raw()) * float(percent) / 100
        elif "snr" in options:
            snr = float(options.pop("snr"))
            roi = self.get_roi(options, grid=data.grid)
            signal = np.mean(data.raw()[roi.raw() > 0])
            std = signal / snr
        else:
            raise QpException(
                "AddNoiseProcess: Must specify either std, percent or snr")

        noise = np.random.normal(loc=0,
                                 scale=std,
                                 size=list(data.grid.shape) + [
                                     data.nvols,
                                 ])
        if data.nvols == 1:
            noise = np.squeeze(noise, -1)
        noisy_data = data.raw() + noise
        self.ivm.add(noisy_data,
                     grid=data.grid,
                     name=output_name,
                     make_current=True)
Esempio n. 17
0
    def run_threshold(self):
        # Check if an image and roi exists or otherwise throw an error
        if self.ivm.vol is None:
            raise QpException("No data loaded")

        if self.ivm.current_roi is None:
            raise QpException("No ROI loaded")

        slice = int(self.val_s1.text())
        thresh = float(self.val_t1.text())

        img = self.ivm.vol[:, :, :, slice1]
        img = img * (self.ivm.current_roi > 0)
        img[img1 < thresh] = 0

        self.ivm.add_data(img, name='thresh', make_current=True)
Esempio n. 18
0
    def get_roi(self, options, grid=None, use_current=False):
        """
        Standard method to get the ROI the process is to operate on

        If no 'roi' option is specified, go with currently selected ROI, 
        if it exists.

        :param options: Dictionary of options - ``roi`` will be consumed if present
        :param grid:    If specified, return ROI on this grid
        :param use_current: If True, return current ROI if no ROI specified
        :return:        QpData instance. If no ROI can be found and ``grid`` is specified, will
                        return an ROI where all voxels are unmasked.
        """
        roi_name = options.pop("roi", None)
        if roi_name is None or roi_name.strip() == "":
            if use_current and self.ivm.current_roi is not None:
                roidata = self.ivm.current_roi
            elif grid is not None:
                roidata = NumpyData(np.ones(grid.shape[:3]), grid=grid, name="dummy_roi", roi=True)
            else:
                return None
        else:
            if roi_name in self.ivm.rois:
                roidata = self.ivm.rois[roi_name]
            else:
                raise QpException("ROI not found: %s" % roi_name)

        if grid is not None:
            roidata = roidata.resample(grid)
        return roidata
Esempio n. 19
0
    def setChoices(self, choices, return_values=None):
        """
        Set the list of options to be chosen from

        :param choices: Sequence of strings
        :param return_values: Optional matching sequence of strings to be returned
                              as the ``value`` for each choice
        """
        if return_values is None:
            return_values = list(choices)

        if len(return_values) != len(choices):
            raise QpException("Number of return values must match number of choices")
        self.return_values = return_values
        self.choice_map = dict(zip([str(choice) for choice in choices], return_values))

        try:
            self.blockSignals(True)
            self.clear()
            for choice in choices:
                self.addItem(str(choice))
            self.setCurrentIndex(0)
        finally:
            self.blockSignals(False)
        self._changed()
    def _smooth_pv(self, qpdata, sigma):
        """
        Do Gaussian smoothing on a partial volume map

        Typically when the map is a discrete ROI and we want to smoothly blend it into
        the other tissue PV maps

        :param qpdata: PV map
        :param sigma: Gaussian kernel std.dev in mm
        :return: Smoothed PV map as Numpy array
        """
        smooth_processes = get_plugins("processes", "SmoothingProcess")
        if len(smooth_processes) != 1:
            raise QpException("Can't identify smoothing process")

        ivm = ImageVolumeManagement()
        ivm.add(qpdata)
        process = smooth_processes[0](ivm)
        smooth_options = {
            "data": qpdata.name,
            "sigma": sigma,
            "output-name": "output_smooth",
        }
        process.execute(smooth_options)
        while process.status == Process.RUNNING:
            time.sleep(1)

        if process.status == Process.FAILED:
            raise process.exception

        # FIXME hack
        process._complete()
        return ivm.data["output_smooth"].raw()
Esempio n. 21
0
    def run(self, options):
        data = self.get_data(options)
        roi = self.get_roi(options, data.grid)
        n_clusters = options.pop('n-clusters', 5)
        invert_roi = options.pop('invert-roi', False)
        output_name = options.pop('output-name', data.name + '_clusters')
        
        kmeans_data, mask = data.mask(roi, invert=invert_roi, output_flat=True, output_mask=True)
        start1 = time.time()

        if data.nvols > 1:
            # Do PCA reduction
            norm_data = options.pop('norm-data', True)
            norm_type = options.pop('norm-type', "sigenh")
            n_pca = options.pop('n-pca', 5)
            reduction = options.pop('reduction', 'pca')

            if reduction == "pca":
                self.log("Using PCA dimensionality reduction")
                pca = PCA(n_components=n_pca, norm_input=True, norm_type=norm_type,
                          norm_modes=norm_data)
                kmeans_data = pca.get_training_features(kmeans_data)
            else:
                raise QpException("Unknown reduction method: %s" % reduction)
        else:
            kmeans_data = kmeans_data[:, np.newaxis]

        kmeans = cl.KMeans(init='k-means++', n_clusters=n_clusters, n_init=10, n_jobs=1)
        kmeans.fit(kmeans_data)
        
        self.log("Elapsed time: %s" % (time.time() - start1))

        label_image = np.zeros(data.grid.shape, dtype=np.int)
        label_image[mask] = kmeans.labels_ + 1
        self.ivm.add(NumpyData(label_image, grid=data.grid, name=output_name, roi=True), make_current=True)
Esempio n. 22
0
    def _new_roi(self):
        dialog = QtGui.QDialog(self)
        dialog.setWindowTitle("New ROI")
        vbox = QtGui.QVBoxLayout()
        dialog.setLayout(vbox)

        optbox = OptionBox()
        optbox.add("ROI name", TextOption(), key="name")
        optbox.add("Data space from", DataOption(self.ivm), key="grid")
        vbox.addWidget(optbox)

        buttons = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel)
        buttons.accepted.connect(dialog.accept)
        buttons.rejected.connect(dialog.reject)
        vbox.addWidget(buttons)
        
        ok = dialog.exec_()
        if ok:
            roiname = optbox.option("name").value
            gridfrom = optbox.option("grid").value
            if not roiname or not gridfrom:
                raise QpException("Must provide a ROI name and a dataset to base it on")
            grid = self.ivm.data[gridfrom].grid
            roidata = np.zeros(grid.shape, dtype=np.int)
            self.ivm.add(NumpyData(roidata, grid=grid, roi=True, name=roiname), make_current=True)

            # Throw away old history. FIXME is this right, should we keep existing data and history?
            # Also should we cache old history in case we go back to this ROI?
            self._history = []
            self._undo_btn.setEnabled(False)
            self.options.option("roi").value = roiname
Esempio n. 23
0
 def set_data_name(self, name):
     """ Set the name of the data item being displayed """
     if name not in self.ivm.data:
         raise QpException("Data not found: %s" % name)
     else:
         idx = self.data_combo.findText(name)
         self.data_combo.setCurrentIndex(idx)
    def _add_activation_mask(self, struc, qpdata, strucs):
        """
        Add an activation mask

        This is a binary mask which splits a parent structure (e.g. GM) into two separate
        structures, inside and outside the mask, which can have different parameter properties

        :param struc: Structure dictionary. Must define the parent structure.
        :param qpdata: Activation mask map defined on same grid as existing data
        :param strucs: Dictionary of structure name to PV map for existing structures.
                       Will be updated to include split parent structure.
        """
        # Activation mask - replace parent structure
        parent_struc = struc.get("parent_struc", None)
        if parent_struc is None:
            raise QpException(
                "Parent structure not defined for activation mask: %s" %
                struc["name"])
        elif parent_struc not in strucs:
            raise QpException(
                "Parent structure '%s' not found in structures list for activation mask: %s"
                % (parent_struc, struc["name"]))
        activation_mask = np.copy(qpdata.raw())
        if "region" in struc:
            # If a specific region is given, isolate it
            activation_mask[activation_mask != struc["region"]] = 0

        if qpdata.roi:
            # If mask is an ROI, make it take values 0 and 1
            activation_mask[activation_mask <= 0] = 1
            activation_mask[activation_mask > 0] = 1

        # Activation structure takes over parent structure within the mask.
        # We do this by multiplication so the activation mask can in principle
        # be probabilistic
        activation_mask = activation_mask.astype(np.float32)
        parent_qpdata = strucs[parent_struc]
        parent_data = np.copy(parent_qpdata.raw())
        activation_data = np.copy(parent_qpdata.raw())
        activation_data *= activation_mask
        parent_data *= (1 - activation_mask)
        strucs[parent_struc] = NumpyData(parent_data,
                                         grid=parent_qpdata.grid,
                                         name=parent_qpdata.name)
        strucs[struc["name"]] = NumpyData(activation_data,
                                          grid=parent_qpdata.grid,
                                          name=struc["name"])
Esempio n. 25
0
    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)
Esempio n. 26
0
    def moco(cls, moco_data, ref, options, queue):
        """
        Motion correction
        
        The default implementation uses the ``reg_4d`` function to perform motion correction
        as registration to a common reference, however this function can have a custom
        implementation specific to motion correction if required.
        
        :param moco_data: A single 4D QpData 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 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 a QpData object containing 
                a sequence of 3 warp images or an Extra object containing a transformation matrix
                If ``output-transform`` is not given or not supported, returns None instead.

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

        log = "Default MOCO implementation using multiple 3d registrations\n"
        if isinstance(ref, int):
            if ref >= moco_data.nvols:
                raise QpException(
                    "Reference volume index of %i, but data has only %i volumes"
                    % (ref, moco_data.nvols))
            ref = moco_data.volume(ref)

        options["output-space"] = "reg"
        out_data, transforms, moco_log = cls.reg_4d(moco_data, ref, options,
                                                    queue)
        log += moco_log
        return out_data, transforms, log
Esempio n. 27
0
 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_()
Esempio n. 28
0
    def _get_cmdline(self, options):
        cmd = options.pop("cmd", None)
        if cmd is None:
            raise QpException("No command provided")

        cmdline = options.pop("cmdline", None)
        argdict = options.pop("argdict", {})
        if cmdline is None and not argdict:
            raise QpException("No command arguments provided")

        for arg, value in argdict.items():
            if value != "":
                cmdline += " --%s=%s" % (arg, value)
            else:
                cmdline += " --%s" % arg

        cmd = self._find(cmd)
        cmdline = cmd + " " + cmdline
        LOG.debug(cmdline)
        return cmdline
Esempio n. 29
0
    def run(self, options):
        """ Run the process """
        from oxasl import Workspace, calib
        from fsl.data.image import Image

        data = self.get_data(options)
        img = Image(data.raw(), name=data.name)

        roi = self.get_roi(options, data.grid)
        options["mask"] = Image(roi.raw(), name=roi.name)

        calib_name = options.pop("calib-data")
        if calib_name not in self.ivm.data:
            raise QpException("Calibration data not found: %s" % calib_name)
        else:
            calib_img = Image(self.ivm.data[calib_name].resample(
                data.grid).raw(),
                              name=calib_name)

        ref_roi_name = options.pop("ref-roi", None)
        if ref_roi_name is not None:
            if ref_roi_name not in self.ivm.rois:
                raise QpException("Reference ROI not found: %s" % calib_name)
            else:
                options["ref_mask"] = Image(
                    self.ivm.rois[ref_roi_name].resample(data.grid).raw(),
                    name=ref_roi_name)

        options["calib_method"] = options.pop("method", None)
        output_name = options.pop("output-name", data.name + "_calib")

        logbuf = six.StringIO()
        wsp = Workspace(log=logbuf, **options)
        wsp.calib = calib_img
        ## FIXME variance mode
        calibrated = calib.calibrate(wsp, img)
        self.log(logbuf.getvalue())
        self.ivm.add(name=output_name,
                     data=calibrated.data,
                     grid=data.grid,
                     make_current=True)
Esempio n. 30
0
def load(fname):
    """
    Load a data file

    :return: QpData instance
    """
    if os.path.isdir(fname):
        return DicomFolder(fname)
    elif fname.endswith(".nii") or fname.endswith(".nii.gz"):
        return NiftiData(fname)
    else:
        raise QpException("%s: Unrecognized file type" % fname)