From c8f5a623b3b7c399de4f413dab47d79b78ef67ae Mon Sep 17 00:00:00 2001
From: Joshua Steer <Joshua.Steer@soton.ac.uk>
Date: Sat, 11 Aug 2018 19:18:21 +0100
Subject: [PATCH] Updated registration to enable barycentric checks that inside
 triangle

---
 AmpScan/registration.py | 71 ++++++++++++++++++++++++++++++++++-------
 AmpScan/smooth.py       |  2 ++
 GUIs/AmpScanGUI.py      | 13 ++++++--
 3 files changed, 72 insertions(+), 14 deletions(-)

diff --git a/AmpScan/registration.py b/AmpScan/registration.py
index 62f0d28..3780329 100644
--- a/AmpScan/registration.py
+++ b/AmpScan/registration.py
@@ -5,6 +5,7 @@ Created on Wed Sep 13 16:07:10 2017
 @author: js22g12
 """
 import numpy as np
+import copy
 from scipy import spatial
 from .core import AmpObject
 
@@ -18,10 +19,13 @@ class registration(object):
             getattr(self, method)()
         
         
-    def point2plane(self):
+    def point2plane(self, neigh = 10, inside = True, smooth=0):
         """
         Function to register the regObject to the baseline mesh
         
+        Need to add test to ensure inside triangle, currently not performing
+        that so ending up in weird places on plane 
+        
         Parameters
         ----------
         Steps: int, default 1
@@ -33,11 +37,12 @@ class registration(object):
         tTree = spatial.cKDTree(fC)
         bData = dict(zip(['vert', 'faces', 'values'], 
                          [self.b.vert, self.b.faces, self.b.values]))
-        self.reg = AmpObject(bData, stype='reg')
-        for step in np.arange(self.steps, 0, -1):
+        regData = copy.deepcopy(bData)
+        self.reg = AmpObject(regData, stype='reg')
+        for step in np.arange(self.steps, 0, -1, dtype=float):
             # Index of 10 centroids nearest to each baseline vertex
-            ind = tTree.query(self.reg.vert, 10)[1]
-            D = np.zeros(self.reg.vert.shape)
+            ind = tTree.query(self.reg.vert, neigh)[1]
+#            D = np.zeros(self.reg.vert.shape)
             # Define normals for faces of nearest faces
             norms = self.t.norm[ind]
             # Get a point on each face
@@ -45,15 +50,23 @@ class registration(object):
             # Calculate dot product between point on face and normals
             d = np.einsum('ijk, ijk->ij', norms, fPoints)
             t = d - np.einsum('ijk, ik->ij', norms, self.reg.vert)
-            # Calculate new points
+            # Calculate the vector from old point to new point
             G = np.einsum('ijk, ij->ijk', norms, t)
-            GMag = np.sqrt(np.einsum('ijk, ijk->ij', G, G)).argmin(axis=1)
+            # Ensure new points lie inside points otherwise set to 99999
+            # Find smallest distance from old to new point 
+            if inside is False:
+                GMag = np.sqrt(np.einsum('ijk, ijk->ij', G, G))
+                GInd = GMag.argmin(axis=1)
+            else:
+                GInd = GInd = self.calcBarycentric(G, ind)
             # Define vector from baseline point to intersect point
-            D = G[np.arange(len(G)), GMag, :]
+            D = G[np.arange(len(G)), GInd, :]
             self.reg.vert += D/step
-            self.reg.lp_smooth(1)
+            if smooth > 0:
+                self.reg.lp_smooth(smooth)
         
         self.reg.calcStruct()
+#        self.reg.values[:] = self.calcError(False)
         self.reg.values[:] = self.calcError(False)
         
     def calcError(self, direct):
