Ejemplo n.º 1
0
    def __init__(self, *args, positions: np.array=None, data: np.array=None, colormap: str=None, **kwargs):
        super().__init__(*args, **kwargs)

        # Initialize data structures
        self.positions = positions
        self.data = data
        self.rgb_data: Union[None, np.array] = None
        self.polygons: List[QtCore.QPolygonF] = []
        self.xmin, self.xmax = 0, 0
        self.ymin, self.ymax = 0, 0
        if positions is not None and data is not None:
            self.calc_lims()
        elif not (positions is None and data is None):
            raise ValueError("Either positions and data must both be given, or neither.")

        # Initialize menus
        self.menu = None
        self.gradientSelectorMenu = None

        # Create LUT item
        self._LUTitem = HistogramLUTItem()
        self._LUTitem.sigLookupTableChanged.connect(self.changedColorScale)
        self._LUTitem.sigLevelChangeFinished.connect(self.updateRGBData)
        if colormap is not None:
            self.changeColorScale(name=colormap)
        else:
            self.changeColorScale(name=DEFAULT_CMAP)

        # And update color and polygon data
        if self.data is not None:
            self.updateRGBData()
            self.calculate_polygons()

        # Attach a signal handler on parent changed
        self._parent = None
Ejemplo n.º 2
0
 def __init_ui__(self):
     self.setWindowTitle('Image Viewer')
     self.image_plot = self.addPlot()
     self.image_plot.vb.setAspectLocked()
     self.image_plot.vb.invertY()
     self.image_item = ImageItem()
     self.image_plot.addItem(self.image_item)
     self.hist = HistogramLUTItem()
     self.hist.setImageItem(self.image_item)
     self.addItem(self.hist)
Ejemplo n.º 3
0
 def _init_ui(self, **kwargs):
     self.setWindowTitle('Image Viewer')
     self.image_plot = self.addPlot(**kwargs)
     self.image_plot.vb.setAspectLocked()
     self.image_plot.vb.invertY()
     self.image_item = ImageItem()
     self.image_plot.addItem(self.image_item)
     self.image_plot.setMenuEnabled(False)
     self.hist = HistogramLUTItem()
     self.hist.setImageItem(self.image_item)
     self.addItem(self.hist)
     self.hist.vb.menu = CustomViewBoxMenu(self.hist.vb)
     self.hist.vb.menu.sigSigmaChanged.connect(self.set_sigma_factor)
     self.hist.vb.menu.sigRangeAsDefault.connect(self.set_limit_as_default)
     self.hist.vb.menu.sigUseClahe.connect(self.enable_clahe)
Ejemplo n.º 4
0
    def __init__(self, setpoint_x, setpoint_y, *args, colormap=None, **kwargs):
        # Create the attached histogram
        self._LUTitem = HistogramLUTItem()

        # Initialize self
        super().__init__(setpoint_x,
                         setpoint_y,
                         *args,
                         colormap=colormap,
                         **kwargs)

        # Update _LUTitem
        self._LUTitem.setImageItem(self)
        self._LUTitem.autoHistogramRange()  # enable autoscaling

        # Attach a signal handler on parent changed
        self._parent = None
Ejemplo n.º 5
0
 def populate_spectrogram_widget(self, widget: GraphicsLayoutWidget,
                                 game_name: str, player_name: PlayerName,
                                 electrode_name: str):
     # https://stackoverflow.com/questions/51312923/plotting-the-spectrum-of-a-wavfile-in-pyqtgraph-using-scipy-signal-spectrogram
     f, t, Sxx = self._acquire_spectrogram_signal(game_name, player_name,
                                                  electrode_name)
     plot = widget.addPlot()
     plot.setTitle('Frequency over time for electrode %s' %
                   ELECTRODES[electrode_name])
     img = ImageItem()
     plot.addItem(img)
     hist = HistogramLUTItem()
     hist.setImageItem(img)
     widget.addItem(hist)
     hist.setLevels(np.min(Sxx), np.max(Sxx))
     hist.gradient.restoreState({
         'mode':
         'rgb',
         'ticks': [(0.5, (0, 182, 188, 255)), (1.0, (246, 111, 0, 255)),
                   (0.0, (75, 0, 113, 255))]
     })
     img.setImage(Sxx)
     img.scale(t[-1] / np.size(Sxx, axis=1), f[-1] / np.size(Sxx, axis=0))
     plot.setLimits(xMin=0, xMax=t[-1], yMin=0, yMax=f[-1])
     plot.setLabel('bottom', "Time", units='s')
     plot.setLabel('left', "Frequency", units='Hz')
