Commit 2b313b75 authored by Stephen Thompson's avatar Stephen Thompson

Merge branch '3-add-configuration'

parents 253a2a55 080cdfd6
Pipeline #3851 failed with stages
in 4 minutes and 34 seconds
......@@ -5,7 +5,6 @@ variables:
stages:
- build
- installer
- test
- deploy
......@@ -19,34 +18,6 @@ stages:
variables:
- $CI_COMMIT_MESSAGE =~ /\[skip[ _-]build?\]/i
.build-install-template: &build-install-template
script:
- tox -e installer
artifacts:
paths:
- dist/
expire_in: 1 week
# The win/mac/linux build stages inherit settings
# from build-install-template
build linux installer:
<<: *build-skip-template
<<: *build-install-template
tags:
- shared-linux
build mac installer:
<<: *build-skip-template
<<: *build-install-template
tags:
- shared-mac
build windows installer:
<<: *build-skip-template
<<: *build-install-template
tags:
- shared-win
build docs:
<<: *build-skip-template
script:
......
......@@ -33,7 +33,7 @@ Example usage:
::
python sksurgeryspherefitting.py polydata_in.vtp --output polydatata_in.vtp
python sksurgeryspherefitting.py polydata_in.vtp --output polydatata_out.vtp --config conf.json
It was created in part to provide a simple demonstration of algorithm development as part of a
program of SNAPPY Tutorials, but also provides a useful service should you want to fit a sphere
......
{
"initial values" : [85.0, -95.0, -208.0, 4.5],
"fixed radius" : 4.5
}
{
"initial values" : [85.0, -95.0, -208.0, 5.0],
"bounds" : [[ 70.0, -120.0, -230.0, 3.0 ],
[ 120.0, -70.0, -180.0, 10.0 ]]
}
This diff is collapsed.
......@@ -2,7 +2,7 @@
# It is used by pip to manage software dependencies. It is not to be
# confused with the software requirements, which are listed in
# doc/requirements.rst
numpy
numpy>=1.11
scipy
vtk
scikit-surgeryvtk
......@@ -2,10 +2,13 @@
""" Module for fitting a sphere to a list of 3D points """
#scipy has a nice least squares optimisor
from scipy.optimize import leastsq
from scipy.optimize import least_squares
import numpy
def fit_sphere_least_squares(x_values, y_values, z_values, initial_parameters):
def fit_sphere_least_squares(x_values, y_values, z_values, initial_parameters,
bounds=
((-numpy.inf, -numpy.inf, -numpy.inf, -numpy.inf),
(numpy.inf, numpy.inf, numpy.inf, numpy.inf))):
"""
Uses scipy's least squares optimisor to fit a sphere to a set
of 3D Points
......@@ -17,8 +20,11 @@ def fit_sphere_least_squares(x_values, y_values, z_values, initial_parameters):
coordinates.
:param: an array containing four initial values (centre, and radius)
"""
return leastsq(_calculate_residual_sphere, initial_parameters,
args=(x_values, y_values, z_values))
return least_squares(_calculate_residual_sphere, initial_parameters,
bounds=bounds,
method='trf',
jac='3-point',
args=(x_values, y_values, z_values))
def _calculate_residual_sphere(parameters, x_values, y_values, z_values):
"""
......
......@@ -27,6 +27,15 @@ def main(args=None):
help="Write the fitted sphere to file"
)
# ADD OPTINAL ARGUMENTS
parser.add_argument("-c", "--config",
required=False,
type=str,
default="",
help="An optional configuration file."
)
version_string = __version__
friendly_version_string = version_string if version_string else 'unknown'
parser.add_argument(
......@@ -38,4 +47,4 @@ def main(args=None):
args = parser.parse_args(args)
run_demo(args.model, args.output)
run_demo(args.model, args.output, args.config)
......@@ -2,29 +2,50 @@
"""Uses sphere fitting to fit to vtk model"""
import vtk
from numpy import inf, mean, std
from sksurgeryvtk.models.vtk_surface_model import VTKSurfaceModel
from sksurgerycore.configuration.configuration_manager import (
ConfigurationManager
)
from sksurgeryspherefitting.algorithms import sphere_fitting
def run_demo(model_file_name, output=""):
def run_demo(model_file_name, output="", configfile=False):
""" Run the application """
model = VTKSurfaceModel(model_file_name, [1., 0., 0.])
x_values = model.get_points_as_numpy()[:, 0]
y_values = model.get_points_as_numpy()[:, 1]
z_values = model.get_points_as_numpy()[:, 2]
initial_parameters = [0.0, 0.0, 0.0, 0.0]
initial_parameters = [mean(x_values), mean(y_values), mean(z_values),
std(x_values)]
bounds = ((-inf, -inf, -inf, -inf),
(inf, inf, inf, inf))
if configfile:
configurer = ConfigurationManager(configfile)
configuration = configurer.get_copy()
if "initial values" in configuration:
initial_parameters = configuration.get("initial values")
if "bounds" in configuration:
bounds = configuration.get("bounds")
if "fixed radius" in configuration:
radius = configuration.get("fixed radius")
bounds = ((-inf, -inf, -inf, radius - 1e-6),
(inf, inf, inf, radius + 1e-6))
result = sphere_fitting.fit_sphere_least_squares(x_values,
y_values,
z_values,
initial_parameters)
initial_parameters,
bounds=bounds)
print("Result is {}".format(result))
if output != "":
sphere = vtk.vtkSphereSource()
sphere.SetCenter(result[0][0], result[0][1], result[0][2])
sphere.SetRadius(result[0][3])
sphere.SetCenter(result.x[0], result.x[1], result.x[2])
sphere.SetRadius(result.x[3])
sphere.SetThetaResolution(60)
sphere.SetPhiResolution(60)
......
......@@ -3,33 +3,23 @@
"""scikit-surgery-sphere-fitting tests"""
import numpy
#from sksurgeryspherefitting.ui.sksurgeryspherefitting_demo import run_demo
from sksurgeryvtk.models.vtk_surface_model import VTKSurfaceModel
from sksurgeryspherefitting.algorithms import sphere_fitting
import six
# Pytest style
#def test_using_pytest_sksurgeryspherefitting():
# x = 1
# y = 2
# verbose = False
# multiply = False
# expected_answer = 3
# assert run_demo(x, y, multiply, verbose) == expected_answer
def test_fit_sphere_least_squares():
"""Test that it fits a sphere with some gaussian noise"""
x_centre = 1.74839
y_centre = 167.0899222
z_centre = 200.738829
x_centre = 1.0
y_centre = 167.0
z_centre = 200.0
radius = 7.5
radius = 7.543589
#some arrays to fit data to
xs=numpy.ndarray(shape=(1000,),dtype=float )
ys=numpy.ndarray(shape=(1000,),dtype=float )
zs=numpy.ndarray(shape=(1000,),dtype=float )
x_values = numpy.ndarray(shape=(1000,), dtype=float)
y_values = numpy.ndarray(shape=(1000,), dtype=float)
z_values = numpy.ndarray(shape=(1000,), dtype=float)
#fill the arrays with points uniformly spread on
#a sphere centred at x,y,z with radius radius
......@@ -37,22 +27,60 @@ def test_fit_sphere_least_squares():
numpy.random.seed(seed=0)
for i in range(1000):
#make a random vector
x=numpy.random.uniform(-1.0, 1.0)
y=numpy.random.uniform(-1.0, 1.0)
z=numpy.random.uniform(-1.0, 1.0)
x_vector = numpy.random.uniform(-1.0, 1.0)
y_vector = numpy.random.uniform(-1.0, 1.0)
z_vector = numpy.random.uniform(-1.0, 1.0)
#scale it to length radius
length=numpy.sqrt( (x)**2 + (y)**2 + (z)**2 )
length = numpy.sqrt((x_vector)**2 + (y_vector)**2 + (z_vector)**2)
factor = radius / length
xs[i] = x*factor + x_centre
ys[i] = y*factor + y_centre
zs[i] = z*factor + z_centre
x_values[i] = x_vector*factor + x_centre
y_values[i] = y_vector*factor + y_centre
z_values[i] = z_vector*factor + z_centre
parameters = [0.0, 0.0, 0.0, 0.0]
result = sphere_fitting.fit_sphere_least_squares (xs, ys, zs, parameters)
numpy.testing.assert_approx_equal (result[0][0], x_centre, significant = 10)
numpy.testing.assert_approx_equal (result[0][1], y_centre, significant = 10)
numpy.testing.assert_approx_equal (result[0][2], z_centre, significant = 10)
numpy.testing.assert_approx_equal (result[0][3], radius, significant = 10)
result = sphere_fitting.fit_sphere_least_squares(x_values, y_values,
z_values, parameters)
numpy.testing.assert_approx_equal(result.x[0], x_centre, significant=10)
numpy.testing.assert_approx_equal(result.x[1], y_centre, significant=10)
numpy.testing.assert_approx_equal(result.x[2], z_centre, significant=10)
numpy.testing.assert_approx_equal(result.x[3], radius, significant=10)
def test_regression_test1():
"""A regression test with manually init"""
parameters = [136.0, 151.0, -91.0, 8.0]
model = VTKSurfaceModel("data/CT_Level_1.vtp", [1., 0., 0.])
x_values = model.get_points_as_numpy()[:, 0]
y_values = model.get_points_as_numpy()[:, 1]
z_values = model.get_points_as_numpy()[:, 2]
result = sphere_fitting.fit_sphere_least_squares(x_values,
y_values,
z_values,
parameters)
numpy.testing.assert_approx_equal(result.x[0], 136.571, significant=3)
numpy.testing.assert_approx_equal(result.x[1], 151.973, significant=3)
numpy.testing.assert_approx_equal(result.x[2], -95.518, significant=3)
numpy.testing.assert_approx_equal(result.x[3], 8.119, significant=3)
def test_regression_test2():
"""A regression test with fixed radius"""
model = VTKSurfaceModel("data/US_Sphere_2.vtp", [1., 0., 0.])
x_values = model.get_points_as_numpy()[:, 0]
y_values = model.get_points_as_numpy()[:, 1]
z_values = model.get_points_as_numpy()[:, 2]
parameters = [numpy.mean(x_values), numpy.mean(y_values),
numpy.mean(z_values), 4.5]
radius = 4.5
epsilon = 1e-6
bounds = ((-numpy.inf, -numpy.inf, -numpy.inf, radius-epsilon),
(numpy.inf, numpy.inf, numpy.inf, radius+epsilon))
result = sphere_fitting.fit_sphere_least_squares(x_values,
y_values,
z_values,
parameters,
bounds=bounds)
numpy.testing.assert_approx_equal(result.x[0], 86.474, significant=3)
numpy.testing.assert_approx_equal(result.x[1], -97.243, significant=3)
numpy.testing.assert_approx_equal(result.x[2], -208.528, significant=3)
numpy.testing.assert_approx_equal(result.x[3], 4.500, significant=3)
......@@ -4,10 +4,28 @@
from sksurgeryspherefitting.ui.sksurgeryspherefitting_demo import run_demo
def test_fit_sphere_least_squares_demo():
def test_fit_sphere_least_sqs_demo():
""" test the run demo entry point """
model_name = 'data/CT_Level_1.vtp'
output_name = 'out_temp.vtp'
run_demo ( model_name, output_name )
run_demo(model_name, output_name)
def test_fit_sphere_config_demo():
""" test the run demo entry point """
model_name = 'data/US_Sphere_2.vtp'
config_name = 'data/config_init_with_bounds.json'
run_demo(model_name, configfile=config_name)
def test_fit_sphere_config_rad_demo():
""" test the run demo entry point """
model_name = 'data/US_Sphere_2.vtp'
config_name = 'data/config_fixed_radius.json'
run_demo(model_name, configfile=config_name)
......@@ -19,7 +19,7 @@ commands = coverage erase
basepython=python3.6
deps=pylint
{[testenv]deps}
commands=pylint --rcfile=tests/pylintrc sksurgeryspherefitting
commands=pylint --rcfile=tests/pylintrc sksurgeryspherefitting tests
[testenv:docs]
basepython=python3.6
......
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