@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,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLIP's encoder tower, which OWLv2's image and text graphs are each one of.
|
|
3
|
+
*
|
|
4
|
+
* **Pre-norm layers**: a layer norm, attention over the row's own window, and the projection added
|
|
5
|
+
* back; then a layer norm, a perceptron, and that added back. The activation is the upstream's
|
|
6
|
+
* quick GELU, `x·σ(1.702·x)`, which is the logistic and two multiplies rather than the error
|
|
7
|
+
* function `gelu` computes.
|
|
8
|
+
*
|
|
9
|
+
* **A causal tower masks its scores** with −10⁴ above the diagonal, where the upstream uses the
|
|
10
|
+
* least finite single. Both give a masked key a weight of exactly zero — exp of anything below
|
|
11
|
+
* −104 is zero in single precision, and scores stay within a few hundred of each other — and −10⁴
|
|
12
|
+
* is exact at half precision, where the upstream's value would be infinite and a device may assume
|
|
13
|
+
* no infinities. **What would make it wrong** is scores reaching thousands, which would need a
|
|
14
|
+
* checkpoint whose norms let them.
|
|
15
|
+
*/
|
|
16
|
+
import type { GraphBuilder, Weights } from '@driftengine/texture';
|
|
17
|
+
|
|
18
|
+
export interface ClipTowerConfig {
|
|
19
|
+
readonly dim: number;
|
|
20
|
+
readonly heads: number;
|
|
21
|
+
readonly depth: number;
|
|
22
|
+
readonly hidden: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const EPSILON = 1e-5;
|
|
26
|
+
const MASKED = -1e4;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* CLIP's layers over `x`, `[batch · rows, dim]`, each batch's rows attending among themselves —
|
|
30
|
+
* causally when `causal` is set.
|
|
31
|
+
*/
|
|
32
|
+
export function clipLayers(
|
|
33
|
+
weights: Weights,
|
|
34
|
+
graph: GraphBuilder,
|
|
35
|
+
prefix: string,
|
|
36
|
+
config: ClipTowerConfig,
|
|
37
|
+
x: string,
|
|
38
|
+
batch: number,
|
|
39
|
+
rows: number,
|
|
40
|
+
causal: boolean,
|
|
41
|
+
): string {
|
|
42
|
+
const { dim, heads, hidden } = config;
|
|
43
|
+
const node = graph.node.bind(graph);
|
|
44
|
+
const w = (name: string, shape: readonly number[]): string =>
|
|
45
|
+
weights.read(`${prefix}${name}`, shape);
|
|
46
|
+
const linear = (value: string, p: string, out: number, inputs: number): string =>
|
|
47
|
+
node('linear', [value, w(`${p}.weight`, [out, inputs]), w(`${p}.bias`, [out])]);
|
|
48
|
+
const norm = (value: string, p: string): string =>
|
|
49
|
+
node('layerNorm', [value, w(`${p}.weight`, [dim]), w(`${p}.bias`, [dim])], {
|
|
50
|
+
epsilon: EPSILON,
|
|
51
|
+
});
|
|
52
|
+
const quick = weights.constant(
|
|
53
|
+
`quick_gelu.${hidden}`,
|
|
54
|
+
[hidden],
|
|
55
|
+
new Float32Array(hidden).fill(1.702),
|
|
56
|
+
);
|
|
57
|
+
let mask: string | null = null;
|
|
58
|
+
if (causal) {
|
|
59
|
+
const table = new Float32Array(heads * rows * rows);
|
|
60
|
+
for (let h = 0; h < heads; h += 1) {
|
|
61
|
+
for (let q = 0; q < rows; q += 1) {
|
|
62
|
+
for (let k = q + 1; k < rows; k += 1) table[(h * rows + q) * rows + k] = MASKED;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
mask = weights.constant(`causal.${heads}x${rows}`, [heads, rows, rows], table);
|
|
66
|
+
}
|
|
67
|
+
const batched = (value: string): string =>
|
|
68
|
+
batch > 1 || mask !== null ? node('reshape', [value], { shape: [batch, rows, dim] }) : value;
|
|
69
|
+
|
|
70
|
+
let out = x;
|
|
71
|
+
for (let i = 0; i < config.depth; i += 1) {
|
|
72
|
+
const p = `encoder.layers.${i}.`;
|
|
73
|
+
const h = norm(out, `${p}layer_norm1`);
|
|
74
|
+
const q = batched(linear(h, `${p}self_attn.q_proj`, dim, dim));
|
|
75
|
+
const k = batched(linear(h, `${p}self_attn.k_proj`, dim, dim));
|
|
76
|
+
const v = batched(linear(h, `${p}self_attn.v_proj`, dim, dim));
|
|
77
|
+
let attended = node('attention', mask === null ? [q, k, v] : [q, k, v, mask], { heads });
|
|
78
|
+
if (batch > 1 || mask !== null) {
|
|
79
|
+
attended = node('reshape', [attended], { shape: [batch * rows, dim] });
|
|
80
|
+
}
|
|
81
|
+
out = node('add', [out, linear(attended, `${p}self_attn.out_proj`, dim, dim)]);
|
|
82
|
+
const inner = linear(norm(out, `${p}layer_norm2`), `${p}mlp.fc1`, hidden, dim);
|
|
83
|
+
const activated = node('mul', [inner, node('sigmoid', [node('mul', [inner, quick])])]);
|
|
84
|
+
out = node('add', [out, linear(activated, `${p}mlp.fc2`, dim, hidden)]);
|
|
85
|
+
}
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Depth Anything V2 Small, the lighter fallback: plain DINOv2 and a DPT, in the Transformers layout
|
|
3
|
+
* its Apache-2.0 checkpoint is published in.
|
|
4
|
+
*
|
|
5
|
+
* **Plain where Depth Anything 3 is not**: one view, no camera token, no query-key norms, no rotary
|
|
6
|
+
* embedding, and positions resized to the grid by the ratio of the sizes, as Transformers' DINOv2
|
|
7
|
+
* resizes them. Four taps — after the third, sixth, ninth and twelfth blocks, each through the
|
|
8
|
+
* backbone's last norm — are reassembled into a pyramid and fused as Depth Anything 3's head fuses
|
|
9
|
+
* its own, without the sinusoidal positions that head adds. The output is one channel through a
|
|
10
|
+
* rectifier: relative inverse depth, larger nearer, with no scale.
|
|
11
|
+
*
|
|
12
|
+
* **The checkpoint keeps Transformers' older names** — `attention.attention.query`,
|
|
13
|
+
* `layer_scale1.lambda1` — which the library renames as it loads; this reads them as stored. Its
|
|
14
|
+
* mask token is for training and set aside, and so is the first fusion block's lateral unit, which
|
|
15
|
+
* the upstream builds and never runs because the coarsest level has no level below it.
|
|
16
|
+
*/
|
|
17
|
+
import type { Architecture } from '@driftengine/texture';
|
|
18
|
+
|
|
19
|
+
export interface DepthAnything2Config {
|
|
20
|
+
readonly dim: number;
|
|
21
|
+
readonly heads: number;
|
|
22
|
+
readonly depth: number;
|
|
23
|
+
readonly hidden: number;
|
|
24
|
+
readonly patch: number;
|
|
25
|
+
readonly trainedGrid: number;
|
|
26
|
+
/** The blocks whose outputs are tapped, counted from zero. */
|
|
27
|
+
readonly taps: readonly [number, number, number, number];
|
|
28
|
+
readonly neck: readonly [number, number, number, number];
|
|
29
|
+
readonly fusion: number;
|
|
30
|
+
readonly headHidden: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The checkpoint's configuration at its pinned revision. */
|
|
34
|
+
export const DEPTH_ANYTHING_2_SMALL: DepthAnything2Config = {
|
|
35
|
+
dim: 384,
|
|
36
|
+
heads: 6,
|
|
37
|
+
depth: 12,
|
|
38
|
+
hidden: 1536,
|
|
39
|
+
patch: 14,
|
|
40
|
+
trainedGrid: 37,
|
|
41
|
+
taps: [2, 5, 8, 11],
|
|
42
|
+
neck: [48, 96, 192, 384],
|
|
43
|
+
fusion: 64,
|
|
44
|
+
headHidden: 32,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** Transformers' DINOv2 trains every norm at ε = 1e-6. */
|
|
48
|
+
const EPSILON = 1e-6;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The graph for one image of `height × width` pixels, each a multiple of the patch, normalised by
|
|
52
|
+
* ImageNet's mean and deviation: input `image`, `[3, height, width]`; output `depth`,
|
|
53
|
+
* `[1, height, width]`.
|
|
54
|
+
*/
|
|
55
|
+
export function depthAnything2(
|
|
56
|
+
config: DepthAnything2Config,
|
|
57
|
+
height: number,
|
|
58
|
+
width: number,
|
|
59
|
+
): Architecture {
|
|
60
|
+
const rows = height / config.patch;
|
|
61
|
+
const cols = width / config.patch;
|
|
62
|
+
if (!Number.isInteger(rows) || !Number.isInteger(cols)) {
|
|
63
|
+
throw new RangeError(
|
|
64
|
+
`${height} × ${width} is not a whole number of ${config.patch}-pixel patches`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
return (weights, graph) => {
|
|
68
|
+
const { dim, heads, trainedGrid } = config;
|
|
69
|
+
const node = graph.node.bind(graph);
|
|
70
|
+
const w = (name: string, shape: readonly number[]): string => weights.read(name, shape);
|
|
71
|
+
const linear = (x: string, name: string, to: number, from: number): string =>
|
|
72
|
+
node('linear', [x, w(`${name}.weight`, [to, from]), w(`${name}.bias`, [to])]);
|
|
73
|
+
const conv = (
|
|
74
|
+
x: string,
|
|
75
|
+
name: string,
|
|
76
|
+
to: number,
|
|
77
|
+
from: number,
|
|
78
|
+
kernel: number,
|
|
79
|
+
attributes = {},
|
|
80
|
+
bias = true,
|
|
81
|
+
): string =>
|
|
82
|
+
node(
|
|
83
|
+
'conv2d',
|
|
84
|
+
bias
|
|
85
|
+
? [x, w(`${name}.weight`, [to, from, kernel, kernel]), w(`${name}.bias`, [to])]
|
|
86
|
+
: [x, w(`${name}.weight`, [to, from, kernel, kernel])],
|
|
87
|
+
attributes,
|
|
88
|
+
);
|
|
89
|
+
weights.ignore('backbone.embeddings.mask_token');
|
|
90
|
+
weights.ignore('neck.fusion_stage.layers.0.residual_layer1.');
|
|
91
|
+
|
|
92
|
+
const e = 'backbone.embeddings.';
|
|
93
|
+
const patches = rows * cols;
|
|
94
|
+
const tokens = patches + 1;
|
|
95
|
+
const table = node(
|
|
96
|
+
'reshape',
|
|
97
|
+
[w(`${e}position_embeddings`, [1, trainedGrid * trainedGrid + 1, dim])],
|
|
98
|
+
{
|
|
99
|
+
shape: [trainedGrid * trainedGrid + 1, dim],
|
|
100
|
+
},
|
|
101
|
+
);
|
|
102
|
+
let positions = table;
|
|
103
|
+
if (rows !== trainedGrid || cols !== trainedGrid) {
|
|
104
|
+
const grid = node(
|
|
105
|
+
'permute',
|
|
106
|
+
[
|
|
107
|
+
node(
|
|
108
|
+
'reshape',
|
|
109
|
+
[node('slice', [table], { axis: 0, start: 1, end: trainedGrid * trainedGrid + 1 })],
|
|
110
|
+
{
|
|
111
|
+
shape: [trainedGrid, trainedGrid, dim],
|
|
112
|
+
},
|
|
113
|
+
),
|
|
114
|
+
],
|
|
115
|
+
{ order: [2, 0, 1] },
|
|
116
|
+
);
|
|
117
|
+
const resized = node('resize', [grid], {
|
|
118
|
+
height: rows,
|
|
119
|
+
width: cols,
|
|
120
|
+
mode: 'bicubic',
|
|
121
|
+
alignCorners: false,
|
|
122
|
+
});
|
|
123
|
+
positions = node(
|
|
124
|
+
'concat',
|
|
125
|
+
[
|
|
126
|
+
node('slice', [table], { axis: 0, start: 0, end: 1 }),
|
|
127
|
+
node('reshape', [node('permute', [resized], { order: [1, 2, 0] })], {
|
|
128
|
+
shape: [patches, dim],
|
|
129
|
+
}),
|
|
130
|
+
],
|
|
131
|
+
{ axis: 0 },
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
const embedded = node(
|
|
135
|
+
'patchEmbed',
|
|
136
|
+
[
|
|
137
|
+
'image',
|
|
138
|
+
w(`${e}patch_embeddings.projection.weight`, [dim, 3, config.patch, config.patch]),
|
|
139
|
+
w(`${e}patch_embeddings.projection.bias`, [dim]),
|
|
140
|
+
],
|
|
141
|
+
{ patch: config.patch },
|
|
142
|
+
);
|
|
143
|
+
let x = node('add', [
|
|
144
|
+
node(
|
|
145
|
+
'concat',
|
|
146
|
+
[node('reshape', [w(`${e}cls_token`, [1, 1, dim])], { shape: [1, dim] }), embedded],
|
|
147
|
+
{ axis: 0 },
|
|
148
|
+
),
|
|
149
|
+
positions,
|
|
150
|
+
]);
|
|
151
|
+
|
|
152
|
+
const taps: string[] = [];
|
|
153
|
+
for (let i = 0; i < config.depth; i += 1) {
|
|
154
|
+
const p = `backbone.encoder.layer.${i}.`;
|
|
155
|
+
const h = node('layerNorm', [x, w(`${p}norm1.weight`, [dim]), w(`${p}norm1.bias`, [dim])], {
|
|
156
|
+
epsilon: EPSILON,
|
|
157
|
+
});
|
|
158
|
+
const attended = node(
|
|
159
|
+
'attention',
|
|
160
|
+
[
|
|
161
|
+
linear(h, `${p}attention.attention.query`, dim, dim),
|
|
162
|
+
linear(h, `${p}attention.attention.key`, dim, dim),
|
|
163
|
+
linear(h, `${p}attention.attention.value`, dim, dim),
|
|
164
|
+
],
|
|
165
|
+
{ heads },
|
|
166
|
+
);
|
|
167
|
+
const out = linear(attended, `${p}attention.output.dense`, dim, dim);
|
|
168
|
+
x = node('add', [x, node('mul', [out, w(`${p}layer_scale1.lambda1`, [dim])])]);
|
|
169
|
+
const h2 = node('layerNorm', [x, w(`${p}norm2.weight`, [dim]), w(`${p}norm2.bias`, [dim])], {
|
|
170
|
+
epsilon: EPSILON,
|
|
171
|
+
});
|
|
172
|
+
const mlp = linear(
|
|
173
|
+
node('gelu', [linear(h2, `${p}mlp.fc1`, config.hidden, dim)]),
|
|
174
|
+
`${p}mlp.fc2`,
|
|
175
|
+
dim,
|
|
176
|
+
config.hidden,
|
|
177
|
+
);
|
|
178
|
+
x = node('add', [x, node('mul', [mlp, w(`${p}layer_scale2.lambda1`, [dim])])]);
|
|
179
|
+
if (config.taps.includes(i)) {
|
|
180
|
+
taps.push(
|
|
181
|
+
node(
|
|
182
|
+
'layerNorm',
|
|
183
|
+
[x, w('backbone.layernorm.weight', [dim]), w('backbone.layernorm.bias', [dim])],
|
|
184
|
+
{ epsilon: EPSILON },
|
|
185
|
+
),
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const half = (size: number): number => Math.floor((size - 1) / 2) + 1;
|
|
191
|
+
const sizes: [number, number][] = [
|
|
192
|
+
[4 * rows, 4 * cols],
|
|
193
|
+
[2 * rows, 2 * cols],
|
|
194
|
+
[rows, cols],
|
|
195
|
+
[half(rows), half(cols)],
|
|
196
|
+
];
|
|
197
|
+
const levels = taps.map((tap, k) => {
|
|
198
|
+
const channels = config.neck[k] as number;
|
|
199
|
+
const r = `neck.reassemble_stage.layers.${k}.`;
|
|
200
|
+
const image = node(
|
|
201
|
+
'reshape',
|
|
202
|
+
[
|
|
203
|
+
node('permute', [node('slice', [tap], { axis: 0, start: 1, end: tokens })], {
|
|
204
|
+
order: [1, 0],
|
|
205
|
+
}),
|
|
206
|
+
],
|
|
207
|
+
{
|
|
208
|
+
shape: [dim, rows, cols],
|
|
209
|
+
},
|
|
210
|
+
);
|
|
211
|
+
const projected = conv(image, `${r}projection`, channels, dim, 1);
|
|
212
|
+
const resized =
|
|
213
|
+
k === 0
|
|
214
|
+
? node(
|
|
215
|
+
'convTranspose2d',
|
|
216
|
+
[
|
|
217
|
+
projected,
|
|
218
|
+
w(`${r}resize.weight`, [channels, channels, 4, 4]),
|
|
219
|
+
w(`${r}resize.bias`, [channels]),
|
|
220
|
+
],
|
|
221
|
+
{ stride: 4 },
|
|
222
|
+
)
|
|
223
|
+
: k === 1
|
|
224
|
+
? node(
|
|
225
|
+
'convTranspose2d',
|
|
226
|
+
[
|
|
227
|
+
projected,
|
|
228
|
+
w(`${r}resize.weight`, [channels, channels, 2, 2]),
|
|
229
|
+
w(`${r}resize.bias`, [channels]),
|
|
230
|
+
],
|
|
231
|
+
{ stride: 2 },
|
|
232
|
+
)
|
|
233
|
+
: k === 2
|
|
234
|
+
? projected
|
|
235
|
+
: conv(projected, `${r}resize`, channels, channels, 3, { stride: 2, padding: 1 });
|
|
236
|
+
return conv(resized, `neck.convs.${k}`, config.fusion, channels, 3, { padding: 1 }, false);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const f = config.fusion;
|
|
240
|
+
const residual = (value: string, name: string): string => {
|
|
241
|
+
const first = conv(node('relu', [value]), `${name}.convolution1`, f, f, 3, { padding: 1 });
|
|
242
|
+
return node('add', [
|
|
243
|
+
conv(node('relu', [first]), `${name}.convolution2`, f, f, 3, { padding: 1 }),
|
|
244
|
+
value,
|
|
245
|
+
]);
|
|
246
|
+
};
|
|
247
|
+
/* Coarsest first: layer 0 takes the fourth level alone, and each after it adds the next finer. */
|
|
248
|
+
let fused = '';
|
|
249
|
+
for (let n = 0; n < 4; n += 1) {
|
|
250
|
+
const level = levels[3 - n] as string;
|
|
251
|
+
const name = `neck.fusion_stage.layers.${n}`;
|
|
252
|
+
const merged =
|
|
253
|
+
n === 0 ? level : node('add', [fused, residual(level, `${name}.residual_layer1`)]);
|
|
254
|
+
const refined = residual(merged, `${name}.residual_layer2`);
|
|
255
|
+
const size =
|
|
256
|
+
n < 3
|
|
257
|
+
? (sizes[2 - n] as [number, number])
|
|
258
|
+
: ([2 * sizes[0][0], 2 * sizes[0][1]] as [number, number]);
|
|
259
|
+
const up = node('resize', [refined], {
|
|
260
|
+
height: size[0],
|
|
261
|
+
width: size[1],
|
|
262
|
+
mode: 'bilinear',
|
|
263
|
+
alignCorners: true,
|
|
264
|
+
});
|
|
265
|
+
fused = conv(up, `${name}.projection`, f, f, 1);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const first = conv(fused, 'head.conv1', f / 2, f, 3, { padding: 1 });
|
|
269
|
+
const up = node('resize', [first], { height, width, mode: 'bilinear', alignCorners: true });
|
|
270
|
+
const second = node('relu', [
|
|
271
|
+
conv(up, 'head.conv2', config.headHidden, f / 2, 3, { padding: 1 }),
|
|
272
|
+
]);
|
|
273
|
+
node('relu', [conv(second, 'head.conv3', 1, config.headHidden, 1)], {}, 'depth');
|
|
274
|
+
return { inputs: [{ name: 'image', shape: [3, height, width] }], outputs: ['depth'] };
|
|
275
|
+
};
|
|
276
|
+
}
|
|
@@ -0,0 +1,229 @@
|
|
|
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 type { Architecture } from '@driftengine/texture';
|
|
22
|
+
|
|
23
|
+
import { dualDptHead, type HeadConfig } from './dualDpt.ts';
|
|
24
|
+
import { vitBackbone, type VitConfig } from './vit.ts';
|
|
25
|
+
|
|
26
|
+
export interface DepthAnything3Config {
|
|
27
|
+
readonly backbone: VitConfig;
|
|
28
|
+
readonly head: HeadConfig;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const PATCH = 14;
|
|
32
|
+
|
|
33
|
+
/** The two accepted sizes, from each checkpoint's own configuration at its pinned revision. */
|
|
34
|
+
export const DEPTH_ANYTHING_3: Readonly<Record<'small' | 'base', DepthAnything3Config>> = {
|
|
35
|
+
small: {
|
|
36
|
+
backbone: {
|
|
37
|
+
dim: 384,
|
|
38
|
+
heads: 6,
|
|
39
|
+
depth: 12,
|
|
40
|
+
hidden: 1536,
|
|
41
|
+
patch: PATCH,
|
|
42
|
+
trainedGrid: 37,
|
|
43
|
+
taps: [5, 7, 9, 11],
|
|
44
|
+
altStart: 4,
|
|
45
|
+
qkNormStart: 4,
|
|
46
|
+
ropeStart: 4,
|
|
47
|
+
},
|
|
48
|
+
head: { dimIn: 768, features: 64, outChannels: [48, 96, 192, 384], outputDim: 2, patch: PATCH },
|
|
49
|
+
},
|
|
50
|
+
base: {
|
|
51
|
+
backbone: {
|
|
52
|
+
dim: 768,
|
|
53
|
+
heads: 12,
|
|
54
|
+
depth: 12,
|
|
55
|
+
hidden: 3072,
|
|
56
|
+
patch: PATCH,
|
|
57
|
+
trainedGrid: 37,
|
|
58
|
+
taps: [5, 7, 9, 11],
|
|
59
|
+
altStart: 4,
|
|
60
|
+
qkNormStart: 4,
|
|
61
|
+
ropeStart: 4,
|
|
62
|
+
},
|
|
63
|
+
head: {
|
|
64
|
+
dimIn: 1536,
|
|
65
|
+
features: 128,
|
|
66
|
+
outChannels: [96, 192, 384, 768],
|
|
67
|
+
outputDim: 2,
|
|
68
|
+
patch: PATCH,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/** What the upstream checkpoints hold and no graph here reads. */
|
|
74
|
+
const SET_ASIDE = [
|
|
75
|
+
'model.cam_enc.',
|
|
76
|
+
'model.head.scratch.output_conv1_aux.',
|
|
77
|
+
'model.head.scratch.output_conv2_aux.',
|
|
78
|
+
'model.head.scratch.refinenet1_aux.',
|
|
79
|
+
'model.head.scratch.refinenet2_aux.',
|
|
80
|
+
'model.head.scratch.refinenet3_aux.',
|
|
81
|
+
'model.head.scratch.refinenet4_aux.',
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* The graph for `views` images of `height × width` pixels, each a multiple of 14, normalised by
|
|
86
|
+
* ImageNet's mean and deviation. Inputs are `image0` … as `[3, height, width]`; outputs are
|
|
87
|
+
* `logits0` … as `[2, height, width]` and `pose` as `[views, 9]`. The first view is the reference.
|
|
88
|
+
*/
|
|
89
|
+
export function depthAnything3(
|
|
90
|
+
config: DepthAnything3Config,
|
|
91
|
+
views: number,
|
|
92
|
+
height: number,
|
|
93
|
+
width: number,
|
|
94
|
+
): Architecture {
|
|
95
|
+
const rows = height / PATCH;
|
|
96
|
+
const cols = width / PATCH;
|
|
97
|
+
if (!Number.isInteger(rows) || !Number.isInteger(cols)) {
|
|
98
|
+
throw new RangeError(`${height} × ${width} is not a whole number of ${PATCH}-pixel patches`);
|
|
99
|
+
}
|
|
100
|
+
return (weights, graph) => {
|
|
101
|
+
for (const prefix of SET_ASIDE) weights.ignore(prefix);
|
|
102
|
+
const images = Array.from({ length: views }, (_, v) => `image${v}`);
|
|
103
|
+
const { features, cameras } = vitBackbone(
|
|
104
|
+
weights,
|
|
105
|
+
graph,
|
|
106
|
+
config.backbone,
|
|
107
|
+
'model.backbone.pretrained.',
|
|
108
|
+
images,
|
|
109
|
+
rows,
|
|
110
|
+
cols,
|
|
111
|
+
);
|
|
112
|
+
const outputs = images.map((_, v) =>
|
|
113
|
+
dualDptHead(
|
|
114
|
+
weights,
|
|
115
|
+
graph,
|
|
116
|
+
config.head,
|
|
117
|
+
'model.head.',
|
|
118
|
+
features.map((tap) => tap[v] as string),
|
|
119
|
+
rows,
|
|
120
|
+
cols,
|
|
121
|
+
`logits${v}`,
|
|
122
|
+
),
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const width2 = 2 * config.backbone.dim;
|
|
126
|
+
const node = graph.node.bind(graph);
|
|
127
|
+
const layer = (x: string, name: string, to: number): string =>
|
|
128
|
+
node('linear', [
|
|
129
|
+
x,
|
|
130
|
+
weights.read(`model.cam_dec.${name}.weight`, [to, width2]),
|
|
131
|
+
weights.read(`model.cam_dec.${name}.bias`, [to]),
|
|
132
|
+
]);
|
|
133
|
+
const trunk = node('relu', [
|
|
134
|
+
layer(
|
|
135
|
+
node('relu', [layer(cameras[cameras.length - 1] as string, 'backbone.0', width2)]),
|
|
136
|
+
'backbone.2',
|
|
137
|
+
width2,
|
|
138
|
+
),
|
|
139
|
+
]);
|
|
140
|
+
node(
|
|
141
|
+
'concat',
|
|
142
|
+
[
|
|
143
|
+
layer(trunk, 'fc_t', 3),
|
|
144
|
+
layer(trunk, 'fc_qvec', 4),
|
|
145
|
+
node('relu', [layer(trunk, 'fc_fov.0', 2)]),
|
|
146
|
+
],
|
|
147
|
+
{ axis: 1 },
|
|
148
|
+
'pose',
|
|
149
|
+
);
|
|
150
|
+
return {
|
|
151
|
+
inputs: images.map((name) => ({ name, shape: [3, height, width] })),
|
|
152
|
+
outputs: [...outputs, 'pose'],
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Depth and confidence from one view's logits, `[2, height, width]`: `exp` of the first channel,
|
|
159
|
+
* and one more than `exp` of the second, into the caller's maps.
|
|
160
|
+
*/
|
|
161
|
+
export function decodeDepth(
|
|
162
|
+
logits: Float32Array,
|
|
163
|
+
depth: Float32Array,
|
|
164
|
+
confidence: Float32Array,
|
|
165
|
+
): void {
|
|
166
|
+
const pixels = depth.length;
|
|
167
|
+
for (let i = 0; i < pixels; i += 1) {
|
|
168
|
+
depth[i] = exactExp(logits[i] as number);
|
|
169
|
+
confidence[i] = exactExp(logits[pixels + i] as number) + 1;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* One view's camera from its pose encoding: `worldToCamera`, 3 × 4 row-major — the inverse of the
|
|
175
|
+
* camera-to-world rotation and translation the decoder predicts — and `intrinsics`, 3 × 3 row-major,
|
|
176
|
+
* with focal lengths from the two fields of view and the principal point at the centre.
|
|
177
|
+
*/
|
|
178
|
+
export function decodeCamera(
|
|
179
|
+
pose: Float32Array,
|
|
180
|
+
at: number,
|
|
181
|
+
height: number,
|
|
182
|
+
width: number,
|
|
183
|
+
worldToCamera: Float32Array,
|
|
184
|
+
intrinsics: Float32Array,
|
|
185
|
+
): void {
|
|
186
|
+
const p = at * 9;
|
|
187
|
+
const tx = pose[p] as number;
|
|
188
|
+
const ty = pose[p + 1] as number;
|
|
189
|
+
const tz = pose[p + 2] as number;
|
|
190
|
+
const i = pose[p + 3] as number;
|
|
191
|
+
const j = pose[p + 4] as number;
|
|
192
|
+
const k = pose[p + 5] as number;
|
|
193
|
+
const r = pose[p + 6] as number;
|
|
194
|
+
const s = 2 / (i * i + j * j + k * k + r * r);
|
|
195
|
+
/*
|
|
196
|
+
* The camera-to-world rotation is `quat_to_mat`'s; its inverse is the transpose, so row `n` here
|
|
197
|
+
* is column `n` there, and the translation is carried back through it.
|
|
198
|
+
*/
|
|
199
|
+
const r00 = 1 - s * (j * j + k * k);
|
|
200
|
+
const r01 = s * (i * j - k * r);
|
|
201
|
+
const r02 = s * (i * k + j * r);
|
|
202
|
+
const r10 = s * (i * j + k * r);
|
|
203
|
+
const r11 = 1 - s * (i * i + k * k);
|
|
204
|
+
const r12 = s * (j * k - i * r);
|
|
205
|
+
const r20 = s * (i * k - j * r);
|
|
206
|
+
const r21 = s * (j * k + i * r);
|
|
207
|
+
const r22 = 1 - s * (i * i + j * j);
|
|
208
|
+
worldToCamera[0] = r00;
|
|
209
|
+
worldToCamera[1] = r10;
|
|
210
|
+
worldToCamera[2] = r20;
|
|
211
|
+
worldToCamera[3] = -(r00 * tx + r10 * ty + r20 * tz);
|
|
212
|
+
worldToCamera[4] = r01;
|
|
213
|
+
worldToCamera[5] = r11;
|
|
214
|
+
worldToCamera[6] = r21;
|
|
215
|
+
worldToCamera[7] = -(r01 * tx + r11 * ty + r21 * tz);
|
|
216
|
+
worldToCamera[8] = r02;
|
|
217
|
+
worldToCamera[9] = r12;
|
|
218
|
+
worldToCamera[10] = r22;
|
|
219
|
+
worldToCamera[11] = -(r02 * tx + r12 * ty + r22 * tz);
|
|
220
|
+
const tangent = (angle: number): number => exactSin(angle / 2) / exactCos(angle / 2);
|
|
221
|
+
const fy = height / 2 / Math.max(tangent(pose[p + 7] as number), 1e-6);
|
|
222
|
+
const fx = width / 2 / Math.max(tangent(pose[p + 8] as number), 1e-6);
|
|
223
|
+
intrinsics.fill(0);
|
|
224
|
+
intrinsics[0] = fx;
|
|
225
|
+
intrinsics[2] = width / 2;
|
|
226
|
+
intrinsics[4] = fy;
|
|
227
|
+
intrinsics[5] = height / 2;
|
|
228
|
+
intrinsics[8] = 1;
|
|
229
|
+
}
|