amaazetools 0.1.2__tar.gz → 0.1.4__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.
Files changed (26) hide show
  1. {amaazetools-0.1.2/amaazetools.egg-info → amaazetools-0.1.4}/PKG-INFO +2 -2
  2. {amaazetools-0.1.2 → amaazetools-0.1.4}/README.md +1 -1
  3. {amaazetools-0.1.2 → amaazetools-0.1.4}/amaazetools/trimesh.py +159 -123
  4. {amaazetools-0.1.2 → amaazetools-0.1.4/amaazetools.egg-info}/PKG-INFO +2 -2
  5. {amaazetools-0.1.2 → amaazetools-0.1.4}/pyproject.toml +1 -1
  6. {amaazetools-0.1.2 → amaazetools-0.1.4}/src/cextensions.c +2 -0
  7. {amaazetools-0.1.2 → amaazetools-0.1.4}/LICENSE +0 -0
  8. {amaazetools-0.1.2 → amaazetools-0.1.4}/MANIFEST.in +0 -0
  9. {amaazetools-0.1.2 → amaazetools-0.1.4}/amaazetools/__init__.py +0 -0
  10. {amaazetools-0.1.2 → amaazetools-0.1.4}/amaazetools/dicom.py +0 -0
  11. {amaazetools-0.1.2 → amaazetools-0.1.4}/amaazetools/edge_detection.py +0 -0
  12. {amaazetools-0.1.2 → amaazetools-0.1.4}/amaazetools/mesh_segmentation.py +0 -0
  13. {amaazetools-0.1.2 → amaazetools-0.1.4}/amaazetools/svi.py +0 -0
  14. {amaazetools-0.1.2 → amaazetools-0.1.4}/amaazetools.egg-info/SOURCES.txt +0 -0
  15. {amaazetools-0.1.2 → amaazetools-0.1.4}/amaazetools.egg-info/dependency_links.txt +0 -0
  16. {amaazetools-0.1.2 → amaazetools-0.1.4}/amaazetools.egg-info/requires.txt +0 -0
  17. {amaazetools-0.1.2 → amaazetools-0.1.4}/amaazetools.egg-info/top_level.txt +0 -0
  18. {amaazetools-0.1.2 → amaazetools-0.1.4}/setup.cfg +0 -0
  19. {amaazetools-0.1.2 → amaazetools-0.1.4}/setup.py +0 -0
  20. {amaazetools-0.1.2 → amaazetools-0.1.4}/src/memory_allocation.c +0 -0
  21. {amaazetools-0.1.2 → amaazetools-0.1.4}/src/memory_allocation.h +0 -0
  22. {amaazetools-0.1.2 → amaazetools-0.1.4}/src/mesh_operations.c +0 -0
  23. {amaazetools-0.1.2 → amaazetools-0.1.4}/src/mesh_operations.h +0 -0
  24. {amaazetools-0.1.2 → amaazetools-0.1.4}/src/svi_computations.c +0 -0
  25. {amaazetools-0.1.2 → amaazetools-0.1.4}/src/svi_computations.h +0 -0
  26. {amaazetools-0.1.2 → amaazetools-0.1.4}/src/vector_operations.h +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: amaazetools
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Python package for mesh processing tools developed by AMAAZE
5
5
  Author-email: Jeff Calder <jwcalder@umn.edu>
6
6
  License: MIT
@@ -50,7 +50,7 @@ Email <jwcalder@umn.edu> with any questions or comments.
50
50
 
51
51
  ## Contributors
52
52
 
53
- Several people have contributed to the development of this software:
53
+ Several people have contributed to the development of this software:
54
54
 
55
55
  1. David Floeder
56
56
  2. Riley O'Neill
@@ -26,7 +26,7 @@ Email <jwcalder@umn.edu> with any questions or comments.
26
26
 
27
27
  ## Contributors
28
28
 
29
- Several people have contributed to the development of this software:
29
+ Several people have contributed to the development of this software:
30
30
 
31
31
  1. David Floeder
32
32
  2. Riley O'Neill
@@ -10,6 +10,7 @@ from plyfile import PlyData, PlyElement
10
10
  import scipy.sparse as sparse
