@photivra/engine 2.15.0 → 2.17.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.
Files changed (119) hide show
  1. package/CHANGELOG.md +102 -1
  2. package/CITATION.cff +1 -1
  3. package/README.md +6 -6
  4. package/SECURITY.md +1 -1
  5. package/THIRD_PARTY.md +38 -0
  6. package/dist/camera/lens-projection.d.ts +5 -0
  7. package/dist/camera/lens-projection.js +10 -0
  8. package/dist/camera/lens-projection.js.map +1 -1
  9. package/dist/capture/native-environment-lean.js +1 -1
  10. package/dist/capture/native-environment-lean.js.map +1 -1
  11. package/dist/capture/native-raw-development.d.ts +15 -0
  12. package/dist/capture/native-raw-development.js +12 -6
  13. package/dist/capture/native-raw-development.js.map +1 -1
  14. package/dist/capture/raw-development-kernel.d.ts +10 -3
  15. package/dist/capture/raw-development-kernel.js +25 -6
  16. package/dist/capture/raw-development-kernel.js.map +1 -1
  17. package/dist/core/internal-radiance-evaluator.d.ts +5 -2
  18. package/dist/core/internal-radiance-evaluator.js.map +1 -1
  19. package/dist/core/version.d.ts +1 -1
  20. package/dist/core/version.js +1 -1
  21. package/dist/core/version.js.map +1 -1
  22. package/dist/equipment/generic-lens-catalogue.d.ts +227 -0
  23. package/dist/equipment/generic-lens-catalogue.js +243 -0
  24. package/dist/equipment/generic-lens-catalogue.js.map +1 -0
  25. package/dist/facade/defocus-bokeh-zones.d.ts +33 -0
  26. package/dist/facade/defocus-bokeh-zones.js +53 -0
  27. package/dist/facade/defocus-bokeh-zones.js.map +1 -0
  28. package/dist/facade/defocus-image-space-provider.d.ts +118 -0
  29. package/dist/facade/defocus-image-space-provider.js +448 -0
  30. package/dist/facade/defocus-image-space-provider.js.map +1 -0
  31. package/dist/facade/diffraction-provider.d.ts +45 -0
  32. package/dist/facade/diffraction-provider.js +120 -14
  33. package/dist/facade/diffraction-provider.js.map +1 -1
  34. package/dist/facade/display-view-gpu-session.js +4 -0
  35. package/dist/facade/display-view-gpu-session.js.map +1 -1
  36. package/dist/facade/display-view-gpu.d.ts +10 -0
  37. package/dist/facade/display-view-gpu.js.map +1 -1
  38. package/dist/facade/display-view-lens-signal.d.ts +30 -0
  39. package/dist/facade/display-view-lens-signal.js +427 -0
  40. package/dist/facade/display-view-lens-signal.js.map +1 -0
  41. package/dist/facade/display-view.d.ts +21 -0
  42. package/dist/facade/display-view.js +66 -8
  43. package/dist/facade/display-view.js.map +1 -1
  44. package/dist/facade/expected-signal-setup.d.ts +7 -0
  45. package/dist/facade/expected-signal-setup.js +12 -2
  46. package/dist/facade/expected-signal-setup.js.map +1 -1
  47. package/dist/facade/expected-signal.d.ts +8 -0
  48. package/dist/facade/expected-signal.js +150 -0
  49. package/dist/facade/expected-signal.js.map +1 -1
  50. package/dist/facade/generic-lens-capture.d.ts +6 -0
  51. package/dist/facade/generic-lens-capture.js +8 -0
  52. package/dist/facade/generic-lens-capture.js.map +1 -0
  53. package/dist/facade/large-defocus-lattice.d.ts +39 -0
  54. package/dist/facade/large-defocus-lattice.js +64 -0
  55. package/dist/facade/large-defocus-lattice.js.map +1 -0
  56. package/dist/facade/scene-radiance.d.ts +5 -0
  57. package/dist/facade/scene-radiance.js +5 -2
  58. package/dist/facade/scene-radiance.js.map +1 -1
  59. package/dist/facade/simulate-capture.d.ts +148 -6
  60. package/dist/facade/simulate-capture.js +521 -88
  61. package/dist/facade/simulate-capture.js.map +1 -1
  62. package/dist/facade/simulate-display-view.js +29 -19
  63. package/dist/facade/simulate-display-view.js.map +1 -1
  64. package/dist/gpu/display-view-quality-gpu.d.ts +2 -9
  65. package/dist/gpu/display-view-quality-gpu.js +63 -13
  66. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  67. package/dist/gpu/display-view-speed-gpu.d.ts +2 -0
  68. package/dist/gpu/display-view-speed-gpu.js +47 -16
  69. package/dist/gpu/display-view-speed-gpu.js.map +1 -1
  70. package/dist/gpu/image-kernel-accelerator.js +3 -0
  71. package/dist/gpu/image-kernel-accelerator.js.map +1 -1
  72. package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +4 -0
  73. package/dist/gpu/wgsl-display-view-quality-kernel.js +53 -2
  74. package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
  75. package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +15 -0
  76. package/dist/gpu/wgsl-display-view-speed-kernel.js +42 -1
  77. package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
  78. package/dist/index.d.ts +5 -2
  79. package/dist/index.js +4 -1
  80. package/dist/index.js.map +1 -1
  81. package/dist/optics/defocus-bokeh-kernel.d.ts +207 -0
  82. package/dist/optics/defocus-bokeh-kernel.js +380 -0
  83. package/dist/optics/defocus-bokeh-kernel.js.map +1 -0
  84. package/dist/optics/defocus-disc-kernel.d.ts +44 -21
  85. package/dist/optics/defocus-disc-kernel.js +47 -25
  86. package/dist/optics/defocus-disc-kernel.js.map +1 -1
  87. package/dist/optics/generic-lens-psf.d.ts +68 -0
  88. package/dist/optics/generic-lens-psf.js +337 -0
  89. package/dist/optics/generic-lens-psf.js.map +1 -0
  90. package/dist/optics/illumination-vignetting.d.ts +6 -0
  91. package/dist/optics/illumination-vignetting.js +24 -7
  92. package/dist/optics/illumination-vignetting.js.map +1 -1
  93. package/dist/optics/sensor-environment-query.d.ts +37 -0
  94. package/dist/optics/sensor-environment-query.js +58 -19
  95. package/dist/optics/sensor-environment-query.js.map +1 -1
  96. package/dist/optics/site-kernel-runs.d.ts +77 -0
  97. package/dist/optics/site-kernel-runs.js +200 -0
  98. package/dist/optics/site-kernel-runs.js.map +1 -0
  99. package/dist/output/generic-lens-corrections.d.ts +24 -0
  100. package/dist/output/generic-lens-corrections.js +51 -0
  101. package/dist/output/generic-lens-corrections.js.map +1 -0
  102. package/dist/processing/development-detail.d.ts +132 -0
  103. package/dist/processing/development-detail.js +325 -0
  104. package/dist/processing/development-detail.js.map +1 -0
  105. package/docs/AGENT_GUIDE.md +1 -0
  106. package/docs/API_REFERENCE.md +54 -1
  107. package/docs/QUICK_START.md +444 -26
  108. package/docs/USAGE.md +71 -8
  109. package/docs/api/capture-native-raw-development.md +16 -0
  110. package/docs/api/core-version.md +1 -1
  111. package/docs/api/equipment-generic-lens-catalogue.md +281 -0
  112. package/docs/api/exports.json +162 -2
  113. package/docs/api/facade-simulate-capture.md +151 -5
  114. package/docs/api/optics-defocus-bokeh-kernel.md +145 -0
  115. package/docs/api/optics-defocus-disc-kernel.md +25 -17
  116. package/docs/api/optics-sensor-environment-query.md +6 -0
  117. package/docs/api/output-generic-lens-corrections.md +39 -0
  118. package/llms.txt +1 -0
  119. package/package.json +1 -1
