@photivra/engine 2.0.0 → 2.2.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 +39 -1
- package/CITATION.cff +1 -1
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/dist/capture/export-jpeg.js +7 -1
- package/dist/capture/export-jpeg.js.map +1 -1
- package/dist/capture/native-environment-lean.js +5 -2
- package/dist/capture/native-environment-lean.js.map +1 -1
- package/dist/capture/native-environment-raw.js +2 -2
- package/dist/capture/native-environment-raw.js.map +1 -1
- package/dist/capture/native-raw-development.js +1 -1
- package/dist/capture/native-raw-development.js.map +1 -1
- package/dist/capture/native-raw.d.ts +16 -0
- package/dist/capture/native-raw.js +16 -3
- package/dist/capture/native-raw.js.map +1 -1
- package/dist/capture/packed-sensor-raw.d.ts +7 -0
- package/dist/capture/packed-sensor-raw.js +6 -2
- package/dist/capture/packed-sensor-raw.js.map +1 -1
- package/dist/core/pixel-limits.d.ts +14 -0
- package/dist/core/pixel-limits.js +40 -0
- package/dist/core/pixel-limits.js.map +1 -0
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/core/version.js.map +1 -1
- package/dist/facade/chart-radiance-batch.d.ts +7 -0
- package/dist/facade/chart-radiance-batch.js +7 -3
- package/dist/facade/chart-radiance-batch.js.map +1 -1
- package/dist/facade/diffraction-provider.d.ts +30 -0
- package/dist/facade/diffraction-provider.js +127 -0
- package/dist/facade/diffraction-provider.js.map +1 -0
- package/dist/facade/host-yield.d.ts +1 -1
- package/dist/facade/host-yield.js +0 -4
- package/dist/facade/host-yield.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +88 -2
- package/dist/facade/simulate-capture.js +343 -41
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/index.d.ts +5 -2
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/motion/temporal-exposure-sum.d.ts +7 -0
- package/dist/motion/temporal-exposure-sum.js +6 -2
- package/dist/motion/temporal-exposure-sum.js.map +1 -1
- package/dist/optics/diffraction-psf-profile.d.ts +118 -0
- package/dist/optics/diffraction-psf-profile.js +252 -0
- package/dist/optics/diffraction-psf-profile.js.map +1 -0
- package/dist/optics/irradiance-convolution.d.ts +7 -0
- package/dist/optics/irradiance-convolution.js +6 -2
- package/dist/optics/irradiance-convolution.js.map +1 -1
- package/dist/output/native-capture-sdr.d.ts +7 -0
- package/dist/output/native-capture-sdr.js +6 -3
- package/dist/output/native-capture-sdr.js.map +1 -1
- package/dist/output/png-encoder.d.ts +10 -1
- package/dist/output/png-encoder.js +10 -4
- package/dist/output/png-encoder.js.map +1 -1
- package/dist/output/print-jpeg.d.ts +18 -0
- package/dist/output/print-jpeg.js +15 -4
- package/dist/output/print-jpeg.js.map +1 -1
- package/dist/sensor/sampling-aperture.d.ts +15 -0
- package/dist/sensor/sampling-aperture.js +31 -10
- package/dist/sensor/sampling-aperture.js.map +1 -1
- package/docs/API_REFERENCE.md +27 -1
- package/docs/QUICK_START.md +285 -13
- package/docs/USAGE.md +3 -1
- package/docs/api/capture-native-raw.md +11 -2
- package/docs/api/capture-packed-sensor-raw.md +7 -1
- package/docs/api/core-pixel-limits.md +19 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/exports.json +62 -2
- package/docs/api/facade-chart-radiance-batch.md +8 -2
- package/docs/api/facade-simulate-capture.md +83 -3
- package/docs/api/motion-temporal-exposure-sum.md +7 -1
- package/docs/api/optics-diffraction-psf-profile.md +96 -0
- package/docs/api/optics-irradiance-convolution.md +7 -1
- package/docs/api/output-native-capture-sdr.md +7 -2
- package/docs/api/output-png-encoder.md +8 -1
- package/docs/api/output-print-jpeg.md +17 -0
- package/package.json +3 -2
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
/** Returns a function that yields to the host event loop once per call, and the mechanism it uses. */
|
|
3
3
|
export function createHostYield(host = globalThis) {
|
|
4
4
|
const g = host;
|
|
5
|
-
const scheduler = g.scheduler;
|
|
6
|
-
if (scheduler !== undefined && typeof scheduler.yield === "function") {
|
|
7
|
-
return { mechanism: "scheduler.yield", yieldControl: async () => { await scheduler.yield.call(scheduler); } };
|
|
8
|
-
}
|
|
9
5
|
const Channel = g.MessageChannel;
|
|
10
6
|
if (typeof Channel === "function") {
|
|
11
7
|
return { mechanism: "message-channel", yieldControl: () => new Promise((resolve) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host-yield.js","sourceRoot":"","sources":["../../src/facade/host-yield.ts"],"names":[],"mappings":"AAAA,sCAAsC;
|
|
1
|
+
{"version":3,"file":"host-yield.js","sourceRoot":"","sources":["../../src/facade/host-yield.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAsCtC,sGAAsG;AACtG,MAAM,UAAU,eAAe,CAAC,OAAe,UAAU;IACvD,MAAM,CAAC,GAAG,IAAoB,CAAC;IAC/B,MAAM,OAAO,GAAG,CAAC,CAAC,cAAc,CAAC;IACjC,IAAI,OAAO,OAAO,KAAK,UAAU,EAAE,CAAC;QAClC,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;gBACvF,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;gBAC9B,OAAO,CAAC,KAAK,CAAC,SAAS,GAAG,GAAS,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC;gBAC5E,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;YACvC,CAAC,CAAC,EAAE,CAAC;IACP,CAAC;IACD,MAAM,SAAS,GAAG,CAAC,CAAC,YAAY,CAAC;IACjC,IAAI,OAAO,SAAS,KAAK,UAAU,EAAE,CAAC;QACpC,OAAO,EAAE,SAAS,EAAE,eAAe,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAChI,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,aAAa,EAAE,YAAY,EAAE,GAAG,EAAE,CAAC,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,GAAG,UAAU,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACvH,CAAC","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Host yield for the capture facade's cooperative tasks. Browsers clamp nested `setTimeout(…, 0)` to\n * at least 4 ms (WHATWG HTML timers, nesting level above 5), so a task that yields every few\n * milliseconds through timers can sit idle for a large share of its run time. The first available\n * mechanism is used:\n *\n * 1. a `MessageChannel` message, a new task without the timer clamp (one channel per yield, closed after\n * delivery, so no port keeps a Node process alive);\n * 2. `setImmediate` (Node and some other non-browser hosts);\n * 3. `setTimeout(…, 0)` as the last resort.\n *\n * Every yield must let tasks that are already queued run first, so that a caller's cancel message (for\n * example a Web Worker `message` event that aborts the capture's `AbortSignal`) is handled at the next\n * yield. A `MessageChannel` message is queued on the HTML posted message task source, the same source as\n * worker and port `message` events, and tasks from one source run in the order they were queued.\n *\n * `scheduler.yield()` is deliberately not used (engine 2.0.0 used it first). Its continuation inherits\n * the \"user-visible\" priority and gets a higher effective priority than ordinary tasks; the WICG\n * Prioritized Task Scheduling draft (§ \"Task and Continuation Priorities\", effective priority table;\n * § \"Event loop: processing model\" note) intends such continuations to run ahead of most other task\n * sources, and its explainer (\"Preventing Task Starvation by Continuations\") only bounds that by\n * duration. A capture that yields every 8 ms through `scheduler.yield()` therefore starved a worker's\n * cancel message until the capture finished (measured in Chromium).\n *\n * Only global feature detection is used; nothing is imported, so the browser package surface is unchanged.\n * The yield changes scheduling only, never results.\n */\n\ntype YieldGlobals = {\n MessageChannel?: new () => { port1: { onmessage: ((event: unknown) => void) | null; close(): void }; port2: { postMessage(message: unknown): void } };\n setImmediate?: (callback: () => void) => unknown;\n};\n\n/** Mechanism chosen by {@link createHostYield}, for diagnostics and tests. */\nexport type HostYieldMechanism = \"message-channel\" | \"set-immediate\" | \"set-timeout\";\n\n/** Returns a function that yields to the host event loop once per call, and the mechanism it uses. */\nexport function createHostYield(host: object = globalThis): { mechanism: HostYieldMechanism; yieldControl: () => Promise<void> } {\n const g = host as YieldGlobals;\n const Channel = g.MessageChannel;\n if (typeof Channel === \"function\") {\n return { mechanism: \"message-channel\", yieldControl: () => new Promise<void>((resolve) => {\n const channel = new Channel();\n channel.port1.onmessage = (): void => { channel.port1.close(); resolve(); };\n channel.port2.postMessage(undefined);\n }) };\n }\n const immediate = g.setImmediate;\n if (typeof immediate === \"function\") {\n return { mechanism: \"set-immediate\", yieldControl: () => new Promise<void>((resolve) => { immediate.call(host, resolve); }) };\n }\n return { mechanism: \"set-timeout\", yieldControl: () => new Promise<void>((resolve) => { setTimeout(resolve, 0); }) };\n}\n"]}
|
|
@@ -12,6 +12,8 @@ import type { RadialDistortionProfile } from "../optics/radial-distortion.js";
|
|
|
12
12
|
import type { BrownConradyDistortionProfile } from "../optics/brown-conrady-distortion.js";
|
|
13
13
|
import type { IlluminationVignettingProfile } from "../optics/illumination-vignetting.js";
|
|
14
14
|
import type { CalculateEnvironmentSensorPhotoSignalInput } from "../sensor/environment-photo-signal.js";
|
|
15
|
+
import { type DiffractionLimitedPsfProfile } from "../optics/diffraction-psf-profile.js";
|
|
16
|
+
import { type AntiAliasingPointSplitComponent } from "../sensor/optical-stack.js";
|
|
15
17
|
import { type ChartScene } from "./chart-scenes.js";
|
|
16
18
|
import { type CaptureIlluminant, type CallerRadianceScene, type CallerReflectanceScene } from "./scene-radiance.js";
|
|
17
19
|
/** Version of how the facade builds requests. It changes whenever the same input would build a different request. */
|
|
@@ -38,8 +40,12 @@ export type SimulateCaptureWhiteBalance = "scene-illuminant" | "auto" | "none" |
|
|
|
38
40
|
* PQ and HLG (BT.2100) are not part of 2.0; see THIRD_PARTY.md.
|
|
39
41
|
*/
|
|
40
42
|
export type SimulateCaptureColorEncoding = "srgb" | "display-p3" | "rec2020";
|
|
41
|
-
/**
|
|
42
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Demosaic. `bilinear`: the built-in Bayer kernels (`createBuiltInBayerDemosaicProfiles`). `ahd` (2.1): adaptive
|
|
45
|
+
* homogeneity-directed demosaicing (`createAhdDemosaicTask`, after Hirakawa & Parks 2005), a non-linear
|
|
46
|
+
* edge-directed method that reduces zipper and false colour on sharp edges.
|
|
47
|
+
*/
|
|
48
|
+
export type SimulateCaptureDemosaic = "bilinear" | "ahd";
|
|
43
49
|
/**
|
|
44
50
|
* `margin` renders extra native sites around the output, as far as the kernel reaches. `mirror` renders
|
|
45
51
|
* exactly the output and reads mirrored (reflect-101) copies of the missing border sites. Mirroring is
|
|
@@ -53,6 +59,32 @@ export type SimulateCaptureLensDistortion = {
|
|
|
53
59
|
kind: "brown-conrady";
|
|
54
60
|
profile: BrownConradyDistortionProfile;
|
|
55
61
|
};
|
|
62
|
+
/** Diffraction model (2.1): the shorthand or a record with an explicit kernel radius in sites. */
|
|
63
|
+
export type SimulateCaptureLensDiffraction = "circular-aperture" | {
|
|
64
|
+
aperture: "circular-aperture";
|
|
65
|
+
kernelRadiusSamples?: number;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Optical anti-aliasing (low-pass) filter (2.1), modelled as birefringent point splitting: each image point is
|
|
69
|
+
* split into equally bright copies (the engine's `normalized-point-splitting-kernel` optical-stack response).
|
|
70
|
+
* - `four-spot`: two crossed birefringent plates; copies at (±d/2, ±d/2), weight 1/4 each, MTF |cos(π·f_x·d)·cos(π·f_y·d)|.
|
|
71
|
+
* - `two-spot`: one plate splitting along `axis`; copies at ±d/2, weight 1/2 each, MTF |cos(π·f·d)|.
|
|
72
|
+
* - `point-splitting`: the engine contract itself, offsets in micrometres (+x right, +y down) and positive weights
|
|
73
|
+
* summing to 1 (at most 16 components, each within 4 sites).
|
|
74
|
+
* d = `separationSites` × pixel pitch, 0 < separationSites ≤ 4, default 1 (first MTF zero at the sensor's Nyquist
|
|
75
|
+
* frequency 1/(2·pitch)).
|
|
76
|
+
*/
|
|
77
|
+
export type SimulateCaptureSensorAntiAliasing = {
|
|
78
|
+
kind: "four-spot";
|
|
79
|
+
separationSites?: number;
|
|
80
|
+
} | {
|
|
81
|
+
kind: "two-spot";
|
|
82
|
+
axis: "horizontal" | "vertical";
|
|
83
|
+
separationSites?: number;
|
|
84
|
+
} | {
|
|
85
|
+
kind: "point-splitting";
|
|
86
|
+
components: readonly AntiAliasingPointSplitComponent[];
|
|
87
|
+
};
|
|
56
88
|
export interface SimulateCaptureMotion {
|
|
57
89
|
/** Constant camera angular velocity, rad/s, in the engine's pitch/yaw/roll convention. Missing axes are 0. */
|
|
58
90
|
angularVelocityRadPerSec?: {
|
|
@@ -93,6 +125,13 @@ export interface SimulateCaptureOptions {
|
|
|
93
125
|
psf?: Extract<CalculateEnvironmentSensorPhotoSignalInput["psf"], {
|
|
94
126
|
kind: "sampled-local";
|
|
95
127
|
}>;
|
|
128
|
+
/**
|
|
129
|
+
* Opt-in (2.1) diffraction blur of an ideal, aberration-free, in-focus lens with a circular pupil at the
|
|
130
|
+
* capture's resolved f-number: the engine builds the Airy kernels (`createDiffractionLimitedPsfProfile`)
|
|
131
|
+
* on the site pitch. `kernelRadiusSamples` (0–32) sets the kernel window; default: the third dark ring at
|
|
132
|
+
* the longest spectral node. Not combinable with `psf` or `pupilSampling`. Default off.
|
|
133
|
+
*/
|
|
134
|
+
diffraction?: SimulateCaptureLensDiffraction;
|
|
96
135
|
};
|
|
97
136
|
exposure?: {
|
|
98
137
|
/** Default "manual". */
|
|
@@ -145,6 +184,13 @@ export interface SimulateCaptureOptions {
|
|
|
145
184
|
dng?: {
|
|
146
185
|
capturedAtUtc?: string;
|
|
147
186
|
};
|
|
187
|
+
/**
|
|
188
|
+
* Opt-in large images: the most native sensor sites (the output plus the demosaic margin on each side) the
|
|
189
|
+
* capture may allocate, an integer up to `NATIVE_RAW_LIMITS.ceilingPixels` (2^27). With it, `width` and
|
|
190
|
+
* `height` may each reach 16 384 sites including the margin; without it they stay at most 4094. Time and
|
|
191
|
+
* memory grow with the pixel count (see "Large images" in the quick start); browsers may fail at large sizes.
|
|
192
|
+
*/
|
|
193
|
+
maximumPixels?: number;
|
|
148
194
|
};
|
|
149
195
|
/** Camera motion and shutter type. Default: still camera, global electronic shutter. */
|
|
150
196
|
motion?: SimulateCaptureMotion;
|
|
@@ -157,12 +203,23 @@ export interface SimulateCaptureOptions {
|
|
|
157
203
|
temporalSampleCount?: number;
|
|
158
204
|
/** Noise seed (uint32). Default 1. */
|
|
159
205
|
noiseSeed?: number;
|
|
206
|
+
/**
|
|
207
|
+
* Opt-in (2.1) optical anti-aliasing filter (birefringent point splitting, `SimulateCaptureSensorAntiAliasing`).
|
|
208
|
+
* Multiplies the scene queries by the number of split points. Default off (the tiers declare no filter).
|
|
209
|
+
*/
|
|
210
|
+
antiAliasing?: SimulateCaptureSensorAntiAliasing;
|
|
160
211
|
};
|
|
161
212
|
/** Aborting cancels the running task; the promise rejects with a domain error and returns no partial image. */
|
|
162
213
|
signal?: AbortSignal;
|
|
163
214
|
/** Called after each committed RAW, development or JPEG tile. Cannot change output. */
|
|
164
215
|
onProgress?: (progress: SimulateCaptureProgress) => void;
|
|
165
216
|
}
|
|
217
|
+
/**
|
|
218
|
+
* Options reported in `diagnostics.options` (2.1), in this order, when they ran: the ones that change how much work
|
|
219
|
+
* a capture does. `motion.rotation` is a non-zero angular velocity; `sensor.temporalSampleCount` a count above 1.
|
|
220
|
+
*/
|
|
221
|
+
export declare const SIMULATE_CAPTURE_COST_OPTIONS: readonly ["lens.pupilSampling", "lens.projection", "lens.distortion", "lens.vignetting", "lens.psf", "lens.diffraction", "sensor.antiAliasing", "sensor.temporalSampleCount", "motion.rotation", "motion.shutter.rolling", "output.borderHandling.mirror", "output.whiteBalance.auto", "output.demosaic.ahd", "output.highlightMode.reconstruct", "output.colorEncoding.wide-gamut", "output.gamutMapping.perceptual", "output.format.jpeg", "output.format.png", "output.format.dng"];
|
|
222
|
+
export type SimulateCaptureCostOption = typeof SIMULATE_CAPTURE_COST_OPTIONS[number];
|
|
166
223
|
export interface SimulateCaptureProgress {
|
|
167
224
|
stage: "raw" | "white-balance" | "development" | "jpeg";
|
|
168
225
|
completedTileCount: number;
|
|
@@ -204,6 +261,19 @@ export interface SimulateCaptureResult {
|
|
|
204
261
|
siteInput(nativeIndex: number): SimulateEnvironmentSensorRawFrameInput["sites"][number];
|
|
205
262
|
development: Omit<NativeRawDevelopmentInput, "raw">;
|
|
206
263
|
whiteBalanceState: ResolvedWhiteBalanceState | null;
|
|
264
|
+
/**
|
|
265
|
+
* Present with lens.diffraction (2.1): the engine-built Airy profile, the equivalent environment sampled PSF
|
|
266
|
+
* selection, and how it was applied. With `object-space-convolution` the site inputs declare no PSF and the
|
|
267
|
+
* provider answers with the PSF-convolved radiance; putting `psf` into each site input and using the plain
|
|
268
|
+
* scene provider gives the same expected signal (to float rounding) through the per-tap environment quadrature.
|
|
269
|
+
*/
|
|
270
|
+
diffraction?: {
|
|
271
|
+
profile: DiffractionLimitedPsfProfile;
|
|
272
|
+
psf: Extract<CalculateEnvironmentSensorPhotoSignalInput["psf"], {
|
|
273
|
+
kind: "sampled-local";
|
|
274
|
+
}>;
|
|
275
|
+
execution: "object-space-convolution" | "environment-psf-quadrature";
|
|
276
|
+
};
|
|
207
277
|
};
|
|
208
278
|
exposure: {
|
|
209
279
|
mode: SimulateCaptureExposureMode;
|
|
@@ -250,13 +320,29 @@ export interface SimulateCaptureResult {
|
|
|
250
320
|
adjustedPixelCount: number;
|
|
251
321
|
lightnessClippedPixelCount: number;
|
|
252
322
|
};
|
|
323
|
+
/**
|
|
324
|
+
* Wall-clock milliseconds per stage. `raw`, `whiteBalance` (the second RAW pass of auto white balance), `development`
|
|
325
|
+
* (includes DNG writing), `jpeg` and `total` are whole milliseconds (`Date.now()`). Since 2.1, where a stage ran:
|
|
326
|
+
* `diffraction` (the object-space convolution of `lens.diffraction`; part of `raw` and `whiteBalance`), `demosaic`
|
|
327
|
+
* (the AHD demosaic; part of `development`) and `encode` (PNG, JPEG or DNG serialization), with sub-millisecond
|
|
328
|
+
* resolution where the host has `performance.now()`. Timings vary from run to run and never enter any digest or output byte.
|
|
329
|
+
*/
|
|
253
330
|
timingsMs: {
|
|
254
331
|
raw: number;
|
|
255
332
|
whiteBalance: number;
|
|
256
333
|
development: number;
|
|
257
334
|
jpeg: number;
|
|
258
335
|
total: number;
|
|
336
|
+
diffraction?: number;
|
|
337
|
+
demosaic?: number;
|
|
338
|
+
encode?: number;
|
|
259
339
|
};
|
|
340
|
+
/**
|
|
341
|
+
* 2.1: the opt-in options that changed the work of this run, in a fixed order (see `SIMULATE_CAPTURE_COST_OPTIONS`).
|
|
342
|
+
* Empty for a capture with every default. With `timingsMs` it lets an application measure each option's cost on
|
|
343
|
+
* the user's device.
|
|
344
|
+
*/
|
|
345
|
+
options: readonly SimulateCaptureCostOption[];
|
|
260
346
|
};
|
|
261
347
|
provenance: {
|
|
262
348
|
scientificStatus: "approximation";
|