@photivra/engine 2.15.0 → 2.16.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 +47 -1
- package/CITATION.cff +1 -1
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +14 -0
- package/dist/capture/native-environment-lean.js +1 -1
- package/dist/capture/native-environment-lean.js.map +1 -1
- package/dist/capture/native-raw-development.d.ts +15 -0
- package/dist/capture/native-raw-development.js +12 -6
- package/dist/capture/native-raw-development.js.map +1 -1
- package/dist/capture/raw-development-kernel.d.ts +10 -3
- package/dist/capture/raw-development-kernel.js +25 -6
- package/dist/capture/raw-development-kernel.js.map +1 -1
- package/dist/core/internal-radiance-evaluator.d.ts +5 -2
- package/dist/core/internal-radiance-evaluator.js.map +1 -1
- 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/defocus-image-space-provider.d.ts +109 -0
- package/dist/facade/defocus-image-space-provider.js +359 -0
- package/dist/facade/defocus-image-space-provider.js.map +1 -0
- package/dist/facade/diffraction-provider.d.ts +16 -0
- package/dist/facade/diffraction-provider.js +3 -3
- package/dist/facade/diffraction-provider.js.map +1 -1
- package/dist/facade/display-view.d.ts +21 -0
- package/dist/facade/display-view.js +66 -8
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +111 -2
- package/dist/facade/simulate-capture.js +220 -44
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.js +7 -3
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/display-view-quality-gpu.d.ts +2 -0
- package/dist/gpu/display-view-quality-gpu.js +57 -13
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/display-view-speed-gpu.d.ts +2 -0
- package/dist/gpu/display-view-speed-gpu.js +45 -16
- package/dist/gpu/display-view-speed-gpu.js.map +1 -1
- package/dist/gpu/image-kernel-accelerator.js +3 -0
- package/dist/gpu/image-kernel-accelerator.js.map +1 -1
- package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +4 -0
- package/dist/gpu/wgsl-display-view-quality-kernel.js +53 -2
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +15 -0
- package/dist/gpu/wgsl-display-view-speed-kernel.js +42 -1
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/optics/defocus-disc-kernel.d.ts +3 -2
- package/dist/optics/defocus-disc-kernel.js +3 -2
- package/dist/optics/defocus-disc-kernel.js.map +1 -1
- package/dist/optics/sensor-environment-query.d.ts +31 -0
- package/dist/optics/sensor-environment-query.js +45 -13
- package/dist/optics/sensor-environment-query.js.map +1 -1
- package/dist/processing/development-detail.d.ts +132 -0
- package/dist/processing/development-detail.js +325 -0
- package/dist/processing/development-detail.js.map +1 -0
- package/docs/API_REFERENCE.md +6 -1
- package/docs/QUICK_START.md +190 -12
- package/docs/USAGE.md +14 -3
- package/docs/api/capture-native-raw-development.md +16 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/exports.json +27 -2
- package/docs/api/facade-simulate-capture.md +128 -3
- package/docs/api/optics-defocus-disc-kernel.md +3 -2
- package/package.json +1 -1
|
@@ -219,6 +219,58 @@ export type SimulateCapturePixelAperture = "point" | "box" | {
|
|
|
219
219
|
};
|
|
220
220
|
/** Default midpoint samples per axis of `sensor.pixelAperture: "box"`. */
|
|
221
221
|
export declare const SIMULATE_CAPTURE_BOX_APERTURE_DEFAULT_SAMPLES = 4;
|
|
222
|
+
/**
|
|
223
|
+
* `output.sharpening` (#552): unsharp mask y = x + amount·(x − G_σ∗x) where |x − G_σ∗x| ≥ threshold (GIMP and ImageMagick
|
|
224
|
+
* unsharp mask), per channel on sRGB-encoded values in [0, 1], then clipped to [0, 1]. Every field is optional.
|
|
225
|
+
*/
|
|
226
|
+
export interface SimulateCaptureSharpening {
|
|
227
|
+
/** Gaussian standard deviation σ in output pixels, (0, 8]; the kernel reaches ⌈3σ⌉. Default 1. */
|
|
228
|
+
radiusPixels?: number;
|
|
229
|
+
/** Gain on the detail signal, (0, 5]. Default 0.5. */
|
|
230
|
+
amount?: number;
|
|
231
|
+
/** Detail smaller than this (encoded units, [0, 1]) is left unchanged. Default 0 (every difference is sharpened). */
|
|
232
|
+
threshold?: number;
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* `output.diffractionCorrection` (#552): the Wiener–Tikhonov gain G(s) = min(maximumGain, H(s)(1 + α)/(H(s)² + α)) on the circular-pupil
|
|
236
|
+
* OTF H at s = f/f_c, f_c = 1/(λ·N_w), as a (2R + 1)² FIR per channel. Every field is optional.
|
|
237
|
+
*/
|
|
238
|
+
export interface SimulateCaptureDiffractionCorrection {
|
|
239
|
+
/** Design wavelength in nm, 380–780, used for all three channels. Default 550. */
|
|
240
|
+
wavelengthNm?: number;
|
|
241
|
+
/** α, an assumed noise-to-signal power ratio, 1e-4–1. Larger is safer and restores less. Default 0.01. */
|
|
242
|
+
regularization?: number;
|
|
243
|
+
/** Upper bound on the gain at any frequency, 1–10 (it bounds the noise amplification). Default 4. */
|
|
244
|
+
maximumGain?: number;
|
|
245
|
+
/** FIR radius R in pixels, 1–16. Default: the Airy pattern's third dark ring at the design wavelength, within 1–16. */
|
|
246
|
+
kernelRadiusPixels?: number;
|
|
247
|
+
}
|
|
248
|
+
/** `diagnostics.detail` (#552): what the development detail stages did. Present only when one ran. */
|
|
249
|
+
export interface SimulateCaptureDetailDiagnostics {
|
|
250
|
+
diffractionCorrection?: {
|
|
251
|
+
/** Corrected linear samples below 0 (ringing below black; clipped later by gamut handling) and above the RAW white level 1. */
|
|
252
|
+
negativeSampleCount: number;
|
|
253
|
+
aboveWhiteSampleCount: number;
|
|
254
|
+
/** The diffraction cut-off p/(λ·N_w) in cycles per pixel (Nyquist is 0.5); nothing beyond it is recovered. */
|
|
255
|
+
cutoffCyclesPerPixel: number;
|
|
256
|
+
/** Measured largest |response| of the filter, and largest |response| beyond the cut-off (null when the Nyquist square is inside it). */
|
|
257
|
+
responsePeakGain: number;
|
|
258
|
+
responseMaximumBeyondCutoff: number | null;
|
|
259
|
+
};
|
|
260
|
+
sharpening?: {
|
|
261
|
+
/** Half-width ⌈3σ⌉ of the Gaussian in pixels. */
|
|
262
|
+
kernelRadiusPixels: number;
|
|
263
|
+
/** Samples changed by the mask, and samples left alone because their detail was under the threshold. */
|
|
264
|
+
sharpenedSampleCount: number;
|
|
265
|
+
thresholdHeldSampleCount: number;
|
|
266
|
+
/** Sharpened samples clipped at 0 or 1. */
|
|
267
|
+
clippedLowSampleCount: number;
|
|
268
|
+
clippedHighSampleCount: number;
|
|
269
|
+
/** Largest increase and decrease amount·detail before clipping, in encoded units: the halo strength. */
|
|
270
|
+
maximumIncrease: number;
|
|
271
|
+
maximumDecrease: number;
|
|
272
|
+
};
|
|
273
|
+
}
|
|
222
274
|
export interface SimulateCaptureMotion {
|
|
223
275
|
/** Constant camera angular velocity, rad/s, in the engine's pitch/yaw/roll convention. Missing axes are 0. */
|
|
224
276
|
angularVelocityRadPerSec?: {
|
|
@@ -370,6 +422,16 @@ export interface SimulateCaptureOptions {
|
|
|
370
422
|
toneCurveParameters?: SdrToneCurveParameters;
|
|
371
423
|
/** Default "clip". */
|
|
372
424
|
highlightMode?: RawHighlightMode;
|
|
425
|
+
/**
|
|
426
|
+
* Opt-in (#556) development exposure push in stops, −2 to +5 (`NATIVE_RAW_DEVELOPMENT_EXPOSURE_RANGE_STOPS`); default 0.
|
|
427
|
+
* A linear gain 2^stops on the RAW after black subtraction and white normalization, before demosaic, white balance,
|
|
428
|
+
* the highlight mode, colour, the tone curve and encoding, like a raw editor's exposure slider ("brighten in development").
|
|
429
|
+
* A site pushed to or past white clips there and counts as saturated (`highlightMode: "reconstruct"` rolls it off);
|
|
430
|
+
* raised shadows keep their real noise and ADC quantization. The RAW codes, `result.raw` and a DNG are not changed;
|
|
431
|
+
* the developed samples (`image.samples`, also with format "dng"), JPEG, PNG, the ideal reference and the display view
|
|
432
|
+
* honour it. Reported in the assumption `output.exposureCompensationStops`. 0 or omitted: byte-identical to before.
|
|
433
|
+
*/
|
|
434
|
+
exposureCompensationStops?: number;
|
|
373
435
|
/** Default "scene-illuminant". */
|
|
374
436
|
whiteBalance?: SimulateCaptureWhiteBalance;
|
|
375
437
|
/** JPEG constant quantization step 1-255 (not a manufacturer 'quality'). Default 2. */
|
|
@@ -420,6 +482,20 @@ export interface SimulateCaptureOptions {
|
|
|
420
482
|
width: number;
|
|
421
483
|
height: number;
|
|
422
484
|
};
|
|
485
|
+
/**
|
|
486
|
+
* Opt-in (#552) diffraction correction: a bounded, regularized deconvolution of the developed linear colour with the
|
|
487
|
+
* diffraction OTF of the ideal circular pupil at the capture's working f-number (see {@link SimulateCaptureDiffractionCorrection}).
|
|
488
|
+
* It runs after demosaic, colour and white balance and before the tone curve. Frequencies beyond the diffraction cut-off
|
|
489
|
+
* 1/(λ·N_w) never reached the sensor and stay lost: the correction's design gain there is 0. Needs `lens.diffraction`.
|
|
490
|
+
* Not combinable with `region`. The DNG's RAW codes are not changed. Default off.
|
|
491
|
+
*/
|
|
492
|
+
diffractionCorrection?: SimulateCaptureDiffractionCorrection;
|
|
493
|
+
/**
|
|
494
|
+
* Opt-in (#552) unsharp mask (see {@link SimulateCaptureSharpening}) on the sRGB-encoded values after the tone curve and gamut
|
|
495
|
+
* handling, before quantization. It raises edge contrast and creates halos; it adds no optical detail. sRGB output only
|
|
496
|
+
* (not the wide-gamut or perceptual PNG paths); not combinable with `region`. The DNG's RAW codes are not changed. Default off.
|
|
497
|
+
*/
|
|
498
|
+
sharpening?: SimulateCaptureSharpening;
|
|
423
499
|
};
|
|
424
500
|
/** Camera motion and shutter type. Default: still camera, global electronic shutter. */
|
|
425
501
|
motion?: SimulateCaptureMotion;
|
|
@@ -465,7 +541,7 @@ export interface SimulateCaptureOptions {
|
|
|
465
541
|
* Options reported in `diagnostics.options` (2.1), in this order, when they ran: the ones that change how much work
|
|
466
542
|
* a capture does. `motion.rotation` is a non-zero angular velocity; `sensor.temporalSampleCount` a count above 1.
|
|
467
543
|
*/
|
|
468
|
-
export declare const SIMULATE_CAPTURE_COST_OPTIONS: readonly ["lens.pupilSampling", "lens.projection", "lens.distortion", "lens.vignetting", "lens.psf", "lens.diffraction", "lens.defocus", "sensor.antiAliasing", "sensor.pixelAperture.box", "sensor.temporalSampleCount", "motion.rotation", "motion.shutter.rolling", "output.borderHandling.mirror", "output.region", "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"];
|
|
544
|
+
export declare const SIMULATE_CAPTURE_COST_OPTIONS: readonly ["lens.pupilSampling", "lens.projection", "lens.distortion", "lens.vignetting", "lens.psf", "lens.diffraction", "lens.defocus", "sensor.antiAliasing", "sensor.pixelAperture.box", "sensor.temporalSampleCount", "motion.rotation", "motion.shutter.rolling", "output.borderHandling.mirror", "output.region", "output.whiteBalance.auto", "output.demosaic.ahd", "output.highlightMode.reconstruct", "output.colorEncoding.wide-gamut", "output.gamutMapping.perceptual", "output.diffractionCorrection", "output.sharpening", "output.format.jpeg", "output.format.png", "output.format.dng"];
|
|
469
545
|
export type SimulateCaptureCostOption = typeof SIMULATE_CAPTURE_COST_OPTIONS[number];
|
|
470
546
|
/**
|
|
471
547
|
* The options that take the exact-or-fast flag (#395), in the path style of {@link SIMULATE_CAPTURE_COST_OPTIONS}: each accepts
|
|
@@ -558,7 +634,7 @@ export interface SimulateCaptureResult {
|
|
|
558
634
|
psf: Extract<CalculateEnvironmentSensorPhotoSignalInput["psf"], {
|
|
559
635
|
kind: "sampled-local";
|
|
560
636
|
}>;
|
|
561
|
-
execution: "object-space-convolution" | "environment-psf-quadrature";
|
|
637
|
+
execution: "object-space-convolution" | "environment-psf-quadrature" | "image-space-convolution";
|
|
562
638
|
};
|
|
563
639
|
};
|
|
564
640
|
exposure: {
|
|
@@ -655,6 +731,8 @@ export interface SimulateCaptureResult {
|
|
|
655
731
|
adjustedPixelCount: number;
|
|
656
732
|
lightnessClippedPixelCount: number;
|
|
657
733
|
};
|
|
734
|
+
/** #552: present with output.diffractionCorrection or output.sharpening. */
|
|
735
|
+
detail?: SimulateCaptureDetailDiagnostics;
|
|
658
736
|
/**
|
|
659
737
|
* Wall-clock milliseconds per stage. `raw`, `whiteBalance` (the second RAW pass of auto white balance), `development`
|
|
660
738
|
* (includes DNG writing), `jpeg` and `total` are whole milliseconds (`Date.now()`). Since 2.1, where a stage ran:
|
|
@@ -915,6 +993,8 @@ export interface DisplayViewResult {
|
|
|
915
993
|
};
|
|
916
994
|
/** #395: exact or fast, as requested and as run; 2.11 (#481): plus `execution`, where the render ran (see {@link DisplayViewFastReport}). */
|
|
917
995
|
fast: DisplayViewFastReport;
|
|
996
|
+
/** #533: the saturated (clipped highlight) pixels of the real capture in the rendered region, as an expected value (see {@link DisplayViewHighlightDiagnostics}). */
|
|
997
|
+
highlight: DisplayViewHighlightDiagnostics;
|
|
918
998
|
};
|
|
919
999
|
provenance: {
|
|
920
1000
|
scientificStatus: "approximation";
|
|
@@ -927,6 +1007,33 @@ export interface DisplayViewResult {
|
|
|
927
1007
|
/** The capture request's assumptions, then `view.definition`, `view.estimation`, `view.noise`, `view.demosaic` and `view.source`. */
|
|
928
1008
|
assumptions: readonly SimulateCaptureAssumption[];
|
|
929
1009
|
}
|
|
1010
|
+
/**
|
|
1011
|
+
* #533: the display view's highlight count. `simulateCapture` reports `diagnostics.highlight.saturatedPixelCount`, the
|
|
1012
|
+
* output pixels with at least one clipped contributing RAW site (a site flagged saturated or at the white level) counted on its
|
|
1013
|
+
* one noise sample (`sensor.noiseSeed`). The display view renders no RAW, so it reports that count's expected value over the
|
|
1014
|
+
* noise: Σ_j w_j·P_j over the real output pixels j of the rendered region, w_j the part of pixel j inside the region (1 inside,
|
|
1015
|
+
* a fraction at a region edge that cuts a real pixel) and P_j the probability, under the view's own site model (each site's
|
|
1016
|
+
* normalized value normal with its exact expected electrons, shot, dark, read and ADC noise, as the expected value uses), that at
|
|
1017
|
+
* least one site pixel j's demosaic reads is at or above the white level, the sites' noises independent. It is estimated as the
|
|
1018
|
+
* view's expected value is (`view.estimation`: exact over every real pixel, or the stratified samples), so it is not an integer.
|
|
1019
|
+
*
|
|
1020
|
+
* - Where every contributing site is far from white (more than 8.5 noise standard deviations either side) P_j is exactly 0 or 1,
|
|
1021
|
+
* and the expected count equals the capture's count of the same pixels exactly (they then have no noise to differ by).
|
|
1022
|
+
* - Regions are additive: the counts of regions that tile the view sum to the whole view's count (up to floating-point summation
|
|
1023
|
+
* order), each display pixel's share being the same in every region that renders it.
|
|
1024
|
+
* - With the bilinear demosaic the sites are the capture's own contributing sites. With AHD the capture counts every site within
|
|
1025
|
+
* 3 sites of the pixel (its interpolation reach, clipped to the raster); the view counts the 25–29 of those 49 that its AHD
|
|
1026
|
+
* candidates read, so its count can be lower where a clipped area ends in a corner or a speck 2–3 sites away diagonally
|
|
1027
|
+
* (an approximation; along a straight clipped edge the two agree).
|
|
1028
|
+
* - It never changes a sample of the view. On the GPU (`prepareDisplayView` with `gpu`) it is the CPU reference's value: the
|
|
1029
|
+
* kernel decides P_j only where it is certainly 0 or 1 and the host computes every other pixel's share with the reference.
|
|
1030
|
+
*/
|
|
1031
|
+
export interface DisplayViewHighlightDiagnostics {
|
|
1032
|
+
/** Expected number of saturated real output pixels in the rendered region (a non-negative real number; see above). */
|
|
1033
|
+
saturatedPixelCount: number;
|
|
1034
|
+
/** The statistic: always "expected-value" (the capture's own count is one noise sample of it). */
|
|
1035
|
+
statistic: "expected-value";
|
|
1036
|
+
}
|
|
930
1037
|
/** Internal: the resolved request state that the display view's renderer (simulate-display-view.ts) reads. */
|
|
931
1038
|
export interface DisplayViewRunContext {
|
|
932
1039
|
ahd: boolean;
|
|
@@ -976,6 +1083,8 @@ export interface DisplayViewRunContext {
|
|
|
976
1083
|
focusDistanceM: number;
|
|
977
1084
|
height: number;
|
|
978
1085
|
highlightMode: RawHighlightMode;
|
|
1086
|
+
/** #556: the development exposure gain 2^output.exposureCompensationStops (1 without a push). */
|
|
1087
|
+
exposureGain: number;
|
|
979
1088
|
illuminanceLux: number;
|
|
980
1089
|
illuminant: ResolvedCaptureIlluminant;
|
|
981
1090
|
illuminantOption: CaptureIlluminant;
|