Source code for stytra.gui.monitor_control
from PyQt5.QtCore import Qt, QRectF, pyqtSignal
from PyQt5.QtWidgets import QLabel, QWidget, QHBoxLayout, QPushButton
import numpy as np
import pyqtgraph as pg
from stytra.calibration import CircleCalibrator, CrossCalibrator, CalibrationException
from PyQt5.QtWidgets import QVBoxLayout
from lightparam.gui import ControlSpin
import cv2
[docs]class ProjectorViewer(pg.GraphicsLayoutWidget):
"""Widget that displays the whole projector screen and allows
configuring the stimulus display window
Parameters
----------
Returns
-------
"""
sig_dim_changed = pyqtSignal(tuple)
def __init__(self, *args, display_size=(1280, 800), display, **kwargs):
super().__init__(*args, **kwargs)
self.display = display
self.view_box = pg.ViewBox(invertY=True, lockAspect=1, enableMouse=False)
self.addItem(self.view_box)
self.roi_box = pg.ROI(
maxBounds=QRectF(0, 0, display_size[0], display_size[1]),
size=display.size,
pos=display.pos,
)
self.roi_box.addScaleHandle([0, 0], [1, 1])
self.roi_box.addScaleHandle([1, 1], [0, 0])
self.roi_box.sigRegionChanged.connect(self.set_param_val)
self.display.sig_param_changed.connect(self.set_roi)
self.view_box.addItem(self.roi_box)
self.view_box.setRange(
QRectF(0, 0, display_size[0], display_size[1]),
update=True,
disableAutoRange=True,
)
self.view_box.addItem(
pg.ROI(
pos=(1, 1),
size=(display_size[0] - 1, display_size[1] - 1),
movable=False,
pen=(80, 80, 80),
)
)
self.calibration_points = pg.ScatterPlotItem(pen=(255, 0, 0), brush=None)
self.calibration_frame = pg.PlotCurveItem(
brush=(120, 10, 10), pen=(200, 10, 10), fill_level=1
)
self.camera_image = pg.ImageItem()
self.view_box.addItem(self.calibration_frame)
self.view_box.addItem(self.camera_image)
self.view_box.addItem(self.calibration_points)
self.setting_param_val = False
self.set_param_val()
[docs] def set_roi(self):
""" """
if not self.setting_param_val:
self.roi_box.setPos(self.display.pos)
self.roi_box.setSize(self.display.size)
[docs] def set_param_val(self):
""" """
self.setting_param_val = True
size = tuple([int(p) for p in self.roi_box.size()])
self.display.size = size
self.sig_dim_changed.emit(size)
self.display.pos = tuple([int(p) for p in self.roi_box.pos()])
self.setting_param_val = False
[docs] def display_calibration_pattern(
self, calibrator, camera_resolution=(480, 640), image=None
):
"""
Parameters
----------
calibrator :
camera_resolution :
(Default value = (480)
640) :
image :
(Default value = None)
Returns
-------
"""
cw = camera_resolution[0]
ch = camera_resolution[1]
points_cam = np.array([[0, 0], [0, cw], [ch, cw], [ch, 0], [0, 0]])
x0, y0 = self.roi_box.pos()
try:
points_calib = np.pad(
calibrator.points, ((0, 0), (0, 1)), mode="constant", constant_values=1
)
self.calibration_points.setData(
x=points_calib[:, 0] + x0, y=points_calib[:, 1] + y0
)
except ValueError:
pass
try:
if calibrator.cam_to_proj is not None:
points_cam = np.pad(
points_cam, ((0, 0), (0, 1)), mode="constant", constant_values=1
)
points_proj = points_cam @ np.array(calibrator.cam_to_proj).T
self.calibration_frame.setData(
x=points_proj[:, 0] + x0, y=points_proj[:, 1] + y0
)
if image is not None and calibrator.cam_to_proj is not None:
tr_im = cv2.warpAffine(
image,
np.array(calibrator.cam_to_proj).astype(np.float64),
dsize=tuple([int(p) for p in self.roi_box.size()]),
)
self.camera_image.setImage(tr_im)
self.camera_image.setRect(
QRectF(
self.roi_box.pos().x(),
self.roi_box.pos().y(),
self.roi_box.size().x(),
self.roi_box.size().y(),
)
)
except ValueError:
pass
[docs]class ProjectorAndCalibrationWidget(QWidget):
""" """
sig_calibrating = pyqtSignal()
def __init__(self, experiment, **kwargs):
""" Instantiate the widget that controls the display on the projector
:param experiment: Experiment class with calibrator and display window
"""
super().__init__(**kwargs)
self.experiment = experiment
self.calibrator = experiment.calibrator
self.container_layout = QVBoxLayout()
self.container_layout.setContentsMargins(0, 0, 0, 0)
self.widget_proj_viewer = ProjectorViewer(display=experiment.window_display)
self.container_layout.addWidget(self.widget_proj_viewer)
self.widget_proj_viewer.sig_dim_changed.connect(self.update_size)
self.layout_calibrate = QHBoxLayout()
self.button_show_calib = QPushButton("Show calibration")
self.button_show_calib.clicked.connect(self.toggle_calibration)
if isinstance(experiment.calibrator, CircleCalibrator):
self.button_calibrate = QPushButton("Calibrate")
self.button_calibrate.clicked.connect(self.calibrate)
self.layout_calibrate.addWidget(self.button_calibrate)
self.label_calibrate = QLabel(self.calibrator.length_to_measure)
self.label_calibrate.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
self.layout_calibrate.addWidget(self.button_show_calib)
self.layout_calibrate.addWidget(self.label_calibrate)
if isinstance(experiment.calibrator, CircleCalibrator):
self.calibrator_px_len = ControlSpin(self.calibrator, "triangle_height")
self.layout_calibrate.addWidget(QLabel("calibrator size"))
self.layout_calibrate.addWidget(self.calibrator_px_len)
self.calibrator_len_spin = ControlSpin(self.calibrator, "length_mm")
self.layout_calibrate.addWidget(self.calibrator_len_spin)
self.layout_calibrate.setContentsMargins(12, 0, 12, 12)
self.container_layout.addLayout(self.layout_calibrate)
self.setLayout(self.container_layout)
[docs] def update_size(self, size):
self.calibrator.set_pixel_scale(size[0], size[1])
self.calibrator_len_spin.update_display()
[docs] def toggle_calibration(self):
""" """
if isinstance(self.calibrator, CircleCalibrator):
_, frame = self.experiment.frame_dispatcher.gui_queue.get()
self.widget_proj_viewer.display_calibration_pattern(
self.calibrator, frame.shape, frame
)
self.calibrator.toggle()
if self.calibrator.enabled:
self.button_show_calib.setText("Hide calibration")
else:
self.button_show_calib.setText("Show calibration")
self.sig_calibrating.emit()
self.experiment.window_display.widget_display.update()
[docs] def calibrate(self):
""" """
_, frame = self.experiment.frame_dispatcher.gui_queue.get()
try:
self.calibrator.find_transform_matrix(frame)
self.widget_proj_viewer.display_calibration_pattern(
self.calibrator, frame.shape, frame
)
except CalibrationException:
pass