amaazetools 0.1.2__tar.gz → 0.1.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {amaazetools-0.1.2/amaazetools.egg-info → amaazetools-0.1.3}/PKG-INFO +1 -1
- {amaazetools-0.1.2 → amaazetools-0.1.3}/amaazetools/trimesh.py +131 -79
- {amaazetools-0.1.2 → amaazetools-0.1.3/amaazetools.egg-info}/PKG-INFO +1 -1
- {amaazetools-0.1.2 → amaazetools-0.1.3}/pyproject.toml +1 -1
- {amaazetools-0.1.2 → amaazetools-0.1.3}/LICENSE +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/MANIFEST.in +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/README.md +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/amaazetools/__init__.py +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/amaazetools/dicom.py +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/amaazetools/edge_detection.py +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/amaazetools/mesh_segmentation.py +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/amaazetools/svi.py +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/amaazetools.egg-info/SOURCES.txt +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/amaazetools.egg-info/dependency_links.txt +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/amaazetools.egg-info/requires.txt +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/amaazetools.egg-info/top_level.txt +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/setup.cfg +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/setup.py +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/src/cextensions.c +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/src/memory_allocation.c +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/src/memory_allocation.h +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/src/mesh_operations.c +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/src/mesh_operations.h +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/src/svi_computations.c +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/src/svi_computations.h +0 -0
- {amaazetools-0.1.2 → amaazetools-0.1.3}/src/vector_operations.h +0 -0
|
@@ -18,6 +18,58 @@ import urllib.request as url
|
|
|
18
18
|
|
|
19
19
|
#Non-Class Specific Functions
|
|
20
20
|
|
|
21
|
+
|
|
22
|
+
def marching_cubes(volume,level=None,spacing=(1,1,1)):
|
|
23
|
+
""" SK-Image's marching cubes does not return a clean triangulations -
|
|
24
|
+
often has replicate points, self-triangles, etc. This fixes that.
|
|
25
|
+
|
|
26
|
+
Parameters
|
|
27
|
+
----------
|
|
28
|
+
volume : (l,w,h) float array
|
|
29
|
+
A 3-D grid discretization of the function to be surfaced.
|
|
30
|
+
level : float
|
|
31
|
+
isolevel to extract surface at.
|
|
32
|
+
spacing : (3) float
|
|
33
|
+
denotes the dimensions of each voxel in the volume.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
-------
|
|
38
|
+
p : (n,3) float
|
|
39
|
+
points of triangulation
|
|
40
|
+
t : (m,3) int
|
|
41
|
+
triangles of triangulation
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
p,t,n,val = measure.marching_cubes(volume,level=level,spacing=spacing)
|
|
45
|
+
|
|
46
|
+
#sometimes marching cubes produces... artifacts... so here's a very basic intro to mesh cleaning!
|
|
47
|
+
#first: eliminate repeated points:
|
|
48
|
+
p,index,inv = np.unique(p,axis=0, return_index=True,return_inverse=True)
|
|
49
|
+
|
|
50
|
+
#next: rid triangulation of non-triangles:
|
|
51
|
+
t = inv[t]
|
|
52
|
+
badt = (t[:,0]==t[:,1])+(t[:,1]==t[:,2])+(t[:,2]==t[:,0])
|
|
53
|
+
t = t[badt ==False,:]
|
|
54
|
+
|
|
55
|
+
#remove unreferenced points, if any
|
|
56
|
+
ind = -1*np.ones(p.shape[0],int)
|
|
57
|
+
uni = np.unique(t.flatten()) #these are already sorted for numpy
|
|
58
|
+
ind[uni] = np.arange(uni.shape[0])
|
|
59
|
+
p = p[ind>-1,:]
|
|
60
|
+
t = ind[t]
|
|
61
|
+
|
|
62
|
+
#finally, check right hand rule... this works for ~convex objects, anyway...
|
|
63
|
+
#m = tm.mesh(p,t)
|
|
64
|
+
|
|
65
|
+
#tc = m.face_centers()
|
|
66
|
+
#tn = m.face_normals()
|
|
67
|
+
|
|
68
|
+
#cent = m.points.mean(0)
|
|
69
|
+
#fliporder = np.sum(tn*(tc-cent),1)<0
|
|
70
|
+
#t[fliporder,1:3] = t[fliporder,2:0:-1]
|
|
71
|
+
return p,t
|
|
72
|
+
|
|
21
73
|
def withiness(x):
|
|
22
74
|
""" Computes withiness (how well 1-D data clusters into two groups).
|
|
23
75
|
|
|
@@ -25,7 +77,7 @@ def withiness(x):
|
|
|
25
77
|
----------
|
|
26
78
|
x : (n,1) float array
|
|
27
79
|
A 1-D collection of data.
|
|
28
|
-
|
|
80
|
+
|
|
29
81
|
Returns
|
|
30
82
|
-------
|
|
31
83
|
w : float
|
|
@@ -56,7 +108,7 @@ def pca(P):
|
|
|
56
108
|
----------
|
|
57
109
|
P : (n,d) float array
|
|
58
110
|
A point cloud.
|
|
59
|
-
|
|
111
|
+
|
|
60
112
|
Returns
|
|
61
113
|
-------
|
|
62
114
|
vals : (d,) float arrayy
|
|
@@ -64,13 +116,13 @@ def pca(P):
|
|
|
64
116
|
vecs : (d,d) float array
|
|
65
117
|
The principal component vectors.
|
|
66
118
|
"""
|
|
67
|
-
|
|
119
|
+
|
|
68
120
|
P = P - np.mean(P,axis=0)
|
|
69
121
|
vals,vecs = np.linalg.eig(P.T@P)
|
|
70
122
|
idx = np.argsort(-vals)
|
|
71
123
|
|
|
72
124
|
return vals[idx],vecs[:,idx]
|
|
73
|
-
|
|
125
|
+
|
|
74
126
|
def weighted_pca(P,W):
|
|
75
127
|
""" Computes weighted principal component analysis (PCA) on a point cloud P.
|
|
76
128
|
|
|
@@ -80,7 +132,7 @@ def weighted_pca(P,W):
|
|
|
80
132
|
A point cloud.
|
|
81
133
|
W : (n,) float array
|
|
82
134
|
An array containing the weights of the points.
|
|
83
|
-
|
|
135
|
+
|
|
84
136
|
Returns
|
|
85
137
|
-------
|
|
86
138
|
vals : (d,) float array
|
|
@@ -107,7 +159,7 @@ def power_method(A,tol=1e-12):
|
|
|
107
159
|
A square matrix that one wishes to find the smallest (in absolute value) eigenvalue and corresponding eigenvector of.
|
|
108
160
|
tol : float, default is 1e-12
|
|
109
161
|
The desired tolerance threshold after which to stop iteration.
|
|
110
|
-
|
|
162
|
+
|
|
111
163
|
Parameters
|
|
112
164
|
----------
|
|
113
165
|
l : float
|
|
@@ -137,7 +189,7 @@ def pca_smallest_eig_powermethod(X,center=True):
|
|
|
137
189
|
A point cloud.
|
|
138
190
|
center : boolean, default is True
|
|
139
191
|
Data is centered if True.
|
|
140
|
-
|
|
192
|
+
|
|
141
193
|
Returns
|
|
142
194
|
-------
|
|
143
195
|
A float array of size (3,) containing the last principal component vector.
|
|
@@ -162,7 +214,7 @@ def pca_smallest_eig(X,center=True):
|
|
|
162
214
|
A point cloud.
|
|
163
215
|
center : boolean, default is True
|
|
164
216
|
Data is centered if True.
|
|
165
|
-
|
|
217
|
+
|
|
166
218
|
Returns
|
|
167
219
|
-------
|
|
168
220
|
A float array of size (3,) containing the last principal component vector.
|
|
@@ -187,7 +239,7 @@ def read_ply(fname):
|
|
|
187
239
|
----------
|
|
188
240
|
fname: str
|
|
189
241
|
Name of the file to read from.
|
|
190
|
-
|
|
242
|
+
|
|
191
243
|
Returns
|
|
192
244
|
-------
|
|
193
245
|
P : (num_verts,3) float array
|
|
@@ -221,7 +273,7 @@ def load_ply(path):
|
|
|
221
273
|
----------
|
|
222
274
|
path : str
|
|
223
275
|
URL or file path at which to access .ply file.
|
|
224
|
-
|
|
276
|
+
|
|
225
277
|
Returns
|
|
226
278
|
-------
|
|
227
279
|
A mesh object generated from a .ply file found at the file path location.
|
|
@@ -247,12 +299,12 @@ def synth_mesh(angle, num_pts):
|
|
|
247
299
|
|
|
248
300
|
Parameters
|
|
249
301
|
----------
|
|
250
|
-
angle: float
|
|
302
|
+
angle: float
|
|
251
303
|
Intersection angle.
|
|
252
304
|
num_pts : int
|
|
253
305
|
Number of vertices in the mesh.
|
|
254
|
-
|
|
255
|
-
|
|
306
|
+
|
|
307
|
+
|
|
256
308
|
Returns
|
|
257
309
|
-------
|
|
258
310
|
A mesh object.
|
|
@@ -279,7 +331,7 @@ def synth_mesh(angle, num_pts):
|
|
|
279
331
|
verts[:,1] *= np.tan(angle*np.pi/180/2)
|
|
280
332
|
|
|
281
333
|
#Create mesh and flip normals
|
|
282
|
-
m = mesh(verts,faces)
|
|
334
|
+
m = mesh(verts,faces)
|
|
283
335
|
m.flip_normals()
|
|
284
336
|
|
|
285
337
|
return m
|
|
@@ -386,7 +438,7 @@ class mesh:
|
|
|
386
438
|
#so F can be used to interplate from triangles to vertices
|
|
387
439
|
def tri_vert_adj(self,normalize=False):
|
|
388
440
|
""" Computes a sparse vertex-triangle adjacency matrix.
|
|
389
|
-
|
|
441
|
+
|
|
390
442
|
Parameters
|
|
391
443
|
----------
|
|
392
444
|
normalize : boolean, default is False
|
|
@@ -417,7 +469,7 @@ class mesh:
|
|
|
417
469
|
#Returns unit normal vectors to vertices (averaging adjacent faces and normalizing)
|
|
418
470
|
def vertex_normals(self):
|
|
419
471
|
""" Computes normal vectors to vertices.
|
|
420
|
-
|
|
472
|
+
|
|
421
473
|
Returns
|
|
422
474
|
-------
|
|
423
475
|
A (num_verts,3) float array containing the vertex normal vectors.
|
|
@@ -432,16 +484,16 @@ class mesh:
|
|
|
432
484
|
norms[norms==0] = 1
|
|
433
485
|
|
|
434
486
|
return vn/norms[:,np.newaxis]
|
|
435
|
-
|
|
487
|
+
|
|
436
488
|
#Returns unit normal vectors
|
|
437
489
|
def face_normals(self,normalize=True):
|
|
438
490
|
""" Computes normal vectors to triangles (faces).
|
|
439
|
-
|
|
491
|
+
|
|
440
492
|
Parameters
|
|
441
493
|
----------
|
|
442
494
|
normalize: boolean, default is True
|
|
443
495
|
Whether or not to normalize to unit vectors; if False, vector magnitude is twice the area of the corresponding triangle.
|
|
444
|
-
|
|
496
|
+
|
|
445
497
|
Returns
|
|
446
498
|
-------
|
|
447
499
|
N : (num_tri,3) float array
|
|
@@ -460,7 +512,7 @@ class mesh:
|
|
|
460
512
|
else:
|
|
461
513
|
self.norms = N
|
|
462
514
|
return N
|
|
463
|
-
|
|
515
|
+
|
|
464
516
|
def flip_normals(self):
|
|
465
517
|
""" Reverses the orientation of all normal vectors in the mesh
|
|
466
518
|
"""
|
|
@@ -470,7 +522,7 @@ class mesh:
|
|
|
470
522
|
#Areas of all triangles in mesh
|
|
471
523
|
def tri_areas(self):
|
|
472
524
|
""" Computes areas of all triangles in the mesh.
|
|
473
|
-
|
|
525
|
+
|
|
474
526
|
Returns
|
|
475
527
|
-------
|
|
476
528
|
A (num_tri,) float array containing the areas of each triangle (face).
|
|
@@ -483,18 +535,18 @@ class mesh:
|
|
|
483
535
|
#Surface area of mesh
|
|
484
536
|
def surf_area(self):
|
|
485
537
|
""" Computes surface area of the mesh.
|
|
486
|
-
|
|
538
|
+
|
|
487
539
|
Returns
|
|
488
540
|
-------
|
|
489
541
|
The surface area of the entire mesh as a float.
|
|
490
542
|
"""
|
|
491
543
|
|
|
492
544
|
return np.sum(self.tri_areas())
|
|
493
|
-
|
|
545
|
+
|
|
494
546
|
#Centers of each face
|
|
495
547
|
def face_centers(self):
|
|
496
548
|
""" Computes coordinates of the center of each triangle (face).
|
|
497
|
-
|
|
549
|
+
|
|
498
550
|
Returns
|
|
499
551
|
-------
|
|
500
552
|
A (num_tri,3) float array containing the coordinates of the face centers.
|
|
@@ -507,11 +559,11 @@ class mesh:
|
|
|
507
559
|
result = (P1 + P2 + P3)/3
|
|
508
560
|
self.centers = result
|
|
509
561
|
return result
|
|
510
|
-
|
|
562
|
+
|
|
511
563
|
#Volume enclosed by mesh
|
|
512
564
|
def volume(self):
|
|
513
565
|
""" Computes the volume of the mesh.
|
|
514
|
-
|
|
566
|
+
|
|
515
567
|
Returns
|
|
516
568
|
-------
|
|
517
569
|
The volume of the mesh as a float.
|
|
@@ -524,10 +576,10 @@ class mesh:
|
|
|
524
576
|
if self.norms is None:
|
|
525
577
|
self.face_normals(False)
|
|
526
578
|
return np.sum(X*self.norms)/6
|
|
527
|
-
|
|
579
|
+
|
|
528
580
|
def bbox(self):
|
|
529
581
|
""" Computes the bounding box of the mesh.
|
|
530
|
-
|
|
582
|
+
|
|
531
583
|
Returns
|
|
532
584
|
-------
|
|
533
585
|
A (3,) float array containing the dimensions of the bounding box.
|
|
@@ -547,10 +599,10 @@ class mesh:
|
|
|
547
599
|
|
|
548
600
|
Y = X@vecs
|
|
549
601
|
bb = np.max(Y,axis=0) - np.min(Y,axis=0)
|
|
550
|
-
|
|
602
|
+
|
|
551
603
|
return bb
|
|
552
|
-
|
|
553
|
-
|
|
604
|
+
|
|
605
|
+
|
|
554
606
|
#Plot triangulated surface
|
|
555
607
|
def plotsurf(self,C=None):
|
|
556
608
|
""" Plots the mesh as a surface using mayavi.
|
|
@@ -559,7 +611,7 @@ class mesh:
|
|
|
559
611
|
----------
|
|
560
612
|
C : (num_verts,3) int array, default is None
|
|
561
613
|
An optional per-vertex labeling scheme to use.
|
|
562
|
-
|
|
614
|
+
|
|
563
615
|
Returns
|
|
564
616
|
-------
|
|
565
617
|
A visualization of the mesh.
|
|
@@ -578,7 +630,7 @@ class mesh:
|
|
|
578
630
|
----------
|
|
579
631
|
C : (num_verts,3) int array, default is -1
|
|
580
632
|
An optional per-vertex labeling scheme to use.
|
|
581
|
-
|
|
633
|
+
|
|
582
634
|
Returns
|
|
583
635
|
-------
|
|
584
636
|
mesh : amaazetools.trimesh.mesh object
|
|
@@ -588,7 +640,7 @@ class mesh:
|
|
|
588
640
|
from mayavi import mlab
|
|
589
641
|
if C.any == -1: #if no C given
|
|
590
642
|
C = np.ones((len(x),1))
|
|
591
|
-
|
|
643
|
+
|
|
592
644
|
n = len(np.unique(C))
|
|
593
645
|
C = C.astype(int)
|
|
594
646
|
if n>20:
|
|
@@ -597,9 +649,9 @@ class mesh:
|
|
|
597
649
|
col = (np.arange(1,n+1)) / n
|
|
598
650
|
colors = col[C-1]
|
|
599
651
|
mesh = mlab.triangular_mesh(self.points[:,0],self.points[:,1],self.points[:,2],self.triangles,scalars=colors)
|
|
600
|
-
|
|
652
|
+
|
|
601
653
|
return mesh
|
|
602
|
-
|
|
654
|
+
|
|
603
655
|
#Write a ply file
|
|
604
656
|
def to_ply(self,fname):
|
|
605
657
|
""" Writes the mesh to a .ply file.
|
|
@@ -635,7 +687,7 @@ class mesh:
|
|
|
635
687
|
|
|
636
688
|
#close file
|
|
637
689
|
f.close()
|
|
638
|
-
|
|
690
|
+
|
|
639
691
|
#Write a ply file
|
|
640
692
|
def write_color_ply(self,color,fname):
|
|
641
693
|
""" Writes the colored mesh to a .ply file.
|
|
@@ -697,21 +749,21 @@ class mesh:
|
|
|
697
749
|
histeq : boolean, default is True
|
|
698
750
|
Performs histogram equalization on scalar color array; else should normalize prior to input.
|
|
699
751
|
"""
|
|
700
|
-
|
|
752
|
+
|
|
701
753
|
from skimage import exposure
|
|
702
754
|
from mayavi import mlab
|
|
703
755
|
import moviepy.editor as mpy
|
|
704
756
|
from pyface.api import GUI
|
|
705
|
-
|
|
757
|
+
|
|
706
758
|
#Make copy of points
|
|
707
759
|
X = self.points.copy()
|
|
708
|
-
|
|
760
|
+
|
|
709
761
|
if np.shape(color)[0] == np.shape(X)[0]: #scalars for plot
|
|
710
762
|
opt = 2
|
|
711
763
|
if histeq:
|
|
712
764
|
color = color - np.amin(color)
|
|
713
765
|
color = 1-exposure.equalize_hist(color/np.max(color),nbins=1000)
|
|
714
|
-
|
|
766
|
+
|
|
715
767
|
if np.shape(np.shape(color))[0]>1: #handle input
|
|
716
768
|
color = color[:,0]
|
|
717
769
|
elif max(np.shape(color)) == 3: #single rgb color
|
|
@@ -719,7 +771,7 @@ class mesh:
|
|
|
719
771
|
else : #not input - default to single color
|
|
720
772
|
color = (0.7,0.7,0.7)
|
|
721
773
|
opt = 1
|
|
722
|
-
|
|
774
|
+
|
|
723
775
|
#PCA
|
|
724
776
|
Mean = np.mean(X,axis=0)
|
|
725
777
|
cov_matrix = (X-Mean).T@(X-Mean)
|
|
@@ -754,14 +806,14 @@ class mesh:
|
|
|
754
806
|
|
|
755
807
|
def svi(self,r,ID=None):
|
|
756
808
|
""" Computes spherical volume invariant.
|
|
757
|
-
|
|
809
|
+
|
|
758
810
|
Parameters
|
|
759
811
|
----------
|
|
760
812
|
r : (k,1) float array
|
|
761
813
|
List of radii to use.
|
|
762
814
|
ID : (n,1) boolean array, default is None
|
|
763
|
-
Spherical volume is only computed at points with True indices.
|
|
764
|
-
|
|
815
|
+
Spherical volume is only computed at points with True indices.
|
|
816
|
+
|
|
765
817
|
Returns
|
|
766
818
|
-------
|
|
767
819
|
S : (n,1) float array
|
|
@@ -769,7 +821,7 @@ class mesh:
|
|
|
769
821
|
G : (n,1) float array
|
|
770
822
|
The gamma values corresponding to each point.
|
|
771
823
|
"""
|
|
772
|
-
|
|
824
|
+
|
|
773
825
|
return svi.svi(self.points,self.triangles,r,ID=ID)
|
|
774
826
|
|
|
775
827
|
def svipca(self,r):
|
|
@@ -789,7 +841,7 @@ class mesh:
|
|
|
789
841
|
K2 : (n,1) float array
|
|
790
842
|
The second principle curvature for each point.
|
|
791
843
|
V1 : (n,3) float array
|
|
792
|
-
The first principal direction for each point.
|
|
844
|
+
The first principal direction for each point.
|
|
793
845
|
V2 : (n,3) float array
|
|
794
846
|
The second principal direction for each point.
|
|
795
847
|
V3 : (n,3) float array
|
|
@@ -800,7 +852,7 @@ class mesh:
|
|
|
800
852
|
|
|
801
853
|
def edge_graph_detect(self,**kwargs):
|
|
802
854
|
""" Detects edges using SVIPCA and principal direction metric.
|
|
803
|
-
|
|
855
|
+
|
|
804
856
|
Parameters
|
|
805
857
|
----------
|
|
806
858
|
M : amaazetools.trimesh.mesh object
|
|
@@ -828,7 +880,7 @@ class mesh:
|
|
|
828
880
|
Edges : (n,1) boolean array
|
|
829
881
|
A true value corresponds to that index being an edge point.
|
|
830
882
|
"""
|
|
831
|
-
|
|
883
|
+
|
|
832
884
|
return edge_detection.edge_graph_detect(self,**kwargs)
|
|
833
885
|
|
|
834
886
|
def graph_setup(self,n,r,p,seed=None):
|
|
@@ -844,7 +896,7 @@ class mesh:
|
|
|
844
896
|
Weight matrix parameter.
|
|
845
897
|
seed : int, default is None
|
|
846
898
|
Optional seed for random number generator.
|
|
847
|
-
|
|
899
|
+
|
|
848
900
|
Returns
|
|
849
901
|
-------
|
|
850
902
|
poisson_W_matrix : (n,n) scipy.sparse.lil_matrix
|
|
@@ -865,7 +917,7 @@ class mesh:
|
|
|
865
917
|
|
|
866
918
|
v = self.vertex_normals()
|
|
867
919
|
N = self.num_verts()
|
|
868
|
-
|
|
920
|
+
|
|
869
921
|
#Random subsample
|
|
870
922
|
ss_idx = np.matrix(rng.choice(self.points.shape[0],n,replace=False))
|
|
871
923
|
y = np.squeeze(self.points[ss_idx,:])
|
|
@@ -875,7 +927,7 @@ class mesh:
|
|
|
875
927
|
nn_idx = xTree.query_ball_point(y, r)
|
|
876
928
|
yTree = spatial.cKDTree(y)
|
|
877
929
|
nodes_idx = yTree.query_ball_point(y, r)
|
|
878
|
-
|
|
930
|
+
|
|
879
931
|
bn = np.zeros((n,3))
|
|
880
932
|
J = sparse.lil_matrix((N,n))
|
|
881
933
|
for i in range(n):
|
|
@@ -883,16 +935,16 @@ class mesh:
|
|
|
883
935
|
normal_diff = w[i] - vj
|
|
884
936
|
weights = np.exp(-8 * np.sum(np.square(normal_diff),1,keepdims=True))
|
|
885
937
|
bn[i] = np.sum(weights*vj,0) / np.sum(weights,0)
|
|
886
|
-
|
|
938
|
+
|
|
887
939
|
#Set ith row of J
|
|
888
940
|
normal_diff = bn[i]- vj
|
|
889
941
|
weights = np.exp(-8 * np.sum(np.square(normal_diff),1))#,keepdims=True))
|
|
890
942
|
J[nn_idx[i],i] = weights
|
|
891
|
-
|
|
943
|
+
|
|
892
944
|
#Normalize rows of J
|
|
893
945
|
RSM = sparse.spdiags((1 / np.sum(J,1)).ravel(),0,N,N)
|
|
894
946
|
J = RSM @ J
|
|
895
|
-
|
|
947
|
+
|
|
896
948
|
#Compute weight matrix W
|
|
897
949
|
W = sparse.lil_matrix((n,n))
|
|
898
950
|
for i in range(n):
|
|
@@ -900,7 +952,7 @@ class mesh:
|
|
|
900
952
|
normal_diff = bn[i] - nj
|
|
901
953
|
weights = np.exp(-32 * ((np.sqrt(np.sum(np.square(normal_diff),1)))/2)**p)
|
|
902
954
|
W[i,nodes_idx[i]] = weights
|
|
903
|
-
|
|
955
|
+
|
|
904
956
|
#Find nearest node to each vertex
|
|
905
957
|
nbrs = NearestNeighbors(n_neighbors=1, algorithm='ball_tree').fit(y)
|
|
906
958
|
instances, node_idx = nbrs.kneighbors(self.points)
|
|
@@ -908,8 +960,8 @@ class mesh:
|
|
|
908
960
|
self.poisson_W_matrix = W
|
|
909
961
|
self.poisson_J_matrix = J
|
|
910
962
|
self.poisson_node_idx = node_idx
|
|
911
|
-
|
|
912
|
-
return self.poisson_W_matrix, self.poisson_J_matrix, self.poisson_node_idx
|
|
963
|
+
|
|
964
|
+
return self.poisson_W_matrix, self.poisson_J_matrix, self.poisson_node_idx
|
|
913
965
|
|
|
914
966
|
def poisson_label(self,g,I,n=5000,r=0.5,p=1,s=None,graph_setup=False):
|
|
915
967
|
""" Performs poisson learning on the mesh.
|
|
@@ -930,13 +982,13 @@ class mesh:
|
|
|
930
982
|
Weights for fine-tuning Poisson learning.
|
|
931
983
|
graph_setup : boolean, default is False
|
|
932
984
|
Force graph construction if True.
|
|
933
|
-
|
|
985
|
+
|
|
934
986
|
Returns
|
|
935
987
|
-------
|
|
936
988
|
L : (num_verts,1) int array
|
|
937
989
|
Poisson labelling of each point in mesh.
|
|
938
990
|
"""
|
|
939
|
-
|
|
991
|
+
|
|
940
992
|
if graph_setup or (self.poisson_node_idx is None):
|
|
941
993
|
self.graph_setup(n,r,p)
|
|
942
994
|
|
|
@@ -955,7 +1007,7 @@ class mesh:
|
|
|
955
1007
|
self.poisson_labels = L
|
|
956
1008
|
|
|
957
1009
|
return L
|
|
958
|
-
|
|
1010
|
+
|
|
959
1011
|
def virtual_goniometer(self,point,r,k=7,SegParam=2,return_edge_points=False,
|
|
960
1012
|
number_edge_points=None,return_euclidean_radius=False):
|
|
961
1013
|
""" Runs a virtual goniometer to measure break angles.
|
|
@@ -977,7 +1029,7 @@ class mesh:
|
|
|
977
1029
|
Specifies how many edge points to return.
|
|
978
1030
|
return_euclidean_radius : boolean, default is False
|
|
979
1031
|
If True, returns Euclidean radius of patch.
|
|
980
|
-
|
|
1032
|
+
|
|
981
1033
|
Returns
|
|
982
1034
|
-------
|
|
983
1035
|
theta : float
|
|
@@ -1039,10 +1091,10 @@ def __virtual_goniometer__(P,N,SegParam=2,UsePCA=True,UsePower=False):
|
|
|
1039
1091
|
SegParam : float, default is 2
|
|
1040
1092
|
Segmentation parameter that encourages splitting patch in half as it increases in size.
|
|
1041
1093
|
UsePCA: boolean, default is True
|
|
1042
|
-
Uses PCA instead of averaged surface normals if True.
|
|
1094
|
+
Uses PCA instead of averaged surface normals if True.
|
|
1043
1095
|
UsePower : boolean, default is False
|
|
1044
1096
|
Uses the power method when doing PCA if True.
|
|
1045
|
-
|
|
1097
|
+
|
|
1046
1098
|
Returns
|
|
1047
1099
|
-------
|
|
1048
1100
|
theta : float
|
|
@@ -1107,11 +1159,11 @@ def __virtual_goniometer__(P,N,SegParam=2,UsePCA=True,UsePower=False):
|
|
|
1107
1159
|
n1 = n1/np.linalg.norm(n1)
|
|
1108
1160
|
n2 = np.average(N[C==2,:],axis=0)
|
|
1109
1161
|
n2 = n2/np.linalg.norm(n2)
|
|
1110
|
-
|
|
1162
|
+
|
|
1111
1163
|
#Angle between
|
|
1112
1164
|
theta = 180-np.arccos(np.dot(n1,n2))*180/np.pi
|
|
1113
1165
|
return theta,n1,n2,C
|
|
1114
|
-
|
|
1166
|
+
|
|
1115
1167
|
def conjgrad(A,b,x,T,tol):
|
|
1116
1168
|
""" Performs conjugate gradient descent.
|
|
1117
1169
|
|
|
@@ -1119,19 +1171,19 @@ def conjgrad(A,b,x,T,tol):
|
|
|
1119
1171
|
----------
|
|
1120
1172
|
A : matrix multiplying x
|
|
1121
1173
|
b : vector equal to product of A and x
|
|
1122
|
-
x : initial estimate for x
|
|
1174
|
+
x : initial estimate for x
|
|
1123
1175
|
T : int
|
|
1124
1176
|
Number of time steps allowed.
|
|
1125
1177
|
Tol : float
|
|
1126
1178
|
Desired convergence tolerance of result.
|
|
1127
|
-
|
|
1179
|
+
|
|
1128
1180
|
Returns
|
|
1129
1181
|
-------
|
|
1130
1182
|
x : calculated value for x
|
|
1131
1183
|
i : int
|
|
1132
1184
|
Number of iterations required for convergence.
|
|
1133
1185
|
"""
|
|
1134
|
-
|
|
1186
|
+
|
|
1135
1187
|
r = b - A@x
|
|
1136
1188
|
p = r
|
|
1137
1189
|
rsold = np.sum(r * r,0)
|
|
@@ -1158,13 +1210,13 @@ def poisson_learning(W,g,I):
|
|
|
1158
1210
|
Labels to assign to selected vertices.
|
|
1159
1211
|
I : (m,1) int array
|
|
1160
1212
|
Indices of user-selected vertices.
|
|
1161
|
-
|
|
1213
|
+
|
|
1162
1214
|
Returns
|
|
1163
1215
|
-------
|
|
1164
1216
|
u : (num_verts,1) int array
|
|
1165
1217
|
Poisson labels for each vertex in the mesh.
|
|
1166
1218
|
"""
|
|
1167
|
-
|
|
1219
|
+
|
|
1168
1220
|
k = len(np.unique(g))
|
|
1169
1221
|
n = W.shape[0]
|
|
1170
1222
|
m = len(I)
|
|
@@ -1176,19 +1228,19 @@ def poisson_learning(W,g,I):
|
|
|
1176
1228
|
F[I[i],g[i]] = 1
|
|
1177
1229
|
c = np.ones((1,n)) @ F / len(g)
|
|
1178
1230
|
F[I] -= c
|
|
1179
|
-
|
|
1231
|
+
|
|
1180
1232
|
deg = np.sum(W,1)
|
|
1181
1233
|
D = sparse.spdiags(deg.T,0,n,n)
|
|
1182
1234
|
L = D-W #Unnormalized graph laplacian matrix
|
|
1183
|
-
|
|
1235
|
+
|
|
1184
1236
|
#Preconditioning
|
|
1185
|
-
Dinv2 = sparse.spdiags(np.power(np.sum(W,1),-1/2).T,0,n,n)
|
|
1237
|
+
Dinv2 = sparse.spdiags(np.power(np.sum(W,1),-1/2).T,0,n,n)
|
|
1186
1238
|
Lnorm = Dinv2 @ L @ Dinv2
|
|
1187
1239
|
F = Dinv2 @ F
|
|
1188
|
-
|
|
1240
|
+
|
|
1189
1241
|
#Conjugate Gradient Solver
|
|
1190
1242
|
u,i = conjgrad(Lnorm,F,np.zeros((n,k)),1e5, np.sqrt(n)*1e-10)
|
|
1191
|
-
|
|
1243
|
+
|
|
1192
1244
|
#Undo preconditioning
|
|
1193
1245
|
u = Dinv2 @ u
|
|
1194
1246
|
return u
|
|
@@ -1200,18 +1252,18 @@ def canonical_labels(u):
|
|
|
1200
1252
|
----------
|
|
1201
1253
|
u : (num_verts,1) int array
|
|
1202
1254
|
A label vector.
|
|
1203
|
-
|
|
1255
|
+
|
|
1204
1256
|
Returns
|
|
1205
1257
|
-------
|
|
1206
1258
|
u : (num_verts,1) int array
|
|
1207
1259
|
A reodered label vector.
|
|
1208
1260
|
"""
|
|
1209
|
-
|
|
1261
|
+
|
|
1210
1262
|
n = len(u)
|
|
1211
1263
|
k = len(np.unique(u))
|
|
1212
1264
|
label_set = np.zeros((k,1))
|
|
1213
1265
|
label = 0
|
|
1214
|
-
|
|
1266
|
+
|
|
1215
1267
|
for i in range(n):
|
|
1216
1268
|
if u[i] > label:
|
|
1217
1269
|
label += 1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|