@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,130 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { createRandomizer } from "../utils/randomizer.js";
|
|
3
|
-
const MIN_FEATURE_PER_NODE = 32;
|
|
4
|
-
const NUM_ASSIGNMENT_HYPOTHESES = 12;
|
|
5
|
-
const NUM_CENTERS = 8;
|
|
6
|
-
export function popcount32(n) {
|
|
7
|
-
n = n - ((n >> 1) & 0x55555555);
|
|
8
|
-
n = (n & 0x33333333) + ((n >> 2) & 0x33333333);
|
|
9
|
-
return (((n + (n >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
|
|
10
|
-
}
|
|
11
|
-
const _computeKMedoids = (options) => {
|
|
12
|
-
const { descriptors, pointIndexes, randomizer, useHDC } = options;
|
|
13
|
-
const numPointIndexes = pointIndexes.length;
|
|
14
|
-
const randomPointIndexes = new Int32Array(numPointIndexes);
|
|
15
|
-
for (let i = 0; i < numPointIndexes; i++) {
|
|
16
|
-
randomPointIndexes[i] = i;
|
|
17
|
-
}
|
|
18
|
-
let bestSumD = Number.MAX_SAFE_INTEGER;
|
|
19
|
-
let bestAssignment = null;
|
|
20
|
-
const centerPointIndices = new Int32Array(NUM_CENTERS);
|
|
21
|
-
for (let i = 0; i < NUM_ASSIGNMENT_HYPOTHESES; i++) {
|
|
22
|
-
randomizer.arrayShuffle({ arr: randomPointIndexes, sampleSize: NUM_CENTERS });
|
|
23
|
-
for (let k = 0; k < NUM_CENTERS; k++) {
|
|
24
|
-
centerPointIndices[k] = pointIndexes[randomPointIndexes[k]];
|
|
25
|
-
}
|
|
26
|
-
let sumD = 0;
|
|
27
|
-
const currentAssignment = new Int32Array(numPointIndexes);
|
|
28
|
-
for (let j = 0; j < numPointIndexes; j++) {
|
|
29
|
-
const pIdx = pointIndexes[j];
|
|
30
|
-
let bestD = 255;
|
|
31
|
-
let bestCenterIdx = -1;
|
|
32
|
-
for (let k = 0; k < NUM_CENTERS; k++) {
|
|
33
|
-
const cIdx = centerPointIndices[k];
|
|
34
|
-
let d;
|
|
35
|
-
if (useHDC) {
|
|
36
|
-
d = popcount32(descriptors[pIdx] ^ descriptors[cIdx]);
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
d = hammingCompute64(descriptors, pIdx * 2, descriptors, cIdx * 2);
|
|
40
|
-
}
|
|
41
|
-
if (d < bestD) {
|
|
42
|
-
bestCenterIdx = randomPointIndexes[k];
|
|
43
|
-
bestD = d;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
currentAssignment[j] = bestCenterIdx;
|
|
47
|
-
sumD += bestD;
|
|
48
|
-
}
|
|
49
|
-
if (sumD < bestSumD) {
|
|
50
|
-
bestSumD = sumD;
|
|
51
|
-
bestAssignment = currentAssignment;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return bestAssignment;
|
|
55
|
-
};
|
|
56
|
-
const build = ({ points }) => {
|
|
57
|
-
const numPoints = points.length;
|
|
58
|
-
if (numPoints === 0)
|
|
59
|
-
return { rootNode: { leaf: true, pointIndexes: [], centerPointIndex: null } };
|
|
60
|
-
const useHDC = points[0] && points[0].hdcSignature !== undefined;
|
|
61
|
-
const descriptors = new Uint32Array(useHDC ? numPoints : numPoints * 2);
|
|
62
|
-
for (let i = 0; i < numPoints; i++) {
|
|
63
|
-
if (useHDC) {
|
|
64
|
-
descriptors[i] = points[i].hdcSignature;
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
const d = points[i].descriptors;
|
|
68
|
-
descriptors[i * 2] = d[0];
|
|
69
|
-
descriptors[i * 2 + 1] = d[1];
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
const pointIndexes = new Int32Array(numPoints);
|
|
73
|
-
for (let i = 0; i < numPoints; i++) {
|
|
74
|
-
pointIndexes[i] = i;
|
|
75
|
-
}
|
|
76
|
-
const randomizer = createRandomizer();
|
|
77
|
-
const rootNode = _build({
|
|
78
|
-
descriptors,
|
|
79
|
-
pointIndexes,
|
|
80
|
-
centerPointIndex: null,
|
|
81
|
-
randomizer,
|
|
82
|
-
useHDC
|
|
83
|
-
});
|
|
84
|
-
return { rootNode };
|
|
85
|
-
};
|
|
86
|
-
const _build = (options) => {
|
|
87
|
-
const { descriptors, pointIndexes, centerPointIndex, randomizer, useHDC } = options;
|
|
88
|
-
const numPoints = pointIndexes.length;
|
|
89
|
-
let isLeaf = false;
|
|
90
|
-
if (numPoints <= NUM_CENTERS || numPoints <= MIN_FEATURE_PER_NODE) {
|
|
91
|
-
isLeaf = true;
|
|
92
|
-
}
|
|
93
|
-
const clusters = new Map();
|
|
94
|
-
if (!isLeaf) {
|
|
95
|
-
const assignment = _computeKMedoids({ descriptors, pointIndexes, randomizer, useHDC });
|
|
96
|
-
for (let i = 0; i < assignment.length; i++) {
|
|
97
|
-
const centerIdx = pointIndexes[assignment[i]];
|
|
98
|
-
let cluster = clusters.get(centerIdx);
|
|
99
|
-
if (cluster === undefined) {
|
|
100
|
-
cluster = [];
|
|
101
|
-
clusters.set(centerIdx, cluster);
|
|
102
|
-
}
|
|
103
|
-
cluster.push(pointIndexes[i]);
|
|
104
|
-
}
|
|
105
|
-
if (clusters.size === 1) {
|
|
106
|
-
isLeaf = true;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
const node = {
|
|
110
|
-
centerPointIndex: centerPointIndex,
|
|
111
|
-
};
|
|
112
|
-
if (isLeaf) {
|
|
113
|
-
node.leaf = true;
|
|
114
|
-
node.pointIndexes = new Int32Array(pointIndexes);
|
|
115
|
-
return node;
|
|
116
|
-
}
|
|
117
|
-
node.leaf = false;
|
|
118
|
-
node.children = [];
|
|
119
|
-
for (const [cIdx, clusterPoints] of clusters) {
|
|
120
|
-
node.children.push(_build({
|
|
121
|
-
descriptors,
|
|
122
|
-
pointIndexes: new Int32Array(clusterPoints),
|
|
123
|
-
centerPointIndex: cIdx,
|
|
124
|
-
randomizer,
|
|
125
|
-
useHDC
|
|
126
|
-
}));
|
|
127
|
-
}
|
|
128
|
-
return node;
|
|
129
|
-
};
|
|
130
|
-
export { build };
|
|
1
|
+
import{compute64 as e}from"./hamming-distance.js";import{createRandomizer as n}from"../utils/randomizer.js";export function popcount32(e){return 16843009*((e=(858993459&(e-=e>>1&1431655765))+(e>>2&858993459))+(e>>4)&252645135)>>24}const t=({points:e})=>{const t=e.length;if(0===t)return{rootNode:{leaf:!0,pointIndexes:[],centerPointIndex:null}};const o=e[0]&&void 0!==e[0].hdcSignature,s=new Uint32Array(o?t:2*t);for(let n=0;n<t;n++)if(o)s[n]=e[n].hdcSignature;else{const t=e[n].descriptors;s[2*n]=t[0],s[2*n+1]=t[1]}const i=new Int32Array(t);for(let e=0;e<t;e++)i[e]=e;const c=n();return{rootNode:r({descriptors:s,pointIndexes:i,centerPointIndex:null,randomizer:c,useHDC:o})}},r=n=>{const{descriptors:t,pointIndexes:o,centerPointIndex:s,randomizer:i,useHDC:c}=n,d=o.length;let l=!1;(d<=8||d<=32)&&(l=!0);const p=new Map;if(!l){const n=(n=>{const{descriptors:t,pointIndexes:r,randomizer:o,useHDC:s}=n,i=r.length,c=new Int32Array(i);for(let e=0;e<i;e++)c[e]=e;let d=Number.MAX_SAFE_INTEGER,l=null;const p=new Int32Array(8);for(let n=0;n<12;n++){o.arrayShuffle({arr:c,sampleSize:8});for(let e=0;e<8;e++)p[e]=r[c[e]];let n=0;const a=new Int32Array(i);for(let o=0;o<i;o++){const i=r[o];let d=255,l=-1;for(let n=0;n<8;n++){const r=p[n];let o;o=s?popcount32(t[i]^t[r]):e(t,2*i,t,2*r),o<d&&(l=c[n],d=o)}a[o]=l,n+=d}n<d&&(d=n,l=a)}return l})({descriptors:t,pointIndexes:o,randomizer:i,useHDC:c});for(let e=0;e<n.length;e++){const t=o[n[e]];let r=p.get(t);void 0===r&&(r=[],p.set(t,r)),r.push(o[e])}1===p.size&&(l=!0)}const a={centerPointIndex:s};if(l)return a.leaf=!0,a.pointIndexes=new Int32Array(o),a;a.leaf=!1,a.children=[];for(const[e,n]of p)a.children.push(r({descriptors:t,pointIndexes:new Int32Array(n),centerPointIndex:e,randomizer:i,useHDC:c}));return a};export{t as build};
|
|
@@ -1,170 +1 @@
|
|
|
1
|
-
const
|
|
2
|
-
// mathces [querypointIndex:x, keypointIndex: x]
|
|
3
|
-
const computeHoughMatches = (options) => {
|
|
4
|
-
const { keywidth, keyheight, querywidth, queryheight, matches } = options;
|
|
5
|
-
const maxX = querywidth * 1.2;
|
|
6
|
-
const minX = -maxX;
|
|
7
|
-
const maxY = queryheight * 1.2;
|
|
8
|
-
const minY = -maxY;
|
|
9
|
-
const numAngleBins = 12;
|
|
10
|
-
const numScaleBins = 12; // 🚀 Increased bins
|
|
11
|
-
const minScale = -2; // 📐 Support 1% scale (10^-2)
|
|
12
|
-
const maxScale = 1; // 📐 Support 1000% scale (10^1)
|
|
13
|
-
const scaleK = 10.0;
|
|
14
|
-
const scaleOneOverLogK = 1.0 / Math.log(scaleK);
|
|
15
|
-
const maxDim = Math.max(keywidth, keyheight);
|
|
16
|
-
const keycenterX = Math.floor(keywidth / 2);
|
|
17
|
-
const keycenterY = Math.floor(keyheight / 2);
|
|
18
|
-
// compute numXBins and numYBins based on matches
|
|
19
|
-
const projectedDims = [];
|
|
20
|
-
for (let i = 0; i < matches.length; i++) {
|
|
21
|
-
const queryscale = matches[i].querypoint.scale;
|
|
22
|
-
const keyscale = matches[i].keypoint.scale;
|
|
23
|
-
if (keyscale == 0)
|
|
24
|
-
console.log("ERROR divide zero");
|
|
25
|
-
const scale = queryscale / keyscale;
|
|
26
|
-
projectedDims.push(scale * maxDim);
|
|
27
|
-
}
|
|
28
|
-
// TODO optimize median
|
|
29
|
-
// weird. median should be [Math.floor(projectedDims.length/2) - 1] ?
|
|
30
|
-
projectedDims.sort((a1, a2) => {
|
|
31
|
-
return a1 - a2;
|
|
32
|
-
});
|
|
33
|
-
const medianProjectedDim = projectedDims[Math.floor(projectedDims.length / 2) - (projectedDims.length % 2 == 0 ? 1 : 0) - 1];
|
|
34
|
-
const binSize = Math.max(20, 0.25 * medianProjectedDim); // 🚀 Ensure bins aren't too small for noise
|
|
35
|
-
const numXBins = Math.max(5, Math.min(40, Math.ceil((maxX - minX) / binSize))); // 🎯 Cap bins to keep voting dense
|
|
36
|
-
const numYBins = Math.max(5, Math.min(40, Math.ceil((maxY - minY) / binSize)));
|
|
37
|
-
const numXYBins = numXBins * numYBins;
|
|
38
|
-
const numXYAngleBins = numXYBins * numAngleBins;
|
|
39
|
-
// do voting
|
|
40
|
-
const querypointValids = [];
|
|
41
|
-
const querypointBinLocations = [];
|
|
42
|
-
const votes = {};
|
|
43
|
-
for (let i = 0; i < matches.length; i++) {
|
|
44
|
-
const querypoint = matches[i].querypoint;
|
|
45
|
-
const keypoint = matches[i].keypoint;
|
|
46
|
-
const { x, y, scale, angle } = _mapCorrespondence({
|
|
47
|
-
querypoint,
|
|
48
|
-
keypoint,
|
|
49
|
-
keycenterX,
|
|
50
|
-
keycenterY,
|
|
51
|
-
scaleOneOverLogK,
|
|
52
|
-
});
|
|
53
|
-
// Check that the vote is within range
|
|
54
|
-
if (x < minX ||
|
|
55
|
-
x >= maxX ||
|
|
56
|
-
y < minY ||
|
|
57
|
-
y >= maxY ||
|
|
58
|
-
angle <= -Math.PI ||
|
|
59
|
-
angle > Math.PI ||
|
|
60
|
-
scale < minScale ||
|
|
61
|
-
scale >= maxScale) {
|
|
62
|
-
querypointValids[i] = false;
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
// map properties to bins
|
|
66
|
-
let fbinX = (numXBins * (x - minX)) / (maxX - minX);
|
|
67
|
-
let fbinY = (numYBins * (y - minY)) / (maxY - minY);
|
|
68
|
-
let fbinAngle = (numAngleBins * (angle + Math.PI)) / (2.0 * Math.PI);
|
|
69
|
-
let fbinScale = (numScaleBins * (scale - minScale)) / (maxScale - minScale);
|
|
70
|
-
querypointBinLocations[i] = {
|
|
71
|
-
binX: fbinX,
|
|
72
|
-
binY: fbinY,
|
|
73
|
-
binAngle: fbinAngle,
|
|
74
|
-
binScale: fbinScale,
|
|
75
|
-
};
|
|
76
|
-
let binX = Math.floor(fbinX - 0.5);
|
|
77
|
-
let binY = Math.floor(fbinY - 0.5);
|
|
78
|
-
let binScale = Math.floor(fbinScale - 0.5);
|
|
79
|
-
let binAngle = (Math.floor(fbinAngle - 0.5) + numAngleBins) % numAngleBins;
|
|
80
|
-
// check can vote all 16 bins
|
|
81
|
-
if (binX < 0 ||
|
|
82
|
-
binX + 1 >= numXBins ||
|
|
83
|
-
binY < 0 ||
|
|
84
|
-
binY + 1 >= numYBins ||
|
|
85
|
-
binScale < 0 ||
|
|
86
|
-
binScale + 1 >= numScaleBins) {
|
|
87
|
-
querypointValids[i] = false;
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
for (let dx = 0; dx < 2; dx++) {
|
|
91
|
-
let binX2 = binX + dx;
|
|
92
|
-
for (let dy = 0; dy < 2; dy++) {
|
|
93
|
-
let binY2 = binY + dy;
|
|
94
|
-
for (let dangle = 0; dangle < 2; dangle++) {
|
|
95
|
-
let binAngle2 = (binAngle + dangle) % numAngleBins;
|
|
96
|
-
for (let dscale = 0; dscale < 2; dscale++) {
|
|
97
|
-
let binScale2 = binScale + dscale;
|
|
98
|
-
const binIndex = binX2 + binY2 * numXBins + binAngle2 * numXYBins + binScale2 * numXYAngleBins;
|
|
99
|
-
if (votes[binIndex] === undefined)
|
|
100
|
-
votes[binIndex] = 0;
|
|
101
|
-
votes[binIndex] += 1;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
querypointValids[i] = true;
|
|
107
|
-
}
|
|
108
|
-
let maxVotes = 0;
|
|
109
|
-
let maxVoteIndex = -1;
|
|
110
|
-
Object.keys(votes).forEach((index) => {
|
|
111
|
-
if (votes[index] > maxVotes) {
|
|
112
|
-
maxVotes = votes[index];
|
|
113
|
-
maxVoteIndex = index;
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
if (maxVotes < 3)
|
|
117
|
-
return [];
|
|
118
|
-
// get back bins from vote index
|
|
119
|
-
const binX = Math.floor(((maxVoteIndex % numXYAngleBins) % numXYBins) % numXBins);
|
|
120
|
-
const binY = Math.floor((((maxVoteIndex - binX) % numXYAngleBins) % numXYBins) / numXBins);
|
|
121
|
-
const binAngle = Math.floor(((maxVoteIndex - binX - binY * numXBins) % numXYAngleBins) / numXYBins);
|
|
122
|
-
const binScale = Math.floor((maxVoteIndex - binX - binY * numXBins - binAngle * numXYBins) / numXYAngleBins);
|
|
123
|
-
// console.log(`[Hough] Peak votes: ${maxVotes} out of ${matches.length} matches.`);
|
|
124
|
-
const houghMatches = [];
|
|
125
|
-
const relaxedDelta = 2.0; // 🚀 Increased for better cluster robustness
|
|
126
|
-
for (let i = 0; i < matches.length; i++) {
|
|
127
|
-
if (!querypointValids[i])
|
|
128
|
-
continue;
|
|
129
|
-
const queryBins = querypointBinLocations[i];
|
|
130
|
-
// compute bin difference
|
|
131
|
-
const distBinX = Math.abs(queryBins.binX - (binX + 0.5));
|
|
132
|
-
if (distBinX >= relaxedDelta)
|
|
133
|
-
continue;
|
|
134
|
-
const distBinY = Math.abs(queryBins.binY - (binY + 0.5));
|
|
135
|
-
if (distBinY >= relaxedDelta)
|
|
136
|
-
continue;
|
|
137
|
-
const distBinScale = Math.abs(queryBins.binScale - (binScale + 0.5));
|
|
138
|
-
if (distBinScale >= relaxedDelta)
|
|
139
|
-
continue;
|
|
140
|
-
const temp = Math.abs(queryBins.binAngle - (binAngle + 0.5));
|
|
141
|
-
const distBinAngle = Math.min(temp, numAngleBins - temp);
|
|
142
|
-
if (distBinAngle >= relaxedDelta)
|
|
143
|
-
continue;
|
|
144
|
-
houghMatches.push(matches[i]);
|
|
145
|
-
}
|
|
146
|
-
return houghMatches;
|
|
147
|
-
};
|
|
148
|
-
const _mapCorrespondence = ({ querypoint, keypoint, keycenterX, keycenterY, scaleOneOverLogK }) => {
|
|
149
|
-
// map angle to (-pi, pi]
|
|
150
|
-
let angle = querypoint.angle - keypoint.angle;
|
|
151
|
-
if (angle <= -Math.PI)
|
|
152
|
-
angle += 2 * Math.PI;
|
|
153
|
-
else if (angle > Math.PI)
|
|
154
|
-
angle -= 2 * Math.PI;
|
|
155
|
-
const scale = querypoint.scale / keypoint.scale;
|
|
156
|
-
// 2x2 similarity
|
|
157
|
-
const cos = scale * Math.cos(angle);
|
|
158
|
-
const sin = scale * Math.sin(angle);
|
|
159
|
-
const S = [cos, -sin, sin, cos];
|
|
160
|
-
const tp = [S[0] * keypoint.x + S[1] * keypoint.y, S[2] * keypoint.x + S[3] * keypoint.y];
|
|
161
|
-
const tx = querypoint.x - tp[0];
|
|
162
|
-
const ty = querypoint.y - tp[1];
|
|
163
|
-
return {
|
|
164
|
-
x: S[0] * keycenterX + S[1] * keycenterY + tx,
|
|
165
|
-
y: S[2] * keycenterX + S[3] * keycenterY + ty,
|
|
166
|
-
angle: angle,
|
|
167
|
-
scale: Math.log(scale) * scaleOneOverLogK,
|
|
168
|
-
};
|
|
169
|
-
};
|
|
170
|
-
export { computeHoughMatches };
|
|
1
|
+
const t=t=>{const{keywidth:o,keyheight:n,querywidth:a,queryheight:l,matches:h}=t,i=1.2*a,r=-i,c=1.2*l,M=-c,s=12,f=1/Math.log(10),y=Math.max(o,n),g=Math.floor(o/2),u=Math.floor(n/2),b=[];for(let t=0;t<h.length;t++){const e=h[t].querypoint.scale,o=h[t].keypoint.scale;0==o&&console.log("ERROR divide zero");const n=e/o;b.push(n*y)}b.sort((t,e)=>t-e);const k=b[Math.floor(b.length/2)-(b.length%2==0?1:0)-1],p=Math.max(20,.25*k),x=Math.max(5,Math.min(40,Math.ceil((i-r)/p))),m=Math.max(5,Math.min(40,Math.ceil((c-M)/p))),I=x*m,P=I*s,q=[],O=[],d={};for(let t=0;t<h.length;t++){const o=h[t].querypoint,n=h[t].keypoint,{x:a,y:l,scale:y,angle:b}=e({querypoint:o,keypoint:n,keycenterX:g,keycenterY:u,scaleOneOverLogK:f});if(a<r||a>=i||l<M||l>=c||b<=-Math.PI||b>Math.PI||y<-2||y>=1){q[t]=!1;continue}let k=x*(a-r)/(i-r),p=m*(l-M)/(c-M),v=s*(b+Math.PI)/(2*Math.PI),X=12*(y- -2)/3;O[t]={binX:k,binY:p,binAngle:v,binScale:X};let Y=Math.floor(k-.5),R=Math.floor(p-.5),w=Math.floor(X-.5),A=(Math.floor(v-.5)+s)%s;if(Y<0||Y+1>=x||R<0||R+1>=m||w<0||w+1>=12)q[t]=!1;else{for(let t=0;t<2;t++){let e=Y+t;for(let t=0;t<2;t++){let o=R+t;for(let t=0;t<2;t++){let n=(A+t)%s;for(let t=0;t<2;t++){const a=e+o*x+n*I+(w+t)*P;void 0===d[a]&&(d[a]=0),d[a]+=1}}}}q[t]=!0}}let v=0,X=-1;if(Object.keys(d).forEach(t=>{d[t]>v&&(v=d[t],X=t)}),v<3)return[];const Y=Math.floor(X%P%I%x),R=Math.floor((X-Y)%P%I/x),w=Math.floor((X-Y-R*x)%P/I),A=Math.floor((X-Y-R*x-w*I)/P),E=[];for(let t=0;t<h.length;t++){if(!q[t])continue;const e=O[t];if(Math.abs(e.binX-(Y+.5))>=2)continue;if(Math.abs(e.binY-(R+.5))>=2)continue;if(Math.abs(e.binScale-(A+.5))>=2)continue;const o=Math.abs(e.binAngle-(w+.5));Math.min(o,s-o)>=2||E.push(h[t])}return E},e=({querypoint:t,keypoint:e,keycenterX:o,keycenterY:n,scaleOneOverLogK:a})=>{let l=t.angle-e.angle;l<=-Math.PI?l+=2*Math.PI:l>Math.PI&&(l-=2*Math.PI);const h=t.scale/e.scale,i=h*Math.cos(l),r=h*Math.sin(l),c=[i,-r,r,i],M=[c[0]*e.x+c[1]*e.y,c[2]*e.x+c[3]*e.y],s=t.x-M[0],f=t.y-M[1];return{x:c[0]*o+c[1]*n+s,y:c[2]*o+c[3]*n+f,angle:l,scale:Math.log(h)*a}};export{t as computeHoughMatches};
|
|
@@ -1,66 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
class Matcher {
|
|
3
|
-
constructor(queryWidth, queryHeight, debugMode = false) {
|
|
4
|
-
this.queryWidth = queryWidth;
|
|
5
|
-
this.queryHeight = queryHeight;
|
|
6
|
-
this.debugMode = debugMode;
|
|
7
|
-
}
|
|
8
|
-
matchDetection(keyframes, featurePoints, expectedScale) {
|
|
9
|
-
let debugExtra = { frames: [] };
|
|
10
|
-
let bestResult = null;
|
|
11
|
-
// keyframes is actually the matchingData array for a single target
|
|
12
|
-
if (!keyframes || !Array.isArray(keyframes)) {
|
|
13
|
-
return { targetIndex: -1, keyframeIndex: -1, debugExtra };
|
|
14
|
-
}
|
|
15
|
-
for (let j = 0; j < keyframes.length; j++) {
|
|
16
|
-
const { H, matches, debugExtra: frameDebugExtra, } = match({
|
|
17
|
-
keyframe: keyframes[j],
|
|
18
|
-
querypoints: featurePoints,
|
|
19
|
-
querywidth: this.queryWidth,
|
|
20
|
-
queryheight: this.queryHeight,
|
|
21
|
-
debugMode: this.debugMode,
|
|
22
|
-
expectedScale,
|
|
23
|
-
});
|
|
24
|
-
if (frameDebugExtra) {
|
|
25
|
-
frameDebugExtra.keyframeIndex = j;
|
|
26
|
-
debugExtra.frames.push(frameDebugExtra);
|
|
27
|
-
}
|
|
28
|
-
if (H) {
|
|
29
|
-
if (bestResult === null || bestResult.matches.length < matches.length) {
|
|
30
|
-
bestResult = { keyframeIndex: j, H, matches };
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
if (bestResult === null) {
|
|
35
|
-
return { targetIndex: -1, keyframeIndex: -1, debugExtra };
|
|
36
|
-
}
|
|
37
|
-
const screenCoords = [];
|
|
38
|
-
const worldCoords = [];
|
|
39
|
-
const keyframe = keyframes[bestResult.keyframeIndex];
|
|
40
|
-
const kfScale = keyframe.s || keyframe.scale || 1.0;
|
|
41
|
-
for (let i = 0; i < bestResult.matches.length; i++) {
|
|
42
|
-
const querypoint = bestResult.matches[i].querypoint;
|
|
43
|
-
const keypoint = bestResult.matches[i].keypoint;
|
|
44
|
-
// 🚀 NANITE-STYLE: Use per-keypoint scale (octave) for accurate world mapping
|
|
45
|
-
const pointScale = keypoint.scale || kfScale;
|
|
46
|
-
screenCoords.push({
|
|
47
|
-
x: querypoint.x,
|
|
48
|
-
y: querypoint.y,
|
|
49
|
-
});
|
|
50
|
-
worldCoords.push({
|
|
51
|
-
x: (keypoint.x + 0.5) / kfScale,
|
|
52
|
-
y: (keypoint.y + 0.5) / kfScale,
|
|
53
|
-
z: 0,
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
return {
|
|
57
|
-
screenCoords,
|
|
58
|
-
worldCoords,
|
|
59
|
-
targetIndex: -1, // Caller knows the targetIndex
|
|
60
|
-
keyframeIndex: bestResult.keyframeIndex,
|
|
61
|
-
H: bestResult.H,
|
|
62
|
-
debugExtra
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
export { Matcher };
|
|
1
|
+
import{match as e}from"./matching.js";class t{constructor(e,t,r=!1){this.queryWidth=e,this.queryHeight=t,this.debugMode=r}matchDetection(t,r,s){let a={frames:[]},n=null;if(!t||!Array.isArray(t))return{targetIndex:-1,keyframeIndex:-1,debugExtra:a};for(let h=0;h<t.length;h++){const{H:d,matches:u,debugExtra:y}=e({keyframe:t[h],querypoints:r,querywidth:this.queryWidth,queryheight:this.queryHeight,debugMode:this.debugMode,expectedScale:s});y&&(y.keyframeIndex=h,a.frames.push(y)),d&&(null===n||n.matches.length<u.length)&&(n={keyframeIndex:h,H:d,matches:u})}if(null===n)return{targetIndex:-1,keyframeIndex:-1,debugExtra:a};const h=[],d=[],u=t[n.keyframeIndex],y=u.s||u.scale||1;for(let e=0;e<n.matches.length;e++){const t=n.matches[e].querypoint,r=n.matches[e].keypoint;r.scale,h.push({x:t.x,y:t.y}),d.push({x:(r.x+.5)/y,y:(r.y+.5)/y,z:0})}return{screenCoords:h,worldCoords:d,targetIndex:-1,keyframeIndex:n.keyframeIndex,H:n.H,debugExtra:a}}}export{t as Matcher};
|