@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,171 @@
1
+ /**
2
+ * Depth Anything 3, as a definition the runtime builds a graph from at whatever size a clip needs.
3
+ *
4
+ * **From one to a handful of views at once, a depth map, its confidence and a camera per view.**
5
+ * The backbone is `vit.ts`, the head `dualDpt.ts`, and the camera decoder here: two layers over
6
+ * each view's camera token from the last tap, then a translation, a scalar-last quaternion and two
7
+ * fields of view. The graph ends at logits and at that pose encoding; `decodeDepth` and
8
+ * `decodeCamera` turn them into metres of relative depth, a confidence of at least one, a
9
+ * world-to-camera transform and a pinhole, as the upstream's own code does after its network.
10
+ *
11
+ * **What is set aside, by name**: the camera encoder, which conditions on poses a caller already
12
+ * has and this capture never does; and the head's ray branch, whose output the upstream discards
13
+ * whenever the camera decoder is present. Converting a checkpoint that holds anything else fails
14
+ * naming it.
15
+ *
16
+ * **What would make this wrong**: a checkpoint trained with another block layout — the upstream's
17
+ * large and giant models use a different feed-forward — which their configurations would say, and
18
+ * which are refused for their licences in any case.
19
+ */
20
+ import { exactCos, exactExp, exactSin } from '@driftengine/core';
21
+ import { dualDptHead } from './dualDpt.js';
22
+ import { vitBackbone } from './vit.js';
23
+ const PATCH = 14;
24
+ /** The two accepted sizes, from each checkpoint's own configuration at its pinned revision. */
25
+ export const DEPTH_ANYTHING_3 = {
26
+ small: {
27
+ backbone: {
28
+ dim: 384,
29
+ heads: 6,
30
+ depth: 12,
31
+ hidden: 1536,
32
+ patch: PATCH,
33
+ trainedGrid: 37,
34
+ taps: [5, 7, 9, 11],
35
+ altStart: 4,
36
+ qkNormStart: 4,
37
+ ropeStart: 4,
38
+ },
39
+ head: { dimIn: 768, features: 64, outChannels: [48, 96, 192, 384], outputDim: 2, patch: PATCH },
40
+ },
41
+ base: {
42
+ backbone: {
43
+ dim: 768,
44
+ heads: 12,
45
+ depth: 12,
46
+ hidden: 3072,
47
+ patch: PATCH,
48
+ trainedGrid: 37,
49
+ taps: [5, 7, 9, 11],
50
+ altStart: 4,
51
+ qkNormStart: 4,
52
+ ropeStart: 4,
53
+ },
54
+ head: {
55
+ dimIn: 1536,
56
+ features: 128,
57
+ outChannels: [96, 192, 384, 768],
58
+ outputDim: 2,
59
+ patch: PATCH,
60
+ },
61
+ },
62
+ };
63
+ /** What the upstream checkpoints hold and no graph here reads. */
64
+ const SET_ASIDE = [
65
+ 'model.cam_enc.',
66
+ 'model.head.scratch.output_conv1_aux.',
67
+ 'model.head.scratch.output_conv2_aux.',
68
+ 'model.head.scratch.refinenet1_aux.',
69
+ 'model.head.scratch.refinenet2_aux.',
70
+ 'model.head.scratch.refinenet3_aux.',
71
+ 'model.head.scratch.refinenet4_aux.',
72
+ ];
73
+ /**
74
+ * The graph for `views` images of `height × width` pixels, each a multiple of 14, normalised by
75
+ * ImageNet's mean and deviation. Inputs are `image0` … as `[3, height, width]`; outputs are
76
+ * `logits0` … as `[2, height, width]` and `pose` as `[views, 9]`. The first view is the reference.
77
+ */
78
+ export function depthAnything3(config, views, height, width) {
79
+ const rows = height / PATCH;
80
+ const cols = width / PATCH;
81
+ if (!Number.isInteger(rows) || !Number.isInteger(cols)) {
82
+ throw new RangeError(`${height} × ${width} is not a whole number of ${PATCH}-pixel patches`);
83
+ }
84
+ return (weights, graph) => {
85
+ for (const prefix of SET_ASIDE)
86
+ weights.ignore(prefix);
87
+ const images = Array.from({ length: views }, (_, v) => `image${v}`);
88
+ const { features, cameras } = vitBackbone(weights, graph, config.backbone, 'model.backbone.pretrained.', images, rows, cols);
89
+ const outputs = images.map((_, v) => dualDptHead(weights, graph, config.head, 'model.head.', features.map((tap) => tap[v]), rows, cols, `logits${v}`));
90
+ const width2 = 2 * config.backbone.dim;
91
+ const node = graph.node.bind(graph);
92
+ const layer = (x, name, to) => node('linear', [
93
+ x,
94
+ weights.read(`model.cam_dec.${name}.weight`, [to, width2]),
95
+ weights.read(`model.cam_dec.${name}.bias`, [to]),
96
+ ]);
97
+ const trunk = node('relu', [
98
+ layer(node('relu', [layer(cameras[cameras.length - 1], 'backbone.0', width2)]), 'backbone.2', width2),
99
+ ]);
100
+ node('concat', [
101
+ layer(trunk, 'fc_t', 3),
102
+ layer(trunk, 'fc_qvec', 4),
103
+ node('relu', [layer(trunk, 'fc_fov.0', 2)]),
104
+ ], { axis: 1 }, 'pose');
105
+ return {
106
+ inputs: images.map((name) => ({ name, shape: [3, height, width] })),
107
+ outputs: [...outputs, 'pose'],
108
+ };
109
+ };
110
+ }
111
+ /**
112
+ * Depth and confidence from one view's logits, `[2, height, width]`: `exp` of the first channel,
113
+ * and one more than `exp` of the second, into the caller's maps.
114
+ */
115
+ export function decodeDepth(logits, depth, confidence) {
116
+ const pixels = depth.length;
117
+ for (let i = 0; i < pixels; i += 1) {
118
+ depth[i] = exactExp(logits[i]);
119
+ confidence[i] = exactExp(logits[pixels + i]) + 1;
120
+ }
121
+ }
122
+ /**
123
+ * One view's camera from its pose encoding: `worldToCamera`, 3 × 4 row-major — the inverse of the
124
+ * camera-to-world rotation and translation the decoder predicts — and `intrinsics`, 3 × 3 row-major,
125
+ * with focal lengths from the two fields of view and the principal point at the centre.
126
+ */
127
+ export function decodeCamera(pose, at, height, width, worldToCamera, intrinsics) {
128
+ const p = at * 9;
129
+ const tx = pose[p];
130
+ const ty = pose[p + 1];
131
+ const tz = pose[p + 2];
132
+ const i = pose[p + 3];
133
+ const j = pose[p + 4];
134
+ const k = pose[p + 5];
135
+ const r = pose[p + 6];
136
+ const s = 2 / (i * i + j * j + k * k + r * r);
137
+ /*
138
+ * The camera-to-world rotation is `quat_to_mat`'s; its inverse is the transpose, so row `n` here
139
+ * is column `n` there, and the translation is carried back through it.
140
+ */
141
+ const r00 = 1 - s * (j * j + k * k);
142
+ const r01 = s * (i * j - k * r);
143
+ const r02 = s * (i * k + j * r);
144
+ const r10 = s * (i * j + k * r);
145
+ const r11 = 1 - s * (i * i + k * k);
146
+ const r12 = s * (j * k - i * r);
147
+ const r20 = s * (i * k - j * r);
148
+ const r21 = s * (j * k + i * r);
149
+ const r22 = 1 - s * (i * i + j * j);
150
+ worldToCamera[0] = r00;
151
+ worldToCamera[1] = r10;
152
+ worldToCamera[2] = r20;
153
+ worldToCamera[3] = -(r00 * tx + r10 * ty + r20 * tz);
154
+ worldToCamera[4] = r01;
155
+ worldToCamera[5] = r11;
156
+ worldToCamera[6] = r21;
157
+ worldToCamera[7] = -(r01 * tx + r11 * ty + r21 * tz);
158
+ worldToCamera[8] = r02;
159
+ worldToCamera[9] = r12;
160
+ worldToCamera[10] = r22;
161
+ worldToCamera[11] = -(r02 * tx + r12 * ty + r22 * tz);
162
+ const tangent = (angle) => exactSin(angle / 2) / exactCos(angle / 2);
163
+ const fy = height / 2 / Math.max(tangent(pose[p + 7]), 1e-6);
164
+ const fx = width / 2 / Math.max(tangent(pose[p + 8]), 1e-6);
165
+ intrinsics.fill(0);
166
+ intrinsics[0] = fx;
167
+ intrinsics[2] = width / 2;
168
+ intrinsics[4] = fy;
169
+ intrinsics[5] = height / 2;
170
+ intrinsics[8] = 1;
171
+ }
@@ -0,0 +1,19 @@
1
+ import type { GraphBuilder, Weights } from '@driftengine/texture';
2
+ export interface HeadConfig {
3
+ readonly dimIn: number;
4
+ readonly features: number;
5
+ readonly outChannels: readonly [number, number, number, number];
6
+ readonly outputDim: number;
7
+ readonly patch: number;
8
+ }
9
+ /**
10
+ * `[channels, height, width]`: the upstream's `_add_pos_embed` for a map of this size in an image of
11
+ * `aspect` width over height — sine and cosine of the horizontal coordinate, then of the vertical,
12
+ * over `channels / 4` frequencies each, times a tenth.
13
+ */
14
+ export declare function uvEmbedding(channels: number, height: number, width: number, aspect: number): Float32Array;
15
+ /**
16
+ * The head over one view's four taps, each `[rows · cols, dimIn]`; the result is the logits,
17
+ * `[outputDim, rows · patch, cols · patch]`, named `output`. Several views share its constants.
18
+ */
19
+ export declare function dualDptHead(weights: Weights, graph: GraphBuilder, config: HeadConfig, prefix: string, taps: readonly string[], rows: number, cols: number, output: string): string;
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Depth Anything 3's head, the DPT that turns four taps of tokens into depth and its confidence.
3
+ *
4
+ * **Each tap is normed, laid back out as an image, projected, and resized to its level of a
5
+ * pyramid** — four times the patch grid, twice, once, and half — then four fusion blocks walk the
6
+ * pyramid from coarse to fine, each adding the level below through a residual unit, and the result
7
+ * is brought up to the image by an aligned bilinear resize and read out by two convolutions: a
8
+ * depth logit and a confidence logit per pixel. Their activations, `exp` and `exp + 1`, are the
9
+ * caller's, applied to the two channels as they are read back, since the runtime has no `exp` and
10
+ * one pass over a map on the host costs less than a kernel for it.
11
+ *
12
+ * **A sinusoidal position is added three times**, after each projection and before the readout:
13
+ * a grid spanning the image's aspect normalised by its diagonal, as the upstream's `create_uv_grid`
14
+ * makes it, embedded at base 100 and scaled by a tenth. It depends only on shapes, so each is a
15
+ * constant of the graph.
16
+ *
17
+ * **The upstream's second head is not built.** It predicts rays for a pose estimate the camera
18
+ * decoder already makes, and the upstream deletes its output when that decoder is present, as it is
19
+ * in every accepted checkpoint; its weights are set aside by name.
20
+ */
21
+ import { exactCos, exactExp, exactSin } from '@driftengine/core';
22
+ import { LOG_BASE } from './rope.js';
23
+ /** The head's own norms keep PyTorch's default ε. */
24
+ const EPSILON = 1e-5;
25
+ const f = Math.fround;
26
+ /**
27
+ * `[channels, height, width]`: the upstream's `_add_pos_embed` for a map of this size in an image of
28
+ * `aspect` width over height — sine and cosine of the horizontal coordinate, then of the vertical,
29
+ * over `channels / 4` frequencies each, times a tenth.
30
+ */
31
+ export function uvEmbedding(channels, height, width, aspect) {
32
+ const diagonal = Math.sqrt(aspect * aspect + 1);
33
+ const spanX = aspect / diagonal;
34
+ const spanY = 1 / diagonal;
35
+ const coordinate = (span, count, i) => {
36
+ const edge = (span * (count - 1)) / count;
37
+ return count === 1 ? -edge : f(-edge + ((2 * edge) / (count - 1)) * i);
38
+ };
39
+ const quarter = channels / 4;
40
+ const omega = Float32Array.from({ length: quarter }, (_, k) => f(1 / f(exactExp(f(k / quarter) * LOG_BASE))));
41
+ const out = new Float32Array(channels * height * width);
42
+ for (let y = 0; y < height; y += 1) {
43
+ const v = coordinate(spanY, height, y);
44
+ for (let x = 0; x < width; x += 1) {
45
+ const u = coordinate(spanX, width, x);
46
+ for (let k = 0; k < quarter; k += 1) {
47
+ const au = f(u * omega[k]);
48
+ const av = f(v * omega[k]);
49
+ const at = y * width + x;
50
+ const plane = height * width;
51
+ out[k * plane + at] = f(f(exactSin(au)) * 0.1);
52
+ out[(quarter + k) * plane + at] = f(f(exactCos(au)) * 0.1);
53
+ out[(2 * quarter + k) * plane + at] = f(f(exactSin(av)) * 0.1);
54
+ out[(3 * quarter + k) * plane + at] = f(f(exactCos(av)) * 0.1);
55
+ }
56
+ }
57
+ }
58
+ return out;
59
+ }
60
+ /** The size a stride-2 3×3 convolution with padding 1 leaves. */
61
+ const halved = (size) => Math.floor((size - 1) / 2) + 1;
62
+ /**
63
+ * The head over one view's four taps, each `[rows · cols, dimIn]`; the result is the logits,
64
+ * `[outputDim, rows · patch, cols · patch]`, named `output`. Several views share its constants.
65
+ */
66
+ export function dualDptHead(weights, graph, config, prefix, taps, rows, cols, output) {
67
+ const node = graph.node.bind(graph);
68
+ const w = (name, shape) => weights.read(`${prefix}${name}`, shape);
69
+ const height = rows * config.patch;
70
+ const width = cols * config.patch;
71
+ const aspect = width / height;
72
+ const features = config.features;
73
+ const embed = (x, channels, h, wide) => node('add', [
74
+ x,
75
+ weights.constant(`uv.${channels}.${h}.${wide}`, [channels, h, wide], uvEmbedding(channels, h, wide, aspect)),
76
+ ]);
77
+ const conv = (x, name, from, to, kernel, attributes = {}, bias = true, named) => node('conv2d', bias
78
+ ? [x, w(`${name}.weight`, [to, from, kernel, kernel]), w(`${name}.bias`, [to])]
79
+ : [x, w(`${name}.weight`, [to, from, kernel, kernel])], attributes, named);
80
+ const sizes = [
81
+ [4 * rows, 4 * cols],
82
+ [2 * rows, 2 * cols],
83
+ [rows, cols],
84
+ [halved(rows), halved(cols)],
85
+ ];
86
+ const levels = taps.map((tap, k) => {
87
+ const channels = config.outChannels[k];
88
+ const normed = node('layerNorm', [tap, w('norm.weight', [config.dimIn]), w('norm.bias', [config.dimIn])], { epsilon: EPSILON });
89
+ const image = node('reshape', [node('permute', [normed], { order: [1, 0] })], {
90
+ shape: [config.dimIn, rows, cols],
91
+ });
92
+ const projected = embed(conv(image, `projects.${k}`, config.dimIn, channels, 1), channels, rows, cols);
93
+ const resized = k === 0
94
+ ? node('convTranspose2d', [
95
+ projected,
96
+ w('resize_layers.0.weight', [channels, channels, 4, 4]),
97
+ w('resize_layers.0.bias', [channels]),
98
+ ], { stride: 4 })
99
+ : k === 1
100
+ ? node('convTranspose2d', [
101
+ projected,
102
+ w('resize_layers.1.weight', [channels, channels, 2, 2]),
103
+ w('resize_layers.1.bias', [channels]),
104
+ ], { stride: 2 })
105
+ : k === 2
106
+ ? projected
107
+ : conv(projected, 'resize_layers.3', channels, channels, 3, { stride: 2, padding: 1 });
108
+ return conv(resized, `scratch.layer${k + 1}_rn`, channels, features, 3, { padding: 1 }, false);
109
+ });
110
+ const residual = (x, name) => {
111
+ const first = conv(node('relu', [x]), `${name}.conv1`, features, features, 3, { padding: 1 });
112
+ return node('add', [
113
+ conv(node('relu', [first]), `${name}.conv2`, features, features, 3, { padding: 1 }),
114
+ x,
115
+ ]);
116
+ };
117
+ const fuse = (level, top, lateral, size) => {
118
+ const name = `scratch.refinenet${level}`;
119
+ const merged = lateral === null ? top : node('add', [top, residual(lateral, `${name}.resConfUnit1`)]);
120
+ const refined = residual(merged, `${name}.resConfUnit2`);
121
+ const up = node('resize', [refined], {
122
+ height: size[0],
123
+ width: size[1],
124
+ mode: 'bilinear',
125
+ alignCorners: true,
126
+ });
127
+ return conv(up, `${name}.out_conv`, features, features, 1);
128
+ };
129
+ const [l1, l2, l3, l4] = levels;
130
+ let out = fuse(4, l4, null, sizes[2]);
131
+ out = fuse(3, out, l3, sizes[1]);
132
+ out = fuse(2, out, l2, sizes[0]);
133
+ out = fuse(1, out, l1, [2 * sizes[0][0], 2 * sizes[0][1]]);
134
+ out = conv(out, 'scratch.output_conv1', features, features / 2, 3, { padding: 1 });
135
+ const up = node('resize', [out], { height, width, mode: 'bilinear', alignCorners: true });
136
+ const read = conv(embed(up, features / 2, height, width), 'scratch.output_conv2.0', features / 2, 32, 3, { padding: 1 });
137
+ return conv(node('relu', [read]), 'scratch.output_conv2.2', 32, config.outputDim, 1, {}, true, output);
138
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Hiera and its feature pyramid, as SAM 2 runs them: an image in, three levels of features out.
3
+ *
4
+ * **Four stages of transformer blocks over a shrinking grid.** A 7×7 convolution at stride 4 takes
5
+ * the image to a quarter; each later stage's first block halves the grid by pooling its queries 2×2
6
+ * — within each window for attention, and over the whole grid for the residual, which a projection
7
+ * widens — so attention reads every key and writes a quarter as many queries. Blocks attend within
8
+ * windows, padded with zeros after the block's norm where the window does not divide the grid, and
9
+ * the blocks the configuration names attend over the whole grid instead. A first block partitions by
10
+ * the previous stage's window and unpartitions by half of it.
11
+ *
12
+ * **The positional embedding is computed in the graph, from its two weights**: a table resized
13
+ * bicubically to the grid, plus a window's table tiled across it. Laid out at conversion it would be
14
+ * 12.6 MB of the file at half precision; computed, it costs two permutes a run.
15
+ *
16
+ * **The pyramid runs top-down only into the levels the configuration names**: each stage's output is
17
+ * projected to the pyramid's width, and a named level adds the level above it doubled by nearest
18
+ * resizing. The two finest levels are then narrowed by the mask decoder's own projections, as the
19
+ * upstream's `get_image_features` narrows them once rather than on every prompt. Norms take ε 1e-6.
20
+ */
21
+ import type { GraphBuilder, Weights } from '@driftengine/texture';
22
+ type Four = readonly [number, number, number, number];
23
+ export interface HieraConfig {
24
+ readonly dims: Four;
25
+ readonly blocks: Four;
26
+ readonly heads: Four;
27
+ readonly windows: Four;
28
+ /** Block indices, counted across stages, that attend over the whole grid. */
29
+ readonly global: readonly number[];
30
+ /** The positional table's trained size, square. */
31
+ readonly background: number;
32
+ readonly mlpRatio: number;
33
+ /** The pyramid's width, and the levels (by stage) that take the level above them. */
34
+ readonly fpn: number;
35
+ readonly topDown: readonly number[];
36
+ }
37
+ export interface HieraOutput {
38
+ /** `[fpn, grid, grid]` at a sixteenth of the image: the features the decoder and memory read. */
39
+ readonly features: string;
40
+ /** `[fpn / 4, 2·grid, 2·grid]` and `[fpn / 8, 4·grid, 4·grid]`: the decoder's skips, narrowed. */
41
+ readonly high1: string;
42
+ readonly high0: string;
43
+ }
44
+ /**
45
+ * The encoder over `image`, `[3, size, size]` normalised as the upstream prepares it, writing its
46
+ * three outputs under the names given.
47
+ */
48
+ export declare function hiera(weights: Weights, graph: GraphBuilder, config: HieraConfig, image: string, size: number, names: HieraOutput): void;
49
+ export {};
@@ -0,0 +1,120 @@
1
+ import { hieraAttention } from './hieraAttention.js';
2
+ const EPSILON = 1e-6;
3
+ /**
4
+ * The encoder over `image`, `[3, size, size]` normalised as the upstream prepares it, writing its
5
+ * three outputs under the names given.
6
+ */
7
+ export function hiera(weights, graph, config, image, size, names) {
8
+ const node = graph.node.bind(graph);
9
+ const b = 'vision_encoder.backbone.';
10
+ const read = weights.read.bind(weights);
11
+ const linear = (x, p, out, inputs) => node('linear', [x, read(`${p}weight`, [out, inputs]), read(`${p}bias`, [out])]);
12
+ const norm = (x, p, dim) => node('layerNorm', [x, read(`${p}weight`, [dim]), read(`${p}bias`, [dim])], {
13
+ epsilon: EPSILON,
14
+ });
15
+ /* The patch embedding, then the positions, both channel-major. */
16
+ const [d0] = config.dims;
17
+ let grid = size / 4;
18
+ const patches = node('conv2d', [
19
+ image,
20
+ read(`${b}patch_embed.projection.weight`, [d0, 3, 7, 7]),
21
+ read(`${b}patch_embed.projection.bias`, [d0]),
22
+ ], { stride: 4, padding: 3 });
23
+ const window0 = config.windows[0];
24
+ const tiles = grid / window0;
25
+ const background = node('resize', [
26
+ node('reshape', [read(`${b}pos_embed`, [1, d0, config.background, config.background])], {
27
+ shape: [d0, config.background, config.background],
28
+ }),
29
+ ], { height: grid, width: grid, mode: 'bicubic', alignCorners: false });
30
+ /* The window's table added to every tile: tiles to the leading axes, the window to the last. */
31
+ const byTile = node('reshape', [
32
+ node('permute', [node('reshape', [background], { shape: [d0, tiles, window0, tiles, window0] })], { order: [1, 3, 0, 2, 4] }),
33
+ ], { shape: [tiles * tiles, d0 * window0 * window0] });
34
+ const window = node('reshape', [read(`${b}pos_embed_window`, [1, d0, window0, window0])], {
35
+ shape: [d0 * window0 * window0],
36
+ });
37
+ const positions = node('reshape', [
38
+ node('permute', [
39
+ node('reshape', [node('add', [byTile, window])], {
40
+ shape: [tiles, tiles, d0, window0, window0],
41
+ }),
42
+ ], { order: [2, 0, 3, 1, 4] }),
43
+ ], { shape: [d0, grid, grid] });
44
+ const embedded = node('add', [patches, positions]);
45
+ /* Tokens, row by row, `[grid · grid, dim]`, from here to the pyramid. */
46
+ let x = node('permute', [node('reshape', [embedded], { shape: [d0, grid * grid] })], {
47
+ order: [1, 0],
48
+ });
49
+ const pooled = (tokens, rows, dim) => {
50
+ const map = node('reshape', [node('permute', [tokens], { order: [1, 0] })], {
51
+ shape: [dim, rows, rows],
52
+ });
53
+ const pool = node('maxPool2d', [map], { kernel: 2 });
54
+ return node('permute', [node('reshape', [pool], { shape: [dim, (rows / 2) * (rows / 2)] })], {
55
+ order: [1, 0],
56
+ });
57
+ };
58
+ const outputs = [];
59
+ let index = 0;
60
+ for (let stage = 0; stage < 4; stage += 1) {
61
+ for (let block = 0; block < config.blocks[stage]; block += 1) {
62
+ const p = `${b}blocks.${index}.`;
63
+ const first = stage > 0 && block === 0;
64
+ const dim = first ? config.dims[stage - 1] : config.dims[stage];
65
+ const out = config.dims[stage];
66
+ const heads = config.heads[stage];
67
+ const side = config.global.includes(index)
68
+ ? 0
69
+ : first
70
+ ? config.windows[stage - 1]
71
+ : config.windows[stage];
72
+ const normed = norm(x, `${p}layer_norm1.`, dim);
73
+ const residual = first ? pooled(linear(normed, `${p}proj.`, out, dim), grid, out) : x;
74
+ const attended = hieraAttention(weights, graph, p, normed, grid, dim, out, heads, side, first);
75
+ if (first)
76
+ grid /= 2;
77
+ x = node('add', [residual, attended]);
78
+ const hidden = node('gelu', [
79
+ linear(norm(x, `${p}layer_norm2.`, out), `${p}mlp.proj_in.`, out * config.mlpRatio, out),
80
+ ]);
81
+ x = node('add', [x, linear(hidden, `${p}mlp.proj_out.`, out, out * config.mlpRatio)]);
82
+ index += 1;
83
+ }
84
+ outputs.push(x);
85
+ }
86
+ /* The pyramid, coarsest first: each stage projected, and a named level adding the one above. */
87
+ const n = 3;
88
+ const sides = [size / 4, size / 8, size / 16, size / 32];
89
+ const levels = [];
90
+ let previous = '';
91
+ for (let i = n; i >= 0; i -= 1) {
92
+ const p = `vision_encoder.neck.convs.${n - i}.`;
93
+ const channels = config.dims[i];
94
+ const side = sides[i];
95
+ const map = node('reshape', [node('permute', [outputs[i]], { order: [1, 0] })], {
96
+ shape: [channels, side, side],
97
+ });
98
+ const down = config.topDown.includes(i) && i !== n;
99
+ const name = i === 2 ? names.features : undefined;
100
+ let lateral = node('conv2d', [map, read(`${p}weight`, [config.fpn, channels, 1, 1]), read(`${p}bias`, [config.fpn])], {}, down ? undefined : name);
101
+ if (down) {
102
+ const above = node('resize', [previous], { height: side, width: side, mode: 'nearest' });
103
+ lateral = node('add', [lateral, above], {}, name);
104
+ }
105
+ previous = lateral;
106
+ levels[i] = lateral;
107
+ }
108
+ const d = 'mask_decoder.';
109
+ node('conv2d', [
110
+ levels[0],
111
+ read(`${d}conv_s0.weight`, [config.fpn / 8, config.fpn, 1, 1]),
112
+ read(`${d}conv_s0.bias`, [config.fpn / 8]),
113
+ ], {}, names.high0);
114
+ node('conv2d', [
115
+ levels[1],
116
+ read(`${d}conv_s1.weight`, [config.fpn / 4, config.fpn, 1, 1]),
117
+ read(`${d}conv_s1.bias`, [config.fpn / 4]),
118
+ ], {}, names.high1);
119
+ /* The coarsest level feeds the one below and is not itself an output, as the upstream keeps three. */
120
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * One Hiera block's attention: within windows of its grid, or over the whole grid, its queries
3
+ * pooled 2×2 within each window where the block changes stage.
4
+ *
5
+ * **Windows are partitioned after the block's norm**, padded with zeros where the window does not
6
+ * divide the grid — so a padded token is zero, where TinyViT's is its norm's β — and unpartitioned
7
+ * by the queries' window, the padding recomputed for the pooled grid, as the upstream's
8
+ * `window_unpartition` is given it. The projection lays each head's queries, keys and values out
9
+ * contiguously, as the attention operator takes them.
10
+ */
11
+ import type { GraphBuilder, Weights } from '@driftengine/texture';
12
+ /**
13
+ * `[rows² or (rows/2)², out]` from `normed`, `[rows², dim]`: the attention under `p` within windows
14
+ * of `side` — the whole grid for 0 — pooling its queries when `pool`.
15
+ */
16
+ export declare function hieraAttention(weights: Weights, graph: GraphBuilder, p: string, normed: string, rows: number, dim: number, out: number, heads: number, side: number, pool: boolean): string;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * `[rows² or (rows/2)², out]` from `normed`, `[rows², dim]`: the attention under `p` within windows
3
+ * of `side` — the whole grid for 0 — pooling its queries when `pool`.
4
+ */
5
+ export function hieraAttention(weights, graph, p, normed, rows, dim, out, heads, side, pool) {
6
+ const node = graph.node.bind(graph);
7
+ const linear = (x, q, outputs, inputs) => node('linear', [
8
+ x,
9
+ weights.read(`${q}weight`, [outputs, inputs]),
10
+ weights.read(`${q}bias`, [outputs]),
11
+ ]);
12
+ const span = side === 0 ? rows : side;
13
+ const pad = (span - (rows % span)) % span;
14
+ const across = (rows + pad) / span;
15
+ const count = across * across;
16
+ let tokens = normed;
17
+ if (side > 0) {
18
+ let map = node('reshape', [normed], { shape: [rows, rows, dim] });
19
+ if (pad > 0)
20
+ map = node('pad', [map], { after: [pad, pad, 0] });
21
+ tokens = node('reshape', [
22
+ node('permute', [node('reshape', [map], { shape: [across, span, across, span, dim] })], {
23
+ order: [0, 2, 1, 3, 4],
24
+ }),
25
+ ], { shape: [count * span * span, dim] });
26
+ }
27
+ const qkv = linear(tokens, `${p}attn.qkv.`, 3 * out, dim);
28
+ const part = (i) => node('slice', [qkv], { axis: 1, start: i * out, end: (i + 1) * out });
29
+ let queries = node('reshape', [part(0)], { shape: [count, span * span, out] });
30
+ let qSpan = span;
31
+ if (pool) {
32
+ /* Each window's queries as a map, pooled 2×2: windows and channels together as the pool's planes. */
33
+ const maps = node('reshape', [
34
+ node('permute', [node('reshape', [part(0)], { shape: [count, span, span, out] })], {
35
+ order: [0, 3, 1, 2],
36
+ }),
37
+ ], { shape: [count * out, span, span] });
38
+ qSpan = span / 2;
39
+ queries = node('reshape', [
40
+ node('permute', [
41
+ node('reshape', [node('maxPool2d', [maps], { kernel: 2 })], {
42
+ shape: [count, out, qSpan, qSpan],
43
+ }),
44
+ ], { order: [0, 2, 3, 1] }),
45
+ ], { shape: [count, qSpan * qSpan, out] });
46
+ }
47
+ const keys = node('reshape', [part(1)], { shape: [count, span * span, out] });
48
+ const values = node('reshape', [part(2)], { shape: [count, span * span, out] });
49
+ const attended = node('attention', [queries, keys, values], { heads });
50
+ const projected = linear(node('reshape', [attended], { shape: [count * qSpan * qSpan, out] }), `${p}attn.proj.`, out, out);
51
+ if (side === 0)
52
+ return projected;
53
+ /* Unpartitioned by the queries' window, the padding recomputed for the pooled grid. */
54
+ const pooledRows = pool ? rows / 2 : rows;
55
+ const qPad = (qSpan - (pooledRows % qSpan)) % qSpan;
56
+ const qAcross = (pooledRows + qPad) / qSpan;
57
+ let map = node('reshape', [
58
+ node('permute', [node('reshape', [projected], { shape: [qAcross, qAcross, qSpan, qSpan, out] })], { order: [0, 2, 1, 3, 4] }),
59
+ ], { shape: [pooledRows + qPad, pooledRows + qPad, out] });
60
+ if (qPad > 0) {
61
+ map = node('slice', [node('slice', [map], { axis: 0, start: 0, end: pooledRows })], {
62
+ axis: 1,
63
+ start: 0,
64
+ end: pooledRows,
65
+ });
66
+ }
67
+ return node('reshape', [map], { shape: [pooledRows * pooledRows, out] });
68
+ }
@@ -0,0 +1,36 @@
1
+ import type { GraphTensor } from '@driftengine/texture';
2
+ import type { DepthAnything2Config } from './depthAnything2.ts';
3
+ import type { DepthAnything3Config } from './depthAnything3.ts';
4
+ /**
5
+ * Four blocks of 32 channels in four heads of 8 — the smallest a two-dimensional rotary embedding
6
+ * divides — with the camera token, query-key norms and rotary embedding from the third block, so
7
+ * one within-view and one across-view block each carry all three; a trained grid of 2, so any other
8
+ * grid is resized; and a head of 16 features.
9
+ */
10
+ export declare const MINIATURE_DEPTH_ANYTHING_3: DepthAnything3Config;
11
+ /** The seed the tests and the oracle share. */
12
+ export declare const MINIATURE_SEED = 20260919;
13
+ /** One view on a grid its positions are resized to, and two views on the trained grid. */
14
+ export declare const MINIATURE_CASES: {
15
+ readonly single: {
16
+ readonly views: 1;
17
+ readonly height: 28;
18
+ readonly width: 42;
19
+ };
20
+ readonly pair: {
21
+ readonly views: 2;
22
+ readonly height: 28;
23
+ readonly width: 28;
24
+ };
25
+ };
26
+ /** Every tensor the upstream's backbone, head and camera decoder hold, named as the checkpoints are. */
27
+ export declare function miniatureCheckpoint(config: DepthAnything3Config, seed: number): Map<string, GraphTensor>;
28
+ /**
29
+ * Depth Anything V2 in miniature: four blocks of 32 channels in four heads, every block tapped, a
30
+ * trained grid of 2, and a neck and head of 16 and 8.
31
+ */
32
+ export declare const MINIATURE_DEPTH_ANYTHING_2: DepthAnything2Config;
33
+ /** Every tensor Transformers' Depth Anything holds, in the older names its checkpoints keep. */
34
+ export declare function miniatureCheckpoint2(config: DepthAnything2Config, seed: number): Map<string, GraphTensor>;
35
+ /** Seeded images for `views` views, normalised as the model expects, `[3, height, width]` each. */
36
+ export declare function miniatureImages(views: number, height: number, width: number, seed: number): Float32Array[];