@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.
- package/LICENSE +202 -0
- package/NOTICE +29 -0
- package/README.md +68 -0
- package/dist/browserFrames.d.ts +31 -0
- package/dist/browserFrames.js +105 -0
- package/dist/collide.d.ts +59 -0
- package/dist/collide.js +92 -0
- package/dist/decimate.d.ts +42 -0
- package/dist/decimate.js +439 -0
- package/dist/delight.d.ts +42 -0
- package/dist/delight.js +351 -0
- package/dist/delightScene.d.ts +37 -0
- package/dist/delightScene.js +183 -0
- package/dist/depth.d.ts +44 -0
- package/dist/depth.js +53 -0
- package/dist/features.d.ts +33 -0
- package/dist/features.js +260 -0
- package/dist/frames.d.ts +47 -0
- package/dist/frames.js +87 -0
- package/dist/fusion.d.ts +77 -0
- package/dist/fusion.js +152 -0
- package/dist/gaussians/adam.d.ts +57 -0
- package/dist/gaussians/adam.js +88 -0
- package/dist/gaussians/depth.d.ts +8 -0
- package/dist/gaussians/depth.js +73 -0
- package/dist/gaussians/gradients.d.ts +36 -0
- package/dist/gaussians/gradients.js +228 -0
- package/dist/gaussians/harmonics.d.ts +46 -0
- package/dist/gaussians/harmonics.js +99 -0
- package/dist/gaussians/optimise.d.ts +50 -0
- package/dist/gaussians/optimise.js +125 -0
- package/dist/gaussians/parameters.d.ts +35 -0
- package/dist/gaussians/parameters.js +165 -0
- package/dist/gaussians/project.d.ts +72 -0
- package/dist/gaussians/project.js +137 -0
- package/dist/gaussians/projectGradient.d.ts +15 -0
- package/dist/gaussians/projectGradient.js +213 -0
- package/dist/gaussians/rasterise.d.ts +13 -0
- package/dist/gaussians/rasterise.js +65 -0
- package/dist/gaussians/refine.d.ts +36 -0
- package/dist/gaussians/refine.js +136 -0
- package/dist/gaussians/similarity.d.ts +25 -0
- package/dist/gaussians/similarity.js +80 -0
- package/dist/index.d.ts +101 -0
- package/dist/index.js +96 -0
- package/dist/marching.d.ts +38 -0
- package/dist/marching.js +258 -0
- package/dist/masks.d.ts +59 -0
- package/dist/masks.js +165 -0
- package/dist/math/dense.d.ts +68 -0
- package/dist/math/dense.js +294 -0
- package/dist/math/schur.d.ts +23 -0
- package/dist/math/schur.js +140 -0
- package/dist/models/batchNorm.d.ts +17 -0
- package/dist/models/batchNorm.js +24 -0
- package/dist/models/clipTokenizer.d.ts +40 -0
- package/dist/models/clipTokenizer.js +122 -0
- package/dist/models/clipTower.d.ts +27 -0
- package/dist/models/clipTower.js +45 -0
- package/dist/models/depthAnything2.d.ts +38 -0
- package/dist/models/depthAnything2.js +163 -0
- package/dist/models/depthAnything3.d.ts +26 -0
- package/dist/models/depthAnything3.js +171 -0
- package/dist/models/dualDpt.d.ts +19 -0
- package/dist/models/dualDpt.js +138 -0
- package/dist/models/hiera.d.ts +49 -0
- package/dist/models/hiera.js +120 -0
- package/dist/models/hieraAttention.d.ts +16 -0
- package/dist/models/hieraAttention.js +68 -0
- package/dist/models/miniature.d.ts +36 -0
- package/dist/models/miniature.js +216 -0
- package/dist/models/mobileSam.d.ts +41 -0
- package/dist/models/mobileSam.js +88 -0
- package/dist/models/owlv2.d.ts +49 -0
- package/dist/models/owlv2.js +131 -0
- package/dist/models/owlv2Detect.d.ts +25 -0
- package/dist/models/owlv2Detect.js +80 -0
- package/dist/models/owlv2Miniature.d.ts +27 -0
- package/dist/models/owlv2Miniature.js +87 -0
- package/dist/models/rope.d.ts +23 -0
- package/dist/models/rope.js +84 -0
- package/dist/models/sam21.d.ts +45 -0
- package/dist/models/sam21.js +126 -0
- package/dist/models/sam21Miniature.d.ts +37 -0
- package/dist/models/sam21Miniature.js +194 -0
- package/dist/models/sam21Video.d.ts +30 -0
- package/dist/models/sam21Video.js +101 -0
- package/dist/models/sam2Choice.d.ts +19 -0
- package/dist/models/sam2Choice.js +55 -0
- package/dist/models/sam2Decoder.d.ts +36 -0
- package/dist/models/sam2Decoder.js +108 -0
- package/dist/models/sam2Memories.d.ts +54 -0
- package/dist/models/sam2Memories.js +93 -0
- package/dist/models/sam2Memory.d.ts +30 -0
- package/dist/models/sam2Memory.js +136 -0
- package/dist/models/sam2Positions.d.ts +24 -0
- package/dist/models/sam2Positions.js +94 -0
- package/dist/models/sam2Tracker.d.ts +69 -0
- package/dist/models/sam2Tracker.js +172 -0
- package/dist/models/samDecoder.d.ts +45 -0
- package/dist/models/samDecoder.js +96 -0
- package/dist/models/samMiniature.d.ts +43 -0
- package/dist/models/samMiniature.js +161 -0
- package/dist/models/samPrompt.d.ts +57 -0
- package/dist/models/samPrompt.js +160 -0
- package/dist/models/seeded.d.ts +16 -0
- package/dist/models/seeded.js +48 -0
- package/dist/models/tinyVit.d.ts +38 -0
- package/dist/models/tinyVit.js +82 -0
- package/dist/models/twoWay.d.ts +46 -0
- package/dist/models/twoWay.js +67 -0
- package/dist/models/vit.d.ts +45 -0
- package/dist/models/vit.js +157 -0
- package/dist/models/windowAttention.d.ts +25 -0
- package/dist/models/windowAttention.js +104 -0
- package/dist/poses.d.ts +31 -0
- package/dist/poses.js +750 -0
- package/dist/prepare.d.ts +30 -0
- package/dist/prepare.js +238 -0
- package/dist/propose.d.ts +36 -0
- package/dist/propose.js +63 -0
- package/dist/resample.d.ts +39 -0
- package/dist/resample.js +165 -0
- package/dist/run.d.ts +3 -0
- package/dist/run.js +1 -0
- package/dist/scene.d.ts +104 -0
- package/dist/scene.js +109 -0
- package/dist/segment.d.ts +40 -0
- package/dist/segment.js +184 -0
- package/dist/testScene.d.ts +55 -0
- package/dist/testScene.js +214 -0
- package/dist/twoView.d.ts +22 -0
- package/dist/twoView.js +435 -0
- package/package.json +63 -0
- package/src/browserFrames.ts +145 -0
- package/src/collide.ts +128 -0
- package/src/decimate.ts +510 -0
- package/src/delight.ts +427 -0
- package/src/delightScene.ts +249 -0
- package/src/depth.ts +100 -0
- package/src/features.ts +302 -0
- package/src/frames.ts +111 -0
- package/src/fusion.ts +229 -0
- package/src/gaussians/adam.ts +131 -0
- package/src/gaussians/depth.ts +87 -0
- package/src/gaussians/gradients.ts +285 -0
- package/src/gaussians/harmonics.ts +122 -0
- package/src/gaussians/optimise.ts +191 -0
- package/src/gaussians/parameters.ts +186 -0
- package/src/gaussians/project.ts +207 -0
- package/src/gaussians/projectGradient.ts +234 -0
- package/src/gaussians/rasterise.ts +81 -0
- package/src/gaussians/refine.ts +176 -0
- package/src/gaussians/similarity.ts +87 -0
- package/src/index.ts +225 -0
- package/src/marching.ts +358 -0
- package/src/masks.ts +201 -0
- package/src/math/dense.ts +317 -0
- package/src/math/schur.ts +167 -0
- package/src/models/batchNorm.ts +57 -0
- package/src/models/clipTokenizer.ts +129 -0
- package/src/models/clipTower.ts +87 -0
- package/src/models/depthAnything2.ts +276 -0
- package/src/models/depthAnything3.ts +229 -0
- package/src/models/dualDpt.ts +233 -0
- package/src/models/hiera.ts +231 -0
- package/src/models/hieraAttention.ts +121 -0
- package/src/models/miniature.ts +241 -0
- package/src/models/mobileSam.ts +135 -0
- package/src/models/owlv2.ts +190 -0
- package/src/models/owlv2Detect.ts +116 -0
- package/src/models/owlv2Miniature.ts +108 -0
- package/src/models/rope.ts +111 -0
- package/src/models/sam21.ts +182 -0
- package/src/models/sam21Miniature.ts +225 -0
- package/src/models/sam21Video.ts +154 -0
- package/src/models/sam2Choice.ts +62 -0
- package/src/models/sam2Decoder.ts +218 -0
- package/src/models/sam2Memories.ts +165 -0
- package/src/models/sam2Memory.ts +247 -0
- package/src/models/sam2Positions.ts +106 -0
- package/src/models/sam2Tracker.ts +300 -0
- package/src/models/samDecoder.ts +225 -0
- package/src/models/samMiniature.ts +190 -0
- package/src/models/samPrompt.ts +227 -0
- package/src/models/seeded.ts +50 -0
- package/src/models/tinyVit.ts +178 -0
- package/src/models/twoWay.ts +122 -0
- package/src/models/vit.ts +265 -0
- package/src/models/windowAttention.ts +155 -0
- package/src/poses.ts +894 -0
- package/src/prepare.ts +306 -0
- package/src/propose.ts +96 -0
- package/src/resample.ts +197 -0
- package/src/run.ts +7 -0
- package/src/scene.ts +141 -0
- package/src/segment.ts +220 -0
- package/src/testScene.ts +283 -0
- package/src/twoView.ts +536 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A seeded miniature of Depth Anything 3: the upstream's layout, every tensor name and shape of it,
|
|
3
|
+
* at a size small enough to evaluate on the CPU in milliseconds.
|
|
4
|
+
*
|
|
5
|
+
* **It is what the model's tests and its hand-run oracle share.** The oracle loads this checkpoint
|
|
6
|
+
* into the upstream's own modules — strictly, so a name or shape that differs from theirs fails
|
|
7
|
+
* there — and runs their code; the tests run the same checkpoint through the definition here and
|
|
8
|
+
* compare. So the numbers a test asserts come from an implementation that is not the one under
|
|
9
|
+
* test, and no weights are downloaded or committed: the checkpoint is a function of a seed.
|
|
10
|
+
*
|
|
11
|
+
* **Every tensor the upstream holds is here, those the definition sets aside included** — the ray
|
|
12
|
+
* branch and, since it would only be loaded to be ignored, not the camera encoder, which the
|
|
13
|
+
* oracle does not build. Shipped, as `@driftengine/nav`'s test field is, because a consumer
|
|
14
|
+
* checking a port of their own wants the same fixture.
|
|
15
|
+
*/
|
|
16
|
+
import { mulberry32 } from '@driftengine/core';
|
|
17
|
+
import { draw, nameSeed } from './seeded.js';
|
|
18
|
+
/**
|
|
19
|
+
* Four blocks of 32 channels in four heads of 8 — the smallest a two-dimensional rotary embedding
|
|
20
|
+
* divides — with the camera token, query-key norms and rotary embedding from the third block, so
|
|
21
|
+
* one within-view and one across-view block each carry all three; a trained grid of 2, so any other
|
|
22
|
+
* grid is resized; and a head of 16 features.
|
|
23
|
+
*/
|
|
24
|
+
export const MINIATURE_DEPTH_ANYTHING_3 = {
|
|
25
|
+
backbone: {
|
|
26
|
+
dim: 32,
|
|
27
|
+
heads: 4,
|
|
28
|
+
depth: 4,
|
|
29
|
+
hidden: 128,
|
|
30
|
+
patch: 14,
|
|
31
|
+
trainedGrid: 2,
|
|
32
|
+
taps: [0, 1, 2, 3],
|
|
33
|
+
altStart: 2,
|
|
34
|
+
qkNormStart: 2,
|
|
35
|
+
ropeStart: 2,
|
|
36
|
+
},
|
|
37
|
+
head: { dimIn: 64, features: 16, outChannels: [4, 8, 8, 16], outputDim: 2, patch: 14 },
|
|
38
|
+
};
|
|
39
|
+
/** The seed the tests and the oracle share. */
|
|
40
|
+
export const MINIATURE_SEED = 20260919;
|
|
41
|
+
/** One view on a grid its positions are resized to, and two views on the trained grid. */
|
|
42
|
+
export const MINIATURE_CASES = {
|
|
43
|
+
single: { views: 1, height: 28, width: 42 },
|
|
44
|
+
pair: { views: 2, height: 28, width: 28 },
|
|
45
|
+
};
|
|
46
|
+
/** Every tensor the upstream's backbone, head and camera decoder hold, named as the checkpoints are. */
|
|
47
|
+
export function miniatureCheckpoint(config, seed) {
|
|
48
|
+
const out = new Map();
|
|
49
|
+
const add = (name, shape) => {
|
|
50
|
+
out.set(name, { shape, data: draw(name, shape, seed) });
|
|
51
|
+
};
|
|
52
|
+
const { dim, heads, depth, hidden, patch, trainedGrid } = config.backbone;
|
|
53
|
+
const b = 'model.backbone.pretrained.';
|
|
54
|
+
add(`${b}cls_token`, [1, 1, dim]);
|
|
55
|
+
add(`${b}camera_token`, [1, 2, dim]);
|
|
56
|
+
add(`${b}pos_embed`, [1, trainedGrid * trainedGrid + 1, dim]);
|
|
57
|
+
add(`${b}patch_embed.proj.weight`, [dim, 3, patch, patch]);
|
|
58
|
+
add(`${b}patch_embed.proj.bias`, [dim]);
|
|
59
|
+
for (let i = 0; i < depth; i += 1) {
|
|
60
|
+
const p = `${b}blocks.${i}.`;
|
|
61
|
+
add(`${p}norm1.weight`, [dim]);
|
|
62
|
+
add(`${p}norm1.bias`, [dim]);
|
|
63
|
+
add(`${p}attn.qkv.weight`, [3 * dim, dim]);
|
|
64
|
+
add(`${p}attn.qkv.bias`, [3 * dim]);
|
|
65
|
+
if (i >= config.backbone.qkNormStart) {
|
|
66
|
+
for (const norm of ['q_norm', 'k_norm']) {
|
|
67
|
+
add(`${p}attn.${norm}.weight`, [dim / heads]);
|
|
68
|
+
add(`${p}attn.${norm}.bias`, [dim / heads]);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
add(`${p}attn.proj.weight`, [dim, dim]);
|
|
72
|
+
add(`${p}attn.proj.bias`, [dim]);
|
|
73
|
+
add(`${p}ls1.gamma`, [dim]);
|
|
74
|
+
add(`${p}norm2.weight`, [dim]);
|
|
75
|
+
add(`${p}norm2.bias`, [dim]);
|
|
76
|
+
add(`${p}mlp.fc1.weight`, [hidden, dim]);
|
|
77
|
+
add(`${p}mlp.fc1.bias`, [hidden]);
|
|
78
|
+
add(`${p}mlp.fc2.weight`, [dim, hidden]);
|
|
79
|
+
add(`${p}mlp.fc2.bias`, [dim]);
|
|
80
|
+
add(`${p}ls2.gamma`, [dim]);
|
|
81
|
+
}
|
|
82
|
+
add(`${b}norm.weight`, [dim]);
|
|
83
|
+
add(`${b}norm.bias`, [dim]);
|
|
84
|
+
const h = 'model.head.';
|
|
85
|
+
const { dimIn, features, outChannels, outputDim } = config.head;
|
|
86
|
+
const conv = (name, to, from, kernel, bias = true) => {
|
|
87
|
+
add(`${name}.weight`, [to, from, kernel, kernel]);
|
|
88
|
+
if (bias)
|
|
89
|
+
add(`${name}.bias`, [to]);
|
|
90
|
+
};
|
|
91
|
+
add(`${h}norm.weight`, [dimIn]);
|
|
92
|
+
add(`${h}norm.bias`, [dimIn]);
|
|
93
|
+
outChannels.forEach((channels, k) => {
|
|
94
|
+
conv(`${h}projects.${k}`, channels, dimIn, 1);
|
|
95
|
+
conv(`${h}scratch.layer${k + 1}_rn`, features, channels, 3, false);
|
|
96
|
+
});
|
|
97
|
+
conv(`${h}resize_layers.0`, outChannels[0], outChannels[0], 4);
|
|
98
|
+
conv(`${h}resize_layers.1`, outChannels[1], outChannels[1], 2);
|
|
99
|
+
conv(`${h}resize_layers.3`, outChannels[3], outChannels[3], 3);
|
|
100
|
+
for (const branch of ['', '_aux']) {
|
|
101
|
+
for (let level = 1; level <= 4; level += 1) {
|
|
102
|
+
const r = `${h}scratch.refinenet${level}${branch}.`;
|
|
103
|
+
for (const unit of level === 4 ? ['resConfUnit2'] : ['resConfUnit1', 'resConfUnit2']) {
|
|
104
|
+
conv(`${r}${unit}.conv1`, features, features, 3);
|
|
105
|
+
conv(`${r}${unit}.conv2`, features, features, 3);
|
|
106
|
+
}
|
|
107
|
+
conv(`${r}out_conv`, features, features, 1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
conv(`${h}scratch.output_conv1`, features / 2, features, 3);
|
|
111
|
+
conv(`${h}scratch.output_conv2.0`, 32, features / 2, 3);
|
|
112
|
+
conv(`${h}scratch.output_conv2.2`, outputDim, 32, 1);
|
|
113
|
+
for (let level = 0; level < 4; level += 1) {
|
|
114
|
+
const widths = [features / 2, features, features / 2, features, features / 2];
|
|
115
|
+
widths.forEach((to, n) => {
|
|
116
|
+
conv(`${h}scratch.output_conv1_aux.${level}.${n}`, to, n === 0 ? features : widths[n - 1], 3);
|
|
117
|
+
});
|
|
118
|
+
conv(`${h}scratch.output_conv2_aux.${level}.0`, 32, features / 2, 3);
|
|
119
|
+
add(`${h}scratch.output_conv2_aux.${level}.2.weight`, [32]);
|
|
120
|
+
add(`${h}scratch.output_conv2_aux.${level}.2.bias`, [32]);
|
|
121
|
+
conv(`${h}scratch.output_conv2_aux.${level}.5`, 7, 32, 1);
|
|
122
|
+
}
|
|
123
|
+
const c = 'model.cam_dec.';
|
|
124
|
+
const wide = 2 * dim;
|
|
125
|
+
for (const [name, to] of [
|
|
126
|
+
['backbone.0', wide],
|
|
127
|
+
['backbone.2', wide],
|
|
128
|
+
['fc_t', 3],
|
|
129
|
+
['fc_qvec', 4],
|
|
130
|
+
['fc_fov.0', 2],
|
|
131
|
+
]) {
|
|
132
|
+
add(`${c}${name}.weight`, [to, wide]);
|
|
133
|
+
add(`${c}${name}.bias`, [to]);
|
|
134
|
+
}
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Depth Anything V2 in miniature: four blocks of 32 channels in four heads, every block tapped, a
|
|
139
|
+
* trained grid of 2, and a neck and head of 16 and 8.
|
|
140
|
+
*/
|
|
141
|
+
export const MINIATURE_DEPTH_ANYTHING_2 = {
|
|
142
|
+
dim: 32,
|
|
143
|
+
heads: 4,
|
|
144
|
+
depth: 4,
|
|
145
|
+
hidden: 128,
|
|
146
|
+
patch: 14,
|
|
147
|
+
trainedGrid: 2,
|
|
148
|
+
taps: [0, 1, 2, 3],
|
|
149
|
+
neck: [4, 8, 8, 16],
|
|
150
|
+
fusion: 16,
|
|
151
|
+
headHidden: 8,
|
|
152
|
+
};
|
|
153
|
+
/** Every tensor Transformers' Depth Anything holds, in the older names its checkpoints keep. */
|
|
154
|
+
export function miniatureCheckpoint2(config, seed) {
|
|
155
|
+
const out = new Map();
|
|
156
|
+
const add = (name, shape) => {
|
|
157
|
+
out.set(name, { shape, data: draw(name, shape, seed) });
|
|
158
|
+
};
|
|
159
|
+
const pair = (name, to, from) => {
|
|
160
|
+
add(`${name}.weight`, [to, from]);
|
|
161
|
+
add(`${name}.bias`, [to]);
|
|
162
|
+
};
|
|
163
|
+
const conv = (name, to, from, kernel, bias = true) => {
|
|
164
|
+
add(`${name}.weight`, [to, from, kernel, kernel]);
|
|
165
|
+
if (bias)
|
|
166
|
+
add(`${name}.bias`, [to]);
|
|
167
|
+
};
|
|
168
|
+
const { dim, depth, hidden, patch, trainedGrid, neck, fusion, headHidden } = config;
|
|
169
|
+
const e = 'backbone.embeddings.';
|
|
170
|
+
add(`${e}cls_token`, [1, 1, dim]);
|
|
171
|
+
add(`${e}mask_token`, [1, dim]);
|
|
172
|
+
add(`${e}position_embeddings`, [1, trainedGrid * trainedGrid + 1, dim]);
|
|
173
|
+
conv(`${e}patch_embeddings.projection`, dim, 3, patch);
|
|
174
|
+
for (let i = 0; i < depth; i += 1) {
|
|
175
|
+
const p = `backbone.encoder.layer.${i}.`;
|
|
176
|
+
add(`${p}norm1.weight`, [dim]);
|
|
177
|
+
add(`${p}norm1.bias`, [dim]);
|
|
178
|
+
for (const part of ['query', 'key', 'value'])
|
|
179
|
+
pair(`${p}attention.attention.${part}`, dim, dim);
|
|
180
|
+
pair(`${p}attention.output.dense`, dim, dim);
|
|
181
|
+
add(`${p}layer_scale1.lambda1`, [dim]);
|
|
182
|
+
add(`${p}norm2.weight`, [dim]);
|
|
183
|
+
add(`${p}norm2.bias`, [dim]);
|
|
184
|
+
pair(`${p}mlp.fc1`, hidden, dim);
|
|
185
|
+
pair(`${p}mlp.fc2`, dim, hidden);
|
|
186
|
+
add(`${p}layer_scale2.lambda1`, [dim]);
|
|
187
|
+
}
|
|
188
|
+
add('backbone.layernorm.weight', [dim]);
|
|
189
|
+
add('backbone.layernorm.bias', [dim]);
|
|
190
|
+
neck.forEach((channels, k) => {
|
|
191
|
+
const r = `neck.reassemble_stage.layers.${k}.`;
|
|
192
|
+
conv(`${r}projection`, channels, dim, 1);
|
|
193
|
+
if (k !== 2)
|
|
194
|
+
conv(`${r}resize`, channels, channels, k === 0 ? 4 : k === 1 ? 2 : 3);
|
|
195
|
+
conv(`neck.convs.${k}`, fusion, channels, 3, false);
|
|
196
|
+
});
|
|
197
|
+
for (let n = 0; n < 4; n += 1) {
|
|
198
|
+
const name = `neck.fusion_stage.layers.${n}`;
|
|
199
|
+
conv(`${name}.projection`, fusion, fusion, 1);
|
|
200
|
+
for (const unit of ['residual_layer1', 'residual_layer2']) {
|
|
201
|
+
conv(`${name}.${unit}.convolution1`, fusion, fusion, 3);
|
|
202
|
+
conv(`${name}.${unit}.convolution2`, fusion, fusion, 3);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
conv('head.conv1', fusion / 2, fusion, 3);
|
|
206
|
+
conv('head.conv2', headHidden, fusion / 2, 3);
|
|
207
|
+
conv('head.conv3', 1, headHidden, 1);
|
|
208
|
+
return out;
|
|
209
|
+
}
|
|
210
|
+
/** Seeded images for `views` views, normalised as the model expects, `[3, height, width]` each. */
|
|
211
|
+
export function miniatureImages(views, height, width, seed) {
|
|
212
|
+
return Array.from({ length: views }, (_, v) => {
|
|
213
|
+
const next = mulberry32(nameSeed(`image${v}`, seed));
|
|
214
|
+
return Float32Array.from({ length: 3 * height * width }, () => (next() * 2 - 1) * 2);
|
|
215
|
+
});
|
|
216
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MobileSAM: masks from a point or a box, in two graphs — an encoder run once per image, and a
|
|
3
|
+
* decoder run once per prompt against the embedding it left.
|
|
4
|
+
*
|
|
5
|
+
* **The encoder takes the image at 1024 square**: resized with its longest side to 1024, normalised
|
|
6
|
+
* by the upstream's pixel mean and deviation, and padded with zeros after its last row and column.
|
|
7
|
+
* The decoder takes that embedding, a prompt's tokens from `samPromptTokens`, and optionally the
|
|
8
|
+
* previous mask's logits to refine, and answers four masks at a quarter of the image's size with a
|
|
9
|
+
* predicted quality each; `samMasksToImage` brings them to the original image as the upstream's
|
|
10
|
+
* `postprocess_masks` does — up to the encoder's size, cropped to the prepared image, and down to the
|
|
11
|
+
* original.
|
|
12
|
+
*
|
|
13
|
+
* **One decoder graph per prompt size**, since every kernel bakes its shapes: a point and its
|
|
14
|
+
* padding are two tokens, and a box is two.
|
|
15
|
+
*/
|
|
16
|
+
import type { Architecture } from '@driftengine/texture';
|
|
17
|
+
import { type SamDecoderConfig } from './samDecoder.ts';
|
|
18
|
+
import { type TinyVitConfig } from './tinyVit.ts';
|
|
19
|
+
export interface MobileSamConfig {
|
|
20
|
+
/** The side the encoder takes an image at. */
|
|
21
|
+
readonly size: number;
|
|
22
|
+
readonly encoder: TinyVitConfig;
|
|
23
|
+
readonly decoder: SamDecoderConfig;
|
|
24
|
+
}
|
|
25
|
+
export declare const MOBILE_SAM: MobileSamConfig;
|
|
26
|
+
/** The pixel mean and deviation the encoder's input is normalised by, in 0–255 RGB. */
|
|
27
|
+
export declare const SAM_PIXEL_MEAN: readonly [123.675, 116.28, 103.53];
|
|
28
|
+
export declare const SAM_PIXEL_STD: readonly [58.395, 57.12, 57.375];
|
|
29
|
+
/** `image`, `[3, size, size]`, to `embedding`, `[neck, grid, grid]`. */
|
|
30
|
+
export declare function mobileSamEncoder(config: MobileSamConfig): Architecture;
|
|
31
|
+
/**
|
|
32
|
+
* `embedding` and `prompt`, `[tokens, dim]` — absent when `tokens` is zero — and `mask` when
|
|
33
|
+
* `refine` is set, to `masks`, `[4, 4·grid, 4·grid]`, and `quality`, `[1, 4]`.
|
|
34
|
+
*/
|
|
35
|
+
export declare function mobileSamDecoder(config: MobileSamConfig, tokens: number, refine?: boolean): Architecture;
|
|
36
|
+
/**
|
|
37
|
+
* `masks`, `[count, 4·grid, 4·grid]`, to `image`, `[count, height, width]`: up to the encoder's
|
|
38
|
+
* size, cropped to the image as it was prepared, and down to the original — bilinearly, without
|
|
39
|
+
* aligned corners, as the upstream's `postprocess_masks` does both. Reads no weights.
|
|
40
|
+
*/
|
|
41
|
+
export declare function samMasksToImage(config: MobileSamConfig, count: number, height: number, width: number): Architecture;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { samDecoder } from './samDecoder.js';
|
|
2
|
+
import { samInputSize } from './samPrompt.js';
|
|
3
|
+
import { tinyVit, tinyVitGrid } from './tinyVit.js';
|
|
4
|
+
export const MOBILE_SAM = {
|
|
5
|
+
size: 1024,
|
|
6
|
+
encoder: {
|
|
7
|
+
embedDims: [64, 128, 160, 320],
|
|
8
|
+
depths: [2, 2, 6, 2],
|
|
9
|
+
heads: [2, 4, 5, 10],
|
|
10
|
+
windows: [7, 7, 14, 7],
|
|
11
|
+
mergeStrides: [2, 2, 1],
|
|
12
|
+
mlpRatio: 4,
|
|
13
|
+
expandRatio: 4,
|
|
14
|
+
neck: 256,
|
|
15
|
+
},
|
|
16
|
+
decoder: {
|
|
17
|
+
dim: 256,
|
|
18
|
+
heads: 8,
|
|
19
|
+
mlp: 2048,
|
|
20
|
+
depth: 2,
|
|
21
|
+
downsample: 2,
|
|
22
|
+
masks: 4,
|
|
23
|
+
iouHidden: 256,
|
|
24
|
+
iouDepth: 3,
|
|
25
|
+
maskChannels: 16,
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
/** The pixel mean and deviation the encoder's input is normalised by, in 0–255 RGB. */
|
|
29
|
+
export const SAM_PIXEL_MEAN = [123.675, 116.28, 103.53];
|
|
30
|
+
export const SAM_PIXEL_STD = [58.395, 57.12, 57.375];
|
|
31
|
+
/** `image`, `[3, size, size]`, to `embedding`, `[neck, grid, grid]`. */
|
|
32
|
+
export function mobileSamEncoder(config) {
|
|
33
|
+
return (weights, graph) => {
|
|
34
|
+
weights.ignore('prompt_encoder.');
|
|
35
|
+
weights.ignore('mask_decoder.');
|
|
36
|
+
tinyVit(weights, graph, config.encoder, 'image_encoder.', 'image', config.size, 'embedding');
|
|
37
|
+
return {
|
|
38
|
+
inputs: [{ name: 'image', shape: [3, config.size, config.size] }],
|
|
39
|
+
outputs: ['embedding'],
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* `embedding` and `prompt`, `[tokens, dim]` — absent when `tokens` is zero — and `mask` when
|
|
45
|
+
* `refine` is set, to `masks`, `[4, 4·grid, 4·grid]`, and `quality`, `[1, 4]`.
|
|
46
|
+
*/
|
|
47
|
+
export function mobileSamDecoder(config, tokens, refine = false) {
|
|
48
|
+
return (weights, graph) => {
|
|
49
|
+
weights.ignore('image_encoder.');
|
|
50
|
+
const { dim } = config.decoder;
|
|
51
|
+
const grid = tinyVitGrid(config.encoder, config.size);
|
|
52
|
+
const inputs = [
|
|
53
|
+
{ name: 'embedding', shape: [config.encoder.neck, grid, grid] },
|
|
54
|
+
...(tokens > 0 ? [{ name: 'prompt', shape: [tokens, dim] }] : []),
|
|
55
|
+
...(refine ? [{ name: 'mask', shape: [1, 4 * grid, 4 * grid] }] : []),
|
|
56
|
+
];
|
|
57
|
+
samDecoder(weights, graph, config.decoder, 'embedding', tokens > 0 ? 'prompt' : null, refine ? 'mask' : null, grid, { masks: 'masks', quality: 'quality' });
|
|
58
|
+
return { inputs, outputs: ['masks', 'quality'] };
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* `masks`, `[count, 4·grid, 4·grid]`, to `image`, `[count, height, width]`: up to the encoder's
|
|
63
|
+
* size, cropped to the image as it was prepared, and down to the original — bilinearly, without
|
|
64
|
+
* aligned corners, as the upstream's `postprocess_masks` does both. Reads no weights.
|
|
65
|
+
*/
|
|
66
|
+
export function samMasksToImage(config, count, height, width) {
|
|
67
|
+
return (_weights, graph) => {
|
|
68
|
+
const grid = tinyVitGrid(config.encoder, config.size);
|
|
69
|
+
const [inputHeight, inputWidth] = samInputSize(height, width, config.size);
|
|
70
|
+
const bilinear = { mode: 'bilinear', alignCorners: false };
|
|
71
|
+
let x = graph.node('resize', ['masks'], {
|
|
72
|
+
height: config.size,
|
|
73
|
+
width: config.size,
|
|
74
|
+
...bilinear,
|
|
75
|
+
});
|
|
76
|
+
if (inputHeight < config.size) {
|
|
77
|
+
x = graph.node('slice', [x], { axis: 1, start: 0, end: inputHeight });
|
|
78
|
+
}
|
|
79
|
+
if (inputWidth < config.size) {
|
|
80
|
+
x = graph.node('slice', [x], { axis: 2, start: 0, end: inputWidth });
|
|
81
|
+
}
|
|
82
|
+
graph.node('resize', [x], { height, width, ...bilinear }, 'image');
|
|
83
|
+
return {
|
|
84
|
+
inputs: [{ name: 'masks', shape: [count, 4 * grid, 4 * grid] }],
|
|
85
|
+
outputs: ['image'],
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OWLv2: boxes for things named in words, in two graphs — the image's, run once a frame, and the
|
|
3
|
+
* text's, run once a set of queries — which the host joins (`owlv2Detect.ts`).
|
|
4
|
+
*
|
|
5
|
+
* **Both are CLIP's transformer**: pre-norm layers of attention and a perceptron whose activation is
|
|
6
|
+
* `x·σ(1.702·x)`, every norm at PyTorch's ε. The image is cut into patches of 16, a class token put
|
|
7
|
+
* first, and after the last layer each patch is multiplied by the class token and normed again;
|
|
8
|
+
* from that come a class embedding, a logit shift and scale, a box and an objectness, each a patch.
|
|
9
|
+
* A box is the upstream's: a perceptron's four numbers plus the patch's own prior — the logit of its
|
|
10
|
+
* corner and of the grid's cell size — through the logistic, a centre and a size in the image.
|
|
11
|
+
*
|
|
12
|
+
* **The text reads its queries causally** — `clipTower.ts` holds that mask — and each query is
|
|
13
|
+
* read at the row of its end-of-text token, which is what the upstream pools.
|
|
14
|
+
*
|
|
15
|
+
* **The tokenizer travels in the text graph**, as `tokenizer.merges`, which no node reads.
|
|
16
|
+
*/
|
|
17
|
+
import type { Architecture } from '@driftengine/texture';
|
|
18
|
+
import { type ClipTowerConfig } from './clipTower.ts';
|
|
19
|
+
export interface Owlv2Config {
|
|
20
|
+
/** The side the image graph takes an image at. */
|
|
21
|
+
readonly size: number;
|
|
22
|
+
readonly patch: number;
|
|
23
|
+
readonly vision: ClipTowerConfig;
|
|
24
|
+
/**
|
|
25
|
+
* The text tower, and how many tokens a query may be. Its width is also the class embedding's and
|
|
26
|
+
* the projected query's, which the upstream's class head and projection each take from it.
|
|
27
|
+
*/
|
|
28
|
+
readonly text: ClipTowerConfig & {
|
|
29
|
+
readonly positions: number;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export declare const OWLV2_BASE: Owlv2Config;
|
|
33
|
+
/** The pixel mean and deviation an image is normalised by, CLIP's, in 0–1 RGB. */
|
|
34
|
+
export declare const OWLV2_PIXEL_MEAN: readonly [0.48145466, 0.4578275, 0.40821073];
|
|
35
|
+
export declare const OWLV2_PIXEL_STD: readonly [0.26862954, 0.26130258, 0.27577711];
|
|
36
|
+
/** `[x, y, w, h]` for every cell, row by row: the logits of its far corner and of the cell's size. */
|
|
37
|
+
export declare function owlv2BoxBias(grid: number): Float32Array;
|
|
38
|
+
/**
|
|
39
|
+
* `image`, `[3, size, size]`, to five values a patch: `classes`, `[cells, text.dim]`, the class
|
|
40
|
+
* embedding before it is normalised; `shift` and `scale`, `[cells, 1]`, the logit's shift and its
|
|
41
|
+
* scale before the ELU; `boxes`, `[cells, 4]`, centre and size in the image's fractions; and
|
|
42
|
+
* `objectness`, `[cells, 1]`, a logit.
|
|
43
|
+
*/
|
|
44
|
+
export declare function owlv2Image(config: Owlv2Config): Architecture;
|
|
45
|
+
/**
|
|
46
|
+
* `tokens`, `[queries · positions]`, and `ends`, `[queries]`, as `owlv2Tokens` makes them, to
|
|
47
|
+
* `queries`, `[queries, text.dim]`: each query's embedding before it is normalised.
|
|
48
|
+
*/
|
|
49
|
+
export declare function owlv2Text(config: Owlv2Config, queries: number): Architecture;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { exactLog } from '@driftengine/core';
|
|
2
|
+
import { clipLayers } from './clipTower.js';
|
|
3
|
+
export const OWLV2_BASE = {
|
|
4
|
+
size: 960,
|
|
5
|
+
patch: 16,
|
|
6
|
+
vision: { dim: 768, heads: 12, depth: 12, hidden: 3072 },
|
|
7
|
+
text: { dim: 512, heads: 8, depth: 12, hidden: 2048, positions: 16 },
|
|
8
|
+
};
|
|
9
|
+
/** The pixel mean and deviation an image is normalised by, CLIP's, in 0–1 RGB. */
|
|
10
|
+
export const OWLV2_PIXEL_MEAN = [0.48145466, 0.4578275, 0.40821073];
|
|
11
|
+
export const OWLV2_PIXEL_STD = [0.26862954, 0.26130258, 0.27577711];
|
|
12
|
+
const EPSILON = 1e-5;
|
|
13
|
+
const f = Math.fround;
|
|
14
|
+
/** `[x, y, w, h]` for every cell, row by row: the logits of its far corner and of the cell's size. */
|
|
15
|
+
export function owlv2BoxBias(grid) {
|
|
16
|
+
/* The upstream's order of roundings: log(c + 1e-4) − log1p(−c + 1e-4), every step single. */
|
|
17
|
+
const offset = f(1e-4);
|
|
18
|
+
const logit = (c) => f(f(exactLog(f(c + offset))) - f(exactLog(1 + f(-c + offset))));
|
|
19
|
+
const size = logit(f(1 / grid));
|
|
20
|
+
const out = new Float32Array(grid * grid * 4);
|
|
21
|
+
for (let row = 0; row < grid; row += 1) {
|
|
22
|
+
for (let col = 0; col < grid; col += 1) {
|
|
23
|
+
const at = (row * grid + col) * 4;
|
|
24
|
+
out[at] = logit(f((col + 1) / grid));
|
|
25
|
+
out[at + 1] = logit(f((row + 1) / grid));
|
|
26
|
+
out[at + 2] = size;
|
|
27
|
+
out[at + 3] = size;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
const TEXT = 'owlv2.text_model.';
|
|
33
|
+
const VISION = 'owlv2.vision_model.';
|
|
34
|
+
/**
|
|
35
|
+
* `image`, `[3, size, size]`, to five values a patch: `classes`, `[cells, text.dim]`, the class
|
|
36
|
+
* embedding before it is normalised; `shift` and `scale`, `[cells, 1]`, the logit's shift and its
|
|
37
|
+
* scale before the ELU; `boxes`, `[cells, 4]`, centre and size in the image's fractions; and
|
|
38
|
+
* `objectness`, `[cells, 1]`, a logit.
|
|
39
|
+
*/
|
|
40
|
+
export function owlv2Image(config) {
|
|
41
|
+
return (weights, graph) => {
|
|
42
|
+
for (const aside of [TEXT, 'owlv2.text_projection.weight', 'owlv2.visual_projection.weight']) {
|
|
43
|
+
weights.ignore(aside);
|
|
44
|
+
}
|
|
45
|
+
weights.ignore('owlv2.logit_scale');
|
|
46
|
+
weights.ignore('tokenizer.merges');
|
|
47
|
+
const node = graph.node.bind(graph);
|
|
48
|
+
const { dim } = config.vision;
|
|
49
|
+
const grid = config.size / config.patch;
|
|
50
|
+
const cells = grid * grid;
|
|
51
|
+
const read = weights.read.bind(weights);
|
|
52
|
+
const norm = (value, p) => node('layerNorm', [value, read(`${p}.weight`, [dim]), read(`${p}.bias`, [dim])], {
|
|
53
|
+
epsilon: EPSILON,
|
|
54
|
+
});
|
|
55
|
+
const linear = (value, p, out, output) => node('linear', [value, read(`${p}.weight`, [out, dim]), read(`${p}.bias`, [out])], {}, output);
|
|
56
|
+
const patches = node('patchEmbed', [
|
|
57
|
+
'image',
|
|
58
|
+
read(`${VISION}embeddings.patch_embedding.weight`, [dim, 3, config.patch, config.patch]),
|
|
59
|
+
], { patch: config.patch });
|
|
60
|
+
const cls = node('reshape', [read(`${VISION}embeddings.class_embedding`, [dim])], {
|
|
61
|
+
shape: [1, dim],
|
|
62
|
+
});
|
|
63
|
+
let x = node('add', [
|
|
64
|
+
node('concat', [cls, patches], { axis: 0 }),
|
|
65
|
+
read(`${VISION}embeddings.position_embedding.weight`, [cells + 1, dim]),
|
|
66
|
+
]);
|
|
67
|
+
x = norm(x, `${VISION}pre_layernorm`);
|
|
68
|
+
x = clipLayers(weights, graph, VISION, config.vision, x, 1, cells + 1, false);
|
|
69
|
+
x = norm(x, `${VISION}post_layernorm`);
|
|
70
|
+
const token = node('reshape', [node('slice', [x], { axis: 0, start: 0, end: 1 })], {
|
|
71
|
+
shape: [dim],
|
|
72
|
+
});
|
|
73
|
+
const features = norm(node('mul', [node('slice', [x], { axis: 0, start: 1, end: cells + 1 }), token]), 'layer_norm');
|
|
74
|
+
linear(features, 'class_head.dense0', config.text.dim, 'classes');
|
|
75
|
+
linear(features, 'class_head.logit_shift', 1, 'shift');
|
|
76
|
+
linear(features, 'class_head.logit_scale', 1, 'scale');
|
|
77
|
+
const head = (p, out, output) => {
|
|
78
|
+
const inner = node('gelu', [
|
|
79
|
+
linear(node('gelu', [linear(features, `${p}.dense0`, dim)]), `${p}.dense1`, dim),
|
|
80
|
+
]);
|
|
81
|
+
return linear(inner, `${p}.dense2`, out, output);
|
|
82
|
+
};
|
|
83
|
+
const bias = weights.constant(`box_bias.${grid}`, [cells, 4], owlv2BoxBias(grid));
|
|
84
|
+
graph.node('sigmoid', [node('add', [head('box_head', 4), bias])], {}, 'boxes');
|
|
85
|
+
head('objectness_head', 1, 'objectness');
|
|
86
|
+
return {
|
|
87
|
+
inputs: [{ name: 'image', shape: [3, config.size, config.size] }],
|
|
88
|
+
outputs: ['classes', 'shift', 'scale', 'boxes', 'objectness'],
|
|
89
|
+
};
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* `tokens`, `[queries · positions]`, and `ends`, `[queries]`, as `owlv2Tokens` makes them, to
|
|
94
|
+
* `queries`, `[queries, text.dim]`: each query's embedding before it is normalised.
|
|
95
|
+
*/
|
|
96
|
+
export function owlv2Text(config, queries) {
|
|
97
|
+
return (weights, graph) => {
|
|
98
|
+
for (const aside of [VISION, 'owlv2.visual_projection.weight', 'owlv2.logit_scale']) {
|
|
99
|
+
weights.ignore(aside);
|
|
100
|
+
}
|
|
101
|
+
for (const aside of ['class_head.', 'box_head.', 'objectness_head.', 'layer_norm.']) {
|
|
102
|
+
weights.ignore(aside);
|
|
103
|
+
}
|
|
104
|
+
weights.read('tokenizer.merges');
|
|
105
|
+
const node = graph.node.bind(graph);
|
|
106
|
+
const { dim, positions } = config.text;
|
|
107
|
+
const read = weights.read.bind(weights);
|
|
108
|
+
const embedded = node('gather', [read(`${TEXT}embeddings.token_embedding.weight`), 'tokens']);
|
|
109
|
+
const placed = node('add', [
|
|
110
|
+
node('reshape', [embedded], { shape: [queries, positions * dim] }),
|
|
111
|
+
node('reshape', [read(`${TEXT}embeddings.position_embedding.weight`, [positions, dim])], {
|
|
112
|
+
shape: [positions * dim],
|
|
113
|
+
}),
|
|
114
|
+
]);
|
|
115
|
+
let x = node('reshape', [placed], { shape: [queries * positions, dim] });
|
|
116
|
+
x = clipLayers(weights, graph, TEXT, config.text, x, queries, positions, true);
|
|
117
|
+
x = node('layerNorm', [
|
|
118
|
+
node('gather', [x, 'ends']),
|
|
119
|
+
read(`${TEXT}final_layer_norm.weight`, [dim]),
|
|
120
|
+
read(`${TEXT}final_layer_norm.bias`, [dim]),
|
|
121
|
+
], { epsilon: EPSILON });
|
|
122
|
+
graph.node('linear', [x, read('owlv2.text_projection.weight', [dim, dim])], {}, 'queries');
|
|
123
|
+
return {
|
|
124
|
+
inputs: [
|
|
125
|
+
{ name: 'tokens', shape: [queries * positions] },
|
|
126
|
+
{ name: 'ends', shape: [queries] },
|
|
127
|
+
],
|
|
128
|
+
outputs: ['queries'],
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export interface Owlv2Patches {
|
|
2
|
+
/** `[cells, projection]`. */
|
|
3
|
+
readonly classes: Float32Array;
|
|
4
|
+
/** `[cells]`. */
|
|
5
|
+
readonly shift: Float32Array;
|
|
6
|
+
/** `[cells]`, before the ELU. */
|
|
7
|
+
readonly scale: Float32Array;
|
|
8
|
+
}
|
|
9
|
+
export interface Owlv2Detection {
|
|
10
|
+
/** The query it answers. */
|
|
11
|
+
readonly label: number;
|
|
12
|
+
readonly score: number;
|
|
13
|
+
/** Left, top, right and bottom, in the original image's pixels. */
|
|
14
|
+
readonly box: readonly [number, number, number, number];
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* `out`, `[cells, count]`: every patch's logit for every one of `count` queries, `[count, width]`
|
|
18
|
+
* as the text graph answers them.
|
|
19
|
+
*/
|
|
20
|
+
export declare function owlv2Logits(patches: Owlv2Patches, queries: Float32Array, count: number, out: Float32Array): void;
|
|
21
|
+
/**
|
|
22
|
+
* The detections above `threshold` among `logits`, `[cells, count]`, with `boxes`, `[cells, 4]` as
|
|
23
|
+
* the image graph answers them, for an original image of `height` by `width`.
|
|
24
|
+
*/
|
|
25
|
+
export declare function owlv2Detections(logits: Float32Array, boxes: Float32Array, count: number, threshold: number, height: number, width: number): Owlv2Detection[];
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OWLv2's answer to a set of text queries, on the host: the image graph's patches and the text
|
|
3
|
+
* graph's queries joined into a logit a patch and query, and those into boxes.
|
|
4
|
+
*
|
|
5
|
+
* **The logit is the upstream class head's**: the class embedding and the query each divided by
|
|
6
|
+
* their length plus 10⁻⁶ — the query already once divided by its length alone, as the upstream's
|
|
7
|
+
* model normalises it before the head does again — their dot product, plus the patch's shift, times
|
|
8
|
+
* one plus the ELU of its scale. Every step is rounded to single precision where the upstream's is,
|
|
9
|
+
* with sums taken in double.
|
|
10
|
+
*
|
|
11
|
+
* **A detection is the upstream's post-processing**: each patch's best query, kept when the
|
|
12
|
+
* logistic of its logit is above the threshold, its box turned from centre and size to corners and
|
|
13
|
+
* scaled by the image's longer side — the side of the square the image was padded to, since
|
|
14
|
+
* padding goes after the image's end.
|
|
15
|
+
*/
|
|
16
|
+
import { exactExp } from '@driftengine/core';
|
|
17
|
+
const f = Math.fround;
|
|
18
|
+
const APART = f(1e-6);
|
|
19
|
+
/* Each row of `values` divided by its length (plus `apart`), in place, as the upstream rounds it. */
|
|
20
|
+
function normalise(values, width, apart) {
|
|
21
|
+
for (let at = 0; at < values.length; at += width) {
|
|
22
|
+
let sum = 0;
|
|
23
|
+
for (let i = 0; i < width; i += 1)
|
|
24
|
+
sum += values[at + i] * values[at + i];
|
|
25
|
+
const length = f(f(Math.sqrt(sum)) + apart);
|
|
26
|
+
for (let i = 0; i < width; i += 1)
|
|
27
|
+
values[at + i] = f(values[at + i] / length);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* `out`, `[cells, count]`: every patch's logit for every one of `count` queries, `[count, width]`
|
|
32
|
+
* as the text graph answers them.
|
|
33
|
+
*/
|
|
34
|
+
export function owlv2Logits(patches, queries, count, out) {
|
|
35
|
+
const width = queries.length / count;
|
|
36
|
+
const cells = patches.shift.length;
|
|
37
|
+
const query = Float32Array.from(queries);
|
|
38
|
+
normalise(query, width, 0);
|
|
39
|
+
normalise(query, width, APART);
|
|
40
|
+
const classes = Float32Array.from(patches.classes);
|
|
41
|
+
normalise(classes, width, APART);
|
|
42
|
+
for (let cell = 0; cell < cells; cell += 1) {
|
|
43
|
+
const raw = patches.scale[cell];
|
|
44
|
+
const scale = f((raw > 0 ? raw : f(exactExp(raw) - 1)) + 1);
|
|
45
|
+
const shift = patches.shift[cell];
|
|
46
|
+
for (let q = 0; q < count; q += 1) {
|
|
47
|
+
let dot = 0;
|
|
48
|
+
for (let i = 0; i < width; i += 1) {
|
|
49
|
+
dot += classes[cell * width + i] * query[q * width + i];
|
|
50
|
+
}
|
|
51
|
+
out[cell * count + q] = f(f(f(dot) + shift) * scale);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The detections above `threshold` among `logits`, `[cells, count]`, with `boxes`, `[cells, 4]` as
|
|
57
|
+
* the image graph answers them, for an original image of `height` by `width`.
|
|
58
|
+
*/
|
|
59
|
+
export function owlv2Detections(logits, boxes, count, threshold, height, width) {
|
|
60
|
+
const side = Math.max(height, width);
|
|
61
|
+
const found = [];
|
|
62
|
+
for (let cell = 0; cell < boxes.length / 4; cell += 1) {
|
|
63
|
+
let label = 0;
|
|
64
|
+
for (let q = 1; q < count; q += 1) {
|
|
65
|
+
if (logits[cell * count + q] > logits[cell * count + label])
|
|
66
|
+
label = q;
|
|
67
|
+
}
|
|
68
|
+
const score = f(1 / (1 + exactExp(-logits[cell * count + label])));
|
|
69
|
+
if (!(score > threshold))
|
|
70
|
+
continue;
|
|
71
|
+
const corner = (centre, extent, sign) => f(f(boxes[cell * 4 + centre] +
|
|
72
|
+
sign * f(0.5 * boxes[cell * 4 + extent])) * side);
|
|
73
|
+
found.push({
|
|
74
|
+
label,
|
|
75
|
+
score,
|
|
76
|
+
box: [corner(0, 2, -1), corner(1, 3, -1), corner(0, 2, 1), corner(1, 3, 1)],
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return found;
|
|
80
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
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
|
+
import type { Owlv2Config } from './owlv2.ts';
|
|
13
|
+
export declare const MINIATURE_OWLV2: Owlv2Config;
|
|
14
|
+
/** c+a, ca+t</w> and a+t</w>: tokens 512 to 514, and the start and end of text 515 and 516. */
|
|
15
|
+
export declare const MINIATURE_OWLV2_MERGES: Float32Array<ArrayBuffer>;
|
|
16
|
+
/**
|
|
17
|
+
* Three queries as token ids: one ending early, one with the padding token inside it — the "!" a
|
|
18
|
+
* text may hold — and one filling every position.
|
|
19
|
+
*/
|
|
20
|
+
export declare const MINIATURE_OWLV2_QUERIES: readonly (readonly number[])[];
|
|
21
|
+
/** The original image's size the boxes are scaled to: wider than tall, padded below. */
|
|
22
|
+
export declare const MINIATURE_OWLV2_IMAGE: {
|
|
23
|
+
readonly height: 36;
|
|
24
|
+
readonly width: 48;
|
|
25
|
+
};
|
|
26
|
+
/** Every tensor the upstream's `Owlv2ForObjectDetection` holds, named as its checkpoints are. */
|
|
27
|
+
export declare function miniatureOwlv2Checkpoint(config: Owlv2Config, seed: number): Map<string, GraphTensor>;
|