Ejemplo n.º 6
0
class GraphicsWidget(QWidget):
    """Widget defined in Qt Designer"""
    def __init__(self, parent=None):
        # initialization of widget
        super(GraphicsWidget, self).__init__(parent)

        # Create a central Graphics Layout Widget
        self.widget = GraphicsLayoutWidget()

        # A plot area (ViewBox + axes) for displaying the image
        self.p1 = self.widget.addPlot()
        # Item for displaying image data
        self.img_item = ImageItem()
        self.img_item.axisOrder = 'row-major'
        self.p1.addItem(self.img_item)
        self.p1.getViewBox().invertY(True)

        # create a vertical box layout
        self.vbl = QVBoxLayout()
        # add widget to vertical box
        self.vbl.addWidget(self.widget)
        # set the layout to the vertical box
        self.setLayout(self.vbl)

        # Levels/color control with a histogram
        # self.hist = HistogramLUTWidget()
        # self.hist.setImageItem(self.img)
        # parent.horizontalLayout_View.addWidget(self.hist)
        # # self.widget.addWidget(self.hist, 0, 1)
        # self.hist.vb.setMouseEnabled(y=False)  # makes user interaction a little easier

        # Create histogram
        # Levels/color control with a histogram
        self.histogram = HistogramLUTItem()
        # TODO Halve histogram width
        self.histogram.vb.setMouseEnabled(
            y=False)  # makes user interaction a little easier
        self.histogram.setImageItem(self.img_item)
        self.widget.addItem(self.histogram)
Ejemplo n.º 7
0
    def __init__(self, parent=None):
        # initialization of widget
        super(GraphicsWidget, self).__init__(parent)

        # Create a central Graphics Layout Widget
        self.widget = GraphicsLayoutWidget()

        # A plot area (ViewBox + axes) for displaying the image
        self.p1 = self.widget.addPlot()
        # Item for displaying an array of image data stacks
        self.stacks = []
        img = ImageItem()
        self.p1.addItem(img)
        self.stacks.append(img)

        # create a vertical box layout
        self.vbl = QVBoxLayout()
        # add widget to vertical box
        self.vbl.addWidget(self.widget)
        # set the layout to the vertical box
        self.setLayout(self.vbl)

        # Levels/color control with a histogram
        # self.hist = HistogramLUTWidget()
        # self.hist.setImageItem(self.img)
        # parent.horizontalLayout_View.addWidget(self.hist)
        # # self.widget.addWidget(self.hist, 0, 1)
        # self.hist.vb.setMouseEnabled(y=False)  # makes user interaction a little easier

        # Create an array of histograms
        self.histograms = []
        # Levels/color control with a histogram
        hist = HistogramLUTItem()
        hist.vb.setMouseEnabled(
            y=False)  # makes user interaction a little easier
        hist.setImageItem(img)
        self.widget.addItem(hist)
        self.histograms.append(hist)
Ejemplo n.º 8
0
class ImageItemWithHistogram(ExtendedImageItem):
    def __init__(self, setpoint_x, setpoint_y, *args, colormap=None, **kwargs):
        # Create the attached histogram
        self._LUTitem = HistogramLUTItem()

        # Initialize self
        super().__init__(setpoint_x,
                         setpoint_y,
                         *args,
                         colormap=colormap,
                         **kwargs)

        # Update _LUTitem
        self._LUTitem.setImageItem(self)
        self._LUTitem.autoHistogramRange()  # enable autoscaling

        # Attach a signal handler on parent changed
        self._parent = None

    def setLevels(self, levels, update=True):
        """
        Hook setLevels to update histogram when the levels are changed in
        the image
        """
        super().setLevels(levels, update)
        self._LUTitem.setLevels(*self.levels)

    def changeColorScale(self, name=None):
        if name is None:
            raise ValueError("Name of color map must be given")
        self.cmap = name
        self._LUTitem.gradient.setColorMap(COLORMAPS[name])

    def getHistogramLUTItem(self):
        return self._LUTitem

    def parentChanged(self):
        super().parentChanged()
        # Add the histogram to the parent
        view_box = self.getViewBox()
        if isinstance(view_box, ExtendedPlotWindow):
            logger.debug("Adding _LUTitem to parent %r.", view_box)
            view_box.addItem(self._LUTitem)
            self._parent = view_box
        elif view_box is None:
            if getattr(self, "_parent", None) is not None:
                self._parent.removeItem(self._LUTitem)
                self._parent = None
        elif isinstance(view_box, CustomViewBox):
            # This second call always seems to occur... Ignore it, since we've added
            # ourselves to the plot window.
            pass
        else:
            raise NotImplementedError(
                "parentChanged is not implemented for anything "
                "other than ExtendedPlotWindows at this time. "
                f"Got {type(view_box)}.")
