@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
@@ -1,6 +1,6 @@
1
1
  {
2
- "packageVersion": "2.15.0",
3
- "engineApiVersion": "2.15.0",
2
+ "packageVersion": "2.17.0",
3
+ "engineApiVersion": "2.17.0",
4
4
  "exports": [
5
5
  {
6
6
  "name": "AbsoluteUncertaintyEstimate",
@@ -562,6 +562,16 @@
562
562
  "source": "src/color/illuminants.ts",
563
563
  "kind": "runtime"
564
564
  },
565
+ {
566
+ "name": "calculateDefocusBokehKernel",
567
+ "source": "src/optics/defocus-bokeh-kernel.ts",
568
+ "kind": "runtime"
569
+ },
570
+ {
571
+ "name": "CalculateDefocusBokehKernelInput",
572
+ "source": "src/optics/defocus-bokeh-kernel.ts",
573
+ "kind": "type"
574
+ },
565
575
  {
566
576
  "name": "calculateDefocusCircle",
567
577
  "source": "src/optics/depth-of-field.ts",
@@ -2432,6 +2442,16 @@
2432
2442
  "source": "src/equipment/generic-tier-presets.ts",
2433
2443
  "kind": "runtime"
2434
2444
  },
2445
+ {
2446
+ "name": "createGenericLensCorrectionProfile",
2447
+ "source": "src/output/generic-lens-corrections.ts",
2448
+ "kind": "runtime"
2449
+ },
2450
+ {
2451
+ "name": "CreateGenericLensCorrectionProfileInput",
2452
+ "source": "src/output/generic-lens-corrections.ts",
2453
+ "kind": "type"
2454
+ },
2435
2455
  {
2436
2456
  "name": "createHdrBracketMergeTask",
2437
2457
  "source": "src/processing/hdr.ts",
@@ -2702,11 +2722,46 @@
2702
2722
  "source": "src/sensor/capture-mode.ts",
2703
2723
  "kind": "type"
2704
2724
  },
2725
+ {
2726
+ "name": "DEFOCUS_BOKEH_MAXIMUM_BLADES",
2727
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2728
+ "kind": "runtime"
2729
+ },
2705
2730
  {
2706
2731
  "name": "DEFOCUS_DISC_KERNEL_MAXIMUM_RADIUS_SITES",
2707
2732
  "source": "src/optics/defocus-disc-kernel.ts",
2708
2733
  "kind": "runtime"
2709
2734
  },
2735
+ {
2736
+ "name": "DefocusBokehBlades",
2737
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2738
+ "kind": "type"
2739
+ },
2740
+ {
2741
+ "name": "DefocusBokehCatsEye",
2742
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2743
+ "kind": "type"
2744
+ },
2745
+ {
2746
+ "name": "DefocusBokehClippingAperture",
2747
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2748
+ "kind": "type"
2749
+ },
2750
+ {
2751
+ "name": "DefocusBokehKernel",
2752
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2753
+ "kind": "type"
2754
+ },
2755
+ {
2756
+ "name": "DefocusBokehOptions",
2757
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2758
+ "kind": "type"
2759
+ },
2760
+ {
2761
+ "name": "DefocusBokehSphericalAberration",
2762
+ "source": "src/optics/defocus-bokeh-kernel.ts",
2763
+ "kind": "type"
2764
+ },
2710
2765
  {
2711
2766
  "name": "DefocusCircle",
2712
2767
  "source": "src/optics/depth-of-field.ts",
@@ -2802,6 +2857,11 @@
2802
2857
  "source": "src/facade/display-view-gpu.ts",
2803
2858
  "kind": "type"
2804
2859
  },
2860
+ {
2861
+ "name": "DisplayViewHighlightDiagnostics",
2862
+ "source": "src/facade/simulate-capture.ts",
2863
+ "kind": "type"
2864
+ },
2805
2865
  {
2806
2866
  "name": "DisplayViewResult",
2807
2867
  "source": "src/facade/simulate-capture.ts",
@@ -3477,6 +3537,26 @@
3477
3537
  "source": "src/sensor/iso-signal-chain.ts",
3478
3538
  "kind": "runtime"
3479
3539
  },
3540
+ {
3541
+ "name": "GENERIC_LENS_CATALOGUE",
3542
+ "source": "src/equipment/generic-lens-catalogue.ts",
3543
+ "kind": "runtime"
3544
+ },
3545
+ {
3546
+ "name": "GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM",
3547
+ "source": "src/output/generic-lens-corrections.ts",
3548
+ "kind": "runtime"
3549
+ },
3550
+ {
3551
+ "name": "GENERIC_LENS_IDS",
3552
+ "source": "src/equipment/generic-lens-catalogue.ts",
3553
+ "kind": "runtime"
3554
+ },
3555
+ {
3556
+ "name": "GENERIC_LENS_SET_BASELINES",
3557
+ "source": "src/equipment/generic-lens-catalogue.ts",
3558
+ "kind": "runtime"
3559
+ },
3480
3560
  {
3481
3561
  "name": "GENERIC_PRIME_EQUIVALENT_FOCAL_LENGTHS_MM",
3482
3562
  "source": "src/equipment/generic-prime-lenses.ts",
@@ -3582,6 +3662,26 @@
3582
3662
  "source": "src/sensor/iso-signal-chain.ts",
3583
3663
  "kind": "type"
3584
3664
  },
3665
+ {
3666
+ "name": "GenericLensAdjustments",
3667
+ "source": "src/equipment/generic-lens-catalogue.ts",
3668
+ "kind": "type"
3669
+ },
3670
+ {
3671
+ "name": "GenericLensCatalogueEntry",
3672
+ "source": "src/equipment/generic-lens-catalogue.ts",
3673
+ "kind": "type"
3674
+ },
3675
+ {
3676
+ "name": "GenericLensCharacter",
3677
+ "source": "src/equipment/generic-lens-catalogue.ts",
3678
+ "kind": "type"
3679
+ },
3680
+ {
3681
+ "name": "GenericLensCharacterValues",
3682
+ "source": "src/equipment/generic-lens-catalogue.ts",
3683
+ "kind": "type"
3684
+ },
3585
3685
  {
3586
3686
  "name": "GenericLensCorrectionProfile",
3587
3687
  "source": "src/output/lens-corrections.ts",
@@ -3592,6 +3692,21 @@
3592
3692
  "source": "src/equipment/exposure-capabilities.ts",
3593
3693
  "kind": "type"
3594
3694
  },
3695
+ {
3696
+ "name": "GenericLensFocusError",
3697
+ "source": "src/equipment/generic-lens-catalogue.ts",
3698
+ "kind": "type"
3699
+ },
3700
+ {
3701
+ "name": "GenericLensKind",
3702
+ "source": "src/equipment/generic-lens-catalogue.ts",
3703
+ "kind": "type"
3704
+ },
3705
+ {
3706
+ "name": "GenericLensSet",
3707
+ "source": "src/equipment/generic-lens-catalogue.ts",
3708
+ "kind": "type"
3709
+ },
3595
3710
  {
3596
3711
  "name": "GenericOpticalEvidence",
3597
3712
  "source": "src/optics/profile-contract.ts",
@@ -4577,6 +4692,11 @@
4577
4692
  "source": "src/capture/native-environment-optical-stage.ts",
4578
4693
  "kind": "runtime"
4579
4694
  },
4695
+ {
4696
+ "name": "NATIVE_RAW_DEVELOPMENT_EXPOSURE_RANGE_STOPS",
4697
+ "source": "src/capture/native-raw-development.ts",
4698
+ "kind": "runtime"
4699
+ },
4580
4700
  {
4581
4701
  "name": "NATIVE_RAW_DNG_SCHEMA_VERSION",
4582
4702
  "source": "src/capture/native-raw-dng.ts",
@@ -6612,6 +6732,11 @@
6612
6732
  "source": "src/sensor/iso-signal-chain.ts",
6613
6733
  "kind": "type"
6614
6734
  },
6735
+ {
6736
+ "name": "ResolvedGenericLens",
6737
+ "source": "src/equipment/generic-lens-catalogue.ts",
6738
+ "kind": "type"
6739
+ },
6615
6740
  {
6616
6741
  "name": "ResolvedGenericTierLensProfiles",
6617
6742
  "source": "src/equipment/generic-tier-presets.ts",
@@ -6772,6 +6897,16 @@
6772
6897
  "source": "src/sensor/iso-signal-chain.ts",
6773
6898
  "kind": "runtime"
6774
6899
  },
6900
+ {
6901
+ "name": "resolveGenericLens",
6902
+ "source": "src/equipment/generic-lens-catalogue.ts",
6903
+ "kind": "runtime"
6904
+ },
6905
+ {
6906
+ "name": "ResolveGenericLensInput",
6907
+ "source": "src/equipment/generic-lens-catalogue.ts",
6908
+ "kind": "type"
6909
+ },
6775
6910
  {
6776
6911
  "name": "resolveGenericPrimeLens",
6777
6912
  "source": "src/equipment/generic-prime-lenses.ts",
@@ -8347,6 +8482,16 @@
8347
8482
  "source": "src/facade/simulate-capture.ts",
8348
8483
  "kind": "type"
8349
8484
  },
8485
+ {
8486
+ "name": "SimulateCaptureDetailDiagnostics",
8487
+ "source": "src/facade/simulate-capture.ts",
8488
+ "kind": "type"
8489
+ },
8490
+ {
8491
+ "name": "SimulateCaptureDiffractionCorrection",
8492
+ "source": "src/facade/simulate-capture.ts",
8493
+ "kind": "type"
8494
+ },
8350
8495
  {
8351
8496
  "name": "SimulateCaptureDngPart",
8352
8497
  "source": "src/facade/simulate-capture.ts",
@@ -8412,6 +8557,11 @@
8412
8557
  "source": "src/facade/simulate-capture.ts",
8413
8558
  "kind": "type"
8414
8559
  },
8560
+ {
8561
+ "name": "SimulateCaptureSharpening",
8562
+ "source": "src/facade/simulate-capture.ts",
8563
+ "kind": "type"
8564
+ },
8415
8565
  {
8416
8566
  "name": "SimulateCaptureWhiteBalance",
8417
8567
  "source": "src/facade/simulate-capture.ts",
@@ -8452,6 +8602,16 @@
8452
8602
  "source": "src/capture/environment-raw-producer.ts",
8453
8603
  "kind": "type"
8454
8604
  },
8605
+ {
8606
+ "name": "simulateGenericLensFocusError",
8607
+ "source": "src/equipment/generic-lens-catalogue.ts",
8608
+ "kind": "runtime"
8609
+ },
8610
+ {
8611
+ "name": "SimulateGenericLensFocusErrorInput",
8612
+ "source": "src/equipment/generic-lens-catalogue.ts",
8613
+ "kind": "type"
8614
+ },
8455
8615
  {
8456
8616
  "name": "simulateIdealReference",
8457
8617
  "source": "src/facade/simulate-capture.ts",
@@ -2,6 +2,37 @@
2
2
 
3
3
  The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
4
 
5
+ ## DisplayViewHighlightDiagnostics
6
+
7
+ #533: the display view's highlight count. `simulateCapture` reports `diagnostics.highlight.saturatedPixelCount`, the
8
+ output pixels with at least one clipped contributing RAW site (a site flagged saturated or at the white level) counted on its
9
+ one noise sample (`sensor.noiseSeed`). The display view renders no RAW, so it reports that count's expected value over the
10
+ 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,
11
+ 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
12
+ normalized value normal with its exact expected electrons, shot, dark, read and ADC noise, as the expected value uses), that at
13
+ least one site pixel j's demosaic reads is at or above the white level, the sites' noises independent. It is estimated as the
14
+ view's expected value is (`view.estimation`: exact over every real pixel, or the stratified samples), so it is not an integer.
15
+
16
+ - Where every contributing site is far from white (more than 8.5 noise standard deviations either side) P_j is exactly 0 or 1,
17
+ and the expected count equals the capture's count of the same pixels exactly (they then have no noise to differ by).
18
+ - Regions are additive: the counts of regions that tile the view sum to the whole view's count (up to floating-point summation
19
+ order), each display pixel's share being the same in every region that renders it.
20
+ - With the bilinear demosaic the sites are the capture's own contributing sites. With AHD the capture counts every site within
21
+ 3 sites of the pixel (its interpolation reach, clipped to the raster); the view counts the 25–29 of those 49 that its AHD
22
+ candidates read, so its count can be lower where a clipped area ends in a corner or a speck 2–3 sites away diagonally
23
+ (an approximation; along a straight clipped edge the two agree).
24
+ - It never changes a sample of the view. On the GPU (`prepareDisplayView` with `gpu`) it is the CPU reference's value: the
25
+ kernel decides P_j only where it is certainly 0 or 1 and the host computes every other pixel's share with the reference.
26
+
27
+ ```ts
28
+ export interface DisplayViewHighlightDiagnostics {
29
+ /** Expected number of saturated real output pixels in the rendered region (a non-negative real number; see above). */
30
+ saturatedPixelCount: number;
31
+ /** The statistic: always "expected-value" (the capture's own count is one noise sample of it). */
32
+ statistic: "expected-value";
33
+ }
34
+ ```
35
+
5
36
  ## DisplayViewResult
6
37
 
7
38
  Result of {@link simulateDisplayView }: the full-resolution capture resized for display, estimated; not a capture.
@@ -43,7 +74,9 @@ export interface DisplayViewResult {
43
74
  diagnostics: { sampleCount: number; siteEvaluations: number; sceneLookups: number; detailPixelCount: number; uniformPixelCount: number; ahdCorrectedPixelCount: number;
44
75
  timingsMs: { view: number; total: number };
45
76
  /** #395: exact or fast, as requested and as run; 2.11 (#481): plus `execution`, where the render ran (see {@link DisplayViewFastReport}). */
46
- fast: DisplayViewFastReport };
77
+ fast: DisplayViewFastReport;
78
+ /** #533: the saturated (clipped highlight) pixels of the real capture in the rendered region, as an expected value (see {@link DisplayViewHighlightDiagnostics}). */
79
+ highlight: DisplayViewHighlightDiagnostics };
47
80
  provenance: { scientificStatus: "approximation"; cameraPresetId: string; cameraPresetVersion: string; evidence: readonly EvidenceProvenance[];
48
81
  composedContracts: readonly string[]; limitations: readonly string[] };
49
82
  /** The capture request's assumptions, then `view.definition`, `view.estimation`, `view.noise`, `view.demosaic` and `view.source`. */
@@ -109,9 +142,9 @@ a capture does. `motion.rotation` is a non-zero angular velocity; `sensor.tempor
109
142
 
110
143
  ```ts
111
144
  SIMULATE_CAPTURE_COST_OPTIONS = Object.freeze(["lens.pupilSampling", "lens.projection", "lens.distortion", "lens.vignetting", "lens.psf",
112
- "lens.diffraction", "lens.defocus", "sensor.antiAliasing", "sensor.pixelAperture.box", "sensor.temporalSampleCount", "motion.rotation", "motion.shutter.rolling", "output.borderHandling.mirror", "output.region",
145
+ "lens.diffraction", "lens.defocus", "lens.catalogue", "sensor.antiAliasing", "sensor.pixelAperture.box", "sensor.temporalSampleCount", "motion.rotation", "motion.shutter.rolling", "output.borderHandling.mirror", "output.region",
113
146
  "output.whiteBalance.auto", "output.demosaic.ahd", "output.highlightMode.reconstruct", "output.colorEncoding.wide-gamut", "output.gamutMapping.perceptual",
114
- "output.format.jpeg", "output.format.png", "output.format.dng"] as const)
147
+ "output.diffractionCorrection", "output.sharpening", "output.format.jpeg", "output.format.png", "output.format.dng"] as const)
115
148
  ```
116
149
 
117
150
  ## SIMULATE_CAPTURE_DEFAULT_ILLUMINANCE_LUX
@@ -334,6 +367,56 @@ export type SimulateCaptureDemosaic = "bilinear" | "ahd";
334
367
  export interface SimulateCaptureDemosaicRecord { kind: SimulateCaptureDemosaic; fast?: boolean }
335
368
  ```
