/
ClosestPointRegistrator.scala
123 lines (108 loc) · 6.09 KB
/
ClosestPointRegistrator.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package api.registration.utils
import scalismo.common.{DiscreteDomain, PointId, UnstructuredPoints, UnstructuredPointsDomain}
import scalismo.geometry.{EuclideanVector, Point, _1D, _2D, _3D}
import scalismo.mesh.TriangleMesh
trait ClosestPointRegistrator[D, DDomain[D] <: DiscreteDomain[D]] {
/*
returns: Seq of point on template to corresponding point on target + a weight identifying the robustness of the closest point (1.0 = robust, 0.0 = not-robust)
Additionally the average closest point distance is returned
*/
def closestPointCorrespondence(template: DDomain[D], target: DDomain[D]): (Seq[(PointId, Point[D], Double)], Double)
}
object NonRigidClosestPointRegistrator {
// Todo: Different ICP "flavours", closest point in pointset, closest point on surface, closest point along normal
// Todo: Swap directions
private def isPointOnBoundary(id: PointId, mesh: TriangleMesh[_3D]): Boolean = {
mesh.operations.pointIsOnBoundary(id)
}
private def isNormalDirectionOpposite(n1: EuclideanVector[_3D], n2: EuclideanVector[_3D]): Boolean = {
// Todo: Add angle hyperparameter - currently it only looks if the vectors are opposite
(n1 dot n2) < 0
}
private def isClosestPointIntersecting(id: PointId, cp: Point[_3D], mesh: TriangleMesh[_3D]): Boolean = {
val p = mesh.pointSet.point(id)
val v = p-cp
val intersectingPoints = mesh.operations.getIntersectionPoints(p, v).filter(f => f != p) // All intersecting points with the closest point vector
val closestIntersectingPoint = if (intersectingPoints.nonEmpty) intersectingPoints.map(ip => (p - ip).norm).min else Double.PositiveInfinity // Distance to closest intersecting point on template
(closestIntersectingPoint < (v).norm)
}
object ClosestPointTriangleMesh3D extends ClosestPointRegistrator[_3D, TriangleMesh] {
override def closestPointCorrespondence(template: TriangleMesh[_3D], target: TriangleMesh[_3D]): (Seq[(PointId, Point[_3D], Double)], Double) = {
var distance = 0.0
val corr = template.pointSet.pointIds.toSeq.map { id =>
val p = template.pointSet.point(id)
val closestPointOnSurface = target.operations.closestPointOnSurface(p)
val closestPoint = target.pointSet.findClosestPoint(closestPointOnSurface.point)
val w = if (isPointOnBoundary(closestPoint.id, target)) 0.0
else if (isNormalDirectionOpposite(template.vertexNormals.atPoint(id), target.vertexNormals.atPoint(closestPoint.id))) 0.0
else if (isClosestPointIntersecting(id, closestPointOnSurface.point, template)) 0.0
else 1.0
distance += closestPointOnSurface.distance
(id, closestPointOnSurface.point, w)
}
(corr, distance / template.pointSet.numberOfPoints)
}
}
object ClosestPointAlongNormalTriangleMesh3D extends ClosestPointRegistrator[_3D, TriangleMesh] {
override def closestPointCorrespondence(template: TriangleMesh[_3D], target: TriangleMesh[_3D]): (Seq[(PointId, Point[_3D], Double)], Double) = {
var distance = 0.0
val corr = template.pointSet.pointIds.toSeq.map { id =>
val p = template.pointSet.point(id)
val n = template.vertexNormals.atPoint(id)
val intersectingPoints = target.operations.getIntersectionPoints(p, n).filter(f => f != p)
val closestPointAlongNormal = if (intersectingPoints.nonEmpty) Some(intersectingPoints.minBy(ip => (p - ip).norm)) else None
val (closestPoint, w) = if(closestPointAlongNormal.nonEmpty) {
val closestPoint = target.pointSet.findClosestPoint(closestPointAlongNormal.get)
val weight = if (isPointOnBoundary(closestPoint.id, target)) 0.0
else if (isNormalDirectionOpposite(template.vertexNormals.atPoint(id), target.vertexNormals.atPoint(closestPoint.id))) 0.0
else if (isClosestPointIntersecting(id, closestPointAlongNormal.get, template)) 0.0
else 1.0
(closestPointAlongNormal.get, weight)
}
else (p, 0.0) // return p to avoid influincing the "distance" measure too much
distance += (p - closestPoint).norm
(id, closestPoint, w)
}
(corr, distance / template.pointSet.numberOfPoints)
}
}
object ClosestPointUnstructuredPointsDomain3D extends ClosestPointRegistrator[_3D, UnstructuredPointsDomain] {
override def closestPointCorrespondence(template: UnstructuredPointsDomain[_3D], target: UnstructuredPointsDomain[_3D]): (Seq[(PointId, Point[_3D], Double)], Double) = {
var distance = 0.0
val corr = template.pointSet.pointIds.toSeq.map { id =>
val p = template.pointSet.point(id)
val closestPoint = target.pointSet.findClosestPoint(p)
val w = 1.0
distance += (p - closestPoint.point).norm
(id, closestPoint.point, w)
}
(corr, distance / template.pointSet.numberOfPoints)
}
}
object ClosestPointUnstructuredPointsDomain2D extends ClosestPointRegistrator[_2D, UnstructuredPointsDomain] {
override def closestPointCorrespondence(template: UnstructuredPointsDomain[_2D], target: UnstructuredPointsDomain[_2D]): (Seq[(PointId, Point[_2D], Double)], Double) = {
var distance = 0.0
val corr = template.pointSet.pointIds.toSeq.map { id =>
val p = template.pointSet.point(id)
val closestPoint = target.pointSet.findClosestPoint(p)
val w = 1.0
distance += (p - closestPoint.point).norm
(id, closestPoint.point, w)
}
(corr, distance / template.pointSet.numberOfPoints)
}
}
object ClosestPointUnstructuredPointsDomain1D extends ClosestPointRegistrator[_1D, UnstructuredPointsDomain] {
override def closestPointCorrespondence(template: UnstructuredPointsDomain[_1D], target: UnstructuredPointsDomain[_1D]): (Seq[(PointId, Point[_1D], Double)], Double) = {
var distance = 0.0
val corr = template.pointSet.pointIds.toSeq.map { id =>
val p = template.pointSet.point(id)
val closestPoint = target.pointSet.findClosestPoint(p)
val w = 1.0
distance += (p - closestPoint.point).norm
(id, closestPoint.point, w)
}
(corr, distance / template.pointSet.numberOfPoints)
}
}
}