Ejemplo n.º 9
0
    def _create_color_bar(self,
                          orientation: Optional[Orientation] = None,
                          **kwargs) -> None:

        self._color_bar_orientation = orientation or Orientation.Vertical
        if self._color_bar_orientation == Orientation.Vertical:
            color_bar: HistogramLUTItem = HistogramLUTItem(
                self._image, **kwargs)
            # color_bar: HistogramLUTItem = HistogramLUTItem(**kwargs)
            # for item in self._images:
            #     self._color_bar.setImageItem(item)
        else:
            raise NotImplementedError

        self._view.addItem(color_bar, row=0, col=1)
        # self._raw.addItem(self._color_bar)

        self._color_bar = ColorBar(color_bar, self._figure, self._image)

        if self._data_range is not None:
            self._color_bar.range(self._data_range)
Ejemplo n.º 10
0
    def __init__(self, **kwds):

        HistogramLUTItem.__init__(self, **kwds)
        self.overlay = True
Ejemplo n.º 11
0
 def image_in_vb(name=None) -> Tuple[ImageItem, ViewBox, HistogramLUTItem]:
     im = ImageItem()
     vb = ViewBox(invertY=True, lockAspect=True, name=name)
     vb.addItem(im)
     hist = HistogramLUTItem(im)
     return im, vb, hist
Ejemplo n.º 12
0
class CustomImageViewer(GraphicsLayoutWidget):
    @property
    def view_box(self):
        return self.image_plot.vb

    def __init__(self, parent=None, **kwargs):
        setConfigOptions(imageAxisOrder='row-major')
        super(CustomImageViewer, self).__init__(parent)
        self._scale = (1., 1.)
        self._center = (0, 0)

        self.__init_ui__()

    def __init_ui__(self):
        self.setWindowTitle('Image Viewer')
        self.image_plot = self.addPlot()
        self.image_plot.vb.setAspectLocked()
        self.image_plot.vb.invertY()
        self.image_item = ImageItem()
        self.image_plot.addItem(self.image_item)
        self.hist = HistogramLUTItem()
        self.hist.setImageItem(self.image_item)
        self.addItem(self.hist)

    def set_data(self, data, change_limits: bool = True, reset_axes: bool = False):
        if data is None:
            return
        self.image_item.setImage(data, change_limits)
        if change_limits:
            self.hist.setLevels(data.min(), data.max())
        if reset_axes:
            self.image_item.resetTransform()
        self.set_default_range()

    def set_default_range(self):
        axes = self.get_axes()
        self.image_plot.setRange(xRange=axes[0], yRange=axes[1])

    def set_auto_range(self):
        self.image_plot.autoRange()

    def set_levels(self, levels=None):
        if levels:
            self.hist.setLevels(levels[0], levels[1])
        else:
            self.hist.setLevels(self.image_item.image.min(),
                                self.image_item.image.max())

    def get_levels(self):
        return self.hist.getLevels()

    def set_center(self, center: tuple, pixel_units: bool = True):
        if not pixel_units:
            scale = self.get_scale()
            center = (center[0] / scale[0], center[1] / scale[1])
        if self._center != (0, 0) or self._scale != (1., 1.):
            self.image_item.resetTransform()
            self.image_item.scale(*self._scale)
        self.image_item.translate(- center[0], - center[1])
        self._center = center
        self.set_default_range()

    def set_scale(self, scale: float or tuple):
        if isinstance(scale, float) or isinstance(scale, int):
            scale = (scale, scale)
        if self._center != (0, 0) or self._scale != (1., 1.):
            self.image_item.resetTransform()
        self.image_item.scale(*scale)
        if self._center != (0, 0):
            self.image_item.translate(- self._center[0], - self._center[1])
        self._scale = scale
        self.set_default_range()

    def get_scale(self):
        # scale property is occupied by Qt superclass.
        return self._scale

    def get_center(self):
        return self._center

    def set_x_axis(self, x_min, x_max):
        self._set_axis(x_min, x_max, 0)
        self.set_default_range()

    def set_y_axis(self, y_min, y_max):
        self._set_axis(y_min, y_max, 1)
        self.set_default_range()

    def _set_axis(self, min_: float, max_: float, axis_ind: int):
        shape = self.image_item.image.shape
        scale = np.array(self._scale)
        scale[axis_ind] = (max_ - min_) / shape[axis_ind]
        center = np.array(self._center)
        center[axis_ind] = - min_ / scale[axis_ind]
        if self._center != (0, 0) or self._scale != (1., 1.):
            self.image_item.resetTransform()
        self.image_item.scale(scale[0], scale[1])
        self.image_item.translate(- center[0], - center[1])
        self._scale = tuple(scale)
        self._center = tuple(center)

    def get_axes(self):
        shape = np.array(self.image_item.image.shape)
        scale = np.array(self._scale)
        min_ = - np.array(self._center) * scale
        max_ = min_ + shape * scale
        return (min_[0], max_[0]), (min_[1], max_[1])
