def interface(self): grid = Tool.interface(self) grid.addWidget(QtGui.QLabel("Source data: "), 1, 0) self.ov_combo = OverlayCombo(self.ivm) grid.addWidget(self.ov_combo) grid.addWidget(QtGui.QLabel("Click to select points: "), 2, 0) self.pickmode_combo = QtGui.QComboBox() self.pickmode_combo.addItem("Inside the ROI") self.pickmode_combo.addItem("Outside the ROI") self.pickmode_combo.currentIndexChanged.connect(self._pick_mode_changed) grid.addWidget(self.pickmode_combo, 2, 1) grid.addWidget(QtGui.QLabel("Segmentation mode: "), 3, 0) self.segmode_combo = QtGui.QComboBox() self.segmode_combo.addItem("Slice") self.segmode_combo.addItem("3D") self.segmode_combo.currentIndexChanged.connect(self._seg_mode_changed) grid.addWidget(self.segmode_combo, 3, 1) self.beta = NumericOption("Diffusion difficulty", grid, 4, 0, intonly=True, maxval=20000, default=10000, step=1000) btn = QtGui.QPushButton("Segment") btn.clicked.connect(self._segment) grid.addWidget(btn, 5, 0) btn = QtGui.QPushButton("Clear points") btn.clicked.connect(self._init) grid.addWidget(btn, 5, 1) return grid
def __init__(self, ivm, parent=None, **kwargs): LogSource.__init__(self) QtGui.QWidget.__init__(self, parent) self.ivm = ivm self.data = None self.default_md = kwargs.get("default_metadata", DEFAULT_METADATA) self.md = dict(self.default_md) self.aslimage = None self.valid = True vbox = QtGui.QVBoxLayout() self.setLayout(vbox) grid = QtGui.QGridLayout() grid.addWidget(QtGui.QLabel("ASL data"), 0, 0) self.data_combo = OverlayCombo(self.ivm) self.data_combo.currentIndexChanged.connect(self._data_changed) grid.addWidget(self.data_combo, 0, 1) view_classes = [ LabelType, RepeatsChoice, NumPhases, NumEncodings, BlockFormat, DataStructure, SignalView, Labelling, Readout, SliceTime, Multiband, Times, BolusDurations, VariableRepeats ] self.views = [] for idx, view_class in enumerate(view_classes): if view_class in kwargs.get("ignore_views", ()): continue view = view_class(grid, ypos=idx + 2) view.ivm = self.ivm view.sig_md_changed.connect(self._metadata_changed) self.views.append(view) #grid.addWidget(QtGui.QLabel("Data order preview"), 5, 0) # Code below is for specific multiple phases #self.phases_lbl = QtGui.QLabel("Phases (\N{DEGREE SIGN})") #grid.addWidget(self.phases_lbl, 3, 0) #self.phases_lbl.setVisible(False) #self.phases = NumberList([float(x)*360/8 for x in range(8)]) #grid.addWidget(self.phases, 3, 1) #self.phases.setVisible(False) grid.setColumnStretch(0, 0) grid.setColumnStretch(1, 1) grid.setColumnStretch(2, 0) grid.setRowStretch(len(view_classes) + 2, 1) self.grid = grid vbox.addLayout(grid) self.warn_label = WarningBox() vbox.addWidget(self.warn_label) self._update_ui() self._data_changed()
class ChooseDataDialog(QtGui.QDialog): def __init__(self, parent, ivm, used=[]): super(ChooseDataDialog, self).__init__(parent) self.setWindowTitle("Add VFA data set") vbox = QtGui.QVBoxLayout() grid = QtGui.QGridLayout() grid.addWidget(QtGui.QLabel("Data set"), 0, 0) self.data_combo = OverlayCombo(ivm, static_only=True) # Default to first data which hasn't been used data = [ i for i in range(self.data_combo.count()) if self.data_combo.itemText(i) not in used ] if len(data) > 0: self.data_combo.setCurrentIndex(data[0]) self.data_combo.currentIndexChanged.connect(self._guess_fa) grid.addWidget(self.data_combo, 0, 1) grid.addWidget(QtGui.QLabel("Flip angle (\N{DEGREE SIGN})"), 1, 0) self.fa_edit = QtGui.QLineEdit() self.fa_edit.textChanged.connect(self._validate) grid.addWidget(self.fa_edit, 1, 1) vbox.addLayout(grid) self.buttonBox = QtGui.QDialogButtonBox( QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(False) vbox.addWidget(self.buttonBox) self.setLayout(vbox) self._guess_fa() self._validate() def _guess_fa(self): name = self.data_combo.currentText() m = re.search(r".*?(\d+).*$", name) if m is not None: guess = m.group(1) else: guess = "" self.fa_edit.setText(guess) def _validate(self): valid = True try: fa = float(self.fa_edit.text()) self.fa_edit.setStyleSheet("") except: self.fa_edit.setStyleSheet("QLineEdit {background-color: red}") valid = False self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(valid)
def __init__(self, ivl, view): QtGui.QGroupBox.__init__(self) self.ivl = ivl self.ivm = ivl.ivm self.view = view grid = QtGui.QGridLayout() grid.setVerticalSpacing(2) grid.setContentsMargins(5, 5, 5, 5) self.setLayout(grid) grid.addWidget(QtGui.QLabel("Overlay"), 0, 0) self.overlay_combo = OverlayCombo(self.ivm, none_option=True, set_first=False) grid.addWidget(self.overlay_combo, 0, 1) grid.addWidget(QtGui.QLabel("View"), 1, 0) self.ov_view_combo = QtGui.QComboBox() self.ov_view_combo.addItem("All") self.ov_view_combo.addItem("Only in ROI") self.ov_view_combo.addItem("None") grid.addWidget(self.ov_view_combo, 1, 1) grid.addWidget(QtGui.QLabel("Color map"), 2, 0) hbox = QtGui.QHBoxLayout() self.ov_cmap_combo = QtGui.QComboBox() self.ov_cmap_combo.addItem("jet") self.ov_cmap_combo.addItem("hot") self.ov_cmap_combo.addItem("gist_heat") self.ov_cmap_combo.addItem("flame") self.ov_cmap_combo.addItem("bipolar") self.ov_cmap_combo.addItem("spectrum") hbox.addWidget(self.ov_cmap_combo) self.ov_levels_btn = QtGui.QPushButton() self.ov_levels_btn.setIcon(QtGui.QIcon(get_icon("levels.png"))) self.ov_levels_btn.setFixedSize(16, 16) self.ov_levels_btn.setToolTip("Adjust colour map levels") self.ov_levels_btn.clicked.connect(self._show_ov_levels) self.ov_levels_btn.setEnabled(False) hbox.addWidget(self.ov_levels_btn) grid.addLayout(hbox, 2, 1) grid.addWidget(QtGui.QLabel("Alpha"), 3, 0) self.ov_alpha_sld = QtGui.QSlider(QtCore.Qt.Horizontal, self) self.ov_alpha_sld.setFocusPolicy(QtCore.Qt.NoFocus) self.ov_alpha_sld.setRange(0, 255) self.ov_alpha_sld.setValue(255) grid.addWidget(self.ov_alpha_sld, 3, 1) grid.setRowStretch(4, 1) self.overlay_combo.currentIndexChanged.connect(self._combo_changed) self.ov_view_combo.currentIndexChanged.connect(self._view_changed) self.ov_cmap_combo.currentIndexChanged.connect(self._cmap_changed) self.ov_alpha_sld.valueChanged.connect(self._alpha_changed) self.view.sig_view_changed.connect(self._update)
def __init__(self, **kwargs): super(MeanValuesWidget, self).__init__( name="Mean in ROI", icon="meanvals", desc="Replace data with its mean value within each ROI region", group="ROIs", **kwargs) layout = QtGui.QVBoxLayout() title = TitleWidget(self, "Generate Mean Values Data", help="mean_values") layout.addWidget(title) desc = QtGui.QLabel( "This widget will convert the current data set into a " "new data set in which each ROI region contains the mean " "value for that region.\n\nThis is generally only useful for " "multi-level ROIs such as clusters or supervoxels") desc.setWordWrap(True) layout.addWidget(desc) hbox = QtGui.QHBoxLayout() gbox = QtGui.QGroupBox() gbox.setTitle("Options") grid = QtGui.QGridLayout() gbox.setLayout(grid) grid.addWidget(QtGui.QLabel("Data"), 0, 0) self.ovl = OverlayCombo(self.ivm) self.ovl.currentIndexChanged.connect(self._data_changed) grid.addWidget(self.ovl, 0, 1) grid.addWidget(QtGui.QLabel("ROI regions"), 1, 0) self.roi = RoiCombo(self.ivm) grid.addWidget(self.roi, 1, 1) grid.addWidget(QtGui.QLabel("Output name"), 2, 0) self.output_name = QtGui.QLineEdit() grid.addWidget(self.output_name, 2, 1) btn = QtGui.QPushButton('Generate', self) btn.clicked.connect(self._generate) grid.addWidget(btn, 2, 0) hbox.addWidget(gbox) hbox.addStretch(1) layout.addLayout(hbox) layout.addStretch(1) self.setLayout(layout)
def init_ui(self): vbox = QtGui.QVBoxLayout() self.setLayout(vbox) title = TitleWidget(self, title="Data Smoothing", subtitle="Smooth data using a Gaussian kernel", help="smoothing") vbox.addWidget(title) hbox = QtGui.QHBoxLayout() gbox = QtGui.QGroupBox() gbox.setTitle("Options") grid = QtGui.QGridLayout() gbox.setLayout(grid) grid.addWidget(QtGui.QLabel("Data to smooth"), 0, 0) self.data_combo = OverlayCombo(self.ivm) self.data_combo.currentIndexChanged.connect(self.data_changed) grid.addWidget(self.data_combo, 0, 1) self.sigma = NumericOption("Sigma (mm)", grid, xpos=0, ypos=1, minval=0, step=0.1, default=1.0) grid.addWidget(QtGui.QLabel("Output name"), 2, 0) self.output_name = QtGui.QLineEdit() grid.addWidget(self.output_name, 2, 1) hbox.addWidget(gbox) hbox.addStretch(1) vbox.addLayout(hbox) hbox = QtGui.QHBoxLayout() self.run_btn = QtGui.QPushButton("Run") self.run_btn.clicked.connect(self.run) hbox.addWidget(self.run_btn) hbox.addStretch(1) vbox.addLayout(hbox) vbox.addStretch(1) self.data_changed()
class ImageOptionWidget(OptionWidget): """ OptionWidget subclass which allows image options to be chosen from the current list of overlays """ def __init__(self, opt, **kwargs): OptionWidget.__init__(self, opt, **kwargs) self.combo = OverlayCombo(self.ivm, static_only=True) self.widgets.append(self.combo) def get_value(self): return self.combo.currentText() def set_value(self, value): idx = self.combo.findText(value) self.combo.setCurrentIndex(idx) def add(self, grid, row): OptionWidget.add(self, grid, row) grid.addWidget(self.combo, row, 1)
def init_ui(self): layout = QtGui.QVBoxLayout() self.setLayout(layout) title = TitleWidget(self, "Supervoxel Generation", help="sv") layout.addWidget(title) cite = Citation(CITE_TITLE, CITE_AUTHOR, CITE_JOURNAL) layout.addWidget(cite) hbox = QtGui.QHBoxLayout() optbox = QtGui.QGroupBox() optbox.setTitle("Options") grid = QtGui.QGridLayout() optbox.setLayout(grid) grid.addWidget(QtGui.QLabel("Data"), 0, 0) self.ovl = OverlayCombo(self.ivm) self.ovl.currentIndexChanged.connect(self._data_changed) grid.addWidget(self.ovl, 0, 1) grid.addWidget(QtGui.QLabel("ROI"), 1, 0) self.roi = RoiCombo(self.ivm) grid.addWidget(self.roi, 1, 1) self.n_comp = NumericOption("Number of components", grid, 2, minval=1, maxval=3, default=3, intonly=True) self.compactness = NumericOption("Compactness", grid, 3, minval=0.01, maxval=1, step=0.05, default=0.1, intonly=False) self.sigma = NumericOption("Smoothing", grid, 4, minval=0, maxval=5, step=0.1, default=1, intonly=False) self.n_supervoxels = NumericOption("Number of supervoxels", grid, 5, minval=2, maxval=1000, default=20, intonly=True) grid.addWidget(QtGui.QLabel("Output name"), 6, 0) self.output_name = QtGui.QLineEdit("supervoxels") grid.addWidget(self.output_name, 6, 1) self.gen_btn = QtGui.QPushButton('Generate', self) self.gen_btn.clicked.connect(self._generate) grid.addWidget(self.gen_btn, 7, 0) hbox.addWidget(optbox) hbox.addStretch(1) layout.addLayout(hbox) layout.addStretch(1)
def _get_widgets(self, idx): prior_idx = self._get_prior_idx(self.params[idx]) ptype = self.rundata.get("PSP_byname%i_type" % prior_idx, "N") image = self.rundata.get("PSP_byname%i_image" % prior_idx, "") mean = self.rundata.get("PSP_byname%i_mean" % prior_idx, "") prec = self.rundata.get("PSP_byname%i_prec" % prior_idx, "") trans = self.rundata.get("PSP_byname%i_transform" % prior_idx, "") type_combo = QtGui.QComboBox() type_combo.addItem("Normal", "N") type_combo.addItem("Image Prior", "I") type_combo.addItem("Spatial prior, type M", "M") type_combo.addItem("Spatial prior , type P", "P") type_combo.addItem("Spatial prior, type m", "m") type_combo.addItem("Spatial prior , type p", "p") type_combo.addItem("ARD prior", "A") type_combo.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) type_combo.setCurrentIndex(type_combo.findData(ptype)) type_combo.currentIndexChanged.connect(self._changed) image_combo = OverlayCombo(self.ivm, static_only=True) image_combo.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) if image != "": image_combo.setCurrentIndex(image_combo.findText(image)) image_combo.currentIndexChanged.connect(self._changed) mean_cb = QtGui.QCheckBox() mean_cb.setChecked(ptype != "I" and mean != "") mean_cb.stateChanged.connect(self._changed) mean_edit = QtGui.QLineEdit(mean) mean_edit.editingFinished.connect(self._changed) prec_cb = QtGui.QCheckBox() prec_cb.setChecked(prec != "") prec_cb.stateChanged.connect(self._changed) prec_edit = QtGui.QLineEdit(prec) prec_edit.editingFinished.connect(self._changed) trans_combo = QtGui.QComboBox() trans_combo.addItem("Default transformation", "") trans_combo.addItem("No transformation", "I") trans_combo.addItem("Log transformation", "L") trans_combo.addItem("Soft-plus transformation", "S") trans_combo.addItem("Fractional transformation", "F") trans_combo.setSizeAdjustPolicy(QtGui.QComboBox.AdjustToContents) trans_combo.setCurrentIndex(trans_combo.findData(trans)) trans_combo.currentIndexChanged.connect(self._changed) return (type_combo, QtGui.QLabel("Image: "), image_combo, mean_cb, QtGui.QLabel("Custom mean: "), mean_edit, prec_cb, QtGui.QLabel("Custom precision: "), prec_edit, trans_combo)
def __init__(self, parent, ivm, used=[]): super(ChooseDataDialog, self).__init__(parent) self.setWindowTitle("Add VFA data set") vbox = QtGui.QVBoxLayout() grid = QtGui.QGridLayout() grid.addWidget(QtGui.QLabel("Data set"), 0, 0) self.data_combo = OverlayCombo(ivm, static_only=True) # Default to first data which hasn't been used data = [ i for i in range(self.data_combo.count()) if self.data_combo.itemText(i) not in used ] if len(data) > 0: self.data_combo.setCurrentIndex(data[0]) self.data_combo.currentIndexChanged.connect(self._guess_fa) grid.addWidget(self.data_combo, 0, 1) grid.addWidget(QtGui.QLabel("Flip angle (\N{DEGREE SIGN})"), 1, 0) self.fa_edit = QtGui.QLineEdit() self.fa_edit.textChanged.connect(self._validate) grid.addWidget(self.fa_edit, 1, 1) vbox.addLayout(grid) self.buttonBox = QtGui.QDialogButtonBox( QtGui.QDialogButtonBox.Ok | QtGui.QDialogButtonBox.Cancel) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) self.buttonBox.button(QtGui.QDialogButtonBox.Ok).setEnabled(False) vbox.addWidget(self.buttonBox) self.setLayout(vbox) self._guess_fa() self._validate()
class ClusteringWidget(QpWidget): """ Widget for doing K-means clustering on 3D or 4D data """ def __init__(self, **kwargs): super(ClusteringWidget, self).__init__(name="KMeans Clustering", icon="clustering", desc="Generate clusters from 3D or 4D data", group="Clustering", position=2, **kwargs) self.curves = {} def init_ui(self): self.process = KMeansProcess(self.ivm) layout = QtGui.QVBoxLayout() self.setLayout(layout) title = TitleWidget(self, help="cluster") layout.addWidget(title) DESC = """ <i>Performs clustering of 3D or 4D data using the K-means algorithm. PCA reduction is used on 4D data to extract representative curves """ desc = QtGui.QLabel(DESC) desc.setWordWrap(True) layout.addWidget(desc) gbox = QtGui.QGroupBox() gbox.setTitle('Clustering options') grid = QtGui.QGridLayout() gbox.setLayout(grid) # Data to cluster grid.addWidget(QtGui.QLabel("Data"), 0, 0) self.data_combo = OverlayCombo(self.ivm) self.data_combo.currentIndexChanged.connect(self._data_changed) grid.addWidget(self.data_combo, 0, 1) grid.addWidget(QtGui.QLabel("ROI"), 1, 0) self.roi_combo = RoiCombo(self.ivm, none_option=True) grid.addWidget(self.roi_combo, 1, 1) # Number of clusters inside the ROI self.n_clusters = NumericOption("Number of clusters", grid, xpos=2, ypos=0, minval=2, maxval=20, default=4, intonly=True) self.n_clusters.spin.setToolTip("") # Number of PCA modes self.n_pca = NumericOption("Number of PCA modes", grid, xpos=2, ypos=1, minval=1, maxval=10, default=3, intonly=True) self.n_pca.spin.setToolTip("") # Output ROI name grid.addWidget(QtGui.QLabel("Output name"), 2, 0) self.output_name = QtGui.QLineEdit("clusters") grid.addWidget(self.output_name, 2, 1) layout.addWidget(gbox) # Run clustering button hbox = QtGui.QHBoxLayout() self.run_btn = QtGui.QPushButton('Run', self) self.run_btn.clicked.connect(self.run_clustering) hbox.addWidget(self.run_btn) hbox.addStretch(1) layout.addLayout(hbox) # Plot window, showing representative curves for 4D data self.show_curves_btn = QtGui.QPushButton('Show representative curves', self) self.show_curves_btn.clicked.connect(self._show_curves) layout.addWidget(self.show_curves_btn) self.plotwin = pg.GraphicsLayoutWidget() self.plotwin.setBackground(background=None) self.plot = self.plotwin.addPlot(title="Cluster representative curves") self.plot.setLabel('left', "Signal Enhancement") self.plotwin.setVisible(False) layout.addWidget(self.plotwin) # Statistics self.show_count_btn = QtGui.QPushButton('Show voxel counts', self) self.show_count_btn.clicked.connect(self._show_counts) layout.addWidget(self.show_count_btn) self.stats_gbox = QtGui.QGroupBox() self.stats_gbox.setTitle('Voxel count') self.count_table = QtGui.QStandardItemModel() self.count_view = QtGui.QTableView() self.count_view.resizeColumnsToContents() self.count_view.setModel(self.count_table) hbox = QtGui.QHBoxLayout() hbox.addWidget(self.count_view) self.stats_gbox.setLayout(hbox) self.stats_gbox.setVisible(False) layout.addWidget(self.stats_gbox) # Merge regions self.show_merge_btn = QtGui.QPushButton('Show merge options', self) self.show_merge_btn.clicked.connect(self._show_merge) layout.addWidget(self.show_merge_btn) self.merge_gbox = QtGui.QGroupBox() self.merge_gbox.setTitle('Merge regions') vbox = QtGui.QVBoxLayout() self.merge_gbox.setLayout(vbox) hbox = QtGui.QHBoxLayout() self.merge_btn = QtGui.QPushButton('Merge', self) self.merge_btn.clicked.connect(self._run_merge) hbox.addWidget(self.merge_btn) hbox.addWidget(QtGui.QLabel('Merge region ')) self.merge_region1 = QtGui.QLineEdit('1', self) hbox.addWidget(self.merge_region1) hbox.addWidget(QtGui.QLabel(' with ')) self.merge_region2 = QtGui.QLineEdit('2', self) hbox.addWidget(self.merge_region2) vbox.addLayout(hbox) hbox = QtGui.QHBoxLayout() self.auto_merge_btn = QtGui.QPushButton('AutoMerge', self) self.auto_merge_btn.clicked.connect(self._run_automerge) hbox.addWidget(self.auto_merge_btn) hbox.addStretch(1) vbox.addLayout(hbox) self.merge_gbox.setVisible(False) layout.addWidget(self.merge_gbox) layout.addStretch(1) def activate(self): self.ivl.sig_focus_changed.connect(self._focus_changed) self.ivm.sig_current_roi.connect(self._current_roi_changed) self.ivm.sig_main_data.connect(self._main_data_changed) self._data_changed() def deactivate(self): self.ivl.sig_focus_changed.disconnect(self._focus_changed) self.ivm.sig_current_roi.disconnect(self._current_roi_changed) self.ivm.sig_main_data.disconnect(self._main_data_changed) def _current_roi_changed(self, roi): if roi is not None and roi.name != self.output_name.text(): self.roi_combo.setCurrentIndex(self.roi_combo.findText(roi.name)) def _main_data_changed(self, data): if data is not None: idx = self.data_combo.findText(data.name) else: idx = 0 self.data_combo.setCurrentIndex(idx) def _data_changed(self): data = self.ivm.data.get(self.data_combo.currentText(), None) if data is not None: is4d = data.nvols > 1 self.debug("Number of vols: %i, 4d=%s", data.nvols, is4d) self.n_pca.label.setVisible(is4d) self.n_pca.spin.setVisible(is4d) self.show_curves_btn.setVisible(is4d) self.plotwin.setVisible(is4d and self.plotwin.isVisible()) self.auto_merge_btn.setEnabled(is4d) self.run_btn.setEnabled(data is not None) def _focus_changed(self): self._update_voxel_count() def _show_curves(self): if self.plotwin.isVisible(): self.plotwin.setVisible(False) self.show_curves_btn.setText("Show representative curves") else: self.plotwin.setVisible(True) self.show_curves_btn.setText("Hide representative curves") def _show_counts(self): if self.stats_gbox.isVisible(): self.stats_gbox.setVisible(False) self.show_count_btn.setText("Show voxel counts") else: self.stats_gbox.setVisible(True) self.show_count_btn.setText("Hide voxel counts") def _show_merge(self): if self.merge_gbox.isVisible(): self.merge_gbox.setVisible(False) self.show_merge_btn.setText("Show merge_options") else: self.merge_gbox.setVisible(True) self.show_merge_btn.setText("Hide merge options") def batch_options(self): options = { "data": self.data_combo.currentText(), "roi": self.roi_combo.currentText(), "n-clusters": self.n_clusters.spin.value(), "output-name": self.output_name.text(), "invert-roi": False, } if options["roi"] == "<none>": del options["roi"] if self.n_pca.label.isVisible(): # 4D PCA options options["n-pca"] = self.n_pca.spin.value() options["reduction"] = "pca" options["norm-data"] = True return "KMeans", options def run_clustering(self): """ Run kmeans clustering using normalised PCA modes """ options = self.batch_options()[1] self.process.run(options) self._update_voxel_count() if self.n_pca.label.isVisible(): self.update_plot() def _update_voxel_count(self): self.count_table.clear() self.count_table.setVerticalHeaderItem( 0, QtGui.QStandardItem("Voxel count")) roi = self.ivm.rois.get(self.output_name.text(), None) if roi is not None: col_idx = 0 for region, name in roi.regions.items(): self.count_table.setHorizontalHeaderItem( col_idx, QtGui.QStandardItem(name)) # Volume count voxel_count = np.sum(roi.raw() == region) self.count_table.setItem( 0, col_idx, QtGui.QStandardItem(str(np.around(voxel_count)))) col_idx += 1 def update_plot(self): """ Plot the cluster curves :return: """ # Clear graph self.plot.clear() self.plot.setLabel('bottom', "Volume", "") if self.plot.legend is not None: # Work around pyqtgraph legend bug - the legend is recreated multiple times! # https://stackoverflow.com/questions/42792858/pyqtgraph-delete-persisting-legend-in-pyqt4-gui self.plot.legend.scene().removeItem(self.plot.legend) data = self.ivm.data.get(self.data_combo.currentText(), None) roi = self.ivm.rois.get(self.output_name.text(), None) if roi is not None and data is not None and data.nvols > 1: # Generate mean curve for each cluster self._generate_cluster_means(roi, data) self.plot.addLegend() # Plotting using single or multiple plots for region, name in roi.regions.items(): if np.sum(self.curves[region]) == 0: continue pencol = get_pencol(roi, region) curve = self.plot.plot(pen=pencol, width=8.0, name=name) xx = np.arange(len(self.curves[region])) curve.setData(xx, self.curves[region]) def _run_merge(self): m1 = int(self.merge_region1.text()) m2 = int(self.merge_region2.text()) self._merge(m1, m2) def _run_automerge(self): # Use PCA features or true curves? # Mean features from each cluster # Distance matrix between features if len(self.curves) < 2: return curvemat = np.zeros((len(self.curves), self.ivm.main.nvols)) row_region = {} idx = 0 for region, curve in self.curves.items(): row_region[idx] = region curvemat[idx, :] = curve idx += 1 distmat = pairwise.euclidean_distances(curvemat) distmat[distmat == 0] = np.inf loc1 = np.where(distmat == distmat.min())[0] self._merge(row_region[loc1[0]], row_region[loc1[1]]) def _generate_cluster_means(self, roi, data): """ Generate the mean curves for each cluster Returns: """ self.curves = {} for region in roi.regions: roi_data = data.mask(roi, region=region, output_flat=True) mean = np.median(roi_data, axis=0) self.curves[region] = mean def _merge(self, m1, m2): roi = self.ivm.rois.get(self.output_name.text(), None) if roi is not None: roi_data = roi.raw() roi_data[roi_data == m1] = m2 # signal the change self.ivm.add(NumpyData(roi_data, grid=roi.grid, name=self.output_name.text(), roi=True), make_current=True) # replot self.update_plot() self._update_voxel_count()
class SmoothingWidget(QpWidget): """ Gaussian smoothing widget """ def __init__(self, **kwargs): super(SmoothingWidget, self).__init__(name="Smoothing", icon="smooth.png", desc="Gaussian smoothing", group="Processing", **kwargs) def init_ui(self): vbox = QtGui.QVBoxLayout() self.setLayout(vbox) title = TitleWidget(self, title="Data Smoothing", subtitle="Smooth data using a Gaussian kernel", help="smoothing") vbox.addWidget(title) hbox = QtGui.QHBoxLayout() gbox = QtGui.QGroupBox() gbox.setTitle("Options") grid = QtGui.QGridLayout() gbox.setLayout(grid) grid.addWidget(QtGui.QLabel("Data to smooth"), 0, 0) self.data_combo = OverlayCombo(self.ivm) self.data_combo.currentIndexChanged.connect(self.data_changed) grid.addWidget(self.data_combo, 0, 1) self.sigma = NumericOption("Sigma (mm)", grid, xpos=0, ypos=1, minval=0, step=0.1, default=1.0) grid.addWidget(QtGui.QLabel("Output name"), 2, 0) self.output_name = QtGui.QLineEdit() grid.addWidget(self.output_name, 2, 1) hbox.addWidget(gbox) hbox.addStretch(1) vbox.addLayout(hbox) hbox = QtGui.QHBoxLayout() self.run_btn = QtGui.QPushButton("Run") self.run_btn.clicked.connect(self.run) hbox.addWidget(self.run_btn) hbox.addStretch(1) vbox.addLayout(hbox) vbox.addStretch(1) self.data_changed() def data_changed(self): self.output_name.setText("%s_smoothed" % self.data_combo.currentText()) self.run_btn.setEnabled(self.data_combo.currentText() in self.ivm.data) def batch_options(self): return "Smooth", { "data": self.data_combo.currentText(), "sigma": self.sigma.spin.value(), "output-name": self.output_name.text() } def run(self): process = SmoothingProcess(self.ivm) process.run(self.batch_options()[1])
def init_ui(self): vbox = QtGui.QVBoxLayout() self.setLayout(vbox) title = TitleWidget(self, batch_btn=False, help="compare") vbox.addWidget(title) hbox = QtGui.QHBoxLayout() hbox.addWidget(QtGui.QLabel("Compare ")) self.d1_combo = OverlayCombo(self.ivm) self.d1_combo.currentIndexChanged.connect(self._update_data) hbox.addWidget(self.d1_combo) hbox.addWidget(QtGui.QLabel(" with ")) self.d2_combo = OverlayCombo(self.ivm) self.d2_combo.currentIndexChanged.connect(self._update_data) hbox.addWidget(self.d2_combo) hbox.addStretch(1) vbox.addLayout(hbox) hbox = QtGui.QHBoxLayout() hbox.addWidget(QtGui.QLabel("Within ROI ")) self.roi_combo = OverlayCombo(self.ivm, rois=True, data=False, none_option=True) self.roi_combo.currentIndexChanged.connect(self._update_data) hbox.addWidget(self.roi_combo) hbox.addStretch(1) vbox.addLayout(hbox) hbox = QtGui.QHBoxLayout() gbox = QtGui.QGroupBox("Options") grid = QtGui.QGridLayout() gbox.setLayout(grid) self.id_cb = QtGui.QCheckBox("Include identity line") grid.addWidget(self.id_cb, 0, 0) self.sample_cb = QtGui.QCheckBox("Sample values") self.sample_cb.setChecked(True) self.sample_cb.stateChanged.connect(self._update_data) self.sample_cb.stateChanged.connect(self._update_gui) grid.addWidget(self.sample_cb, 1, 0) self.sample_spin = QtGui.QSpinBox() self.sample_spin.setMinimum(10) self.sample_spin.setMaximum(10000000) self.sample_spin.setSingleStep(100) self.sample_spin.setValue(1000) self.sample_spin.valueChanged.connect(self._update_data) grid.addWidget(self.sample_spin, 1, 1) self.warn_label = QtGui.QLabel( "WARNING: plotting all values may take a long time") self.warn_label.setStyleSheet("QLabel { color : red; }") self.warn_label.setVisible(False) grid.addWidget(self.warn_label, 2, 0) self.plot_mode = ChoiceOption("Plot mode", grid, 3, choices=["Scatter", "Heat map"]) self.plot_mode.combo.currentIndexChanged.connect( self._plot_mode_changed) self.bins = NumericOption("Bins", grid, 3, xpos=2, minval=20, default=50, intonly=True) self.bins.label.setVisible(False) self.bins.spin.setVisible(False) self.run_btn = QtGui.QPushButton("Update") self.run_btn.clicked.connect(self._run) grid.addWidget(self.run_btn, 4, 0) hbox.addWidget(gbox) hbox.addStretch(1) vbox.addLayout(hbox) hbox = QtGui.QHBoxLayout() win = pg.GraphicsLayoutWidget() win.setBackground(background=None) self.plot = win.addPlot(enableAutoRange=True) self.plot.clear() # Unsure why this is necessary hbox.addWidget(win) self.hist = pg.HistogramLUTWidget() self.hist.setVisible(False) self.hist.setBackground(None) self.hist.gradient.loadPreset("thermal") hbox.addWidget(self.hist) vbox.addLayout(hbox) self._update_gui() self._update_data()
class CompareDataWidget(QpWidget): """ Compare two data sets """ def __init__(self, **kwargs): QpWidget.__init__(self, name="Compare Data", icon="compare.png", desc="Compare two data sets", group="Visualisation", **kwargs) def init_ui(self): vbox = QtGui.QVBoxLayout() self.setLayout(vbox) title = TitleWidget(self, batch_btn=False, help="compare") vbox.addWidget(title) hbox = QtGui.QHBoxLayout() hbox.addWidget(QtGui.QLabel("Compare ")) self.d1_combo = OverlayCombo(self.ivm) self.d1_combo.currentIndexChanged.connect(self._update_data) hbox.addWidget(self.d1_combo) hbox.addWidget(QtGui.QLabel(" with ")) self.d2_combo = OverlayCombo(self.ivm) self.d2_combo.currentIndexChanged.connect(self._update_data) hbox.addWidget(self.d2_combo) hbox.addStretch(1) vbox.addLayout(hbox) hbox = QtGui.QHBoxLayout() hbox.addWidget(QtGui.QLabel("Within ROI ")) self.roi_combo = OverlayCombo(self.ivm, rois=True, data=False, none_option=True) self.roi_combo.currentIndexChanged.connect(self._update_data) hbox.addWidget(self.roi_combo) hbox.addStretch(1) vbox.addLayout(hbox) hbox = QtGui.QHBoxLayout() gbox = QtGui.QGroupBox("Options") grid = QtGui.QGridLayout() gbox.setLayout(grid) self.id_cb = QtGui.QCheckBox("Include identity line") grid.addWidget(self.id_cb, 0, 0) self.sample_cb = QtGui.QCheckBox("Sample values") self.sample_cb.setChecked(True) self.sample_cb.stateChanged.connect(self._update_data) self.sample_cb.stateChanged.connect(self._update_gui) grid.addWidget(self.sample_cb, 1, 0) self.sample_spin = QtGui.QSpinBox() self.sample_spin.setMinimum(10) self.sample_spin.setMaximum(10000000) self.sample_spin.setSingleStep(100) self.sample_spin.setValue(1000) self.sample_spin.valueChanged.connect(self._update_data) grid.addWidget(self.sample_spin, 1, 1) self.warn_label = QtGui.QLabel( "WARNING: plotting all values may take a long time") self.warn_label.setStyleSheet("QLabel { color : red; }") self.warn_label.setVisible(False) grid.addWidget(self.warn_label, 2, 0) self.plot_mode = ChoiceOption("Plot mode", grid, 3, choices=["Scatter", "Heat map"]) self.plot_mode.combo.currentIndexChanged.connect( self._plot_mode_changed) self.bins = NumericOption("Bins", grid, 3, xpos=2, minval=20, default=50, intonly=True) self.bins.label.setVisible(False) self.bins.spin.setVisible(False) self.run_btn = QtGui.QPushButton("Update") self.run_btn.clicked.connect(self._run) grid.addWidget(self.run_btn, 4, 0) hbox.addWidget(gbox) hbox.addStretch(1) vbox.addLayout(hbox) hbox = QtGui.QHBoxLayout() win = pg.GraphicsLayoutWidget() win.setBackground(background=None) self.plot = win.addPlot(enableAutoRange=True) self.plot.clear() # Unsure why this is necessary hbox.addWidget(win) self.hist = pg.HistogramLUTWidget() self.hist.setVisible(False) self.hist.setBackground(None) self.hist.gradient.loadPreset("thermal") hbox.addWidget(self.hist) vbox.addLayout(hbox) self._update_gui() self._update_data() def _plot_mode_changed(self, idx): self.bins.label.setVisible(idx == 1) self.bins.spin.setVisible(idx == 1) self.sample_cb.setChecked(idx == 0) self._update_gui() self._update_data() def _update_gui(self): self.sample_spin.setEnabled(self.sample_cb.isChecked()) def _update_data(self): name1 = self.d1_combo.currentText() name2 = self.d2_combo.currentText() roi_name = self.roi_combo.currentText() qpd1 = self.ivm.data.get(name1, None) qpd2 = self.ivm.data.get(name2, None) roi = self.ivm.rois.get(roi_name, None) if qpd1 is not None and qpd2 is not None: current_vol = self.ivl.focus()[3] d1 = qpd1.volume(current_vol) d2 = qpd2.resample(qpd1.grid).volume(current_vol) if roi is not None: roi_data = roi.resample(qpd1.grid).raw() d1 = d1[roi_data > 0] d2 = d2[roi_data > 0] else: d1 = d1.reshape(-1) d2 = d2.reshape(-1) if self.sample_cb.isChecked(): n_samples = self.sample_spin.value() idx = np.random.choice(np.arange(len(d1)), n_samples) d1 = np.take(d1, idx) d2 = np.take(d2, idx) self.warn_label.setVisible(False) else: # Warn about plotting all data in scatter mode self.warn_label.setVisible( self.plot_mode.combo.currentIndex() == 0) self.d1 = d1 self.d2 = d2 self.run_btn.setEnabled(True) else: self.run_btn.setEnabled(False) def _run(self): self.plot.clear() self.plot.setLabel('bottom', self.d1_combo.currentText()) self.plot.setLabel('left', self.d2_combo.currentText()) if self.plot_mode.combo.currentIndex() == 0: self.plot.setAspectLocked(False) self.hist.setVisible(False) self.plot.plot(self.d1, self.d2, pen=None, symbolBrush=(200, 200, 200), symbolPen='k', symbolSize=5.0) else: heatmap, xedges, yedges = np.histogram2d(self.d1, self.d2, bins=self.bins.value()) rect = QtCore.QRectF(xedges[0], yedges[0], xedges[-1] - xedges[0], yedges[-1] - yedges[0]) img = pg.ImageItem(heatmap) img.setRect(rect) #self.plot.setAspectLocked(True) self.hist.setVisible(True) self.hist.setImageItem(img) self.plot.addItem(img) if self.id_cb.isChecked(): real_min = max(self.d1.min(), self.d2.min()) real_max = min(self.d1.max(), self.d2.max()) pen = pg.mkPen((255, 255, 255), style=QtCore.Qt.DashLine) self.plot.plot([real_min, real_max], [real_min, real_max], pen=pen, width=2.0)
class FabberT1Widget(QpWidget): """ T1 from VFA images, using the Fabber process """ def __init__(self, **kwargs): QpWidget.__init__( self, name="Fabber T1", icon="t10_fabber", group="T1", desc="T1 mapping from VFA images using Bayesian inference", **kwargs) def init_ui(self): vbox = QtGui.QVBoxLayout() self.setLayout(vbox) try: self.FabberProcess = get_plugins("processes", "FabberProcess")[0] except: 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-t1", subtitle="T1 mapping from VFA images using the Fabber process %s" % __version__) vbox.addWidget(title) cite = Citation(FAB_CITE_TITLE, FAB_CITE_AUTHOR, FAB_CITE_JOURNAL) vbox.addWidget(cite) grid = QtGui.QGridLayout() self.multivol_choice = ChoiceOption( "VFA data in", grid, ypos=0, choices=["Single data set", "Multiple data sets"]) self.multivol_choice.combo.currentIndexChanged.connect(self.update_ui) self.multivol_label = QtGui.QLabel("VFA data set") grid.addWidget(self.multivol_label, 1, 0) self.multivol_combo = OverlayCombo(self.ivm) grid.addWidget(self.multivol_combo, 1, 1) self.multivol_fas_label = QtGui.QLabel("FAs (\N{DEGREE SIGN})") grid.addWidget(self.multivol_fas_label, 2, 0) self.multivol_fas = NumberList(initial=[ 1, ]) grid.addWidget(self.multivol_fas, 2, 1, 1, 2) self.singlevol_label = QtGui.QLabel("VFA data sets") grid.addWidget(self.singlevol_label, 3, 0) grid.setAlignment(self.singlevol_label, QtCore.Qt.AlignTop) self.singlevol_table = QtGui.QTableWidget() self.singlevol_table.setColumnCount(2) self.singlevol_table.setHorizontalHeaderLabels( ["Data set", "Flip angle"]) self.singlevol_table.setEditTriggers( QtGui.QAbstractItemView.NoEditTriggers) grid.addWidget(self.singlevol_table, 3, 1) hbox = QtGui.QHBoxLayout() self.singlevol_add = QtGui.QPushButton("Add") self.singlevol_add.clicked.connect(self.add_vol) hbox.addWidget(self.singlevol_add) self.singlevol_clear = QtGui.QPushButton("Clear") self.singlevol_clear.clicked.connect(self.clear_vols) hbox.addWidget(self.singlevol_clear) grid.addLayout(hbox, 4, 1) self.tr = NumericOption("TR (ms)", grid, ypos=5, default=4.108, minval=0, step=0.1, decimals=3) grid.setColumnStretch(3, 1) vbox.addLayout(grid) self.run = RunBox(self.get_process, self.get_rundata) vbox.addWidget(self.run) vbox.addStretch(1) self.update_ui() def update_ui(self): multivol = self.multivol_choice.combo.currentIndex() == 0 self.multivol_label.setVisible(multivol) self.multivol_combo.setVisible(multivol) self.multivol_fas_label.setVisible(multivol) self.multivol_fas.setVisible(multivol) self.singlevol_label.setVisible(not multivol) self.singlevol_table.setVisible(not multivol) self.singlevol_add.setVisible(not multivol) self.singlevol_clear.setVisible(not multivol) def add_vol(self): used = [ self.singlevol_table.item(i, 0).text() for i in range(self.singlevol_table.rowCount()) ] dlg = ChooseDataDialog(self, self.ivm, used) if dlg.exec_(): nrows = self.singlevol_table.rowCount() self.singlevol_table.setRowCount(nrows + 1) self.singlevol_table.setItem( nrows, 0, QtGui.QTableWidgetItem(dlg.data_combo.currentText())) self.singlevol_table.setItem( nrows, 1, QtGui.QTableWidgetItem(dlg.fa_edit.text())) def clear_vols(self): self.singlevol_table.setRowCount(0) def get_process(self): return self.FabberProcess(self.ivm) def batch_options(self): return "Fabber", self.get_rundata() 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
class PerfSlicWidget(QpWidget): """ Generates supervoxels using SLIC method """ def __init__(self, **kwargs): super(PerfSlicWidget, self).__init__(name="Super Voxels", icon="sv", desc="Generate supervoxel clusters", group="Clustering", **kwargs) def init_ui(self): layout = QtGui.QVBoxLayout() self.setLayout(layout) title = TitleWidget(self, "Supervoxel Generation", help="sv") layout.addWidget(title) cite = Citation(CITE_TITLE, CITE_AUTHOR, CITE_JOURNAL) layout.addWidget(cite) hbox = QtGui.QHBoxLayout() optbox = QtGui.QGroupBox() optbox.setTitle("Options") grid = QtGui.QGridLayout() optbox.setLayout(grid) grid.addWidget(QtGui.QLabel("Data"), 0, 0) self.ovl = OverlayCombo(self.ivm) self.ovl.currentIndexChanged.connect(self._data_changed) grid.addWidget(self.ovl, 0, 1) grid.addWidget(QtGui.QLabel("ROI"), 1, 0) self.roi = RoiCombo(self.ivm) grid.addWidget(self.roi, 1, 1) self.n_comp = NumericOption("Number of components", grid, 2, minval=1, maxval=3, default=3, intonly=True) self.compactness = NumericOption("Compactness", grid, 3, minval=0.01, maxval=1, step=0.05, default=0.1, intonly=False) self.sigma = NumericOption("Smoothing", grid, 4, minval=0, maxval=5, step=0.1, default=1, intonly=False) self.n_supervoxels = NumericOption("Number of supervoxels", grid, 5, minval=2, maxval=1000, default=20, intonly=True) grid.addWidget(QtGui.QLabel("Output name"), 6, 0) self.output_name = QtGui.QLineEdit("supervoxels") grid.addWidget(self.output_name, 6, 1) self.gen_btn = QtGui.QPushButton('Generate', self) self.gen_btn.clicked.connect(self._generate) grid.addWidget(self.gen_btn, 7, 0) hbox.addWidget(optbox) hbox.addStretch(1) layout.addLayout(hbox) layout.addStretch(1) def _data_changed(self, _): name = self.ovl.currentText() if name: ovl = self.ivm.data[name] self.n_comp.label.setVisible(ovl.nvols > 1) self.n_comp.spin.setVisible(ovl.nvols > 1) def batch_options(self): options = { "data" : self.ovl.currentText(), "roi" : self.roi.currentText(), "n-components" : self.n_comp.spin.value(), "compactness" : self.compactness.spin.value(), "sigma" : self.sigma.spin.value(), "n-supervoxels" : self.n_supervoxels.spin.value(), "output-name" : self.output_name.text() } return "Supervoxels", options def _generate(self): process = SupervoxelsProcess(self.ivm, sync=True) process.run(self.batch_options()[1])
def init_ui(self): self.process = KMeansProcess(self.ivm) layout = QtGui.QVBoxLayout() self.setLayout(layout) title = TitleWidget(self, help="cluster") layout.addWidget(title) DESC = """ <i>Performs clustering of 3D or 4D data using the K-means algorithm. PCA reduction is used on 4D data to extract representative curves """ desc = QtGui.QLabel(DESC) desc.setWordWrap(True) layout.addWidget(desc) gbox = QtGui.QGroupBox() gbox.setTitle('Clustering options') grid = QtGui.QGridLayout() gbox.setLayout(grid) # Data to cluster grid.addWidget(QtGui.QLabel("Data"), 0, 0) self.data_combo = OverlayCombo(self.ivm) self.data_combo.currentIndexChanged.connect(self._data_changed) grid.addWidget(self.data_combo, 0, 1) grid.addWidget(QtGui.QLabel("ROI"), 1, 0) self.roi_combo = RoiCombo(self.ivm, none_option=True) grid.addWidget(self.roi_combo, 1, 1) # Number of clusters inside the ROI self.n_clusters = NumericOption("Number of clusters", grid, xpos=2, ypos=0, minval=2, maxval=20, default=4, intonly=True) self.n_clusters.spin.setToolTip("") # Number of PCA modes self.n_pca = NumericOption("Number of PCA modes", grid, xpos=2, ypos=1, minval=1, maxval=10, default=3, intonly=True) self.n_pca.spin.setToolTip("") # Output ROI name grid.addWidget(QtGui.QLabel("Output name"), 2, 0) self.output_name = QtGui.QLineEdit("clusters") grid.addWidget(self.output_name, 2, 1) layout.addWidget(gbox) # Run clustering button hbox = QtGui.QHBoxLayout() self.run_btn = QtGui.QPushButton('Run', self) self.run_btn.clicked.connect(self.run_clustering) hbox.addWidget(self.run_btn) hbox.addStretch(1) layout.addLayout(hbox) # Plot window, showing representative curves for 4D data self.show_curves_btn = QtGui.QPushButton('Show representative curves', self) self.show_curves_btn.clicked.connect(self._show_curves) layout.addWidget(self.show_curves_btn) self.plotwin = pg.GraphicsLayoutWidget() self.plotwin.setBackground(background=None) self.plot = self.plotwin.addPlot(title="Cluster representative curves") self.plot.setLabel('left', "Signal Enhancement") self.plotwin.setVisible(False) layout.addWidget(self.plotwin) # Statistics self.show_count_btn = QtGui.QPushButton('Show voxel counts', self) self.show_count_btn.clicked.connect(self._show_counts) layout.addWidget(self.show_count_btn) self.stats_gbox = QtGui.QGroupBox() self.stats_gbox.setTitle('Voxel count') self.count_table = QtGui.QStandardItemModel() self.count_view = QtGui.QTableView() self.count_view.resizeColumnsToContents() self.count_view.setModel(self.count_table) hbox = QtGui.QHBoxLayout() hbox.addWidget(self.count_view) self.stats_gbox.setLayout(hbox) self.stats_gbox.setVisible(False) layout.addWidget(self.stats_gbox) # Merge regions self.show_merge_btn = QtGui.QPushButton('Show merge options', self) self.show_merge_btn.clicked.connect(self._show_merge) layout.addWidget(self.show_merge_btn) self.merge_gbox = QtGui.QGroupBox() self.merge_gbox.setTitle('Merge regions') vbox = QtGui.QVBoxLayout() self.merge_gbox.setLayout(vbox) hbox = QtGui.QHBoxLayout() self.merge_btn = QtGui.QPushButton('Merge', self) self.merge_btn.clicked.connect(self._run_merge) hbox.addWidget(self.merge_btn) hbox.addWidget(QtGui.QLabel('Merge region ')) self.merge_region1 = QtGui.QLineEdit('1', self) hbox.addWidget(self.merge_region1) hbox.addWidget(QtGui.QLabel(' with ')) self.merge_region2 = QtGui.QLineEdit('2', self) hbox.addWidget(self.merge_region2) vbox.addLayout(hbox) hbox = QtGui.QHBoxLayout() self.auto_merge_btn = QtGui.QPushButton('AutoMerge', self) self.auto_merge_btn.clicked.connect(self._run_automerge) hbox.addWidget(self.auto_merge_btn) hbox.addStretch(1) vbox.addLayout(hbox) self.merge_gbox.setVisible(False) layout.addWidget(self.merge_gbox) layout.addStretch(1)
class AslImageWidget(QtGui.QWidget, LogSource): """ QWidget which allows an ASL data set to be described This is intended to be embedded into a QpWidget which supports processing of ASL data """ # Signal emitted when the data or metadata is changed sig_changed = QtCore.Signal() def __init__(self, ivm, parent=None, **kwargs): LogSource.__init__(self) QtGui.QWidget.__init__(self, parent) self.ivm = ivm self.data = None self.default_md = kwargs.get("default_metadata", DEFAULT_METADATA) self.md = dict(self.default_md) self.aslimage = None self.valid = True vbox = QtGui.QVBoxLayout() self.setLayout(vbox) grid = QtGui.QGridLayout() grid.addWidget(QtGui.QLabel("ASL data"), 0, 0) self.data_combo = OverlayCombo(self.ivm) self.data_combo.currentIndexChanged.connect(self._data_changed) grid.addWidget(self.data_combo, 0, 1) view_classes = [ LabelType, RepeatsChoice, NumPhases, NumEncodings, BlockFormat, DataStructure, SignalView, Labelling, Readout, SliceTime, Multiband, Times, BolusDurations, VariableRepeats ] self.views = [] for idx, view_class in enumerate(view_classes): if view_class in kwargs.get("ignore_views", ()): continue view = view_class(grid, ypos=idx + 2) view.ivm = self.ivm view.sig_md_changed.connect(self._metadata_changed) self.views.append(view) #grid.addWidget(QtGui.QLabel("Data order preview"), 5, 0) # Code below is for specific multiple phases #self.phases_lbl = QtGui.QLabel("Phases (\N{DEGREE SIGN})") #grid.addWidget(self.phases_lbl, 3, 0) #self.phases_lbl.setVisible(False) #self.phases = NumberList([float(x)*360/8 for x in range(8)]) #grid.addWidget(self.phases, 3, 1) #self.phases.setVisible(False) grid.setColumnStretch(0, 0) grid.setColumnStretch(1, 1) grid.setColumnStretch(2, 0) grid.setRowStretch(len(view_classes) + 2, 1) self.grid = grid vbox.addLayout(grid) self.warn_label = WarningBox() vbox.addWidget(self.warn_label) self._update_ui() self._data_changed() 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 _data_changed(self): """ New data selected - load any previously defined metadata, and validate it """ self.data = self.ivm.data.get(self.data_combo.currentText(), None) for idx in range(self.grid.count()): if idx > 1: w = self.grid.itemAt(idx).widget() if w is not None: w.setEnabled(self.data is not None) if self.data is not None: self.md = self.data.metadata.get("AslData", None) if self.md is None: self.md = dict(self.default_md) if self.data.nvols % 2 == 0: # Even number of volumes - guess TC pairs # FIXME don't need block format for single TI self.md["iaf"] = "tc" self.md["ibf"] = "tis" for view in self.views: view.set_data(self.data, self.md) self._validate_metadata() self.sig_changed.emit() def _metadata_changed(self, sender): """ Called when a view has changed the metadata """ self.debug("Metadata changed %s", sender) if self.data is not None: self.debug("Current metadata: %s", self.md) self.debug("New metadata: %s", sender.md) self.md = sender.md self._validate_metadata() self._save_metadata() self.sig_changed.emit() def _save_metadata(self): """ Set the metadata on the dataset """ if self.data is not None: current_md = self.data.metadata.get("AslData", {}) self.debug("Save: Current metadata: %s", current_md) if self.md != current_md: self.debug("Different!") self.data.metadata["AslData"] = dict(self.md) self.debug("Saved: %s ", self.md) self._update_ui() def _update_ui(self, ignore=()): """ Update user interface from the current metadata """ try: for view in self.views: if view not in ignore: view.set_data(self.data, self.md) finally: self.updating_ui = False def _validate_metadata(self): """ Validate data against specified TIs, etc """ try: if self.md and self.data is not None: self.debug("Validating metadata: %s", str(self.md)) self.aslimage, _ = qpdata_to_aslimage(self.data, metadata=self.md) self.warn_label.clear() self.valid = True except ValueError as e: self.debug("Failed: %s", str(e)) self.aslimage = None self.warn_label.warn(str(e)) self.valid = False def get_options(self): """ Get batch options """ options = {} if self.data is not None: options["data"] = self.data.name options.update(self.md) return options
class WalkerTool(Tool): """ Tool which uses the random walker method to select a region FIXME this does not work properly in slice mode at the moment because the zaxis/pos given by the picker does not necessarily correspond to the data axes. """ def __init__(self): Tool.__init__(self, "Walker", "Automatic segmentation using the random walk algorithm") self.segmode = 0 self.pickmode = 0 def interface(self): grid = Tool.interface(self) grid.addWidget(QtGui.QLabel("Source data: "), 1, 0) self.ov_combo = OverlayCombo(self.ivm) grid.addWidget(self.ov_combo) grid.addWidget(QtGui.QLabel("Click to select points: "), 2, 0) self.pickmode_combo = QtGui.QComboBox() self.pickmode_combo.addItem("Inside the ROI") self.pickmode_combo.addItem("Outside the ROI") self.pickmode_combo.currentIndexChanged.connect(self._pick_mode_changed) grid.addWidget(self.pickmode_combo, 2, 1) grid.addWidget(QtGui.QLabel("Segmentation mode: "), 3, 0) self.segmode_combo = QtGui.QComboBox() self.segmode_combo.addItem("Slice") self.segmode_combo.addItem("3D") self.segmode_combo.currentIndexChanged.connect(self._seg_mode_changed) grid.addWidget(self.segmode_combo, 3, 1) self.beta = NumericOption("Diffusion difficulty", grid, 4, 0, intonly=True, maxval=20000, default=10000, step=1000) btn = QtGui.QPushButton("Segment") btn.clicked.connect(self._segment) grid.addWidget(btn, 5, 0) btn = QtGui.QPushButton("Clear points") btn.clicked.connect(self._init) grid.addWidget(btn, 5, 1) return grid def _pick_mode_changed(self, idx=None): self.pickmode = idx if self.pickmode == 0: self.ivl.picker.col = (255, 0, 0) else: self.ivl.picker.col = (255, 255, 255) def _seg_mode_changed(self, idx=None): self.segmode = idx self._init() def _init(self): if self.segmode == 1: self.ivl.set_picker(PickMode.MULTIPLE) else: self.ivl.set_picker(PickMode.SLICE_MULTIPLE) self.labels = np.zeros(self.builder.grid.shape, dtype=np.int) self._pick_mode_changed(self.pickmode) def selected(self): self.pickmode = 0 self.segmode = 0 self.ivl.sig_selection_changed.connect(self._points_changed) self._init() def deselected(self): self.ivl.sig_selection_changed.disconnect(self._points_changed) Tool.deselected(self) def _points_changed(self): for col, points in self.ivl.picker.selection(grid=self.builder.grid).items(): if col == ((255, 0, 0)): label = 1 else: label = 2 for pos in points: pos = [int(p+0.5) for p in pos] # Clamp points to within range of ROI for dim in range(3): pos[dim] = min(max(pos[dim], 0), self.labels.shape[dim]-1) self.labels[pos[0], pos[1], pos[2]] = label def _segment(self): data = self.ivm.data[self.ov_combo.currentText()].resample(self.builder.grid) labels = self.labels kwargs = {} # Use voxel size correctly spacing = [data.grid.spacing[0] / data.grid.spacing[0], data.grid.spacing[0] / data.grid.spacing[1], data.grid.spacing[0] / data.grid.spacing[2]] arr = data.raw() if arr.ndim > 3: # Reduce 4D data to PCA modes pca = PcaFeatReduce(n_components=5) arr = pca.get_training_features(arr, feature_volume=True) kwargs["multichannel"] = True else: # Normalize data arr = (arr / (np.max(arr)-np.min(arr))) + np.min(arr) kwargs["multichannel"] = False if self.segmode == 0: # Segment using 2D slice only zaxis = self.ivl.picker.zaxis zpos = int(self.ivl.picker.zpos + 0.5) #print(zaxis, zpos) sl = [slice(None)] * 3 sl[zaxis] = zpos arr = arr[sl] labels = self.labels[sl] del spacing[zaxis] #print(labels) #print(np.count_nonzero(labels)) #print(np.count_nonzero(arr)) seg = skimage.segmentation.random_walker(arr, labels, beta=self.beta.spin.value(), mode='cg_mg', spacing=spacing, **kwargs) if self.segmode == 0: # Create 3D volume using 2D slice seg_3d = np.zeros(self.builder.grid.shape, dtype=np.int) seg_3d[sl] = seg seg = seg_3d # Label 2 is used for 'outside region' seg[seg == 2] = 0 self.builder.modify(vol=seg, mode=self.builder.ADD) self._init()
class MeanValuesWidget(QpWidget): """ Convert a data + multi-level ROI into mean values data set """ def __init__(self, **kwargs): super(MeanValuesWidget, self).__init__( name="Mean in ROI", icon="meanvals", desc="Replace data with its mean value within each ROI region", group="ROIs", **kwargs) layout = QtGui.QVBoxLayout() title = TitleWidget(self, "Generate Mean Values Data", help="mean_values") layout.addWidget(title) desc = QtGui.QLabel( "This widget will convert the current data set into a " "new data set in which each ROI region contains the mean " "value for that region.\n\nThis is generally only useful for " "multi-level ROIs such as clusters or supervoxels") desc.setWordWrap(True) layout.addWidget(desc) hbox = QtGui.QHBoxLayout() gbox = QtGui.QGroupBox() gbox.setTitle("Options") grid = QtGui.QGridLayout() gbox.setLayout(grid) grid.addWidget(QtGui.QLabel("Data"), 0, 0) self.ovl = OverlayCombo(self.ivm) self.ovl.currentIndexChanged.connect(self._data_changed) grid.addWidget(self.ovl, 0, 1) grid.addWidget(QtGui.QLabel("ROI regions"), 1, 0) self.roi = RoiCombo(self.ivm) grid.addWidget(self.roi, 1, 1) grid.addWidget(QtGui.QLabel("Output name"), 2, 0) self.output_name = QtGui.QLineEdit() grid.addWidget(self.output_name, 2, 1) btn = QtGui.QPushButton('Generate', self) btn.clicked.connect(self._generate) grid.addWidget(btn, 2, 0) hbox.addWidget(gbox) hbox.addStretch(1) layout.addLayout(hbox) layout.addStretch(1) self.setLayout(layout) def _data_changed(self): name = self.ovl.currentText() if name: self.output_name.setText(name + "_means") def batch_options(self): options = { "roi": self.roi.currentText(), "data": self.ovl.currentText(), "output-name": self.output_name.text() } return "MeanValues", options def _generate(self): options = self.batch_options()[1] if not options["data"]: QtGui.QMessageBox.warning( self, "No data selected", "Load data to generate mean values from", QtGui.QMessageBox.Close) return if not options["roi"]: QtGui.QMessageBox.warning(self, "No ROI selected", "Load an ROI for mean value regions", QtGui.QMessageBox.Close) return process = MeanValuesProcess(self.ivm) process.run(options)
def init_ui(self): vbox = QtGui.QVBoxLayout() self.setLayout(vbox) try: self.FabberProcess = get_plugins("processes", "FabberProcess")[0] except: 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-t1", subtitle="T1 mapping from VFA images using the Fabber process %s" % __version__) vbox.addWidget(title) cite = Citation(FAB_CITE_TITLE, FAB_CITE_AUTHOR, FAB_CITE_JOURNAL) vbox.addWidget(cite) grid = QtGui.QGridLayout() self.multivol_choice = ChoiceOption( "VFA data in", grid, ypos=0, choices=["Single data set", "Multiple data sets"]) self.multivol_choice.combo.currentIndexChanged.connect(self.update_ui) self.multivol_label = QtGui.QLabel("VFA data set") grid.addWidget(self.multivol_label, 1, 0) self.multivol_combo = OverlayCombo(self.ivm) grid.addWidget(self.multivol_combo, 1, 1) self.multivol_fas_label = QtGui.QLabel("FAs (\N{DEGREE SIGN})") grid.addWidget(self.multivol_fas_label, 2, 0) self.multivol_fas = NumberList(initial=[ 1, ]) grid.addWidget(self.multivol_fas, 2, 1, 1, 2) self.singlevol_label = QtGui.QLabel("VFA data sets") grid.addWidget(self.singlevol_label, 3, 0) grid.setAlignment(self.singlevol_label, QtCore.Qt.AlignTop) self.singlevol_table = QtGui.QTableWidget() self.singlevol_table.setColumnCount(2) self.singlevol_table.setHorizontalHeaderLabels( ["Data set", "Flip angle"]) self.singlevol_table.setEditTriggers( QtGui.QAbstractItemView.NoEditTriggers) grid.addWidget(self.singlevol_table, 3, 1) hbox = QtGui.QHBoxLayout() self.singlevol_add = QtGui.QPushButton("Add") self.singlevol_add.clicked.connect(self.add_vol) hbox.addWidget(self.singlevol_add) self.singlevol_clear = QtGui.QPushButton("Clear") self.singlevol_clear.clicked.connect(self.clear_vols) hbox.addWidget(self.singlevol_clear) grid.addLayout(hbox, 4, 1) self.tr = NumericOption("TR (ms)", grid, ypos=5, default=4.108, minval=0, step=0.1, decimals=3) grid.setColumnStretch(3, 1) vbox.addLayout(grid) self.run = RunBox(self.get_process, self.get_rundata) vbox.addWidget(self.run) vbox.addStretch(1) self.update_ui()
def __init__(self, opt, **kwargs): OptionWidget.__init__(self, opt, **kwargs) self.combo = OverlayCombo(self.ivm, static_only=True) self.widgets.append(self.combo)
class OverlayViewWidget(QtGui.QGroupBox): """ Change view options for ROI """ def __init__(self, ivl, view): QtGui.QGroupBox.__init__(self) self.ivl = ivl self.ivm = ivl.ivm self.view = view grid = QtGui.QGridLayout() grid.setVerticalSpacing(2) grid.setContentsMargins(5, 5, 5, 5) self.setLayout(grid) grid.addWidget(QtGui.QLabel("Overlay"), 0, 0) self.overlay_combo = OverlayCombo(self.ivm, none_option=True, set_first=False) grid.addWidget(self.overlay_combo, 0, 1) grid.addWidget(QtGui.QLabel("View"), 1, 0) self.ov_view_combo = QtGui.QComboBox() self.ov_view_combo.addItem("All") self.ov_view_combo.addItem("Only in ROI") self.ov_view_combo.addItem("None") grid.addWidget(self.ov_view_combo, 1, 1) grid.addWidget(QtGui.QLabel("Color map"), 2, 0) hbox = QtGui.QHBoxLayout() self.ov_cmap_combo = QtGui.QComboBox() self.ov_cmap_combo.addItem("jet") self.ov_cmap_combo.addItem("hot") self.ov_cmap_combo.addItem("gist_heat") self.ov_cmap_combo.addItem("flame") self.ov_cmap_combo.addItem("bipolar") self.ov_cmap_combo.addItem("spectrum") hbox.addWidget(self.ov_cmap_combo) self.ov_levels_btn = QtGui.QPushButton() self.ov_levels_btn.setIcon(QtGui.QIcon(get_icon("levels.png"))) self.ov_levels_btn.setFixedSize(16, 16) self.ov_levels_btn.setToolTip("Adjust colour map levels") self.ov_levels_btn.clicked.connect(self._show_ov_levels) self.ov_levels_btn.setEnabled(False) hbox.addWidget(self.ov_levels_btn) grid.addLayout(hbox, 2, 1) grid.addWidget(QtGui.QLabel("Alpha"), 3, 0) self.ov_alpha_sld = QtGui.QSlider(QtCore.Qt.Horizontal, self) self.ov_alpha_sld.setFocusPolicy(QtCore.Qt.NoFocus) self.ov_alpha_sld.setRange(0, 255) self.ov_alpha_sld.setValue(255) grid.addWidget(self.ov_alpha_sld, 3, 1) grid.setRowStretch(4, 1) self.overlay_combo.currentIndexChanged.connect(self._combo_changed) self.ov_view_combo.currentIndexChanged.connect(self._view_changed) self.ov_cmap_combo.currentIndexChanged.connect(self._cmap_changed) self.ov_alpha_sld.valueChanged.connect(self._alpha_changed) self.view.sig_view_changed.connect(self._update) def _update(self, view): widgets = [ self.ov_view_combo, self.ov_cmap_combo, self.ov_alpha_sld, self.overlay_combo ] try: for widget in widgets: widget.blockSignals(True) if not view.get("visible"): self.ov_view_combo.setCurrentIndex(2) elif view.get("roi_only"): self.ov_view_combo.setCurrentIndex(1) else: self.ov_view_combo.setCurrentIndex(0) # 'Custom' only appears as a flag to indicate the user has messed with the # LUT using the histogram widget. Otherwise is is hidden cmap = view.get("cmap") if cmap == "custom": idx = self.ov_cmap_combo.findText("custom") if idx >= 0: self.ov_cmap_combo.setCurrentIndex(idx) else: self.ov_cmap_combo.addItem("custom") idx = self.ov_cmap_combo.findText("custom") self.ov_cmap_combo.setCurrentIndex(idx) else: idx = self.ov_cmap_combo.findText("custom") if idx >= 0: self.ov_cmap_combo.removeItem(idx) idx = self.ov_cmap_combo.findText(view.get("cmap")) self.ov_cmap_combo.setCurrentIndex(idx) self.ov_alpha_sld.setValue(view.get("alpha")) self.ov_levels_btn.setEnabled(view.data is not None) if view.data is not None: idx = self.overlay_combo.findText(view.data.name) self.overlay_combo.setCurrentIndex(idx) else: self.overlay_combo.setCurrentIndex(-1) except: import traceback traceback.print_exc() finally: for widget in widgets: widget.blockSignals(False) def _combo_changed(self, idx): if idx > 0: data_name = self.overlay_combo.itemText(idx) self.ivm.set_current_data(data_name) else: self.ivl.ivm.set_current_data(None) def _cmap_changed(self, idx): cmap = self.ov_cmap_combo.itemText(idx) self.view.set("cmap", cmap) def _view_changed(self, idx): """ Viewing style (all or within ROI only) changed """ self.view.set("visible", idx in (0, 1)) self.view.set("roi_only", (idx == 1)) def _alpha_changed(self, alpha): """ Set the data transparency """ self.view.set("alpha", alpha) def _show_ov_levels(self): dlg = LevelsDialog(self, self.ivl, self.ivm, self.view) dlg.exec_()