@srsergio/taptapp-ar 1.0.0 → 1.0.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.
Files changed (88) hide show
  1. package/README.md +102 -26
  2. package/dist/compiler/aframe.js +0 -3
  3. package/dist/compiler/compiler-base.d.ts +3 -7
  4. package/dist/compiler/compiler-base.js +28 -14
  5. package/dist/compiler/compiler.js +1 -1
  6. package/dist/compiler/compiler.worker.js +1 -1
  7. package/dist/compiler/controller.js +4 -5
  8. package/dist/compiler/controller.worker.js +0 -2
  9. package/dist/compiler/detector/crop-detector.js +0 -2
  10. package/dist/compiler/detector/detector-lite.d.ts +73 -0
  11. package/dist/compiler/detector/detector-lite.js +430 -0
  12. package/dist/compiler/detector/detector.js +236 -243
  13. package/dist/compiler/detector/kernels/cpu/binomialFilter.js +0 -1
  14. package/dist/compiler/detector/kernels/cpu/computeLocalization.js +0 -4
  15. package/dist/compiler/detector/kernels/cpu/computeOrientationHistograms.js +0 -18
  16. package/dist/compiler/detector/kernels/cpu/fakeShader.js +1 -1
  17. package/dist/compiler/detector/kernels/cpu/prune.d.ts +7 -1
  18. package/dist/compiler/detector/kernels/cpu/prune.js +1 -42
  19. package/dist/compiler/detector/kernels/webgl/upsampleBilinear.js +2 -2
  20. package/dist/compiler/estimation/refine-estimate.js +0 -1
  21. package/dist/compiler/estimation/utils.d.ts +1 -1
  22. package/dist/compiler/estimation/utils.js +1 -14
  23. package/dist/compiler/image-list.js +4 -4
  24. package/dist/compiler/input-loader.js +2 -2
  25. package/dist/compiler/matching/hamming-distance.js +13 -13
  26. package/dist/compiler/matching/hierarchical-clustering.js +1 -1
  27. package/dist/compiler/matching/matching.d.ts +20 -4
  28. package/dist/compiler/matching/matching.js +67 -41
  29. package/dist/compiler/matching/ransacHomography.js +1 -2
  30. package/dist/compiler/node-worker.d.ts +1 -0
  31. package/dist/compiler/node-worker.js +84 -0
  32. package/dist/compiler/offline-compiler.d.ts +171 -6
  33. package/dist/compiler/offline-compiler.js +303 -421
  34. package/dist/compiler/tensorflow-setup.js +27 -1
  35. package/dist/compiler/three.js +3 -5
  36. package/dist/compiler/tracker/extract.d.ts +1 -0
  37. package/dist/compiler/tracker/extract.js +200 -244
  38. package/dist/compiler/tracker/tracker.d.ts +1 -1
  39. package/dist/compiler/tracker/tracker.js +13 -18
  40. package/dist/compiler/utils/cumsum.d.ts +4 -2
  41. package/dist/compiler/utils/cumsum.js +17 -19
  42. package/dist/compiler/utils/gpu-compute.d.ts +57 -0
  43. package/dist/compiler/utils/gpu-compute.js +262 -0
  44. package/dist/compiler/utils/images.d.ts +4 -4
  45. package/dist/compiler/utils/images.js +67 -53
  46. package/dist/compiler/utils/worker-pool.d.ts +14 -0
  47. package/dist/compiler/utils/worker-pool.js +84 -0
  48. package/dist/index.d.ts +0 -2
  49. package/dist/index.js +0 -2
  50. package/package.json +19 -13
  51. package/src/compiler/aframe.js +2 -4
  52. package/src/compiler/compiler-base.js +29 -14
  53. package/src/compiler/compiler.js +1 -1
  54. package/src/compiler/compiler.worker.js +1 -1
  55. package/src/compiler/controller.js +4 -5
  56. package/src/compiler/controller.worker.js +0 -2
  57. package/src/compiler/detector/crop-detector.js +0 -2
  58. package/src/compiler/detector/detector-lite.js +494 -0
  59. package/src/compiler/detector/detector.js +1052 -1063
  60. package/src/compiler/detector/kernels/cpu/binomialFilter.js +0 -1
  61. package/src/compiler/detector/kernels/cpu/computeLocalization.js +0 -4
  62. package/src/compiler/detector/kernels/cpu/computeOrientationHistograms.js +0 -17
  63. package/src/compiler/detector/kernels/cpu/fakeShader.js +1 -1
  64. package/src/compiler/detector/kernels/cpu/prune.js +1 -37
  65. package/src/compiler/detector/kernels/webgl/upsampleBilinear.js +2 -2
  66. package/src/compiler/estimation/refine-estimate.js +0 -1
  67. package/src/compiler/estimation/utils.js +9 -24
  68. package/src/compiler/image-list.js +4 -4
  69. package/src/compiler/input-loader.js +2 -2
  70. package/src/compiler/matching/hamming-distance.js +11 -15
  71. package/src/compiler/matching/hierarchical-clustering.js +1 -1
  72. package/src/compiler/matching/matching.js +72 -42
  73. package/src/compiler/matching/ransacHomography.js +0 -2
  74. package/src/compiler/node-worker.js +93 -0
  75. package/src/compiler/offline-compiler.js +339 -504
  76. package/src/compiler/tensorflow-setup.js +29 -1
  77. package/src/compiler/three.js +3 -5
  78. package/src/compiler/tracker/extract.js +211 -267
  79. package/src/compiler/tracker/tracker.js +13 -22
  80. package/src/compiler/utils/cumsum.js +17 -19
  81. package/src/compiler/utils/gpu-compute.js +303 -0
  82. package/src/compiler/utils/images.js +84 -53
  83. package/src/compiler/utils/worker-pool.js +89 -0
  84. package/src/index.ts +0 -2
  85. package/src/compiler/estimation/esimate-experiment.js +0 -316
  86. package/src/compiler/estimation/refine-estimate-experiment.js +0 -512
  87. package/src/react/AREditor.tsx +0 -394
  88. package/src/react/ProgressDialog.tsx +0 -185
