@photivra/engine 0.2.0 → 0.5.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 (89) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/CITATION.cff +1 -1
  3. package/README.md +59 -10
  4. package/dist/camera/equivalent-focal-length.d.ts +34 -0
  5. package/dist/camera/equivalent-focal-length.d.ts.map +1 -0
  6. package/dist/camera/equivalent-focal-length.js +33 -0
  7. package/dist/camera/equivalent-focal-length.js.map +1 -0
  8. package/dist/camera/field-of-view.d.ts +40 -3
  9. package/dist/camera/field-of-view.d.ts.map +1 -1
  10. package/dist/camera/field-of-view.js +66 -24
  11. package/dist/camera/field-of-view.js.map +1 -1
  12. package/dist/core/calculation-result.d.ts.map +1 -1
  13. package/dist/core/calculation-result.js +14 -2
  14. package/dist/core/calculation-result.js.map +1 -1
  15. package/dist/core/configuration-error.d.ts +9 -0
  16. package/dist/core/configuration-error.d.ts.map +1 -0
  17. package/dist/core/configuration-error.js +13 -0
  18. package/dist/core/configuration-error.js.map +1 -0
  19. package/dist/core/evidence-provenance.d.ts +40 -0
  20. package/dist/core/evidence-provenance.d.ts.map +1 -0
  21. package/dist/core/evidence-provenance.js +79 -0
  22. package/dist/core/evidence-provenance.js.map +1 -0
  23. package/dist/core/image-formation.d.ts +70 -0
  24. package/dist/core/image-formation.d.ts.map +1 -0
  25. package/dist/core/image-formation.js +335 -0
  26. package/dist/core/image-formation.js.map +1 -0
  27. package/dist/index.d.ts +15 -3
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +15 -3
  30. package/dist/index.js.map +1 -1
  31. package/dist/motion/camera-rotation.d.ts +91 -0
  32. package/dist/motion/camera-rotation.d.ts.map +1 -0
  33. package/dist/motion/camera-rotation.js +157 -0
  34. package/dist/motion/camera-rotation.js.map +1 -0
  35. package/dist/optics/focus-breathing.d.ts +73 -0
  36. package/dist/optics/focus-breathing.d.ts.map +1 -0
  37. package/dist/optics/focus-breathing.js +77 -0
  38. package/dist/optics/focus-breathing.js.map +1 -0
  39. package/dist/optics/illumination-vignetting.d.ts +59 -0
  40. package/dist/optics/illumination-vignetting.d.ts.map +1 -0
  41. package/dist/optics/illumination-vignetting.js +107 -0
  42. package/dist/optics/illumination-vignetting.js.map +1 -0
  43. package/dist/optics/lateral-chromatic-aberration.d.ts +117 -0
  44. package/dist/optics/lateral-chromatic-aberration.d.ts.map +1 -0
  45. package/dist/optics/lateral-chromatic-aberration.js +228 -0
  46. package/dist/optics/lateral-chromatic-aberration.js.map +1 -0
  47. package/dist/optics/psf-foundation.d.ts +102 -0
  48. package/dist/optics/psf-foundation.d.ts.map +1 -0
  49. package/dist/optics/psf-foundation.js +164 -0
  50. package/dist/optics/psf-foundation.js.map +1 -0
  51. package/dist/optics/radial-distortion.d.ts +131 -0
  52. package/dist/optics/radial-distortion.d.ts.map +1 -0
  53. package/dist/optics/radial-distortion.js +227 -0
  54. package/dist/optics/radial-distortion.js.map +1 -0
  55. package/dist/output/capture-geometry.d.ts +278 -0
  56. package/dist/output/capture-geometry.d.ts.map +1 -0
  57. package/dist/output/capture-geometry.js +562 -0
  58. package/dist/output/capture-geometry.js.map +1 -0
  59. package/dist/schema/validation.d.ts +1 -8
  60. package/dist/schema/validation.d.ts.map +1 -1
  61. package/dist/schema/validation.js +2 -11
  62. package/dist/schema/validation.js.map +1 -1
  63. package/dist/sensor/architecture.d.ts +40 -0
  64. package/dist/sensor/architecture.d.ts.map +1 -0
  65. package/dist/sensor/architecture.js +98 -0
  66. package/dist/sensor/architecture.js.map +1 -0
  67. package/dist/sensor/radiometry-readiness.d.ts +111 -0
  68. package/dist/sensor/radiometry-readiness.d.ts.map +1 -0
  69. package/dist/sensor/radiometry-readiness.js +294 -0
  70. package/dist/sensor/radiometry-readiness.js.map +1 -0
  71. package/dist/sensor/sensor-geometry.d.ts +99 -0
  72. package/dist/sensor/sensor-geometry.d.ts.map +1 -0
  73. package/dist/sensor/sensor-geometry.js +71 -0
  74. package/dist/sensor/sensor-geometry.js.map +1 -0
  75. package/dist/simulation/poc-simulation.d.ts +93 -1
  76. package/dist/simulation/poc-simulation.d.ts.map +1 -1
  77. package/dist/simulation/poc-simulation.js +308 -24
  78. package/dist/simulation/poc-simulation.js.map +1 -1
  79. package/docs/API_STYLE.md +36 -2
  80. package/docs/ARCHITECTURE.md +179 -1
  81. package/docs/IMAGE_FORMATION.md +151 -0
  82. package/docs/MOTION_AND_SIGNAL.md +52 -1
  83. package/docs/PHYSICS_FOUNDATION.md +190 -3
  84. package/docs/POC_API.md +73 -7
  85. package/docs/PROVENANCE.md +24 -0
  86. package/docs/PSF_FOUNDATION.md +133 -0
  87. package/docs/STABILIZATION.md +17 -4
  88. package/docs/USAGE.md +714 -3
  89. package/package.json +3 -2
