...

Commits (9)
 ... ... @@ -6,7 +6,6 @@ import numpy as np from sksurgerycore.algorithms.errors \ import validate_procrustes_inputs, compute_fre # pylint: disable=invalid-name, line-too-long ... ... @@ -53,8 +52,9 @@ def orthogonal_procrustes(fixed, moving): # Note: numpy factors h = u * np.diag(s) * v svd = np.linalg.svd(H) # Arun equation 13 X = np.matmul(svd[2].transpose(), svd[0].transpose()) # Replace Arun Equation 13 with Fitzpatrick, chapter 8, page 470, # to avoid reflections, see issue #19 X = _fitzpatricks_X(svd) # Arun step 5, after equation 13. det_X = np.linalg.det(X) ... ... @@ -67,7 +67,6 @@ def orthogonal_procrustes(fixed, moving): " and no singular values are close enough to zero") if det_X < 0 and np.any(np.isclose(svd[1], np.zeros((3, 1)))): # Implement 2a in section VI in Arun paper. v_prime = svd[2].transpose() v_prime[0][2] *= -1 ... ... @@ -85,3 +84,18 @@ def orthogonal_procrustes(fixed, moving): fre = compute_fre(fixed, moving, R, T) return R, T, fre def _fitzpatricks_X(svd): """This is from Fitzpatrick, chapter 8, page 470. it's used in preference to Arun's equation 13, X = np.matmul(svd[2].transpose(), svd[0].transpose()) to avoid reflections. """ VU = np.matmul(svd[2].transpose(), svd[0]) detVU = np.linalg.det(VU) diag = np.eye(3, 3) diag[2][2] = detVU X = np.matmul(svd[2].transpose(), np.matmul(diag, svd[0].transpose())) return X
 ... ... @@ -13,8 +13,10 @@ Design principles: | its up to the consumer to know where to find the data. """ import os import json import copy import sksurgerycore.utilities.file_utilities as fu import sksurgerycore.utilities.validate_file as f ... ... @@ -30,18 +32,37 @@ class ConfigurationManager: def __init__(self, file_name, write_on_setter=False ): """ Constructor. """ f.validate_is_file(file_name) abs_file = fu.get_absolute_path_of_file(file_name) f.validate_is_file(abs_file) if write_on_setter: f.validate_is_writable_file(file_name) f.validate_is_writable_file(abs_file) with open(file_name, "r") as read_file: with open(abs_file, "r") as read_file: self.config_data = json.load(read_file) self.file_name = file_name self.file_name = abs_file self.write_on_setter = write_on_setter def get_file_name(self): """ Returns the absolute filename that was used when the ConfigurationManager was created. :return: str absolute file name """ return self.file_name def get_dir_name(self): """ Returns the directory name of the file that was used when creating the ConfigurationManager. :return: str dir name """ return os.path.dirname(self.file_name) def get_copy(self): """ Returns a copy of the data read from file. ... ...
 ... ... @@ -236,12 +236,14 @@ class TransformManager: return for candidate in candidates: if candidate in list_of_nodes: continue else: list_of_nodes.append(candidate) self.__get_list(candidate, after, list_of_nodes) if list_of_nodes[-1] == after: break else: list_of_nodes.pop() list_of_nodes.append(candidate) self.__get_list(candidate, after, list_of_nodes) if list_of_nodes[-1] == after: break list_of_nodes.pop()
 # -*- coding: utf-8 -*- """ File processing utils. """ import os def get_absolute_path_of_file(file_name, dir_name=None): """ Filenames in our .json config could be absolute or relative to the current working dir. This method tries to find the valid, full file path. :param file_name: :param dir_name: prefix, for example, the dirname of our .json file. :return: absolute path name of file if found, otherwise None. """ if not file_name: raise ValueError("Empty file_name.") file = None if os.path.isabs(file_name): if os.path.isfile(file_name): file = file_name elif dir_name is not None: joined = os.path.join(dir_name, file_name) if os.path.isfile(joined): file = joined elif os.getcwd() is not None: joined = os.path.join(os.getcwd(), file_name) if os.path.isfile(joined): file = joined return file
 ... ... @@ -65,7 +65,10 @@ def test_identity_result(): def test_reflection_data(): """This seems to be testing that a rotation is prefered to a reflection. To transform these points you can either reflect through yz, or rotate 180 about y""" fixed = np.zeros((4, 3)) fixed[0][1] = 1 fixed[2][0] = 2 ... ... @@ -80,11 +83,60 @@ def test_reflection_data(): expected_rotation = np.eye(3) expected_rotation[0][0] = -1 expected_rotation[2][2] = -1 print (np.linalg.det(rotation)) print (rotation) assert np.allclose(rotation, expected_rotation, 0.0000001) assert np.allclose(translation, np.zeros((3, 1)), 0.0000001) assert error < 0.0000001 def test_reflection_BARD_data(): """This is a reflection test using data taken from using BARD at the 2019 summer school. This data set will give you reflections, unless you replace equation 13 from Arun with Fitzpatrick's modification. This was done at issue #19""" ct_fids = np.zeros((4,3)) world_fids = np.zeros((4,3)) ct_fids[0][0] = 37.82 ct_fids[0][1] = 52.99 ct_fids[0][2] = -191.00 ct_fids[1][0] = 310.90 ct_fids[1][1] = 70.36 ct_fids[1][2] = -196.50 ct_fids[2][0] = 171.98 ct_fids[2][1] = 57.59 ct_fids[2][2] = -376.96 ct_fids[3][0] = 176.56 ct_fids[3][1] = 71.30 ct_fids[3][2] = -7.42 world_fids[0][0] = -7.060625292517708829e+01 world_fids[0][1] = -4.340731346419998715e+01 world_fids[0][2] = -7.678145283688323275e+01 world_fids[1][0] = -8.290290497289761618e+01 world_fids[1][1] = 2.365685716505672360e+02 world_fids[1][2] = -6.248063455571644909e+01 world_fids[2][0] = 1.061984137321565527e+02 world_fids[2][1] = 1.054365783777915624e+02 world_fids[2][2] = -6.347752061138085367e+01 world_fids[3][0] = -2.658595567434794020e+02 world_fids[3][1] = 9.556619277927123335e+01 world_fids[3][2] = -7.335614476329364209e+01 rotation, translation, error = p.orthogonal_procrustes(world_fids, ct_fids) expected_rotation = np.eye(3) expected_rotation[0][0] = -0.04933494 expected_rotation[0][1] = -0.01438556 expected_rotation[0][2] = -0.99867869 expected_rotation[1][0] = 0.99212873 expected_rotation[1][1] = 0.11451658 expected_rotation[1][2] = -0.05066094 expected_rotation[2][0] = 0.11509405 expected_rotation[2][1] = -0.99331717 expected_rotation[2][2] = 0.00862266 assert np.allclose(rotation, expected_rotation, 0.0000001) def test_translation_result(): ... ...
 # -*- coding: utf-8 -*- import os import pytest import sksurgerycore.configuration.configuration_manager as cm ... ... @@ -20,6 +21,8 @@ def test_constructor_with_valid_file(): manager = cm.ConfigurationManager("tests/data/FordPrefect.json") assert manager is not None assert manager.get_file_name() is not None assert manager.get_dir_name() == os.path.dirname(manager.get_file_name()) def test_setter_getter_loop(): ... ...
 # -*- coding: utf-8 -*- """ File processing utils. """ import os import pytest import sksurgerycore.utilities.file_utilities as fu def test_get_absolute_file(): cwd = os.getcwd() this_file = "tests/utilities/test_file_utilities.py" fp = fu.get_absolute_path_of_file(this_file) assert fp == os.path.join(cwd, fp) fp2 = fu.get_absolute_path_of_file(fp) assert fp2 == fp fp3 = fu.get_absolute_path_of_file(this_file, cwd) assert fp3 == fp
 # content of: tox.ini , put in same dir as setup.py [tox] envlist = py27,py36,lint envlist = py36,lint skipsdist = True [travis] python = 2.7: py27 3.6: py36, docs, lint [testenv] ... ... @@ -31,13 +30,6 @@ commands = sphinx-build -M html . build basepython=python3.6 commands=pyinstaller --onefile sksurgerycore.py --noconfirm --windowed [testenv:pip2] basepython=python2.7 changedir=pip_test skip_install=True commands = pip install {posargs} sksurgerycore --help [testenv:pip3] basepython=python3.6 changedir=pip_test ... ...