Source code for mesoSPIM.src.mesoSPIM_CameraWindow

'''
mesoSPIM CameraWindow
'''
import sys
import numpy as np
import logging
from PyQt5 import QtWidgets, QtCore, QtGui
from PyQt5.uic import loadUi
import pyqtgraph as pg
from .utils.optimization import shannon_dct
from .utils.utility_functions import log_cpu_core

logger = logging.getLogger(__name__)

[docs] class mesoSPIM_CameraWindow(QtWidgets.QWidget): sig_update_roi = QtCore.pyqtSignal(tuple) sig_update_status = QtCore.pyqtSignal() def __init__(self, parent=None): super().__init__() self.parent = parent # the mesoSPIM_MainWindow() instance self.cfg = parent.cfg self.state = self.parent.state # the mesoSPIM_StateSingleton() instance pg.setConfigOptions(imageAxisOrder='row-major') if (hasattr(self.cfg, 'ui_options') and self.cfg.ui_options['dark_mode']) or\ (hasattr(self.cfg, 'dark_mode') and self.cfg.dark_mode): pg.setConfigOptions(background=pg.mkColor('#19232D')) # To avoid pitch black bg for the image view else: pg.setConfigOptions(background="w") '''Set up the UI''' if __name__ == '__main__': loadUi('../gui/mesoSPIM_CameraWindow.ui', self) else: loadUi(self.parent.package_directory + '/gui/mesoSPIM_CameraWindow.ui', self) self.setWindowTitle('mesoSPIM-Control: Camera Window') self.status_label.setText("Status: OK") ''' Set histogram Range ''' self.image_view.setLevels(100, 3000) self.imageItem = self.image_view.getImageItem() self.histogram = self.image_view.getHistogramWidget() self.histogram.setMinimumWidth(100) self.histogram.item.vb.setMaximumWidth(100) ''' This is flipped to account for image rotation ''' self.y_image_width = self.cfg.camera_parameters['x_pixels'] self.x_image_width = self.cfg.camera_parameters['y_pixels'] self.ini_subsampling = self.cfg.startup['camera_display_live_subsampling'] ''' Initialize crosshairs ''' self.crosspen = pg.mkPen({'color': "r", 'width': 1}) self.vLine = pg.InfiniteLine(pos=self.x_image_width/2, angle=90, movable=False, pen=self.crosspen) self.hLine = pg.InfiniteLine(pos=self.y_image_width/2, angle=0, movable=False, pen=self.crosspen) self.image_view.addItem(self.vLine) self.image_view.addItem(self.hLine) # Create overlay ROIs self.overlay = 'LS marker' # 'box', None, 'LS marker' w, h = self.x_image_width//self.ini_subsampling, self.y_image_width//self.ini_subsampling self.roi_box = pg.RectROI((0, 0), (w, h), sideScalers=True) self.roi_drawn = False # Create polygons that show light-sheet direction self.points_R = np.array([[0, self.y_image_width//self.ini_subsampling//2 - 25], [0, self.y_image_width//self.ini_subsampling//2 + 25], [100, self.y_image_width//self.ini_subsampling//2]]) self.points_L = np.array([[self.x_image_width//self.ini_subsampling, self.y_image_width//self.ini_subsampling//2 - 25], [self.x_image_width//self.ini_subsampling, self.y_image_width//self.ini_subsampling//2 + 25], [self.x_image_width//self.ini_subsampling - 100, self.y_image_width//self.ini_subsampling//2]]) self.lightsheet_marker_R = pg.PolyLineROI(positions=self.points_R, closed=True, pen='y', movable=False, rotatable=False, removable=False, aspectLocked=True) self.lightsheet_marker_L = pg.PolyLineROI(positions=self.points_L, closed=True, pen='y', movable=False, rotatable=False, removable=False, aspectLocked=True) self.image_view.addItem(self.lightsheet_marker_R) self.image_view.addItem(self.lightsheet_marker_L) self.hide_light_sheet_marker() # Set up internal CameraWindow signals self.adjustLevelsButton.clicked.connect(self.adjust_levels) self.overlayCombo.currentTextChanged.connect(self.change_overlay) self.roi_box.sigRegionChangeFinished.connect(self.update_status) self.sig_update_status.connect(self.update_status)
[docs] def adjust_levels(self, pct_low=25, pct_hi=99.99): ''''Adjust histogram levels''' img = self.image_view.getImageItem().image self.image_view.setLevels(min=np.percentile(img, pct_low), max=np.percentile(img, pct_hi))
[docs] def px2um(self, px, scale=1): '''Unit converter''' return scale * px * self.cfg.pixelsize[self.state['zoom']]
@QtCore.pyqtSlot(str) def change_overlay(self, overlay_name): w, h = self.get_image_shape() if overlay_name == 'Box roi': self.set_roi('box', (w//2 - 50, h//2 - 50, 100, 100)) elif overlay_name == 'Overlay: none': self.set_roi(None, (0, 0, w, h)) elif overlay_name == 'LS marker': self.set_roi('LS marker', (0, 0, w, h))
[docs] def get_roi(self): im_item = self.image_view.getImageItem() if self.overlay == 'box' and self.roi_drawn: roi = self.roi_box.getArrayRegion(im_item.image, im_item) x, y = self.roi_box.pos() w, h = self.roi_box.size() self.sig_update_roi.emit((x, y, w, h)) else: roi = im_item.image w, h = im_item.image.shape self.sig_update_roi.emit((0, 0, w, h)) return roi
[docs] def set_roi(self, mode='box', x_y_w_h=(0, 0, 100, 100)): assert mode in ('box', None, 'LS marker'), f"Mode must be in ('box', None, 'LS marker'), received {mode} instead" self.overlay = mode x, y, w, h = x_y_w_h self.roi_box.setPos((x, y)) self.roi_box.setSize((w, h)) if self.overlay in (None, 'LS marker') and self.roi_drawn: self.image_view.removeItem(self.roi_box) self.roi_drawn = False elif self.overlay == 'box' and not self.roi_drawn: self.image_view.addItem(self.roi_box) self.roi_drawn = True self.sig_update_status.emit()
[docs] def get_image_shape(self): return self.image_view.getImageItem().image.shape
@QtCore.pyqtSlot() def update_status(self, subsampling=2.0): roi = self.get_roi() if self.overlay == 'box': w, h = self.roi_box.size() self.status_label.setText(f"Screen ROI size: W {int(w)} px, {int(self.px2um(w, subsampling)):,} \u03BCm. " f"H {int(h)} px, {int(self.px2um(h, subsampling)):,} \u03BCm. " f"Screen subsampling {subsampling}.") #f"sharpness {np.round(1e4 * shannon_dct(roi)):.0f}") self.hide_light_sheet_marker() elif self.overlay == None: self.hide_light_sheet_marker() self.status_label.setText(f"Image dimensions: {roi.shape}") elif self.overlay == 'LS marker': self.draw_lightsheet_marker() else: self.status_label.setText(f"Image dimensions: {roi.shape}")
[docs] def draw_crosshairs(self): self.image_view.addItem(self.vLine) self.image_view.addItem(self.hLine)
[docs] def draw_lightsheet_marker(self): if self.state['shutterconfig'] == 'Left': self.lightsheet_marker_R.setOpacity(0) self.lightsheet_marker_L.setOpacity(1) elif self.state['shutterconfig'] == 'Right': self.lightsheet_marker_R.setOpacity(1) self.lightsheet_marker_L.setOpacity(0) elif self.state['shutterconfig'] == 'Both': self.lightsheet_marker_R.setOpacity(1) self.lightsheet_marker_L.setOpacity(1)
[docs] def hide_light_sheet_marker(self): self.lightsheet_marker_R.setOpacity(0) self.lightsheet_marker_L.setOpacity(0)
#@QtCore.pyqtSlot(np.ndarray) # deprecated due to slow performance
[docs] def set_image(self, image): log_cpu_core(logger, msg='set_image()') logger.debug(f"setImage() with shape {image.shape} started") if self.state['state'] in ('live', 'idle'): subsampling_ratio = self.state['camera_display_live_subsampling'] elif self.state['state'] in ('run_acquisition_list', 'run_selected_acquisition'): subsampling_ratio = self.state['camera_display_acquisition_subsampling'] else: subsampling_ratio = self.ini_subsampling self.image_view.setImage(image[::subsampling_ratio, ::subsampling_ratio], autoLevels=False, autoHistogramRange=False, autoRange=False) logger.debug(f"setImage() finished") # update roi size if subsampling has changed interactively: # if self.overlay == 'box': # x, y = self.roi_box.pos() # w, h = self.roi_box.size() # self.roi_box.setPos((x / subsampling_ratio, y / subsampling_ratio)) # self.roi_box.setSize((w / subsampling_ratio, h / subsampling_ratio)) self.update_status(subsampling_ratio) h, w = image.shape[-2]//subsampling_ratio, image.shape[-1]//subsampling_ratio # works for both 2D and 3/4D loaded TIFF files. if h != self.y_image_width or w != self.x_image_width: self.x_image_width, self.y_image_width = w, h self.vLine.setPos(self.x_image_width/2.) self.hLine.setPos(self.y_image_width/2.) new_points_R = [p/(subsampling_ratio/self.ini_subsampling) for p in self.points_R] new_points_L = [p/(subsampling_ratio/self.ini_subsampling) for p in self.points_L] self.lightsheet_marker_R.setPoints(new_points_R) self.lightsheet_marker_L.setPoints(new_points_L) # hide the light sheet marker draggable handles: for h in self.lightsheet_marker_R.getHandles(): h.setVisible(False) for h in self.lightsheet_marker_L.getHandles(): h.setVisible(False) self.draw_crosshairs()
@QtCore.pyqtSlot() def update_image_from_deque(self): if len(self.parent.core.frame_queue_display) > 0: image = self.parent.core.frame_queue_display[0] self.set_image(image) else: return