package/docs/POC_API.md CHANGED
@@ -50,7 +50,7 @@ Example:
50
50
  {
51
51
  "ok": true,
52
52
  "service": "photivra-engine",
53
- "apiVersion": "0.17.0"
53
+ "apiVersion": "0.20.0"
54
54
  }
55
55
  ```
56
56
 
@@ -62,13 +62,48 @@ Requests must use `Content-Type: application/json`.
62
62
 
63
63
  All physical quantities use explicit units in their property names.
64
64
 
65
- API 0.17 composes projection using the selected focus plane. The response includes a `projection` block with ideal thin-lens image distance, scale relative to the infinity-focus approximation, and model provenance. Full-sensor field of view, crop field of view, object sampling, subject motion, and camera-shake projection use that same selected projection plane.
65
+ The POC transport reports the composed simulation contract version, not the root library `ENGINE_API_VERSION`. The current composed contract is exposed as `POC_SIMULATION_API_VERSION`.
66
+
67
+ POC simulation API 0.20 composes projection using the selected focus plane. The response includes a `projection` block with ideal thin-lens image distance, scale relative to the infinity-focus approximation, and model provenance. Full-sensor field of view, crop field of view, object sampling, subject motion, and camera-shake projection use that same selected projection plane.
68
+
69
+ ### Sensor/capture composition in POC API 0.20
70
+
71
+ POC simulation API 0.20 begins composing the standalone sensor/capture foundation **additively**.
72
+
73
+ The existing request remains valid. New callers may opt into staged capture geometry with a `capture` object containing:
74
+
75
+ - physical `orientation`;
76
+ - optional native `activeCaptureRect`;
77
+ - optional oriented `outputCropRect`;
78
+ - optional final `outputRaster`.
79
+
80
+ Compatibility rules are intentionally strict:
81
+
82
+ - legacy `crop.factor` remains available for existing callers;
83
+ - when `capture` is supplied, legacy `crop.factor` must be `1` so two crop models are not silently stacked;
84
+ - legacy top-level `subjectCrop` response semantics remain unchanged when staged capture is absent;
85
+ - with staged capture, subject framing is reported under `capture.subjectFraming` and is applied after declared output crop/resampling;
86
+ - equivalent-viewing CoC in staged capture is based on the final retained physical image region used for viewing, including subject framing when present; output pixel count alone does not change it.
87
+
88
+ The response keeps existing full-sensor/native-vector fields intact and adds an optional `capture` block with:
89
+
90
+ - resolved native/active/oriented/output geometry, including final retained physical bounds;
91
+ - active-capture and final-output FOV, including asymmetric bounds for off-center capture/output crop;
92
+ - physical actual focal length plus active-capture diagonal-based 35 mm equivalence;
93
+ - explicit oriented-capture→output pixel scaling;
94
+ - optional capture-specific subject framing with final retained physical area and effective FOV;
95
+ - explicit native-raster, oriented-capture, and final-output motion-vector diagnostics;
96
+ - corresponding native-raster/oriented/output camera-shake vectors when camera shake is requested.
97
+
98
+ Final digital/output crop does not redefine physical focal length or active-capture 35 mm equivalence. Physical `lens.focalLengthMm` remains the optical input.
99
+
100
+ Sensor architecture metadata and radiometry-readiness profiles remain standalone foundations and are not yet composed into the POC.
66
101
 
67
102
  ### Required request groups
68
103
 
69
104
  The request includes:
70
105
 
71
- - sensor dimensions and resolution;
106
+ - sensor physical imaging dimensions and native effective raster;
72
107
  - focal length and aperture;
73
108
  - shutter duration and ISO;
74
109
  - focus distance;
@@ -109,9 +144,33 @@ The top-level `fieldOfView` always describes the full sensor before digital crop
109
144
 
110
145
  This remains ideal paraxial geometry, not a real-lens focus-breathing calibration.
111
146
 
112
- ### Crop
147
+ ### Sensor and staged capture geometry
113
148
 
114
- `crop` reports the requested fixed crop factor, retained pixel dimensions/area, megapixels, and its effective field of view.
149
+ The `sensor` response now includes the shared `SensorGeometryMetrics` result in addition to the backwards-compatible representative pitch fields. This exposes physical imaging-area crop factor, exact native-raster megapixels, and independent X/Y geometric sample pitch without treating sample pitch as photosite collection area.
150
+
151
+ When the opt-in `capture` request is present, the response additionally exposes the staged geometry pipeline:
152
+
153
+ ```text
154
+ native physical imaging area + native raster
155
+ ↓
156
+ native active-capture rectangle
157
+ ↓
158
+ physical camera orientation
159
+ ↓
160
+ oriented active capture
161
+ ↓
162
+ digital/output crop
163
+ ↓
164
+ final output raster
165
+ ```
166
+
167
+ Native sensor coordinates remain invariant under orientation. Active-capture FOV is calculated from the physical retained area, including off-center asymmetric angular bounds.
168
+
169
+ Legacy projected-motion/camera-shake X/Y values use the existing image-plane basis (+X right, +Y up). Capture raster coordinates use +X right, +Y down. The capture diagnostics therefore expose the explicit conversion to `nativeRasterDeltaPixels` before rotating to `orientedCaptureDeltaPixels` and scaling to `outputDeltaPixels`; legacy fields remain unchanged.
170
+
171
+ ### Legacy crop
172
+
173
+ `crop` reports the requested legacy centered crop factor, retained pixel dimensions/area, megapixels, and its effective field of view.
115
174
 
116
175
  If `subjectCrop` is requested, subject framing is composed **after** the fixed crop. The response distinguishes:
117
176
 
@@ -122,8 +181,13 @@ If `subjectCrop` is requested, subject framing is composed **after** the fixed c
122
181
 
123
182
  The framing calculation assumes the crop can be positioned around the subject; it does not validate subject position against source-image edges.
124
183
 
184
+ ### Sensor sampling
185
+
186
+ The response exposes horizontal and vertical geometric sampling pitch plus their relative difference. The composed POC still uses the backwards-compatible horizontal pitch as its representative scalar for blur/sampling calculations and therefore rejects sensor geometry whose X/Y pitch differs by more than 1%. This prevents silent directional error until the composed POC becomes fully axis-aware.
187
+
125
188
  ### Subject/object sampling
126
189
 
190
+
127
191
  When physical subject/object dimensions are supplied, projected dimensions are returned in sensor millimetres and pixels using the focus-aware projection plane.
128
192
 
129
193
  These values can drive pre-authored asset/detail selection without moving projection math into a renderer.
@@ -156,9 +220,11 @@ Polygon aperture geometry does not convert the circular Airy result into a polyg
156
220
 
157
221
  ### Focus criterion
158
222
 
159
- For cross-format comparisons, the equivalent-viewing helper scales a caller-supplied reference CoC by sensor-diagonal ratio.
223
+ For cross-format comparisons, the equivalent-viewing helper scales a caller-supplied reference CoC by the diagonal of the physical image region assumed to be enlarged to the final viewing size.
224
+
225
+ Legacy requests continue to use the full physical sensor. In staged capture mode the target is the final physical region retained by active capture + output crop and, when requested, centered subject framing. Changing only the encoded output raster does not change this viewing criterion.
160
226
 
161
- That is explicitly an approximation based on equivalent final viewing assumptions; it is not a physical lens/sensor blur threshold.
227
+ This remains explicitly a viewing approximation, not a physical lens/sensor blur threshold. An explicit caller-supplied `circleOfConfusionMm` is never changed by crop or output geometry.
162
228
 
163
229
  ### Primary-subject diagnostics
164
230
 
@@ -15,6 +15,20 @@ An implementation may be based on:
15
15
 
16
16
  A paper, standard, or patent publication may explain a method without granting copyright permission to copy its expression and without granting a patent license. Independent implementation addresses source-code/prose provenance; it does not by itself resolve active third-party patent claims.
17
17
 
18
+ ## Source evidence and reuse rights
19
+
20
+ Factual/source evidence keeps **origin** separate from **reuse rights**.
21
+
22
+ - `sourceOrigin` identifies who originated the referenced fact or data: manufacturer, third party, or Photivra.
23
+ - `reuseStatus` identifies how Photivra may use it: factual-reference-only, reusable-data, or Photivra-owned.
24
+ - Reusable data requires an explicit license.
25
+ - Photivra-owned material must originate from Photivra.
26
+ - Manufacturer or third-party sources may be either factual-reference-only or explicitly reusable when licensing supports that use.
27
+
28
+ One evidence record must not silently support unrelated facts. Scalar metadata may cite multiple evidence records, and multi-valued capabilities should carry evidence per value.
29
+
30
+ Evidence provenance is not a confidence score and does not establish scientific effect by itself. Descriptive hardware facts only affect calculations when a separate documented model explicitly consumes them.
31
+
18
32
  ## Independent implementation
19
33
 
20
34
  When a reference explains a method but does not license its implementation or text for reuse:
@@ -82,6 +96,16 @@ Valid-range metadata describes where a model/calibration is considered applicabl
82
96
 
83
97
  When composing models, preserve uncertainty components separately unless a documented model provides a defensible combination rule. Do not imply statistical independence, add errors in quadrature, or publish a single aggregate confidence score without evidence.
84
98
 
99
+ ## Radiometry readiness evidence
100
+
101
+ Radiometry readiness is a declaration/validation gate, not proof that a calibration is correct.
102
+
103
+ A component labeled `calibrated` must identify its model/version, supporting evidence, and quantified uncertainty. If uncertainty cannot yet be quantified, the component may still be represented, but the overall assessment cannot become `calibrated-ready`.
104
+
105
+ Data-bearing calibration artifacts are referenced by stable identifier plus SHA-256 checksum. Publicly viewable curves/tables without reuse rights must not be copied into those artifacts. Manufacturer-published factual specifications may support independently authored models, while copied calibration datasets require explicit reusable licensing or Photivra ownership.
106
+
107
+ An `approximation` remains explicitly approximate even when all prerequisite categories are present. Readiness must never be used to upgrade approximation provenance into calibration provenance.
108
+
85
109
  ## Release gate
86
110
 
87
111
  Ambiguous licensing, unresolved material patent risk, uncertain provenance, unexplained copied material, fabricated uncertainty, undocumented uncertainty combination, or unsupported scientific claims block release until resolved.
@@ -0,0 +1,133 @@
1
+ # PSF and Pupil Foundation
2
+
3
+ Photivra's PSF/pupil foundation keeps optical blur contributions **separate and explicitly contextualized** until a defensible composition model exists.
4
+
5
+ Use:
6
+
7
+ ```ts
8
+ import {
9
+ calculatePsfFoundationComponents,
10
+ getPsfFoundationContract
11
+ } from "@photivra/engine";
12
+
13
+ const contract = getPsfFoundationContract();
14
+
15
+ const components = calculatePsfFoundationComponents({
16
+ focalLengthMm: 85,
17
+ aperture: 2.8,
18
+ focusDistanceM: 10,
19
+ subjectDistanceM: 20,
20
+ fieldPointMm: { x: 12, y: 8 },
21
+ fieldNormalizationRadiusMm: 21.6,
22
+ spectralBasis: {
23
+ kind: "monochromatic",
24
+ wavelengthNm: 550
25
+ }
26
+ });
27
+ ```
28
+
29
+ ## What is implemented
30
+
31
+ The current foundation evaluates two existing diagnostics in one explicit context:
32
+
33
+ - **geometric defocus circle** — the existing thin-lens defocus-circle diameter;
34
+ - **circular diffraction first-zero diameter** — the existing ideal circular-pupil Airy diagnostic.
35
+
36
+ Each contribution retains the provenance of its underlying primitive.
37
+
38
+ The foundation records:
39
+
40
+ - physical image-plane field position;
41
+ - normalized field radius for comparison;
42
+ - focus and subject depth;
43
+ - monochromatic wavelength basis;
44
+ - ideal circular f-number-derived pupil diameter.
45
+
46
+ ## What is deliberately not calculated
47
+
48
+ The foundation does **not** currently calculate:
49
+
50
+ - a combined PSF;
51
+ - a convolution kernel;
52
+ - MTF;
53
+ - one combined blur diameter;
54
+ - a lens-sharpness score.
55
+
56
+ Defocus and diffraction are independent diagnostics. A future combined PSF must document the mathematical composition method and assumptions rather than adding their diameters or scores ad hoc.
57
+
58
+ ## Reserved contributions
59
+
60
+ The public contract reserves ownership for:
61
+
62
+ - non-circular diffraction;
63
+ - mechanical/pupil clipping;
64
+ - field curvature;
65
+ - field-dependent aberration/PSF structure;
66
+ - field-dependent bokeh.
67
+
68
+ A reserved contribution is not an implemented capability.
69
+
70
+ ## Field position
71
+
72
+ Field position uses physical image-plane coordinates:
73
+
74
+ - optical axis at the origin;
75
+ - +X right;
76
+ - +Y up;
77
+ - millimetres.
78
+
79
+ `fieldNormalizationRadiusMm` exists only to make center/mid/edge/corner comparisons reproducible. It is not a real-lens calibration or model-validity limit by itself.
80
+
81
+ The current defocus and circular Airy diagnostics are field invariant. Recording field position now gives future field-dependent contributions a stable context without changing those existing calculations.
82
+
83
+ ## Spectral basis
84
+
85
+ The current foundation accepts an explicit monochromatic wavelength because the circular Airy diagnostic is wavelength dependent.
86
+
87
+ This does not create:
88
+
89
+ - a spectral lens model;
90
+ - longitudinal chromatic aberration;
91
+ - sensor spectral response;
92
+ - CFA/color calibration.
93
+
94
+ Future wavelength-dependent PSFs must declare their basis explicitly.
95
+
96
+ ## Pupil boundary
97
+
98
+ The current context records an ideal circular, f-number-derived pupil diameter because the existing defocus/Airy diagnostics use circular-pupil assumptions.
99
+
100
+ Polygon aperture geometry is not automatically a non-circular diffraction PSF.
101
+
102
+ Mechanical/pupil vignetting is also separate from illumination vignetting:
103
+
104
+ - illumination vignetting changes throughput only;
105
+ - pupil clipping can change throughput **and** PSF/bokeh shape.
106
+
107
+ ## Preview and reference fidelity
108
+
109
+ Browser preview and higher-fidelity reference implementations may use different numerical approximations or sampling budgets.
110
+
111
+ They must preserve the same:
112
+
113
+ - contribution identities;
114
+ - field/depth/spectral context;
115
+ - pupil semantics;
116
+ - engine-owned parameters;
117
+ - scientific limitations.
118
+
119
+ A preview approximation must not redefine the science merely because it is cheaper to render.
120
+
121
+ ## Test Fixture relationship
122
+
123
+ Test Fixture v0.3 provides deterministic center/mid/edge/corner detail and point-highlight targets for integration regression.
124
+
125
+ It does not establish:
126
+
127
+ - calibrated MTF;
128
+ - a measured real-lens PSF;
129
+ - spectral lens behavior;
130
+ - calibrated mechanical vignetting;
131
+ - named-lens performance.
132
+
133
+ Engine analytical/reference tests remain the scientific source of truth.
@@ -26,11 +26,24 @@ Projection has two modes:
26
26
 
27
27
  When pixel pitch is supplied, the same displacement is also reported in sensor pixels.
28
28
 
29
+ The returned X/Y shake components are expressed in the primitive's sensor/image-plane axes. `estimateCameraShakeBlur()` does not accept `CaptureOrientation` and does not rotate its vector into portrait/oriented output coordinates. A caller combining this primitive with the capture-orientation layer must explicitly transform the vector with the orientation helpers.
30
+
29
31
  The supplied shake profile is synthetic input. The model is not an empirical statement about how much a particular photographer shakes.
30
32
 
31
- ### Spatial limitation
33
+ ### Spatial limitation and the separate rotation primitive
34
+
35
+ `estimateCameraShakeBlur()` intentionally remains one global image-plane yaw/pitch vector for backwards compatibility with the existing stabilization-equivalent teaching model.
36
+
37
+ The root engine now separately exposes `calculateCameraRotationImageMapping()`, which models field-position-dependent **pure camera rotation** for yaw/pitch/roll at arbitrary physical time from exposure start.
38
+
39
+ The two APIs do not share sign semantics:
40
+
41
+ - the legacy shake estimator preserves its historical direct image-plane displacement convention;
42
+ - the spatial rotation primitive represents physical camera rotation using right-hand pitch/yaw/roll, then maps a stationary world ray through the inverse camera rotation.
43
+
44
+ Do not substitute one result for the other without an explicit migration.
32
45
 
33
- The current result is one global image-plane yaw/pitch vector. Real camera rotation produces position-dependent optical flow away from the optical axis; that spatial variation is not yet modeled. Roll and translational camera motion are also omitted.
46
+ The spatial primitive does not apply stabilization stops. Real IBIS/OIS behavior and translation/parallax remain separate future models.
34
47
 
35
48
  ## Equivalent stabilization stops
36
49
 
@@ -60,11 +73,11 @@ Only the public standard identifier and high-level terminology are referenced he
60
73
 
61
74
  ## Not yet modeled
62
75
 
63
- The current model intentionally omits:
76
+ The legacy stabilization-equivalent model intentionally omits:
64
77
 
65
78
  - roll;
66
79
  - translational camera shake;
67
- - spatially varying rotational optical flow;
80
+ - spatially varying rotational optical flow inside this compatibility API;
68
81
  - frequency-dependent shake spectra;
69
82
  - photographer-to-photographer variation;
70
83
  - shutter-button impulse;