Ejemplo n.º 13
0
class MeshPlot(GraphicsObject):
    def __init__(self, *args, positions: np.array=None, data: np.array=None, colormap: str=None, **kwargs):
        super().__init__(*args, **kwargs)

        # Initialize data structures
        self.positions = positions
        self.data = data
        self.rgb_data: Union[None, np.array] = None
        self.polygons: List[QtCore.QPolygonF] = []
        self.xmin, self.xmax = 0, 0
        self.ymin, self.ymax = 0, 0
        if positions is not None and data is not None:
            self.calc_lims()
        elif not (positions is None and data is None):
            raise ValueError("Either positions and data must both be given, or neither.")

        # Initialize menus
        self.menu = None
        self.gradientSelectorMenu = None

        # Create LUT item
        self._LUTitem = HistogramLUTItem()
        self._LUTitem.sigLookupTableChanged.connect(self.changedColorScale)
        self._LUTitem.sigLevelChangeFinished.connect(self.updateRGBData)
        if colormap is not None:
            self.changeColorScale(name=colormap)
        else:
            self.changeColorScale(name=DEFAULT_CMAP)

        # And update color and polygon data
        if self.data is not None:
            self.updateRGBData()
            self.calculate_polygons()

        # Attach a signal handler on parent changed
        self._parent = None

    ###
    # Function related to plot data
    def setData(self, positions, data):
        self.positions = positions
        self.data = data

        # Calculate data size
        self.calc_lims()

        # Update plot
        self.updateRGBData()
        self.calculate_polygons()

        # Update histogram and autorange
        hist, bins = np.histogram(self.data, "auto")
        newBins = np.ndarray(bins.size+1)
        newHist = np.ndarray(hist.size+2)
        newBins[0] = bins[0]
        newBins[-1] = bins[-1]
        newBins[1:-1] = (bins[:-1] + bins[1:])/2
        newHist[[0,-1]] = 0
        newHist[1:-1] = hist
        self._LUTitem.plot.setData(newBins, newHist)
        self._LUTitem.setLevels(newBins[0], newBins[-1])
        self._LUTitem.plot.getViewBox().itemBoundsChanged(self._LUTitem.plot)

        # Force viewport update
        self.getViewBox().itemBoundsChanged(self)
        self.update()

    ###
    # Functions relating to the size of the image
    def calc_lims(self):
        if not self.positions:
            self.xmin, self.xmax = 0, 0
            self.ymin, self.ymax = 0, 0
            return
        self.xmin, self.ymin = self.positions[0]
        self.xmax, self.ymax = self.positions[0]
        for x, y in islice(self.positions, 1, None):
            self.xmin, self.xmax = min(self.xmin, x), max(self.xmax, x)
            self.ymin, self.ymax = min(self.ymin, y), max(self.ymax, y)
        logger.debug("Calculated limits (%f, %f) - (%f, %f)", self.xmin, self.ymin,
                     self.xmax, self.ymax)

    def width(self):
        return self.xmax - self.xmin

    def height(self):
        return self.ymax - self.ymin

    def boundingRect(self):
        tl = QtCore.QPointF(self.xmin, self.ymin)
        br = QtCore.QPointF(self.xmax, self.ymax)
        return QtCore.QRectF(tl, br)

    ###
    # Functions relating to the colorscale

    def setLevels(self, levels, update=True):
        """
        Hook setLevels to update histogram when the levels are changed in
        the image
        """
        super().setLevels(levels, update)
        self._LUTitem.setLevels(*self.levels)

    def changeColorScale(self, name=None):
        if name is None:
            raise ValueError("Name of color map must be given")
        logger.debug("Changed color scale to %s.", name)
        self._LUTitem.gradient.setColorMap(COLORMAPS[name])

    def getHistogramLUTItem(self):
        return self._LUTitem

    @property
    def histogram(self):
        return self.getHistogramLUTItem()

    def changedColorScale(self):
        logger.debug("Changed color scale")
        self.updateRGBData()

    def updateRGBData(self):
        minr, maxr = self._LUTitem.getLevels()
        logger.debug("Recoloring to changed levels: (%f, %f)", minr, maxr)
        if self.data is not None:
            scaled = (self.data - minr)/(maxr - minr)

            logger.debug("Calculating new colors")
            self.rgb_data = self._LUTitem.gradient.colorMap().map(scaled, mode="qcolor")
            logger.debug("Done")
            self.update()
    ###
    # Functions relating to drawing

    def calculate_polygons(self):
        """
        Calculate the polygons to be drawn by the mesh plot
        """
        raise NotImplementedError()

    def paint(self, p, _options, _widget):
        logger.debug("Starting paint")
        visible = self.parentItem().boundingRect()
        if self.polygons is not None and self.polygons:
            p.setPen(mkPen(None))
            for poly in self.polygons: #pylint: disable=not-an-iterable
                if not poly[1].boundingRect().intersects(visible):
                    continue
                p.setBrush(self.rgb_data[poly[0]])
                p.drawPolygon(poly[1])
            logger.debug("Done painting")
        else:
            logger.debug("No polygons to draw")

    def parentChanged(self):
        super().parentChanged()
        # Add the histogram to the parent
        view_box = self.getViewBox()
        if isinstance(view_box, ExtendedPlotWindow):
            logger.debug("Adding _LUTitem to parent %r.", view_box)
            view_box.addItem(self._LUTitem)
            self._parent = view_box
        elif view_box is None:
            if getattr(self, "_parent", None) is not None:
                self._parent.removeItem(self._LUTitem)
                self._parent = None
        elif isinstance(view_box, CustomViewBox):
            # This second call always seems to occur... Ignore it, since we've added
            # ourselves to the plot window.
            pass
        else:
            raise NotImplementedError("parentChanged is not implemented for anything "
                                      "other than ExtendedPlotWindows at this time. "
                                      f"Got {type(view_box)}.")
