def setUp(self): super(TestScalarFieldView, self).setUp() self.scalarFieldView = ScalarFieldView() self.scalarFieldView.resize(300, 300) self.scalarFieldView.show() self.statsWidget = BasicStatsWidget() self.statsWidget.setPlot(self.scalarFieldView)
def __init__(self, parent=None): """ :param parent: Parent QWidget """ super(ArrayVolumePlot, self).__init__(parent) self.__signal = None self.__signal_name = None # the Z, Y, X axes apply to the last three dimensions of the signal # (in that order) self.__z_axis = None self.__z_axis_name = None self.__y_axis = None self.__y_axis_name = None self.__x_axis = None self.__x_axis_name = None from silx.gui.plot3d.ScalarFieldView import ScalarFieldView from silx.gui.plot3d import SFViewParamTree self._view = ScalarFieldView(self) def computeIsolevel(data): data = data[numpy.isfinite(data)] if len(data) == 0: return 0 else: return numpy.mean(data) + numpy.std(data) self._view.addIsosurface(computeIsolevel, '#FF0000FF') # Create a parameter tree for the scalar field view options = SFViewParamTree.TreeView(self._view) options.setSfView(self._view) # Add the parameter tree to the main window in a dock widget dock = qt.QDockWidget() dock.setWidget(options) self._view.addDockWidget(qt.Qt.RightDockWidgetArea, dock) self._hline = qt.QFrame(self) self._hline.setFrameStyle(qt.QFrame.HLine) self._hline.setFrameShadow(qt.QFrame.Sunken) self._legend = qt.QLabel(self) self._selector = NumpyAxesSelector(self) self._selector.setNamedAxesSelectorVisibility(False) self.__selector_is_connected = False layout = qt.QVBoxLayout() layout.addWidget(self._view) layout.addWidget(self._hline) layout.addWidget(self._legend) layout.addWidget(self._selector) self.setLayout(layout)
def setUp(self): super(TestScalarFieldView, self).setUp() self.widget = ScalarFieldView() self.widget.show() paramTreeWidget = TreeView() paramTreeWidget.setSfView(self.widget) dock = qt.QDockWidget() dock.setWidget(paramTreeWidget) self.widget.addDockWidget(qt.Qt.BottomDockWidgetArea, dock)
def setUp(self): super(TestScalarFieldView, self).setUp() self.widget = ScalarFieldView() self.widget.show() # Create a parameter tree for the scalar field view self.treeView = TreeView() self.treeView.setSfView( self.widget) # Attach the parameter tree to the view self.treeView.show() # Add the parameter tree to the main window in a dock widget dock = qt.QDockWidget() dock.setWindowTitle('Parameters') dock.setWidget(self.treeView) self.widget.addDockWidget(qt.Qt.RightDockWidgetArea, dock)
class TestScalarFieldView(TestCaseQt, ParametricTestCase): """Tests of ScalarFieldView widget.""" def setUp(self): super(TestScalarFieldView, self).setUp() self.widget = ScalarFieldView() self.widget.show() paramTreeWidget = TreeView() paramTreeWidget.setSfView(self.widget) dock = qt.QDockWidget() dock.setWidget(paramTreeWidget) self.widget.addDockWidget(qt.Qt.BottomDockWidgetArea, dock) # Commented as it slows down the tests # self.qWaitForWindowExposed(self.widget) def tearDown(self): self.qapp.processEvents() self.widget.setAttribute(qt.Qt.WA_DeleteOnClose) self.widget.close() del self.widget super(TestScalarFieldView, self).tearDown() @staticmethod def _buildData(size): """Make a 3D dataset""" coords = numpy.linspace(-10, 10, size) z = coords.reshape(-1, 1, 1) y = coords.reshape(1, -1, 1) x = coords.reshape(1, 1, -1) return numpy.sin(x * y * z) / (x * y * z) def testSimple(self): """Set the data and an isosurface""" data = self._buildData(size=32) self.widget.setData(data) self.widget.addIsosurface(0.5, (1., 0., 0., 0.5)) self.widget.addIsosurface(0.7, qt.QColor('green')) self.qapp.processEvents() def testNotFinite(self): """Test with NaN and inf in data set""" # Some NaNs and inf data = self._buildData(size=32) data[8, :, :] = numpy.nan data[16, :, :] = numpy.inf data[24, :, :] = - numpy.inf self.widget.addIsosurface(0.5, 'red') self.widget.setData(data, copy=True) self.qapp.processEvents() self.widget.setData(None) # All NaNs or inf data = numpy.empty((4, 4, 4), dtype=numpy.float32) for value in (numpy.nan, numpy.inf): with self.subTest(value=str(value)): data[:] = value self.widget.setData(data, copy=True) self.qapp.processEvents() def testIsoSliderNormalization(self): """Test set TreeView with a different isoslider normalization""" data = self._buildData(size=32) self.widget.setData(data) self.widget.addIsosurface(0.5, (1., 0., 0., 0.5)) self.widget.addIsosurface(0.7, qt.QColor('green')) self.qapp.processEvents() # Add a second TreeView paramTreeWidget = TreeView(self.widget) paramTreeWidget.setIsoLevelSliderNormalization('arcsinh') paramTreeWidget.setSfView(self.widget) dock = qt.QDockWidget() dock.setWidget(paramTreeWidget) self.widget.addDockWidget(qt.Qt.BottomDockWidgetArea, dock)
class TestScalarFieldView(TestCaseQt): """Tests StatsWidget combined with ScalarFieldView""" def setUp(self): super(TestScalarFieldView, self).setUp() self.scalarFieldView = ScalarFieldView() self.scalarFieldView.resize(300, 300) self.scalarFieldView.show() self.statsWidget = BasicStatsWidget() self.statsWidget.setPlot(self.scalarFieldView) # self.qWaitForWindowExposed(self.sceneWidget) def tearDown(self): Stats._getContext.cache_clear() self.qapp.processEvents() self.scalarFieldView.setAttribute(qt.Qt.WA_DeleteOnClose) self.scalarFieldView.close() del self.scalarFieldView self.statsWidget.setAttribute(qt.Qt.WA_DeleteOnClose) self.statsWidget.close() del self.statsWidget super(TestScalarFieldView, self).tearDown() def _getTextFor(self, row, name): """Returns text in table at given row for column name :param int row: Row number in the table :param str name: Column id :rtype: Union[str,None] """ statsTable = self.statsWidget._getStatsTable() for column in range(statsTable.columnCount()): headerItem = statsTable.horizontalHeaderItem(column) if headerItem.data(qt.Qt.UserRole) == name: tableItem = statsTable.item(row, column) return tableItem.text() return None def test(self): """Test StatsWidget with ScalarFieldView""" data = numpy.arange(64**3, dtype=numpy.float64).reshape(64, 64, 64) self.scalarFieldView.setData(data) statsTable = self.statsWidget._getStatsTable() # Test selection only self.statsWidget.setDisplayOnlyActiveItem(True) self.assertEqual(statsTable.rowCount(), 1) # Test all data self.statsWidget.setDisplayOnlyActiveItem(False) self.assertEqual(statsTable.rowCount(), 1) for column in range(statsTable.columnCount()): self.assertEqual(float(self._getTextFor(0, 'min')), numpy.min(data)) self.assertEqual(float(self._getTextFor(0, 'max')), numpy.max(data)) sum_ = numpy.sum(data) comz = numpy.sum( numpy.arange(data.shape[0]) * numpy.sum(data, axis=(1, 2))) / sum_ comy = numpy.sum( numpy.arange(data.shape[1]) * numpy.sum(data, axis=(0, 2))) / sum_ comx = numpy.sum( numpy.arange(data.shape[2]) * numpy.sum(data, axis=(0, 1))) / sum_ self.assertEqual(self._getTextFor(0, 'COM'), str( (comx, comy, comz)))
class ArrayVolumePlot(qt.QWidget): """ Widget for plotting a n-D array (n >= 3) as a 3D scalar field. Three axis arrays can be provided to calibrate the axes. The signal array can have an arbitrary number of dimensions, the only limitation being that the last 3 dimensions must have the same length as the axes arrays. Sliders are provided to select indices on the first (n - 3) dimensions of the signal array, and the plot is updated to load the stack corresponding to the selection. """ def __init__(self, parent=None): """ :param parent: Parent QWidget """ super(ArrayVolumePlot, self).__init__(parent) self.__signal = None self.__signal_name = None # the Z, Y, X axes apply to the last three dimensions of the signal # (in that order) self.__z_axis = None self.__z_axis_name = None self.__y_axis = None self.__y_axis_name = None self.__x_axis = None self.__x_axis_name = None from silx.gui.plot3d.ScalarFieldView import ScalarFieldView from silx.gui.plot3d import SFViewParamTree self._view = ScalarFieldView(self) def computeIsolevel(data): data = data[numpy.isfinite(data)] if len(data) == 0: return 0 else: return numpy.mean(data) + numpy.std(data) self._view.addIsosurface(computeIsolevel, '#FF0000FF') # Create a parameter tree for the scalar field view options = SFViewParamTree.TreeView(self._view) options.setSfView(self._view) # Add the parameter tree to the main window in a dock widget dock = qt.QDockWidget() dock.setWidget(options) self._view.addDockWidget(qt.Qt.RightDockWidgetArea, dock) self._hline = qt.QFrame(self) self._hline.setFrameStyle(qt.QFrame.HLine) self._hline.setFrameShadow(qt.QFrame.Sunken) self._legend = qt.QLabel(self) self._selector = NumpyAxesSelector(self) self._selector.setNamedAxesSelectorVisibility(False) self.__selector_is_connected = False layout = qt.QVBoxLayout() layout.addWidget(self._view) layout.addWidget(self._hline) layout.addWidget(self._legend) layout.addWidget(self._selector) self.setLayout(layout) def getVolumeView(self): """Returns the plot used for the display :rtype: ScalarFieldView """ return self._view def normalizeComplexData(self, data): """ Converts a complex data array to its amplitude, if necessary. :param data: the data to normalize :return: """ if hasattr(data, "dtype"): isComplex = numpy.issubdtype(data.dtype, numpy.complexfloating) else: isComplex = isinstance(data, numbers.Complex) if isComplex: data = numpy.absolute(data) return data def setData(self, signal, x_axis=None, y_axis=None, z_axis=None, signal_name=None, xlabel=None, ylabel=None, zlabel=None, title=None): """ :param signal: n-D dataset, whose last 3 dimensions are used as the 3D stack values. :param x_axis: 1-D dataset used as the image's x coordinates. If provided, its lengths must be equal to the length of the last dimension of ``signal``. :param y_axis: 1-D dataset used as the image's y. If provided, its lengths must be equal to the length of the 2nd to last dimension of ``signal``. :param z_axis: 1-D dataset used as the image's z. If provided, its lengths must be equal to the length of the 3rd to last dimension of ``signal``. :param signal_name: Label used in the legend :param xlabel: Label for X axis :param ylabel: Label for Y axis :param zlabel: Label for Z axis :param title: Graph title """ signal = self.normalizeComplexData(signal) if self.__selector_is_connected: self._selector.selectionChanged.disconnect(self._updateVolume) self.__selector_is_connected = False self.__signal = signal self.__signal_name = signal_name or "" self.__x_axis = x_axis self.__x_axis_name = xlabel self.__y_axis = y_axis self.__y_axis_name = ylabel self.__z_axis = z_axis self.__z_axis_name = zlabel self._selector.setData(signal) self._selector.setAxisNames(["Y", "X", "Z"]) self._view.setAxesLabels(self.__x_axis_name or 'X', self.__y_axis_name or 'Y', self.__z_axis_name or 'Z') self._updateVolume() # the legend label shows the selection slice producing the volume # (only interesting for ndim > 3) if signal.ndim > 3: self._selector.setVisible(True) self._legend.setVisible(True) self._hline.setVisible(True) else: self._selector.setVisible(False) self._legend.setVisible(False) self._hline.setVisible(False) if not self.__selector_is_connected: self._selector.selectionChanged.connect(self._updateVolume) self.__selector_is_connected = True def _updateVolume(self): """Update displayed stack according to the current axes selector data.""" data = self._selector.selectedData() x_axis = self.__x_axis y_axis = self.__y_axis z_axis = self.__z_axis offset = [] scale = [] for axis in [x_axis, y_axis, z_axis]: if axis is None: calibration = NoCalibration() elif len(axis) == 2: calibration = LinearCalibration(y_intercept=axis[0], slope=axis[1]) else: calibration = ArrayCalibration(axis) if not calibration.is_affine(): _logger.warning("Axis has not linear values, ignored") offset.append(0.) scale.append(1.) else: offset.append(calibration(0)) scale.append(calibration.get_slope()) legend = self.__signal_name + "[" for sl in self._selector.selection(): if sl == slice(None): legend += ":, " else: legend += str(sl) + ", " legend = legend[:-2] + "]" self._legend.setText("Displayed data: " + legend) self._view.setData(data, copy=False) self._view.setScale(*scale) self._view.setTranslation(*offset) self._view.setAxesLabels(self.__x_axis_name, self.__y_axis_name, self.__z_axis_name) def clear(self): old = self._selector.blockSignals(True) self._selector.clear() self._selector.blockSignals(old) self._view.setData(None)
Pjlk[:, :, k] = np.roll(Pjlk[:, :, k], prerolls[k], axis=1) # load the truths dct = np.load('data.npz') phi = dct['rolls'] theta = dct['offsets'] # make some plots fig, ax = plt.subplots(nrows=2, ncols=2) ax[0, 0].plot(theta) ax[0, 1].plot(phi) ax[1, 0].imshow(np.flip(Pjlk.sum(axis=1), axis=0), aspect='auto', vmax=.5) ax[1, 1].imshow(Pjlk.sum(axis=0), aspect='auto', vmax=.5) # show the intensity app = qt.QApplication([]) window = ScalarFieldView() window.setData(W[6:-6]) window.setScale(1, 1, 1) # voxel sizes window.addIsosurface(W.max() / 500, '#FF0000AA') cut = CutPlane(window) cut.setVisible(True) cut.setPoint((20, 20, 20), constraint=False) cut.setNormal((1, 0, 0)) cut.moveToCenter() print(cut.isValid()) window.show() app.exec_()
It also support nD data set (n>=3) stored in a HDF5 file. For HDF5, provide the filename and path as: <filename>::<path_in_file>. If the data set has more than 3 dimensions, it is possible to choose a 3D data set as a subset by providing the indices along the first n-3 dimensions with '#': <filename>::<path_in_file>#<1st_dim_index>...#<n-3th_dim_index> E.g.: data.h5::/data_5D#1#1 """) args = parser.parse_args(args=sys.argv[1:]) # Start GUI app = qt.QApplication([]) # Create the viewer main window window = ScalarFieldView() # Create a parameter tree for the scalar field view treeView = SFViewParamTree.TreeView(window) treeView.setSfView(window) # Attach the parameter tree to the view # Add the parameter tree to the main window in a dock widget dock = qt.QDockWidget() dock.setWindowTitle('Parameters') dock.setWidget(treeView) window.addDockWidget(qt.Qt.RightDockWidgetArea, dock) # Load data from file if args.filename is not None: data = load(args.filename) _logger.info('Data:\n\tShape: %s\n\tRange: [%f, %f]', str(data.shape),
def main(argv=None): # Parse input arguments parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('-l', '--level', nargs='?', type=float, default=float('nan'), help="The value at which to generate the iso-surface") parser.add_argument('-sx', '--xscale', nargs='?', type=float, default=1., help="The scale of the data on the X axis") parser.add_argument('-sy', '--yscale', nargs='?', type=float, default=1., help="The scale of the data on the Y axis") parser.add_argument('-sz', '--zscale', nargs='?', type=float, default=1., help="The scale of the data on the Z axis") parser.add_argument('-ox', '--xoffset', nargs='?', type=float, default=0., help="The offset of the data on the X axis") parser.add_argument('-oy', '--yoffset', nargs='?', type=float, default=0., help="The offset of the data on the Y axis") parser.add_argument('-oz', '--zoffset', nargs='?', type=float, default=0., help="The offset of the data on the Z axis") parser.add_argument('filename', nargs='?', default=None, help="""Filename to open. It supports 3D volume saved as .npy or in .h5 files. It also support nD data set (n>=3) stored in a HDF5 file. For HDF5, provide the filename and path as: <filename>::<path_in_file>. If the data set has more than 3 dimensions, it is possible to choose a 3D data set as a subset by providing the indices along the first n-3 dimensions with '#': <filename>::<path_in_file>#<1st_dim_index>...#<n-3th_dim_index> E.g.: data.h5::/data_5D#1#1 """) args = parser.parse_args(args=argv) # Start GUI global app # QApplication must be global to avoid seg fault on quit app = qt.QApplication([]) # Create the viewer main window window = ScalarFieldView() window.setAttribute(qt.Qt.WA_DeleteOnClose) # Create a parameter tree for the scalar field view treeView = SFViewParamTree.TreeView(window) treeView.setSfView(window) # Attach the parameter tree to the view # Add the parameter tree to the main window in a dock widget dock = qt.QDockWidget() dock.setWindowTitle('Parameters') dock.setWidget(treeView) window.addDockWidget(qt.Qt.RightDockWidgetArea, dock) # Load data from file if args.filename is not None: data = load(args.filename) _logger.info('Data:\n\tShape: %s\n\tRange: [%f, %f]', str(data.shape), data.min(), data.max()) else: # Create dummy data _logger.warning('Not data file provided, creating dummy data') coords = numpy.linspace(-10, 10, 64) z = coords.reshape(-1, 1, 1) y = coords.reshape(1, -1, 1) x = coords.reshape(1, 1, -1) data = numpy.sin(x * y * z) / (x * y * z) # Set ScalarFieldView data window.setData(data) # Set scale of the data window.setScale(args.xscale, args.yscale, args.zscale) # Set offset of the data window.setTranslation(args.xoffset, args.yoffset, args.zoffset) # Set axes labels window.setAxesLabels('X', 'Y', 'Z') # Add an iso-surface if not numpy.isnan(args.level): # Add an iso-surface at the given iso-level window.addIsosurface(args.level, '#FF0000FF') else: # Add an iso-surface from a function window.addIsosurface(default_isolevel, '#FF0000FF') window.show() return app.exec_()
class TestScalarFieldView(TestCaseQt): """Tests StatsWidget combined with ScalarFieldView""" def setUp(self): super(TestScalarFieldView, self).setUp() self.scalarFieldView = ScalarFieldView() self.scalarFieldView.resize(300, 300) self.scalarFieldView.show() self.statsWidget = BasicStatsWidget() self.statsWidget.setPlot(self.scalarFieldView) # self.qWaitForWindowExposed(self.sceneWidget) def tearDown(self): self.qapp.processEvents() self.scalarFieldView.setAttribute(qt.Qt.WA_DeleteOnClose) self.scalarFieldView.close() del self.scalarFieldView self.statsWidget.setAttribute(qt.Qt.WA_DeleteOnClose) self.statsWidget.close() del self.statsWidget super(TestScalarFieldView, self).tearDown() def _getTextFor(self, row, name): """Returns text in table at given row for column name :param int row: Row number in the table :param str name: Column id :rtype: Union[str,None] """ statsTable = self.statsWidget._getStatsTable() for column in range(statsTable.columnCount()): headerItem = statsTable.horizontalHeaderItem(column) if headerItem.data(qt.Qt.UserRole) == name: tableItem = statsTable.item(row, column) return tableItem.text() return None def test(self): """Test StatsWidget with ScalarFieldView""" data = numpy.arange(64**3, dtype=numpy.float64).reshape(64, 64, 64) self.scalarFieldView.setData(data) statsTable = self.statsWidget._getStatsTable() # Test selection only self.statsWidget.setDisplayOnlyActiveItem(True) self.assertEqual(statsTable.rowCount(), 1) # Test all data self.statsWidget.setDisplayOnlyActiveItem(False) self.assertEqual(statsTable.rowCount(), 1) for column in range(statsTable.columnCount()): self.assertEqual(float(self._getTextFor(0, 'min')), numpy.min(data)) self.assertEqual(float(self._getTextFor(0, 'max')), numpy.max(data)) sum_ = numpy.sum(data) comz = numpy.sum(numpy.arange(data.shape[0]) * numpy.sum(data, axis=(1, 2))) / sum_ comy = numpy.sum(numpy.arange(data.shape[1]) * numpy.sum(data, axis=(0, 2))) / sum_ comx = numpy.sum(numpy.arange(data.shape[2]) * numpy.sum(data, axis=(0, 1))) / sum_ self.assertEqual(self._getTextFor(0, 'COM'), str((comx, comy, comz)))
It also support nD data set (n>=3) stored in a HDF5 file. For HDF5, provide the filename and path as: <filename>::<path_in_file>. If the data set has more than 3 dimensions, it is possible to choose a 3D data set as a subset by providing the indices along the first n-3 dimensions with '#': <filename>::<path_in_file>#<1st_dim_index>...#<n-3th_dim_index> E.g.: data.h5::/data_5D#1#1 """) args = parser.parse_args(args=sys.argv[1:]) # Start GUI app = qt.QApplication([]) # Create the viewer main window window = ScalarFieldView() # Create a parameter tree for the scalar field view treeView = SFViewParamTree.TreeView(window) treeView.setSfView(window) # Attach the parameter tree to the view # Add the parameter tree to the main window in a dock widget dock = qt.QDockWidget() dock.setWindowTitle('Parameters') dock.setWidget(treeView) window.addDockWidget(qt.Qt.RightDockWidgetArea, dock) # Load data from file if args.filename is not None: data = load(args.filename) _logger.info('Data:\n\tShape: %s\n\tRange: [%f, %f]',
class TestScalarFieldView(TestCaseQt, ParametricTestCase): """Tests of ScalarFieldView widget.""" def setUp(self): super(TestScalarFieldView, self).setUp() self.widget = ScalarFieldView() self.widget.show() # Commented as it slows down the tests # self.qWaitForWindowExposed(self.widget) def tearDown(self): self.qapp.processEvents() self.widget.setAttribute(qt.Qt.WA_DeleteOnClose) self.widget.close() del self.widget super(TestScalarFieldView, self).tearDown() @staticmethod def _buildData(size): """Make a 3D dataset""" coords = numpy.linspace(-10, 10, size) z = coords.reshape(-1, 1, 1) y = coords.reshape(1, -1, 1) x = coords.reshape(1, 1, -1) return numpy.sin(x * y * z) / (x * y * z) def testSimple(self): """Set the data and an isosurface""" data = self._buildData(size=32) self.widget.setData(data) self.widget.addIsosurface(0.5, (1., 0., 0., 0.5)) self.widget.addIsosurface(0.7, qt.QColor('green')) self.qapp.processEvents() def testNotFinite(self): """Test with NaN and inf in data set""" # Some NaNs and inf data = self._buildData(size=32) data[8, :, :] = numpy.nan data[16, :, :] = numpy.inf data[24, :, :] = -numpy.inf self.widget.addIsosurface(0.5, 'red') self.widget.setData(data, copy=True) self.qapp.processEvents() self.widget.setData(None) # All NaNs or inf data = numpy.empty((4, 4, 4), dtype=numpy.float32) for value in (numpy.nan, numpy.inf): with self.subTest(value=str(value)): data[:] = value self.widget.setData(data, copy=True) self.qapp.processEvents()
def setUp(self): super(TestScalarFieldView, self).setUp() self.widget = ScalarFieldView() self.widget.show()