@@ -62,14 +75,15 @@ class registration(object):
 
         """
         if direct is True:
+            self.b.calcVNorm()
             values = np.linalg.norm(self.reg.vert - self.b.vert, axis=1)
             # Calculate the unit vector normal between corresponding vertices
             # baseline and target
             vector = (self.reg.vert - self.b.vert)/values[:, None]
             # Calculate angle between the two unit vectors using normal of cross
             # product between vNorm and vector and dot
-            normcrossP = np.linalg.norm(np.cross(vector, self.t.vNorm), axis=1)
-            dotP = np.einsum('ij,ij->i', vector, self.t.vNorm)
+            normcrossP = np.linalg.norm(np.cross(vector, self.b.vNorm), axis=1)
+            dotP = np.einsum('ij,ij->i', vector, self.b.vNorm)
             angle = np.arctan2(normcrossP, dotP)
             polarity = np.ones(angle.shape)
             polarity[angle < np.pi/2] =-1.0
@@ -78,4 +92,37 @@ class registration(object):
         else:
             values = np.linalg.norm(self.reg.vert - self.b.vert, axis=1)
             return values
-
+        
+    def calcBarycentric(self, G, ind):
+        P0 = self.t.vert[self.t.faces[ind, 0]]
+        P1 = self.t.vert[self.t.faces[ind, 1]]
+        P2 = self.t.vert[self.t.faces[ind, 2]]
+        NP = self.reg.vert[:, None, :] + G
+        
+        v0 = P2 - P0
+        v1 = P1 - P0
+        v2 = NP + G - P0
+        
+        d00 = np.einsum('ijk, ijk->ij', v0, v0)
+        d01 = np.einsum('ijk, ijk->ij', v0, v1)
+        d02 = np.einsum('ijk, ijk->ij', v0, v2)
+        d11 = np.einsum('ijk, ijk->ij', v1, v1)
+        d12 = np.einsum('ijk, ijk->ij', v2, v2)
+        
+        denom = d00*d01 - d01*d01
+        u = (d11 * d02 - d01 * d12)/denom
+        v = (d00 * d12 - d01 * d02)/denom
+        # Test if inside 
+        logic = (u >= 0) * (v >= 0) * (u + v < 1)
+        
+        P = np.stack([P0, P1, P2],axis=3)
+        PG = NP[:, :, :, None] - P
+        PD =  np.linalg.norm(PG, axis=3)
+        pdx = PD.argmin(axis=2)
+        i, j = np.meshgrid(np.arange(P.shape[0]), np.arange(P.shape[1]))
+        nearP = P[i.T, j.T, :, pdx]
+        nearG = nearP - self.reg.vert[:, None, :]
+        G[~logic, :] = nearG[~logic, :] 
+        GMag = np.sqrt(np.einsum('ijk, ijk->ij', G, G))
+        GInd = GMag.argmin(axis=1)
+        return GInd
diff --git a/AmpScan/smooth.py b/AmpScan/smooth.py
index 6737819..e4da601 100644
--- a/AmpScan/smooth.py
+++ b/AmpScan/smooth.py
@@ -37,6 +37,8 @@ class smoothMixin(object):
             for j in np.arange(self.vert.shape[0]):
                 # Calculate the mean of the vertex set
                 self.vert[j, :] = neighVerts[ndx[j]:ndx[j+1]].mean(axis=0)
+        self.calcNorm()
+        self.calcVNorm()
     
     def smoothValues(self, n=1):
         """
diff --git a/GUIs/AmpScanGUI.py b/GUIs/AmpScanGUI.py
index 01c9dcc..7b05ee7 100644
--- a/GUIs/AmpScanGUI.py
+++ b/GUIs/AmpScanGUI.py
@@ -124,6 +124,10 @@ class AmpScanGUI(QMainWindow):
         self.fileManager.setTable(static, [1,0,0], 0.5, 2)
         self.fileManager.setTable(moving, [0,0,1], 0.5, 2)
         print('Run the ICP code between %s and %s' % (static, moving))
+        if hasattr(self, 'alCont'):
+            self.alCont.getNames()
+        if hasattr(self, 'regCont'):
+            self.regCont.getNames()
 
     def runRegistration(self):
         c1 = [31.0, 73.0, 125.0]
@@ -138,11 +142,16 @@ class AmpScanGUI(QMainWindow):
         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 = 20).reg
-        reg.addActor(CMap = self.CMapN2P)
+        reg = registration(self.files[baseline], self.files[target], steps = 5).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))
         
-- 
GitLab