11
11
  import scipy.spatial as spatial
12
12
  from skimage import measure
13
+ from skimage.color import convert_colorspace
13
14
  from sklearn.neighbors import NearestNeighbors
14
15
  from . import svi
15
16
  from . import edge_detection
@@ -18,6 +19,58 @@ import urllib.request as url
18
19
 
19
20
  #Non-Class Specific Functions
20
21
 
22
+
23
+ def marching_cubes(volume,level=None,spacing=(1,1,1)):
24
+ """ SK-Image's marching cubes does not return a clean triangulations -
25
+ often has replicate points, self-triangles, etc. This fixes that.
26
+
27
+ Parameters
28
+ ----------
29
+ volume : (l,w,h) float array
30
+ A 3-D grid discretization of the function to be surfaced.
31
+ level : float
32
+ isolevel to extract surface at.
33
+ spacing : (3) float
34
+ denotes the dimensions of each voxel in the volume.
35
+
36
+
37
+ Returns
38
+ -------
39
+ p : (n,3) float
40
+ points of triangulation
41
+ t : (m,3) int
42
+ triangles of triangulation
43
+ """
44
+
45
+ p,t,n,val = measure.marching_cubes(volume,level=level,spacing=spacing)
46
+
47
+ #sometimes marching cubes produces... artifacts... so here's a very basic intro to mesh cleaning!
48
+ #first: eliminate repeated points:
49
+ p,index,inv = np.unique(p,axis=0, return_index=True,return_inverse=True)
50
+
51
+ #next: rid triangulation of non-triangles:
52
+ t = inv[t]
53
+ badt = (t[:,0]==t[:,1])+(t[:,1]==t[:,2])+(t[:,2]==t[:,0])
54
+ t = t[badt ==False,:]
55
+
56
+ #remove unreferenced points, if any
57
+ ind = -1*np.ones(p.shape[0],int)
58
+ uni = np.unique(t.flatten()) #these are already sorted for numpy
59
+ ind[uni] = np.arange(uni.shape[0])
60
+ p = p[ind>-1,:]
61
+ t = ind[t]
62
+
63
+ #finally, check right hand rule... this works for ~convex objects, anyway...
64
+ #m = tm.mesh(p,t)
65
+
66
+ #tc = m.face_centers()
67
+ #tn = m.face_normals()
68
+
69
+ #cent = m.points.mean(0)
70
+ #fliporder = np.sum(tn*(tc-cent),1)<0
71
+ #t[fliporder,1:3] = t[fliporder,2:0:-1]
72
+ return p,t
73
+
21
74
  def withiness(x):
22
75
  """ Computes withiness (how well 1-D data clusters into two groups).
23
76
 
@@ -25,7 +78,7 @@ def withiness(x):
25
78
  ----------
26
79
  x : (n,1) float array
27
80
  A 1-D collection of data.
28
-
81
+
29
82
  Returns
30
83
  -------
31
84
  w : float
@@ -56,7 +109,7 @@ def pca(P):
56
109
  ----------
57
110
  P : (n,d) float array
58
111
  A point cloud.
59
-
112
+
60
113
  Returns
61
114
  -------
62
115
  vals : (d,) float arrayy
@@ -64,13 +117,13 @@ def pca(P):
64
117
  vecs : (d,d) float array
65
118
  The principal component vectors.
66
119
  """
67
-
120
+
68
121
  P = P - np.mean(P,axis=0)
69
122
  vals,vecs = np.linalg.eig(P.T@P)
70
123
  idx = np.argsort(-vals)
71
124
 
72
125
  return vals[idx],vecs[:,idx]
73
-
126
+
74
127
  def weighted_pca(P,W):
