@driftengine/capture 4.0.0

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 (199) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +29 -0
  3. package/README.md +68 -0
  4. package/dist/browserFrames.d.ts +31 -0
  5. package/dist/browserFrames.js +105 -0
  6. package/dist/collide.d.ts +59 -0
  7. package/dist/collide.js +92 -0
  8. package/dist/decimate.d.ts +42 -0
  9. package/dist/decimate.js +439 -0
  10. package/dist/delight.d.ts +42 -0
  11. package/dist/delight.js +351 -0
  12. package/dist/delightScene.d.ts +37 -0
  13. package/dist/delightScene.js +183 -0
  14. package/dist/depth.d.ts +44 -0
  15. package/dist/depth.js +53 -0
  16. package/dist/features.d.ts +33 -0
  17. package/dist/features.js +260 -0
  18. package/dist/frames.d.ts +47 -0
  19. package/dist/frames.js +87 -0
  20. package/dist/fusion.d.ts +77 -0
  21. package/dist/fusion.js +152 -0
  22. package/dist/gaussians/adam.d.ts +57 -0
  23. package/dist/gaussians/adam.js +88 -0
  24. package/dist/gaussians/depth.d.ts +8 -0
  25. package/dist/gaussians/depth.js +73 -0
  26. package/dist/gaussians/gradients.d.ts +36 -0
  27. package/dist/gaussians/gradients.js +228 -0
  28. package/dist/gaussians/harmonics.d.ts +46 -0
  29. package/dist/gaussians/harmonics.js +99 -0
  30. package/dist/gaussians/optimise.d.ts +50 -0
  31. package/dist/gaussians/optimise.js +125 -0
  32. package/dist/gaussians/parameters.d.ts +35 -0
  33. package/dist/gaussians/parameters.js +165 -0
  34. package/dist/gaussians/project.d.ts +72 -0
  35. package/dist/gaussians/project.js +137 -0
  36. package/dist/gaussians/projectGradient.d.ts +15 -0
  37. package/dist/gaussians/projectGradient.js +213 -0
  38. package/dist/gaussians/rasterise.d.ts +13 -0
  39. package/dist/gaussians/rasterise.js +65 -0
  40. package/dist/gaussians/refine.d.ts +36 -0
  41. package/dist/gaussians/refine.js +136 -0
  42. package/dist/gaussians/similarity.d.ts +25 -0
  43. package/dist/gaussians/similarity.js +80 -0
  44. package/dist/index.d.ts +101 -0
  45. package/dist/index.js +96 -0
  46. package/dist/marching.d.ts +38 -0
  47. package/dist/marching.js +258 -0
  48. package/dist/masks.d.ts +59 -0
  49. package/dist/masks.js +165 -0
  50. package/dist/math/dense.d.ts +68 -0
  51. package/dist/math/dense.js +294 -0
  52. package/dist/math/schur.d.ts +23 -0
  53. package/dist/math/schur.js +140 -0
  54. package/dist/models/batchNorm.d.ts +17 -0
  55. package/dist/models/batchNorm.js +24 -0
  56. package/dist/models/clipTokenizer.d.ts +40 -0
  57. package/dist/models/clipTokenizer.js +122 -0
  58. package/dist/models/clipTower.d.ts +27 -0
  59. package/dist/models/clipTower.js +45 -0
  60. package/dist/models/depthAnything2.d.ts +38 -0
  61. package/dist/models/depthAnything2.js +163 -0
  62. package/dist/models/depthAnything3.d.ts +26 -0
  63. package/dist/models/depthAnything3.js +171 -0
  64. package/dist/models/dualDpt.d.ts +19 -0
  65. package/dist/models/dualDpt.js +138 -0
  66. package/dist/models/hiera.d.ts +49 -0
  67. package/dist/models/hiera.js +120 -0
  68. package/dist/models/hieraAttention.d.ts +16 -0
  69. package/dist/models/hieraAttention.js +68 -0
  70. package/dist/models/miniature.d.ts +36 -0
  71. package/dist/models/miniature.js +216 -0
  72. package/dist/models/mobileSam.d.ts +41 -0
  73. package/dist/models/mobileSam.js +88 -0
  74. package/dist/models/owlv2.d.ts +49 -0
  75. package/dist/models/owlv2.js +131 -0
  76. package/dist/models/owlv2Detect.d.ts +25 -0
  77. package/dist/models/owlv2Detect.js +80 -0
  78. package/dist/models/owlv2Miniature.d.ts +27 -0
  79. package/dist/models/owlv2Miniature.js +87 -0
  80. package/dist/models/rope.d.ts +23 -0
  81. package/dist/models/rope.js +84 -0
  82. package/dist/models/sam21.d.ts +45 -0
  83. package/dist/models/sam21.js +126 -0
  84. package/dist/models/sam21Miniature.d.ts +37 -0
  85. package/dist/models/sam21Miniature.js +194 -0
  86. package/dist/models/sam21Video.d.ts +30 -0
  87. package/dist/models/sam21Video.js +101 -0
  88. package/dist/models/sam2Choice.d.ts +19 -0
  89. package/dist/models/sam2Choice.js +55 -0
  90. package/dist/models/sam2Decoder.d.ts +36 -0
  91. package/dist/models/sam2Decoder.js +108 -0
  92. package/dist/models/sam2Memories.d.ts +54 -0
  93. package/dist/models/sam2Memories.js +93 -0
  94. package/dist/models/sam2Memory.d.ts +30 -0
  95. package/dist/models/sam2Memory.js +136 -0
  96. package/dist/models/sam2Positions.d.ts +24 -0
  97. package/dist/models/sam2Positions.js +94 -0
  98. package/dist/models/sam2Tracker.d.ts +69 -0
  99. package/dist/models/sam2Tracker.js +172 -0
  100. package/dist/models/samDecoder.d.ts +45 -0
  101. package/dist/models/samDecoder.js +96 -0
  102. package/dist/models/samMiniature.d.ts +43 -0
  103. package/dist/models/samMiniature.js +161 -0
  104. package/dist/models/samPrompt.d.ts +57 -0
  105. package/dist/models/samPrompt.js +160 -0
  106. package/dist/models/seeded.d.ts +16 -0
  107. package/dist/models/seeded.js +48 -0
  108. package/dist/models/tinyVit.d.ts +38 -0
  109. package/dist/models/tinyVit.js +82 -0
  110. package/dist/models/twoWay.d.ts +46 -0
  111. package/dist/models/twoWay.js +67 -0
  112. package/dist/models/vit.d.ts +45 -0
  113. package/dist/models/vit.js +157 -0
  114. package/dist/models/windowAttention.d.ts +25 -0
  115. package/dist/models/windowAttention.js +104 -0
  116. package/dist/poses.d.ts +31 -0
  117. package/dist/poses.js +750 -0
  118. package/dist/prepare.d.ts +30 -0
  119. package/dist/prepare.js +238 -0
  120. package/dist/propose.d.ts +36 -0
  121. package/dist/propose.js +63 -0
  122. package/dist/resample.d.ts +39 -0
  123. package/dist/resample.js +165 -0
  124. package/dist/run.d.ts +3 -0
  125. package/dist/run.js +1 -0
  126. package/dist/scene.d.ts +104 -0
  127. package/dist/scene.js +109 -0
  128. package/dist/segment.d.ts +40 -0
  129. package/dist/segment.js +184 -0
  130. package/dist/testScene.d.ts +55 -0
  131. package/dist/testScene.js +214 -0
  132. package/dist/twoView.d.ts +22 -0
  133. package/dist/twoView.js +435 -0
  134. package/package.json +63 -0
  135. package/src/browserFrames.ts +145 -0
  136. package/src/collide.ts +128 -0
  137. package/src/decimate.ts +510 -0
  138. package/src/delight.ts +427 -0
  139. package/src/delightScene.ts +249 -0
  140. package/src/depth.ts +100 -0
  141. package/src/features.ts +302 -0
  142. package/src/frames.ts +111 -0
  143. package/src/fusion.ts +229 -0
  144. package/src/gaussians/adam.ts +131 -0
  145. package/src/gaussians/depth.ts +87 -0
  146. package/src/gaussians/gradients.ts +285 -0
  147. package/src/gaussians/harmonics.ts +122 -0
  148. package/src/gaussians/optimise.ts +191 -0
  149. package/src/gaussians/parameters.ts +186 -0
  150. package/src/gaussians/project.ts +207 -0
  151. package/src/gaussians/projectGradient.ts +234 -0
  152. package/src/gaussians/rasterise.ts +81 -0
  153. package/src/gaussians/refine.ts +176 -0
  154. package/src/gaussians/similarity.ts +87 -0
  155. package/src/index.ts +225 -0
  156. package/src/marching.ts +358 -0
  157. package/src/masks.ts +201 -0
  158. package/src/math/dense.ts +317 -0
  159. package/src/math/schur.ts +167 -0
  160. package/src/models/batchNorm.ts +57 -0
  161. package/src/models/clipTokenizer.ts +129 -0
  162. package/src/models/clipTower.ts +87 -0
  163. package/src/models/depthAnything2.ts +276 -0
  164. package/src/models/depthAnything3.ts +229 -0
  165. package/src/models/dualDpt.ts +233 -0
  166. package/src/models/hiera.ts +231 -0
  167. package/src/models/hieraAttention.ts +121 -0
  168. package/src/models/miniature.ts +241 -0
  169. package/src/models/mobileSam.ts +135 -0
  170. package/src/models/owlv2.ts +190 -0
  171. package/src/models/owlv2Detect.ts +116 -0
  172. package/src/models/owlv2Miniature.ts +108 -0
  173. package/src/models/rope.ts +111 -0
  174. package/src/models/sam21.ts +182 -0
  175. package/src/models/sam21Miniature.ts +225 -0
  176. package/src/models/sam21Video.ts +154 -0
  177. package/src/models/sam2Choice.ts +62 -0
  178. package/src/models/sam2Decoder.ts +218 -0
  179. package/src/models/sam2Memories.ts +165 -0
  180. package/src/models/sam2Memory.ts +247 -0
  181. package/src/models/sam2Positions.ts +106 -0
  182. package/src/models/sam2Tracker.ts +300 -0
  183. package/src/models/samDecoder.ts +225 -0
  184. package/src/models/samMiniature.ts +190 -0
  185. package/src/models/samPrompt.ts +227 -0
  186. package/src/models/seeded.ts +50 -0
  187. package/src/models/tinyVit.ts +178 -0
  188. package/src/models/twoWay.ts +122 -0
  189. package/src/models/vit.ts +265 -0
  190. package/src/models/windowAttention.ts +155 -0
  191. package/src/poses.ts +894 -0
  192. package/src/prepare.ts +306 -0
  193. package/src/propose.ts +96 -0
  194. package/src/resample.ts +197 -0
  195. package/src/run.ts +7 -0
  196. package/src/scene.ts +141 -0
  197. package/src/segment.ts +220 -0
  198. package/src/testScene.ts +283 -0
  199. package/src/twoView.ts +536 -0
