@srsergio/taptapp-ar 1.1.1 → 1.1.3
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.
- package/dist/compiler/node-worker.js +1 -197
- package/dist/compiler/offline-compiler.js +1 -207
- package/dist/core/constants.js +1 -38
- package/dist/core/detector/crop-detector.js +1 -88
- package/dist/core/detector/detector-lite.js +1 -455
- package/dist/core/detector/freak.js +1 -89
- package/dist/core/estimation/estimate.js +1 -16
- package/dist/core/estimation/estimator.js +1 -30
- package/dist/core/estimation/morph-refinement.js +1 -116
- package/dist/core/estimation/non-rigid-refine.js +1 -70
- package/dist/core/estimation/pnp-solver.js +1 -109
- package/dist/core/estimation/refine-estimate.js +1 -311
- package/dist/core/estimation/utils.js +1 -67
- package/dist/core/features/auto-rotation-feature.js +1 -30
- package/dist/core/features/crop-detection-feature.js +1 -26
- package/dist/core/features/feature-base.js +1 -1
- package/dist/core/features/feature-manager.js +1 -55
- package/dist/core/features/one-euro-filter-feature.js +1 -44
- package/dist/core/features/temporal-filter-feature.js +1 -57
- package/dist/core/image-list.js +1 -54
- package/dist/core/input-loader.js +1 -87
- package/dist/core/matching/hamming-distance.js +1 -66
- package/dist/core/matching/hdc.js +1 -102
- package/dist/core/matching/hierarchical-clustering.js +1 -130
- package/dist/core/matching/hough.js +1 -170
- package/dist/core/matching/matcher.js +1 -66
- package/dist/core/matching/matching.js +1 -401
- package/dist/core/matching/ransacHomography.js +1 -132
- package/dist/core/perception/bio-inspired-engine.js +1 -232
- package/dist/core/perception/foveal-attention.js +1 -280
- package/dist/core/perception/index.js +1 -17
- package/dist/core/perception/predictive-coding.js +1 -278
- package/dist/core/perception/saccadic-controller.js +1 -269
- package/dist/core/perception/saliency-map.js +1 -254
- package/dist/core/perception/scale-orchestrator.js +1 -68
- package/dist/core/protocol.js +1 -254
- package/dist/core/tracker/extract-utils.js +1 -29
- package/dist/core/tracker/extract.js +1 -306
- package/dist/core/tracker/tracker.js +1 -352
- package/dist/core/utils/cumsum.js +1 -37
- package/dist/core/utils/delaunay.js +1 -125
- package/dist/core/utils/geometry.js +1 -101
- package/dist/core/utils/gpu-compute.js +1 -231
- package/dist/core/utils/homography.js +1 -138
- package/dist/core/utils/images.js +1 -108
- package/dist/core/utils/lsh-binarizer.js +1 -37
- package/dist/core/utils/lsh-direct.js +1 -76
- package/dist/core/utils/projection.js +1 -51
- package/dist/core/utils/randomizer.js +1 -25
- package/dist/core/utils/worker-pool.js +1 -89
- package/dist/index.js +1 -7
- package/dist/libs/one-euro-filter.js +1 -70
- package/dist/react/TaptappAR.js +1 -151
- package/dist/react/types.js +1 -16
- package/dist/react/use-ar.js +1 -118
- package/dist/runtime/aframe.js +1 -272
- package/dist/runtime/bio-inspired-controller.js +1 -358
- package/dist/runtime/controller.js +1 -592
- package/dist/runtime/controller.worker.js +1 -93
- package/dist/runtime/index.js +1 -5
- package/dist/runtime/three.js +1 -304
- package/dist/runtime/track.js +1 -381
- package/package.json +10 -4
|
@@ -1,116 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Morphological Refinement - "Active Edge Alignment"
|
|
3
|
-
*
|
|
4
|
-
* This Moonshot algorithm snaps the projected target to the real image edges.
|
|
5
|
-
* It solves the "Small Box / Drift" problem by maximizing alignment with
|
|
6
|
-
* local image gradients using a Spring-Mass optimization system.
|
|
7
|
-
*/
|
|
8
|
-
import { Matrix, SingularValueDecomposition } from "ml-matrix";
|
|
9
|
-
export function refineWithMorphology({ imageData, width, height, targetData, initialH, iterations = 3 }) {
|
|
10
|
-
let currentH = [...initialH];
|
|
11
|
-
// 1. Boundary Points (The "Anchors" of our elastic malla)
|
|
12
|
-
const boundaryPoints = [];
|
|
13
|
-
const step = 0.05;
|
|
14
|
-
for (let i = 0; i <= 1.0; i += step) {
|
|
15
|
-
boundaryPoints.push({ x: i * targetData.w, y: 0 });
|
|
16
|
-
boundaryPoints.push({ x: i * targetData.w, y: targetData.h });
|
|
17
|
-
boundaryPoints.push({ x: 0, y: i * targetData.h });
|
|
18
|
-
boundaryPoints.push({ x: targetData.w, y: i * targetData.h });
|
|
19
|
-
}
|
|
20
|
-
for (let iter = 0; iter < iterations; iter++) {
|
|
21
|
-
const correspondences = [];
|
|
22
|
-
for (const pt of boundaryPoints) {
|
|
23
|
-
// Project
|
|
24
|
-
const w = currentH[6] * pt.x + currentH[7] * pt.y + currentH[8];
|
|
25
|
-
const sx = (currentH[0] * pt.x + currentH[1] * pt.y + currentH[2]) / w;
|
|
26
|
-
const sy = (currentH[3] * pt.x + currentH[4] * pt.y + currentH[5]) / w;
|
|
27
|
-
if (sx < 2 || sx >= width - 2 || sy < 2 || sy >= height - 2)
|
|
28
|
-
continue;
|
|
29
|
-
// 2. Local Gradient Search (The "Pull" of the image)
|
|
30
|
-
const searchDist = 10;
|
|
31
|
-
let bestX = sx;
|
|
32
|
-
let bestY = sy;
|
|
33
|
-
let maxGrad = -1;
|
|
34
|
-
for (let dy = -searchDist; dy <= searchDist; dy += 2) {
|
|
35
|
-
for (let dx = -searchDist; dx <= searchDist; dx += 2) {
|
|
36
|
-
const nx = Math.floor(sx + dx);
|
|
37
|
-
const ny = Math.floor(sy + dy);
|
|
38
|
-
const idx = ny * width + nx;
|
|
39
|
-
// Sobel-like gradient magnitude
|
|
40
|
-
const gx = imageData[idx + 1] - imageData[idx - 1];
|
|
41
|
-
const gy = imageData[idx + width] - imageData[idx - width];
|
|
42
|
-
const grad = gx * gx + gy * gy;
|
|
43
|
-
if (grad > maxGrad) {
|
|
44
|
-
maxGrad = grad;
|
|
45
|
-
bestX = nx;
|
|
46
|
-
bestY = ny;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
if (maxGrad > 500) {
|
|
51
|
-
correspondences.push({
|
|
52
|
-
src: pt,
|
|
53
|
-
dst: { x: bestX, y: bestY },
|
|
54
|
-
weight: Math.min(1.0, maxGrad / 15000)
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
if (correspondences.length < 10)
|
|
59
|
-
break;
|
|
60
|
-
// 3. Solve for best Homography using SVD
|
|
61
|
-
const nextH = solveDLTWeight(correspondences);
|
|
62
|
-
if (nextH) {
|
|
63
|
-
// Soft-Update (Momentum)
|
|
64
|
-
for (let i = 0; i < 9; i++) {
|
|
65
|
-
currentH[i] = currentH[i] * 0.5 + nextH[i] * 0.5;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
return currentH;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Direct Linear Transform with Weights
|
|
73
|
-
*/
|
|
74
|
-
function solveDLTWeight(pairs) {
|
|
75
|
-
const n = pairs.length;
|
|
76
|
-
const A = new Matrix(n * 2, 9);
|
|
77
|
-
for (let i = 0; i < n; i++) {
|
|
78
|
-
const { src, dst, weight: w } = pairs[i];
|
|
79
|
-
const x = src.x;
|
|
80
|
-
const y = src.y;
|
|
81
|
-
const xp = dst.x;
|
|
82
|
-
const yp = dst.y;
|
|
83
|
-
// Row 1
|
|
84
|
-
A.set(i * 2, 0, 0);
|
|
85
|
-
A.set(i * 2, 1, 0);
|
|
86
|
-
A.set(i * 2, 2, 0);
|
|
87
|
-
A.set(i * 2, 3, -x * w);
|
|
88
|
-
A.set(i * 2, 4, -y * w);
|
|
89
|
-
A.set(i * 2, 5, -w);
|
|
90
|
-
A.set(i * 2, 6, yp * x * w);
|
|
91
|
-
A.set(i * 2, 7, yp * y * w);
|
|
92
|
-
A.set(i * 2, 8, yp * w);
|
|
93
|
-
// Row 2
|
|
94
|
-
A.set(i * 2 + 1, 0, x * w);
|
|
95
|
-
A.set(i * 2 + 1, 1, y * w);
|
|
96
|
-
A.set(i * 2 + 1, 2, w);
|
|
97
|
-
A.set(i * 2 + 1, 3, 0);
|
|
98
|
-
A.set(i * 2 + 1, 4, 0);
|
|
99
|
-
A.set(i * 2 + 1, 5, 0);
|
|
100
|
-
A.set(i * 2 + 1, 6, -xp * x * w);
|
|
101
|
-
A.set(i * 2 + 1, 7, -xp * y * w);
|
|
102
|
-
A.set(i * 2 + 1, 8, -xp * w);
|
|
103
|
-
}
|
|
104
|
-
try {
|
|
105
|
-
const svd = new SingularValueDecomposition(A);
|
|
106
|
-
const V = svd.rightSingularVectors;
|
|
107
|
-
// Last column of V is the solution
|
|
108
|
-
const h = V.getColumn(8);
|
|
109
|
-
// Normalize H[8] to 1
|
|
110
|
-
const scale = 1.0 / h[8];
|
|
111
|
-
return h.map(v => v * scale);
|
|
112
|
-
}
|
|
113
|
-
catch (e) {
|
|
114
|
-
return null;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
1
|
+
import{Matrix as t,SingularValueDecomposition as e}from"ml-matrix";export function refineWithMorphology({imageData:t,width:e,height:o,targetData:n,initialH:r,iterations:i=3}){let h=[...r];const l=[];for(let t=0;t<=1;t+=.05)l.push({x:t*n.w,y:0}),l.push({x:t*n.w,y:n.h}),l.push({x:0,y:t*n.h}),l.push({x:n.w,y:t*n.h});for(let n=0;n<i;n++){const n=[];for(const s of l){const r=h[6]*s.x+h[7]*s.y+h[8],i=(h[0]*s.x+h[1]*s.y+h[2])/r,l=(h[3]*s.x+h[4]*s.y+h[5])/r;if(i<2||i>=e-2||l<2||l>=o-2)continue;const c=10;let f=i,a=l,u=-1;for(let s=-c;s<=c;s+=2)for(let o=-c;o<=c;o+=2){const n=Math.floor(i+o),r=Math.floor(l+s),h=r*e+n,c=t[h+1]-t[h-1],x=t[h+e]-t[h-e],y=c*c+x*x;y>u&&(u=y,f=n,a=r)}u>500&&n.push({src:s,dst:{x:f,y:a},weight:Math.min(1,u/15e3)})}if(n.length<10)break;const r=s(n);if(r)for(let t=0;t<9;t++)h[t]=.5*h[t]+.5*r[t]}return h}function s(s){const o=s.length,n=new t(2*o,9);for(let t=0;t<o;t++){const{src:e,dst:o,weight:r}=s[t],i=e.x,h=e.y,l=o.x,c=o.y;n.set(2*t,0,0),n.set(2*t,1,0),n.set(2*t,2,0),n.set(2*t,3,-i*r),n.set(2*t,4,-h*r),n.set(2*t,5,-r),n.set(2*t,6,c*i*r),n.set(2*t,7,c*h*r),n.set(2*t,8,c*r),n.set(2*t+1,0,i*r),n.set(2*t+1,1,h*r),n.set(2*t+1,2,r),n.set(2*t+1,3,0),n.set(2*t+1,4,0),n.set(2*t+1,5,0),n.set(2*t+1,6,-l*i*r),n.set(2*t+1,7,-l*h*r),n.set(2*t+1,8,-l*r)}try{const t=new e(n).rightSingularVectors.getColumn(8),s=1/t[8];return t.map(t=>t*s)}catch(t){return null}}
|
|
@@ -1,70 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* 🚀 Moonshot: Non-Rigid Surface Refinement (Mass-Spring System)
|
|
3
|
-
*
|
|
4
|
-
* Instead of a single homography, we relax a triangle mesh to match tracked points
|
|
5
|
-
* while preserving edge lengths (Isometric constraint).
|
|
6
|
-
*/
|
|
7
|
-
export function refineNonRigid({ mesh, trackedPoints, currentVertices, iterations = 5 }) {
|
|
8
|
-
const { e: edges, rl: restLengths } = mesh;
|
|
9
|
-
const numVertices = currentVertices.length / 2;
|
|
10
|
-
const vertices = new Float32Array(currentVertices); // copy
|
|
11
|
-
// lambda for stiffness
|
|
12
|
-
const stiffness = 0.8;
|
|
13
|
-
const dataFidelity = 0.5;
|
|
14
|
-
for (let iter = 0; iter < iterations; iter++) {
|
|
15
|
-
// 1. Edge Length Constraints (Isometric term)
|
|
16
|
-
for (let i = 0; i < restLengths.length; i++) {
|
|
17
|
-
const idx1 = edges[i * 2];
|
|
18
|
-
const idx2 = edges[i * 2 + 1];
|
|
19
|
-
const restL = restLengths[i];
|
|
20
|
-
const vx1 = vertices[idx1 * 2];
|
|
21
|
-
const vy1 = vertices[idx1 * 2 + 1];
|
|
22
|
-
const vx2 = vertices[idx2 * 2];
|
|
23
|
-
const vy2 = vertices[idx2 * 2 + 1];
|
|
24
|
-
const dx = vx2 - vx1;
|
|
25
|
-
const dy = vy2 - vy1;
|
|
26
|
-
const currentL = Math.sqrt(dx * dx + dy * dy);
|
|
27
|
-
if (currentL < 0.0001)
|
|
28
|
-
continue;
|
|
29
|
-
const diff = (currentL - restL) / currentL;
|
|
30
|
-
const moveX = dx * 0.5 * diff * stiffness;
|
|
31
|
-
const moveY = dy * 0.5 * diff * stiffness;
|
|
32
|
-
vertices[idx1 * 2] += moveX;
|
|
33
|
-
vertices[idx1 * 2 + 1] += moveY;
|
|
34
|
-
vertices[idx2 * 2] -= moveX;
|
|
35
|
-
vertices[idx2 * 2 + 1] -= moveY;
|
|
36
|
-
}
|
|
37
|
-
// 2. Data Fidelity Constraints (Alignment with NCC tracker)
|
|
38
|
-
for (const tp of trackedPoints) {
|
|
39
|
-
const idx = tp.meshIndex;
|
|
40
|
-
if (idx === undefined)
|
|
41
|
-
continue;
|
|
42
|
-
const targetX = tp.x;
|
|
43
|
-
const targetY = tp.y;
|
|
44
|
-
vertices[idx * 2] += (targetX - vertices[idx * 2]) * dataFidelity;
|
|
45
|
-
vertices[idx * 2 + 1] += (targetY - vertices[idx * 2 + 1]) * dataFidelity;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return vertices;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Maps a mesh from reference space to screen space using a homography
|
|
52
|
-
*/
|
|
53
|
-
export function projectMesh(mesh, homography, width, height) {
|
|
54
|
-
const { px, py } = mesh; // original octave points used as mesh vertices
|
|
55
|
-
const numVertices = px.length;
|
|
56
|
-
const projected = new Float32Array(numVertices * 2);
|
|
57
|
-
const h = homography;
|
|
58
|
-
const h00 = h[0][0], h01 = h[0][1], h02 = h[0][3];
|
|
59
|
-
const h10 = h[1][0], h11 = h[1][1], h12 = h[1][3];
|
|
60
|
-
const h20 = h[2][0], h21 = h[2][1], h22 = h[2][3];
|
|
61
|
-
for (let i = 0; i < numVertices; i++) {
|
|
62
|
-
const x = px[i];
|
|
63
|
-
const y = py[i];
|
|
64
|
-
const uz = (x * h20) + (y * h21) + h22;
|
|
65
|
-
const invZ = 1.0 / uz;
|
|
66
|
-
projected[i * 2] = ((x * h00) + (y * h01) + h02) * invZ;
|
|
67
|
-
projected[i * 2 + 1] = ((x * h10) + (y * h11) + h12) * invZ;
|
|
68
|
-
}
|
|
69
|
-
return projected;
|
|
70
|
-
}
|
|
1
|
+
export function refineNonRigid({mesh:t,trackedPoints:n,currentVertices:e,iterations:o=5}){const{e:r,rl:c}=t,s=(e.length,new Float32Array(e));for(let t=0;t<o;t++){for(let t=0;t<c.length;t++){const n=r[2*t],e=r[2*t+1],o=c[t],i=s[2*n],f=s[2*n+1],l=s[2*e]-i,a=s[2*e+1]-f,h=Math.sqrt(l*l+a*a);if(h<1e-4)continue;const u=(h-o)/h,p=.5*l*u*.8,x=.5*a*u*.8;s[2*n]+=p,s[2*n+1]+=x,s[2*e]-=p,s[2*e+1]-=x}for(const t of n){const n=t.meshIndex;if(void 0===n)continue;const e=t.x,o=t.y;s[2*n]+=.5*(e-s[2*n]),s[2*n+1]+=.5*(o-s[2*n+1])}}return s}export function projectMesh(t,n,e,o){const{px:r,py:c}=t,s=r.length,i=new Float32Array(2*s),f=n,l=f[0][0],a=f[0][1],h=f[0][3],u=f[1][0],p=f[1][1],x=f[1][3],d=f[2][0],g=f[2][1],y=f[2][3];for(let t=0;t<s;t++){const n=r[t],e=c[t],o=1/(n*d+e*g+y);i[2*t]=(n*l+e*a+h)*o,i[2*t+1]=(n*u+e*p+x)*o}return i}
|
|
@@ -1,109 +1 @@
|
|
|
1
|
-
|
|
2
|
-
* Direct PnP (Perspective-n-Point) Solver for Planar Targets
|
|
3
|
-
*
|
|
4
|
-
* This Moonshot algorithm ignores octave-relative scales and works
|
|
5
|
-
* purely in Physical World Units. It uses the Camera Matrix (K)
|
|
6
|
-
* to deduce the real-world distance (Z).
|
|
7
|
-
*/
|
|
8
|
-
import { Matrix, SingularValueDecomposition } from "ml-matrix";
|
|
9
|
-
export function solvePosePnP({ screenCoords, worldCoords, projectionTransform }) {
|
|
10
|
-
const K = new Matrix(projectionTransform);
|
|
11
|
-
const n = screenCoords.length;
|
|
12
|
-
// 1. Build the DLT matrix for Pose (Directly estimating [R|t])
|
|
13
|
-
// We assume worldCoords are [X, Y, 0]
|
|
14
|
-
// Eq: x = K * [R|t] * X
|
|
15
|
-
// K^-1 * x = [r1 r2 t] * [X Y 1]^T
|
|
16
|
-
const KI = Inverse3x3(projectionTransform);
|
|
17
|
-
const A = new Matrix(n * 2, 9);
|
|
18
|
-
for (let i = 0; i < n; i++) {
|
|
19
|
-
const sci = screenCoords[i];
|
|
20
|
-
const wci = worldCoords[i];
|
|
21
|
-
// Normalized camera coordinates
|
|
22
|
-
const nx = KI[0] * sci.x + KI[1] * sci.y + KI[2];
|
|
23
|
-
const ny = KI[3] * sci.x + KI[4] * sci.y + KI[5];
|
|
24
|
-
const nz = KI[6] * sci.x + KI[7] * sci.y + KI[8];
|
|
25
|
-
const unx = nx / nz;
|
|
26
|
-
const uny = ny / nz;
|
|
27
|
-
// DLT equations for [r11 r12 r21 r22 r31 r32 t1 t2 t3]
|
|
28
|
-
const X = wci.x;
|
|
29
|
-
const Y = wci.y;
|
|
30
|
-
// Row 1: X*r11 + Y*r12 + t1 - unx*(X*r31 + Y*r32 + t3) = 0
|
|
31
|
-
A.set(i * 2, 0, X);
|
|
32
|
-
A.set(i * 2, 1, Y);
|
|
33
|
-
A.set(i * 2, 2, 1);
|
|
34
|
-
A.set(i * 2, 3, 0);
|
|
35
|
-
A.set(i * 2, 4, 0);
|
|
36
|
-
A.set(i * 2, 5, 0);
|
|
37
|
-
A.set(i * 2, 6, -unx * X);
|
|
38
|
-
A.set(i * 2, 7, -unx * Y);
|
|
39
|
-
A.set(i * 2, 8, -unx);
|
|
40
|
-
// Row 2: X*r21 + Y*r22 + t2 - uny*(X*r31 + Y*r32 + t3) = 0
|
|
41
|
-
A.set(i * 2 + 1, 0, 0);
|
|
42
|
-
A.set(i * 2 + 1, 1, 0);
|
|
43
|
-
A.set(i * 2 + 1, 2, 0);
|
|
44
|
-
A.set(i * 2 + 1, 3, X);
|
|
45
|
-
A.set(i * 2 + 1, 4, Y);
|
|
46
|
-
A.set(i * 2 + 1, 5, 1);
|
|
47
|
-
A.set(i * 2 + 1, 6, -uny * X);
|
|
48
|
-
A.set(i * 2 + 1, 7, -uny * Y);
|
|
49
|
-
A.set(i * 2 + 1, 8, -uny);
|
|
50
|
-
}
|
|
51
|
-
// Solve via SVD
|
|
52
|
-
const svd = new SingularValueDecomposition(A);
|
|
53
|
-
const V = svd.rightSingularVectors;
|
|
54
|
-
const sol = V.getColumn(8); // last column
|
|
55
|
-
// 3. Extract r1, r2 and t from the DLT solution
|
|
56
|
-
// Standard DLT has an overall sign ambiguity. We force sol[8] (t3) to be positive.
|
|
57
|
-
if (sol[8] < 0) {
|
|
58
|
-
for (let i = 0; i < 9; i++)
|
|
59
|
-
sol[i] = -sol[i];
|
|
60
|
-
}
|
|
61
|
-
const r1_raw = [sol[0], sol[3], sol[6]];
|
|
62
|
-
const r2_raw = [sol[1], sol[4], sol[7]];
|
|
63
|
-
const t_raw = [sol[2], sol[5], sol[8]];
|
|
64
|
-
const scale1 = Math.sqrt(r1_raw[0] ** 2 + r1_raw[1] ** 2 + r1_raw[2] ** 2);
|
|
65
|
-
const scale2 = Math.sqrt(r2_raw[0] ** 2 + r2_raw[1] ** 2 + r2_raw[2] ** 2);
|
|
66
|
-
const scale = (scale1 + scale2) / 2;
|
|
67
|
-
// 4. Construct Rotation Matrix and orthogonalize via SVD
|
|
68
|
-
const R_approx = new Matrix([
|
|
69
|
-
[r1_raw[0] / scale1, r2_raw[0] / scale2, 0],
|
|
70
|
-
[r1_raw[1] / scale1, r2_raw[1] / scale2, 0],
|
|
71
|
-
[r1_raw[2] / scale1, r2_raw[2] / scale2, 0]
|
|
72
|
-
]);
|
|
73
|
-
// R3 = R1 x R2
|
|
74
|
-
R_approx.set(0, 2, R_approx.get(1, 0) * R_approx.get(2, 1) - R_approx.get(2, 0) * R_approx.get(1, 1));
|
|
75
|
-
R_approx.set(1, 2, R_approx.get(2, 0) * R_approx.get(0, 1) - R_approx.get(0, 0) * R_approx.get(2, 1));
|
|
76
|
-
R_approx.set(2, 2, R_approx.get(0, 0) * R_approx.get(1, 1) - R_approx.get(1, 0) * R_approx.get(0, 1));
|
|
77
|
-
const svdRot = new SingularValueDecomposition(R_approx);
|
|
78
|
-
const U = svdRot.leftSingularVectors;
|
|
79
|
-
const Vrot = svdRot.rightSingularVectors;
|
|
80
|
-
let R = U.mmul(Vrot.transpose());
|
|
81
|
-
const getDet3 = (m) => {
|
|
82
|
-
return m.get(0, 0) * (m.get(1, 1) * m.get(2, 2) - m.get(1, 2) * m.get(2, 1)) -
|
|
83
|
-
m.get(0, 1) * (m.get(1, 0) * m.get(2, 2) - m.get(1, 2) * m.get(2, 0)) +
|
|
84
|
-
m.get(0, 2) * (m.get(1, 0) * m.get(2, 1) - m.get(1, 1) * m.get(2, 0));
|
|
85
|
-
};
|
|
86
|
-
if (getDet3(R) < 0) {
|
|
87
|
-
const U_mat = U.clone();
|
|
88
|
-
for (let i = 0; i < 3; i++)
|
|
89
|
-
U_mat.set(i, 2, -U_mat.get(i, 2));
|
|
90
|
-
R = U_mat.mmul(Vrot.transpose());
|
|
91
|
-
}
|
|
92
|
-
return [
|
|
93
|
-
[R.get(0, 0), R.get(0, 1), R.get(0, 2), t_raw[0] / scale],
|
|
94
|
-
[R.get(1, 0), R.get(1, 1), R.get(1, 2), t_raw[1] / scale],
|
|
95
|
-
[R.get(2, 0), R.get(2, 1), R.get(2, 2), t_raw[2] / scale]
|
|
96
|
-
];
|
|
97
|
-
}
|
|
98
|
-
function Inverse3x3(m) {
|
|
99
|
-
const k00 = m[0][0], k01 = m[0][1], k02 = m[0][2];
|
|
100
|
-
const k10 = m[1][0], k11 = m[1][1], k12 = m[1][2];
|
|
101
|
-
const k20 = m[2][0], k21 = m[2][1], k22 = m[2][2];
|
|
102
|
-
const det = k00 * (k11 * k22 - k21 * k12) - k01 * (k10 * k22 - k12 * k20) + k02 * (k10 * k21 - k11 * k20);
|
|
103
|
-
const invDet = 1.0 / det;
|
|
104
|
-
return [
|
|
105
|
-
(k11 * k22 - k12 * k21) * invDet, (k02 * k21 - k01 * k22) * invDet, (k01 * k12 - k02 * k11) * invDet,
|
|
106
|
-
(k12 * k20 - k10 * k22) * invDet, (k00 * k22 - k02 * k20) * invDet, (k10 * k02 - k00 * k12) * invDet,
|
|
107
|
-
(k10 * k21 - k11 * k20) * invDet, (k20 * k01 - k21 * k00) * invDet, (k00 * k11 - k10 * k01) * invDet
|
|
108
|
-
];
|
|
109
|
-
}
|
|
1
|
+
import{Matrix as t,SingularValueDecomposition as e}from"ml-matrix";export function solvePosePnP({screenCoords:g,worldCoords:s,projectionTransform:o}){new t(o);const r=g.length,n=function(t){const e=t[0][0],g=t[0][1],s=t[0][2],o=t[1][0],r=t[1][1],n=t[1][2],l=t[2][0],c=t[2][1],i=t[2][2],a=1/(e*(r*i-c*n)-g*(o*i-n*l)+s*(o*c-r*l));return[(r*i-n*c)*a,(s*c-g*i)*a,(g*n-s*r)*a,(n*l-o*i)*a,(e*i-s*l)*a,(o*s-e*n)*a,(o*c-r*l)*a,(l*g-c*e)*a,(e*r-o*g)*a]}(o),l=new t(2*r,9);for(let t=0;t<r;t++){const e=g[t],o=s[t],r=n[0]*e.x+n[1]*e.y+n[2],c=n[3]*e.x+n[4]*e.y+n[5],i=n[6]*e.x+n[7]*e.y+n[8],a=r/i,f=c/i,m=o.x,u=o.y;l.set(2*t,0,m),l.set(2*t,1,u),l.set(2*t,2,1),l.set(2*t,3,0),l.set(2*t,4,0),l.set(2*t,5,0),l.set(2*t,6,-a*m),l.set(2*t,7,-a*u),l.set(2*t,8,-a),l.set(2*t+1,0,0),l.set(2*t+1,1,0),l.set(2*t+1,2,0),l.set(2*t+1,3,m),l.set(2*t+1,4,u),l.set(2*t+1,5,1),l.set(2*t+1,6,-f*m),l.set(2*t+1,7,-f*u),l.set(2*t+1,8,-f)}const c=new e(l).rightSingularVectors.getColumn(8);if(c[8]<0)for(let t=0;t<9;t++)c[t]=-c[t];const i=[c[0],c[3],c[6]],a=[c[1],c[4],c[7]],f=[c[2],c[5],c[8]],m=Math.sqrt(i[0]**2+i[1]**2+i[2]**2),u=Math.sqrt(a[0]**2+a[1]**2+a[2]**2),w=(m+u)/2,x=new t([[i[0]/m,a[0]/u,0],[i[1]/m,a[1]/u,0],[i[2]/m,a[2]/u,0]]);x.set(0,2,x.get(1,0)*x.get(2,1)-x.get(2,0)*x.get(1,1)),x.set(1,2,x.get(2,0)*x.get(0,1)-x.get(0,0)*x.get(2,1)),x.set(2,2,x.get(0,0)*x.get(1,1)-x.get(1,0)*x.get(0,1));const h=new e(x),p=h.leftSingularVectors,y=h.rightSingularVectors;let d=p.mmul(y.transpose());if((C=d).get(0,0)*(C.get(1,1)*C.get(2,2)-C.get(1,2)*C.get(2,1))-C.get(0,1)*(C.get(1,0)*C.get(2,2)-C.get(1,2)*C.get(2,0))+C.get(0,2)*(C.get(1,0)*C.get(2,1)-C.get(1,1)*C.get(2,0))<0){const t=p.clone();for(let e=0;e<3;e++)t.set(e,2,-t.get(e,2));d=t.mmul(y.transpose())}var C;return[[d.get(0,0),d.get(0,1),d.get(0,2),f[0]/w],[d.get(1,0),d.get(1,1),d.get(1,2),f[1]/w],[d.get(2,0),d.get(2,1),d.get(2,2),f[2]/w]]}
|
|
@@ -1,311 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { applyModelViewProjectionTransform, buildModelViewProjectionTransform, computeScreenCoordiate, } from "./utils.js";
|
|
3
|
-
const TRACKING_THRESH = 5.0; // default
|
|
4
|
-
const K2_FACTOR = 4.0; // Question: should it be relative to the size of the screen instead of hardcoded?
|
|
5
|
-
const ICP_MAX_LOOP = 10;
|
|
6
|
-
const ICP_BREAK_LOOP_ERROR_THRESH = 0.1;
|
|
7
|
-
const ICP_BREAK_LOOP_ERROR_RATIO_THRESH = 0.99;
|
|
8
|
-
// some temporary/intermediate variables used later. Declare them beforehand to reduce new object allocations
|
|
9
|
-
let mat = [[], [], []];
|
|
10
|
-
let J_U_Xc = [[], []]; // 2x3
|
|
11
|
-
let J_Xc_S = [[], [], []]; // 3x6
|
|
12
|
-
const refineEstimate = ({ initialModelViewTransform, projectionTransform, worldCoords, screenCoords, stabilities, // Stability-based weighting
|
|
13
|
-
}) => {
|
|
14
|
-
// Question: shall we normlize the screen coords as well?
|
|
15
|
-
// Question: do we need to normlize the scale as well, i.e. make coords from -1 to 1
|
|
16
|
-
//
|
|
17
|
-
// normalize world coords - reposition them to center of mass
|
|
18
|
-
// assume z coordinate is always zero (in our case, the image target is planar with z = 0
|
|
19
|
-
let dx = 0;
|
|
20
|
-
let dy = 0;
|
|
21
|
-
for (let i = 0; i < worldCoords.length; i++) {
|
|
22
|
-
dx += worldCoords[i].x;
|
|
23
|
-
dy += worldCoords[i].y;
|
|
24
|
-
}
|
|
25
|
-
dx /= worldCoords.length;
|
|
26
|
-
dy /= worldCoords.length;
|
|
27
|
-
const normalizedWorldCoords = [];
|
|
28
|
-
for (let i = 0; i < worldCoords.length; i++) {
|
|
29
|
-
normalizedWorldCoords.push({
|
|
30
|
-
x: worldCoords[i].x - dx,
|
|
31
|
-
y: worldCoords[i].y - dy,
|
|
32
|
-
z: worldCoords[i].z,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
const diffModelViewTransform = [[], [], []];
|
|
36
|
-
for (let j = 0; j < 3; j++) {
|
|
37
|
-
for (let i = 0; i < 3; i++) {
|
|
38
|
-
diffModelViewTransform[j][i] = initialModelViewTransform[j][i];
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
diffModelViewTransform[0][3] =
|
|
42
|
-
initialModelViewTransform[0][0] * dx +
|
|
43
|
-
initialModelViewTransform[0][1] * dy +
|
|
44
|
-
initialModelViewTransform[0][3];
|
|
45
|
-
diffModelViewTransform[1][3] =
|
|
46
|
-
initialModelViewTransform[1][0] * dx +
|
|
47
|
-
initialModelViewTransform[1][1] * dy +
|
|
48
|
-
initialModelViewTransform[1][3];
|
|
49
|
-
diffModelViewTransform[2][3] =
|
|
50
|
-
initialModelViewTransform[2][0] * dx +
|
|
51
|
-
initialModelViewTransform[2][1] * dy +
|
|
52
|
-
initialModelViewTransform[2][3];
|
|
53
|
-
// use iterative closest point algorithm to refine the modelViewTransform
|
|
54
|
-
const inlierProbs = [1.0, 0.8, 0.6, 0.4, 0.0];
|
|
55
|
-
let updatedModelViewTransform = diffModelViewTransform; // iteratively update this transform
|
|
56
|
-
let finalModelViewTransform = null;
|
|
57
|
-
for (let i = 0; i < inlierProbs.length; i++) {
|
|
58
|
-
const ret = _doICP({
|
|
59
|
-
initialModelViewTransform: updatedModelViewTransform,
|
|
60
|
-
projectionTransform,
|
|
61
|
-
worldCoords: normalizedWorldCoords,
|
|
62
|
-
screenCoords,
|
|
63
|
-
stabilities, // Pass weights to ICP
|
|
64
|
-
inlierProb: inlierProbs[i],
|
|
65
|
-
});
|
|
66
|
-
updatedModelViewTransform = ret.modelViewTransform;
|
|
67
|
-
//console.log("err", ret.err);
|
|
68
|
-
if (ret.err < TRACKING_THRESH) {
|
|
69
|
-
finalModelViewTransform = updatedModelViewTransform;
|
|
70
|
-
break;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
if (finalModelViewTransform === null)
|
|
74
|
-
return null;
|
|
75
|
-
// de-normalize
|
|
76
|
-
finalModelViewTransform[0][3] =
|
|
77
|
-
finalModelViewTransform[0][3] -
|
|
78
|
-
finalModelViewTransform[0][0] * dx -
|
|
79
|
-
finalModelViewTransform[0][1] * dy;
|
|
80
|
-
finalModelViewTransform[1][3] =
|
|
81
|
-
finalModelViewTransform[1][3] -
|
|
82
|
-
finalModelViewTransform[1][0] * dx -
|
|
83
|
-
finalModelViewTransform[1][1] * dy;
|
|
84
|
-
finalModelViewTransform[2][3] =
|
|
85
|
-
finalModelViewTransform[2][3] -
|
|
86
|
-
finalModelViewTransform[2][0] * dx -
|
|
87
|
-
finalModelViewTransform[2][1] * dy;
|
|
88
|
-
return finalModelViewTransform;
|
|
89
|
-
};
|
|
90
|
-
// ICP iteration
|
|
91
|
-
// Question: can someone provide theoretical reference / mathematical proof for the following computations?
|
|
92
|
-
const _doICP = ({ initialModelViewTransform, projectionTransform, worldCoords, screenCoords, stabilities, inlierProb, }) => {
|
|
93
|
-
const isRobustMode = inlierProb < 1;
|
|
94
|
-
let modelViewTransform = initialModelViewTransform;
|
|
95
|
-
let err0 = 0.0;
|
|
96
|
-
let err1 = 0.0;
|
|
97
|
-
let E = new Array(worldCoords.length);
|
|
98
|
-
let E2 = new Array(worldCoords.length);
|
|
99
|
-
let dxs = new Array(worldCoords.length);
|
|
100
|
-
let dys = new Array(worldCoords.length);
|
|
101
|
-
for (let l = 0; l <= ICP_MAX_LOOP; l++) {
|
|
102
|
-
const modelViewProjectionTransform = buildModelViewProjectionTransform(projectionTransform, modelViewTransform);
|
|
103
|
-
for (let n = 0; n < worldCoords.length; n++) {
|
|
104
|
-
const u = computeScreenCoordiate(modelViewProjectionTransform, worldCoords[n].x, worldCoords[n].y, worldCoords[n].z);
|
|
105
|
-
const dx = screenCoords[n].x - u.x;
|
|
106
|
-
const dy = screenCoords[n].y - u.y;
|
|
107
|
-
dxs[n] = dx;
|
|
108
|
-
dys[n] = dy;
|
|
109
|
-
E[n] = dx * dx + dy * dy;
|
|
110
|
-
}
|
|
111
|
-
let K2; // robust mode only
|
|
112
|
-
err1 = 0.0;
|
|
113
|
-
if (isRobustMode) {
|
|
114
|
-
const inlierNum = Math.max(3, Math.floor(worldCoords.length * inlierProb) - 1);
|
|
115
|
-
for (let n = 0; n < worldCoords.length; n++) {
|
|
116
|
-
E2[n] = E[n];
|
|
117
|
-
}
|
|
118
|
-
E2.sort((a, b) => {
|
|
119
|
-
return a - b;
|
|
120
|
-
});
|
|
121
|
-
K2 = Math.max(E2[inlierNum] * K2_FACTOR, 16.0);
|
|
122
|
-
for (let n = 0; n < worldCoords.length; n++) {
|
|
123
|
-
if (E2[n] > K2)
|
|
124
|
-
err1 += K2 / 6;
|
|
125
|
-
else
|
|
126
|
-
err1 += (K2 / 6.0) * (1.0 - (1.0 - E2[n] / K2) * (1.0 - E2[n] / K2) * (1.0 - E2[n] / K2));
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
for (let n = 0; n < worldCoords.length; n++) {
|
|
131
|
-
err1 += E[n];
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
err1 /= worldCoords.length;
|
|
135
|
-
//console.log("icp loop", inlierProb, l, err1);
|
|
136
|
-
if (err1 < ICP_BREAK_LOOP_ERROR_THRESH)
|
|
137
|
-
break;
|
|
138
|
-
//if (l > 0 && err1 < ICP_BREAK_LOOP_ERROR_THRESH2 && err1/err0 > ICP_BREAK_LOOP_ERROR_RATIO_THRESH) break;
|
|
139
|
-
if (l > 0 && err1 / err0 > ICP_BREAK_LOOP_ERROR_RATIO_THRESH)
|
|
140
|
-
break;
|
|
141
|
-
if (l === ICP_MAX_LOOP)
|
|
142
|
-
break;
|
|
143
|
-
err0 = err1;
|
|
144
|
-
const dU = [];
|
|
145
|
-
const allJ_U_S = [];
|
|
146
|
-
for (let n = 0; n < worldCoords.length; n++) {
|
|
147
|
-
if (isRobustMode && E[n] > K2) {
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
const J_U_S = _getJ_U_S({
|
|
151
|
-
modelViewProjectionTransform,
|
|
152
|
-
modelViewTransform,
|
|
153
|
-
projectionTransform,
|
|
154
|
-
worldCoord: worldCoords[n],
|
|
155
|
-
});
|
|
156
|
-
if (isRobustMode) {
|
|
157
|
-
const robustW = (1.0 - E[n] / K2) * (1.0 - E[n] / K2);
|
|
158
|
-
// Log-weighted stability: suppresses vibrators aggressively but allows recovery
|
|
159
|
-
const s = stabilities ? stabilities[n] : 1.0;
|
|
160
|
-
const stabilityW = s * Math.log10(9 * s + 1);
|
|
161
|
-
const W = robustW * stabilityW;
|
|
162
|
-
for (let j = 0; j < 2; j++) {
|
|
163
|
-
for (let i = 0; i < 6; i++) {
|
|
164
|
-
J_U_S[j][i] *= W;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
dU.push([dxs[n] * W]);
|
|
168
|
-
dU.push([dys[n] * W]);
|
|
169
|
-
}
|
|
170
|
-
else {
|
|
171
|
-
const s = stabilities ? stabilities[n] : 1.0;
|
|
172
|
-
const W = s * Math.log10(9 * s + 1);
|
|
173
|
-
for (let j = 0; j < 2; j++) {
|
|
174
|
-
for (let i = 0; i < 6; i++) {
|
|
175
|
-
J_U_S[j][i] *= W;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
dU.push([dxs[n] * W]);
|
|
179
|
-
dU.push([dys[n] * W]);
|
|
180
|
-
}
|
|
181
|
-
for (let i = 0; i < J_U_S.length; i++) {
|
|
182
|
-
allJ_U_S.push(J_U_S[i]);
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
const dS = _getDeltaS({ dU, J_U_S: allJ_U_S });
|
|
186
|
-
if (dS === null)
|
|
187
|
-
break;
|
|
188
|
-
modelViewTransform = _updateModelViewTransform({ modelViewTransform, dS });
|
|
189
|
-
}
|
|
190
|
-
return { modelViewTransform, err: err1 };
|
|
191
|
-
};
|
|
192
|
-
const _updateModelViewTransform = ({ modelViewTransform, dS }) => {
|
|
193
|
-
/**
|
|
194
|
-
* dS has 6 paragrams, first half is rotation, second half is translation
|
|
195
|
-
* rotation is expressed in angle-axis,
|
|
196
|
-
* [S[0], S[1] ,S[2]] is the axis of rotation, and the magnitude is the angle
|
|
197
|
-
*/
|
|
198
|
-
let ra = dS[0] * dS[0] + dS[1] * dS[1] + dS[2] * dS[2];
|
|
199
|
-
let q0, q1, q2;
|
|
200
|
-
if (ra < 0.000001) {
|
|
201
|
-
q0 = 1.0;
|
|
202
|
-
q1 = 0.0;
|
|
203
|
-
q2 = 0.0;
|
|
204
|
-
ra = 0.0;
|
|
205
|
-
}
|
|
206
|
-
else {
|
|
207
|
-
ra = Math.sqrt(ra);
|
|
208
|
-
q0 = dS[0] / ra;
|
|
209
|
-
q1 = dS[1] / ra;
|
|
210
|
-
q2 = dS[2] / ra;
|
|
211
|
-
}
|
|
212
|
-
const cra = Math.cos(ra);
|
|
213
|
-
const sra = Math.sin(ra);
|
|
214
|
-
const one_cra = 1.0 - cra;
|
|
215
|
-
// mat is [R|t], 3D rotation and translation
|
|
216
|
-
mat[0][0] = q0 * q0 * one_cra + cra;
|
|
217
|
-
mat[0][1] = q0 * q1 * one_cra - q2 * sra;
|
|
218
|
-
mat[0][2] = q0 * q2 * one_cra + q1 * sra;
|
|
219
|
-
mat[0][3] = dS[3];
|
|
220
|
-
mat[1][0] = q1 * q0 * one_cra + q2 * sra;
|
|
221
|
-
mat[1][1] = q1 * q1 * one_cra + cra;
|
|
222
|
-
mat[1][2] = q1 * q2 * one_cra - q0 * sra;
|
|
223
|
-
mat[1][3] = dS[4];
|
|
224
|
-
mat[2][0] = q2 * q0 * one_cra - q1 * sra;
|
|
225
|
-
mat[2][1] = q2 * q1 * one_cra + q0 * sra;
|
|
226
|
-
mat[2][2] = q2 * q2 * one_cra + cra;
|
|
227
|
-
mat[2][3] = dS[5];
|
|
228
|
-
// the updated transform is the original transform x delta transform
|
|
229
|
-
const mat2 = [[], [], []];
|
|
230
|
-
for (let j = 0; j < 3; j++) {
|
|
231
|
-
for (let i = 0; i < 4; i++) {
|
|
232
|
-
mat2[j][i] =
|
|
233
|
-
modelViewTransform[j][0] * mat[0][i] +
|
|
234
|
-
modelViewTransform[j][1] * mat[1][i] +
|
|
235
|
-
modelViewTransform[j][2] * mat[2][i];
|
|
236
|
-
}
|
|
237
|
-
mat2[j][3] += modelViewTransform[j][3];
|
|
238
|
-
}
|
|
239
|
-
return mat2;
|
|
240
|
-
};
|
|
241
|
-
const _getDeltaS = ({ dU, J_U_S }) => {
|
|
242
|
-
const J = new Matrix(J_U_S);
|
|
243
|
-
const U = new Matrix(dU);
|
|
244
|
-
const JT = J.transpose();
|
|
245
|
-
const JTJ = JT.mmul(J);
|
|
246
|
-
const JTU = JT.mmul(U);
|
|
247
|
-
let JTJInv;
|
|
248
|
-
try {
|
|
249
|
-
JTJInv = inverse(JTJ);
|
|
250
|
-
}
|
|
251
|
-
catch (e) {
|
|
252
|
-
return null;
|
|
253
|
-
}
|
|
254
|
-
const S = JTJInv.mmul(JTU);
|
|
255
|
-
return S.to1DArray();
|
|
256
|
-
};
|
|
257
|
-
const _getJ_U_S = ({ modelViewProjectionTransform, modelViewTransform, projectionTransform, worldCoord, }) => {
|
|
258
|
-
const T = modelViewTransform;
|
|
259
|
-
const { x, y, z } = worldCoord;
|
|
260
|
-
const u = applyModelViewProjectionTransform(modelViewProjectionTransform, x, y, z);
|
|
261
|
-
const z2 = u.z * u.z;
|
|
262
|
-
// Question: This is the most confusing matrix to me. I've no idea how to derive this.
|
|
263
|
-
//J_U_Xc[0][0] = (projectionTransform[0][0] * u.z - projectionTransform[2][0] * u.x) / z2;
|
|
264
|
-
//J_U_Xc[0][1] = (projectionTransform[0][1] * u.z - projectionTransform[2][1] * u.x) / z2;
|
|
265
|
-
//J_U_Xc[0][2] = (projectionTransform[0][2] * u.z - projectionTransform[2][2] * u.x) / z2;
|
|
266
|
-
//J_U_Xc[1][0] = (projectionTransform[1][0] * u.z - projectionTransform[2][0] * u.y) / z2;
|
|
267
|
-
//J_U_Xc[1][1] = (projectionTransform[1][1] * u.z - projectionTransform[2][1] * u.y) / z2;
|
|
268
|
-
//J_U_Xc[1][2] = (projectionTransform[1][2] * u.z - projectionTransform[2][2] * u.y) / z2;
|
|
269
|
-
// The above is the original implementation, but simplify to below becuase projetionTransform[2][0] and [2][1] are zero
|
|
270
|
-
J_U_Xc[0][0] = (projectionTransform[0][0] * u.z) / z2;
|
|
271
|
-
J_U_Xc[0][1] = (projectionTransform[0][1] * u.z) / z2;
|
|
272
|
-
J_U_Xc[0][2] = (projectionTransform[0][2] * u.z - projectionTransform[2][2] * u.x) / z2;
|
|
273
|
-
J_U_Xc[1][0] = (projectionTransform[1][0] * u.z) / z2;
|
|
274
|
-
J_U_Xc[1][1] = (projectionTransform[1][1] * u.z) / z2;
|
|
275
|
-
J_U_Xc[1][2] = (projectionTransform[1][2] * u.z - projectionTransform[2][2] * u.y) / z2;
|
|
276
|
-
/*
|
|
277
|
-
J_Xc_S should be like this, but z is zero, so we can simplify
|
|
278
|
-
[T[0][2] * y - T[0][1] * z, T[0][0] * z - T[0][2] * x, T[0][1] * x - T[0][0] * y, T[0][0], T[0][1], T[0][2]],
|
|
279
|
-
[T[1][2] * y - T[1][1] * z, T[1][0] * z - T[1][2] * x, T[1][1] * x - T[1][0] * y, T[1][0], T[1][1], T[1][2]],
|
|
280
|
-
[T[2][2] * y - T[2][1] * z, T[2][0] * z - T[2][2] * x, T[2][1] * x - T[2][0] * y, T[2][0], T[2][1], T[2][2]],
|
|
281
|
-
*/
|
|
282
|
-
J_Xc_S[0][0] = T[0][2] * y;
|
|
283
|
-
J_Xc_S[0][1] = -T[0][2] * x;
|
|
284
|
-
J_Xc_S[0][2] = T[0][1] * x - T[0][0] * y;
|
|
285
|
-
J_Xc_S[0][3] = T[0][0];
|
|
286
|
-
J_Xc_S[0][4] = T[0][1];
|
|
287
|
-
J_Xc_S[0][5] = T[0][2];
|
|
288
|
-
J_Xc_S[1][0] = T[1][2] * y;
|
|
289
|
-
J_Xc_S[1][1] = -T[1][2] * x;
|
|
290
|
-
J_Xc_S[1][2] = T[1][1] * x - T[1][0] * y;
|
|
291
|
-
J_Xc_S[1][3] = T[1][0];
|
|
292
|
-
J_Xc_S[1][4] = T[1][1];
|
|
293
|
-
J_Xc_S[1][5] = T[1][2];
|
|
294
|
-
J_Xc_S[2][0] = T[2][2] * y;
|
|
295
|
-
J_Xc_S[2][1] = -T[2][2] * x;
|
|
296
|
-
J_Xc_S[2][2] = T[2][1] * x - T[2][0] * y;
|
|
297
|
-
J_Xc_S[2][3] = T[2][0];
|
|
298
|
-
J_Xc_S[2][4] = T[2][1];
|
|
299
|
-
J_Xc_S[2][5] = T[2][2];
|
|
300
|
-
const J_U_S = [[], []];
|
|
301
|
-
for (let j = 0; j < 2; j++) {
|
|
302
|
-
for (let i = 0; i < 6; i++) {
|
|
303
|
-
J_U_S[j][i] = 0.0;
|
|
304
|
-
for (let k = 0; k < 3; k++) {
|
|
305
|
-
J_U_S[j][i] += J_U_Xc[j][k] * J_Xc_S[k][i];
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
return J_U_S;
|
|
310
|
-
};
|
|
311
|
-
export { refineEstimate };
|
|
1
|
+
import{Matrix as r,inverse as o}from"ml-matrix";import{applyModelViewProjectionTransform as e,buildModelViewProjectionTransform as t,computeScreenCoordiate as l}from"./utils.js";let n=[[],[],[]],s=[[],[]],i=[[],[],[]];const f=({initialModelViewTransform:r,projectionTransform:o,worldCoords:e,screenCoords:t,stabilities:l})=>{let n=0,s=0;for(let r=0;r<e.length;r++)n+=e[r].x,s+=e[r].y;n/=e.length,s/=e.length;const i=[];for(let r=0;r<e.length;r++)i.push({x:e[r].x-n,y:e[r].y-s,z:e[r].z});const f=[[],[],[]];for(let o=0;o<3;o++)for(let e=0;e<3;e++)f[o][e]=r[o][e];f[0][3]=r[0][0]*n+r[0][1]*s+r[0][3],f[1][3]=r[1][0]*n+r[1][1]*s+r[1][3],f[2][3]=r[2][0]*n+r[2][1]*s+r[2][3];const m=[1,.8,.6,.4,0];let c=f,h=null;for(let r=0;r<m.length;r++){const e=a({initialModelViewTransform:c,projectionTransform:o,worldCoords:i,screenCoords:t,stabilities:l,inlierProb:m[r]});if(c=e.modelViewTransform,e.err<5){h=c;break}}return null===h?null:(h[0][3]=h[0][3]-h[0][0]*n-h[0][1]*s,h[1][3]=h[1][3]-h[1][0]*n-h[1][1]*s,h[2][3]=h[2][3]-h[2][0]*n-h[2][1]*s,h)},a=({initialModelViewTransform:r,projectionTransform:o,worldCoords:e,screenCoords:n,stabilities:s,inlierProb:i})=>{const f=i<1;let a=r,d=0,u=0,w=new Array(e.length),g=new Array(e.length),T=new Array(e.length),p=new Array(e.length);for(let r=0;r<=10;r++){const y=t(o,a);for(let r=0;r<e.length;r++){const o=l(y,e[r].x,e[r].y,e[r].z),t=n[r].x-o.x,s=n[r].y-o.y;T[r]=t,p[r]=s,w[r]=t*t+s*s}let x;if(u=0,f){const r=Math.max(3,Math.floor(e.length*i)-1);for(let r=0;r<e.length;r++)g[r]=w[r];g.sort((r,o)=>r-o),x=Math.max(4*g[r],16);for(let r=0;r<e.length;r++)g[r]>x?u+=x/6:u+=x/6*(1-(1-g[r]/x)*(1-g[r]/x)*(1-g[r]/x))}else for(let r=0;r<e.length;r++)u+=w[r];if(u/=e.length,u<.1)break;if(r>0&&u/d>.99)break;if(10===r)break;d=u;const z=[],M=[];for(let r=0;r<e.length;r++){if(f&&w[r]>x)continue;const t=h({modelViewProjectionTransform:y,modelViewTransform:a,projectionTransform:o,worldCoord:e[r]});if(f){const o=(1-w[r]/x)*(1-w[r]/x),e=s?s[r]:1,l=o*(e*Math.log10(9*e+1));for(let r=0;r<2;r++)for(let o=0;o<6;o++)t[r][o]*=l;z.push([T[r]*l]),z.push([p[r]*l])}else{const o=s?s[r]:1,e=o*Math.log10(9*o+1);for(let r=0;r<2;r++)for(let o=0;o<6;o++)t[r][o]*=e;z.push([T[r]*e]),z.push([p[r]*e])}for(let r=0;r<t.length;r++)M.push(t[r])}const V=c({dU:z,J_U_S:M});if(null===V)break;a=m({modelViewTransform:a,dS:V})}return{modelViewTransform:a,err:u}},m=({modelViewTransform:r,dS:o})=>{let e,t,l,s=o[0]*o[0]+o[1]*o[1]+o[2]*o[2];s<1e-6?(e=1,t=0,l=0,s=0):(s=Math.sqrt(s),e=o[0]/s,t=o[1]/s,l=o[2]/s);const i=Math.cos(s),f=Math.sin(s),a=1-i;n[0][0]=e*e*a+i,n[0][1]=e*t*a-l*f,n[0][2]=e*l*a+t*f,n[0][3]=o[3],n[1][0]=t*e*a+l*f,n[1][1]=t*t*a+i,n[1][2]=t*l*a-e*f,n[1][3]=o[4],n[2][0]=l*e*a-t*f,n[2][1]=l*t*a+e*f,n[2][2]=l*l*a+i,n[2][3]=o[5];const m=[[],[],[]];for(let o=0;o<3;o++){for(let e=0;e<4;e++)m[o][e]=r[o][0]*n[0][e]+r[o][1]*n[1][e]+r[o][2]*n[2][e];m[o][3]+=r[o][3]}return m},c=({dU:e,J_U_S:t})=>{const l=new r(t),n=new r(e),s=l.transpose(),i=s.mmul(l),f=s.mmul(n);let a;try{a=o(i)}catch(r){return null}return a.mmul(f).to1DArray()},h=({modelViewProjectionTransform:r,modelViewTransform:o,projectionTransform:t,worldCoord:l})=>{const n=o,{x:f,y:a,z:m}=l,c=e(r,f,a,m),h=c.z*c.z;s[0][0]=t[0][0]*c.z/h,s[0][1]=t[0][1]*c.z/h,s[0][2]=(t[0][2]*c.z-t[2][2]*c.x)/h,s[1][0]=t[1][0]*c.z/h,s[1][1]=t[1][1]*c.z/h,s[1][2]=(t[1][2]*c.z-t[2][2]*c.y)/h,i[0][0]=n[0][2]*a,i[0][1]=-n[0][2]*f,i[0][2]=n[0][1]*f-n[0][0]*a,i[0][3]=n[0][0],i[0][4]=n[0][1],i[0][5]=n[0][2],i[1][0]=n[1][2]*a,i[1][1]=-n[1][2]*f,i[1][2]=n[1][1]*f-n[1][0]*a,i[1][3]=n[1][0],i[1][4]=n[1][1],i[1][5]=n[1][2],i[2][0]=n[2][2]*a,i[2][1]=-n[2][2]*f,i[2][2]=n[2][1]*f-n[2][0]*a,i[2][3]=n[2][0],i[2][4]=n[2][1],i[2][5]=n[2][2];const d=[[],[]];for(let r=0;r<2;r++)for(let o=0;o<6;o++){d[r][o]=0;for(let e=0;e<3;e++)d[r][o]+=s[r][e]*i[e][o]}return d};export{f as refineEstimate};
|