Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
AmpScan
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
26
Issues
26
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Joshua Steer
AmpScan
Commits
13398e31
Commit
13398e31
authored
Jul 26, 2019
by
jp6g18
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'Jack' into 'master'
Merge in Jack's changes Closes
#45
and
#43
See merge request
!23
parents
164f6938
7097056c
Pipeline
#883
passed with stage
in 43 seconds
Changes
21
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
583 additions
and
204 deletions
+583
-204
.gitlab-ci.yml
.gitlab-ci.yml
+12
-2
AmpScan/align.py
AmpScan/align.py
+19
-14
AmpScan/core.py
AmpScan/core.py
+74
-20
AmpScan/registration.py
AmpScan/registration.py
+10
-4
AmpScan/smooth.py
AmpScan/smooth.py
+1
-1
AmpScan/ssm.py
AmpScan/ssm.py
+20
-18
AmpScan/trim.py
AmpScan/trim.py
+31
-23
GUIs/AmpScanGUI.py
GUIs/AmpScanGUI.py
+125
-64
tests/__init__py.py
tests/__init__py.py
+0
-0
tests/ascii_examples/sample_stl_sphere_ASCII.stl
tests/ascii_examples/sample_stl_sphere_ASCII.stl
+0
-0
tests/core_tests.py
tests/core_tests.py
+0
-5
tests/pca_tests/stl_file_3.stl
tests/pca_tests/stl_file_3.stl
+3
-0
tests/sample_test.py
tests/sample_test.py
+0
-53
tests/stl_file.stl
tests/stl_file.stl
+3
-0
tests/stl_file_2.stl
tests/stl_file_2.stl
+3
-0
tests/stl_file_3.stl
tests/stl_file_3.stl
+0
-0
tests/test_basics.py
tests/test_basics.py
+34
-0
tests/test_core.py
tests/test_core.py
+167
-0
tests/test_smoothing.py
tests/test_smoothing.py
+23
-0
tests/test_trim.py
tests/test_trim.py
+30
-0
tests/util.py
tests/util.py
+28
-0
No files found.
.gitlab-ci.yml
View file @
13398e31
sample_job
:
script
:
python tests/sample_test.py
#doctests and unitests:
# script: pytest --doctest-modules -v --ignore=GUIs
# Temporarily added back old testing
unittests
:
script
:
python -m unittest discover tests -v
core doctests
:
script
:
python -m doctest -v AmpScan/core.py
registration doctests
:
script
:
python -m doctest -v AmpScan/registration.py
align doctests
:
script
:
python -m doctest -v AmpScan/align.py
AmpScan/align.py
View file @
13398e31
...
...
@@ -10,8 +10,13 @@ import vtk
import
math
from
scipy
import
spatial
from
scipy.optimize
import
minimize
from
.core
import
AmpObject
from
.ampVis
import
vtkRenWin
from
AmpScan.core
import
AmpObject
from
AmpScan.ampVis
import
vtkRenWin
# For doc examples
import
os
staticfh
=
os
.
getcwd
()
+
"
\\
tests
\\
stl_file.stl"
movingfh
=
os
.
getcwd
()
+
"
\\
tests
\\
stl_file_2.stl"
class
align
(
object
):
...
...
@@ -41,9 +46,9 @@ class align(object):
Examples
--------
>>> static = Amp
Scan.Amp
Object(staticfh)
>>> moving = Amp
Scan.Amp
Object(movingfh)
>>> al =
AmpScan.
align(moving, static).m
>>> static = AmpObject(staticfh)
>>> moving = AmpObject(movingfh)
>>> al = align(moving, static).m
"""
...
...
@@ -176,9 +181,9 @@ class align(object):
Examples
--------
>>> static = Amp
Scan.Amp
Object(staticfh)
>>> moving = Amp
Scan.Amp
Object(movingfh)
>>> al =
AmpScan.
align(moving, static, method='linPoint2Plane').m
>>> static = AmpObject(staticfh)
>>> moving = AmpObject(movingfh)
>>> al = align(moving, static, method='linPoint2Plane').m
"""
cn
=
np
.
c_
[
np
.
cross
(
mv
,
sn
),
sn
]
...
...
@@ -229,9 +234,9 @@ class align(object):
Examples
--------
>>> static = Amp
Scan.Amp
Object(staticfh)
>>> moving = Amp
Scan.Amp
Object(movingfh)
>>> al =
AmpScan.
align(moving, static, method='linPoint2Point').m
>>> static = AmpObject(staticfh)
>>> moving = AmpObject(movingfh)
>>> al = align(moving, static, method='linPoint2Point').m
"""
mCent
=
mv
-
mv
.
mean
(
axis
=
0
)
...
...
@@ -271,9 +276,9 @@ class align(object):
Examples
--------
>>> static = Amp
Scan.Amp
Object(staticfh)
>>> moving = Amp
Scan.Amp
Object(movingfh)
>>> al =
AmpScan.
align(moving, static, method='optPoint2Point', opt='SLSQP').m
>>> static = AmpObject(staticfh)
>>> moving = AmpObject(movingfh)
>>> al = align(moving, static, method='optPoint2Point', opt='SLSQP').m
"""
X
=
np
.
zeros
(
6
)
...
...
AmpScan/core.py
View file @
13398e31
...
...
@@ -6,11 +6,17 @@ Copyright: Joshua Steer 2018, Joshua.Steer@soton.ac.uk
"""
import
numpy
as
np
import
os
import
struct
from
.trim
import
trimMixin
from
.smooth
import
smoothMixin
from
.analyse
import
analyseMixin
from
.ampVis
import
visMixin
from
AmpScan.trim
import
trimMixin
from
AmpScan.smooth
import
smoothMixin
from
AmpScan.analyse
import
analyseMixin
from
AmpScan.ampVis
import
visMixin
# The file path used in doc examples
filename
=
os
.
getcwd
()
+
"
\\
tests
\\
stl_file.stl"
class
AmpObject
(
trimMixin
,
smoothMixin
,
analyseMixin
,
visMixin
):
r
"""
...
...
@@ -36,8 +42,7 @@ class AmpObject(trimMixin, smoothMixin, analyseMixin, visMixin):
Examples
-------
>>> fh = 'test.stl'
>>> amp = AmpScan.AmpObject(fh)
>>> amp = AmpObject(filename)
"""
...
...
@@ -149,13 +154,12 @@ class AmpObject(trimMixin, smoothMixin, analyseMixin, visMixin):
Examples
--------
>>> fh = 'test.stl'
>>> amp = AmpObject(fh, unify=False)
>>> amp = AmpObject(filename, unify=False)
>>> amp.vert.shape
(
600
, 3)
(
44832
, 3)
>>> amp.unifyVert()
>>> amp.vert.shape
(
125
, 3)
(
7530
, 3)
"""
# Requires numpy 1.13
...
...
@@ -314,7 +318,16 @@ class AmpObject(trimMixin, smoothMixin, analyseMixin, visMixin):
Translation in [x, y, z]
"""
self
.
vert
[:]
+=
trans
# Check that trans is array like
if
isinstance
(
trans
,
(
list
,
np
.
ndarray
,
tuple
)):
# Check that trans has exactly 3 dimensions
if
len
(
trans
)
==
3
:
self
.
vert
[:]
+=
trans
else
:
raise
ValueError
(
"Translation has incorrect dimensions. Expected 3 but found: "
+
str
(
len
(
trans
)))
else
:
raise
TypeError
(
"Translation is not array_like: "
+
trans
)
def
centre
(
self
):
r
"""
...
...
@@ -337,10 +350,15 @@ class AmpObject(trimMixin, smoothMixin, analyseMixin, visMixin):
Examples
--------
>>> amp = AmpObject(
'test.stl'
)
>>> amp = AmpObject(
filename
)
>>> ang = [np.pi/2, -np.pi/4, np.pi/3]
>>> amp.rotateAng(ang, ang='rad')
"""
# Check that ang is valid
if
ang
not
in
(
'rad'
,
'deg'
):
raise
ValueError
(
"Ang expected 'rad' or 'deg' but {} was found"
.
format
(
ang
))
if
isinstance
(
rot
,
(
tuple
,
list
,
np
.
ndarray
)):
R
=
self
.
rotMatrix
(
rot
,
ang
)
self
.
rotate
(
R
,
norms
)
...
...
@@ -359,6 +377,18 @@ class AmpObject(trimMixin, smoothMixin, analyseMixin, visMixin):
norms: boolean, default True
"""
if
isinstance
(
R
,
(
list
,
tuple
)):
# Make R a np array if its a list or tuple
R
=
np
.
array
(
R
,
np
.
float
)
elif
not
isinstance
(
R
,
np
.
ndarray
):
# If
raise
TypeError
(
"Expected R to be array-like but found: "
+
str
(
type
(
R
)))
if
len
(
R
)
!=
3
or
len
(
R
[
0
])
!=
3
:
# Incorrect dimensions
if
isinstance
(
R
,
np
.
ndarray
):
raise
ValueError
(
"Expected 3x3 array, but found: {}"
.
format
(
R
.
shape
))
else
:
raise
ValueError
(
"Expected 3x3 array, but found: 3x"
+
str
(
len
(
R
)))
self
.
vert
[:,
:]
=
np
.
dot
(
self
.
vert
,
R
.
T
)
if
norms
is
True
:
self
.
norm
[:,
:]
=
np
.
dot
(
self
.
norm
,
R
.
T
)
...
...
@@ -380,9 +410,15 @@ class AmpObject(trimMixin, smoothMixin, analyseMixin, visMixin):
"""
if
R
is
not
None
:
self
.
rotate
(
R
,
True
)
if
isinstance
(
R
,
(
tuple
,
list
,
np
.
ndarray
)):
self
.
rotate
(
R
,
True
)
else
:
raise
TypeError
(
"Expecting array-like rotation, but found: "
+
type
(
R
))
if
T
is
not
None
:
self
.
translate
(
T
)
if
isinstance
(
T
,
(
tuple
,
list
,
np
.
ndarray
)):
self
.
translate
(
T
)
else
:
raise
TypeError
(
"Expecting array-like translation, but found: "
+
type
(
T
))
@
staticmethod
...
...
@@ -396,7 +432,7 @@ class AmpObject(trimMixin, smoothMixin, analyseMixin, visMixin):
rot: array_like
Rotation around [x, y, z]
ang: str, default 'rad'
Specif
t if the Euler angles are in degrees or radians
Specif
y if the Euler angles are in degrees or radians
Returns
-------
...
...
@@ -404,8 +440,20 @@ class AmpObject(trimMixin, smoothMixin, analyseMixin, visMixin):
The calculated 3x3 rotation matrix
"""
# Check that rot is valid
if
not
isinstance
(
rot
,
(
tuple
,
list
,
np
.
ndarray
)):
raise
TypeError
(
"Expecting array-like rotation, but found: "
+
type
(
rot
))
elif
len
(
rot
)
!=
3
:
raise
ValueError
(
"Expecting 3 arguments but found: {}"
.
format
(
len
(
rot
)))
# Check that ang is valid
if
ang
not
in
(
'rad'
,
'deg'
):
raise
ValueError
(
"Ang expected 'rad' or 'deg' but {} was found"
.
format
(
ang
))
if
ang
==
'deg'
:
rot
=
np
.
deg2rad
(
rot
)
[
angx
,
angy
,
angz
]
=
rot
Rx
=
np
.
array
([[
1
,
0
,
0
],
[
0
,
np
.
cos
(
angx
),
-
np
.
sin
(
angx
)],
...
...
@@ -429,8 +477,14 @@ class AmpObject(trimMixin, smoothMixin, analyseMixin, visMixin):
The axis in which to flip the mesh
"""
self
.
vert
[:,
axis
]
*=
-
1.0
# Switch face order to normals face same direction
self
.
faces
[:,
[
1
,
2
]]
=
self
.
faces
[:,
[
2
,
1
]]
self
.
calcNorm
()
self
.
calcVNorm
()
if
isinstance
(
axis
,
int
):
if
0
<=
axis
<
3
:
# Check axis is between 0-2
self
.
vert
[:,
axis
]
*=
-
1.0
# Switch face order to normals face same direction
self
.
faces
[:,
[
1
,
2
]]
=
self
.
faces
[:,
[
2
,
1
]]
self
.
calcNorm
()
self
.
calcVNorm
()
else
:
raise
ValueError
(
"Expected axis to be within range 0-2 but found: {}"
.
format
(
axis
))
else
:
raise
TypeError
(
"Expected axis to be int, but found: {}"
.
format
(
type
(
axis
)))
AmpScan/registration.py
View file @
13398e31
...
...
@@ -6,9 +6,14 @@ Copyright: Joshua Steer 2018, Joshua.Steer@soton.ac.uk
import
numpy
as
np
import
copy
from
scipy
import
spatial
from
.core
import
AmpObject
from
AmpScan
.core
import
AmpObject
import
matplotlib.pyplot
as
plt
# For the doc examples
import
os
basefh
=
os
.
getcwd
()
+
"
\\
tests
\\
stl_file.stl"
targfh
=
os
.
getcwd
()
+
"
\\
tests
\\
stl_file_2.stl"
class
registration
(
object
):
r
"""
Registration methods between two AmpObject meshes. This function morphs the baseline
...
...
@@ -36,9 +41,10 @@ class registration(object):
Examples
--------
>>> baseline = AmpScan.AmpObject(basefh)
>>> target = AmpScan.AmpObject(targfh)
>>> reg = AmpScan.registration(steps=10, neigh=10, smooth=1).reg
>>> from AmpScan.core import AmpObject
>>> baseline = AmpObject(basefh)
>>> target = AmpObject(targfh)
>>> reg = registration(baseline, target, steps=10, neigh=10, smooth=1).reg
"""
def
__init__
(
self
,
baseline
,
target
,
method
=
'point2plane'
,
*
args
,
**
kwargs
):
...
...
AmpScan/smooth.py
View file @
13398e31
...
...
@@ -52,7 +52,7 @@ class smoothMixin(object):
def
smoothValues
(
self
,
n
=
1
):
"""
Function to apply a simple laplacian smooth to the values array.
Identical to the vertex smoothing ex
pect it applies the smoothing
Identical to the vertex smoothing ex
cept it applies the smoothing
to the values
Parameters
...
...
AmpScan/ssm.py
View file @
13398e31
...
...
@@ -19,20 +19,20 @@ class pca(object):
Examples
--------
>>> import os
>>> p = pca()
>>> p.importFolder(
'/path/'
)
>>> p.
baseline('dir/baselinefh.stl'
)
>>> p.register(save
= '/regpath/'
)
>>> p.importFolder(
os.getcwd()+"\\tests\\pca_tests"
)
>>> p.
setBaseline(os.getcwd()+"\\tests\\stl_file_3.stl"
)
>>> p.register(save
=os.getcwd()+"\\tests\\pca_tests\\"
)
>>> p.pca()
>>> sfs = [
0, 0.1, -0.5 ... 0
]
>>> sfs = [
1, 2
]
>>> newS = p.newShape(sfs)
"""
def
__init__
(
self
):
self
.
shapes
=
[]
def
setBaseline
(
self
,
baseline
):
r
"""
Function to set the baseline mesh used for registration of the
...
...
@@ -45,8 +45,7 @@ class pca(object):
"""
self
.
baseline
=
AmpObject
(
baseline
,
'limb'
)
def
importFolder
(
self
,
path
,
unify
=
True
):
r
"""
Function to import multiple stl files from folder into the pca object
...
...
@@ -64,7 +63,7 @@ class pca(object):
self
.
shapes
=
[
AmpObject
(
os
.
path
.
join
(
path
,
f
),
'limb'
,
unify
=
unify
)
for
f
in
self
.
fnames
]
for
s
in
self
.
shapes
:
s
.
lp_smooth
(
3
,
brim
=
True
)
def
sliceFiles
(
self
,
height
):
r
"""
Function to run a planar trim on all the training data for the PCA
...
...
@@ -78,7 +77,7 @@ class pca(object):
"""
for
s
in
self
.
shapes
:
s
.
planarTrim
(
height
)
def
register
(
self
,
scale
=
None
,
save
=
None
,
baseline
=
True
):
r
"""
Register all the AmpObject training data to the baseline AmpObject
...
...
@@ -105,8 +104,7 @@ class pca(object):
self
.
X
=
np
.
array
([
r
.
vert
.
flatten
()
for
r
in
self
.
registered
]).
T
if
baseline
is
True
:
self
.
X
=
np
.
c_
[
self
.
X
,
self
.
baseline
.
vert
.
flatten
()]
def
pca
(
self
):
r
"""
Function to run mean centered pca using a singular value decomposition
...
...
@@ -118,8 +116,8 @@ class pca(object):
(
self
.
pca_U
,
self
.
pca_S
,
self
.
pca_V
)
=
np
.
linalg
.
svd
(
X_meanC
,
full_matrices
=
False
)
self
.
pc_weights
=
np
.
dot
(
np
.
diag
(
self
.
pca_S
),
self
.
pca_V
)
self
.
pc_stdevs
=
np
.
std
(
self
.
pc_weights
,
axis
=
1
)
def
newShape
(
self
,
sfs
,
scale
=
'eigs'
):
def
newShape
(
self
,
sfs
,
scale
=
'eigs'
):
r
"""
Function to calculate a new shape based upon the eigenvalues
or stdevs
...
...
@@ -134,11 +132,15 @@ class pca(object):
to standard deviations about the mean
"""
try
:
len
(
sfs
)
==
len
(
self
.
pc_stdevs
)
except
:
ValueError
(
'sfs must be of the same length as the number of '
'principal components'
)
if
not
isinstance
(
sfs
,
(
list
,
tuple
,
np
.
ndarray
)):
raise
TypeError
(
'sfs is invalid type (expected array-like, found: {}'
.
format
(
type
(
sfs
)))
if
len
(
sfs
)
!=
len
(
self
.
pc_stdevs
):
raise
ValueError
(
'sfs must be of the same length as the number of '
'principal components (expected {} but found {})'
.
format
(
len
(
self
.
pc_stdevs
),
len
(
sfs
)))
if
scale
==
'eigs'
:
sf
=
(
self
.
pca_U
*
sfs
).
sum
(
axis
=
1
)
elif
scale
==
'std'
:
sf
=
(
self
.
pca_U
*
self
.
pc_stdevs
*
sfs
).
sum
(
axis
=
1
)
else
:
raise
ValueError
(
"Invalid scale (expected 'eigs' or 'std' but found{}"
.
format
(
scale
))
return
self
.
pca_mean
+
sf
AmpScan/trim.py
View file @
13398e31
...
...
@@ -5,6 +5,12 @@ Copyright: Joshua Steer 2018, Joshua.Steer@soton.ac.uk
"""
import
numpy
as
np
from
numbers
import
Number
import
os
# Used by doc tests
filename
=
os
.
getcwd
()
+
"
\\
tests
\\
stl_file.stl"
class
trimMixin
(
object
):
r
"""
...
...
@@ -26,30 +32,32 @@ class trimMixin(object):
Examples
--------
>>> amp = AmpObject(fh)
>>> from AmpScan import AmpObject
>>> amp = AmpObject(filename)
>>> amp.planarTrim(100, 2)
"""
# if isinstance(height, floa
t):
if
isinstance
(
height
,
Number
)
and
isinstance
(
plane
,
in
t
):
# planar values for each vert on face
fv
=
self
.
vert
[
self
.
faces
,
plane
]
# Number points on each face are above cut plane
fvlogic
=
(
fv
>
height
).
sum
(
axis
=
1
)
# Faces with points both above and below cut plane
adjf
=
self
.
faces
[
np
.
logical_or
(
fvlogic
==
2
,
fvlogic
==
1
)]
# Get adjacent vertices
adjv
=
np
.
unique
(
adjf
)
# Get vert above height and set to height
abvInd
=
adjv
[
self
.
vert
[
adjv
,
plane
]
>
height
]
self
.
vert
[
abvInd
,
plane
]
=
height
# Find all verts above plane
delv
=
self
.
vert
[:,
plane
]
>
height
# Reorder verts to account for deleted one
vInd
=
np
.
cumsum
(
~
delv
)
-
1
self
.
faces
=
self
.
faces
[
fvlogic
!=
3
,
:]
self
.
faces
=
vInd
[
self
.
faces
]
self
.
vert
=
self
.
vert
[
~
delv
,
:]
self
.
values
=
self
.
values
[
~
delv
]
self
.
calcStruct
()
# else:
# raise TypeError("height arg must be a float")
\ No newline at end of file
fv
=
self
.
vert
[
self
.
faces
,
plane
]
# Number points on each face are above cut plane
fvlogic
=
(
fv
>
height
).
sum
(
axis
=
1
)
# Faces with points both above and below cut plane
adjf
=
self
.
faces
[
np
.
logical_or
(
fvlogic
==
2
,
fvlogic
==
1
)]
# Get adjacent vertices
adjv
=
np
.
unique
(
adjf
)
# Get vert above height and set to height
abvInd
=
adjv
[
self
.
vert
[
adjv
,
plane
]
>
height
]
self
.
vert
[
abvInd
,
plane
]
=
height
# Find all verts above plane
delv
=
self
.
vert
[:,
plane
]
>
height
# Reorder verts to account for deleted one
vInd
=
np
.
cumsum
(
~
delv
)
-
1
self
.
faces
=
self
.
faces
[
fvlogic
!=
3
,
:]
self
.
faces
=
vInd
[
self
.
faces
]
self
.
vert
=
self
.
vert
[
~
delv
,
:]
self
.
values
=
self
.
values
[
~
delv
]
self
.
calcStruct
()
else
:
raise
TypeError
(
"height arg must be a float"
)
GUIs/AmpScanGUI.py
View file @
13398e31
...
...
@@ -10,9 +10,9 @@ from PyQt5.QtGui import (QColor, QFontMetrics, QImage, QPainter, QIcon,
QOpenGLVersionProfile
)
from
PyQt5.QtWidgets
import
(
QAction
,
QApplication
,
QGridLayout
,
QHBoxLayout
,
QMainWindow
,
QMessageBox
,
QComboBox
,
QButtonGroup
,
QOpenGLWidget
,
QFileDialog
,
QLabel
,
QPushButton
,
QOpenGLWidget
,
QFileDialog
,
QLabel
,
QPushButton
,
QSlider
,
QWidget
,
QTableWidget
,
QTableWidgetItem
,
QAbstractButton
)
QAbstractButton
,
QErrorMessage
)
class
AmpScanGUI
(
QMainWindow
):
...
...
@@ -107,16 +107,19 @@ class AmpScanGUI(QMainWindow):
Numpy style docstring.
"""
self
.
alCont
=
AlignControls
(
self
.
filesDrop
,
self
)
self
.
alCont
.
show
()
self
.
alCont
.
centre
.
clicked
.
connect
(
self
.
centreMesh
)
self
.
alCont
.
icp
.
clicked
.
connect
(
self
.
runICP
)
self
.
alCont
.
xrotButton
.
buttonClicked
[
QAbstractButton
].
connect
(
self
.
rotatex
)
self
.
alCont
.
yrotButton
.
buttonClicked
[
QAbstractButton
].
connect
(
self
.
rotatey
)
self
.
alCont
.
zrotButton
.
buttonClicked
[
QAbstractButton
].
connect
(
self
.
rotatez
)
self
.
alCont
.
xtraButton
.
buttonClicked
[
QAbstractButton
].
connect
(
self
.
transx
)
self
.
alCont
.
ytraButton
.
buttonClicked
[
QAbstractButton
].
connect
(
self
.
transy
)
self
.
alCont
.
ztraButton
.
buttonClicked
[
QAbstractButton
].
connect
(
self
.
transz
)
if
self
.
objectsReady
(
1
):
self
.
alCont
=
AlignControls
(
self
.
filesDrop
,
self
)
self
.
alCont
.
show
()
self
.
alCont
.
centre
.
clicked
.
connect
(
self
.
centreMesh
)
self
.
alCont
.
icp
.
clicked
.
connect
(
self
.
runICP
)
self
.
alCont
.
xrotButton
.
buttonClicked
[
QAbstractButton
].
connect
(
self
.
rotatex
)
self
.
alCont
.
yrotButton
.
buttonClicked
[
QAbstractButton
].
connect
(
self
.
rotatey
)
self
.
alCont
.
zrotButton
.
buttonClicked
[
QAbstractButton
].
connect
(
self
.
rotatez
)
self
.
alCont
.
xtraButton
.
buttonClicked
[
QAbstractButton
].
connect
(
self
.
transx
)
self
.
alCont
.
ytraButton
.
buttonClicked
[
QAbstractButton
].
connect
(
self
.
transy
)
self
.
alCont
.
ztraButton
.
buttonClicked
[
QAbstractButton
].
connect
(
self
.
transz
)
else
:
show_message
(
"Must be at least 1 object loaded to run align"
)
def
rotatex
(
self
,
button
):
moving
=
str
(
self
.
alCont
.
moving
.
currentText
())
...
...
@@ -178,53 +181,59 @@ class AmpScanGUI(QMainWindow):
self
.
renWin
.
Render
()
def
runICP
(
self
):
static
=
str
(
self
.
alCont
.
static
.
currentText
())
moving
=
str
(
self
.
alCont
.
moving
.
currentText
())
al
=
align
(
self
.
files
[
moving
],
self
.
files
[
static
],
maxiter
=
10
,
method
=
'linPoint2Plane'
).
m
al
.
tform
=
vtk
.
vtkTransform
()
al
.
tform
.
PostMultiply
()
al
.
addActor
()
al
.
actor
.
SetUserTransform
(
al
.
tform
)
alName
=
moving
+
'_al'
self
.
files
[
alName
]
=
al
self
.
filesDrop
.
append
(
alName
)
self
.
fileManager
.
addRow
(
alName
,
self
.
files
[
alName
])
self
.
fileManager
.
setTable
(
static
,
[
1
,
0
,
0
],
0.5
,
2
)
self
.
fileManager
.
setTable
(
moving
,
[
1
,
1
,
1
],
1
,
0
)
self
.
fileManager
.
setTable
(
alName
,
[
0
,
0
,
1
],
0.5
,
2
)
if
hasattr
(
self
,
'alCont'
):
self
.
alCont
.
getNames
()
if
hasattr
(
self
,
'regCont'
):
self
.
regCont
.
getNames
()
if
self
.
objectsReady
(
1
):
static
=
str
(
self
.
alCont
.
static
.
currentText
())
moving
=
str
(
self
.
alCont
.
moving
.
currentText
())
al
=
align
(
self
.
files
[
moving
],
self
.
files
[
static
],
maxiter
=
10
,
method
=
'linPoint2Plane'
).
m
al
.
tform
=
vtk
.
vtkTransform
()
al
.
tform
.
PostMultiply
()
al
.
addActor
()
al
.
actor
.
SetUserTransform
(
al
.
tform
)
alName
=
moving
+
'_al'
self
.
files
[
alName
]
=
al
self
.
filesDrop
.
append
(
alName
)
self
.
fileManager
.
addRow
(
alName
,
self
.
files
[
alName
])
self
.
fileManager
.
setTable
(
static
,
[
1
,
0
,
0
],
0.5
,
2
)
self
.
fileManager
.
setTable
(
moving
,
[
1
,
1
,
1
],
1
,
0
)
self
.
fileManager
.
setTable
(
alName
,
[
0
,
0
,
1
],
0.5
,
2
)
if
hasattr
(
self
,
'alCont'
):
self
.
alCont
.
getNames
()
if
hasattr
(
self
,
'regCont'
):
self
.
regCont
.
getNames
()
else
:
show_message
(
"Must be at least 2 objects loaded to run ICP"
)
def
runRegistration
(
self
):
c1
=
[
31.0
,
73.0
,
125.0
]
c3
=
[
170.0
,
75.0
,
65.0
]
c2
=
[
212.0
,
221.0
,
225.0
]
CMap1
=
np
.
c_
[[
np
.
linspace
(
st
,
en
)
for
(
st
,
en
)
in
zip
(
c1
,
c2
)]]
CMap2
=
np
.
c_
[[
np
.
linspace
(
st
,
en
)
for
(
st
,
en
)
in
zip
(
c2
,
c3
)]]
CMap
=
np
.
c_
[
CMap1
[:,
:
-
1
],
CMap2
]
self
.
CMapN2P
=
np
.
transpose
(
CMap
)
/
255.0
self
.
CMap02P
=
np
.
flip
(
np
.
transpose
(
CMap1
)
/
255.0
,
axis
=
0
)
baseline
=
str
(
self
.
regCont
.
baseline
.
currentText
())
target
=
str
(
self
.
regCont
.
target
.
currentText
())
self
.
fileManager
.
setTable
(
baseline
,
[
1
,
0
,
0
],
0.5
,
0
)
self
.
fileManager
.
setTable
(
target
,
[
0
,
0
,
1
],
0.5
,
0
)
reg
=
registration
(
self
.
files
[
baseline
],
self
.
files
[
target
],
steps
=
5
,
smooth
=
1
).
reg
reg
.
addActor
(
CMap
=
self
.
CMap02P
)
regName
=
target
+
'_reg'
self
.
files
[
regName
]
=
reg
self
.
filesDrop
.
append
(
regName
)
self
.
fileManager
.
addRow
(
regName
,
self
.
files
[
regName
])
if
hasattr
(
self
,
'alCont'
):
self
.
alCont
.
getNames
()
if
hasattr
(
self
,
'regCont'
):
self
.
regCont
.
getNames
()
print
(
'Run the Registration code between %s and %s'
%
(
baseline
,
target
))
if
self
.
objectsReady
(
2
):
# Needs to be at least 2 files to run registration
c1
=
[
31.0
,
73.0
,
125.0
]
c3
=
[
170.0
,
75.0
,
65.0
]
c2
=
[
212.0
,
221.0
,
225.0
]
CMap1
=
np
.
c_
[[
np
.
linspace
(
st
,
en
)
for
(
st
,
en
)
in
zip
(
c1
,
c2
)]]
CMap2
=
np
.
c_
[[
np
.
linspace
(
st
,
en
)
for
(
st
,
en
)
in
zip
(
c2
,
c3
)]]
CMap
=
np
.
c_
[
CMap1
[:,
:
-
1
],
CMap2
]
self
.
CMapN2P
=
np
.
transpose
(
CMap
)
/
255.0
self
.
CMap02P
=
np
.
flip
(
np
.
transpose
(
CMap1
)
/
255.0
,
axis
=
0
)
baseline
=
str
(
self
.
regCont
.
baseline
.
currentText
())
target
=
str
(
self
.
regCont
.
target
.
currentText
())
self
.
fileManager
.
setTable
(
baseline
,
[
1
,
0
,
0
],
0.5
,
0
)
self
.
fileManager
.
setTable
(
target
,
[
0
,
0
,
1
],
0.5
,
0
)
reg
=
registration
(
self
.
files
[
baseline
],
self
.
files
[
target
],
steps
=
5
,
smooth
=
1
).
reg
reg
.
addActor
(
CMap
=
self
.
CMap02P
)
regName
=
target
+
'_reg'
self
.
files
[
regName
]
=
reg
self
.
filesDrop
.
append
(
regName
)
self
.
fileManager
.
addRow
(
regName
,
self
.
files
[
regName
])
if
hasattr
(
self
,
'alCont'
):
self
.
alCont
.
getNames
()
if
hasattr
(
self
,
'regCont'
):
self
.
regCont
.
getNames
()
print
(
'Run the Registration code between %s and %s'
%
(
baseline
,
target
))
else
:
show_message
(
"Must be at least 2 objects loaded to run registration"
)
def
register
(
self
):
"""
...
...
@@ -256,13 +265,15 @@ class AmpScanGUI(QMainWindow):
"""
FEname
=
QFileDialog
.
getOpenFileName
(
self
,
'Open file'
,
filter
=
"FE results (*.npy)"
)
self
.
renWin
.
setnumViewports
(
1
)
self
.
FE
=
AmpObject
([
FEname
[
0
],],
stype
=
'FE'
)
self
.
AmpObj
.
lp_smooth
()
self
.
AmpObj
.
addActor
(
CMap
=
self
.
AmpObj
.
CMap02P
,
bands
=
5
)
self
.
AmpObj
.
actor
.
setScalarRange
(
smin
=
0.0
,
smax
=
50
)
self
.
renWin
.
renderActors
(
self
.
FE
.
actor
,
shading
=
True
)
self
.
renWin
.
setScalarBar
(
self
.
FE
.
actor
)
if
FEname
[
0
]
!=
""
:
# Check that there was a file selected
print
(
FEname
)
self
.
renWin
.
setnumViewports
(
1
)