75
128
  """ Computes weighted principal component analysis (PCA) on a point cloud P.
76
129
 
@@ -80,7 +133,7 @@ def weighted_pca(P,W):
80
133
  A point cloud.
81
134
  W : (n,) float array
82
135
  An array containing the weights of the points.
83
-
136
+
84
137
  Returns
85
138
  -------
86
139
  vals : (d,) float array
@@ -107,7 +160,7 @@ def power_method(A,tol=1e-12):
107
160
  A square matrix that one wishes to find the smallest (in absolute value) eigenvalue and corresponding eigenvector of.
108
161
  tol : float, default is 1e-12
109
162
  The desired tolerance threshold after which to stop iteration.
110
-
163
+
111
164
  Parameters
112
165
  ----------
113
166
  l : float
@@ -137,7 +190,7 @@ def pca_smallest_eig_powermethod(X,center=True):
137
190
  A point cloud.
138
191
  center : boolean, default is True
139
192
  Data is centered if True.
140
-
193
+
141
194
  Returns
142
195
  -------
143
196
  A float array of size (3,) containing the last principal component vector.
@@ -162,7 +215,7 @@ def pca_smallest_eig(X,center=True):
162
215
  A point cloud.
163
216
  center : boolean, default is True
164
217
  Data is centered if True.
165
-
218
+
166
219
  Returns
167
220
  -------
168
221
  A float array of size (3,) containing the last principal component vector.
@@ -187,7 +240,7 @@ def read_ply(fname):
187
240
  ----------
188
241
  fname: str
189
242
  Name of the file to read from.
190
-
243
+
191
244
  Returns
192
245
  -------
193
246
  P : (num_verts,3) float array
@@ -221,7 +274,7 @@ def load_ply(path):
221
274
  ----------
222
275
  path : str
223
276
  URL or file path at which to access .ply file.
224
-
277
+
225
278
  Returns
226
279
  -------
227
280
  A mesh object generated from a .ply file found at the file path location.
@@ -247,12 +300,12 @@ def synth_mesh(angle, num_pts):
247
300
 
248
301
  Parameters
249
302
  ----------
250
- angle: float
303
+ angle: float
251
304
  Intersection angle.
252
305
  num_pts : int
253
306
  Number of vertices in the mesh.
254
-
255
-
307
+
308
+
256
309
  Returns
257
310
  -------
258
311
  A mesh object.
@@ -279,7 +332,7 @@ def synth_mesh(angle, num_pts):
279
332
  verts[:,1] *= np.tan(angle*np.pi/180/2)
280
333
 
281
334
  #Create mesh and flip normals
282
- m = mesh(verts,faces)
335
+ m = mesh(verts,faces)
283
336
  m.flip_normals()
284
337
 
285
338
  return m
@@ -386,7 +439,7 @@ class mesh:
386
439
  #so F can be used to interplate from triangles to vertices
387
440
  def tri_vert_adj(self,normalize=False):
388
441
  """ Computes a sparse vertex-triangle adjacency matrix.
389
-
442
+
390
443
  Parameters
391
444
  ----------
392
445
  normalize : boolean, default is False
@@ -417,7 +470,7 @@ class mesh:
417
470
  #Returns unit normal vectors to vertices (averaging adjacent faces and normalizing)
418
471
  def vertex_normals(self):
419
472
  """ Computes normal vectors to vertices.
420
-
473
+
421
474
  Returns
422
475
  -------
423
476
  A (num_verts,3) float array containing the vertex normal vectors.
@@ -432,16 +485,16 @@ class mesh:
432
485
  norms[norms==0] = 1
433
486
 
434
487
  return vn/norms[:,np.newaxis]
435
-
488
+
436
489
  #Returns unit normal vectors
437
490
  def face_normals(self,normalize=True):
438
491
  """ Computes normal vectors to triangles (faces).
439
-
492
+
440
493
  Parameters
441
494
  ----------
442
495
  normalize: boolean, default is True
443
496
  Whether or not to normalize to unit vectors; if False, vector magnitude is twice the area of the corresponding triangle.
444
-
497
+
445
498
  Returns
446
499
  -------
447
500
  N : (num_tri,3) float array
@@ -460,7 +513,7 @@ class mesh:
460
513
  else:
461
514
  self.norms = N
462
515
  return N
463
-
516
+
464
517
  def flip_normals(self):
465
518
  """ Reverses the orientation of all normal vectors in the mesh
466
519
  """
@@ -470,7 +523,7 @@ class mesh:
470
523
  #Areas of all triangles in mesh
471
524
  def tri_areas(self):
