Commit a645147c authored by Andy Regensky's avatar Andy Regensky
Browse files

Subdivided MainViewModel did change signalling

parent 035b5ae8
import json
from PySide2.QtCore import QObject, Signal, QThreadPool, Slot
import numpy as np
import imageio
from fishui import projections
from fishui import coordinate_conversion
from fishui.workers import BlockProjectionWorker, InterpolationWorker
class MainViewModel(QObject):
didChange = Signal()
propertiesDidChange = Signal()
blockProjectionsDidChange = Signal()
imagesDidChange = Signal()
def __init__(self, parent=None):
super().__init__(parent)
......@@ -45,9 +46,6 @@ class MainViewModel(QObject):
# Values used for drawing
self.samples_per_edge = 100
self.projection = None
self.perspective = None
self.fov_180_radius = None
self.block_xf = None
self.block_yf = None
self.block_xp = None
......@@ -61,7 +59,7 @@ class MainViewModel(QObject):
self.block_ctr_pxm = None
self.block_ctr_pym = None
self.uwc_sign = None
self.updateProjections()
self.updateBlockProjection()
# Background images
self._fisheye_images = []
......@@ -77,19 +75,46 @@ class MainViewModel(QObject):
return np.isclose(self.sensor_size_px[0]/self.sensor_size_mm[0],
self.sensor_size_px[1]/self.sensor_size_mm[1])
@property
def focal_length_px(self):
return projections.Projection.to_focal_length_px(self.focal_length_mm,
self.sensor_size_mm,
self.sensor_size_px)
@property
def projection(self):
if self.projection_key == 'calibrated':
px_per_mm = self.sensor_size_px[0] / self.sensor_size_mm[0]
calib_coeffs = self.calibration_coeffs * px_per_mm
projection = projections.CalibratedProjection(calib_coeffs, self.focal_length_px)
else:
projection = self.projections[self.projection_key](self.focal_length_px)
return projection
@property
def perspective(self):
return projections.PerspectiveProjection(self.focal_length_px)
@property
def fov_180_radius(self):
return self.projection.radius(np.deg2rad(90))
def setSensorSize(self, sensor_size):
self.sensor_size_mm = sensor_size
self.updateProjections()
self.propertiesDidChange.emit()
self.updateBlockProjection()
self.updatePerspectiveImages()
def setSensorResolution(self, resolution):
self.sensor_size_px = resolution
self.updateProjections()
self.propertiesDidChange.emit()
self.updateBlockProjection()
self.updatePerspectiveImages()
def setFocalLength(self, focal_length_mm):
self.focal_length_mm = focal_length_mm
self.updateProjections()
self.propertiesDidChange.emit()
self.updateBlockProjection()
self.updatePerspectiveImages()
def setProjection(self, projection_key, calib_file=None):
......@@ -100,60 +125,61 @@ class MainViewModel(QObject):
elif projection_key != 'calibrated' and calib_file is not None:
raise Exception("Calibration file argument is valid for calibrated projection only.")
self.projection_key = projection_key
self.updateProjections()
self.propertiesDidChange.emit()
self.updateBlockProjection()
self.updatePerspectiveImages()
def readCalibrationData(self, calib_file):
with open(calib_file, 'r') as file:
calib_json = json.load(file)
focal_length = calib_json['focal_length']
calib_coeffs = np.array([float(coeff) for coeff in calib_json['coefficients']])
if len(calib_coeffs) == 0:
raise ValueError
return focal_length, calib_coeffs
def setOrigin(self, origin):
self.origin = origin
self.updateProjections()
self.propertiesDidChange.emit()
self.updateBlockProjection()
def setBlocksize(self, blocksize):
self.blocksize = blocksize
self.updateProjections()
self.propertiesDidChange.emit()
self.updateBlockProjection()
def setAffineModel(self, enabled):
if not self.affineModel and enabled:
self.motion_vector_cp1 = self.motion_vector_cp0
self.affineModel = enabled
self.updateProjections()
self.propertiesDidChange.emit()
self.updateBlockProjection()
def setMotionVectorCP0(self, motion_vector):
self.motion_vector_cp0 = motion_vector
self.updateProjections()
self.propertiesDidChange.emit()
self.updateBlockProjection()
def setMotionVectorCP1(self, motion_vector):
self.motion_vector_cp1 = motion_vector
self.updateProjections()
self.propertiesDidChange.emit()
self.updateBlockProjection()
def setMotionVectors(self, motion_vector_cp0, motion_vector_cp1):
self.motion_vector_cp0 = motion_vector_cp0
self.motion_vector_cp1 = motion_vector_cp1
self.propertiesDidChange.emit()
self.updateBlockProjection()
def setPerspectiveScale(self, scale):
self.perspective_scale = scale
self.updateProjections()
self.propertiesDidChange.emit()
self.updateBlockProjection()
def readCalibrationData(self, calib_file):
with open(calib_file, 'r') as file:
calib_json = json.load(file)
focal_length = calib_json['focal_length']
calib_coeffs = np.array([float(coeff) for coeff in calib_json['coefficients']])
if len(calib_coeffs) == 0:
raise ValueError
return focal_length, calib_coeffs
def updateProjections(self):
# Do not update the projections, if the sensor itself is not valid.
def updateBlockProjection(self):
self.bp_threadpool.clear()
if not self.sensor_dimensions_valid:
self.didChange.emit()
return
focal_length_px = projections.Projection.to_focal_length_px(self.focal_length_mm, self.sensor_size_mm, self.sensor_size_px)
if self.projection_key == 'calibrated':
px_per_mm = self.sensor_size_px[0]/self.sensor_size_mm[0]
calib_coeffs = self.calibration_coeffs * px_per_mm
self.projection = projections.CalibratedProjection(calib_coeffs, focal_length_px)
else:
self.projection = self.projections[self.projection_key](focal_length_px)
self.perspective = projections.PerspectiveProjection(focal_length_px)
self.fov_180_radius = self.projection.radius(np.deg2rad(90))
self.bp_threadpool.clear()
worker = BlockProjectionWorker(self.perspective,
self.projection,
self.samples_per_edge,
......@@ -180,21 +206,21 @@ class MainViewModel(QObject):
self.block_ctr_px = result.block_ctr_px
self.block_ctr_pym = result.block_ctr_pym
self.block_ctr_pxm = result.block_ctr_pxm
self.didChange.emit()
self.blockProjectionsDidChange.emit()
def setFisheyeImages(self, images):
self._fisheye_images = images
self._current_idx = 0
self.updatePerspectiveImages()
self.didChange.emit()
self.imagesDidChange.emit()
def nextFisheyeImage(self):
self._current_idx += 1
self.didChange.emit()
self.imagesDidChange.emit()
def previousFisheyeImage(self):
self._current_idx -= 1
self.didChange.emit()
self.imagesDidChange.emit()
@property
def currentFisheyeImage(self):
......@@ -211,13 +237,14 @@ class MainViewModel(QObject):
if key in self._perspective_images:
return self._perspective_images[key]
else:
None
return None
def updatePerspectiveImages(self):
self._perspective_images = {}
self.didChange.emit()
self.ip_threadpool.clear()
if not self.sensor_dimensions_valid:
return
self._perspective_images = {}
self.imagesDidChange.emit()
for image_file in self._fisheye_images:
for scale in range(1, 11):
worker = InterpolationWorker(image_file, scale, self.perspective, self.projection, self.center)
......@@ -227,7 +254,7 @@ class MainViewModel(QObject):
@Slot(InterpolationWorker.Result)
def interpolationDidFinish(self, result: InterpolationWorker.Result):
self._perspective_images[(result.image_file, result.scale)] = result.interpolated_image
self.didChange.emit()
self.imagesDidChange.emit()
def deleteLater(self):
self.bp_threadpool.clear()
......
from PySide2.QtCore import QObject, QRunnable, Slot, Signal
class Worker(QRunnable):
"""Worker thread for running background tasks."""
def __init__(self, fn, *args, **kwargs):
super().__init__()
# Store constructor arguments (re-used for processing)
self.fn = fn
self.args = args
self.kwargs = kwargs
self.signals = WorkerSignals()
@Slot()
def run(self):
result = self.fn(
*self.args, **self.kwargs,
)
self.signals.result.emit(result)
self.signals.finished.emit()
class WorkerSignals(QObject):
"""
Defines the signals available from a running worker thread.
Supported signals are:
finished
No data
error
`tuple` (exctype, value, traceback.format_exc() )
result
`object` data returned from processing, anything
"""
finished = Signal()
error = Signal(tuple)
result = Signal(object)
......@@ -12,7 +12,7 @@ class DistortionVisualizationWidget(QWidget):
super().__init__(parent)
self.viewModel = viewModel
self.viewModel.didChange.connect(self.viewModelChanged)
self.viewModel.propertiesDidChange.connect(self.viewModelChanged)
# Fisheye visualization
self.fisheyeLabel = QLabel(self)
......
......@@ -10,7 +10,8 @@ class FisheyeBackgroundWidget(QWidget):
def __init__(self, parent, viewModel: MainViewModel):
super().__init__(parent)
self.viewModel = viewModel
self.viewModel.didChange.connect(self.repaint)
self.viewModel.propertiesDidChange.connect(self.update)
self.viewModel.imagesDidChange.connect(self.update)
self.show()
......@@ -27,7 +28,8 @@ class FisheyeBackgroundWidget(QWidget):
painter.setRenderHint(QPainter.Antialiasing)
painter.fillRect(painter.window(), QColor(255, 255, 255))
self.drawImage(painter)
self.drawFisheyeBoundary(painter)
if self.viewModel.sensor_dimensions_valid:
self.drawFisheyeBoundary(painter)
painter.end()
def drawImage(self, painter):
......
......@@ -10,7 +10,8 @@ class FisheyeBlockWidget(QWidget):
def __init__(self, parent, viewModel: MainViewModel):
super().__init__(parent)
self.viewModel = viewModel
self.viewModel.didChange.connect(self.updatePolygons)
self.viewModel.propertiesDidChange.connect(self.updatePolygons)
self.viewModel.blockProjectionsDidChange.connect(self.updatePolygons)
self.fisheyePolygon = None
self.reprojectPolygon = None
......
......@@ -10,7 +10,8 @@ class PerspectiveBackgroundWidget(QWidget):
def __init__(self, parent, viewModel: MainViewModel):
super().__init__(parent)
self.viewModel = viewModel
self.viewModel.didChange.connect(self.repaint)
self.viewModel.propertiesDidChange.connect(self.update)
self.viewModel.imagesDidChange.connect(self.update)
self.show()
......
......@@ -10,7 +10,8 @@ class PerspectiveBlockWidget(QWidget):
def __init__(self, parent, viewModel: MainViewModel):
super().__init__(parent)
self.viewModel = viewModel
self.viewModel.didChange.connect(self.updatePolygon)
self.viewModel.propertiesDidChange.connect(self.updatePolygon)
self.viewModel.blockProjectionsDidChange.connect(self.updatePolygon)
self.minCoord = None
self.maxCoord = None
......@@ -47,6 +48,8 @@ class PerspectiveBlockWidget(QWidget):
self.perspectivePolygon = perspectivePolygon
self.perspectiveMovedPolygon = perspectiveMovedPolygon
if self.viewModel.affineModel:
if self.viewModel.block_ctr_px is None:
return
ctr_px = self.viewModel.block_ctr_px + sensor_offset_x
ctr_py = self.viewModel.block_ctr_py + sensor_offset_y
ctr_pxm = self.viewModel.block_ctr_pxm + sensor_offset_x
......@@ -114,8 +117,7 @@ class PerspectiveBlockWidget(QWidget):
if self.viewModel.affineModel:
motion_vector_cp0 = (self.initialMV0[0] + relativeMotion.x(), self.initialMV0[1] + relativeMotion.y())
motion_vector_cp1 = (self.initialMV1[0] + relativeMotion.x(), self.initialMV1[1] + relativeMotion.y())
self.viewModel.setMotionVectorCP0(motion_vector_cp0)
self.viewModel.setMotionVectorCP1(motion_vector_cp1)
self.viewModel.setMotionVectors(motion_vector_cp0, motion_vector_cp1)
else:
motion_vector = (self.initialMV0[0] + relativeMotion.x(), self.initialMV0[1] + relativeMotion.y())
self.viewModel.setMotionVectorCP0(motion_vector)
......
......@@ -11,7 +11,7 @@ class PropertiesWidget(QWidget):
def __init__(self, parent, viewModel: MainViewModel):
super().__init__(parent)
self.viewModel = viewModel
self.viewModel.didChange.connect(self.updateValues)
self.viewModel.propertiesDidChange.connect(self.updateValues)
negIntValidator = QIntValidator(-999999, 999999, self)
intValidator = QIntValidator(0, 999999, self)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment