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