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

Compute and show interpolated perspective image

parent 8c4e7ed9
......@@ -9,13 +9,14 @@ Download or clone this repository and enter the main folder. Python 3 is require
run the application. Furthermore, the following dependencies need to be
installed:
* numpy
* imageio
* pyside2
It is recommended to install the dependencies in a dedicated python environment. Using
the anaconda/miniconda package manager, a new environment named `FishUI` with all
dependencies pre-installed can be created by executing
```sh
$ conda create -n FishUI numpy pyside2
$ conda create -n FishUI numpy imageio pyside2
```
in the terminal. The FishUI application can then be started by activating the environment
and running the fishui module.
......
import json
from PySide2.QtCore import QObject, Signal
import numpy as np
import imageio
from fishui import projections
from fishui import coordinate_conversion
......@@ -56,9 +57,10 @@ class MainViewModel(QObject):
self.uwc_sign = None
self.updateProjections()
# Fisheye background image
# Background images
self._fisheye_images = []
self._current_fisheye_idx = 0
self._perspective_images = []
self._current_idx = 0
@property
def sensor_dimensions_valid(self):
......@@ -86,6 +88,7 @@ class MainViewModel(QObject):
raise Exception("Calibration file argument is valid for calibrated projection only.")
self.projection_key = projection_key
self.updateProjections()
self.updatePerspectiveImages()
def setOrigin(self, origin):
self.origin = origin
......@@ -259,19 +262,61 @@ class MainViewModel(QObject):
def setFisheyeImages(self, images):
self._fisheye_images = images
self._current_fisheye_idx = 0
self._current_idx = 0
self.blockSignals(True)
self.updatePerspectiveImages()
self.blockSignals(False)
self.didChange.emit()
def nextFisheyeImage(self):
self._current_fisheye_idx += 1
self._current_idx += 1
self.didChange.emit()
def previousFisheyeImage(self):
self._current_fisheye_idx -= 1
self._current_idx -= 1
self.didChange.emit()
@property
def currentFisheyeImage(self):
if len(self._fisheye_images) == 0:
return None
return self._fisheye_images[self._current_fisheye_idx % len(self._fisheye_images)]
return self._fisheye_images[self._current_idx % len(self._fisheye_images)]
@property
def currentPerspectiveImage(self):
if len(self._perspective_images) == 0:
return None
return self._perspective_images[self._current_idx % len(self._perspective_images)][
self.perspective_scale - 1]
def updatePerspectiveImages(self):
self._perspective_images = []
for fisheye_image in self._fisheye_images:
image = imageio.imread(fisheye_image)
scale_images = []
for scale in range(1, 11):
scale_images.append(self.interpolatePerspectiveImage(image, scale))
self._perspective_images.append(scale_images)
self.didChange.emit()
def interpolatePerspectiveImage(self, fisheye_image, scale):
yp, xp = np.mgrid[:fisheye_image.shape[0], :fisheye_image.shape[1]]
yp = yp - self.center[1]
xp = xp - self.center[0]
yp = yp * scale
xp = xp * scale
rp, phi = coordinate_conversion.cartesian_to_polar(yp, xp)
rf = self.projection.radius(self.perspective.theta(rp))
yf, xf = coordinate_conversion.polar_to_cartesian(rf, phi)
yf = yf + self.center[1]
xf = xf + self.center[0]
# Nearest neighbor
ynn = np.round(yf).astype(np.int)
xnn = np.round(xf).astype(np.int)
perspective_image = np.zeros(fisheye_image.shape, dtype=fisheye_image.dtype)
mask_low = np.logical_and(0 <= ynn, 0 <= xnn)
mask_high = np.logical_and(ynn < fisheye_image.shape[0], xnn < fisheye_image.shape[1])
mask = np.logical_and(mask_low, mask_high)
perspective_image[mask] = fisheye_image[ynn[mask], xnn[mask]]
return perspective_image
......@@ -3,7 +3,7 @@ from PySide2.QtCore import Qt, Slot
from fishui.MainViewModel import MainViewModel
from fishui.widgets.AspectRatioWidget import AspectRatioWidget
from fishui.widgets.FisheyeWidget import FisheyeWidget
from fishui.widgets.PerspectiveBlockWidget import PerspectiveBlockWidget
from fishui.widgets.PerspectiveWidget import PerspectiveWidget
class DistortionVisualizationWidget(QWidget):
......@@ -28,7 +28,7 @@ class DistortionVisualizationWidget(QWidget):
self.perspectiveLabel.setStyleSheet("font-weight: bold; font-size: 16px")
self.perspectiveLabel.setAlignment(Qt.AlignCenter)
self.perspectiveLabel.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed)
self.perspectiveBlockWidget = AspectRatioWidget(PerspectiveBlockWidget(self, viewModel), self)
self.perspectiveWidget = AspectRatioWidget(PerspectiveWidget(self, viewModel), self)
# Constrain both labels to have same minimum width
fisheye_label_width = self.fisheyeLabel.sizeHint().width()
......@@ -42,7 +42,7 @@ class DistortionVisualizationWidget(QWidget):
layout.addWidget(self.fisheyeLabel, 0, 0)
layout.addWidget(self.fisheyeWidget, 1, 0)
layout.addWidget(self.perspectiveLabel, 0, 1)
layout.addWidget(self.perspectiveBlockWidget, 1, 1)
layout.addWidget(self.perspectiveWidget, 1, 1)
self.setLayout(layout)
......@@ -71,13 +71,13 @@ class DistortionVisualizationWidget(QWidget):
self.fisheyeLabel.setGraphicsEffect(None)
self.fisheyeWidget.setGraphicsEffect(None)
self.perspectiveLabel.setGraphicsEffect(None)
self.perspectiveBlockWidget.setGraphicsEffect(None)
self.perspectiveWidget.setGraphicsEffect(None)
aspect_ratio = self.viewModel.sensor_size_px[0]/self.viewModel.sensor_size_px[1]
self.fisheyeWidget.setAspectRatio(aspect_ratio)
self.perspectiveBlockWidget.setAspectRatio(aspect_ratio)
self.perspectiveWidget.setAspectRatio(aspect_ratio)
else:
self.overlay.setVisible(True)
self.fisheyeLabel.setGraphicsEffect(QGraphicsBlurEffect(self))
self.fisheyeWidget.setGraphicsEffect(QGraphicsBlurEffect(self))
self.perspectiveLabel.setGraphicsEffect(QGraphicsBlurEffect(self))
self.perspectiveBlockWidget.setGraphicsEffect(QGraphicsBlurEffect(self))
self.perspectiveWidget.setGraphicsEffect(QGraphicsBlurEffect(self))
import sys
from PySide2.QtGui import QPainter, QColor, QPainterPath, QPen, QPalette, QPolygonF, QImage
from PySide2.QtWidgets import QWidget, QApplication, QMenu, QAction, QFileDialog
from PySide2.QtCore import QSize, Qt, QPointF, QRect, QRectF, Slot
from fishui.MainViewModel import MainViewModel
class PerspectiveBackgroundWidget(QWidget):
def __init__(self, parent, viewModel: MainViewModel):
super().__init__(parent)
self.viewModel = viewModel
self.viewModel.didChange.connect(self.repaint)
self.show()
def sizeHint(self):
return QSize(400, 400)
def paintEvent(self, event):
painter = QPainter()
painter.begin(self)
painter.setWindow(QRect(0,
0,
self.viewModel.sensor_size_px[0],
self.viewModel.sensor_size_px[1]))
painter.setRenderHint(QPainter.Antialiasing)
painter.fillRect(painter.window(), QColor(255, 255, 255))
self.drawImage(painter)
painter.end()
def drawImage(self, painter):
image = self.viewModel.currentPerspectiveImage
if image is None:
return
image = QImage(image.data,
image.shape[1],
image.shape[0],
image.strides[0],
QImage.Format_RGB888)
rect = QRectF(0,
0,
self.viewModel.sensor_size_px[0],
self.viewModel.sensor_size_px[1])
painter.drawImage(rect, image, rect)
......@@ -66,7 +66,6 @@ class PerspectiveBlockWidget(QWidget):
painter.setWindow(QRect(0, 0, self.viewModel.perspective_scale * self.viewModel.sensor_size_px[0],
self.viewModel.perspective_scale * self.viewModel.sensor_size_px[1]))
painter.setRenderHint(QPainter.Antialiasing)
painter.fillRect(painter.window(), QColor(255, 255, 255))
self.drawPolygon(event, painter)
if self.viewModel.affineModel:
self.drawControlPointBlock(event, painter)
......
from PySide2.QtWidgets import QWidget, QGridLayout, QLabel, QGraphicsBlurEffect, QSizePolicy
from PySide2.QtCore import Qt, Slot, QSize
from fishui.MainViewModel import MainViewModel
from fishui.widgets.PerspectiveBackgroundWidget import PerspectiveBackgroundWidget
from fishui.widgets.PerspectiveBlockWidget import PerspectiveBlockWidget
# TODO: Overlaying widgets using QWidget leads to repainting all widgets if child widget is updated. Use QGraphics...?
class PerspectiveWidget(QWidget):
def __init__(self, parent, viewModel: MainViewModel):
super().__init__(parent)
self.perspectiveBackgroundWidget = PerspectiveBackgroundWidget(self, viewModel)
self.perspectiveBlockWidget = PerspectiveBlockWidget(self, viewModel)
self.show()
def sizeHint(self):
return QSize(400, 400)
def resizeEvent(self, event):
w = event.size().width()
h = event.size().height()
self.perspectiveBackgroundWidget.setGeometry(0, 0, w, h)
self.perspectiveBlockWidget.setGeometry(0, 0, w, h)
......@@ -2,6 +2,8 @@ from .AspectRatioWidget import AspectRatioWidget
from .FisheyeBackgroundWidget import FisheyeBackgroundWidget
from .FisheyeBlockWidget import FisheyeBlockWidget
from .FisheyeWidget import FisheyeWidget
from .PerspectiveBackgroundWidget import PerspectiveBackgroundWidget
from .PerspectiveBlockWidget import PerspectiveBlockWidget
from .PerspectiveWidget import PerspectiveWidget
from .DistortionVisualizationWidget import DistortionVisualizationWidget
from .PropertiesWidget import PropertiesWidget
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