472
525
  """ Computes areas of all triangles in the mesh.
473
-
526
+
474
527
  Returns
475
528
  -------
476
529
  A (num_tri,) float array containing the areas of each triangle (face).
@@ -483,18 +536,18 @@ class mesh:
483
536
  #Surface area of mesh
484
537
  def surf_area(self):
485
538
  """ Computes surface area of the mesh.
486
-
539
+
487
540
  Returns
488
541
  -------
489
542
  The surface area of the entire mesh as a float.
490
543
  """
491
544
 
492
545
  return np.sum(self.tri_areas())
493
-
546
+
494
547
  #Centers of each face
495
548
  def face_centers(self):
496
549
  """ Computes coordinates of the center of each triangle (face).
497
-
550
+
498
551
  Returns
499
552
  -------
500
553
  A (num_tri,3) float array containing the coordinates of the face centers.
@@ -507,11 +560,11 @@ class mesh:
507
560
  result = (P1 + P2 + P3)/3
508
561
  self.centers = result
509
562
  return result
510
-
563
+
511
564
  #Volume enclosed by mesh
512
565
  def volume(self):
513
566
  """ Computes the volume of the mesh.
514
-
567
+
515
568
  Returns
516
569
  -------
517
570
  The volume of the mesh as a float.
@@ -524,10 +577,10 @@ class mesh:
524
577
  if self.norms is None:
525
578
  self.face_normals(False)
526
579
  return np.sum(X*self.norms)/6
527
-
580
+
528
581
  def bbox(self):
529
582
  """ Computes the bounding box of the mesh.
530
-
583
+
531
584
  Returns
532
585
  -------
533
586
  A (3,) float array containing the dimensions of the bounding box.
@@ -547,10 +600,10 @@ class mesh:
547
600
 
548
601
  Y = X@vecs
549
602
  bb = np.max(Y,axis=0) - np.min(Y,axis=0)
550
-
603
+
551
604
  return bb
552
-
553
-
605
+
606
+
554
607
  #Plot triangulated surface
555
608
  def plotsurf(self,C=None):
556
609
  """ Plots the mesh as a surface using mayavi.
@@ -559,7 +612,7 @@ class mesh:
559
612
  ----------
560
613
  C : (num_verts,3) int array, default is None
561
614
  An optional per-vertex labeling scheme to use.
562
-
615
+
563
616
  Returns
564
617
  -------
565
618
  A visualization of the mesh.
@@ -578,7 +631,7 @@ class mesh:
578
631
  ----------
579
632
  C : (num_verts,3) int array, default is -1
580
633
  An optional per-vertex labeling scheme to use.
581
-
634
+
582
635
  Returns
583
636
  -------
584
637
  mesh : amaazetools.trimesh.mesh object
@@ -588,7 +641,7 @@ class mesh:
588
641
  from mayavi import mlab
589
642
  if C.any == -1: #if no C given
590
643
  C = np.ones((len(x),1))
591
-
644
+
592
645
  n = len(np.unique(C))
593
646
  C = C.astype(int)
594
647
  if n>20:
@@ -597,17 +650,22 @@ class mesh:
597
650
  col = (np.arange(1,n+1)) / n
598
651
  colors = col[C-1]
599
652
  mesh = mlab.triangular_mesh(self.points[:,0],self.points[:,1],self.points[:,2],self.triangles,scalars=colors)
600
-
653
+
601
654
  return mesh
602
-
655
+
603
656
  #Write a ply file
604
- def to_ply(self,fname):
657
+ def to_ply(self,fname,c=None):
605
658
  """ Writes the mesh to a .ply file.
606
659
 
607
660
  Parameters
608
661
  ----------
609
662
  fname : str
610
663
  The name of the .ply file to write the mesh to.
