@srsergio/taptapp-ar 1.0.8 → 1.0.10

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 (125) hide show
  1. package/dist/compiler/controller.d.ts +15 -22
  2. package/dist/compiler/controller.js +73 -92
  3. package/dist/compiler/detector/crop-detector.d.ts +20 -51
  4. package/dist/compiler/detector/crop-detector.js +21 -15
  5. package/dist/compiler/input-loader.d.ts +15 -17
  6. package/dist/compiler/input-loader.js +58 -76
  7. package/dist/compiler/matching/hamming-distance.js +4 -4
  8. package/dist/compiler/matching/matcher.js +2 -2
  9. package/dist/compiler/matching/matching.d.ts +2 -16
  10. package/dist/compiler/matching/matching.js +72 -60
  11. package/dist/compiler/offline-compiler.d.ts +14 -65
  12. package/dist/compiler/offline-compiler.js +86 -55
  13. package/dist/compiler/three.js +0 -4
  14. package/dist/compiler/tracker/tracker.d.ts +26 -12
  15. package/dist/compiler/tracker/tracker.js +158 -259
  16. package/dist/compiler/utils/worker-pool.d.ts +2 -1
  17. package/dist/compiler/utils/worker-pool.js +4 -8
  18. package/package.json +1 -1
  19. package/src/compiler/controller.js +71 -93
  20. package/src/compiler/detector/crop-detector.js +26 -15
  21. package/src/compiler/input-loader.js +62 -88
  22. package/src/compiler/matching/hamming-distance.js +4 -4
  23. package/src/compiler/matching/hough.js +1 -1
  24. package/src/compiler/matching/matcher.js +2 -2
  25. package/src/compiler/matching/matching.js +80 -72
  26. package/src/compiler/offline-compiler.js +92 -58
  27. package/src/compiler/three.js +0 -4
  28. package/src/compiler/tracker/tracker.js +183 -283
  29. package/src/compiler/utils/worker-pool.js +4 -8
  30. package/dist/compiler/compiler-base.d.ts +0 -8
  31. package/dist/compiler/compiler-base.js +0 -179
  32. package/dist/compiler/compiler.d.ts +0 -9
  33. package/dist/compiler/compiler.js +0 -24
  34. package/dist/compiler/compiler.worker.d.ts +0 -1
  35. package/dist/compiler/compiler.worker.js +0 -28
  36. package/dist/compiler/detector/detector.d.ts +0 -97
  37. package/dist/compiler/detector/detector.js +0 -1042
  38. package/dist/compiler/detector/kernels/cpu/binomialFilter.d.ts +0 -6
  39. package/dist/compiler/detector/kernels/cpu/binomialFilter.js +0 -50
  40. package/dist/compiler/detector/kernels/cpu/buildExtremas.d.ts +0 -6
  41. package/dist/compiler/detector/kernels/cpu/buildExtremas.js +0 -89
  42. package/dist/compiler/detector/kernels/cpu/computeExtremaAngles.d.ts +0 -7
  43. package/dist/compiler/detector/kernels/cpu/computeExtremaAngles.js +0 -79
  44. package/dist/compiler/detector/kernels/cpu/computeExtremaFreak.d.ts +0 -6
  45. package/dist/compiler/detector/kernels/cpu/computeExtremaFreak.js +0 -68
  46. package/dist/compiler/detector/kernels/cpu/computeFreakDescriptors.d.ts +0 -6
  47. package/dist/compiler/detector/kernels/cpu/computeFreakDescriptors.js +0 -57
  48. package/dist/compiler/detector/kernels/cpu/computeLocalization.d.ts +0 -6
  49. package/dist/compiler/detector/kernels/cpu/computeLocalization.js +0 -50
  50. package/dist/compiler/detector/kernels/cpu/computeOrientationHistograms.d.ts +0 -6
  51. package/dist/compiler/detector/kernels/cpu/computeOrientationHistograms.js +0 -100
  52. package/dist/compiler/detector/kernels/cpu/downsampleBilinear.d.ts +0 -6
  53. package/dist/compiler/detector/kernels/cpu/downsampleBilinear.js +0 -29
  54. package/dist/compiler/detector/kernels/cpu/extremaReduction.d.ts +0 -6
  55. package/dist/compiler/detector/kernels/cpu/extremaReduction.js +0 -50
  56. package/dist/compiler/detector/kernels/cpu/fakeShader.d.ts +0 -20
  57. package/dist/compiler/detector/kernels/cpu/fakeShader.js +0 -80
  58. package/dist/compiler/detector/kernels/cpu/index.d.ts +0 -1
  59. package/dist/compiler/detector/kernels/cpu/index.js +0 -25
  60. package/dist/compiler/detector/kernels/cpu/prune.d.ts +0 -7
  61. package/dist/compiler/detector/kernels/cpu/prune.js +0 -62
  62. package/dist/compiler/detector/kernels/cpu/smoothHistograms.d.ts +0 -6
  63. package/dist/compiler/detector/kernels/cpu/smoothHistograms.js +0 -47
  64. package/dist/compiler/detector/kernels/cpu/upsampleBilinear.d.ts +0 -6
  65. package/dist/compiler/detector/kernels/cpu/upsampleBilinear.js +0 -43
  66. package/dist/compiler/detector/kernels/index.d.ts +0 -1
  67. package/dist/compiler/detector/kernels/index.js +0 -2
  68. package/dist/compiler/detector/kernels/webgl/binomialFilter.d.ts +0 -6
  69. package/dist/compiler/detector/kernels/webgl/binomialFilter.js +0 -67
  70. package/dist/compiler/detector/kernels/webgl/buildExtremas.d.ts +0 -6
  71. package/dist/compiler/detector/kernels/webgl/buildExtremas.js +0 -101
  72. package/dist/compiler/detector/kernels/webgl/computeExtremaAngles.d.ts +0 -6
  73. package/dist/compiler/detector/kernels/webgl/computeExtremaAngles.js +0 -78
  74. package/dist/compiler/detector/kernels/webgl/computeExtremaFreak.d.ts +0 -6
  75. package/dist/compiler/detector/kernels/webgl/computeExtremaFreak.js +0 -86
  76. package/dist/compiler/detector/kernels/webgl/computeFreakDescriptors.d.ts +0 -6
  77. package/dist/compiler/detector/kernels/webgl/computeFreakDescriptors.js +0 -52
  78. package/dist/compiler/detector/kernels/webgl/computeLocalization.d.ts +0 -6
  79. package/dist/compiler/detector/kernels/webgl/computeLocalization.js +0 -58
  80. package/dist/compiler/detector/kernels/webgl/computeOrientationHistograms.d.ts +0 -6
  81. package/dist/compiler/detector/kernels/webgl/computeOrientationHistograms.js +0 -116
  82. package/dist/compiler/detector/kernels/webgl/downsampleBilinear.d.ts +0 -6
  83. package/dist/compiler/detector/kernels/webgl/downsampleBilinear.js +0 -46
  84. package/dist/compiler/detector/kernels/webgl/extremaReduction.d.ts +0 -6
  85. package/dist/compiler/detector/kernels/webgl/extremaReduction.js +0 -48
  86. package/dist/compiler/detector/kernels/webgl/index.d.ts +0 -1
  87. package/dist/compiler/detector/kernels/webgl/index.js +0 -25
  88. package/dist/compiler/detector/kernels/webgl/smoothHistograms.d.ts +0 -6
  89. package/dist/compiler/detector/kernels/webgl/smoothHistograms.js +0 -49
  90. package/dist/compiler/detector/kernels/webgl/upsampleBilinear.d.ts +0 -6
  91. package/dist/compiler/detector/kernels/webgl/upsampleBilinear.js +0 -56
  92. package/dist/compiler/tensorflow-setup.d.ts +0 -6
  93. package/dist/compiler/tensorflow-setup.js +0 -99
  94. package/src/compiler/compiler-base.js +0 -210
  95. package/src/compiler/compiler.js +0 -25
  96. package/src/compiler/compiler.worker.js +0 -30
  97. package/src/compiler/detector/detector.js +0 -1119
  98. package/src/compiler/detector/kernels/cpu/binomialFilter.js +0 -58
  99. package/src/compiler/detector/kernels/cpu/buildExtremas.js +0 -108
  100. package/src/compiler/detector/kernels/cpu/computeExtremaAngles.js +0 -91
  101. package/src/compiler/detector/kernels/cpu/computeExtremaFreak.js +0 -92
  102. package/src/compiler/detector/kernels/cpu/computeFreakDescriptors.js +0 -68
  103. package/src/compiler/detector/kernels/cpu/computeLocalization.js +0 -67
  104. package/src/compiler/detector/kernels/cpu/computeOrientationHistograms.js +0 -124
  105. package/src/compiler/detector/kernels/cpu/downsampleBilinear.js +0 -33
  106. package/src/compiler/detector/kernels/cpu/extremaReduction.js +0 -53
  107. package/src/compiler/detector/kernels/cpu/fakeShader.js +0 -88
  108. package/src/compiler/detector/kernels/cpu/index.js +0 -26
  109. package/src/compiler/detector/kernels/cpu/prune.js +0 -78
  110. package/src/compiler/detector/kernels/cpu/smoothHistograms.js +0 -57
  111. package/src/compiler/detector/kernels/cpu/upsampleBilinear.js +0 -51
  112. package/src/compiler/detector/kernels/index.js +0 -2
  113. package/src/compiler/detector/kernels/webgl/binomialFilter.js +0 -72
  114. package/src/compiler/detector/kernels/webgl/buildExtremas.js +0 -109
  115. package/src/compiler/detector/kernels/webgl/computeExtremaAngles.js +0 -82
  116. package/src/compiler/detector/kernels/webgl/computeExtremaFreak.js +0 -105
  117. package/src/compiler/detector/kernels/webgl/computeFreakDescriptors.js +0 -56
  118. package/src/compiler/detector/kernels/webgl/computeLocalization.js +0 -70
  119. package/src/compiler/detector/kernels/webgl/computeOrientationHistograms.js +0 -129
  120. package/src/compiler/detector/kernels/webgl/downsampleBilinear.js +0 -50
  121. package/src/compiler/detector/kernels/webgl/extremaReduction.js +0 -50
  122. package/src/compiler/detector/kernels/webgl/index.js +0 -26
  123. package/src/compiler/detector/kernels/webgl/smoothHistograms.js +0 -53
  124. package/src/compiler/detector/kernels/webgl/upsampleBilinear.js +0 -62
  125. package/src/compiler/tensorflow-setup.js +0 -116
