class BaseLine(object): @property def status(self): return self._status @property def roi(self): return self._roi @property def baseline(self): if self._status == BaseLineStatus.baseline_subtracted: return self._baseline else: return @property def parent(self): return self._parent() def __init__(self, parent: PlotWithBaseLineCorrection): self._parent = weakref.ref(parent) self._baseline = None self._x_axis = None self._x1 = None self._x2 = None self._smoothness_param = None self._asymmetry_param = None self._baseline_setup_widget = None self.baseline_plot = None self._status = BaseLineStatus.no_baseline self.__init_parameters__() self.__init_roi__() def clear(self): self._baseline = None self._set_status(BaseLineStatus.no_baseline) self._remove_baseline_from_plot() def open_setup(self): self.set_axis(self.parent.x) self._baseline_setup_widget = setup = BaseLineSetup( self._status, **self.get_parameters()) if None in (self._x1, self._x2): self.set_default_bounds() self.roi.show() setup.calculate_signal.connect(self._on_calculate_baseline) setup.subtract_signal.connect(self._on_subtracting_baseline) setup.restore_signal.connect(self._on_restoring_data) setup.close_signal.connect(self._on_closing_setup) setup.show() def _set_status(self, status: 'BaseLineStatus'): self._status = status if self._baseline_setup_widget: self._baseline_setup_widget.set_status(status) def _on_calculate_baseline(self, params: dict): self.set_parameters(**params) self.update_bounds() try: self.get_baseline_correction(self.parent.smoothed_y) except Exception as err: logger.exception(err) show_error( 'Failed calculating baseline. Change roi region or parameters and try again.', 'Baseline calculation error') self._plot_baseline() self._set_status(BaseLineStatus.baseline_calculated) def _on_subtracting_baseline(self): self._remove_baseline_from_plot() self._set_status(BaseLineStatus.baseline_subtracted) self.parent.update_smoothed_y() self.parent.plot() def _on_restoring_data(self): self._set_status(BaseLineStatus.baseline_restored) self._plot_baseline() self.parent.update_smoothed_y() self.parent.plot() def _on_closing_setup(self): self._baseline_setup_widget = None self.roi.hide() if (self.status == BaseLineStatus.baseline_calculated or self.status == BaseLineStatus.baseline_restored): self._remove_baseline_from_plot() self.clear() def _plot_baseline(self): if not self.baseline_plot: self.baseline_plot = self.parent.image_view.plot_item.plot() pen = QPen(QColor('red')) pen.setStyle(Qt.DashDotLine) pen.setWidth(4) pen.setCapStyle(Qt.RoundCap) pen.setJoinStyle(Qt.RoundJoin) pen.setCosmetic(True) self.baseline_plot.setData(self._x_axis, self._baseline, pen=pen) def _remove_baseline_from_plot(self): if self.baseline_plot: self.parent.image_view.plot_item.removeItem(self.baseline_plot) self.baseline_plot = None def __init_parameters__(self): # not necessary params = read_config('Baseline correction') self.set_parameters(**params) def __init_roi__(self): self._roi = LinearRegionItem() self._roi.hide() self._roi.setBrush(QColor(255, 255, 255, 50)) self.parent.image_view.plot_item.addItem(self.roi) def update_bounds(self): self._x1, self._x2 = self.roi.getRegion() def set_parameters(self, **kwargs): if 'smoothness_param' in kwargs: self._smoothness_param = kwargs['smoothness_param'] if 'asymmetry_param' in kwargs: self._asymmetry_param = kwargs['asymmetry_param'] def get_parameters(self): params = dict() if self._asymmetry_param is not None: params['asymmetry_param'] = self._asymmetry_param if self._smoothness_param is not None: params['smoothness_param'] = self._smoothness_param return params def set_axis(self, x: np.ndarray): self._x_axis = x self.set_default_bounds() def set_bounds(self, x1: float, x2: float): self._x1, self._x2 = x1, x2 self.roi.setRegion((x1, x2)) def set_default_bounds(self): if self._x_axis is None: self.set_bounds(0, 1) else: self.set_bounds(self._x_axis.min(), self._x_axis.max()) def get_baseline_correction(self, y: np.ndarray): if (self._x_axis is None or y.size != self._x_axis.size or None in (self._x1, self._x2, self._smoothness_param, self._asymmetry_param)): return x1, x2 = self._get_coords() baseline = baseline_correction(y[x1:x2], self._smoothness_param, self._asymmetry_param) self._baseline = np.zeros_like(y) self._baseline[x1:x2] = baseline return self.baseline def _get_coords(self): scale_factor = self._x_axis.size / (self._x_axis.max() - self._x_axis.min()) x_min = self._x_axis.min() min_ind, max_ind = 0, self._x_axis.size x1 = int((self._x1 - x_min) * scale_factor) x2 = int((self._x2 - x_min) * scale_factor) x1 = min((max((x1, min_ind)), max_ind)) x2 = min((max((x2, min_ind)), max_ind)) xs = (x1, x2) return min(xs), max(xs)
class PlotBC(Smooth1DPlot): sigBackgroundChanged = pyqtSignal() def __init__(self, profile: BasicProfile, parent=None): super().__init__(profile, parent) self._status = BaseLineStatus.no_baseline self.baseline_plot = self.image_view.plot_item.plot() self._init_roi() self._baseline_setup_widget = BaseLineSetup( self, self._status, **self.profile.get_parameters()) self.profile.sigDataUpdated.connect(self.update_plot) @property def y(self): if self._status == BaseLineStatus.baseline_subtracted and self.profile.baseline is not None: return self.profile.y - self.profile.baseline else: return self.profile.y def update_data(self, *args, **kwargs): self.profile.update_data(*args, **kwargs) def is_shown(self, shown: bool): self.profile.is_shown = shown def _init_toolbars(self): super()._init_toolbars() baseline_toolbar = BlackToolBar('Baseline Correction') self.addToolBar(baseline_toolbar) baseline_button = RoundedPushButton(parent=baseline_toolbar, icon=Icon('baseline'), radius=30) baseline_button.clicked.connect(self.open_baseline_setup) baseline_toolbar.addWidget(baseline_button) def _init_roi(self): self._roi = LinearRegionItem() self._roi.hide() self._roi.setBrush(QColor(255, 255, 255, 50)) self.image_view.plot_item.addItem(self._roi) def open_baseline_setup(self): if self.y is None: return setup = self._baseline_setup_widget if self.profile.x_range is None: self._set_default_bounds() self._roi.setRegion(self.profile.x_range) if self.profile.baseline is None: self._set_status(BaseLineStatus.no_baseline) # elif self._status == BaseLineStatus.no_baseline: # self._set_status(BaseLineStatus.baseline_subtracted) # else: # self._set_status(self._status) # self.plot() setup.set_parameters(self.profile.get_parameters()) setup.calculate_signal.connect(self._on_calculate_baseline) setup.subtract_signal.connect(self._on_subtracting_baseline) setup.restore_signal.connect(self._on_restoring_data) setup.close_signal.connect(self._on_closing_setup) setup.show() self._roi.show() # def show_baseline(self): # if (self.profile.baseline is None or self._status == BaseLineStatus.baseline_calculated or # self._status == BaseLineStatus.baseline_restored): # return # self._on_restoring_data() def update_plot(self): self.sigma_slider.set_value(self.profile.sigma, True) if self.profile.baseline is None: self.clear_baseline() else: self._set_status(BaseLineStatus.baseline_subtracted) self.plot() def plot_baseline(self): if self.profile.baseline is not None: self.baseline_plot.setData(self.profile.x, self.profile.baseline, pen=get_pen(width=4, color='red', style=Qt.DashDotLine)) def _set_default_bounds(self): if self.x is None: self.profile.x_range = (0, 1) else: self.profile.x_range = (self.x.min(), self.x.max()) def _update_bounds(self): self.profile.x_range = self._roi.getRegion() def _set_status(self, status: 'BaseLineStatus'): self._status = status self._baseline_setup_widget.set_status(status) def _on_calculate_baseline(self, params: dict): self.profile.set_parameters(**params) self._update_bounds() try: self.profile.update_baseline() except Exception as err: logger.exception(err) show_error( 'Failed calculating baseline. Change roi region or parameters and try again.', error_title='Baseline calculation error') return self._set_status(BaseLineStatus.baseline_calculated) self.plot_baseline() def _on_subtracting_baseline(self): self.baseline_plot.clear() self._set_status(BaseLineStatus.baseline_subtracted) self.plot() def _on_restoring_data(self): self._set_status(BaseLineStatus.baseline_restored) self.plot_baseline() self.plot() def _on_closing_setup(self): self._baseline_setup_widget.hide() self._roi.hide() self.clear_baseline() if self._status != BaseLineStatus.baseline_subtracted: self._set_status(BaseLineStatus.no_baseline) self.profile.clear_baseline(clear_range=False) self.sigBackgroundChanged.emit() def clear_baseline(self): self.baseline_plot.clear()