664
+ c : numpy array
665
+ Color array. If provided, then color is added to ply file.
666
+ If array is num_vert x 3, it is interprted as RGB colors
667
+ in the range 0,...,255. If the array is one dimensional
668
+ of length num_vert, then the values are interpreted as hues.
611
669
  """
612
670
 
613
671
  f = open(fname,"w")
@@ -619,6 +677,11 @@ class mesh:
619
677
  f.write('property double x\n')
620
678
  f.write('property double y\n')
621
679
  f.write('property double z\n')
680
+ #Write color header if colors are provided
681
+ if c is not None:
682
+ f.write('property uchar red\n')
683
+ f.write('property uchar green\n')
684
+ f.write('property uchar blue\n')
622
685
  f.write('element face %u\n'%self.num_tri())
623
686
  f.write('property list int int vertex_indices\n')
624
687
  f.write('end_header\n')
@@ -626,8 +689,23 @@ class mesh:
626
689
 
627
690
  f = open(fname,"ab")
628
691
 
629
- #write vertices
630
- f.write(self.points.astype('float64').tobytes())
692
+ #If no colors are provided
693
+ if c is None:
694
+ #write vertices
695
+ f.write(self.points.astype('float64').tobytes())
696
+ #If colors are provided
697
+ else:
698
+ #If scalars provided, then convert from hue to rgb
699
+ if c.ndim == 1:
700
+ c = c - np.min(c)
701
+ c = c/np.max(c)
702
+ arr = np.vstack((c,np.ones_like(c),np.ones_like(c))).T
703
+ c = 255*convert_colorspace(arr,'HSV','RGB')
704
+
705
+ #write vertices
706
+ for i in range(self.num_verts()):
707
+ f.write(self.points[i,:].astype('float64').tobytes())
708
+ f.write(c[i,:].astype('uint8').tobytes())
631
709
 
632
710
  #write faces
633
711
  T = np.hstack((np.ones((self.num_tri(),1))*3,self.triangles)).astype(int)
@@ -635,49 +713,7 @@ class mesh:
635
713
 
636
714
  #close file
637
715
  f.close()
638
-
639
- #Write a ply file
640
- def write_color_ply(self,color,fname):
641
- """ Writes the colored mesh to a .ply file.
642
-
643
- Parameters
644
- ----------
645
- color : (num,verts,3) float array
646
- An array of color data for each point.
647
- fname : str
648
- The name of the .ply file to write the colored mesh to.
649
- """
650
-
651
- f = open(fname,"w")
652
-
653
- #Write header
654
- f.write('ply\n')
655
- f.write('format binary_little_endian 1.0\n')
656
- f.write('element vertex %u\n'%self.num_verts())
657
- f.write('property double x\n')
658
- f.write('property double y\n')
659
- f.write('property double z\n')
660
- f.write('property uchar red\n')
661
- f.write('property uchar green\n')
662
- f.write('property uchar blue\n')
663
- f.write('element face %u\n'%self.num_tri())
664
- f.write('property list int int vertex_indices\n')
665
- f.write('end_header\n')
666
- f.close()
667
716
 
668
- f = open(fname,"ab")
669
-
670
- #write vertices
671
- for i in range(self.num_verts()):
672
- f.write(P[i,:].astype('float64').tobytes())
673
- f.write(color[i,:].astype('uint8').tobytes())
674
-
675
- #write faces
676
- T = np.hstack((np.ones((self.num_tri(),1))*3,T)).astype(int)
677
- f.write(T.astype('int32').tobytes())
678
-
679
- #close file
680
- f.close()
681
717
 
682
718
  def to_gif(self,fname,color = [],duration=7,fps=20,size=750,histeq = True):
683
719
  """ Writes rotating gif
@@ -697,21 +733,21 @@ class mesh:
697
733
  histeq : boolean, default is True
698
734
  Performs histogram equalization on scalar color array; else should normalize prior to input.
699
735
  """
700
-
736
+
701
737
  from skimage import exposure
702
738
  from mayavi import mlab
703
739
  import moviepy.editor as mpy
704
740
  from pyface.api import GUI
705
-
741
+
706
742
  #Make copy of points
707
743
  X = self.points.copy()
708
-
744
+
709
745
  if np.shape(color)[0] == np.shape(X)[0]: #scalars for plot
710
746
  opt = 2
711
747
  if histeq:
712
748
  color = color - np.amin(color)
713
749
  color = 1-exposure.equalize_hist(color/np.max(color),nbins=1000)
714
-
750
+
715
751
  if np.shape(np.shape(color))[0]>1: #handle input
716
752
  color = color[:,0]
717
753
  elif max(np.shape(color)) == 3: #single rgb color
@@ -719,7 +755,7 @@ class mesh:
719
755
  else : #not input - default to single color
720
756
  color = (0.7,0.7,0.7)
721
757
  opt = 1
722
-
758
+
723
759
  #PCA
724
760
  Mean = np.mean(X,axis=0)
725
761
  cov_matrix = (X-Mean).T@(X-Mean)
@@ -754,14 +790,14 @@ class mesh:
754
790
 
755
791
  def svi(self,r,ID=None):
756
792
  """ Computes spherical volume invariant.