@@ -0,0 +1,140 @@
1
+ /**
2
+ * The Schur complement over points: a bundle adjustment's normal equations solved by eliminating
3
+ * the points first, which leaves a system the size of the cameras.
4
+ *
5
+ * **It is an ordering, not an approximation.** A point appears in the rows of its own observations
6
+ * and nowhere else, so its three parameters can be solved for in terms of the cameras and
7
+ * substituted out exactly; what is left is the cameras' own system with a correction per link, and
8
+ * the points come back by back-substitution. The answer is the answer a dense solve would give —
9
+ * `schur.test.ts` holds it to one — and the saving is that a clip with a thousand points and twenty
10
+ * cameras solves a matrix of 120 rows rather than 3,120.
11
+ *
12
+ * **The damping is the caller's, applied here**, since it has to sit on the diagonal before the
13
+ * points are eliminated rather than after: `diagonal · (1 + λ) + λ`, which is Marquardt's scaling
14
+ * with a floor for a parameter whose curvature is zero.
15
+ *
16
+ * **A block that cannot be factored is refused rather than stepped through.** A point nothing
17
+ * constrains would be a division by nothing, and a reduced system that is singular means the
18
+ * cameras are not determined by what was seen — both are answers a caller must have, so they come
19
+ * back as false rather than as a step full of infinities.
20
+ */
21
+ import { cholesky, choleskySolve } from './dense.js';
22
+ const POINT = 3;
23
+ /**
24
+ * The step for `damping`, into `cameraStep` (`cameras · cameraSize`) and `pointStep`
25
+ * (`points · 3`); false where a point's block or the reduced system cannot be factored.
26
+ */
27
+ export function schurSolve(normals, damping, cameraStep, pointStep) {
28
+ const { cameras, cameraSize, points, links, linkCamera, linkPoint } = normals;
29
+ const width = cameras * cameraSize;
30
+ const linkCount = linkCamera.length;
31
+ /* Each point's own block, damped and factored once: everything below reads it. */
32
+ const factors = new Float64Array(points * POINT * POINT);
33
+ const block = new Float64Array(POINT * POINT);
34
+ for (let p = 0; p < points; p += 1) {
35
+ for (let r = 0; r < POINT; r += 1) {
36
+ for (let c = 0; c < POINT; c += 1) {
37
+ const value = normals.pointBlocks[(p * POINT + r) * POINT + c];
38
+ block[r * POINT + c] = r === c ? value * (1 + damping) + damping : value;
39
+ }
40
+ }
41
+ if (!cholesky(block, POINT, block))
42
+ return false;
43
+ factors.set(block, p * POINT * POINT);
44
+ }
45
+ /* The reduced system: the cameras' own blocks, less each link's share of its point. */
46
+ const reduced = new Float64Array(width * width);
47
+ const right = new Float64Array(width);
48
+ for (let c = 0; c < cameras; c += 1) {
49
+ for (let r = 0; r < cameraSize; r += 1) {
50
+ right[c * cameraSize + r] = normals.cameraGradient[c * cameraSize + r];
51
+ for (let k = 0; k < cameraSize; k += 1) {
52
+ const value = normals.cameraBlocks[(c * cameraSize + r) * cameraSize + k];
53
+ reduced[(c * cameraSize + r) * width + c * cameraSize + k] =
54
+ r === k ? value * (1 + damping) + damping : value;
55
+ }
56
+ }
57
+ }
58
+ /* Each link's `w · v⁻¹`, kept for the reduction and for the back-substitution after it. */
59
+ const solved = new Float64Array(linkCount * cameraSize * POINT);
60
+ const column = new Float64Array(POINT);
61
+ for (let link = 0; link < linkCount; link += 1) {
62
+ const point = linkPoint[link];
63
+ const factor = factors.subarray(point * POINT * POINT, (point + 1) * POINT * POINT);
64
+ for (let r = 0; r < cameraSize; r += 1) {
65
+ for (let k = 0; k < POINT; k += 1) {
66
+ column[k] = links[(link * cameraSize + r) * POINT + k];
67
+ }
68
+ choleskySolve(factor, POINT, column);
69
+ for (let k = 0; k < POINT; k += 1) {
70
+ solved[(link * cameraSize + r) * POINT + k] = column[k];
71
+ }
72
+ }
73
+ }
74
+ /* Two links on one point couple their cameras, which is where the reduction's off-diagonals are. */
75
+ for (let a = 0; a < linkCount; a += 1) {
76
+ for (let b = 0; b < linkCount; b += 1) {
77
+ if (linkPoint[a] !== linkPoint[b])
78
+ continue;
79
+ const rowCamera = linkCamera[a] * cameraSize;
80
+ const colCamera = linkCamera[b] * cameraSize;
81
+ for (let r = 0; r < cameraSize; r += 1) {
82
+ for (let c = 0; c < cameraSize; c += 1) {
83
+ let sum = 0;
84
+ for (let k = 0; k < POINT; k += 1) {
85
+ sum +=
86
+ solved[(a * cameraSize + r) * POINT + k] *
87
+ links[(b * cameraSize + c) * POINT + k];
88
+ }
89
+ reduced[(rowCamera + r) * width + colCamera + c] -= sum;
90
+ }
91
+ }
92
+ }
93
+ }
94
+ for (let link = 0; link < linkCount; link += 1) {
95
+ const camera = linkCamera[link] * cameraSize;
96
+ const point = linkPoint[link] * POINT;
97
+ for (let r = 0; r < cameraSize; r += 1) {
98
+ let sum = 0;
99
+ for (let k = 0; k < POINT; k += 1) {
100
+ sum +=
101
+ solved[(link * cameraSize + r) * POINT + k] *
102
+ normals.pointGradient[point + k];
103
+ }
104
+ right[camera + r] -= sum;
105
+ }
106
+ }
107
+ const factor = new Float64Array(width * width);
108
+ if (!cholesky(reduced, width, factor))
109
+ return false;
110
+ cameraStep.set(right);
111
+ choleskySolve(factor, width, cameraStep);
112
+ /* And the points come back: their own gradient, less what the cameras just took. */
113
+ for (let p = 0; p < points; p += 1) {
114
+ for (let k = 0; k < POINT; k += 1) {
115
+ pointStep[p * POINT + k] = normals.pointGradient[p * POINT + k];
116
+ }
117
+ }
118
+ for (let link = 0; link < linkCount; link += 1) {
119
+ const camera = linkCamera[link] * cameraSize;
120
+ const point = linkPoint[link] * POINT;
121
+ for (let k = 0; k < POINT; k += 1) {
122
+ let sum = 0;
123
+ for (let r = 0; r < cameraSize; r += 1) {
124
+ sum +=
125
+ links[(link * cameraSize + r) * POINT + k] *
126
+ cameraStep[camera + r];
127
+ }
128
+ pointStep[point + k] = pointStep[point + k] - sum;
129
+ }
130
+ }
131
+ for (let p = 0; p < points; p += 1) {
132
+ const factored = factors.subarray(p * POINT * POINT, (p + 1) * POINT * POINT);
133
+ for (let k = 0; k < POINT; k += 1)
134
+ column[k] = pointStep[p * POINT + k];
135
+ choleskySolve(factored, POINT, column);
136
+ for (let k = 0; k < POINT; k += 1)
137
+ pointStep[p * POINT + k] = column[k];
138
+ }
139
+ return true;
140
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * A convolution with the batch norm after it folded in, once, where the weights are converted.
3
+ *
4
+ * **Folded as the upstream's `Conv2d_BN.fuse` folds it**: the kernel scaled by `γ/√(var + ε)` per
5
+ * output, and the bias `β − mean·γ/√(var + ε)`. The converted file holds the folded pair under
6
+ * names of its own and none of the statistics, and the step counter a batch norm keeps is set aside
7
+ * by name. ε is PyTorch's default, 1e-5.
8
+ *
9
+ * What it gives up: the fold is computed in double precision and rounded once, where the upstream
10
+ * evaluates the norm after the convolution in single precision — the two agree to a rounding.
11
+ */
12
+ import type { GraphBuilder, Weights } from '@driftengine/texture';
13
+ /**
14
+ * `conv2d` of `x` by the convolution and batch norm under `p` — `c.` and `bn.`, the upstream's
15
+ * names — padded to keep the grid at stride one.
16
+ */
17
+ export declare function convBatchNorm(weights: Weights, graph: GraphBuilder, p: string, x: string, cin: number, cout: number, kernel: number, stride: number, groups: number): string;
@@ -0,0 +1,24 @@
1
+ const EPSILON = 1e-5;
2
+ /**
3
+ * `conv2d` of `x` by the convolution and batch norm under `p` — `c.` and `bn.`, the upstream's
4
+ * names — padded to keep the grid at stride one.
5
+ */
6
+ export function convBatchNorm(weights, graph, p, x, cin, cout, kernel, stride, groups) {
7
+ const shape = [cout, cin / groups, kernel, kernel];
8
+ const scale = (values) => {
9
+ const variance = values(`${p}bn.running_var`, [cout]);
10
+ return Array.from(values(`${p}bn.weight`, [cout]), (gamma, o) => gamma / Math.sqrt(variance[o] + EPSILON));
11
+ };
12
+ const weight = weights.derive(`${p}folded.weight`, shape, (values) => {
13
+ const factor = scale(values);
14
+ const per = (cin / groups) * kernel * kernel;
15
+ return Float32Array.from(values(`${p}c.weight`, shape), (w, i) => w * factor[Math.floor(i / per)]);
16
+ });
17
+ const bias = weights.derive(`${p}folded.bias`, [cout], (values) => {
18
+ const factor = scale(values);
19
+ const mean = values(`${p}bn.running_mean`, [cout]);
20
+ return Float32Array.from(values(`${p}bn.bias`, [cout]), (beta, o) => beta - mean[o] * factor[o]);
21
+ });
22
+ weights.ignore(`${p}bn.num_batches_tracked`);
23
+ return graph.node('conv2d', [x, weight, bias], { stride, padding: (kernel - 1) / 2, groups });
24
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * CLIP's tokenizer — byte-level BPE — as Transformers' `CLIPTokenizer` runs it at the revision the
3
+ * manifest pins, for OWLv2's text queries.
4
+ *
5
+ * **Its whole vocabulary is its merges.** A byte's token is the byte's place in GPT-2's order —
6
+ * the printable bytes first, then the rest — the same byte ending a word is 256 further on, the
7
+ * merge of rank r makes token 512 + r, and the start and end of text follow the last merge. So a
8
+ * converted file carries the merges as pairs of token ids and nothing else, and the conversion
9
+ * checks the upstream's `vocab.json` says the same.
10
+ *
11
+ * **In the upstream's order**: `!` and `<|endoftext|>` are taken out of the raw text as the added
12
+ * tokens they are — `!` is the padding token, 0, wherever it appears; the rest is composed (NFC)
13
+ * and lowercased a character at a time; `<|startoftext|>` is then taken out of that; what is left
14
+ * is split by CLIP's pattern into contractions, runs of letters, single digits and runs of anything
15
+ * else; each piece's UTF-8 bytes become tokens with the last marked as a word's end, and adjacent
16
+ * pairs merge lowest rank first, leftmost among equals. A pair listed twice ranks as its last, as
17
+ * the upstream's map of merges keeps it.
18
+ *
19
+ * **What it leaves out, and why that changes nothing**: the upstream also makes each run of
20
+ * whitespace one space, and splits again by GPT-2's pattern after CLIP's. No piece holds
21
+ * whitespace, so the first cannot change a token; and neither pattern crosses whitespace while
22
+ * every piece CLIP's makes is one GPT-2's matches whole, so the second divides nothing. What would
23
+ * make that wrong is a pattern change on either side, which the hand-run parity would show.
24
+ */
25
+ export interface ClipTokenizer {
26
+ /** The text's tokens, between the start and end of text. */
27
+ encode(text: string): number[];
28
+ }
29
+ /** A tokenizer over `merges`, `[count, 2]` token ids in rank order. */
30
+ export declare function clipTokenizer(merges: Float32Array): ClipTokenizer;
31
+ /**
32
+ * OWLv2's text queries as its text graph takes them: `tokens`, `[queries · positions]`, each query
33
+ * padded with zeros to `positions`, and `ends`, `[queries]`, the row of each query's end of text in
34
+ * that list — the upstream pools the row of the largest token, which is that one. A query longer
35
+ * than `positions` is refused, as the upstream's position table refuses it.
36
+ */
37
+ export declare function owlv2Tokens(tokenizer: ClipTokenizer, texts: readonly string[], positions: number): {
38
+ readonly tokens: Float32Array;
39
+ readonly ends: Float32Array;
40
+ };
@@ -0,0 +1,122 @@
1
+ /**
2
+ * CLIP's tokenizer — byte-level BPE — as Transformers' `CLIPTokenizer` runs it at the revision the
3
+ * manifest pins, for OWLv2's text queries.
4
+ *
5
+ * **Its whole vocabulary is its merges.** A byte's token is the byte's place in GPT-2's order —
6
+ * the printable bytes first, then the rest — the same byte ending a word is 256 further on, the
7
+ * merge of rank r makes token 512 + r, and the start and end of text follow the last merge. So a
8
+ * converted file carries the merges as pairs of token ids and nothing else, and the conversion
9
+ * checks the upstream's `vocab.json` says the same.
10
+ *
11
+ * **In the upstream's order**: `!` and `<|endoftext|>` are taken out of the raw text as the added
12
+ * tokens they are — `!` is the padding token, 0, wherever it appears; the rest is composed (NFC)
13
+ * and lowercased a character at a time; `<|startoftext|>` is then taken out of that; what is left
14
+ * is split by CLIP's pattern into contractions, runs of letters, single digits and runs of anything
15
+ * else; each piece's UTF-8 bytes become tokens with the last marked as a word's end, and adjacent
16
+ * pairs merge lowest rank first, leftmost among equals. A pair listed twice ranks as its last, as
17
+ * the upstream's map of merges keeps it.
18
+ *
19
+ * **What it leaves out, and why that changes nothing**: the upstream also makes each run of
20
+ * whitespace one space, and splits again by GPT-2's pattern after CLIP's. No piece holds
21
+ * whitespace, so the first cannot change a token; and neither pattern crosses whitespace while
22
+ * every piece CLIP's makes is one GPT-2's matches whole, so the second divides nothing. What would
23
+ * make that wrong is a pattern change on either side, which the hand-run parity would show.
24
+ */
25
+ /* CLIP's pieces: its specials, contractions, letter runs, one digit, and runs of anything else. */
26
+ const PIECES = /<\|startoftext\|>|<\|endoftext\|>|'s|'t|'re|'ve|'m|'ll|'d|\p{L}+|\p{N}|[^\s\p{L}\p{N}]+/gu;
27
+ /* Each byte's token: its place in GPT-2's order, printable bytes first. */
28
+ const BYTE_TOKEN = (() => {
29
+ const printable = (b) => (b >= 33 && b <= 126) || (b >= 161 && b <= 172) || (b >= 174 && b <= 255);
30
+ const table = new Uint16Array(256);
31
+ let next = 0;
32
+ for (let b = 0; b < 256; b += 1)
33
+ if (printable(b))
34
+ table[b] = next++;
35
+ for (let b = 0; b < 256; b += 1)
36
+ if (!printable(b))
37
+ table[b] = next++;
38
+ return table;
39
+ })();
40
+ const WORD_END = 256;
41
+ const MERGED = 512;
42
+ const PADDING = 0;
43
+ /** A tokenizer over `merges`, `[count, 2]` token ids in rank order. */
44
+ export function clipTokenizer(merges) {
45
+ const count = merges.length / 2;
46
+ const ranks = new Map();
47
+ for (let r = 0; r < count; r += 1) {
48
+ const key = merges[2 * r] * 65536 + merges[2 * r + 1];
49
+ ranks.set(key, r);
50
+ }
51
+ const start = MERGED + count;
52
+ const end = start + 1;
53
+ const bytes = new TextEncoder();
54
+ const word = (piece, out) => {
55
+ const symbols = Array.from(bytes.encode(piece), (b) => BYTE_TOKEN[b]);
56
+ symbols[symbols.length - 1] = symbols[symbols.length - 1] + WORD_END;
57
+ for (;;) {
58
+ let best = -1;
59
+ let rank = count;
60
+ for (let i = 0; i + 1 < symbols.length; i += 1) {
61
+ const r = ranks.get(symbols[i] * 65536 + symbols[i + 1]);
62
+ if (r !== undefined && r < rank) {
63
+ rank = r;
64
+ best = i;
65
+ }
66
+ }
67
+ if (best < 0)
68
+ break;
69
+ symbols.splice(best, 2, MERGED + rank);
70
+ }
71
+ out.push(...symbols);
72
+ };
73
+ const normalised = (text, out) => {
74
+ const lower = Array.from(text.normalize('NFC'), (c) => c.toLowerCase()).join('');
75
+ lower.split('<|startoftext|>').forEach((part, i) => {
76
+ if (i > 0)
77
+ out.push(start);
78
+ for (const [piece] of part.matchAll(PIECES))
79
+ word(piece, out);
80
+ });
81
+ };
82
+ return {
83
+ encode(text) {
84
+ const out = [start];
85
+ text.split('<|endoftext|>').forEach((part, i) => {
86
+ if (i > 0)
87
+ out.push(end);
88
+ part.split('!').forEach((run, j) => {
89
+ if (j > 0)
90
+ out.push(PADDING);
91
+ normalised(run, out);
92
+ });
93
+ });
94
+ out.push(end);
95
+ return out;
96
+ },
97
+ };
98
+ }
99
+ /**
100
+ * OWLv2's text queries as its text graph takes them: `tokens`, `[queries · positions]`, each query
101
+ * padded with zeros to `positions`, and `ends`, `[queries]`, the row of each query's end of text in
102
+ * that list — the upstream pools the row of the largest token, which is that one. A query longer
103
+ * than `positions` is refused, as the upstream's position table refuses it.
104
+ */
105
+ export function owlv2Tokens(tokenizer, texts, positions) {
106
+ const tokens = new Float32Array(texts.length * positions);
107
+ const ends = new Float32Array(texts.length);
108
+ texts.forEach((text, q) => {
109
+ const ids = tokenizer.encode(text);
110
+ if (ids.length > positions) {
111
+ throw new RangeError(`"${text}" is ${ids.length} tokens and the model reads ${positions}`);
112
+ }
113
+ tokens.set(ids, q * positions);
114
+ let largest = 0;
115
+ ids.forEach((id, i) => {
116
+ if (id > ids[largest])
117
+ largest = i;
118
+ });
119
+ ends[q] = q * positions + largest;
120
+ });
121
+ return { tokens, ends };
122
+ }
@@ -0,0 +1,27 @@
1
+ /**
2
+ * CLIP's encoder tower, which OWLv2's image and text graphs are each one of.
3
+ *
4
+ * **Pre-norm layers**: a layer norm, attention over the row's own window, and the projection added
5
+ * back; then a layer norm, a perceptron, and that added back. The activation is the upstream's
6
+ * quick GELU, `x·σ(1.702·x)`, which is the logistic and two multiplies rather than the error
7
+ * function `gelu` computes.
8
+ *
9
+ * **A causal tower masks its scores** with −10⁴ above the diagonal, where the upstream uses the
10
+ * least finite single. Both give a masked key a weight of exactly zero — exp of anything below
11
+ * −104 is zero in single precision, and scores stay within a few hundred of each other — and −10⁴
12
+ * is exact at half precision, where the upstream's value would be infinite and a device may assume
13
+ * no infinities. **What would make it wrong** is scores reaching thousands, which would need a
14
+ * checkpoint whose norms let them.
15
+ */
16
+ import type { GraphBuilder, Weights } from '@driftengine/texture';
17
+ export interface ClipTowerConfig {
18
+ readonly dim: number;
19
+ readonly heads: number;
20
+ readonly depth: number;
21
+ readonly hidden: number;
22
+ }
23
+ /**
24
+ * CLIP's layers over `x`, `[batch · rows, dim]`, each batch's rows attending among themselves —
25
+ * causally when `causal` is set.
26
+ */
27
+ export declare function clipLayers(weights: Weights, graph: GraphBuilder, prefix: string, config: ClipTowerConfig, x: string, batch: number, rows: number, causal: boolean): string;
@@ -0,0 +1,45 @@
1
+ const EPSILON = 1e-5;
2
+ const MASKED = -1e4;
3
+ /**
4
+ * CLIP's layers over `x`, `[batch · rows, dim]`, each batch's rows attending among themselves —
5
+ * causally when `causal` is set.
6
+ */
7
+ export function clipLayers(weights, graph, prefix, config, x, batch, rows, causal) {
8
+ const { dim, heads, hidden } = config;
9
+ const node = graph.node.bind(graph);
10
+ const w = (name, shape) => weights.read(`${prefix}${name}`, shape);
11
+ const linear = (value, p, out, inputs) => node('linear', [value, w(`${p}.weight`, [out, inputs]), w(`${p}.bias`, [out])]);
12
+ const norm = (value, p) => node('layerNorm', [value, w(`${p}.weight`, [dim]), w(`${p}.bias`, [dim])], {
13
+ epsilon: EPSILON,
14
+ });
15
+ const quick = weights.constant(`quick_gelu.${hidden}`, [hidden], new Float32Array(hidden).fill(1.702));
16
+ let mask = null;
17
+ if (causal) {
18
+ const table = new Float32Array(heads * rows * rows);
19
+ for (let h = 0; h < heads; h += 1) {
20
+ for (let q = 0; q < rows; q += 1) {
21
+ for (let k = q + 1; k < rows; k += 1)
22
+ table[(h * rows + q) * rows + k] = MASKED;
23
+ }
24
+ }
25
+ mask = weights.constant(`causal.${heads}x${rows}`, [heads, rows, rows], table);
26
+ }
27
+ const batched = (value) => batch > 1 || mask !== null ? node('reshape', [value], { shape: [batch, rows, dim] }) : value;
28
+ let out = x;
29
+ for (let i = 0; i < config.depth; i += 1) {
30
+ const p = `encoder.layers.${i}.`;
31
+ const h = norm(out, `${p}layer_norm1`);
32
+ const q = batched(linear(h, `${p}self_attn.q_proj`, dim, dim));
33
+ const k = batched(linear(h, `${p}self_attn.k_proj`, dim, dim));
34
+ const v = batched(linear(h, `${p}self_attn.v_proj`, dim, dim));
35
+ let attended = node('attention', mask === null ? [q, k, v] : [q, k, v, mask], { heads });
36
+ if (batch > 1 || mask !== null) {
37
+ attended = node('reshape', [attended], { shape: [batch * rows, dim] });
38
+ }
39
+ out = node('add', [out, linear(attended, `${p}self_attn.out_proj`, dim, dim)]);
40
+ const inner = linear(norm(out, `${p}layer_norm2`), `${p}mlp.fc1`, hidden, dim);
41
+ const activated = node('mul', [inner, node('sigmoid', [node('mul', [inner, quick])])]);
42
+ out = node('add', [out, linear(activated, `${p}mlp.fc2`, dim, hidden)]);
43
+ }
44
+ return out;
45
+ }
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Depth Anything V2 Small, the lighter fallback: plain DINOv2 and a DPT, in the Transformers layout
3
+ * its Apache-2.0 checkpoint is published in.
4
+ *
5
+ * **Plain where Depth Anything 3 is not**: one view, no camera token, no query-key norms, no rotary
6
+ * embedding, and positions resized to the grid by the ratio of the sizes, as Transformers' DINOv2
7
+ * resizes them. Four taps — after the third, sixth, ninth and twelfth blocks, each through the
8
+ * backbone's last norm — are reassembled into a pyramid and fused as Depth Anything 3's head fuses
9
+ * its own, without the sinusoidal positions that head adds. The output is one channel through a
10
+ * rectifier: relative inverse depth, larger nearer, with no scale.
11
+ *
12
+ * **The checkpoint keeps Transformers' older names** — `attention.attention.query`,
13
+ * `layer_scale1.lambda1` — which the library renames as it loads; this reads them as stored. Its
14
+ * mask token is for training and set aside, and so is the first fusion block's lateral unit, which
15
+ * the upstream builds and never runs because the coarsest level has no level below it.
16
+ */
17
+ import type { Architecture } from '@driftengine/texture';
18
+ export interface DepthAnything2Config {
19
+ readonly dim: number;
20
+ readonly heads: number;
21
+ readonly depth: number;
22
+ readonly hidden: number;
23
+ readonly patch: number;
24
+ readonly trainedGrid: number;
25
+ /** The blocks whose outputs are tapped, counted from zero. */
26
+ readonly taps: readonly [number, number, number, number];
27
+ readonly neck: readonly [number, number, number, number];
28
+ readonly fusion: number;
29
+ readonly headHidden: number;
30
+ }
31
+ /** The checkpoint's configuration at its pinned revision. */
32
+ export declare const DEPTH_ANYTHING_2_SMALL: DepthAnything2Config;
33
+ /**
34
+ * The graph for one image of `height × width` pixels, each a multiple of the patch, normalised by
35
+ * ImageNet's mean and deviation: input `image`, `[3, height, width]`; output `depth`,
36
+ * `[1, height, width]`.
37
+ */
38
+ export declare function depthAnything2(config: DepthAnything2Config, height: number, width: number): Architecture;
@@ -0,0 +1,163 @@
1
+ /** The checkpoint's configuration at its pinned revision. */
2
+ export const DEPTH_ANYTHING_2_SMALL = {
3
+ dim: 384,
4
+ heads: 6,
5
+ depth: 12,
6
+ hidden: 1536,
7
+ patch: 14,
8
+ trainedGrid: 37,
9
+ taps: [2, 5, 8, 11],
10
+ neck: [48, 96, 192, 384],
11
+ fusion: 64,
12
+ headHidden: 32,
13
+ };
14
+ /** Transformers' DINOv2 trains every norm at ε = 1e-6. */
15
+ const EPSILON = 1e-6;
16
+ /**
17
+ * The graph for one image of `height × width` pixels, each a multiple of the patch, normalised by
18
+ * ImageNet's mean and deviation: input `image`, `[3, height, width]`; output `depth`,
19
+ * `[1, height, width]`.
20
+ */
21
+ export function depthAnything2(config, height, width) {
22
+ const rows = height / config.patch;
23
+ const cols = width / config.patch;
24
+ if (!Number.isInteger(rows) || !Number.isInteger(cols)) {
25
+ throw new RangeError(`${height} × ${width} is not a whole number of ${config.patch}-pixel patches`);
26
+ }
27
+ return (weights, graph) => {
28
+ const { dim, heads, trainedGrid } = config;
29
+ const node = graph.node.bind(graph);
30
+ const w = (name, shape) => weights.read(name, shape);
31
+ const linear = (x, name, to, from) => node('linear', [x, w(`${name}.weight`, [to, from]), w(`${name}.bias`, [to])]);
32
+ const conv = (x, name, to, from, kernel, attributes = {}, bias = true) => node('conv2d', bias
33
+ ? [x, w(`${name}.weight`, [to, from, kernel, kernel]), w(`${name}.bias`, [to])]
34
+ : [x, w(`${name}.weight`, [to, from, kernel, kernel])], attributes);
35
+ weights.ignore('backbone.embeddings.mask_token');
36
+ weights.ignore('neck.fusion_stage.layers.0.residual_layer1.');
37
+ const e = 'backbone.embeddings.';
38
+ const patches = rows * cols;
39
+ const tokens = patches + 1;
40
+ const table = node('reshape', [w(`${e}position_embeddings`, [1, trainedGrid * trainedGrid + 1, dim])], {
41
+ shape: [trainedGrid * trainedGrid + 1, dim],
42
+ });
43
+ let positions = table;
44
+ if (rows !== trainedGrid || cols !== trainedGrid) {
45
+ const grid = node('permute', [
46
+ node('reshape', [node('slice', [table], { axis: 0, start: 1, end: trainedGrid * trainedGrid + 1 })], {
47
+ shape: [trainedGrid, trainedGrid, dim],
48
+ }),
49
+ ], { order: [2, 0, 1] });
50
+ const resized = node('resize', [grid], {
51
+ height: rows,
52
+ width: cols,
53
+ mode: 'bicubic',
54
+ alignCorners: false,
55
+ });
56
+ positions = node('concat', [
57
+ node('slice', [table], { axis: 0, start: 0, end: 1 }),
58
+ node('reshape', [node('permute', [resized], { order: [1, 2, 0] })], {
59
+ shape: [patches, dim],
60
+ }),
61
+ ], { axis: 0 });
62
+ }
63
+ const embedded = node('patchEmbed', [
64
+ 'image',
65
+ w(`${e}patch_embeddings.projection.weight`, [dim, 3, config.patch, config.patch]),
66
+ w(`${e}patch_embeddings.projection.bias`, [dim]),
67
+ ], { patch: config.patch });
68
+ let x = node('add', [
69
+ node('concat', [node('reshape', [w(`${e}cls_token`, [1, 1, dim])], { shape: [1, dim] }), embedded], { axis: 0 }),
70
+ positions,
71
+ ]);
72
+ const taps = [];
73
+ for (let i = 0; i < config.depth; i += 1) {
74
+ const p = `backbone.encoder.layer.${i}.`;
75
+ const h = node('layerNorm', [x, w(`${p}norm1.weight`, [dim]), w(`${p}norm1.bias`, [dim])], {
76
+ epsilon: EPSILON,
77
+ });
78
+ const attended = node('attention', [
79
+ linear(h, `${p}attention.attention.query`, dim, dim),
80
+ linear(h, `${p}attention.attention.key`, dim, dim),
81
+ linear(h, `${p}attention.attention.value`, dim, dim),
82
+ ], { heads });
83
+ const out = linear(attended, `${p}attention.output.dense`, dim, dim);
84
+ x = node('add', [x, node('mul', [out, w(`${p}layer_scale1.lambda1`, [dim])])]);
85
+ const h2 = node('layerNorm', [x, w(`${p}norm2.weight`, [dim]), w(`${p}norm2.bias`, [dim])], {
86
+ epsilon: EPSILON,
87
+ });
88
+ const mlp = linear(node('gelu', [linear(h2, `${p}mlp.fc1`, config.hidden, dim)]), `${p}mlp.fc2`, dim, config.hidden);
89
+ x = node('add', [x, node('mul', [mlp, w(`${p}layer_scale2.lambda1`, [dim])])]);
90
+ if (config.taps.includes(i)) {
91
+ taps.push(node('layerNorm', [x, w('backbone.layernorm.weight', [dim]), w('backbone.layernorm.bias', [dim])], { epsilon: EPSILON }));
92
+ }
93
+ }
94
+ const half = (size) => Math.floor((size - 1) / 2) + 1;
95
+ const sizes = [
96
+ [4 * rows, 4 * cols],
97
+ [2 * rows, 2 * cols],
98
+ [rows, cols],
99
+ [half(rows), half(cols)],
100
+ ];
101
+ const levels = taps.map((tap, k) => {
102
+ const channels = config.neck[k];
103
+ const r = `neck.reassemble_stage.layers.${k}.`;
104
+ const image = node('reshape', [
105
+ node('permute', [node('slice', [tap], { axis: 0, start: 1, end: tokens })], {
106
+ order: [1, 0],
107
+ }),
108
+ ], {
109
+ shape: [dim, rows, cols],
110
+ });
111
+ const projected = conv(image, `${r}projection`, channels, dim, 1);
112
+ const resized = k === 0
113
+ ? node('convTranspose2d', [
114
+ projected,
115
+ w(`${r}resize.weight`, [channels, channels, 4, 4]),
116
+ w(`${r}resize.bias`, [channels]),
117
+ ], { stride: 4 })
118
+ : k === 1
119
+ ? node('convTranspose2d', [
120
+ projected,
121
+ w(`${r}resize.weight`, [channels, channels, 2, 2]),
122
+ w(`${r}resize.bias`, [channels]),
123
+ ], { stride: 2 })
124
+ : k === 2
125
+ ? projected
126
+ : conv(projected, `${r}resize`, channels, channels, 3, { stride: 2, padding: 1 });
127
+ return conv(resized, `neck.convs.${k}`, config.fusion, channels, 3, { padding: 1 }, false);
128
+ });
129
+ const f = config.fusion;
130
+ const residual = (value, name) => {
131
+ const first = conv(node('relu', [value]), `${name}.convolution1`, f, f, 3, { padding: 1 });
132
+ return node('add', [
133
+ conv(node('relu', [first]), `${name}.convolution2`, f, f, 3, { padding: 1 }),
134
+ value,
135
+ ]);
136
+ };
137
+ /* Coarsest first: layer 0 takes the fourth level alone, and each after it adds the next finer. */
138
+ let fused = '';
139
+ for (let n = 0; n < 4; n += 1) {
140
+ const level = levels[3 - n];
141
+ const name = `neck.fusion_stage.layers.${n}`;
142
+ const merged = n === 0 ? level : node('add', [fused, residual(level, `${name}.residual_layer1`)]);
143
+ const refined = residual(merged, `${name}.residual_layer2`);
144
+ const size = n < 3
145
+ ? sizes[2 - n]
146
+ : [2 * sizes[0][0], 2 * sizes[0][1]];
147
+ const up = node('resize', [refined], {
148
+ height: size[0],
149
+ width: size[1],
150
+ mode: 'bilinear',
151
+ alignCorners: true,
152
+ });
153
+ fused = conv(up, `${name}.projection`, f, f, 1);
154
+ }
155
+ const first = conv(fused, 'head.conv1', f / 2, f, 3, { padding: 1 });
156
+ const up = node('resize', [first], { height, width, mode: 'bilinear', alignCorners: true });
157
+ const second = node('relu', [
158
+ conv(up, 'head.conv2', config.headHidden, f / 2, 3, { padding: 1 }),
159
+ ]);
160
+ node('relu', [conv(second, 'head.conv3', 1, config.headHidden, 1)], {}, 'depth');
161
+ return { inputs: [{ name: 'image', shape: [3, height, width] }], outputs: ['depth'] };
162
+ };
163
+ }
@@ -0,0 +1,26 @@
1
+ import type { Architecture } from '@driftengine/texture';
2
+ import { type HeadConfig } from './dualDpt.ts';
3
+ import { type VitConfig } from './vit.ts';
4
+ export interface DepthAnything3Config {
5
+ readonly backbone: VitConfig;
6
+ readonly head: HeadConfig;
7
+ }
8
+ /** The two accepted sizes, from each checkpoint's own configuration at its pinned revision. */
9
+ export declare const DEPTH_ANYTHING_3: Readonly<Record<'small' | 'base', DepthAnything3Config>>;
10
+ /**
11
+ * The graph for `views` images of `height × width` pixels, each a multiple of 14, normalised by
12
+ * ImageNet's mean and deviation. Inputs are `image0` … as `[3, height, width]`; outputs are
13
+ * `logits0` … as `[2, height, width]` and `pose` as `[views, 9]`. The first view is the reference.
14
+ */
15
+ export declare function depthAnything3(config: DepthAnything3Config, views: number, height: number, width: number): Architecture;
16
+ /**
17
+ * Depth and confidence from one view's logits, `[2, height, width]`: `exp` of the first channel,
18
+ * and one more than `exp` of the second, into the caller's maps.
19
+ */
20
+ export declare function decodeDepth(logits: Float32Array, depth: Float32Array, confidence: Float32Array): void;
21
+ /**
22
+ * One view's camera from its pose encoding: `worldToCamera`, 3 × 4 row-major — the inverse of the
23
+ * camera-to-world rotation and translation the decoder predicts — and `intrinsics`, 3 × 3 row-major,
24
+ * with focal lengths from the two fields of view and the principal point at the centre.
25
+ */
26
+ export declare function decodeCamera(pose: Float32Array, at: number, height: number, width: number, worldToCamera: Float32Array, intrinsics: Float32Array): void;