336
369
 
370
+ ## SimulateCaptureDetailDiagnostics
371
+
372
+ `diagnostics.detail` (#552): what the development detail stages did. Present only when one ran.
373
+
374
+ ```ts
375
+ export interface SimulateCaptureDetailDiagnostics {
376
+ diffractionCorrection?: {
377
+ /** Corrected linear samples below 0 (ringing below black; clipped later by gamut handling) and above the RAW white level 1. */
378
+ negativeSampleCount: number;
379
+ aboveWhiteSampleCount: number;
380
+ /** The diffraction cut-off p/(λ·N_w) in cycles per pixel (Nyquist is 0.5); nothing beyond it is recovered. */
381
+ cutoffCyclesPerPixel: number;
382
+ /** Measured largest |response| of the filter, and largest |response| beyond the cut-off (null when the Nyquist square is inside it). */
383
+ responsePeakGain: number;
384
+ responseMaximumBeyondCutoff: number | null;
385
+ };
386
+ sharpening?: {
387
+ /** Half-width ⌈3σ⌉ of the Gaussian in pixels. */
388
+ kernelRadiusPixels: number;
389
+ /** Samples changed by the mask, and samples left alone because their detail was under the threshold. */
390
+ sharpenedSampleCount: number;
391
+ thresholdHeldSampleCount: number;
392
+ /** Sharpened samples clipped at 0 or 1. */
393
+ clippedLowSampleCount: number;
394
+ clippedHighSampleCount: number;
395
+ /** Largest increase and decrease amount·detail before clipping, in encoded units: the halo strength. */
396
+ maximumIncrease: number;
397
+ maximumDecrease: number;
398
+ };
399
+ }
400
+ ```
401
+
402
+ ## SimulateCaptureDiffractionCorrection
403
+
404
+ `output.diffractionCorrection` (#552): the Wiener–Tikhonov gain G(s) = min(maximumGain, H(s)(1 + α)/(H(s)² + α)) on the circular-pupil
405
+ OTF H at s = f/f_c, f_c = 1/(λ·N_w), as a (2R + 1)² FIR per channel. Every field is optional.
406
+
407
+ ```ts
408
+ export interface SimulateCaptureDiffractionCorrection {
409
+ /** Design wavelength in nm, 380–780, used for all three channels. Default 550. */
410
+ wavelengthNm?: number;
411
+ /** α, an assumed noise-to-signal power ratio, 1e-4–1. Larger is safer and restores less. Default 0.01. */
412
+ regularization?: number;
413
+ /** Upper bound on the gain at any frequency, 1–10 (it bounds the noise amplification). Default 4. */
414
+ maximumGain?: number;
415
+ /** FIR radius R in pixels, 1–16. Default: the Airy pattern's third dark ring at the design wavelength, within 1–16. */
416
+ kernelRadiusPixels?: number;
417
+ }
418
+ ```
419
+
337
420
  ## SimulateCaptureDngPart
338
421
 
339
422
  One part of a whole-frame DNG (#391): a region render's packed RAW and where its native sites lie (`result.raw`, `result.region`).
@@ -431,6 +514,22 @@ export interface SimulateCaptureOptions {
431
514
  * focus distance below its minimum focus distance, is rejected. Not combinable with `focalLengthMm`.
432
515
  */
433
516
  prime?: number;
517
+ /**
518
+ * 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";
519
+ * docs/LENS_CATALOGUE.md). Like `prime`, its focal length is the full-frame equivalent ÷ the simulated frame's diagonal crop
520
+ * factor and it replaces the tier lens's focal length and f-number range (the transmission is kept). The capture renders the
521
+ * lens's character, all labelled approximations: its PSF (diffraction with the lens's Seidel aberrations, axial and lateral
522
+ * colour, at the resolved f-number and focus) through the environment PSF quadrature, its radial distortion, its vignetting
523
+ * and, for the fisheyes, the equisolid projection. Flare, focus breathing and bokeh shape are not rendered. Not combinable
524
+ * with `prime`, `focalLengthMm`, `projection`, `distortion`, `vignetting`, `psf`, `diffraction` or `pupilSampling` (the
525
+ * lens sets them); `defocus` convolves the disc into the lens's PSF. Not offered by the display view. Default off.
526
+ */
527
+ catalogue?: string;
528
+ /**
529
+ * Opt-in (#549), with `catalogue` only: a seeded autofocus error (`simulateGenericLensFocusError`). The focus distance becomes
530
+ * the sampled one for (seed, shotIndex), reported in the `lens.focusScatter` assumption; it is visible with `defocus`. Default off.
531
+ */
532
+ focusScatter?: { seed: number; shotIndex?: number };
434
533
  /** Aperture for manual and aperture-priority modes. Default 8. */
435
534
  fNumber?: number;
436
535
  /** Focus distance in metres. Default: the subject distance. */
@@ -464,8 +563,13 @@ export interface SimulateCaptureOptions {
464
563
  * kernel is convolved with the disc (a further approximation). In focus the result is that of the same request
465
564
  * without the option, plus the assumption.
466
565
  * Not combinable with `pupilSampling` or `psf`. Default off.
566
+ *
567
+ * Bokeh character (#553), each part off by default (without them the disc is unchanged, byte for byte): `blades` (a
568
+ * polygonal or rounded-blade opening), `sphericalAberration` (primary W040: soft-edged blur on one side of the focus
569
+ * plane, bright-rimmed on the other) and `catsEye` (mechanical vignetting clipping the pupil by field position; the
570
+ * capture only, not with `diffraction`). See `calculateDefocusBokehKernel` and docs/QUICK_START.md, "Bokeh character".
467
571
  */
468
- defocus?: { model: "geometric-disc" };
572
+ defocus?: { model: "geometric-disc" } & DefocusBokehOptions;
469
573
  };
470
574
  exposure?: {
471
575
  /** Default "manual". */
@@ -544,6 +648,16 @@ export interface SimulateCaptureOptions {
544
648
  toneCurveParameters?: SdrToneCurveParameters;
545
649
  /** Default "clip". */
546
650
  highlightMode?: RawHighlightMode;
651
+ /**
652
+ * Opt-in (#556) development exposure push in stops, −2 to +5 (`NATIVE_RAW_DEVELOPMENT_EXPOSURE_RANGE_STOPS`); default 0.
653
+ * A linear gain 2^stops on the RAW after black subtraction and white normalization, before demosaic, white balance,
654
+ * the highlight mode, colour, the tone curve and encoding, like a raw editor's exposure slider ("brighten in development").
655
+ * A site pushed to or past white clips there and counts as saturated (`highlightMode: "reconstruct"` rolls it off);
656
+ * raised shadows keep their real noise and ADC quantization. The RAW codes, `result.raw` and a DNG are not changed;
657
+ * the developed samples (`image.samples`, also with format "dng"), JPEG, PNG, the ideal reference and the display view
658
+ * honour it. Reported in the assumption `output.exposureCompensationStops`. 0 or omitted: byte-identical to before.
659
+ */
660
+ exposureCompensationStops?: number;
547
661
  /** Default "scene-illuminant". */
548
662
  whiteBalance?: SimulateCaptureWhiteBalance;
549
663
  /** JPEG constant quantization step 1-255 (not a manufacturer 'quality'). Default 2. */
@@ -587,6 +701,20 @@ export interface SimulateCaptureOptions {
587
701
  * as a capture of the region's size.
588
702
  */
589
703
  region?: { x: number; y: number; width: number; height: number };
704
+ /**
705
+ * Opt-in (#552) diffraction correction: a bounded, regularized deconvolution of the developed linear colour with the
706
+ * diffraction OTF of the ideal circular pupil at the capture's working f-number (see {@link SimulateCaptureDiffractionCorrection}).
707
+ * It runs after demosaic, colour and white balance and before the tone curve. Frequencies beyond the diffraction cut-off
708
+ * 1/(λ·N_w) never reached the sensor and stay lost: the correction's design gain there is 0. Needs `lens.diffraction`.
709
+ * Not combinable with `region`. The DNG's RAW codes are not changed. Default off.
710
+ */
711
+ diffractionCorrection?: SimulateCaptureDiffractionCorrection;
712
+ /**
713
+ * Opt-in (#552) unsharp mask (see {@link SimulateCaptureSharpening}) on the sRGB-encoded values after the tone curve and gamut
714
+ * handling, before quantization. It raises edge contrast and creates halos; it adds no optical detail. sRGB output only
715
+ * (not the wide-gamut or perceptual PNG paths); not combinable with `region`. The DNG's RAW codes are not changed. Default off.
716
+ */
717
+ sharpening?: SimulateCaptureSharpening;
590
718
  };
591
719
  /** Camera motion and shutter type. Default: still camera, global electronic shutter. */
592
720
  motion?: SimulateCaptureMotion;
@@ -708,7 +836,7 @@ export interface SimulateCaptureResult {
708
836
  * scene provider gives the same expected signal (to float rounding) through the per-tap environment quadrature.
709
837
  */
710
838
  diffraction?: { profile: DiffractionLimitedPsfProfile; psf: Extract<CalculateEnvironmentSensorPhotoSignalInput["psf"], { kind: "sampled-local" }>;
711
- execution: "object-space-convolution" | "environment-psf-quadrature" };
839
+ execution: "object-space-convolution" | "environment-psf-quadrature" | "image-space-convolution" };
712
840
  };
713
841
  exposure: {
714
842
  mode: SimulateCaptureExposureMode;
@@ -781,6 +909,8 @@ export interface SimulateCaptureResult {
781
909
  rendering: SdrRenderingResult["diagnostics"];
782
910
  /** Present with output.gamutMapping "perceptual": pixels whose chroma or lightness the mapping changed. */
783
911
  gamutMapping?: { method: "cielab-hue-preserving-chroma-compression"; kneeFraction: number; adjustedPixelCount: number; lightnessClippedPixelCount: number };
912
+ /** #552: present with output.diffractionCorrection or output.sharpening. */
913
+ detail?: SimulateCaptureDetailDiagnostics;
784
914
  /**
785
915
  * Wall-clock milliseconds per stage. `raw`, `whiteBalance` (the second RAW pass of auto white balance), `development`
786
916
  * (includes DNG writing), `jpeg` and `total` are whole milliseconds (`Date.now()`). Since 2.1, where a stage ran:
@@ -831,6 +961,22 @@ export type SimulateCaptureSensorAntiAliasing =
831
961
  | { kind: "point-splitting"; components: readonly AntiAliasingPointSplitComponent[]; fast?: boolean };
832
962
  ```
833
963
 
964
+ ## SimulateCaptureSharpening
965
+
966
+ `output.sharpening` (#552): unsharp mask y = x + amount·(x − G_σ∗x) where |x − G_σ∗x| ≥ threshold (GIMP and ImageMagick
967
+ unsharp mask), per channel on sRGB-encoded values in [0, 1], then clipped to [0, 1]. Every field is optional.
968
+
969
+ ```ts
970
+ export interface SimulateCaptureSharpening {
971
+ /** Gaussian standard deviation σ in output pixels, (0, 8]; the kernel reaches ⌈3σ⌉. Default 1. */
972
+ radiusPixels?: number;
973
+ /** Gain on the detail signal, (0, 5]. Default 0.5. */
974
+ amount?: number;
975
+ /** Detail smaller than this (encoded units, [0, 1]) is left unchanged. Default 0 (every difference is sharpened). */
976
+ threshold?: number;
977
+ }
978
+ ```
979
+
834
980
  ## SimulateCaptureWhiteBalance
835
981
 
836
982
  The exact contract is declared below; use the domain guide for assumptions and staged integration.
@@ -0,0 +1,145 @@
1
+ # optics/defocus-bokeh-kernel.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## calculateDefocusBokehKernel
6
+
7
+ The defocus kernel of `lens.defocus` with bokeh options (#553), on the site lattice, for one field point: the same kernel
8
+ `simulateCapture` and the display view apply (on the field zone's centre with a cat's eye). Pure; for tests, previews and
9
+ lens catalogues. Fails with `InvalidScientificInputError` past {@link DEFOCUS_DISC_KERNEL_MAXIMUM_RADIUS_SITES} (spherical
10
+ aberration can widen the blur beyond the disc) or when the cat's eye closes the pupil, `InvalidConfigurationError` for
11
+ unknown fields. A geometric approximation: see the module comment of `defocus-bokeh-kernel.ts` and docs/QUICK_START.md,
12
+ "Bokeh character".
13
+
14
+ ```ts
15
+ export function calculateDefocusBokehKernel(input: CalculateDefocusBokehKernelInput): CalculationResult<DefocusBokehKernel>;
16
+ ```
17
+
18
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
19
+
20
+ ## CalculateDefocusBokehKernelInput
21
+
22
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
23
+
24
+ ```ts
25
+ export interface CalculateDefocusBokehKernelInput {
26
+ /** Lens focal length, mm. */
27
+ focalLengthMm: number;
28
+ /** Working f-number. */
29
+ aperture: number;
30
+ /** Focus-plane distance, m. */
31
+ focusDistanceM: number;
32
+ /** Scene-plane distance, m. */
33
+ subjectDistanceM: number;
34
+ /** Sensor site pitch, µm: the lattice the kernel is sampled on. */
35
+ pixelPitchMicrometers: number;
36
+ /** The bokeh options, as `lens.defocus` takes them (blades, sphericalAberration, catsEye). */
37
+ bokeh: DefocusBokehOptions;
38
+ /** Field tangent tan θ·(cos φ, sin φ) in output-image axes (+X right, +Y up); only the cat's eye reads it. Default on axis. */
39
+ fieldTangent?: { x: number; y: number };
40
+ }
41
+ ```
42
+
43
+ ## DEFOCUS_BOKEH_MAXIMUM_BLADES
44
+
45
+ Largest blade count of `lens.defocus.blades` (and of {@link DefocusBokehBlades}).
46
+
47
+ ```ts
48
+ DEFOCUS_BOKEH_MAXIMUM_BLADES = 32
49
+ ```
50
+
51
+ ## DefocusBokehBlades
52
+
53
+ Aperture blades: a regular polygon, optionally with rounded blades and open at wide f-numbers.
54
+
55
+ ```ts
56
+ export interface DefocusBokehBlades {
57
+ /** Number of diaphragm blades, 3 to {@link DEFOCUS_BOKEH_MAXIMUM_BLADES}. */
58
+ count: number;
59
+ /** Rotation of the first blade edge, degrees counter-clockwise (`calculateIdealApertureGeometry`'s convention). Default 0. */
60
+ firstBladeEdgeAngleDegrees?: number;
61
+ /** Blade roundness κ in [0, 1]: each edge is an arc of radius 1/κ circumradii (0: straight blades, 1: a circle). Default 0. */
62
+ curvature?: number;
63
+ /** At or below this f-number the blades are open and the opening is circular (the lens's widest aperture). Default: never. */
64
+ circularAtOrBelowFNumber?: number;
65
+ }
66
+ ```
67
+
68
+ ## DefocusBokehCatsEye
69
+
70
+ Mechanical vignetting (cat's eye): the open pupil clipped by each aperture at the field point.
71
+
72
+ ```ts
73
+ export interface DefocusBokehCatsEye {
74
+ /** One through eight clipping apertures. */
75
+ apertures: readonly DefocusBokehClippingAperture[];
76
+ /**
77
+ * `lens.defocus` only: zones across the frame's long side (1 to {@link DEFOCUS_BOKEH_MAXIMUM_FIELD_ZONES}, default
78
+ * {@link DEFOCUS_BOKEH_DEFAULT_FIELD_ZONES}); square zones, each with the kernel of its centre's field point.
79
+ */
80
+ fieldZonesAcrossLongSide?: number;
81
+ }
82
+ ```
83
+
84
+ ## DefocusBokehClippingAperture
85
+
86
+ One clipping aperture of the cat's eye, as `MechanicalVignettingAperture` of `calculateMechanicalVignetting`.
87
+
88
+ ```ts
89
+ export interface DefocusBokehClippingAperture {
90
+ /** Aperture radius referred to the entrance-pupil plane, mm. */
91
+ radiusMm: number;
92
+ /** Shift of its projected centre per unit tan(field angle), mm, toward the field point when positive. */
93
+ shiftPerTanFieldMm: number;
94
+ }
95
+ ```
96
+
97
+ ## DefocusBokehKernel
98
+
99
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
100
+
101
+ ```ts
102
+ export interface DefocusBokehKernel {
103
+ /** "behind-focus" (subject farther than focus), "in-front-of-focus", or "in-focus" (no kernel). */
104
+ side: "behind-focus" | "in-front-of-focus" | "in-focus";
105
+ /** Thin-lens blur diameter c, µm and sites (`calculateDefocusCircle`, thin-lens model 1.1.0). */
106
+ diameterMicrometers: number;
107
+ diameterSites: number;
108
+ /** Kernel radius R and window side 2R + 1 (0 and 1 in focus). */
109
+ kernelRadiusSites: number;
110
+ width: number;
111
+ /** Row-major weights, index (j + R)·width + (i + R) for site offset (i right, j down) from the chief ray; null in focus. */
112
+ weights: Float64Array | null;
113
+ /** Open pupil area / unclipped opening area at the field point (the cat's eye's open fraction; 1 without one). */
114
+ openAreaRatio: number;
115
+ method: "exact-polygon-cell-area" | "pupil-ray-binning" | "none";
116
+ /** Transverse spherical aberration of the marginal ray at the working f-number, sites (8·N·W040(N)/pitch). */
117
+ sphericalAberrationSites: number;
118
+ raySamples: number;
119
+ }
120
+ ```
121
+
122
+ ## DefocusBokehOptions
123
+
124
+ The bokeh options of `lens.defocus` (#553), each off when omitted; a lens catalogue entry can carry them as they are.
125
+
126
+ ```ts
127
+ export interface DefocusBokehOptions {
128
+ blades?: DefocusBokehBlades;
129
+ sphericalAberration?: DefocusBokehSphericalAberration;
130
+ catsEye?: DefocusBokehCatsEye;
131
+ }
132
+ ```
133
+
134
+ ## DefocusBokehSphericalAberration
135
+
136
+ Primary (Seidel) spherical aberration W = W040·ρ⁴, given at a reference f-number.
137
+
138
+ ```ts
139
+ export interface DefocusBokehSphericalAberration {
140
+ /** W040 in µm of optical path at the pupil edge of `atFNumber`; positive = undercorrected (marginal rays focus short). */
141
+ w040Micrometers: number;
142
+ /** The f-number at which `w040Micrometers` is given; the working value is W040·(atFNumber/N)⁴. */
143
+ atFNumber: number;
144
+ }
145
+ ```