757
-
793
+
758
794
  Parameters
759
795
  ----------
760
796
  r : (k,1) float array
761
797
  List of radii to use.
762
798
  ID : (n,1) boolean array, default is None
763
- Spherical volume is only computed at points with True indices.
764
-
799
+ Spherical volume is only computed at points with True indices.
800
+
765
801
  Returns
766
802
  -------
767
803
  S : (n,1) float array
@@ -769,7 +805,7 @@ class mesh:
769
805
  G : (n,1) float array
770
806
  The gamma values corresponding to each point.
771
807
  """
772
-
808
+
773
809
  return svi.svi(self.points,self.triangles,r,ID=ID)
774
810
 
775
811
  def svipca(self,r):
@@ -789,7 +825,7 @@ class mesh:
789
825
  K2 : (n,1) float array
790
826
  The second principle curvature for each point.
791
827
  V1 : (n,3) float array
792
- The first principal direction for each point.
828
+ The first principal direction for each point.
793
829
  V2 : (n,3) float array
794
830
  The second principal direction for each point.
795
831
  V3 : (n,3) float array
@@ -800,7 +836,7 @@ class mesh:
800
836
 
801
837
  def edge_graph_detect(self,**kwargs):
802
838
  """ Detects edges using SVIPCA and principal direction metric.
803
-
839
+
804
840
  Parameters
805
841
  ----------
806
842
  M : amaazetools.trimesh.mesh object
@@ -828,7 +864,7 @@ class mesh:
828
864
  Edges : (n,1) boolean array
829
865
  A true value corresponds to that index being an edge point.
830
866
  """
831
-
867
+
832
868
  return edge_detection.edge_graph_detect(self,**kwargs)
833
869
 
834
870
  def graph_setup(self,n,r,p,seed=None):
@@ -844,7 +880,7 @@ class mesh:
844
880
  Weight matrix parameter.
845
881
  seed : int, default is None
846
882
  Optional seed for random number generator.
847
-
883
+
848
884
  Returns
849
885
  -------
850
886
  poisson_W_matrix : (n,n) scipy.sparse.lil_matrix
@@ -865,7 +901,7 @@ class mesh:
865
901
 
866
902
  v = self.vertex_normals()
867
903
  N = self.num_verts()
868
-
904
+
869
905
  #Random subsample
870
906
  ss_idx = np.matrix(rng.choice(self.points.shape[0],n,replace=False))
871
907
  y = np.squeeze(self.points[ss_idx,:])
@@ -875,7 +911,7 @@ class mesh:
875
911
  nn_idx = xTree.query_ball_point(y, r)
876
912
  yTree = spatial.cKDTree(y)
877
913
  nodes_idx = yTree.query_ball_point(y, r)
878
-
914
+
879
915
  bn = np.zeros((n,3))
880
916
  J = sparse.lil_matrix((N,n))
881
917
  for i in range(n):
@@ -883,16 +919,16 @@ class mesh:
883
919
  normal_diff = w[i] - vj
884
920
  weights = np.exp(-8 * np.sum(np.square(normal_diff),1,keepdims=True))
885
921
  bn[i] = np.sum(weights*vj,0) / np.sum(weights,0)
886
-
922
+
887
923
  #Set ith row of J
888
924
  normal_diff = bn[i]- vj
889
925
  weights = np.exp(-8 * np.sum(np.square(normal_diff),1))#,keepdims=True))
890
926
  J[nn_idx[i],i] = weights
891
-
927
+
892
928
  #Normalize rows of J
893
929
  RSM = sparse.spdiags((1 / np.sum(J,1)).ravel(),0,N,N)
894
930
  J = RSM @ J
895
-
931
+
896
932
  #Compute weight matrix W
897
933
  W = sparse.lil_matrix((n,n))
898
934
  for i in range(n):
@@ -900,7 +936,7 @@ class mesh:
900
936
  normal_diff = bn[i] - nj
901
937
  weights = np.exp(-32 * ((np.sqrt(np.sum(np.square(normal_diff),1)))/2)**p)
902
938
  W[i,nodes_idx[i]] = weights
903
-
939
+
904
940
  #Find nearest node to each vertex
905
941
  nbrs = NearestNeighbors(n_neighbors=1, algorithm='ball_tree').fit(y)
906
942
  instances, node_idx = nbrs.kneighbors(self.points)
@@ -908,8 +944,8 @@ class mesh:
908
944
  self.poisson_W_matrix = W
909
945
  self.poisson_J_matrix = J
910
946
  self.poisson_node_idx = node_idx
911
-
912
- return self.poisson_W_matrix, self.poisson_J_matrix, self.poisson_node_idx
947
+
948
+ return self.poisson_W_matrix, self.poisson_J_matrix, self.poisson_node_idx
913
949
 
914
950
  def poisson_label(self,g,I,n=5000,r=0.5,p=1,s=None,graph_setup=False):
915
951
  """ Performs poisson learning on the mesh.
