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')
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])
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])