@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,108 @@
1
+ /**
2
+ * A seeded miniature of OWLv2: every tensor name of the upstream's detector, at a size the CPU
3
+ * evaluates in a moment — what the model's tests and its hand-run oracle share.
4
+ *
5
+ * **Small where the upstream lets it be**: a 64-pixel image in patches of 16, so a grid of four;
6
+ * towers two layers deep of 32 and 24 channels in two heads. **The text keeps
7
+ * the upstream's 16 positions**, since a query's length is what its causal mask and its pooling are
8
+ * about, and the vocabulary is three merges' worth — 517 tokens — which is also the tokenizer's
9
+ * hand-worked table (`clipTokenizer.test.ts`).
10
+ */
11
+ import type { GraphTensor } from '@driftengine/texture';
12
+
13
+ import type { Owlv2Config } from './owlv2.ts';
14
+ import { draw } from './seeded.ts';
15
+
16
+ export const MINIATURE_OWLV2: Owlv2Config = {
17
+ size: 64,
18
+ patch: 16,
19
+ vision: { dim: 32, heads: 2, depth: 2, hidden: 48 },
20
+ text: { dim: 24, heads: 2, depth: 2, hidden: 40, positions: 16 },
21
+ };
22
+
23
+ /** c+a, ca+t</w> and a+t</w>: tokens 512 to 514, and the start and end of text 515 and 516. */
24
+ export const MINIATURE_OWLV2_MERGES = Float32Array.of(66, 64, 512, 339, 64, 339);
25
+
26
+ /**
27
+ * Three queries as token ids: one ending early, one with the padding token inside it — the "!" a
28
+ * text may hold — and one filling every position.
29
+ */
30
+ export const MINIATURE_OWLV2_QUERIES: readonly (readonly number[])[] = [
31
+ [515, 513, 516],
32
+ [515, 83, 514, 0, 320, 513, 516],
33
+ [515, 10, 200, 300, 400, 500, 511, 512, 1, 2, 3, 100, 250, 350, 450, 516],
34
+ ];
35
+
36
+ /** The original image's size the boxes are scaled to: wider than tall, padded below. */
37
+ export const MINIATURE_OWLV2_IMAGE = { height: 36, width: 48 } as const;
38
+
39
+ /** Every tensor the upstream's `Owlv2ForObjectDetection` holds, named as its checkpoints are. */
40
+ export function miniatureOwlv2Checkpoint(
41
+ config: Owlv2Config,
42
+ seed: number,
43
+ ): Map<string, GraphTensor> {
44
+ const out = new Map<string, GraphTensor>();
45
+ const add = (name: string, shape: readonly number[]): void => {
46
+ out.set(name, { shape, data: draw(name, shape, seed) });
47
+ };
48
+ const tower = (prefix: string, dim: number, hidden: number, depth: number): void => {
49
+ for (let i = 0; i < depth; i += 1) {
50
+ const p = `${prefix}encoder.layers.${i}.`;
51
+ for (const norm of ['layer_norm1', 'layer_norm2']) {
52
+ add(`${p}${norm}.weight`, [dim]);
53
+ add(`${p}${norm}.bias`, [dim]);
54
+ }
55
+ for (const projection of ['q_proj', 'k_proj', 'v_proj', 'out_proj']) {
56
+ add(`${p}self_attn.${projection}.weight`, [dim, dim]);
57
+ add(`${p}self_attn.${projection}.bias`, [dim]);
58
+ }
59
+ add(`${p}mlp.fc1.weight`, [hidden, dim]);
60
+ add(`${p}mlp.fc1.bias`, [hidden]);
61
+ add(`${p}mlp.fc2.weight`, [dim, hidden]);
62
+ add(`${p}mlp.fc2.bias`, [dim]);
63
+ }
64
+ };
65
+ const { vision, text, patch } = config;
66
+ const cells = (config.size / patch) * (config.size / patch);
67
+ const t = 'owlv2.text_model.';
68
+ add(`${t}embeddings.token_embedding.weight`, [
69
+ 512 + MINIATURE_OWLV2_MERGES.length / 2 + 2,
70
+ text.dim,
71
+ ]);
72
+ add(`${t}embeddings.position_embedding.weight`, [text.positions, text.dim]);
73
+ tower(t, text.dim, text.hidden, text.depth);
74
+ add(`${t}final_layer_norm.weight`, [text.dim]);
75
+ add(`${t}final_layer_norm.bias`, [text.dim]);
76
+ const v = 'owlv2.vision_model.';
77
+ add(`${v}embeddings.class_embedding`, [vision.dim]);
78
+ add(`${v}embeddings.patch_embedding.weight`, [vision.dim, 3, patch, patch]);
79
+ add(`${v}embeddings.position_embedding.weight`, [cells + 1, vision.dim]);
80
+ for (const norm of ['pre_layernorm', 'post_layernorm']) {
81
+ add(`${v}${norm}.weight`, [vision.dim]);
82
+ add(`${v}${norm}.bias`, [vision.dim]);
83
+ }
84
+ tower(v, vision.dim, vision.hidden, vision.depth);
85
+ add('owlv2.visual_projection.weight', [text.dim, vision.dim]);
86
+ add('owlv2.text_projection.weight', [text.dim, text.dim]);
87
+ add('owlv2.logit_scale', []);
88
+ add('layer_norm.weight', [vision.dim]);
89
+ add('layer_norm.bias', [vision.dim]);
90
+ add('class_head.dense0.weight', [text.dim, vision.dim]);
91
+ add('class_head.dense0.bias', [text.dim]);
92
+ for (const logit of ['logit_shift', 'logit_scale']) {
93
+ add(`class_head.${logit}.weight`, [1, vision.dim]);
94
+ add(`class_head.${logit}.bias`, [1]);
95
+ }
96
+ for (const [head, last] of [
97
+ ['box_head', 4],
98
+ ['objectness_head', 1],
99
+ ] as const) {
100
+ add(`${head}.dense0.weight`, [vision.dim, vision.dim]);
101
+ add(`${head}.dense0.bias`, [vision.dim]);
102
+ add(`${head}.dense1.weight`, [vision.dim, vision.dim]);
103
+ add(`${head}.dense1.bias`, [vision.dim]);
104
+ add(`${head}.dense2.weight`, [last, vision.dim]);
105
+ add(`${head}.dense2.bias`, [last]);
106
+ }
107
+ return out;
108
+ }
@@ -0,0 +1,111 @@
1
+ /**
2
+ * Two-dimensional rotary position embedding, as Depth Anything 3's backbone applies it to queries
3
+ * and keys, composed from the runtime's operators.
4
+ *
5
+ * **The rotation is `x·cos + rotate(x)·sin`, per head, with a head's first half turned by the
6
+ * token's row and its second half by its column.** Within each half, `rotate` swaps the two
7
+ * quarters and negates the first — so with the sign folded into the sine table it is a swap, and a
8
+ * swap is two slices and a concatenation. The tables depend only on positions, which the graph's
9
+ * shapes fix, so they are constants of the graph, rebuilt at every size.
10
+ *
11
+ * **What it gives up is a dispatch count**: eight small copies a rotation where a kernel of its own
12
+ * would take one. At a depth model's shapes each is a few microseconds against an attention of a
13
+ * millisecond and a half, and every operator used here already has a device bound; a `rope`
14
+ * operator is the step to take when a measurement says the copies matter.
15
+ *
16
+ * **The tables are computed in single precision, operation by operation**, as the upstream's are:
17
+ * a frequency as `100^(k/16)` rounded, an angle as position times frequency rounded, then its
18
+ * cosine and sine. Computing them in double and rounding once would move an angle near 37 by
19
+ * two millionths, which a parity check would then have to allow for. The transcendentals are the
20
+ * engine's reproducible ones, so the tables are the same bits in every engine.
21
+ */
22
+ import { exactCos, exactExp, exactSin } from '@driftengine/core';
23
+ import type { GraphBuilder, Weights } from '@driftengine/texture';
24
+
25
+ const f = Math.fround;
26
+
27
+ /*
28
+ * The base every rotary and sinusoidal embedding in these models is built on, as its logarithm:
29
+ * `100^e` is `exp(e · ln 100)`, through the engine's reproducible `exp`, since neither `**` nor
30
+ * `Math.pow` gives the same bits on every engine and a capture reproduces.
31
+ */
32
+ export const LOG_BASE = 4.605170185988092;
33
+
34
+ export interface RopeTables {
35
+ /** `[tokens, heads · headDim]`: the cosine of each channel's angle. */
36
+ readonly cos: Float32Array;
37
+ /** The same shape: the sine, negated where `rotate` negates. */
38
+ readonly sin: Float32Array;
39
+ }
40
+
41
+ /**
42
+ * The tables for tokens at `positions` — a row and a column each, as integers — over `heads` of
43
+ * `headDim` channels, at the upstream's base of 100.
44
+ */
45
+ export function ropeTables(positions: Int32Array, heads: number, headDim: number): RopeTables {
46
+ const tokens = positions.length / 2;
47
+ const half = headDim / 2;
48
+ const quarter = half / 2;
49
+ const inverse = new Float32Array(quarter);
50
+ for (let k = 0; k < quarter; k += 1) {
51
+ inverse[k] = f(1 / f(exactExp(f((2 * k) / half) * LOG_BASE)));
52
+ }
53
+ const width = heads * headDim;
54
+ const cos = new Float32Array(tokens * width);
55
+ const sin = new Float32Array(tokens * width);
56
+ for (let t = 0; t < tokens; t += 1) {
57
+ for (let axis = 0; axis < 2; axis += 1) {
58
+ const position = positions[2 * t + axis] as number;
59
+ for (let j = 0; j < half; j += 1) {
60
+ const angle = f(position * (inverse[j % quarter] as number));
61
+ const c = f(exactCos(angle));
62
+ const s = f(exactSin(angle)) * (j < quarter ? -1 : 1);
63
+ for (let h = 0; h < heads; h += 1) {
64
+ const at = t * width + h * headDim + axis * half + j;
65
+ cos[at] = c;
66
+ sin[at] = s;
67
+ }
68
+ }
69
+ }
70
+ }
71
+ return { cos, sin };
72
+ }
73
+
74
+ /**
75
+ * The rotation of `x`, `[tokens, heads · headDim]`, by tables already added to the graph as the
76
+ * constants `cos` and `sin`.
77
+ */
78
+ export function applyRope(
79
+ graph: GraphBuilder,
80
+ x: string,
81
+ tokens: number,
82
+ heads: number,
83
+ headDim: number,
84
+ cos: string,
85
+ sin: string,
86
+ ): string {
87
+ const quarter = headDim / 4;
88
+ const quarters = graph.node('reshape', [x], { shape: [tokens * heads * 2, 2, quarter] });
89
+ const first = graph.node('slice', [quarters], { axis: 1, start: 0, end: 1 });
90
+ const second = graph.node('slice', [quarters], { axis: 1, start: 1, end: 2 });
91
+ const swapped = graph.node('reshape', [graph.node('concat', [second, first], { axis: 1 })], {
92
+ shape: [tokens, heads * headDim],
93
+ });
94
+ return graph.node('add', [graph.node('mul', [x, cos]), graph.node('mul', [swapped, sin])]);
95
+ }
96
+
97
+ /** The tables as constants of the graph, named by `name`. */
98
+ export function ropeConstants(
99
+ weights: Weights,
100
+ name: string,
101
+ positions: Int32Array,
102
+ heads: number,
103
+ headDim: number,
104
+ ): { readonly cos: string; readonly sin: string } {
105
+ const tables = ropeTables(positions, heads, headDim);
106
+ const shape = [positions.length / 2, heads * headDim];
107
+ return {
108
+ cos: weights.constant(`${name}.cos`, shape, tables.cos),
109
+ sin: weights.constant(`${name}.sin`, shape, tables.sin),
110
+ };
111
+ }
@@ -0,0 +1,182 @@
1
+ /**
2
+ * SAM 2.1: masks from a point or a box, and masks carried through a video, as graphs — an encoder
3
+ * run once a frame, a decoder once a prompt, and the memory that carries an object from one frame
4
+ * to the next.
5
+ *
6
+ * **The encoder takes a frame at 1024 square**, resized without keeping its aspect, as the upstream's
7
+ * processor resizes it, and normalised by ImageNet's mean and deviation. It answers three levels of
8
+ * features — the decoder's input at a sixteenth of the frame and its two skips at an eighth and a
9
+ * quarter — which a video's memory then reads too.
10
+ */
11
+ import type { Architecture } from '@driftengine/texture';
12
+
13
+ import { hiera, type HieraConfig } from './hiera.ts';
14
+ import { sam2Decoder } from './sam2Decoder.ts';
15
+ import type { SamDecoderConfig } from './samDecoder.ts';
16
+
17
+ export interface Sam21Config {
18
+ /** The side the encoder takes a frame at. */
19
+ readonly size: number;
20
+ readonly encoder: HieraConfig;
21
+ readonly decoder: SamDecoderConfig;
22
+ readonly memory: Sam21MemoryConfig;
23
+ }
24
+
25
+ export interface Sam21MemoryConfig {
26
+ /** A memory's channels: the encoder's are narrowed to these before they are kept. */
27
+ readonly dim: number;
28
+ readonly attentionLayers: number;
29
+ readonly attentionHeads: number;
30
+ readonly feedForward: number;
31
+ readonly fuserLayers: number;
32
+ readonly fuserHidden: number;
33
+ /** Frames of memory kept, the prompted frame among them, and object pointers read. */
34
+ readonly frames: number;
35
+ readonly pointers: number;
36
+ }
37
+
38
+ export const SAM_21_TINY: Sam21Config = {
39
+ size: 1024,
40
+ encoder: {
41
+ dims: [96, 192, 384, 768],
42
+ blocks: [1, 2, 7, 2],
43
+ heads: [1, 2, 4, 8],
44
+ windows: [8, 4, 14, 7],
45
+ global: [5, 7, 9],
46
+ background: 7,
47
+ mlpRatio: 4,
48
+ fpn: 256,
49
+ topDown: [2, 3],
50
+ },
51
+ decoder: {
52
+ dim: 256,
53
+ heads: 8,
54
+ mlp: 2048,
55
+ depth: 2,
56
+ downsample: 2,
57
+ masks: 4,
58
+ iouHidden: 256,
59
+ iouDepth: 3,
60
+ maskChannels: 16,
61
+ },
62
+ memory: {
63
+ dim: 64,
64
+ attentionLayers: 4,
65
+ attentionHeads: 1,
66
+ feedForward: 2048,
67
+ fuserLayers: 2,
68
+ fuserHidden: 1024,
69
+ frames: 7,
70
+ pointers: 16,
71
+ },
72
+ };
73
+
74
+ /** `image`, `[3, size, size]`, to `features`, `high1` and `high0`. */
75
+ export function sam21Encoder(config: Sam21Config): Architecture {
76
+ return (weights, graph) => {
77
+ for (const prefix of [
78
+ 'prompt_encoder.',
79
+ 'shared_image_embedding.',
80
+ 'memory_attention.',
81
+ 'memory_encoder.',
82
+ 'object_pointer_proj.',
83
+ 'temporal_positional_encoding_projection_layer.',
84
+ 'mask_downsample.',
85
+ ]) {
86
+ weights.ignore(prefix);
87
+ }
88
+ for (const name of [
89
+ 'no_memory_embedding',
90
+ 'no_memory_positional_encoding',
91
+ 'memory_temporal_positional_encoding',
92
+ 'no_object_pointer',
93
+ 'occlusion_spatial_embedding_parameter',
94
+ ]) {
95
+ weights.ignore(name);
96
+ }
97
+ /* The decoder's, but for the two projections the encoder applies once a frame, which it reads. */
98
+ weights.ignore('mask_decoder.');
99
+ hiera(weights, graph, config.encoder, 'image', config.size, {
100
+ features: 'features',
101
+ high1: 'high1',
102
+ high0: 'high0',
103
+ });
104
+ return {
105
+ inputs: [{ name: 'image', shape: [3, config.size, config.size] }],
106
+ outputs: ['features', 'high1', 'high0'],
107
+ };
108
+ };
109
+ }
110
+
111
+ /** The prefixes and names of the memory's weights, which the image graphs set aside. */
112
+ const MEMORY = [
113
+ 'memory_attention.',
114
+ 'memory_encoder.',
115
+ 'mask_downsample.',
116
+ 'temporal_positional_encoding_projection_layer.',
117
+ 'memory_temporal_positional_encoding',
118
+ 'no_memory_positional_encoding',
119
+ 'occlusion_spatial_embedding_parameter',
120
+ ] as const;
121
+
122
+ /**
123
+ * `embedding`, `high1`, `high0` and `prompt`, `[tokens, dim]` — absent when `tokens` is zero — and
124
+ * `mask` when `refine` is set, to `masks`, `quality`, `object` and `pointers`. With `noMemory` the
125
+ * decoder adds `no_memory_embedding` to the features itself, as an image with nothing remembered
126
+ * is decoded; otherwise the features are the memory attention's.
127
+ */
128
+ export function sam21Decoder(
129
+ config: Sam21Config,
130
+ tokens: number,
131
+ options: { readonly refine?: boolean; readonly noMemory?: boolean } = {},
132
+ ): Architecture {
133
+ return (weights, graph) => {
134
+ weights.ignore('vision_encoder.');
135
+ weights.ignore('mask_decoder.conv_s0.');
136
+ weights.ignore('mask_decoder.conv_s1.');
137
+ for (const name of MEMORY) weights.ignore(name);
138
+ const { dim } = config.decoder;
139
+ const grid = config.size / 16;
140
+ /* Read whether this graph uses them or not: the host's pointer when no object is seen. */
141
+ weights.read('no_object_pointer', [1, dim]);
142
+ const noMemory = weights.read('no_memory_embedding', [1, 1, dim]);
143
+ let embedding = 'embedding';
144
+ if (options.noMemory === true) {
145
+ const flat = graph.node('reshape', [noMemory], { shape: [dim] });
146
+ const tokensFirst = graph.node(
147
+ 'permute',
148
+ [graph.node('reshape', ['embedding'], { shape: [dim, grid * grid] })],
149
+ { order: [1, 0] },
150
+ );
151
+ embedding = graph.node(
152
+ 'reshape',
153
+ [graph.node('permute', [graph.node('add', [tokensFirst, flat])], { order: [1, 0] })],
154
+ {
155
+ shape: [dim, grid, grid],
156
+ },
157
+ );
158
+ }
159
+ const inputs = [
160
+ { name: 'embedding', shape: [dim, grid, grid] },
161
+ { name: 'high1', shape: [dim / 4, 2 * grid, 2 * grid] },
162
+ { name: 'high0', shape: [dim / 8, 4 * grid, 4 * grid] },
163
+ ...(tokens > 0 ? [{ name: 'prompt', shape: [tokens, dim] }] : []),
164
+ ...(options.refine === true ? [{ name: 'mask', shape: [1, 4 * grid, 4 * grid] }] : []),
165
+ ];
166
+ sam2Decoder(
167
+ weights,
168
+ graph,
169
+ config.decoder,
170
+ grid,
171
+ {
172
+ embedding,
173
+ prompt: tokens > 0 ? 'prompt' : null,
174
+ mask: options.refine === true ? 'mask' : null,
175
+ high1: 'high1',
176
+ high0: 'high0',
177
+ },
178
+ { masks: 'masks', quality: 'quality', object: 'object', pointers: 'pointers' },
179
+ );
180
+ return { inputs, outputs: ['masks', 'quality', 'object', 'pointers'] };
181
+ };
182
+ }
@@ -0,0 +1,225 @@
1
+ /**
2
+ * A seeded miniature of SAM 2.1: every tensor name and shape of the upstream's video model, at a
3
+ * size the CPU evaluates in a moment — what the model's tests and its hand-run oracle share.
4
+ *
5
+ * **Small where the upstream lets it be.** A 64-pixel frame, stages of 8, 16, 32 and 64 channels in
6
+ * 1, 2, 3 and 2 blocks, windows of 8, 4, 14 and 7 over grids of 16, 8, 4 and 2 — so windows divide,
7
+ * pad and outgrow their grid, one block attends globally and every stage change pools its queries.
8
+ * **The width is 128**, where the upstream's is 256: a memory is 64 channels wide, a width the
9
+ * upstream writes as a literal, and an object pointer is cut into width/64 tokens, so the width must
10
+ * be a multiple of 64 — and 128 cuts each pointer in two, as the real model's 256 cuts it in four.
11
+ */
12
+ import type { GraphTensor } from '@driftengine/texture';
13
+
14
+ import type { Sam21Config } from './sam21.ts';
15
+ import type { SamPrompt } from './samPrompt.ts';
16
+ import { draw } from './seeded.ts';
17
+
18
+ export const MINIATURE_SAM_21: Sam21Config = {
19
+ size: 64,
20
+ encoder: {
21
+ dims: [8, 16, 32, 64],
22
+ blocks: [1, 2, 3, 2],
23
+ heads: [1, 2, 2, 4],
24
+ windows: [8, 4, 14, 7],
25
+ global: [4],
26
+ background: 7,
27
+ mlpRatio: 4,
28
+ fpn: 128,
29
+ topDown: [2, 3],
30
+ },
31
+ decoder: {
32
+ dim: 128,
33
+ heads: 2,
34
+ mlp: 32,
35
+ depth: 2,
36
+ downsample: 2,
37
+ masks: 4,
38
+ iouHidden: 16,
39
+ iouDepth: 3,
40
+ maskChannels: 16,
41
+ },
42
+ memory: {
43
+ dim: 64,
44
+ attentionLayers: 2,
45
+ attentionHeads: 1,
46
+ feedForward: 32,
47
+ fuserLayers: 2,
48
+ fuserHidden: 64,
49
+ frames: 7,
50
+ pointers: 16,
51
+ },
52
+ };
53
+
54
+ /**
55
+ * The original frame the prompts are given in, 36 by 48, which SAM 2 resizes to its square — so
56
+ * each axis scales by its own factor — and a point, a box, and both with a second point off the
57
+ * object.
58
+ */
59
+ export const MINIATURE_SAM_21_IMAGE = { height: 36, width: 48 } as const;
60
+ export const MINIATURE_SAM_21_PROMPTS = {
61
+ point: { points: [[20.5, 14.25, 1]] },
62
+ box: { box: [6, 5, 40.5, 30] },
63
+ both: {
64
+ points: [
65
+ [20.5, 14.25, 1],
66
+ [33, 25.75, 0],
67
+ ],
68
+ box: [6, 5, 40.5, 30],
69
+ },
70
+ } as const satisfies Record<string, SamPrompt>;
71
+
72
+ /** Every tensor the upstream's `Sam2VideoModel` holds, named as its checkpoints are. */
73
+ export function miniatureSam21Checkpoint(
74
+ config: Sam21Config,
75
+ seed: number,
76
+ ): Map<string, GraphTensor> {
77
+ const out = new Map<string, GraphTensor>();
78
+ const add = (name: string, shape: readonly number[]): void => {
79
+ out.set(name, { shape, data: draw(name, shape, seed) });
80
+ };
81
+ const linear = (p: string, outputs: number, inputs: number): void => {
82
+ add(`${p}weight`, [outputs, inputs]);
83
+ add(`${p}bias`, [outputs]);
84
+ };
85
+ const conv = (p: string, outputs: number, inputs: number, kernel: number): void => {
86
+ add(`${p}weight`, [outputs, inputs, kernel, kernel]);
87
+ add(`${p}bias`, [outputs]);
88
+ };
89
+ const norm = (p: string, dim: number): void => {
90
+ add(`${p}weight`, [dim]);
91
+ add(`${p}bias`, [dim]);
92
+ };
93
+ /* The upstream's two-to-n-layer perceptron: `proj_in`, then `layers`, then `proj_out`. */
94
+ const perceptron = (
95
+ p: string,
96
+ input: number,
97
+ hidden: number,
98
+ output: number,
99
+ depth: number,
100
+ ): void => {
101
+ linear(`${p}proj_in.`, hidden, input);
102
+ for (let i = 0; i < depth - 2; i += 1) linear(`${p}layers.${i}.`, hidden, hidden);
103
+ linear(`${p}proj_out.`, output, hidden);
104
+ };
105
+
106
+ const { encoder, decoder, memory } = config;
107
+ const width = decoder.dim;
108
+ add('no_memory_embedding', [1, 1, width]);
109
+ add('no_memory_positional_encoding', [1, 1, width]);
110
+ add('memory_temporal_positional_encoding', [memory.frames, 1, 1, memory.dim]);
111
+ add('no_object_pointer', [1, width]);
112
+ add('occlusion_spatial_embedding_parameter', [1, memory.dim]);
113
+ add('shared_image_embedding.positional_embedding', [2, width / 2]);
114
+
115
+ const b = 'vision_encoder.backbone.';
116
+ const d0 = encoder.dims[0];
117
+ add(`${b}pos_embed`, [1, d0, encoder.background, encoder.background]);
118
+ add(`${b}pos_embed_window`, [1, d0, encoder.windows[0], encoder.windows[0]]);
119
+ conv(`${b}patch_embed.projection.`, d0, 3, 7);
120
+ let index = 0;
121
+ for (let stage = 0; stage < 4; stage += 1) {
122
+ for (let block = 0; block < (encoder.blocks[stage] as number); block += 1) {
123
+ const p = `${b}blocks.${index}.`;
124
+ const first = stage > 0 && block === 0;
125
+ const dim = first ? (encoder.dims[stage - 1] as number) : (encoder.dims[stage] as number);
126
+ const outDim = encoder.dims[stage] as number;
127
+ norm(`${p}layer_norm1.`, dim);
128
+ linear(`${p}attn.qkv.`, 3 * outDim, dim);
129
+ linear(`${p}attn.proj.`, outDim, outDim);
130
+ norm(`${p}layer_norm2.`, outDim);
131
+ linear(`${p}mlp.proj_in.`, outDim * encoder.mlpRatio, outDim);
132
+ linear(`${p}mlp.proj_out.`, outDim, outDim * encoder.mlpRatio);
133
+ if (first) linear(`${p}proj.`, outDim, dim);
134
+ index += 1;
135
+ }
136
+ }
137
+ for (let i = 0; i < 4; i += 1)
138
+ conv(`vision_encoder.neck.convs.${i}.`, encoder.fpn, encoder.dims[3 - i] as number, 1);
139
+
140
+ const pe = 'prompt_encoder.';
141
+ add(`${pe}shared_embedding.positional_embedding`, [2, width / 2]);
142
+ const c = decoder.maskChannels;
143
+ conv(`${pe}mask_embed.conv1.`, c / 4, 1, 2);
144
+ conv(`${pe}mask_embed.conv2.`, c, c / 4, 2);
145
+ conv(`${pe}mask_embed.conv3.`, width, c, 1);
146
+ norm(`${pe}mask_embed.layer_norm1.`, c / 4);
147
+ norm(`${pe}mask_embed.layer_norm2.`, c);
148
+ add(`${pe}no_mask_embed.weight`, [1, width]);
149
+ add(`${pe}point_embed.weight`, [4, width]);
150
+ add(`${pe}not_a_point_embed.weight`, [1, width]);
151
+
152
+ const m = 'mask_decoder.';
153
+ const t = `${m}transformer.`;
154
+ const inner = width / decoder.downsample;
155
+ const attention = (p: string, dim: number, kv = width): void => {
156
+ linear(`${p}q_proj.`, dim, width);
157
+ linear(`${p}k_proj.`, dim, kv);
158
+ linear(`${p}v_proj.`, dim, kv);
159
+ linear(`${p}o_proj.`, width, dim);
160
+ };
161
+ add(`${m}iou_token.weight`, [1, width]);
162
+ add(`${m}mask_tokens.weight`, [decoder.masks, width]);
163
+ for (let i = 0; i < decoder.depth; i += 1) {
164
+ const p = `${t}layers.${i}.`;
165
+ attention(`${p}self_attn.`, width);
166
+ norm(`${p}layer_norm1.`, width);
167
+ attention(`${p}cross_attn_token_to_image.`, inner);
168
+ norm(`${p}layer_norm2.`, width);
169
+ perceptron(`${p}mlp.`, width, decoder.mlp, width, decoder.depth);
170
+ norm(`${p}layer_norm3.`, width);
171
+ norm(`${p}layer_norm4.`, width);
172
+ attention(`${p}cross_attn_image_to_token.`, inner);
173
+ }
174
+ attention(`${t}final_attn_token_to_image.`, inner);
175
+ norm(`${t}layer_norm_final_attn.`, width);
176
+ add(`${m}upscale_conv1.weight`, [width, width / 4, 2, 2]);
177
+ add(`${m}upscale_conv1.bias`, [width / 4]);
178
+ add(`${m}upscale_conv2.weight`, [width / 4, width / 8, 2, 2]);
179
+ add(`${m}upscale_conv2.bias`, [width / 8]);
180
+ norm(`${m}upscale_layer_norm.`, width / 4);
181
+ for (let i = 0; i < decoder.masks; i += 1) {
182
+ perceptron(`${m}output_hypernetworks_mlps.${i}.`, width, width, width / 8, 3);
183
+ }
184
+ perceptron(`${m}iou_prediction_head.`, width, decoder.iouHidden, decoder.masks, decoder.iouDepth);
185
+ conv(`${m}conv_s0.`, width / 8, width, 1);
186
+ conv(`${m}conv_s1.`, width / 4, width, 1);
187
+ add(`${m}obj_score_token.weight`, [1, width]);
188
+ perceptron(`${m}pred_obj_score_head.`, width, width, 1, 3);
189
+
190
+ const a = 'memory_attention.';
191
+ for (let i = 0; i < memory.attentionLayers; i += 1) {
192
+ const p = `${a}layers.${i}.`;
193
+ attention(`${p}self_attn.`, width);
194
+ attention(`${p}cross_attn_image.`, width, memory.dim);
195
+ linear(`${p}linear1.`, memory.feedForward, width);
196
+ linear(`${p}linear2.`, width, memory.feedForward);
197
+ norm(`${p}layer_norm1.`, width);
198
+ norm(`${p}layer_norm2.`, width);
199
+ norm(`${p}layer_norm3.`, width);
200
+ }
201
+ norm(`${a}layer_norm.`, width);
202
+
203
+ const e = 'memory_encoder.';
204
+ let channels = 1;
205
+ for (let i = 0; i < 4; i += 1) {
206
+ conv(`${e}mask_downsampler.layers.${i}.conv.`, channels * 4, channels, 3);
207
+ norm(`${e}mask_downsampler.layers.${i}.layer_norm.`, channels * 4);
208
+ channels *= 4;
209
+ }
210
+ conv(`${e}mask_downsampler.final_conv.`, width, channels, 1);
211
+ conv(`${e}feature_projection.`, width, width, 1);
212
+ for (let i = 0; i < memory.fuserLayers; i += 1) {
213
+ const p = `${e}memory_fuser.layers.${i}.`;
214
+ add(`${p}scale`, [width]);
215
+ conv(`${p}depthwise_conv.`, width, 1, 7);
216
+ norm(`${p}layer_norm.`, width);
217
+ linear(`${p}pointwise_conv1.`, memory.fuserHidden, width);
218
+ linear(`${p}pointwise_conv2.`, width, memory.fuserHidden);
219
+ }
220
+ conv(`${e}projection.`, memory.dim, width, 1);
221
+ conv('mask_downsample.', 1, 1, 4);
222
+ perceptron('object_pointer_proj.', width, width, width, 3);
223
+ linear('temporal_positional_encoding_projection_layer.', memory.dim, width);
224
+ return out;
225
+ }