@@ -5,14 +5,14 @@ const AR2_DEFAULT_TS_GAP = 1;
5
5
  const AR2_SEARCH_SIZE = 10;
6
6
  const AR2_SEARCH_GAP = 1;
7
7
  const AR2_SIM_THRESH = 0.8;
8
- const TRACKING_KEYFRAME = 1; // 0: 256px, 1: 128px
8
+ const TRACKING_KEYFRAME = 0; // 0: 128px (optimized)
9
9
  // For some mobile device, only 16bit floating point texture is supported
10
10
  // ref: https://www.tensorflow.org/js/guide/platform_environment#precision
11
11
  // Empirical results shows that modelViewProjectTransform can go up beyond that, resulting in error
12
12
  // We get around this by dividing the transform matrix by 1000, and then multiply back inside webgl program
13
13
  const PRECISION_ADJUST = 1000;
14
14
  class Tracker {
15
- constructor(markerDimensions, trackingDataList, projectionTransform, inputWidth, inputHeight, debugMode = false) {
15
+ constructor(markerDimensions, trackingDataList, projectionTransform, debugMode = false) {
16
16
  this.markerDimensions = markerDimensions;
17
17
  this.trackingDataList = trackingDataList;
18
18
  this.projectionTransform = projectionTransform;
@@ -24,7 +24,7 @@ class Tracker {
24
24
  // prebuild feature and marker pixel tensors
25
25
  let maxCount = 0;
26
26
  for (let i = 0; i < this.trackingKeyframeList.length; i++) {
27
- maxCount = Math.max(maxCount, this.trackingKeyframeList[i].points.length);
27
+ maxCount = Math.max(maxCount, this.trackingKeyframeList[i].px.length);
28
28
  }
29
29
  this.featurePointsListT = [];
30
30
  this.imagePixelsListT = [];
@@ -51,10 +51,6 @@ class Tracker {
51
51
  let debugExtra = {};
52
52
  const modelViewProjectionTransform = buildModelViewProjectionTransform(this.projectionTransform, lastModelViewTransform);
53
53
  const modelViewProjectionTransformT = this._buildAdjustedModelViewTransform(modelViewProjectionTransform);
54
- const markerWidth = this.markerDimensions[targetIndex][0];
55
- const markerHeight = this.markerDimensions[targetIndex][1];
56
- const keyframeWidth = this.trackingKeyframeList[targetIndex].width;
57
- const keyframeHeight = this.trackingKeyframeList[targetIndex].height;
58
54
  const featurePointsT = this.featurePointsListT[targetIndex];
59
55
  const imagePixelsT = this.imagePixelsListT[targetIndex];
60
56
  const imagePropertiesT = this.imagePropertiesListT[targetIndex];
@@ -66,14 +62,15 @@ class Tracker {
66
62
  const worldCoords = [];
67
63
  const screenCoords = [];
68
64
  const goodTrack = [];
65
+ const { px, py, s: scale } = trackingFrame;
69
66
  for (let i = 0; i < matchingPoints.length; i++) {
70
- if (sim[i] > AR2_SIM_THRESH && i < trackingFrame.points.length) {
67
+ if (sim[i] > AR2_SIM_THRESH && i < px.length) {
71
68
  goodTrack.push(i);
72
69
  const point = computeScreenCoordiate(modelViewProjectionTransform, matchingPoints[i][0], matchingPoints[i][1]);
73
70
  screenCoords.push(point);
74
71
  worldCoords.push({
75
- x: trackingFrame.points[i].x / trackingFrame.scale,
76
- y: trackingFrame.points[i].y / trackingFrame.scale,
72
+ x: px[i] / scale,
73
+ y: py[i] / scale,
77
74
  z: 0,
78
75
  });
79
76
  }
@@ -297,20 +294,18 @@ class Tracker {
297
294
  }
298
295
  _prebuild(trackingFrame, maxCount) {
299
296
  return tf.tidy(() => {
300
- const scale = trackingFrame.scale;
297
+ const { px, py, s: scale, d: data, w: width, h: height } = trackingFrame;
301
298
  const p = [];
302
299
  for (let k = 0; k < maxCount; k++) {
303
- if (k < trackingFrame.points.length) {
304
- p.push([trackingFrame.points[k].x / scale, trackingFrame.points[k].y / scale]);
300
+ if (k < px.length) {
301
+ p.push([px[k] / scale, py[k] / scale]);
305
302
  }
306
303
  else {
307
304
  p.push([-1, -1]);
308
305
  }
309
306
  }
310
- const imagePixels = tf.tensor(trackingFrame.data, [
311
- trackingFrame.width * trackingFrame.height,
312
- ]);
313
- const imageProperties = tf.tensor([trackingFrame.width, trackingFrame.height, trackingFrame.scale], [3]);
307
+ const imagePixels = tf.tensor(data, [width * height]);
308
+ const imageProperties = tf.tensor([width, height, scale], [3]);
314
309
  const featurePoints = tf.tensor(p, [p.length, 2], "float32");
315
310
  return {
316
311
  featurePoints,
@@ -321,7 +316,7 @@ class Tracker {
321
316
  }
322
317
  _compileAndRun(program, inputs) {
323
318
  const outInfo = tf.backend().compileAndRun(program, inputs);
324
- return tf.engine().makeTensorFromDataId(outInfo.dataId, outInfo.shape, outInfo.dtype);
319
+ return tf.engine().makeTensor(outInfo.dataId, outInfo.shape, outInfo.dtype);
325
320
  }
326
321
  }
327
322
  export { Tracker };
@@ -1,5 +1,7 @@
1
1
  export class Cumsum {
2
2
  constructor(data: any, width: any, height: any);
3
- cumsum: never[][];
4
- query(x1: any, y1: any, x2: any, y2: any): any;
3
+ width: any;
4
+ height: any;
5
+ cumsum: Int32Array<ArrayBuffer>;
6
+ query(x1: any, y1: any, x2: any, y2: any): number;
5
7
  }
@@ -1,38 +1,36 @@
1
1
  // fast 2D submatrix sum using cumulative sum algorithm
2
2
  class Cumsum {
3
3
  constructor(data, width, height) {
4
- this.cumsum = [];
5
- for (let j = 0; j < height; j++) {
6
- this.cumsum.push([]);
7
- for (let i = 0; i < width; i++) {
8
- this.cumsum[j].push(0);
9
- }
10
- }
11
- this.cumsum[0][0] = data[0];
4
+ this.width = width;
5
+ this.height = height;
6
+ this.cumsum = new Int32Array(width * height);
7
+ this.cumsum[0] = data[0];
12
8
  for (let i = 1; i < width; i++) {
13
- this.cumsum[0][i] = this.cumsum[0][i - 1] + data[i];
9
+ this.cumsum[i] = this.cumsum[i - 1] + data[i];
14
10
  }
15
11
  for (let j = 1; j < height; j++) {
16
- this.cumsum[j][0] = this.cumsum[j - 1][0] + data[j * width];
12
+ this.cumsum[j * width] = this.cumsum[(j - 1) * width] + data[j * width];
17
13
  }
18
14
  for (let j = 1; j < height; j++) {
19
15
  for (let i = 1; i < width; i++) {
20
- this.cumsum[j][i] =
21
- data[j * width + i] +
22
- this.cumsum[j - 1][i] +
23
- this.cumsum[j][i - 1] -
24
- this.cumsum[j - 1][i - 1];
16
+ const pos = j * width + i;
17
+ this.cumsum[pos] =
18
+ data[pos] +
19
+ this.cumsum[(j - 1) * width + i] +
20
+ this.cumsum[j * width + i - 1] -
21
+ this.cumsum[(j - 1) * width + i - 1];
25
22
  }
26
23
  }
27
24
  }
28
25
  query(x1, y1, x2, y2) {
29
- let ret = this.cumsum[y2][x2];
26
+ const { width } = this;
27
+ let ret = this.cumsum[y2 * width + x2];
30
28
  if (y1 > 0)
31
- ret -= this.cumsum[y1 - 1][x2];
29
+ ret -= this.cumsum[(y1 - 1) * width + x2];
32
30
  if (x1 > 0)
33
- ret -= this.cumsum[y2][x1 - 1];
31
+ ret -= this.cumsum[y2 * width + x1 - 1];
34
32
  if (x1 > 0 && y1 > 0)
35
- ret += this.cumsum[y1 - 1][x1 - 1];
33
+ ret += this.cumsum[(y1 - 1) * width + x1 - 1];
36
34
  return ret;
37
35
  }
38
36
  }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * GPU Compute class - provides optimized image processing
3
+ */
4
+ export class GPUCompute {
5
+ gpu: any;
6
+ kernelCache: Map<any, any>;
7
+ initialized: boolean;
8
+ /**
9
+ * Initialize (tries GPU in browser, uses JS in Node)
10
+ */
11
+ init(): void;
12
+ /**
13
+ * Compute edge gradients
14
+ */
15
+ computeGradients(imageData: any, width: any, height: any): Float32Array<ArrayBuffer>;
16
+ /**
17
+ * Find local maxima
18
+ */
19
+ findLocalMaxima(gradients: any, width: any, height: any): Uint8Array<ArrayBuffer>;
20
+ /**
21
+ * Combined edge detection
22
+ */
23
+ edgeDetection(imageData: any, width: any, height: any): {
24
+ dValue: Float32Array<ArrayBuffer>;
25
+ isCandidate: Uint8Array<ArrayBuffer>;
26
+ };
27
+ /**
28
+ * Gaussian blur
29
+ */
30
+ gaussianBlur(imageData: any, width: any, height: any): Float32Array<ArrayBuffer>;
31
+ /**
32
+ * Downsample by factor of 2
33
+ */
34
+ downsample(imageData: any, width: any, height: any): {
35
+ data: Float32Array<ArrayBuffer>;
36
+ width: number;
37
+ height: number;
38
+ };
39
+ /**
40
+ * Build Gaussian pyramid
41
+ */
42
+ buildPyramid(imageData: any, width: any, height: any, numLevels?: number): {
43
+ data: Float32Array<ArrayBuffer>;
44
+ width: any;
45
+ height: any;
46
+ scale: number;
47
+ }[];
48
+ /**
49
+ * Check if GPU is available
50
+ */
51
+ isGPUAvailable(): boolean;
52
+ /**
53
+ * Cleanup resources
54
+ */
55
+ destroy(): void;
56
+ }
57
+ export const gpuCompute: GPUCompute;
@@ -0,0 +1,262 @@
1
+ /**
2
+ * @fileoverview GPU Compute Layer for AR Compiler
3
+ *
4
+ * Provides optimized image processing with GPU acceleration when available.
5
+ * - In browser: Uses GPU.js with WebGL
6
+ * - In Node.js: Uses pure JavaScript (GPU.js requires headless-gl which may not compile)
7
+ *
8
+ * All methods have pure JS fallbacks that work universally.
9
+ */
10
+ // Detect if running in Node.js
11
+ const isNode = typeof process !== 'undefined' &&
12
+ process.versions != null &&
13
+ process.versions.node != null;
14
+ // Lazy load GPU.js only in browser environments
15
+ let GPU = null;
16
+ let gpuInstance = null;
17
+ let gpuLoadAttempted = false;
18
+ /**
19
+ * Try to initialize GPU.js (browser only)
20
+ */
21
+ const tryInitGPU = () => {
22
+ if (gpuLoadAttempted)
23
+ return gpuInstance;
24
+ gpuLoadAttempted = true;
25
+ // Skip GPU.js in Node.js to avoid headless-gl issues
26
+ if (isNode) {
27
+ console.log("⚡ Running in Node.js - using optimized JS");
28
+ return null;
29
+ }
30
+ // Dynamically import GPU.js in browser
31
+ try {
32
+ // Use dynamic import pattern that works in browsers
33
+ const GPUModule = globalThis.GPU || (typeof require !== 'undefined' ? require('gpu.js').GPU : null);
34
+ if (GPUModule) {
35
+ GPU = GPUModule;
36
+ gpuInstance = new GPU({ mode: "gpu" });
37
+ // Test if GPU works
38
+ const testKernel = gpuInstance.createKernel(function () { return 1; }).setOutput([1]);
39
+ testKernel();
40
+ testKernel.destroy();
41
+ console.log("🚀 GPU.js: Using GPU acceleration");
42
+ }
43
+ }
44
+ catch (e) {
45
+ console.log("⚡ GPU.js unavailable, using optimized JS:", e.message);
46
+ gpuInstance = null;
47
+ }
48
+ return gpuInstance;
49
+ };
50
+ // ============================================================================
51
+ // PURE JAVASCRIPT IMPLEMENTATIONS (Always work)
52
+ // ============================================================================
53
+ /**
54
+ * Pure JS: Compute edge gradients
55
+ */
56
+ const computeGradientsJS = (imageData, width, height) => {
57
+ const dValue = new Float32Array(width * height);
58
+ for (let j = 1; j < height - 1; j++) {
59
+ const rowOffset = j * width;
60
+ const prevRowOffset = (j - 1) * width;
61
+ const nextRowOffset = (j + 1) * width;
62
+ for (let i = 1; i < width - 1; i++) {
63
+ const pos = rowOffset + i;
64
+ const dx = (imageData[prevRowOffset + i + 1] - imageData[prevRowOffset + i - 1] +
65
+ imageData[rowOffset + i + 1] - imageData[rowOffset + i - 1] +
66
+ imageData[nextRowOffset + i + 1] - imageData[nextRowOffset + i - 1]) / 768;
67
+ const dy = (imageData[nextRowOffset + i - 1] - imageData[prevRowOffset + i - 1] +
68
+ imageData[nextRowOffset + i] - imageData[prevRowOffset + i] +
69
+ imageData[nextRowOffset + i + 1] - imageData[prevRowOffset + i + 1]) / 768;
70
+ dValue[pos] = Math.sqrt((dx * dx + dy * dy) / 2);
71
+ }
72
+ }
73
+ return dValue;
74
+ };
75
+ /**
76
+ * Pure JS: Find local maxima
77
+ */
78
+ const findLocalMaximaJS = (gradients, width, height) => {
79
+ const isCandidate = new Uint8Array(width * height);
80
+ for (let j = 1; j < height - 1; j++) {
81
+ const rowOffset = j * width;
82
+ for (let i = 1; i < width - 1; i++) {
83
+ const pos = rowOffset + i;
84
+ const val = gradients[pos];
85
+ if (val > 0 &&
86
+ val >= gradients[pos - 1] && val >= gradients[pos + 1] &&
87
+ val >= gradients[pos - width] && val >= gradients[pos + width]) {
88
+ isCandidate[pos] = 1;
89
+ }
90
+ }
91
+ }
92
+ return isCandidate;
93
+ };
94
+ /**
95
+ * Pure JS: Gaussian blur (5x5 binomial)
96
+ */
97
+ const gaussianBlurJS = (data, width, height) => {
98
+ const output = new Float32Array(width * height);
99
+ const temp = new Float32Array(width * height);
100
+ const k0 = 1 / 16, k1 = 4 / 16, k2 = 6 / 16;
101
+ const w1 = width - 1;
102
+ const h1 = height - 1;
103
+ // Horizontal pass
104
+ for (let y = 0; y < height; y++) {
105
+ const rowOffset = y * width;
106
+ for (let x = 0; x < width; x++) {
107
+ const x0 = x < 2 ? 0 : x - 2;
108
+ const x1 = x < 1 ? 0 : x - 1;
109
+ const x3 = x > w1 - 1 ? w1 : x + 1;
110
+ const x4 = x > w1 - 2 ? w1 : x + 2;
111
+ temp[rowOffset + x] =
112
+ data[rowOffset + x0] * k0 +
113
+ data[rowOffset + x1] * k1 +
114
+ data[rowOffset + x] * k2 +
115
+ data[rowOffset + x3] * k1 +
116
+ data[rowOffset + x4] * k0;
117
+ }
118
+ }
119
+ // Vertical pass
120
+ for (let y = 0; y < height; y++) {
121
+ const y0 = (y < 2 ? 0 : y - 2) * width;
122
+ const y1 = (y < 1 ? 0 : y - 1) * width;
123
+ const y2 = y * width;
124
+ const y3 = (y > h1 - 1 ? h1 : y + 1) * width;
125
+ const y4 = (y > h1 - 2 ? h1 : y + 2) * width;
126
+ for (let x = 0; x < width; x++) {
127
+ output[y2 + x] =
128
+ temp[y0 + x] * k0 +
129
+ temp[y1 + x] * k1 +
130
+ temp[y2 + x] * k2 +
131
+ temp[y3 + x] * k1 +
132
+ temp[y4 + x] * k0;
133
+ }
134
+ }
135
+ return output;
136
+ };
137
+ /**
138
+ * Pure JS: Downsample by factor of 2
139
+ */
140
+ const downsampleJS = (data, width, height) => {
141
+ const newWidth = Math.floor(width / 2);
142
+ const newHeight = Math.floor(height / 2);
143
+ const output = new Float32Array(newWidth * newHeight);
144
+ for (let y = 0; y < newHeight; y++) {
145
+ const sy = y * 2;
146
+ for (let x = 0; x < newWidth; x++) {
147
+ const sx = x * 2;
148
+ const pos = sy * width + sx;
149
+ output[y * newWidth + x] =
150
+ (data[pos] + data[pos + 1] + data[pos + width] + data[pos + width + 1]) / 4;
151
+ }
152
+ }
153
+ return { data: output, width: newWidth, height: newHeight };
154
+ };
155
+ // ============================================================================
156
+ // GPU COMPUTE CLASS
157
+ // ============================================================================
158
+ /**
159
+ * GPU Compute class - provides optimized image processing
160
+ */
161
+ export class GPUCompute {
162
+ constructor() {
163
+ this.gpu = null;
164
+ this.kernelCache = new Map();
165
+ this.initialized = false;
166
+ }
167
+ /**
168
+ * Initialize (tries GPU in browser, uses JS in Node)
169
+ */
170
+ init() {
171
+ if (this.initialized)
172
+ return;
173
+ this.gpu = tryInitGPU();
174
+ this.initialized = true;
175
+ }
176
+ /**
177
+ * Compute edge gradients
178
+ */
179
+ computeGradients(imageData, width, height) {
180
+ this.init();
181
+ // Always use JS implementation for reliability
182
+ return computeGradientsJS(imageData, width, height);
183
+ }
184
+ /**
185
+ * Find local maxima
186
+ */
187
+ findLocalMaxima(gradients, width, height) {
188
+ this.init();
189
+ return findLocalMaximaJS(gradients, width, height);
190
+ }
191
+ /**
192
+ * Combined edge detection
193
+ */
194
+ edgeDetection(imageData, width, height) {
195
+ const dValue = this.computeGradients(imageData, width, height);
196
+ const isCandidate = this.findLocalMaxima(dValue, width, height);
197
+ return { dValue, isCandidate };
198
+ }
199
+ /**
200
+ * Gaussian blur
201
+ */
202
+ gaussianBlur(imageData, width, height) {
203
+ this.init();
204
+ return gaussianBlurJS(imageData, width, height);
205
+ }
206
+ /**
207
+ * Downsample by factor of 2
208
+ */
209
+ downsample(imageData, width, height) {
210
+ this.init();
211
+ return downsampleJS(imageData, width, height);
212
+ }
213
+ /**
214
+ * Build Gaussian pyramid
215
+ */
216
+ buildPyramid(imageData, width, height, numLevels = 5) {
217
+ this.init();
218
+ const pyramid = [];
219
+ let currentData = imageData instanceof Float32Array ? imageData : Float32Array.from(imageData);
220
+ let currentWidth = width;
221
+ let currentHeight = height;
222
+ for (let level = 0; level < numLevels; level++) {
223
+ const blurred = this.gaussianBlur(currentData, currentWidth, currentHeight);
224
+ pyramid.push({
225
+ data: blurred,
226
+ width: currentWidth,
227
+ height: currentHeight,
228
+ scale: Math.pow(2, level),
229
+ });
230
+ if (currentWidth > 8 && currentHeight > 8) {
231
+ const downsampled = this.downsample(blurred, currentWidth, currentHeight);
232
+ currentData = downsampled.data;
233
+ currentWidth = downsampled.width;
234
+ currentHeight = downsampled.height;
235
+ }
236
+ else {
237
+ break;
238
+ }
239
+ }
240
+ return pyramid;
241
+ }
242
+ /**
243
+ * Check if GPU is available
244
+ */
245
+ isGPUAvailable() {
246
+ this.init();
247
+ return this.gpu !== null;
248
+ }
249
+ /**
250
+ * Cleanup resources
251
+ */
252
+ destroy() {
253
+ this.kernelCache.clear();
254
+ if (this.gpu && this.gpu.destroy) {
255
+ this.gpu.destroy();
256
+ }
257
+ this.gpu = null;
258
+ this.initialized = false;
259
+ }
260
+ }
261
+ // Singleton instance
262
+ export const gpuCompute = new GPUCompute();
@@ -1,7 +1,7 @@
1
1
  export function downsampleBilinear({ image }: {
2
2
  image: any;
3
3
  }): {
4
- data: Float32Array<ArrayBuffer>;
4
+ data: Uint8Array<ArrayBuffer>;
5
5
  width: number;
6
6
  height: number;
7
7
  };
@@ -18,7 +18,7 @@ export function resize({ image, ratio }: {
18
18
  image: any;
19
19
  ratio: any;
20
20
  }): {
21
- data: Uint8Array<ArrayBuffer>;
22
- width: number;
23
- height: number;
21
+ data: Uint8Array<any>;
22
+ width: any;
23
+ height: any;
24
24
  };
@@ -1,23 +1,3 @@
1
- // simpler version of upsampling. better performance
2
- const _upsampleBilinear = ({ image, padOneWidth, padOneHeight }) => {
3
- const { width, height, data } = image;
4
- const dstWidth = image.width * 2 + (padOneWidth ? 1 : 0);
5
- const dstHeight = image.height * 2 + (padOneHeight ? 1 : 0);
6
- const temp = new Float32Array(dstWidth * dstHeight);
7
- for (let i = 0; i < width; i++) {
8
- for (let j = 0; j < height; j++) {
9
- const v = 0.25 * data[j * width + i];
10
- const ii = Math.floor(i / 2);
11
- const jj = Math.floor(j / 2);
12
- const pos = Math.floor(j / 2) * dstWidth + Math.floor(i / 2);
13
- temp[pos] += v;
14
- temp[pos + 1] += v;
15
- temp[pos + dstWidth] += v;
16
- temp[pos + dstWidth + 1] += v;
17
- }
18
- }
19
- return { data: temp, width: dstWidth, height: dstHeight };
20
- };
21
1
  // artoolkit version. slower. is it necessary?
22
2
  const upsampleBilinear = ({ image, padOneWidth, padOneHeight }) => {
23
3
  const { width, height, data } = image;
@@ -51,47 +31,81 @@ const upsampleBilinear = ({ image, padOneWidth, padOneHeight }) => {
51
31
  };
52
32
  const downsampleBilinear = ({ image }) => {
53
33
  const { data, width, height } = image;
54
- const dstWidth = Math.floor(width / 2);
55
- const dstHeight = Math.floor(height / 2);
56
- const temp = new Float32Array(dstWidth * dstHeight);
57
- const offsets = [0, 1, width, width + 1];
34
+ const dstWidth = width >>> 1; // Floor division by 2
35
+ const dstHeight = height >>> 1;
36
+ const temp = new Uint8Array(dstWidth * dstHeight);
37
+ // Cache width for fast indexing
38
+ const srcWidth = width | 0;
39
+ const srcRowStep = (srcWidth * 2) | 0;
40
+ let srcRowOffset = 0;
41
+ let dstIndex = 0;
58
42
  for (let j = 0; j < dstHeight; j++) {
43
+ let srcPos = srcRowOffset;
59
44
  for (let i = 0; i < dstWidth; i++) {
60
- let srcPos = j * 2 * width + i * 2;
61
- let value = 0.0;
62
- for (let d = 0; d < offsets.length; d++) {
63
- value += data[srcPos + offsets[d]];
64
- }
65
- value *= 0.25;
66
- temp[j * dstWidth + i] = value;
45
+ // Unrolled loop for performance
46
+ // (0,0), (1,0), (0,1), (1,1)
47
+ const value = (data[srcPos] +
48
+ data[srcPos + 1] +
49
+ data[srcPos + srcWidth] +
50
+ data[srcPos + srcWidth + 1]) * 0.25;
51
+ temp[dstIndex++] = value | 0; // Fast floor
52
+ srcPos += 2;
67
53
  }
54
+ srcRowOffset += srcRowStep;
68
55
  }
69
56
  return { data: temp, width: dstWidth, height: dstHeight };
70
57
  };
71
58
  const resize = ({ image, ratio }) => {
72
- const width = Math.round(image.width * ratio);
73
- const height = Math.round(image.height * ratio);
74
- //const imageData = new Float32Array(width * height);
59
+ // Fast path for identity
60
+ if (ratio === 1) {
61
+ return {
62
+ data: new Uint8Array(image.data), // Copy to be safe/consistent
63
+ width: image.width,
64
+ height: image.height
65
+ };
66
+ }
67
+ // Recursive downsampling for better quality on large reductions
68
+ if (ratio <= 0.5) {
69
+ // 1024 -> 512 -> ...
70
+ return resize({
71
+ image: downsampleBilinear({ image }),
72
+ ratio: ratio * 2
73
+ });
74
+ }
75
+ const width = Math.round(image.width * ratio) | 0;
76
+ const height = Math.round(image.height * ratio) | 0;
75
77
  const imageData = new Uint8Array(width * height);
76
- for (let i = 0; i < width; i++) {
77
- let si1 = Math.round((1.0 * i) / ratio);
78
- let si2 = Math.round((1.0 * (i + 1)) / ratio) - 1;
79
- if (si2 >= image.width)
80
- si2 = image.width - 1;
81
- for (let j = 0; j < height; j++) {
82
- let sj1 = Math.round((1.0 * j) / ratio);
83
- let sj2 = Math.round((1.0 * (j + 1)) / ratio) - 1;
84
- if (sj2 >= image.height)
85
- sj2 = image.height - 1;
86
- let sum = 0;
87
- let count = 0;
88
- for (let ii = si1; ii <= si2; ii++) {
89
- for (let jj = sj1; jj <= sj2; jj++) {
90
- sum += 1.0 * image.data[jj * image.width + ii];
91
- count += 1;
92
- }
93
- }
94
- imageData[j * width + i] = Math.floor(sum / count);
78
+ const srcData = image.data;
79
+ const srcW = image.width | 0;
80
+ const srcH = image.height | 0;
81
+ // Pre-calculate limits to avoid Math.min inside loop
82
+ const srcW_1 = (srcW - 1) | 0;
83
+ const srcH_1 = (srcH - 1) | 0;
84
+ let dstIndex = 0;
85
+ for (let j = 0; j < height; j++) {
86
+ // Y coords
87
+ const srcY = j / ratio;
88
+ const y0 = srcY | 0; // Math.floor
89
+ const y1 = (y0 < srcH_1 ? y0 + 1 : srcH_1) | 0;
90
+ const fy = srcY - y0;
91
+ const ify = 1 - fy;
92
+ // Row offsets
93
+ const row0 = (y0 * srcW) | 0;
94
+ const row1 = (y1 * srcW) | 0;
95
+ for (let i = 0; i < width; i++) {
96
+ // X coords
97
+ const srcX = i / ratio;
98
+ const x0 = srcX | 0; // Math.floor
99
+ const x1 = (x0 < srcW_1 ? x0 + 1 : srcW_1) | 0;
100
+ const fx = srcX - x0;
101
+ const ifx = 1 - fx;
102
+ // Bilinear interpolation optimized
103
+ // v = (1-fx)(1-fy)v00 + fx(1-fy)v10 + (1-fx)fy*v01 + fx*fy*v11
104
+ // Factored: (1-fy) * ((1-fx)v00 + fx*v10) + fy * ((1-fx)v01 + fx*v11)
105
+ const val0 = srcData[row0 + x0] * ifx + srcData[row0 + x1] * fx;
106
+ const val1 = srcData[row1 + x0] * ifx + srcData[row1 + x1] * fx;
107
+ const value = val0 * ify + val1 * fy;
108
+ imageData[dstIndex++] = value | 0;
95
109
  }
96
110
  }
97
111
  return { data: imageData, width: width, height: height };
@@ -0,0 +1,14 @@
1
+ export class WorkerPool {
2
+ constructor(workerPath: any, poolSize?: number);
3
+ workerPath: any;
4
+ poolSize: number;
5
+ workers: any[];
6
+ queue: any[];
7
+ activeWorkers: number;
8
+ runTask(taskData: any): Promise<any>;
9
+ _createWorker(): Worker;
10
+ _executeTask(worker: any, task: any): void;
11
+ _finishTask(worker: any, callback: any, result: any): void;
12
+ destroy(): Promise<void>;
13
+ }
14
+ import { Worker } from 'node:worker_threads';