@@ -1,4 +1,3 @@
1
- import * as tf from "@tensorflow/tfjs";
2
1
  import { buildModelViewProjectionTransform, computeScreenCoordiate } from "../estimation/utils.js";
3
2
 
4
3
  const AR2_DEFAULT_TS = 6;
@@ -9,22 +8,20 @@ const AR2_SIM_THRESH = 0.8;
9
8
 
10
9
  const TRACKING_KEYFRAME = 0; // 0: 128px (optimized)
11
10
 
12
- // For some mobile device, only 16bit floating point texture is supported
13
- // ref: https://www.tensorflow.org/js/guide/platform_environment#precision
14
- // Empirical results shows that modelViewProjectTransform can go up beyond that, resulting in error
15
- // We get around this by dividing the transform matrix by 1000, and then multiply back inside webgl program
16
- const PRECISION_ADJUST = 1000;
17
-
18
11
  class Tracker {
19
12
  constructor(
20
13
  markerDimensions,
21
14
  trackingDataList,
22
15
  projectionTransform,
16
+ inputWidth,
17
+ inputHeight,
23
18
  debugMode = false,
24
19
  ) {
25
20
  this.markerDimensions = markerDimensions;
26
21
  this.trackingDataList = trackingDataList;
27
22
  this.projectionTransform = projectionTransform;
23
+ this.inputWidth = inputWidth;
24
+ this.inputHeight = inputHeight;
28
25
  this.debugMode = debugMode;
29
26
 
30
27
  this.trackingKeyframeList = [];
@@ -32,70 +29,63 @@ class Tracker {
32
29
  this.trackingKeyframeList.push(trackingDataList[i][TRACKING_KEYFRAME]);
33
30
  }
34
31
 
35
- // prebuild feature and marker pixel tensors
36
- let maxCount = 0;
32
+ // Prebuild TypedArrays for features and pixels
33
+ this.prebuiltData = [];
37
34
  for (let i = 0; i < this.trackingKeyframeList.length; i++) {
38
- maxCount = Math.max(maxCount, this.trackingKeyframeList[i].px.length);
39
- }
40
- this.featurePointsListT = [];
41
- this.imagePixelsListT = [];
42
- this.imagePropertiesListT = [];
43
-
44
- for (let i = 0; i < this.trackingKeyframeList.length; i++) {
45
- const { featurePoints, imagePixels, imageProperties } = this._prebuild(
46
- this.trackingKeyframeList[i],
47
- maxCount,
48
- );
49
- this.featurePointsListT[i] = featurePoints;
50
- this.imagePixelsListT[i] = imagePixels;
51
- this.imagePropertiesListT[i] = imageProperties;
35
+ const keyframe = this.trackingKeyframeList[i];
36
+ this.prebuiltData[i] = {
37
+ px: new Float32Array(keyframe.px),
38
+ py: new Float32Array(keyframe.py),
39
+ data: new Uint8Array(keyframe.d),
40
+ width: keyframe.w,
41
+ height: keyframe.h,
42
+ scale: keyframe.s,
43
+ // Recyclable projected image buffer
44
+ projectedImage: new Float32Array(keyframe.w * keyframe.h)
45
+ };
52
46
  }
53
47
 
54
- this.kernelCaches = {};
48
+ // Pre-allocate template data buffer to avoid garbage collection
49
+ const templateOneSize = AR2_DEFAULT_TS;
50
+ const templateSize = templateOneSize * 2 + 1;
51
+ this.templateBuffer = new Float32Array(templateSize * templateSize);
55
52
  }
56
53
 
57
- dummyRun(inputT) {
54
+ dummyRun(inputData) {
58
55
  let transform = [
59
- [1, 1, 1, 1],
60
- [1, 1, 1, 1],
61
- [1, 1, 1, 1],
56
+ [1, 0, 0, 0],
57
+ [0, 1, 0, 0],
58
+ [0, 0, 1, 0],
62
59
  ];
63
- for (let targetIndex = 0; targetIndex < this.featurePointsListT.length; targetIndex++) {
64
- this.track(inputT, transform, targetIndex);
60
+ for (let targetIndex = 0; targetIndex < this.trackingKeyframeList.length; targetIndex++) {
61
+ this.track(inputData, transform, targetIndex);
65
62
  }
66
63
  }
67
64
 
68
- track(inputImageT, lastModelViewTransform, targetIndex) {
65
+ track(inputData, lastModelViewTransform, targetIndex) {
69
66
  let debugExtra = {};
70
67
 
71
68
  const modelViewProjectionTransform = buildModelViewProjectionTransform(
72
69
  this.projectionTransform,
73
70
  lastModelViewTransform,
74
71
  );
75
- const modelViewProjectionTransformT = this._buildAdjustedModelViewTransform(
76
- modelViewProjectionTransform,
77
- );
78
-
79
72
 
80
- const featurePointsT = this.featurePointsListT[targetIndex];
81
- const imagePixelsT = this.imagePixelsListT[targetIndex];
82
- const imagePropertiesT = this.imagePropertiesListT[targetIndex];
73
+ const prebuilt = this.prebuiltData[targetIndex];
83
74
 
84
- const projectedImageT = this._computeProjection(
85
- modelViewProjectionTransformT,
86
- inputImageT,
87
- targetIndex,
75
+ // 1. Compute Projection (Warping)
76
+ this._computeProjection(
77
+ modelViewProjectionTransform,
78
+ inputData,
79
+ prebuilt
88
80
  );
89
81
 
90
- const { matchingPointsT, simT } = this._computeMatching(
91
- featurePointsT,
92
- imagePixelsT,
93
- imagePropertiesT,
94
- projectedImageT,
95
- );
82
+ const projectedImage = prebuilt.projectedImage;
96
83
 
97
- const matchingPoints = matchingPointsT.arraySync();
98
- const sim = simT.arraySync();
84
+ // 2. Compute Matching (NCC)
85
+ const { matchingPoints, sim } = this._computeMatching(
86
+ prebuilt,
87
+ projectedImage
88
+ );
99
89
 
100
90
  const trackingFrame = this.trackingKeyframeList[targetIndex];
101
91
  const worldCoords = [];
@@ -123,265 +113,175 @@ class Tracker {
123
113
 
124
114
  if (this.debugMode) {
125
115
  debugExtra = {
126
- projectedImage: projectedImageT.arraySync(),
127
- matchingPoints: matchingPointsT.arraySync(),
116
+ projectedImage: Array.from(projectedImage),
117
+ matchingPoints,
128
118
  goodTrack,
129
119
  trackedPoints: screenCoords,
130
120
  };
131
121
  }
132
122
 
133
- // tensors cleanup
134
- modelViewProjectionTransformT.dispose();
135
- projectedImageT.dispose();
136
- matchingPointsT.dispose();
137
- simT.dispose();
138
-
139
123
  return { worldCoords, screenCoords, debugExtra };
140
124
  }
141
125
 
142
- _computeMatching(featurePointsT, imagePixelsT, imagePropertiesT, projectedImageT) {
126
+ /**
127
+ * Pure JS implementation of NCC matching
128
+ */
129
+ _computeMatching(prebuilt, projectedImage) {
130
+ const { px, py, scale, data: markerPixels, width: markerWidth, height: markerHeight } = prebuilt;
131
+ const featureCount = px.length;
132
+
143
133
  const templateOneSize = AR2_DEFAULT_TS;
144
134
  const templateSize = templateOneSize * 2 + 1;
145
- const templateGap = AR2_DEFAULT_TS_GAP;
146
- const searchOneSize = AR2_SEARCH_SIZE * templateGap;
147
- const searchGap = AR2_SEARCH_GAP;
148
- const searchSize = searchOneSize * 2 + 1;
149
- const targetHeight = projectedImageT.shape[0];
150
- const targetWidth = projectedImageT.shape[1];
151
- const featureCount = featurePointsT.shape[0];
152
-
153
- if (!this.kernelCaches.computeMatching) {
154
- const kernel1 = {
155
- variableNames: ["features", "markerPixels", "markerProperties", "targetPixels"],
156
- outputShape: [featureCount, searchSize * searchSize],
157
- userCode: `
158
- void main() {
159
- ivec2 coords = getOutputCoords();
160
-
161
- int featureIndex = coords[0];
162
- int searchOffsetIndex = coords[1];
163
-
164
- int markerWidth = int(getMarkerProperties(0));
165
- int markerHeight = int(getMarkerProperties(1));
166
- float markerScale = getMarkerProperties(2);
167
-
168
- int searchOffsetX = imod(searchOffsetIndex, ${searchSize}) * ${searchGap};
169
- int searchOffsetY = searchOffsetIndex / ${searchSize} * ${searchGap};
170
-
171
- int sCenterX = int(getFeatures(featureIndex, 0) * markerScale);
172
- int sCenterY = int(getFeatures(featureIndex, 1) * markerScale);
173
-
174
- int sx = sCenterX + searchOffsetX - ${searchOneSize};
175
- int sy = sCenterY + searchOffsetY - ${searchOneSize};
176
-
177
- if (sx < ${templateOneSize} || sx >= (${targetWidth} - ${templateOneSize}) || sy < ${templateOneSize} || sy >= (${targetHeight} - ${templateOneSize})) {
178
- setOutput(-2.);
179
- }
180
- else {
181
- float sumPoint = 0.;
182
- float sumPointSquare = 0.;
183
- float sumTemplate = 0.;
184
- float sumTemplateSquare = 0.;
185
- float sumPointTemplate = 0.;
186
-
187
- for (int templateOffsetY = 0; templateOffsetY < ${templateSize}; templateOffsetY++) {
188
- for (int templateOffsetX = 0; templateOffsetX < ${templateSize}; templateOffsetX++) {
189
- int fx2 = sCenterX + templateOffsetX - ${templateOneSize};
190
- int fy2 = sCenterY + templateOffsetY - ${templateOneSize};
191
-
192
- int sx2 = sx + templateOffsetX - ${templateOneSize};
193
- int sy2 = sy + templateOffsetY - ${templateOneSize};
194
-
195
- int markerPixelIndex = fy2 * markerWidth + fx2;
196
- float markerPixel = getMarkerPixels(markerPixelIndex);
197
- float targetPixel = getTargetPixels(sy2, sx2);
198
-
199
- sumTemplate += markerPixel;
200
- sumTemplateSquare += markerPixel * markerPixel;
201
- sumPoint += targetPixel;
202
- sumPointSquare += targetPixel * targetPixel;
203
- sumPointTemplate += targetPixel * markerPixel;
204
- }
205
- }
206
-
207
- // Normalized cross-correlation
208
- // !important divide first avoid overflow (e.g. sumPoint / count * sumPoint)
209
- float count = float(${templateSize} * ${templateSize});
210
- float pointVariance = sqrt(sumPointSquare - sumPoint / count * sumPoint);
211
- float templateVariance = sqrt(sumTemplateSquare - sumTemplate / count * sumTemplate);
212
-
213
- if (pointVariance < 0.0000001) {
214
- setOutput(-3.);
215
- } else if (templateVariance < 0.0000001) {
216
- //setOutput(sumTemplate);
217
- setOutput(-4.);
218
- } else {
219
- sumPointTemplate -= sumPoint / count * sumTemplate;
220
- float sim = sumPointTemplate / pointVariance / templateVariance;
221
- setOutput(sim);
222
- }
223
- }
224
- }
225
- `,
226
- };
227
-
228
- const kernel2 = {
229
- variableNames: ["featurePoints", "markerProperties", "maxIndex"],
230
- outputShape: [featureCount, 2], // [x, y]
231
- userCode: `
232
- void main() {
233
- ivec2 coords = getOutputCoords();
234
-
235
- float markerScale = getMarkerProperties(2);
236
-
237
- int featureIndex = coords[0];
238
-
239
- int maxIndex = int(getMaxIndex(featureIndex));
240
- int searchLocationIndex = maxIndex / ${searchSize * searchSize};
241
- int searchOffsetIndex = imod(maxIndex, ${searchSize * searchSize});
242
-
243
- if (coords[1] == 0) {
244
- int searchOffsetX = imod(searchOffsetIndex, ${searchSize}) * ${searchGap};
245
- setOutput(getFeaturePoints(featureIndex, 0) + float(searchOffsetX - ${searchOneSize}) / markerScale);
246
- }
247
- else if (coords[1] == 1) {
248
- int searchOffsetY = searchOffsetIndex / ${searchSize} * ${searchGap};
249
- setOutput(getFeaturePoints(featureIndex, 1) + float(searchOffsetY - ${searchOneSize}) / markerScale);
250
- }
251
- }
252
- `,
253
- };
135
+ const nPixels = templateSize * templateSize;
136
+ const oneOverNPixels = 1.0 / nPixels;
254
137
 
255
- const kernel3 = {
256
- variableNames: ["sims", "maxIndex"],
257
- outputShape: [featureCount],
258
- userCode: `
259
- void main() {
260
- int featureIndex = getOutputCoords();
261
- int maxIndex = int(getMaxIndex(featureIndex));
262
- setOutput(getSims(featureIndex, maxIndex));
263
- }
264
- `,
265
- };
138
+ const searchOneSize = AR2_SEARCH_SIZE;
139
+ const searchGap = AR2_SEARCH_GAP;
266
140
 
267
- this.kernelCaches.computeMatching = [kernel1, kernel2, kernel3];
268
- }
141
+ const matchingPoints = [];
142
+ const sims = new Float32Array(featureCount);
269
143
 
270
- return tf.tidy(() => {
271
- const programs = this.kernelCaches.computeMatching;
272
- const allSims = this._compileAndRun(programs[0], [
273
- featurePointsT,
274
- imagePixelsT,
275
- imagePropertiesT,
276
- projectedImageT,
277
- ]);
278
- const maxIndex = allSims.argMax(1);
279
- const matchingPointsT = this._compileAndRun(programs[1], [
280
- featurePointsT,
281
- imagePropertiesT,
282
- maxIndex,
283
- ]);
284
- const simT = this._compileAndRun(programs[2], [allSims, maxIndex]);
285
- return { matchingPointsT, simT };
286
- });
287
- }
144
+ // Reuse shared template buffer
145
+ const templateData = this.templateBuffer;
288
146
 
289
- _computeProjection(modelViewProjectionTransformT, inputImageT, targetIndex) {
290
- const markerWidth = this.trackingKeyframeList[targetIndex].width;
291
- const markerHeight = this.trackingKeyframeList[targetIndex].height;
292
- const markerScale = this.trackingKeyframeList[targetIndex].scale;
293
- const kernelKey = markerWidth + "-" + markerHeight + "-" + markerScale;
147
+ for (let f = 0; f < featureCount; f++) {
148
+ const sCenterX = (px[f] + 0.5) | 0; // Faster Math.round
149
+ const sCenterY = (py[f] + 0.5) | 0;
294
150
 
295
- if (!this.kernelCaches.computeProjection) {
296
- this.kernelCaches.computeProjection = {};
297
- }
151
+ let bestSim = -1.0;
152
+ let bestX = px[f] / scale;
153
+ let bestY = py[f] / scale;
298
154
 
299
- if (!this.kernelCaches.computeProjection[kernelKey]) {
300
- const kernel = {
301
- variableNames: ["M", "pixel"],
302
- outputShape: [markerHeight, markerWidth],
303
- userCode: `
304
- void main() {
305
- ivec2 coords = getOutputCoords();
306
-
307
- float m00 = getM(0, 0) * ${PRECISION_ADJUST}.;
308
- float m01 = getM(0, 1) * ${PRECISION_ADJUST}.;
309
- float m03 = getM(0, 3) * ${PRECISION_ADJUST}.;
310
- float m10 = getM(1, 0) * ${PRECISION_ADJUST}.;
311
- float m11 = getM(1, 1) * ${PRECISION_ADJUST}.;
312
- float m13 = getM(1, 3) * ${PRECISION_ADJUST}.;
313
- float m20 = getM(2, 0) * ${PRECISION_ADJUST}.;
314
- float m21 = getM(2, 1) * ${PRECISION_ADJUST}.;
315
- float m23 = getM(2, 3) * ${PRECISION_ADJUST}.;
316
-
317
- float y = float(coords[0]) / float(${markerScale});
318
- float x = float(coords[1]) / float(${markerScale});
319
- float uz = (x * m20) + (y * m21) + m23;
320
- float oneOverUz = 1. / uz;
321
-
322
- float ux = (x * m00) + (y * m01) + m03;
323
- float uy = (x * m10) + (y * m11) + m13;
324
-
325
- ux = floor(ux * oneOverUz + 0.5);
326
- uy = floor(uy * oneOverUz + 0.5);
327
- setOutput(getPixel(int(uy), int(ux)));
328
- }
329
- `,
330
- };
155
+ // Pre-calculate template stats for this feature
156
+ let sumT = 0;
157
+ let sumT2 = 0;
158
+ let tidx = 0;
331
159
 
332
- this.kernelCaches.computeProjection[kernelKey] = kernel;
333
- }
160
+ for (let ty = -templateOneSize; ty <= templateOneSize; ty++) {
161
+ const fyOffset = (sCenterY + ty) * markerWidth;
162
+ for (let tx = -templateOneSize; tx <= templateOneSize; tx++) {
163
+ const val = markerPixels[fyOffset + sCenterX + tx];
164
+ templateData[tidx++] = val;
165
+ sumT += val;
166
+ sumT2 += val * val;
167
+ }
168
+ }
334
169
 
335
- return tf.tidy(() => {
336
- const program = this.kernelCaches.computeProjection[kernelKey];
337
- const result = this._compileAndRun(program, [modelViewProjectionTransformT, inputImageT]);
338
- return result;
339
- });
340
- }
170
+ const varT = Math.sqrt(Math.max(0, sumT2 - (sumT * sumT) * oneOverNPixels));
171
+ if (varT < 0.0001) {
172
+ sims[f] = -1.0;
173
+ matchingPoints.push([bestX, bestY]);
174
+ continue;
175
+ }
341
176
 
342
- _buildAdjustedModelViewTransform(modelViewProjectionTransform) {
343
- return tf.tidy(() => {
344
- let modelViewProjectionTransformAdjusted = [];
345
- for (let i = 0; i < modelViewProjectionTransform.length; i++) {
346
- modelViewProjectionTransformAdjusted.push([]);
347
- for (let j = 0; j < modelViewProjectionTransform[i].length; j++) {
348
- modelViewProjectionTransformAdjusted[i].push(
349
- modelViewProjectionTransform[i][j] / PRECISION_ADJUST,
350
- );
177
+ // Search in projected image
178
+ for (let sy = -searchOneSize; sy <= searchOneSize; sy += searchGap) {
179
+ const cy = sCenterY + sy;
180
+ if (cy < templateOneSize || cy >= markerHeight - templateOneSize) continue;
181
+
182
+ for (let sx = -searchOneSize; sx <= searchOneSize; sx += searchGap) {
183
+ const cx = sCenterX + sx;
184
+ if (cx < templateOneSize || cx >= markerWidth - templateOneSize) continue;
185
+
186
+ let sumI = 0;
187
+ let sumI2 = 0;
188
+ let sumIT = 0;
189
+
190
+ for (let ty = -templateOneSize; ty <= templateOneSize; ty++) {
191
+ const rowOffset = (cy + ty) * markerWidth;
192
+ const tRowOffset = (ty + templateOneSize) * templateSize;
193
+ for (let tx = -templateOneSize; tx <= templateOneSize; tx++) {
194
+ const valI = projectedImage[rowOffset + (cx + tx)];
195
+ const valT = templateData[tRowOffset + (tx + templateOneSize)];
196
+
197
+ sumI += valI;
198
+ sumI2 += valI * valI;
199
+ sumIT += valI * valT;
200
+ }
201
+ }
202
+
203
+ const varI = Math.sqrt(Math.max(0, sumI2 - (sumI * sumI) * oneOverNPixels));
204
+ if (varI < 0.0001) continue;
205
+
206
+ const sim = (sumIT - (sumI * sumT) * oneOverNPixels) / (varI * varT);
207
+ if (sim > bestSim) {
208
+ bestSim = sim;
209
+ bestX = cx / scale;
210
+ bestY = cy / scale;
211
+ }
351
212
  }
352
213
  }
353
- const t = tf.tensor(modelViewProjectionTransformAdjusted, [3, 4]);
354
- return t;
355
- });
356
- }
357
214
 
358
- _prebuild(trackingFrame, maxCount) {
359
- return tf.tidy(() => {
360
- const { px, py, s: scale, d: data, w: width, h: height } = trackingFrame;
215
+ sims[f] = bestSim;
216
+ matchingPoints.push([bestX, bestY]);
217
+ }
218
+
219
+ return { matchingPoints, sim: sims };
220
+ }
361
221
 
362
- const p = [];
363
- for (let k = 0; k < maxCount; k++) {
364
- if (k < px.length) {
365
- p.push([px[k] / scale, py[k] / scale]);
222
+ /**
223
+ * Pure JS implementation of Bilinear Warping
224
+ */
225
+ _computeProjection(M, inputData, prebuilt) {
226
+ const { width: markerWidth, height: markerHeight, scale: markerScale, projectedImage } = prebuilt;
227
+ const invScale = 1.0 / markerScale;
228
+
229
+ const inputW = this.inputWidth;
230
+ const inputH = this.inputHeight;
231
+
232
+ const m00 = M[0][0];
233
+ const m01 = M[0][1];
234
+ const m03 = M[0][3];
235
+ const m10 = M[1][0];
236
+ const m11 = M[1][1];
237
+ const m13 = M[1][3];
238
+ const m20 = M[2][0];
239
+ const m21 = M[2][1];
240
+ const m23 = M[2][3];
241
+
242
+ for (let j = 0; j < markerHeight; j++) {
243
+ const y = j * invScale;
244
+ const jOffset = j * markerWidth;
245
+
246
+ for (let i = 0; i < markerWidth; i++) {
247
+ const x = i * invScale;
248
+
249
+ const uz = (x * m20) + (y * m21) + m23;
250
+ const invZ = 1.0 / uz;
251
+
252
+ const ux = ((x * m00) + (y * m01) + m03) * invZ;
253
+ const uy = ((x * m10) + (y * m11) + m13) * invZ;
254
+
255
+ // Bilinear interpolation
256
+ const x0 = ux | 0; // Faster Math.floor
257
+ const y0 = uy | 0;
258
+ const x1 = x0 + 1;
259
+ const y1 = y0 + 1;
260
+
261
+ if (x0 >= 0 && x1 < inputW && y0 >= 0 && y1 < inputH) {
262
+ const dx = ux - x0;
263
+ const dy = uy - y0;
264
+ const omDx = 1.0 - dx;
265
+ const omDy = 1.0 - dy;
266
+
267
+ const y0Offset = y0 * inputW;
268
+ const y1Offset = y1 * inputW;
269
+
270
+ const v00 = inputData[y0Offset + x0];
271
+ const v10 = inputData[y0Offset + x1];
272
+ const v01 = inputData[y1Offset + x0];
273
+ const v11 = inputData[y1Offset + x1];
274
+
275
+ projectedImage[jOffset + i] =
276
+ v00 * omDx * omDy +
277
+ v10 * dx * omDy +
278
+ v01 * omDx * dy +
279
+ v11 * dx * dy;
366
280
  } else {
367
- p.push([-1, -1]);
281
+ projectedImage[jOffset + i] = 0;
368
282
  }
369
283
  }
370
- const imagePixels = tf.tensor(data, [width * height]);
371
- const imageProperties = tf.tensor([width, height, scale], [3]);
372
- const featurePoints = tf.tensor(p, [p.length, 2], "float32");
373
-
374
- return {
375
- featurePoints,
376
- imagePixels,
377
- imageProperties,
378
- };
379
- });
380
- }
381
-
382
- _compileAndRun(program, inputs) {
383
- const outInfo = tf.backend().compileAndRun(program, inputs);
384
- return tf.engine().makeTensor(outInfo.dataId, outInfo.shape, outInfo.dtype);
284
+ }
385
285
  }
386
286
  }
387
287
 
@@ -1,10 +1,9 @@
1
- import { Worker } from 'node:worker_threads';
2
- import os from 'node:os';
3
1
 
4
2
  export class WorkerPool {
5
- constructor(workerPath, poolSize = os.cpus().length) {
3
+ constructor(workerPath, poolSize, WorkerClass) {
6
4
  this.workerPath = workerPath;
7
5
  this.poolSize = poolSize;
6
+ this.WorkerClass = WorkerClass;
8
7
  this.workers = [];
9
8
  this.queue = [];
10
9
  this.activeWorkers = 0;
@@ -25,7 +24,7 @@ export class WorkerPool {
25
24
 
26
25
  _createWorker() {
27
26
  this.activeWorkers++;
28
- const worker = new Worker(this.workerPath);
27
+ const worker = new this.WorkerClass(this.workerPath);
29
28
  return worker;
30
29
  }
31
30
 
@@ -66,10 +65,7 @@ export class WorkerPool {
66
65
  }
67
66
  }
68
67
 
69
- worker.postMessage({
70
- type: 'compile',
71
- ...serializableData
72
- });
68
+ worker.postMessage(serializableData);
73
69
  }
74
70
 
75
71
  _finishTask(worker, callback, result) {
@@ -1,8 +0,0 @@
1
- export class CompilerBase {
2
- data: any[] | null;
3
- compileImageTargets(images: any, progressCallback: any): Promise<any>;
4
- exportData(): any;
5
- importData(buffer: any): any[];
6
- createProcessCanvas(): void;
7
- compileTrack(): Promise<never[]>;
8
- }