@@ -4,6 +4,7 @@ import { type AutoIsoLimits, type ExposureResolutionConstraint } from "../exposu
4
4
  import { resolveColorTemperatureWhiteBalance, type ResolvedWhiteBalanceState } from "../color/white-balance.js";
5
5
  import { type CaptureWhiteBalanceIntent } from "../capture/simulated-capture.js";
6
6
  import { type NativeRawOutput, type NativeRawPlan } from "../capture/native-raw.js";
7
+ import { type DisplayViewExpectedSignal, type DisplayViewExpectedSignalInput } from "./expected-signal.js";
7
8
  import type { DisplayViewFastReport } from "./display-view-gpu.js";
8
9
  import { type NativeEnvironmentRawInput } from "../capture/native-environment-raw.js";
9
10
  import { type NativeRawDevelopmentInput, type RawHighlightDiagnostics } from "../capture/native-raw-development.js";
@@ -19,7 +20,8 @@ import type { IlluminationVignettingProfile } from "../optics/illumination-vigne
19
20
  import type { CalculateSensorPsfIrradianceQuadratureInput } from "../optics/sensor-psf-quadrature.js";
20
21
  import type { CalculateEnvironmentSensorPhotoSignalInput } from "../sensor/environment-photo-signal.js";
21
22
  import { type DiffractionLimitedPsfProfile } from "../optics/diffraction-psf-profile.js";
