oidn-webgpu 0.0.0-stage → 0.6.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/CHANGELOG.md +182 -0
- package/LICENSE +21 -0
- package/README.md +89 -2
- package/dist/oidn.js +6140 -0
- package/dist/oidn.umd.cjs +1513 -0
- package/lib/UNet.d.ts +161 -0
- package/lib/UNet.js +536 -0
- package/lib/UNet.js.map +1 -0
- package/lib/WGPUComputePass.d.ts +62 -0
- package/lib/WGPUComputePass.js +278 -0
- package/lib/WGPUComputePass.js.map +1 -0
- package/lib/WGPUFullQuadPass.d.ts +50 -0
- package/lib/WGPUFullQuadPass.js +261 -0
- package/lib/WGPUFullQuadPass.js.map +1 -0
- package/lib/backend.d.ts +1 -0
- package/lib/backend.js +39 -0
- package/lib/backend.js.map +1 -0
- package/lib/finalRgbShader.d.ts +13 -0
- package/lib/finalRgbShader.js +160 -0
- package/lib/finalRgbShader.js.map +1 -0
- package/lib/gpuTypes.d.ts +158 -0
- package/lib/gpuTypes.js +27 -0
- package/lib/gpuTypes.js.map +1 -0
- package/lib/graphOptimizer.d.ts +54 -0
- package/lib/graphOptimizer.js +216 -0
- package/lib/graphOptimizer.js.map +1 -0
- package/lib/hdr.d.ts +31 -0
- package/lib/hdr.js +340 -0
- package/lib/hdr.js.map +1 -0
- package/lib/hdrTransfer.d.ts +22 -0
- package/lib/hdrTransfer.js +128 -0
- package/lib/hdrTransfer.js.map +1 -0
- package/lib/main.d.ts +57 -0
- package/lib/main.js +24 -0
- package/lib/main.js.map +1 -0
- package/lib/modelSpec.d.ts +80 -0
- package/lib/modelSpec.js +270 -0
- package/lib/modelSpec.js.map +1 -0
- package/lib/native/denoise.d.ts +25 -0
- package/lib/native/denoise.js +223 -0
- package/lib/native/denoise.js.map +1 -0
- package/lib/native/program.d.ts +53 -0
- package/lib/native/program.js +310 -0
- package/lib/native/program.js.map +1 -0
- package/lib/native/recordingDevice.d.ts +125 -0
- package/lib/native/recordingDevice.js +247 -0
- package/lib/native/recordingDevice.js.map +1 -0
- package/lib/nativeUNet.d.ts +25 -0
- package/lib/nativeUNet.js +184 -0
- package/lib/nativeUNet.js.map +1 -0
- package/lib/nativeUNetCore.d.ts +202 -0
- package/lib/nativeUNetCore.js +2945 -0
- package/lib/nativeUNetCore.js.map +1 -0
- package/lib/process.d.ts +37 -0
- package/lib/process.js +328 -0
- package/lib/process.js.map +1 -0
- package/lib/resourceTracker.d.ts +26 -0
- package/lib/resourceTracker.js +65 -0
- package/lib/resourceTracker.js.map +1 -0
- package/lib/sanitize.d.ts +15 -0
- package/lib/sanitize.js +28 -0
- package/lib/sanitize.js.map +1 -0
- package/lib/tileScheduler.d.ts +61 -0
- package/lib/tileScheduler.js +199 -0
- package/lib/tileScheduler.js.map +1 -0
- package/lib/tza.d.ts +15 -0
- package/lib/tza.js +114 -0
- package/lib/tza.js.map +1 -0
- package/lib/webnnUNet.d.ts +52 -0
- package/lib/webnnUNet.js +535 -0
- package/lib/webnnUNet.js.map +1 -0
- package/package.json +43 -4
- package/src/UNet.ts +861 -0
- package/src/WGPUComputePass.ts +382 -0
- package/src/WGPUFullQuadPass.ts +348 -0
- package/src/backend.ts +42 -0
- package/src/finalRgbShader.ts +186 -0
- package/src/gpuTypes.ts +179 -0
- package/src/graphOptimizer.ts +301 -0
- package/src/hdr.ts +398 -0
- package/src/hdrTransfer.ts +169 -0
- package/src/main.ts +126 -0
- package/src/modelSpec.ts +414 -0
- package/src/native/core-profile.json +40 -0
- package/src/native/denoise.ts +268 -0
- package/src/native/program.ts +326 -0
- package/src/native/recordingDevice.ts +341 -0
- package/src/nativeUNet.ts +269 -0
- package/src/nativeUNetCore.ts +3735 -0
- package/src/process.ts +368 -0
- package/src/resourceTracker.ts +94 -0
- package/src/sanitize.ts +29 -0
- package/src/tileScheduler.ts +330 -0
- package/src/tza.ts +136 -0
- package/src/webnnUNet.ts +812 -0
package/src/hdr.ts
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
// https://github.com/bbbbx/hdr.js/blob/main/src/hdr.ts
|
|
2
|
+
function frexp(v: number): { f: number; e: number } {
|
|
3
|
+
v = Number(v);
|
|
4
|
+
const result = { f: v, e: 0 };
|
|
5
|
+
|
|
6
|
+
if (v !== 0 && Number.isFinite(v)) {
|
|
7
|
+
const absV = Math.abs(v);
|
|
8
|
+
const log2 =
|
|
9
|
+
Math.log2 ||
|
|
10
|
+
function log2(n) {
|
|
11
|
+
return Math.log(n) * Math.LOG2E;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// Math.pow(2, -exp) === Infinity when exp <= -1024
|
|
15
|
+
let e = Math.max(-1023, Math.floor(log2(absV)) + 1);
|
|
16
|
+
let f = absV * Math.pow(2.0, -e);
|
|
17
|
+
|
|
18
|
+
while (f >= 1.0) {
|
|
19
|
+
f *= 0.5;
|
|
20
|
+
e++;
|
|
21
|
+
}
|
|
22
|
+
while (f < 0.5) {
|
|
23
|
+
f *= 2;
|
|
24
|
+
e--;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (v < 0) {
|
|
28
|
+
f = -f;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
result.f = f;
|
|
32
|
+
result.e = e;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function ldexp(f: number, e: number) {
|
|
39
|
+
return f * Math.pow(2.0, e);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Convert 4 byte uint8 buffer to 3 channels float data
|
|
44
|
+
* @param rgbe input uint8 buffer
|
|
45
|
+
* @param float output float data
|
|
46
|
+
*/
|
|
47
|
+
function rgbe2float(rgbe: Uint8Array, float: Float32Array) {
|
|
48
|
+
if (rgbe[3] !== 0) {
|
|
49
|
+
const f1 = ldexp(1.0, rgbe[3] - (128 + 8));
|
|
50
|
+
float[0] = rgbe[0] * f1;
|
|
51
|
+
float[1] = rgbe[1] * f1;
|
|
52
|
+
float[2] = rgbe[2] * f1;
|
|
53
|
+
} else {
|
|
54
|
+
float[0] = float[1] = float[2] = 0.0;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Convert 3 channels float data to 4 byte uint8 buffer
|
|
60
|
+
* @param float input float data
|
|
61
|
+
* @param rgbe output uint8 buffer
|
|
62
|
+
*/
|
|
63
|
+
function float2rgbe(float: Float32Array, rgbe: Uint8Array) {
|
|
64
|
+
const red = float[0];
|
|
65
|
+
const green = float[1];
|
|
66
|
+
const blue = float[2];
|
|
67
|
+
const v = Math.max(red, Math.max(green, blue));
|
|
68
|
+
if (v < 1e-32) {
|
|
69
|
+
rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
|
|
70
|
+
} else {
|
|
71
|
+
const { f, e } = frexp(v);
|
|
72
|
+
const s = (f / v) * 256.0;
|
|
73
|
+
rgbe[0] = red * s;
|
|
74
|
+
rgbe[1] = green * s;
|
|
75
|
+
rgbe[2] = blue * s;
|
|
76
|
+
rgbe[3] = e + 128;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Write float data to RGBE(.hdr) file buffer
|
|
82
|
+
* @param w image width
|
|
83
|
+
* @param h image height
|
|
84
|
+
* @param data float data, RGB 3 channels.
|
|
85
|
+
* @returns file buffer
|
|
86
|
+
*/
|
|
87
|
+
function write_hdr(w: number, h: number, data: Float32Array): Uint8Array {
|
|
88
|
+
const s: number[] = [];
|
|
89
|
+
const comp = 3;
|
|
90
|
+
write_hdr_core(s, w, h, comp, data);
|
|
91
|
+
return new Uint8Array(s);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function write_hdr_core(
|
|
95
|
+
s: number[],
|
|
96
|
+
x: number,
|
|
97
|
+
y: number,
|
|
98
|
+
comp: number,
|
|
99
|
+
data: Float32Array
|
|
100
|
+
): void {
|
|
101
|
+
if (y <= 0 || x <= 0 || !data) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const header =
|
|
106
|
+
'#?RADIANCE\n' +
|
|
107
|
+
'FORMAT=32-bit_rle_rgbe\n' +
|
|
108
|
+
'EXPOSURE=1.0\n' +
|
|
109
|
+
'\n' +
|
|
110
|
+
'-Y ' +
|
|
111
|
+
y +
|
|
112
|
+
' +X ' +
|
|
113
|
+
x +
|
|
114
|
+
'\n';
|
|
115
|
+
header.split('').forEach((c) => {
|
|
116
|
+
const charCode = c.charCodeAt(0);
|
|
117
|
+
s.push(charCode);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const scratch = new Uint8Array(x * 4);
|
|
121
|
+
const stbi__flip_vertically_on_write = false;
|
|
122
|
+
for (let i = 0; i < y; i++) {
|
|
123
|
+
write_hdr_scanline(
|
|
124
|
+
s,
|
|
125
|
+
x,
|
|
126
|
+
comp,
|
|
127
|
+
scratch,
|
|
128
|
+
data.subarray(comp * x * (stbi__flip_vertically_on_write ? y - 1 - i : i))
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function write_hdr_scanline(
|
|
134
|
+
s: number[],
|
|
135
|
+
width: number,
|
|
136
|
+
ncomp: number,
|
|
137
|
+
scratch: Uint8Array,
|
|
138
|
+
scanline: Float32Array
|
|
139
|
+
): void {
|
|
140
|
+
const scanlineheader = new Uint8Array([2, 2, 0, 0]);
|
|
141
|
+
const rgbe = new Uint8Array(4);
|
|
142
|
+
const linear = new Float32Array(3);
|
|
143
|
+
let x;
|
|
144
|
+
|
|
145
|
+
scanlineheader[2] = (width & 0xff00) >> 8;
|
|
146
|
+
scanlineheader[3] = width & 0x00ff;
|
|
147
|
+
|
|
148
|
+
/* skip RLE for images too small or large */
|
|
149
|
+
if (width < 8 || width >= 32768) {
|
|
150
|
+
for (x = 0; x < width; x++) {
|
|
151
|
+
switch (ncomp) {
|
|
152
|
+
case 4: /* fallthrough */
|
|
153
|
+
case 3:
|
|
154
|
+
linear[2] = scanline[x * ncomp + 2];
|
|
155
|
+
linear[1] = scanline[x * ncomp + 1];
|
|
156
|
+
linear[0] = scanline[x * ncomp + 0];
|
|
157
|
+
break;
|
|
158
|
+
default:
|
|
159
|
+
linear[0] = linear[1] = linear[2] = scanline[x * ncomp + 0];
|
|
160
|
+
break;
|
|
161
|
+
}
|
|
162
|
+
float2rgbe(linear, rgbe);
|
|
163
|
+
s.push(rgbe[0], rgbe[1], rgbe[2], rgbe[3]);
|
|
164
|
+
}
|
|
165
|
+
} else {
|
|
166
|
+
let c, r;
|
|
167
|
+
/* encode into scratch buffer */
|
|
168
|
+
for (x = 0; x < width; x++) {
|
|
169
|
+
switch (ncomp) {
|
|
170
|
+
case 4: /* fallthrough */
|
|
171
|
+
case 3:
|
|
172
|
+
linear[2] = scanline[x * ncomp + 2];
|
|
173
|
+
linear[1] = scanline[x * ncomp + 1];
|
|
174
|
+
linear[0] = scanline[x * ncomp + 0];
|
|
175
|
+
break;
|
|
176
|
+
default:
|
|
177
|
+
linear[0] = linear[1] = linear[2] = scanline[x * ncomp + 0];
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
float2rgbe(linear, rgbe);
|
|
181
|
+
scratch[x + width * 0] = rgbe[0];
|
|
182
|
+
scratch[x + width * 1] = rgbe[1];
|
|
183
|
+
scratch[x + width * 2] = rgbe[2];
|
|
184
|
+
scratch[x + width * 3] = rgbe[3];
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
s.push(
|
|
188
|
+
scanlineheader[0],
|
|
189
|
+
scanlineheader[1],
|
|
190
|
+
scanlineheader[2],
|
|
191
|
+
scanlineheader[3]
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
/* RLE each component separately */
|
|
195
|
+
for (c = 0; c < 4; c++) {
|
|
196
|
+
const comp = scratch.subarray(width * c);
|
|
197
|
+
|
|
198
|
+
x = 0;
|
|
199
|
+
while (x < width) {
|
|
200
|
+
// find first run
|
|
201
|
+
r = x;
|
|
202
|
+
while (r + 2 < width) {
|
|
203
|
+
if (comp[r] === comp[r + 1] && comp[r] === comp[r + 2]) break;
|
|
204
|
+
++r;
|
|
205
|
+
}
|
|
206
|
+
if (r + 2 >= width) r = width;
|
|
207
|
+
// dump up to first run
|
|
208
|
+
while (x < r) {
|
|
209
|
+
let len = r - x;
|
|
210
|
+
if (len > 128) len = 128;
|
|
211
|
+
write_dump_data(s, len, comp.subarray(x));
|
|
212
|
+
x += len;
|
|
213
|
+
}
|
|
214
|
+
// if there's a run, output it
|
|
215
|
+
if (r + 2 < width) {
|
|
216
|
+
// same test as what we break out of in search loop, so only true if we break'd
|
|
217
|
+
// find next byte after run
|
|
218
|
+
while (r < width && comp[r] == comp[x]) ++r;
|
|
219
|
+
// output run up to r
|
|
220
|
+
while (x < r) {
|
|
221
|
+
let len = r - x;
|
|
222
|
+
if (len > 127) len = 127;
|
|
223
|
+
write_run_data(s, len, comp[x]);
|
|
224
|
+
x += len;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function write_dump_data(s: number[], length: number, data: Uint8Array): void {
|
|
233
|
+
const lengthbyte = length & 0xff;
|
|
234
|
+
if (!(length <= 128)) throw new Error('length is greater than 128');
|
|
235
|
+
s.push(lengthbyte);
|
|
236
|
+
for (let i = 0; i < length; i++) {
|
|
237
|
+
s.push(data[i]);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function write_run_data(s: number[], length: number, databyte: number): void {
|
|
242
|
+
const lengthbyte = (length + 128) & 0xff;
|
|
243
|
+
if (!(length + 128 <= 255)) throw new Error('length is greater than 128');
|
|
244
|
+
s.push(lengthbyte, databyte);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function testMagic(uint8: Uint8Array, signature: string): boolean {
|
|
248
|
+
const l = signature.length;
|
|
249
|
+
for (let i = 0; i < l; i++) {
|
|
250
|
+
if (uint8[i] !== signature.charCodeAt(i)) {
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return true;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function isHdr(uint8: Uint8Array): boolean {
|
|
258
|
+
let r = testMagic(uint8, '#?RADIANCE\n');
|
|
259
|
+
if (!r) {
|
|
260
|
+
r = testMagic(uint8, '#?RGBE\n');
|
|
261
|
+
}
|
|
262
|
+
return r;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const MAX_HEADER_LENGTH = 1024 * 10;
|
|
266
|
+
const MAX_DIMENSIONS = 1 << 24;
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Read a RGBE(.hdr) file from buffer
|
|
270
|
+
* @param uint8 RGBE(.hdr) file buffer
|
|
271
|
+
* @returns Failure reason or resolved data.
|
|
272
|
+
*/
|
|
273
|
+
function read_hdr(uint8: Uint8Array):
|
|
274
|
+
| string
|
|
275
|
+
| {
|
|
276
|
+
rgbFloat: Float32Array;
|
|
277
|
+
width: number;
|
|
278
|
+
height: number;
|
|
279
|
+
} {
|
|
280
|
+
let header = '';
|
|
281
|
+
let pos = 0;
|
|
282
|
+
|
|
283
|
+
if (!isHdr(uint8)) {
|
|
284
|
+
return 'Corrupt HDR image.';
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// read header
|
|
288
|
+
while (!header.match(/\n\n[^\n]+\n/g) && pos < MAX_HEADER_LENGTH) {
|
|
289
|
+
header += String.fromCharCode(uint8[pos++]);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// check format
|
|
293
|
+
const format = header.match(/FORMAT=(.*)$/m)?.[1];
|
|
294
|
+
if (format !== '32-bit_rle_rgbe') {
|
|
295
|
+
return 'Unsupported HDR format: ' + format;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// parse resolution
|
|
299
|
+
const rez: string[] = header.split(/\n/).reverse()[1].split(' ');
|
|
300
|
+
if (rez[0] !== '-Y' || rez[2] !== '+X') {
|
|
301
|
+
return 'Unsupported HDR format';
|
|
302
|
+
}
|
|
303
|
+
const width = Number.parseFloat(rez[3]);
|
|
304
|
+
const height = Number.parseFloat(rez[1]);
|
|
305
|
+
if (width > MAX_DIMENSIONS || height > MAX_DIMENSIONS) {
|
|
306
|
+
return 'Very large image (corrupt?)';
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
let i, j;
|
|
310
|
+
let c1: number = uint8[pos];
|
|
311
|
+
let c2: number = uint8[pos + 1];
|
|
312
|
+
let len: number = uint8[pos + 2];
|
|
313
|
+
|
|
314
|
+
// not run-length encoded, so we have to actually use THIS data as a decoded
|
|
315
|
+
// pixel (note this can't be a valid pixel--one of RGB must be >= 128)
|
|
316
|
+
const notRLE: boolean = c1 !== 2 || c2 !== 2 || !!(len & 0x80); // not run-length encoded
|
|
317
|
+
|
|
318
|
+
const hdrData = new Float32Array(width * height * 3);
|
|
319
|
+
if (width < 8 || width >= 32768 || notRLE) {
|
|
320
|
+
// 32768: 2^15
|
|
321
|
+
// Read flat data
|
|
322
|
+
for (j = 0; j < height; ++j) {
|
|
323
|
+
for (i = 0; i < width; ++i) {
|
|
324
|
+
const rgbe = uint8.subarray(pos, pos + 4);
|
|
325
|
+
pos += 4;
|
|
326
|
+
const start = (j * width + i) * 3;
|
|
327
|
+
rgbe2float(rgbe, hdrData.subarray(start, start + 3));
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
} else {
|
|
331
|
+
// Read RLE-encoded data
|
|
332
|
+
let scanline: Uint8Array | undefined;
|
|
333
|
+
let c1: number;
|
|
334
|
+
let c2: number;
|
|
335
|
+
let len: number;
|
|
336
|
+
for (let j = 0; j < height; j++) {
|
|
337
|
+
c1 = uint8[pos++];
|
|
338
|
+
c2 = uint8[pos++];
|
|
339
|
+
len = uint8[pos++];
|
|
340
|
+
if (c1 !== 2 || c2 !== 2 || len & 0x80) {
|
|
341
|
+
return 'Invalid scanline';
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
len = len << 8;
|
|
345
|
+
len |= uint8[pos++];
|
|
346
|
+
if (len !== width) {
|
|
347
|
+
return 'invalid decoded scanline length';
|
|
348
|
+
}
|
|
349
|
+
if (!scanline) {
|
|
350
|
+
scanline = new Uint8Array(width * 4);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
let count: number;
|
|
354
|
+
let value: number;
|
|
355
|
+
for (let k = 0; k < 4; k++) {
|
|
356
|
+
let nLeft: number;
|
|
357
|
+
i = 0;
|
|
358
|
+
while ((nLeft = width - i) > 0) {
|
|
359
|
+
count = uint8[pos++];
|
|
360
|
+
if (count > 128) {
|
|
361
|
+
// is RUN
|
|
362
|
+
value = uint8[pos++];
|
|
363
|
+
count -= 128;
|
|
364
|
+
if (count > nLeft) {
|
|
365
|
+
return 'bad RLE data in HDR';
|
|
366
|
+
}
|
|
367
|
+
for (let z = 0; z < count; z++) {
|
|
368
|
+
scanline[i++ * 4 + k] = value;
|
|
369
|
+
}
|
|
370
|
+
} else {
|
|
371
|
+
// is DUMP
|
|
372
|
+
if (count > nLeft) {
|
|
373
|
+
return 'bad RLE data in HDR';
|
|
374
|
+
}
|
|
375
|
+
for (let z = 0; z < count; z++) {
|
|
376
|
+
scanline[i++ * 4 + k] = uint8[pos++];
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
for (let i = 0; i < width; i++) {
|
|
383
|
+
rgbe2float(
|
|
384
|
+
scanline.subarray(i * 4),
|
|
385
|
+
hdrData.subarray((j * width + i) * 3)
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return {
|
|
392
|
+
rgbFloat: hdrData,
|
|
393
|
+
width: width,
|
|
394
|
+
height: height
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
export { read_hdr as readHDR, write_hdr as writeHDR, float2rgbe, rgbe2float };
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { FLT_MAX, sanitize } from './sanitize.js';
|
|
2
|
+
|
|
3
|
+
/** HDR transfer functions used by the OIDN input and output processing passes. */
|
|
4
|
+
export type HDRTransfer = 'pu' | 'log';
|
|
5
|
+
|
|
6
|
+
const a = 1.41283765e3;
|
|
7
|
+
const b = 1.64593172;
|
|
8
|
+
const c = 4.31384981e-1;
|
|
9
|
+
const d = -2.94139609e-3;
|
|
10
|
+
const e = 1.92653254e-1;
|
|
11
|
+
const f = 6.26026094e-3;
|
|
12
|
+
const g = 9.98620152e-1;
|
|
13
|
+
const y0 = 1.5794576e-6;
|
|
14
|
+
const y1 = 3.22087631e-2;
|
|
15
|
+
const x0 = 2.23151711e-3;
|
|
16
|
+
const x1 = 3.70974749e-1;
|
|
17
|
+
const yMax = 65504;
|
|
18
|
+
const puXMax = puForward(yMax);
|
|
19
|
+
const puNormScale = 1 / puXMax;
|
|
20
|
+
const puRcpNormScale = puXMax;
|
|
21
|
+
const logXMax = Math.log(yMax + 1);
|
|
22
|
+
const logNormScale = 1 / logXMax;
|
|
23
|
+
|
|
24
|
+
function puForward(y: number) {
|
|
25
|
+
if (y <= y0) return a * y;
|
|
26
|
+
if (y <= y1) return b * Math.pow(y, c) + d;
|
|
27
|
+
return e * Math.log(y + f) + g;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function puInverse(x: number) {
|
|
31
|
+
if (x <= x0) return x / a;
|
|
32
|
+
if (x <= x1) return Math.pow((x - d) / b, 1 / c);
|
|
33
|
+
return Math.exp((x - g) / e) - f;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function forward(y: number, transfer: HDRTransfer) {
|
|
37
|
+
return transfer === 'log'
|
|
38
|
+
? Math.log(y + 1) * logNormScale
|
|
39
|
+
: puForward(y) * puNormScale;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function inverse(x: number, transfer: HDRTransfer) {
|
|
43
|
+
return transfer === 'log'
|
|
44
|
+
? Math.exp(x * logXMax) - 1
|
|
45
|
+
: puInverse(x * puRcpNormScale);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function hdrTransferFuncCPU({
|
|
49
|
+
data,
|
|
50
|
+
channels,
|
|
51
|
+
inputScale,
|
|
52
|
+
transfer = 'pu'
|
|
53
|
+
}: {
|
|
54
|
+
data: Float32Array;
|
|
55
|
+
channels: number;
|
|
56
|
+
inputScale: number;
|
|
57
|
+
transfer?: HDRTransfer;
|
|
58
|
+
}) {
|
|
59
|
+
const newData = new Float32Array(data);
|
|
60
|
+
for (let i = 0; i < newData.length; i += channels) {
|
|
61
|
+
for (let channel = 0; channel < 3; channel++) {
|
|
62
|
+
newData[i + channel] = forward(
|
|
63
|
+
sanitize(newData[i + channel] * inputScale, 0, FLT_MAX),
|
|
64
|
+
transfer
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return newData;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function hdrTransferFuncInverseCPU({
|
|
72
|
+
data,
|
|
73
|
+
channels,
|
|
74
|
+
inputScale,
|
|
75
|
+
transfer = 'pu'
|
|
76
|
+
}: {
|
|
77
|
+
data: Float32Array;
|
|
78
|
+
channels: number;
|
|
79
|
+
inputScale: number;
|
|
80
|
+
transfer?: HDRTransfer;
|
|
81
|
+
}) {
|
|
82
|
+
const newData = new Float32Array(data);
|
|
83
|
+
const outputScale = 1 / inputScale;
|
|
84
|
+
for (let i = 0; i < newData.length; i += channels) {
|
|
85
|
+
for (let channel = 0; channel < 3; channel++) {
|
|
86
|
+
// The network output may contain negative values or NaNs.
|
|
87
|
+
newData[i + channel] = inverse(sanitize(newData[i + channel], 0, FLT_MAX), transfer) * outputScale;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return newData;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// TODO put in gpu
|
|
94
|
+
export function avgLogLum({
|
|
95
|
+
data,
|
|
96
|
+
channels
|
|
97
|
+
}: {
|
|
98
|
+
data: Float32Array;
|
|
99
|
+
channels: number;
|
|
100
|
+
}) {
|
|
101
|
+
let totalLuminance = 0;
|
|
102
|
+
|
|
103
|
+
for (let i = 0; i < data.length; i += channels) {
|
|
104
|
+
const r = sanitize(data[i], 0, FLT_MAX);
|
|
105
|
+
const g = sanitize(data[i + 1], 0, FLT_MAX);
|
|
106
|
+
const b = sanitize(data[i + 2], 0, FLT_MAX);
|
|
107
|
+
const lum = 0.212671 * r + 0.71516 * g + 0.072169 * b;
|
|
108
|
+
|
|
109
|
+
// TODO performance
|
|
110
|
+
// https://github.com/RenderKit/oidn/blob/713ec7838ba650f99e0a896549c0dca5eeb3652d/devices/cpu/cpu_autoexposure.cpp#L43
|
|
111
|
+
totalLuminance += Math.log2(lum + 0.0001);
|
|
112
|
+
}
|
|
113
|
+
const numPixels = data.length / channels;
|
|
114
|
+
const averageLuminance = totalLuminance / numPixels;
|
|
115
|
+
const key = 0.18;
|
|
116
|
+
|
|
117
|
+
// https://github.com/RenderKit/oidn/blob/713ec7838ba650f99e0a896549c0dca5eeb3652d/training/color.py#L173
|
|
118
|
+
return key / Math.pow(2, averageLuminance);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const transferConstantsWGSL = `
|
|
122
|
+
const a = ${a};
|
|
123
|
+
const b = ${b};
|
|
124
|
+
const c = ${c};
|
|
125
|
+
const d = ${d};
|
|
126
|
+
const e = ${e};
|
|
127
|
+
const f = ${f};
|
|
128
|
+
const g = ${g};
|
|
129
|
+
const y0 =${y0};
|
|
130
|
+
const y1 =${y1};
|
|
131
|
+
const x0 =${x0};
|
|
132
|
+
const x1 =${x1};
|
|
133
|
+
|
|
134
|
+
const normScale = ${puNormScale};
|
|
135
|
+
const rcpNormScale = ${puRcpNormScale};
|
|
136
|
+
`;
|
|
137
|
+
|
|
138
|
+
/** WGSL declarations of `fn HDRForward(y: f32) -> f32` for the input pass. */
|
|
139
|
+
export function hdrForwardWGSL(transfer: HDRTransfer) {
|
|
140
|
+
const forwardCode = transfer === 'log'
|
|
141
|
+
? `fn HDRForward(y: f32) -> f32 { return log(y + 1.0) * logNormScale; }`
|
|
142
|
+
: `fn HDRForward(y: f32) -> f32 {
|
|
143
|
+
if (y <= y0) { return a * y * normScale; }
|
|
144
|
+
else if (y <= y1) { return (b * pow(y, c) + d) * normScale; }
|
|
145
|
+
else { return (e * log(y + f) + g) * normScale; }
|
|
146
|
+
}`;
|
|
147
|
+
return /* wgsl */ `
|
|
148
|
+
${transferConstantsWGSL}
|
|
149
|
+
const logXMax = ${logXMax};
|
|
150
|
+
const logNormScale = ${logNormScale};
|
|
151
|
+
${forwardCode}`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** WGSL declarations of `fn HDRInverse(x: f32) -> f32` for the output pass. */
|
|
155
|
+
export function hdrInverseWGSL(transfer: HDRTransfer) {
|
|
156
|
+
const inverseCode = transfer === 'log'
|
|
157
|
+
? `fn HDRInverse(x: f32) -> f32 { return exp(x * logXMax) - 1.0; }`
|
|
158
|
+
: `fn HDRInverse(x: f32) -> f32 {
|
|
159
|
+
let y = x * rcpNormScale;
|
|
160
|
+
if (y <= x0) { return y / a; }
|
|
161
|
+
else if (y <= x1) { return pow((y - d) / b, 1 / c); }
|
|
162
|
+
else { return exp((y - g) / e) - f; }
|
|
163
|
+
}`;
|
|
164
|
+
return /* wgsl */ `
|
|
165
|
+
${transferConstantsWGSL}
|
|
166
|
+
const logXMax = ${logXMax};
|
|
167
|
+
${inverseCode}
|
|
168
|
+
`;
|
|
169
|
+
}
|
package/src/main.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { parseTZA } from './tza';
|
|
2
|
+
import UNet from './UNet';
|
|
3
|
+
import type { UNetEngineSetting, UNetExecutionStats } from './UNet';
|
|
4
|
+
import { initWebGPUBackend } from './backend';
|
|
5
|
+
import type { DynamicTileSetting } from './tileScheduler';
|
|
6
|
+
import type { UNetModelSpec } from './modelSpec';
|
|
7
|
+
import type {
|
|
8
|
+
NativeSubgroupMatrixOptions,
|
|
9
|
+
NativeUNetGemmOptions,
|
|
10
|
+
NativeUNetKernelSetting,
|
|
11
|
+
NativeUNetPrecisionSetting
|
|
12
|
+
} from './nativeUNet';
|
|
13
|
+
import type { HDRTransfer } from './process';
|
|
14
|
+
|
|
15
|
+
export { parseTZA, UNet };
|
|
16
|
+
export { planTileGrid } from './tileScheduler';
|
|
17
|
+
export type {
|
|
18
|
+
DynamicTileOptions,
|
|
19
|
+
DynamicTileSetting,
|
|
20
|
+
PlannedTile,
|
|
21
|
+
TilePlan,
|
|
22
|
+
TileRect
|
|
23
|
+
} from './tileScheduler';
|
|
24
|
+
export {
|
|
25
|
+
detectUNetModelSpec,
|
|
26
|
+
OIDN_UNET_LARGE_SPEC,
|
|
27
|
+
OIDN_UNET_SMALL_SPEC,
|
|
28
|
+
validateUNetModel
|
|
29
|
+
} from './modelSpec';
|
|
30
|
+
export type {
|
|
31
|
+
ModelNodeSpec,
|
|
32
|
+
UNetModelGraph,
|
|
33
|
+
UNetModelSpec,
|
|
34
|
+
ValidatedUNetModel
|
|
35
|
+
} from './modelSpec';
|
|
36
|
+
export { optimizeModelGraph, planModelExecution } from './graphOptimizer';
|
|
37
|
+
export type {
|
|
38
|
+
ExecutableModelNode,
|
|
39
|
+
GraphOptimizationOptions,
|
|
40
|
+
ModelExecutionPlan,
|
|
41
|
+
ModelValueShape,
|
|
42
|
+
OptimizedModelGraph
|
|
43
|
+
} from './graphOptimizer';
|
|
44
|
+
export {
|
|
45
|
+
NativeUNetExecutor,
|
|
46
|
+
resolveNativeUNetPrecision,
|
|
47
|
+
selectSubgroupMatrixConfig
|
|
48
|
+
} from './nativeUNet';
|
|
49
|
+
export type {
|
|
50
|
+
NativeSubgroupMatrixConfig,
|
|
51
|
+
NativeSubgroupMatrixOptions,
|
|
52
|
+
NativeUNetGemmWorkgroup,
|
|
53
|
+
NativeUNetGemmOptions,
|
|
54
|
+
NativeUNetExecutionProfile,
|
|
55
|
+
NativeUNetKernel,
|
|
56
|
+
NativeUNetKernelSetting,
|
|
57
|
+
NativeUNetLayerTiming,
|
|
58
|
+
NativeUNetOptions,
|
|
59
|
+
NativeUNetPrecision,
|
|
60
|
+
NativeUNetPrecisionSetting
|
|
61
|
+
} from './nativeUNet';
|
|
62
|
+
|
|
63
|
+
export interface UNetOptions {
|
|
64
|
+
aux?: boolean;
|
|
65
|
+
hdr?: boolean;
|
|
66
|
+
/** HDR transfer function expected by the trained model. Defaults to PU. */
|
|
67
|
+
hdrTransfer?: HDRTransfer;
|
|
68
|
+
/** Hard upper bound for an output tile edge. Defaults to 512. */
|
|
69
|
+
maxTileSize?: number;
|
|
70
|
+
/** Adaptive GPU-time-based tile sizing. Enabled by default. */
|
|
71
|
+
dynamicTile?: DynamicTileSetting;
|
|
72
|
+
/** `auto` uses native WGSL; `webnn` opts into the WebNN backend. */
|
|
73
|
+
engine?: UNetEngineSetting;
|
|
74
|
+
/** `auto` selects FP16 when shader-f16 was enabled on the GPUDevice. */
|
|
75
|
+
precision?: NativeUNetPrecisionSetting;
|
|
76
|
+
/** `auto` uses implicit GEMM for FP16/FP32 convolutions, except the direct output layer. */
|
|
77
|
+
kernel?: NativeUNetKernelSetting;
|
|
78
|
+
/**
|
|
79
|
+
* Experimental implicit-GEMM tuning for native WGSL execution. These knobs
|
|
80
|
+
* exist for benchmarking and may change or be removed in any release.
|
|
81
|
+
* @experimental
|
|
82
|
+
*/
|
|
83
|
+
gemm?: NativeUNetGemmOptions;
|
|
84
|
+
/**
|
|
85
|
+
* Experimental `kernel: 'subgroup-matrix'` tuning. The kernel needs a
|
|
86
|
+
* GPUDevice created with `subgroups` and `chromium-experimental-subgroup-matrix`.
|
|
87
|
+
* @experimental
|
|
88
|
+
*/
|
|
89
|
+
subgroupMatrix?: NativeSubgroupMatrixOptions;
|
|
90
|
+
/** Versioned topology descriptor for future/custom OIDN TZA models. */
|
|
91
|
+
modelSpec?: UNetModelSpec;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export type { UNetEngineSetting, UNetExecutionStats } from './UNet';
|
|
95
|
+
export type { HDRTransfer } from './process';
|
|
96
|
+
export { WebNNUNetExecutor } from './webnnUNet';
|
|
97
|
+
export type { WebNNRuntimeSupport, WebNNUNetOptions } from './webnnUNet';
|
|
98
|
+
export type {
|
|
99
|
+
OIDNResourceKind,
|
|
100
|
+
OIDNResourceSnapshot,
|
|
101
|
+
OIDNResourceStats
|
|
102
|
+
} from './resourceTracker';
|
|
103
|
+
|
|
104
|
+
export async function initUNetFromBuffer(
|
|
105
|
+
tzaBuffer: ArrayBuffer,
|
|
106
|
+
backendParams?: { device: GPUDevice },
|
|
107
|
+
opts?: UNetOptions
|
|
108
|
+
) {
|
|
109
|
+
const device = backendParams?.device ?? await initWebGPUBackend();
|
|
110
|
+
const tensors = parseTZA(tzaBuffer);
|
|
111
|
+
const unet = new UNet(tensors, device, opts);
|
|
112
|
+
await unet.prepare();
|
|
113
|
+
return unet;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function initUNetFromURL(
|
|
117
|
+
modelPath: string,
|
|
118
|
+
backendParams?: { device: GPUDevice },
|
|
119
|
+
opts?: UNetOptions
|
|
120
|
+
) {
|
|
121
|
+
return fetch(modelPath)
|
|
122
|
+
.then((res) => res.arrayBuffer())
|
|
123
|
+
.then((ab) => {
|
|
124
|
+
return initUNetFromBuffer(ab, backendParams, opts);
|
|
125
|
+
});
|
|
126
|
+
}
|