Commit 9095d2a9 authored by Stephen Thompson's avatar Stephen Thompson

Issue #1 fix tests

parent 43649209
Pipeline #3724 passed with stages
in 11 minutes and 19 seconds
# coding=utf-8
""" Algorithms for the surgery evaluation application """
import vtk
from numpy import inf, eye
from sksurgerynditracker.nditracker import NDITracker
from sksurgeryarucotracker.arucotracker import ArUcoTracker
from sksurgeryvtk.models. vtk_surface_model_directory_loader \
import VTKSurfaceModelDirectoryLoader
#from sksurgeryeval.shapes.cone import VTKConeModel
from numpy import inf
def point_in_locator(point, point_locators, radius=1.0):
"""
Tests whether a point is within a set distance of any of a
"""
Tests whether a point is within a set distance of any of a
list of point locators.
:param point: the point to test, in 3D (x,y,z)
:param point_locators: a list of vtkPointLocators
:param radius: optional search radius in mm (default=1.0)
:return locator: the index of the nearest point locator, (-1 if no locators within radius)
:return locator: the index of the nearest point locator,
-1 if no locators within radius)
:return distance: distance to nearest point_locator
:raises: delegates to vtk
"""
minumum_distance = numpy.inf
minumum_distance = inf
locator_index = -1
for index, locator in enumerate(point_locators):
distance = vtk.mutable(0.0)
if locator.FindClosestPointWithinRadius(radius,point,distance) == -1:
continue
if distance > mimumum_distance:
continue
minumum_distance = distance
locator_index = index
distance = vtk.mutable(0.0)
if locator.FindClosestPointWithinRadius(radius, point, distance) == -1:
continue
if distance > minumum_distance:
continue
minumum_distance = distance
locator_index = index
return locator_index, minumum_distance
inside=pl.FindClosestPointWithinRadius(5.0,(0.0,0.0,0.0),distance)
reader = vtk.vtkSTLReader()
reader.SetFileName('phantom_patch_00.stl')
def np2vtk(mat):
"""
Converts a Numpy array to a vtk matrix
:param: the number array, should be 4x4
:return: a vtk 4x4 matrix
:raises: ValueError when matrix is not 4x4
"""
if mat.shape == (4, 4):
obj = vtk.vtkMatrix4x4()
for i in range(4):
for j in range(4):
obj.SetElement(i, j, mat[i, j])
return obj
raise ValueError('Array must be 4x4')
def configure_tracker(config):
"""
Configures the tracking system.
:param: A dictionary containing configuration data
:return: The tracker object
:raises: KeyError if no tracker entry in config
"""
if "tracker type" not in config:
raise KeyError('Tracker configuration requires tracker type')
tracker_type = config.get("tracker type")
tracker = None
if tracker_type in ("vega", "polaris", "aurora", "dummy"):
tracker = NDITracker(config)
if tracker_type in "aruco":
tracker = ArUcoTracker(config)
tracker.start_tracking()
return tracker
reader.Update()
source = reader.GetOutput()
pl = vtk.vtkPointLocator()
#this builds the point locator, which could be time consuming for a large set,
#we want to pass it
pl.SetDataSet(source)
def populate_models(path_name, model_to_world=eye(4, 4)):
"""
Loads vtk models from a directory and returns
a list of vtk actors and associated vtkPointLocators
:param: pathname: directory where models are
:param: model_to_world: optional
:return: locators
:return: actors
"""
models = []
#This isn't very useful, it returns the ID of the closest point
pl.FindClosestPoint(0,0,0)
loader = VTKSurfaceModelDirectoryLoader(path_name)
models = loader.models
locators = []
#this gives us -1 if no points are within the radius
#otherwise gives the point id
for model in models:
model.set_model_transform(np2vtk(model_to_world))
model.transform_filter.Update()
point_locator = vtk.vtkPointLocator()
point_locator.SetDataSet(model.source)
locators.append(point_locator)
return input_x + input_y
return models, locators
"""
A class to provide the background image
"""
from numpy import zeros, uint8
from sksurgeryimage.utilities.weisslogo import WeissLogo
class OverlayBackground():
"""
Provides the background image for the overlay
window.
"""
def __init__(self, config):
"""
Initialises and configures class to provide a background image.
Image can be a WEISS logo, or blank.
:param: A configuration dictionary
:raises: RunTimeError, KeyError
"""
self._logo_maker = None
self._blank_image = None
if config.get("logo"):
self._logo_maker = WeissLogo()
else:
self._blank_image = zeros(shape=[512, 512, 3], dtype=uint8)
def next_image(self):
"""
Returns a background image.
The behaviour is determined by the configuration
dictionary used at init.
"""
if self._logo_maker is not None:
image = self._logo_maker.get_noisy_logo()
else:
image = self._blank_image
return image
# -*- coding: utf-8 -*-
"""
VTK pipeline to represent a surface model via a vtkPolyData.
"""
import vtk
import sksurgeryvtk.models.vtk_surface_model as vbs
# pylint: disable=no-member
class VTKConeModel(vbs.VTKSurfaceModel):
"""
Class to create a VTK surface model of a cone.
"""
def __init__(self, height, radius, colour, name, visibility=True,
opacity=1.0):
"""
Creates a new surface model.
:param height: the height of the cone
:param diameter: the radius of the cone
:param name: a name for the model
:param colour: (R,G,B) where each are floats [0-1]
:param visibility: boolean, True|False
:param opacity: float [0,1]
"""
super(VTKConeModel, self).__init__(None, colour, visibility,
opacity)
self.name = name
cone = vtk.vtkConeSource()
cone.SetResolution(88)
cone.SetRadius(radius)
cone.SetHeight(height)
cone.Update()
self.source = cone.GetOutput()
#this is from super init, have to redo as we now have data
self.normals = None
self.normals = vtk.vtkPolyDataNormals()
self.normals.SetInputData(self.source)
self.normals.SetAutoOrientNormals(True)
self.normals.SetFlipNormals(False)
self.transform = vtk.vtkTransform()
self.transform.Identity()
self.transform_filter = vtk.vtkTransformPolyDataFilter()
self.transform_filter.SetInputConnection(self.normals.GetOutputPort())
self.transform_filter.SetTransform(self.transform)
self.mapper = vtk.vtkPolyDataMapper()
self.mapper.SetInputConnection(self.transform_filter.GetOutputPort())
self.mapper.Update()
self.actor.SetMapper(self.mapper)
......@@ -14,19 +14,12 @@ def main(args=None):
parser = argparse.ArgumentParser(description='scikit-surgery-evaluation')
## ADD POSITIONAL ARGUMENTS
parser.add_argument("x",
type=int,
help="1st number")
parser.add_argument("-c", "--config",
type=str,
help="A configuration file")
parser.add_argument("y",
type=int,
help="2nd number")
# ADD OPTINAL ARGUMENTS
parser.add_argument("-m", "--multiply",
action="store_true",
help="Enable multiplication of inputs."
)
parser.add_argument("-v", "--verbose",
action="store_true",
......@@ -42,4 +35,4 @@ def main(args=None):
args = parser.parse_args(args)
run_demo(args.x, args.y, args.multiply, args.verbose)
run_demo(args.config, args.verbose)
# coding=utf-8
"""Hello world demo module"""
from sksurgeryeval.algorithms import addition, multiplication
def run_demo(input_x, input_y, multiply, verbose):
import sys
from PySide2.QtWidgets import QApplication
from sksurgerycore.configuration.configuration_manager import (
ConfigurationManager
)
from sksurgeryeval.widgets.overlay import OverlayApp
def run_demo(configfile, verbose):
""" Run the application """
if multiply:
result = multiplication.multiply_two_numbers(input_x, input_y)
configurer = ConfigurationManager(configfile)
else:
result = addition.add_two_numbers(input_x, input_y)
app = QApplication([])
configuration = configurer.get_copy()
if verbose:
if multiply:
print("Calculating {} * {}".format(input_x, input_y))
else:
print("Calculating {} + {}".format(input_x, input_y))
print("Starting overlay app")
print("Result is {}".format(result))
overlay = OverlayApp(configuration)
overlay.start()
return result
sys.exit(app.exec_())
# coding=utf-8
"""Main loop for tracking visualisation"""
#from sksurgerytrackervisualisation.shapes import cone, cylinder
"""Main loop for surgery evaluation"""
from math import isnan
from sksurgeryutils.common_overlay_apps import OverlayBaseApp
from sksurgerytrackervisualisation.algorithms.algorithms import (
np2vtk, configure_tracker, populate_models)
from sksurgerytrackervisualisation.algorithms.background_image import \
from sksurgeryeval.algorithms.algorithms import (
configure_tracker, populate_models)
from sksurgeryeval.algorithms.background_image import \
OverlayBackground
class OverlayApp(OverlayBaseApp):
"""Inherits from OverlayBaseApp, adding code to move vtk models
based on input from a scikitsurgery tracker"""
"""Inherits from OverlayBaseApp, adding code to test the
proximity of a tracked object to a set of vtk objects"""
def __init__(self, config):
"""Overides overlay base app's init, to initialise the
......@@ -25,8 +24,8 @@ class OverlayApp(OverlayBaseApp):
self.timer = None
self.save_frame = None
if "image" in config:
self.bg_image = OverlayBackground(config.get("image"))
if "logo" in config:
self.bg_image = OverlayBackground(config)
else:
default_config = {"logo" : True}
self.bg_image = OverlayBackground(default_config)
......
......@@ -32,7 +32,7 @@ unsafe-load-any-extension=no
# A comma-separated list of package or module names from where C extensions may
# be loaded. Extensions are loading into the active Python interpreter and may
# run arbitrary code
extension-pkg-whitelist=numpy
extension-pkg-whitelist=numpy, PySide2, vtk
# Allow optimization of some AST trees. This will activate a peephole AST
# optimizer, which will apply various small optimizations. For instance, it can
......
......@@ -2,26 +2,18 @@
"""scikit-surgery-evaluation tests"""
import pytest
from sksurgeryeval.ui.sksurgeryeval_demo import run_demo
from sksurgeryeval.algorithms import addition, multiplication
import six
from sksurgeryeval.algorithms.algorithms import point_in_locator, np2vtk, configure_tracker, populate_models
# Pytest style
def test_using_pytest_sksurgeryeval():
x = 1
y = 2
verbose = False
multiply = False
with pytest.raises(ValueError):
run_demo("empty", True)
expected_answer = 3
assert run_demo(x, y, multiply, verbose) == expected_answer
def test_populate_models():
def test_addition():
populate_models("data")
assert addition.add_two_numbers(1, 2) == 3
def test_multiplication():
assert multiplication.multiply_two_numbers(2, 2) == 4
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