22
- import { type ResolvedDefocusDisc } from "../optics/defocus-disc-kernel.js";
23
+ import { type DefocusDiscSiteKernel, type ResolvedDefocusDisc } from "../optics/defocus-disc-kernel.js";
24
+ import { type DefocusBokehOptions } from "../optics/defocus-bokeh-kernel.js";
23
25
  import { type AntiAliasingPointSplitComponent } from "../sensor/optical-stack.js";
24
26
  import { type NativeRawDng } from "../capture/native-raw-dng.js";
25
27
  import { type ChartScene, type ResolvedChartScene } from "./chart-scenes.js";
@@ -219,6 +221,58 @@ export type SimulateCapturePixelAperture = "point" | "box" | {
219
221
  };
220
222
  /** Default midpoint samples per axis of `sensor.pixelAperture: "box"`. */
221
223
  export declare const SIMULATE_CAPTURE_BOX_APERTURE_DEFAULT_SAMPLES = 4;
224
+ /**
225
+ * `output.sharpening` (#552): unsharp mask y = x + amount·(x − G_σ∗x) where |x − G_σ∗x| ≥ threshold (GIMP and ImageMagick
226
+ * unsharp mask), per channel on sRGB-encoded values in [0, 1], then clipped to [0, 1]. Every field is optional.
227
+ */
228
+ export interface SimulateCaptureSharpening {
229
+ /** Gaussian standard deviation σ in output pixels, (0, 8]; the kernel reaches ⌈3σ⌉. Default 1. */
230
+ radiusPixels?: number;
231
+ /** Gain on the detail signal, (0, 5]. Default 0.5. */
232
+ amount?: number;
233
+ /** Detail smaller than this (encoded units, [0, 1]) is left unchanged. Default 0 (every difference is sharpened). */
234
+ threshold?: number;
235
+ }
236
+ /**
237
+ * `output.diffractionCorrection` (#552): the Wiener–Tikhonov gain G(s) = min(maximumGain, H(s)(1 + α)/(H(s)² + α)) on the circular-pupil
238
+ * OTF H at s = f/f_c, f_c = 1/(λ·N_w), as a (2R + 1)² FIR per channel. Every field is optional.
239
+ */
240
+ export interface SimulateCaptureDiffractionCorrection {
241
+ /** Design wavelength in nm, 380–780, used for all three channels. Default 550. */
242
+ wavelengthNm?: number;
243
+ /** α, an assumed noise-to-signal power ratio, 1e-4–1. Larger is safer and restores less. Default 0.01. */
244
+ regularization?: number;
245
+ /** Upper bound on the gain at any frequency, 1–10 (it bounds the noise amplification). Default 4. */
246
+ maximumGain?: number;
247
+ /** FIR radius R in pixels, 1–16. Default: the Airy pattern's third dark ring at the design wavelength, within 1–16. */
248
+ kernelRadiusPixels?: number;
249
+ }
250
+ /** `diagnostics.detail` (#552): what the development detail stages did. Present only when one ran. */
251
+ export interface SimulateCaptureDetailDiagnostics {
252
+ diffractionCorrection?: {
253
+ /** Corrected linear samples below 0 (ringing below black; clipped later by gamut handling) and above the RAW white level 1. */
254
+ negativeSampleCount: number;
255
+ aboveWhiteSampleCount: number;
256
+ /** The diffraction cut-off p/(λ·N_w) in cycles per pixel (Nyquist is 0.5); nothing beyond it is recovered. */
257
+ cutoffCyclesPerPixel: number;
258
+ /** Measured largest |response| of the filter, and largest |response| beyond the cut-off (null when the Nyquist square is inside it). */
259
+ responsePeakGain: number;
260
+ responseMaximumBeyondCutoff: number | null;
261
+ };
262
+ sharpening?: {
263
+ /** Half-width ⌈3σ⌉ of the Gaussian in pixels. */
264
+ kernelRadiusPixels: number;
265
+ /** Samples changed by the mask, and samples left alone because their detail was under the threshold. */
266
+ sharpenedSampleCount: number;
267
+ thresholdHeldSampleCount: number;
268
+ /** Sharpened samples clipped at 0 or 1. */
269
+ clippedLowSampleCount: number;
270
+ clippedHighSampleCount: number;
271
+ /** Largest increase and decrease amount·detail before clipping, in encoded units: the halo strength. */
272
+ maximumIncrease: number;
273
+ maximumDecrease: number;
274
+ };
275
+ }
222
276
  export interface SimulateCaptureMotion {
223
277
  /** Constant camera angular velocity, rad/s, in the engine's pitch/yaw/roll convention. Missing axes are 0. */
224
278
  angularVelocityRadPerSec?: {
@@ -245,6 +299,25 @@ export interface SimulateCaptureOptions {
245
299
  * focus distance below its minimum focus distance, is rejected. Not combinable with `focalLengthMm`.
246
300
  */
247
301
  prime?: number;
302
+ /**
303
+ * Opt-in (#549): a lens of the generic lens catalogue by id (`GENERIC_LENS_IDS`, for example "pv-50-1.2-pro" or "pv-50-1.8";
304
+ * docs/LENS_CATALOGUE.md). Like `prime`, its focal length is the full-frame equivalent ÷ the simulated frame's diagonal crop
305
+ * factor and it replaces the tier lens's focal length and f-number range (the transmission is kept). The capture renders the
306
+ * lens's character, all labelled approximations: its PSF (diffraction with the lens's Seidel aberrations, axial and lateral
307
+ * colour, at the resolved f-number and focus) through the environment PSF quadrature, its radial distortion, its vignetting
308
+ * and, for the fisheyes, the equisolid projection. Flare, focus breathing and bokeh shape are not rendered. Not combinable
309
+ * with `prime`, `focalLengthMm`, `projection`, `distortion`, `vignetting`, `psf`, `diffraction` or `pupilSampling` (the
310
+ * lens sets them); `defocus` convolves the disc into the lens's PSF. Not offered by the display view. Default off.
311
+ */
312
+ catalogue?: string;
313
+ /**
314
+ * Opt-in (#549), with `catalogue` only: a seeded autofocus error (`simulateGenericLensFocusError`). The focus distance becomes
315
+ * the sampled one for (seed, shotIndex), reported in the `lens.focusScatter` assumption; it is visible with `defocus`. Default off.
316
+ */
317
+ focusScatter?: {
318
+ seed: number;
319
+ shotIndex?: number;
320
+ };
248
321
  /** Aperture for manual and aperture-priority modes. Default 8. */
249
322
  fNumber?: number;
250
323
  /** Focus distance in metres. Default: the subject distance. */
@@ -283,10 +356,15 @@ export interface SimulateCaptureOptions {
283
356
  * kernel is convolved with the disc (a further approximation). In focus the result is that of the same request
284
357
  * without the option, plus the assumption.
285
358
  * Not combinable with `pupilSampling` or `psf`. Default off.
359
+ *
360
+ * Bokeh character (#553), each part off by default (without them the disc is unchanged, byte for byte): `blades` (a
361
+ * polygonal or rounded-blade opening), `sphericalAberration` (primary W040: soft-edged blur on one side of the focus
362
+ * plane, bright-rimmed on the other) and `catsEye` (mechanical vignetting clipping the pupil by field position; the
363
+ * capture only, not with `diffraction`). See `calculateDefocusBokehKernel` and docs/QUICK_START.md, "Bokeh character".
286
364
  */
287
365
  defocus?: {
288
366
  model: "geometric-disc";
289
- };
367
+ } & DefocusBokehOptions;
290
368
  };
291
369
  exposure?: {
292
370
  /** Default "manual". */
@@ -370,6 +448,16 @@ export interface SimulateCaptureOptions {
370
448
  toneCurveParameters?: SdrToneCurveParameters;
371
449
  /** Default "clip". */
372
450
  highlightMode?: RawHighlightMode;
451
+ /**
452
+ * Opt-in (#556) development exposure push in stops, −2 to +5 (`NATIVE_RAW_DEVELOPMENT_EXPOSURE_RANGE_STOPS`); default 0.
453
+ * A linear gain 2^stops on the RAW after black subtraction and white normalization, before demosaic, white balance,
454
+ * the highlight mode, colour, the tone curve and encoding, like a raw editor's exposure slider ("brighten in development").
455
+ * A site pushed to or past white clips there and counts as saturated (`highlightMode: "reconstruct"` rolls it off);
456
+ * raised shadows keep their real noise and ADC quantization. The RAW codes, `result.raw` and a DNG are not changed;
457
+ * the developed samples (`image.samples`, also with format "dng"), JPEG, PNG, the ideal reference and the display view
458
+ * honour it. Reported in the assumption `output.exposureCompensationStops`. 0 or omitted: byte-identical to before.
459
+ */
460
+ exposureCompensationStops?: number;
373
461
  /** Default "scene-illuminant". */
374
462
  whiteBalance?: SimulateCaptureWhiteBalance;
375
463
  /** JPEG constant quantization step 1-255 (not a manufacturer 'quality'). Default 2. */
@@ -420,6 +508,20 @@ export interface SimulateCaptureOptions {
420
508
  width: number;
421
509
  height: number;
422
510
  };
511
+ /**
512
+ * Opt-in (#552) diffraction correction: a bounded, regularized deconvolution of the developed linear colour with the
513
+ * diffraction OTF of the ideal circular pupil at the capture's working f-number (see {@link SimulateCaptureDiffractionCorrection}).
514
+ * It runs after demosaic, colour and white balance and before the tone curve. Frequencies beyond the diffraction cut-off
515
+ * 1/(λ·N_w) never reached the sensor and stay lost: the correction's design gain there is 0. Needs `lens.diffraction`.
516
+ * Not combinable with `region`. The DNG's RAW codes are not changed. Default off.
517
+ */
518
+ diffractionCorrection?: SimulateCaptureDiffractionCorrection;
519
+ /**
520
+ * Opt-in (#552) unsharp mask (see {@link SimulateCaptureSharpening}) on the sRGB-encoded values after the tone curve and gamut
521
+ * handling, before quantization. It raises edge contrast and creates halos; it adds no optical detail. sRGB output only
522
+ * (not the wide-gamut or perceptual PNG paths); not combinable with `region`. The DNG's RAW codes are not changed. Default off.
523
+ */
524
+ sharpening?: SimulateCaptureSharpening;
423
525
  };
424
526
  /** Camera motion and shutter type. Default: still camera, global electronic shutter. */
425
527
  motion?: SimulateCaptureMotion;
@@ -465,7 +567,7 @@ export interface SimulateCaptureOptions {
465
567
  * Options reported in `diagnostics.options` (2.1), in this order, when they ran: the ones that change how much work
466
568
  * a capture does. `motion.rotation` is a non-zero angular velocity; `sensor.temporalSampleCount` a count above 1.
467
569
  */
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"];
570
+ export declare const SIMULATE_CAPTURE_COST_OPTIONS: readonly ["lens.pupilSampling", "lens.projection", "lens.distortion", "lens.vignetting", "lens.psf", "lens.diffraction", "lens.defocus", "lens.catalogue", "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
571
  export type SimulateCaptureCostOption = typeof SIMULATE_CAPTURE_COST_OPTIONS[number];
470
572
  /**
471
573
  * The options that take the exact-or-fast flag (#395), in the path style of {@link SIMULATE_CAPTURE_COST_OPTIONS}: each accepts
@@ -558,7 +660,7 @@ export interface SimulateCaptureResult {
558
660
  psf: Extract<CalculateEnvironmentSensorPhotoSignalInput["psf"], {
559
661
  kind: "sampled-local";
560
662
  }>;
561
- execution: "object-space-convolution" | "environment-psf-quadrature";
663
+ execution: "object-space-convolution" | "environment-psf-quadrature" | "image-space-convolution";
562
664
  };
563
665
  };
564
666
  exposure: {
@@ -655,6 +757,8 @@ export interface SimulateCaptureResult {
655
757
  adjustedPixelCount: number;
656
758
  lightnessClippedPixelCount: number;
657
759
  };
760
+ /** #552: present with output.diffractionCorrection or output.sharpening. */
761
+ detail?: SimulateCaptureDetailDiagnostics;
658
762
  /**
659
763
  * Wall-clock milliseconds per stage. `raw`, `whiteBalance` (the second RAW pass of auto white balance), `development`
660
764
  * (includes DNG writing), `jpeg` and `total` are whole milliseconds (`Date.now()`). Since 2.1, where a stage ran:
@@ -915,6 +1019,8 @@ export interface DisplayViewResult {
915
1019
  };
916
1020
  /** #395: exact or fast, as requested and as run; 2.11 (#481): plus `execution`, where the render ran (see {@link DisplayViewFastReport}). */
917
1021
  fast: DisplayViewFastReport;
1022
+ /** #533: the saturated (clipped highlight) pixels of the real capture in the rendered region, as an expected value (see {@link DisplayViewHighlightDiagnostics}). */
1023
+ highlight: DisplayViewHighlightDiagnostics;
918
1024
  };
919
1025
  provenance: {
920
1026
  scientificStatus: "approximation";
@@ -927,6 +1033,33 @@ export interface DisplayViewResult {
927
1033
  /** The capture request's assumptions, then `view.definition`, `view.estimation`, `view.noise`, `view.demosaic` and `view.source`. */
928
1034
  assumptions: readonly SimulateCaptureAssumption[];
929
1035
  }
1036
+ /**
1037
+ * #533: the display view's highlight count. `simulateCapture` reports `diagnostics.highlight.saturatedPixelCount`, the
1038
+ * output pixels with at least one clipped contributing RAW site (a site flagged saturated or at the white level) counted on its
1039
+ * one noise sample (`sensor.noiseSeed`). The display view renders no RAW, so it reports that count's expected value over the
1040
+ * 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,
1041
+ * 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
1042
+ * normalized value normal with its exact expected electrons, shot, dark, read and ADC noise, as the expected value uses), that at
1043
+ * least one site pixel j's demosaic reads is at or above the white level, the sites' noises independent. It is estimated as the
1044
+ * view's expected value is (`view.estimation`: exact over every real pixel, or the stratified samples), so it is not an integer.
1045
+ *
1046
+ * - Where every contributing site is far from white (more than 8.5 noise standard deviations either side) P_j is exactly 0 or 1,
1047
+ * and the expected count equals the capture's count of the same pixels exactly (they then have no noise to differ by).
1048
+ * - Regions are additive: the counts of regions that tile the view sum to the whole view's count (up to floating-point summation
1049
+ * order), each display pixel's share being the same in every region that renders it.
1050
+ * - With the bilinear demosaic the sites are the capture's own contributing sites. With AHD the capture counts every site within
1051
+ * 3 sites of the pixel (its interpolation reach, clipped to the raster); the view counts the 25–29 of those 49 that its AHD
1052
+ * candidates read, so its count can be lower where a clipped area ends in a corner or a speck 2–3 sites away diagonally
1053
+ * (an approximation; along a straight clipped edge the two agree).
1054
+ * - It never changes a sample of the view. On the GPU (`prepareDisplayView` with `gpu`) it is the CPU reference's value: the
1055
+ * kernel decides P_j only where it is certainly 0 or 1 and the host computes every other pixel's share with the reference.
1056
+ */
1057
+ export interface DisplayViewHighlightDiagnostics {
1058
+ /** Expected number of saturated real output pixels in the rendered region (a non-negative real number; see above). */
1059
+ saturatedPixelCount: number;
1060
+ /** The statistic: always "expected-value" (the capture's own count is one noise sample of it). */
1061
+ statistic: "expected-value";
1062
+ }
930
1063
  /** Internal: the resolved request state that the display view's renderer (simulate-display-view.ts) reads. */
931
1064
  export interface DisplayViewRunContext {
932
1065
  ahd: boolean;
@@ -965,8 +1098,15 @@ export interface DisplayViewRunContext {
965
1098
  defaulted: ("heightM" | "surroundReflectance")[];
966
1099
  } | undefined;
967
1100
  diffraction: DiffractionLimitedPsfProfile | undefined;
968
- /** `lens.defocus` (#492): the resolved geometric disc out of focus; undefined in focus or without the option. */
969
- defocus: ResolvedDefocusDisc | undefined;
1101
+ /** `lens.defocus` (#492): the resolved geometric disc out of focus; undefined in focus or without the option. #553: with bokeh options, the shaped kernel. */
1102
+ defocus: (ResolvedDefocusDisc & {
1103
+ siteKernel?: DefocusDiscSiteKernel;
1104
+ }) | undefined;
1105
+ /**
1106
+ * #563: with `lens.projection`, `lens.distortion`, `lens.vignetting` or camera rotation, the factory of the view's expected signal
1107
+ * through the lens (display-view-lens-signal.ts, loaded on demand); undefined without them (the rectilinear expected signal).
1108
+ */
1109
+ lens?: ((input: DisplayViewExpectedSignalInput, spanColumns?: number) => DisplayViewExpectedSignal) | undefined;
970
1110
  diffractionPsf: {
971
1111
  kind: "sampled-local";
972
1112
  configuration: Pick<CalculateSensorPsfIrradianceQuadratureInput, "psf" | "psfWavelengthBasis" | "spatialModel">;
@@ -976,6 +1116,8 @@ export interface DisplayViewRunContext {
976
1116
  focusDistanceM: number;
977
1117
  height: number;
978
1118
  highlightMode: RawHighlightMode;
1119
+ /** #556: the development exposure gain 2^output.exposureCompensationStops (1 without a push). */
1120
+ exposureGain: number;
979
1121
  illuminanceLux: number;
980
1122
  illuminant: ResolvedCaptureIlluminant;
981
1123
  illuminantOption: CaptureIlluminant;