@@ -930,13 +966,13 @@ class mesh:
930
966
  Weights for fine-tuning Poisson learning.
931
967
  graph_setup : boolean, default is False
932
968
  Force graph construction if True.
933
-
969
+
934
970
  Returns
935
971
  -------
936
972
  L : (num_verts,1) int array
937
973
  Poisson labelling of each point in mesh.
938
974
  """
939
-
975
+
940
976
  if graph_setup or (self.poisson_node_idx is None):
941
977
  self.graph_setup(n,r,p)
942
978
 
@@ -955,7 +991,7 @@ class mesh:
955
991
  self.poisson_labels = L
956
992
 
957
993
  return L
958
-
994
+
959
995
  def virtual_goniometer(self,point,r,k=7,SegParam=2,return_edge_points=False,
960
996
  number_edge_points=None,return_euclidean_radius=False):
961
997
  """ Runs a virtual goniometer to measure break angles.
@@ -977,7 +1013,7 @@ class mesh:
977
1013
  Specifies how many edge points to return.
978
1014
  return_euclidean_radius : boolean, default is False
979
1015
  If True, returns Euclidean radius of patch.
980
-
1016
+
981
1017
  Returns
982
1018
  -------
983
1019
  theta : float
@@ -1039,10 +1075,10 @@ def __virtual_goniometer__(P,N,SegParam=2,UsePCA=True,UsePower=False):
1039
1075
  SegParam : float, default is 2
1040
1076
  Segmentation parameter that encourages splitting patch in half as it increases in size.
1041
1077
  UsePCA: boolean, default is True
1042
- Uses PCA instead of averaged surface normals if True.
1078
+ Uses PCA instead of averaged surface normals if True.
1043
1079
  UsePower : boolean, default is False
1044
1080
  Uses the power method when doing PCA if True.
1045
-
1081
+
1046
1082
  Returns
1047
1083
  -------
1048
1084
  theta : float
@@ -1107,11 +1143,11 @@ def __virtual_goniometer__(P,N,SegParam=2,UsePCA=True,UsePower=False):
1107
1143
  n1 = n1/np.linalg.norm(n1)
1108
1144
  n2 = np.average(N[C==2,:],axis=0)
1109
1145
  n2 = n2/np.linalg.norm(n2)
1110
-
1146
+
1111
1147
  #Angle between
1112
1148
  theta = 180-np.arccos(np.dot(n1,n2))*180/np.pi
1113
1149
  return theta,n1,n2,C
1114
-
1150
+
1115
1151
  def conjgrad(A,b,x,T,tol):
