Commit 5da1ca3b authored by Thomas Dowrick's avatar Thomas Dowrick
Browse files

Finish refactoring

parent b28e874d
......@@ -46,9 +46,11 @@ class DuplicateOverlayWindow(OverlayOnVideoFeedCropRecord):
if self.source_window.roi:
start_x, start_y = self.source_window.roi[0]
end_x, end_y = self.source_window.roi[1]
self.vtk_overlay_window.set_video_image(self.source_window.img[start_y:end_y,
start_x:end_x,
:])
self.vtk_overlay_window. \
set_video_image(self.source_window.img[start_y:end_y,
start_x:end_x,
:])
else:
self.vtk_overlay_window.set_video_image(self.source_window.img)
......@@ -57,95 +59,76 @@ class DuplicateOverlayWindow(OverlayOnVideoFeedCropRecord):
def on_record_start(self):
""" Don't want to call the base class version, so override."""
pass
def save_scene_to_file(self):
""" Don't want to call the base class version, so override."""
pass
def on_record_stop(self):
""" Don't want to call the base class version, so override."""
pass
def set_roi(self):
""" Don't want to call the base class version, so override."""
pass
class BlankFeed:
def on_record_start(self):
""" Don't want to call the base class version, so override."""
pass
def save_scene_to_file(self):
""" Don't want to call the base class version, so override."""
pass
class FakeOverlayOnVideoFeed:
""" Implement empty methods to replicate OverlayOnVideoFeed. """
def start(self):
""" Intentionally Blank. """
def stop(self):
""" Intentionally Blank. """
def on_record_start(self):
""" Intentionally Blank. """
def on_record_stop(self):
""" Don't want to call the base class version, so override."""
pass
""" Intentionally Blank. """
def set_roi(self):
""" Don't want to call the base class version, so override."""
pass
""" Intentionally Blank. """
def add_vtk_models(self, models):
pass
def add_vtk_overlay(self):
""" Don't want to call the base class version, so override."""
pass
class FakeVTKOverlayWindow:
""" Implement empty methods to replicate VTKOverlayWindow. """
def save_scene_to_file(self, fname):
""" Intentionally Blank. """
def get_foreground_camera(self):
pass
""" Intentionally Blank. """
def set_foreground_camera(self, camera):
""" Blank method. """
pass
""" Intentionally Blank. """
def set_screen(self):
""" Blank method. """
pass
""" Intentionally Blank. """
def set_geometry(self):
""" Blank. """
pass
""" Intentionally Blank. """
def start(self):
pass
def add_vtk_models(self, models):
""" Intentionally Blank. """
def stop(self):
pass
class StereoViewerBase(QtWidgets.QWidget):
"""
Base class for StereoViewers.
Child classes implment the left/right/ui video feeds as appropriate.
"""
def __init__(self):
# Left/right stereo views and UI view.
# These are overridden in subclasses.
# Included here for clarity.
super().__init__()
self.UI = BlankFeed()
self.left_view = BlankFeed()
self.right_view = BlankFeed()
self.ui_view = BlankFeed()
self.left_overlay = BlankFeed()
self.right_overlay = BlankFeed()
self.ui_overlay = BlankFeed()
def setup_widgets(self):
""" Sync the cameras between the widgets,
and setup Qt output signal. """
self.UI.screenshot_button.clicked.connect(self.on_screenshot_clicked)
self.UI.record_button.clicked.connect(self.on_record_start_clicked)
self.UI.crop_button.clicked.connect(self.on_crop_clicked)
self.UI.autocrop_button.clicked.connect(self.on_autocrop_started)
self.UI.exit_signal.connect(self.run_before_quit)
self.sync_camera_view_between_windows()
self.auto_cropper = AutoCropBlackBorder(threshold=50)
self.autocrop_timer = QTimer()
self.autocrop_timer.timeout.connect(self.update_autocrop)
self.UI.exit_signal.connect(self.run_before_quit)
self.sync_camera_view_between_windows()
def sync_camera_view_between_windows(self):
"""
......@@ -202,7 +185,7 @@ class StereoViewerBase(QtWidgets.QWidget):
"""
Check if each widget has been placed on it's own screen.
:param screens: List of screen numbers corresponding to the screen
each widget is displayed on.
each widget is displayed on.
:type screens: list of ints
"""
......@@ -254,8 +237,15 @@ class StereoViewerBase(QtWidgets.QWidget):
def on_screenshot_clicked(self):
""" Save a screenshot to disk, using date and time as filename """
fname_base = datetime.datetime.now().strftime("%Y-%m-%d.%H-%M-%S")
fname_left = fname_base + '-LEFT.png'
fname_base = 'outputs/' \
+ datetime.datetime.now().strftime("%Y-%m-%d.%H-%M-%S")
if self.__class__.__name__ == "MonoViewer":
fname_left = fname_base + '-MONO.png'
else:
fname_left = fname_base + '-LEFT.png'
fname_right = fname_base + '-RIGHT.png'
self.left_overlay.save_scene_to_file(fname_left)
......@@ -267,14 +257,20 @@ class StereoViewerBase(QtWidgets.QWidget):
left.
The proper StereoViewer class extends this method to also record
the right view."""
self.video_fname_base = 'outputs/' + \
fname_base = 'outputs/' + \
datetime.datetime.now().strftime("%Y-%m-%d.%H-%M-%S")
fname_left = self.video_fname_base + '-LEFT.avi'
if self.__class__.__name__ == "MonoViewer":
fname_left = fname_base + '-MONO.avi'
else:
fname_left = fname_base + '-LEFT.avi'
fname_right = fname_base + '-RIGHT.avi'
self.left_view.output_filename = fname_left
self.left_view.on_record_start()
fname_right = self.video_fname_base + '-RIGHT.avi'
self.right_view.output_filename = fname_right
self.right_view.on_record_start()
......@@ -292,35 +288,10 @@ class StereoViewerBase(QtWidgets.QWidget):
self.UI.record_button.clicked.connect(self.on_record_start_clicked)
def on_crop_clicked(self):
""" Set the ROI on the left view, and copy it to the right. """
self.left_view.set_roi()
self.right_view.roi = self.left_view.roi
class MonoViewer(StereoViewerBase):
"""
Generates a VTK interactor UI with a video stream as background
:param video_source: OpenCV compatible video source (int or filename)
"""
def __init__(self, video_source):
super().__init__()
LOGGER.info("Creating Mono Viewer")
self.left_view = OverlayOnVideoFeedCropRecord(video_source)
self.left_overlay = self.left_view.vtk_overlay_window
#TODO: Change this bit, don't really want ui_view diff from UI
self.UI = UI(self.left_view)
self.auto_cropper = AutoCropBlackBorder(threshold = 50)
self.autocrop_timer = QTimer()
self.autocrop_timer.timeout.connect(self.update_autocrop)
# TODO: Move this to super class, then call super at end of this init
self.setup_widgets()
def setup_UI_callbacks(self):
self.UI.autocrop_button.clicked.connect(self.on_autocrop_started)
def on_autocrop_started(self):
""" Start auto cropping. """
self.UI.autocrop_button.setText("Disable Auto Crop")
......@@ -348,36 +319,52 @@ class MonoViewer(StereoViewerBase):
self.left_view.roi = roi
self.right_view.roi = roi
class MonoViewer(StereoViewerBase):
"""
Generates a VTK interactor UI with a single video stream as background.
:param video_source: OpenCV compatible video source (int or filename)
"""
def __init__(self, video_source):
LOGGER.info("Creating Mono Viewer")
self.left_view = OverlayOnVideoFeedCropRecord(video_source)
self.left_overlay = self.left_view.vtk_overlay_window
self.right_view = FakeOverlayOnVideoFeed()
self.right_overlay = FakeVTKOverlayWindow()
self.ui_view = FakeOverlayOnVideoFeed()
self.ui_overlay = FakeVTKOverlayWindow()
self.UI = UI(self.left_view)
super().__init__()
class MockStereoViewer(StereoViewerBase):
"""
Mock stereo viewer, duplicating a single webcam input
Mock stereo viewer, duplicating a single camera input
to multiple screens.
:param video_source: OpenCV compatible video source (int or filename)
"""
def __init__(self, video_source):
super().__init__()
LOGGER.info("Creating Mock Stereo Viewwer")
#TODO: Move to superclass
self.left_view = OverlayOnVideoFeedCropRecord(video_source)
self.left_overlay = self.left_view.vtk_overlay_window
self.right_view = DuplicateOverlayWindow()
self.right_view.set_source_window(self.left_view)
self.right_overlay = self.right_view.vtk_overlay_window
self.ui_view = DuplicateOverlayWindow()
self.ui_view.set_source_window(self.left_view)
self.left_overlay = self.left_view.vtk_overlay_window
self.right_overlay = self.right_view.vtk_overlay_window
self.ui_overlay = self.ui_view.vtk_overlay_window
self.UI = UI(self.ui_view)
self.setup_widgets()
super().__init__()
class StereoViewer(StereoViewerBase):
"""Stereo viewer, creates an overlay window for each
......@@ -389,21 +376,19 @@ class StereoViewer(StereoViewerBase):
def __init__(self, left_source, right_source):
super().__init__()
LOGGER.info("Creating Stereo Viewer")
self.left_view = OverlayOnVideoFeedCropRecord(left_source)
self.left_overlay = self.left_view.vtk_overlay_window
self.right_view = OverlayOnVideoFeedCropRecord(right_source)
self.right_overlay = self.right_view.vtk_overlay_window
self.ui_view = DuplicateOverlayWindow()
self.ui_view.set_source_window(self.left_view)
self.left_overlay = self.left_view.vtk_overlay_window
self.right_overlay = self.right_view.vtk_overlay_window
self.ui_overlay = self.ui_view.vtk_overlay_window
self.UI = UI(self.ui_view)
self.setup_widgets()
super().__init__()
import numpy as np
import cv2
import time
#coding=utf-8
""" AutoCropper Module. """
from sksurgeryimage.acquire.video_source import TimestampedVideoSource
import numpy as np
class AutoCropBlackBorder:
......@@ -31,10 +30,11 @@ class AutoCropBlackBorder:
""" Given a vector, skip any leading values that are > threshold,
then find the first contiguous range of values that are below the
threshold value.
If the whole vector is above the threshold, return the start/end indexes.
If the whole vector is above the threshold, return the start/end
indexes.
e.g.
[0 0 0 50 50 50 0 0 0 ] return 3, 5 - [50 50 50]
[25 25 0 0 25 25 25 0 0] returns 4, 7 [25 25 25] as the leading 25s are
skipped.
......@@ -85,8 +85,8 @@ class AutoCropBlackBorder:
# Only use the red channel, otherwise we need to take the average along
# two dimensions, which is slow. This should be acceptable for the
# DaVinci.
col_mean = np.mean(img[:,:,1], axis=(0))
row_mean = np.mean(img[:,:,1], axis=(1))
col_mean = np.mean(img[:, :, 1], axis=(0))
row_mean = np.mean(img[:, :, 1], axis=(1))
start_y, end_y = self.get_bounds(row_mean)
start_x, end_x = self.get_bounds(col_mean)
......
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