Ejemplo n.º 14
0
class CustomImageViewer(GraphicsLayoutWidget):
    @property
    def view_box(self):
        return self.image_plot.vb

    def __init__(self,
                 parent=None,
                 *,
                 hist_range: tuple = None,
                 sigma_factor: float = 3,
                 **kwargs):
        setConfigOptions(imageAxisOrder='row-major')
        super(CustomImageViewer, self).__init__(parent)

        self._raw_data = None
        self._use_clahe: bool = True

        self._scale = (1., 1.)
        self._center = (0, 0)

        self._hist_range = hist_range
        self._sigma_factor = sigma_factor

        self._init_ui(**kwargs)

    def _init_ui(self, **kwargs):
        self.setWindowTitle('Image Viewer')
        self.image_plot = self.addPlot(**kwargs)
        self.image_plot.vb.setAspectLocked()
        self.image_plot.vb.invertY()
        self.image_item = ImageItem()
        self.image_plot.addItem(self.image_item)
        self.image_plot.setMenuEnabled(False)
        self.hist = HistogramLUTItem()
        self.hist.setImageItem(self.image_item)
        self.addItem(self.hist)
        self.hist.vb.menu = CustomViewBoxMenu(self.hist.vb)
        self.hist.vb.menu.sigSigmaChanged.connect(self.set_sigma_factor)
        self.hist.vb.menu.sigRangeAsDefault.connect(self.set_limit_as_default)
        self.hist.vb.menu.sigUseClahe.connect(self.enable_clahe)

    def set_data(self, data, *, reset_axes: bool = False):
        self._raw_data = data

        if data is None:
            return

        if self._use_clahe:
            data = standard_contrast_correction(data)

        self.image_item.setImage(data)
        self.set_levels()
        if reset_axes:
            self.image_item.resetTransform()
        self.set_default_range()

    def hist_params(self) -> dict:
        return dict(sigma_factor=self._sigma_factor,
                    hist_range=self._hist_range)

    def clear_image(self):
        self.set_data(np.zeros((1, 1)))

    def set_default_range(self):
        if self.image_item.image is None:
            return
        # self.set_auto_range()
        axes = self.get_axes()
        self.image_plot.setRange(xRange=axes[1], yRange=axes[0])

    @pyqtSlot(bool)
    def enable_clahe(self, enable: bool):
        self._use_clahe = enable
        self.set_data(self._raw_data)

    def set_auto_range(self):
        self.image_plot.autoRange()

    def set_levels(self):
        img = self.image_item.image
        if img is None:
            return

        if self._sigma_factor and self._sigma_factor > 0:
            m, s = img.flatten().mean(
            ), img.flatten().std() * self._sigma_factor
            self.hist.setLevels(max(m - s, img.min()), min(m + s, img.max()))
        elif self._hist_range:
            self.hist.setLevels(*self._hist_range)
        else:
            self.hist.setLevels(self.image_item.image.min(),
                                self.image_item.image.max())

    @pyqtSlot(float)
    def set_sigma_factor(self, sigma_factor: float):
        self._sigma_factor = sigma_factor
        self.set_levels()

    @pyqtSlot()
    def set_limit_as_default(self):
        self._hist_range = self.hist.getLevels()
        self._sigma_factor = None
        self.set_levels()

    def get_levels(self):
        return self.hist.getLevels()

    def set_center(self, center: tuple, pixel_units: bool = True):
        if not pixel_units:
            scale = self.get_scale()
            center = (center[0] / scale[0], center[1] / scale[1])
        if self._center != (0, 0) or self._scale != (1., 1.):
            self.image_item.resetTransform()
            self.image_item.scale(*self._scale)
        self.image_item.translate(-center[0], -center[1])
        self._center = center
        self.set_default_range()

    def set_scale(self, scale: float or tuple):
        if isinstance(scale, float) or isinstance(scale, int):
            scale = (scale, scale)
        if self._center != (0, 0) or self._scale != (1., 1.):
            self.image_item.resetTransform()
        self.image_item.scale(*scale)
        if self._center != (0, 0):
            self.image_item.translate(-self._center[0], -self._center[1])
        self._scale = scale
        self.set_default_range()

    def get_scale(self) -> tuple:
        # scale property is occupied by Qt superclass.
        return self._scale

    def get_center(self) -> tuple:
        return self._center

    def set_x_axis(self, x_min, x_max):
        self._set_axis(x_min, x_max, 0)
        self.set_default_range()

    def set_y_axis(self, y_min, y_max):
        self._set_axis(y_min, y_max, 1)
        self.set_default_range()

    def _set_axis(self, min_: float, max_: float, axis_ind: int):
        shape = self.image_item.image.shape
        scale = np.array(self._scale)
        scale[axis_ind] = (max_ - min_) / shape[axis_ind]
        center = np.array(self._center)
        center[axis_ind] = -min_ / scale[axis_ind]
        if self._center != (0, 0) or self._scale != (1., 1.):
            self.image_item.resetTransform()
        self.image_item.scale(scale[0], scale[1])
        self.image_item.translate(-center[0], -center[1])
        self._scale = tuple(scale)
        self._center = tuple(center)

    def get_axes(self):
        shape = np.array(self.image_item.image.shape)
        scale = np.array(self._scale)
        min_ = -np.array((self._center[1], self._center[0])) * scale
        max_ = min_ + shape * scale
        return (min_[0], max_[0]), (min_[1], max_[1])