1116
1152
  """ Performs conjugate gradient descent.
1117
1153
 
@@ -1119,19 +1155,19 @@ def conjgrad(A,b,x,T,tol):
1119
1155
  ----------
1120
1156
  A : matrix multiplying x
1121
1157
  b : vector equal to product of A and x
1122
- x : initial estimate for x
1158
+ x : initial estimate for x
1123
1159
  T : int
1124
1160
  Number of time steps allowed.
1125
1161
  Tol : float
1126
1162
  Desired convergence tolerance of result.
1127
-
1163
+
1128
1164
  Returns
1129
1165
  -------
1130
1166
  x : calculated value for x
1131
1167
  i : int
1132
1168
  Number of iterations required for convergence.
1133
1169
  """
1134
-
1170
+
1135
1171
  r = b - A@x
1136
1172
  p = r
1137
1173
  rsold = np.sum(r * r,0)
@@ -1158,13 +1194,13 @@ def poisson_learning(W,g,I):
1158
1194
  Labels to assign to selected vertices.
1159
1195
  I : (m,1) int array
1160
1196
  Indices of user-selected vertices.
1161
-
1197
+
1162
1198
  Returns
1163
1199
  -------
1164
1200
  u : (num_verts,1) int array
1165
1201
  Poisson labels for each vertex in the mesh.
1166
1202
  """
1167
-
1203
+
1168
1204
  k = len(np.unique(g))
1169
1205
  n = W.shape[0]
1170
1206
  m = len(I)
@@ -1176,19 +1212,19 @@ def poisson_learning(W,g,I):
1176
1212
  F[I[i],g[i]] = 1
1177
1213
  c = np.ones((1,n)) @ F / len(g)
1178
1214
  F[I] -= c
1179
-
1215
+
1180
1216
  deg = np.sum(W,1)
1181
1217
  D = sparse.spdiags(deg.T,0,n,n)
1182
1218
  L = D-W #Unnormalized graph laplacian matrix
1183
-
1219
+
1184
1220
  #Preconditioning
1185
- Dinv2 = sparse.spdiags(np.power(np.sum(W,1),-1/2).T,0,n,n)
1221
+ Dinv2 = sparse.spdiags(np.power(np.sum(W,1),-1/2).T,0,n,n)
1186
1222
  Lnorm = Dinv2 @ L @ Dinv2
1187
1223
  F = Dinv2 @ F
1188
-
1224
+
1189
1225
  #Conjugate Gradient Solver
1190
1226
  u,i = conjgrad(Lnorm,F,np.zeros((n,k)),1e5, np.sqrt(n)*1e-10)
1191
-
1227
+
1192
1228
  #Undo preconditioning
1193
1229
  u = Dinv2 @ u
1194
1230
  return u
@@ -1200,18 +1236,18 @@ def canonical_labels(u):
1200
1236
  ----------
1201
1237
  u : (num_verts,1) int array
1202
1238
  A label vector.
1203
-
1239
+
1204
1240
  Returns
1205
1241
  -------
1206
1242
  u : (num_verts,1) int array
1207
1243
  A reodered label vector.
1208
1244
  """
1209
-
1245
+
1210
1246
  n = len(u)
1211
1247
  k = len(np.unique(u))
1212
1248
  label_set = np.zeros((k,1))
1213
1249
  label = 0
1214
-
1250
+
1215
1251
  for i in range(n):
1216
1252
  if u[i] > label:
1217
1253
  label += 1
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: amaazetools
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Python package for mesh processing tools developed by AMAAZE
5
5
  Author-email: Jeff Calder <jwcalder@umn.edu>
6
6
  License: MIT
@@ -50,7 +50,7 @@ Email <jwcalder@umn.edu> with any questions or comments.
50
50
 
51
51
  ## Contributors
52
52
 
53
- Several people have contributed to the development of this software:
53
+ Several people have contributed to the development of this software:
54
54
 
55
55
  1. David Floeder
56
56
  2. Riley O'Neill
@@ -9,7 +9,7 @@ packages = ['amaazetools']
9
9
 
10
10
  [project]
11
11
  name = "amaazetools"
12
- version = "0.1.2"
12
+ version = "0.1.4"
13
13
  authors = [
14
14
  { name="Jeff Calder", email="jwcalder@umn.edu" },
15
15
  ]
@@ -2,6 +2,8 @@
2
2
  *
3
3
  */
4
4
 
5
+
6
+
5
7
  #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
6
8
 
7
9
  #include <Python.h>
File without changes
File without changes
File without changes
File without changes