@photivra/engine 2.20.0 → 2.21.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 +178 -1
- package/CITATION.cff +1 -1
- package/README.md +7 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +7 -0
- package/dist/capture/dng-lens-correction-opcodes.d.ts +2 -2
- package/dist/capture/dng-lens-correction-opcodes.js +2 -2
- package/dist/capture/dng-lens-correction-opcodes.js.map +1 -1
- package/dist/capture/exposure-build-up-stage.d.ts +16 -0
- package/dist/capture/exposure-build-up-stage.js +4 -0
- package/dist/capture/exposure-build-up-stage.js.map +1 -0
- package/dist/capture/native-environment-lean.d.ts +2 -1
- package/dist/capture/native-environment-lean.js +41 -73
- package/dist/capture/native-environment-lean.js.map +1 -1
- package/dist/capture/native-environment-tabulated.js +4 -3
- package/dist/capture/native-environment-tabulated.js.map +1 -1
- package/dist/capture/native-raw.d.ts +3 -2
- package/dist/capture/native-raw.js +5 -4
- package/dist/capture/native-raw.js.map +1 -1
- package/dist/capture/sensor-raw-producer.d.ts +4 -1
- package/dist/capture/sensor-raw-producer.js +12 -3
- package/dist/capture/sensor-raw-producer.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/equipment/generic-lens-catalogue.d.ts +3 -2
- package/dist/equipment/generic-lens-catalogue.js +18 -5
- package/dist/equipment/generic-lens-catalogue.js.map +1 -1
- package/dist/equipment/generic-tier-assets.d.ts +32 -0
- package/dist/equipment/generic-tier-assets.js +16 -11
- package/dist/equipment/generic-tier-assets.js.map +1 -1
- package/dist/equipment/sensor-profile-body.d.ts +78 -0
- package/dist/equipment/sensor-profile-body.js +77 -0
- package/dist/equipment/sensor-profile-body.js.map +1 -0
- package/dist/equipment/sensor-profiles.d.ts +8 -0
- package/dist/equipment/sensor-profiles.js +82 -0
- package/dist/equipment/sensor-profiles.js.map +1 -0
- package/dist/facade/catalogue-lens-signal.d.ts +2 -2
- package/dist/facade/catalogue-lens-signal.js +58 -71
- package/dist/facade/catalogue-lens-signal.js.map +1 -1
- package/dist/facade/chart-radiance-batch.js +2 -1
- package/dist/facade/chart-radiance-batch.js.map +1 -1
- package/dist/facade/defocus-image-space-provider.d.ts +7 -0
- package/dist/facade/defocus-image-space-provider.js +64 -76
- package/dist/facade/defocus-image-space-provider.js.map +1 -1
- package/dist/facade/display-view-gpu-session.js +2 -0
- package/dist/facade/display-view-gpu-session.js.map +1 -1
- package/dist/facade/display-view-gpu.d.ts +6 -1
- package/dist/facade/display-view-gpu.js.map +1 -1
- package/dist/facade/display-view-lens-signal.js +25 -23
- package/dist/facade/display-view-lens-signal.js.map +1 -1
- package/dist/facade/display-view.js +5 -23
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/expected-signal-setup.d.ts +3 -1
- package/dist/facade/expected-signal-setup.js +1 -1
- package/dist/facade/expected-signal-setup.js.map +1 -1
- package/dist/facade/expected-signal.d.ts +47 -14
- package/dist/facade/expected-signal.js +117 -112
- package/dist/facade/expected-signal.js.map +1 -1
- package/dist/facade/exposure-build-up.d.ts +70 -0
- package/dist/facade/exposure-build-up.js +164 -0
- package/dist/facade/exposure-build-up.js.map +1 -0
- package/dist/facade/fast-methods.js +9 -1
- package/dist/facade/fast-methods.js.map +1 -1
- package/dist/facade/generic-lens-capture.d.ts +2 -2
- package/dist/facade/generic-lens-capture.js +2 -2
- package/dist/facade/generic-lens-capture.js.map +1 -1
- package/dist/facade/image-plane-enable.d.ts +7 -0
- package/dist/facade/image-plane-enable.js +17 -0
- package/dist/facade/image-plane-enable.js.map +1 -0
- package/dist/facade/image-plane-lattice-data.d.ts +6 -0
- package/dist/facade/image-plane-lattice-data.js +10 -0
- package/dist/facade/image-plane-lattice-data.js.map +1 -0
- package/dist/facade/image-plane-support.d.ts +33 -0
- package/dist/facade/image-plane-support.js +11 -0
- package/dist/facade/image-plane-support.js.map +1 -0
- package/dist/facade/image-plane.d.ts +36 -0
- package/dist/facade/image-plane.js +353 -0
- package/dist/facade/image-plane.js.map +1 -0
- package/dist/facade/mixed-illumination.d.ts +108 -0
- package/dist/facade/mixed-illumination.js +174 -0
- package/dist/facade/mixed-illumination.js.map +1 -0
- package/dist/facade/representative-light-sources.d.ts +72 -0
- package/dist/facade/representative-light-sources.js +124 -0
- package/dist/facade/representative-light-sources.js.map +1 -0
- package/dist/facade/scene-radiance.d.ts +129 -4
- package/dist/facade/scene-radiance.js +48 -13
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +39 -10
- package/dist/facade/simulate-capture.js +252 -92
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.js +14 -6
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/chart-composite-gpu.d.ts +13 -1
- package/dist/gpu/chart-composite-gpu.js +48 -9
- package/dist/gpu/chart-composite-gpu.js.map +1 -1
- package/dist/gpu/chart-radiance-gpu.js +4 -30
- package/dist/gpu/chart-radiance-gpu.js.map +1 -1
- package/dist/gpu/display-view-composite-material-module.d.ts +10 -4
- package/dist/gpu/display-view-composite-material-module.js +39 -13
- package/dist/gpu/display-view-composite-material-module.js.map +1 -1
- package/dist/gpu/display-view-image-material-module.d.ts +86 -0
- package/dist/gpu/display-view-image-material-module.js +339 -0
- package/dist/gpu/display-view-image-material-module.js.map +1 -0
- package/dist/gpu/display-view-image-quality.d.ts +45 -0
- package/dist/gpu/display-view-image-quality.js +380 -0
- package/dist/gpu/display-view-image-quality.js.map +1 -0
- package/dist/gpu/display-view-quality-gpu.d.ts +67 -1
- package/dist/gpu/display-view-quality-gpu.js +136 -205
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/display-view-speed-gpu.d.ts +51 -3
- package/dist/gpu/display-view-speed-gpu.js +131 -93
- package/dist/gpu/display-view-speed-gpu.js.map +1 -1
- package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +29 -15
- package/dist/gpu/wgsl-display-view-quality-kernel.js +58 -36
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +21 -6
- package/dist/gpu/wgsl-display-view-speed-kernel.js +46 -11
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
- package/dist/gpu/wgsl-exact-sum.d.ts +20 -0
- package/dist/gpu/wgsl-exact-sum.js +42 -0
- package/dist/gpu/wgsl-exact-sum.js.map +1 -0
- package/dist/index.d.ts +11 -3
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/motion/subject-motion.d.ts +66 -0
- package/dist/motion/subject-motion.js +52 -0
- package/dist/motion/subject-motion.js.map +1 -0
- package/dist/optics/generic-lens-lateral-color.d.ts +83 -0
- package/dist/optics/generic-lens-lateral-color.js +77 -0
- package/dist/optics/generic-lens-lateral-color.js.map +1 -0
- package/dist/optics/generic-lens-psf.js +54 -46
- package/dist/optics/generic-lens-psf.js.map +1 -1
- package/dist/optics/lens-psf-profile.d.ts +7 -0
- package/dist/optics/lens-psf-profile.js +2 -1
- package/dist/optics/lens-psf-profile.js.map +1 -1
- package/dist/output/developed-lens-corrections.d.ts +19 -5
- package/dist/output/developed-lens-corrections.js +28 -9
- package/dist/output/developed-lens-corrections.js.map +1 -1
- package/dist/output/generic-lens-corrections.d.ts +41 -5
- package/dist/output/generic-lens-corrections.js +52 -8
- package/dist/output/generic-lens-corrections.js.map +1 -1
- package/dist/output/print-plan.d.ts +41 -5
- package/dist/output/print-plan.js +59 -14
- package/dist/output/print-plan.js.map +1 -1
- package/dist/sensor/binomial-sample.d.ts +3 -0
- package/dist/sensor/binomial-sample.js +86 -0
- package/dist/sensor/binomial-sample.js.map +1 -0
- package/dist/sensor/sensor-characteristics.d.ts +10 -0
- package/dist/sensor/sensor-characteristics.js +20 -9
- package/dist/sensor/sensor-characteristics.js.map +1 -1
- package/docs/AGENT_GUIDE.md +1 -0
- package/docs/API_REFERENCE.md +98 -1
- package/docs/QUICK_START.md +125 -0
- package/docs/USAGE.md +92 -1
- package/docs/api/core-version.md +1 -1
- package/docs/api/equipment-generic-lens-catalogue.md +10 -5
- package/docs/api/equipment-sensor-profile-body.md +64 -0
- package/docs/api/equipment-sensor-profiles.md +86 -0
- package/docs/api/exports.json +207 -2
- package/docs/api/facade-exposure-build-up.md +105 -0
- package/docs/api/facade-fast-methods.md +9 -1
- package/docs/api/facade-image-plane-enable.md +16 -0
- package/docs/api/facade-mixed-illumination.md +67 -0
- package/docs/api/facade-representative-light-sources.md +131 -0
- package/docs/api/facade-scene-radiance.md +54 -3
- package/docs/api/facade-simulate-capture.md +26 -6
- package/docs/api/motion-subject-motion.md +23 -0
- package/docs/api/optics-generic-lens-lateral-color.md +79 -0
- package/docs/api/output-developed-lens-corrections.md +10 -1
- package/docs/api/output-generic-lens-corrections.md +40 -4
- package/docs/api/output-print-plan.md +35 -1
- package/llms.txt +2 -0
- package/package.json +1 -1
package/docs/API_REFERENCE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Public API reference
|
|
2
2
|
|
|
3
|
-
Generated from the exact root exports for **@photivra/engine 2.
|
|
3
|
+
Generated from the exact root exports for **@photivra/engine 2.21.0 / root contract 2.21.0** by `node scripts/generate-api-reference.mjs`. No module deep import is supported.
|
|
4
4
|
|
|
5
5
|
Use [the developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md) for executable paths and scientific boundaries. Types are contracts, not runtime validation: parse untrusted data, preserve units/reference frames, read assessment blockers, and retain evidence/limitations. Optional properties do not imply a universal default. Model/schema IDs remain independent of the distribution.
|
|
6
6
|
|
|
@@ -976,6 +976,26 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
|
|
|
976
976
|
| [`GenericReleaseDriveMode`](api/equipment-release-capabilities.md#genericreleasedrivemode) | Type |
|
|
977
977
|
| [`parseGenericReleaseCapabilityProfile`](api/equipment-release-capabilities.md#parsegenericreleasecapabilityprofile) | Runtime |
|
|
978
978
|
|
|
979
|
+
## equipment/sensor-profile-body.ts
|
|
980
|
+
|
|
981
|
+
[Detailed contracts](api/equipment-sensor-profile-body.md) · [Source](https://github.com/Photivra/photivra/blob/main/src/equipment/sensor-profile-body.ts)
|
|
982
|
+
|
|
983
|
+
| Export | Kind |
|
|
984
|
+
| --- | --- |
|
|
985
|
+
| [`SENSOR_PROFILE_ISO_ANCHOR`](api/equipment-sensor-profile-body.md#sensor_profile_iso_anchor) | Runtime |
|
|
986
|
+
| [`SensorProfile`](api/equipment-sensor-profile-body.md#sensorprofile) | Type |
|
|
987
|
+
| [`SensorProfileValueKind`](api/equipment-sensor-profile-body.md#sensorprofilevaluekind) | Type |
|
|
988
|
+
|
|
989
|
+
## equipment/sensor-profiles.ts
|
|
990
|
+
|
|
991
|
+
[Detailed contracts](api/equipment-sensor-profiles.md) · [Source](https://github.com/Photivra/photivra/blob/main/src/equipment/sensor-profiles.ts)
|
|
992
|
+
|
|
993
|
+
| Export | Kind |
|
|
994
|
+
| --- | --- |
|
|
995
|
+
| [`resolveSensorProfile`](api/equipment-sensor-profiles.md#resolvesensorprofile) | Runtime |
|
|
996
|
+
| [`SENSOR_PROFILE_IDS`](api/equipment-sensor-profiles.md#sensor_profile_ids) | Runtime |
|
|
997
|
+
| [`SENSOR_PROFILES`](api/equipment-sensor-profiles.md#sensor_profiles) | Runtime |
|
|
998
|
+
|
|
979
999
|
## exposure/auto-iso-slowest-shutter.ts
|
|
980
1000
|
|
|
981
1001
|
[Detailed contracts](api/exposure-auto-iso-slowest-shutter.md) · [Source](https://github.com/Photivra/photivra/blob/main/src/exposure/auto-iso-slowest-shutter.ts)
|
|
@@ -1314,6 +1334,19 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
|
|
|
1314
1334
|
| [`DisplayViewCorrectionStateSet`](api/facade-display-view.md#displayviewcorrectionstateset) | Type |
|
|
1315
1335
|
| [`serializeDisplayViewCorrectionStates`](api/facade-display-view.md#serializedisplayviewcorrectionstates) | Runtime |
|
|
1316
1336
|
|
|
1337
|
+
## facade/exposure-build-up.ts
|
|
1338
|
+
|
|
1339
|
+
[Detailed contracts](api/facade-exposure-build-up.md) · [Source](https://github.com/Photivra/photivra/blob/main/src/facade/exposure-build-up.ts)
|
|
1340
|
+
|
|
1341
|
+
| Export | Kind |
|
|
1342
|
+
| --- | --- |
|
|
1343
|
+
| [`EXPOSURE_BUILD_UP_MAXIMUM_FRACTIONS`](api/facade-exposure-build-up.md#exposure_build_up_maximum_fractions) | Runtime |
|
|
1344
|
+
| [`EXPOSURE_BUILD_UP_STREAM_BASE`](api/facade-exposure-build-up.md#exposure_build_up_stream_base) | Runtime |
|
|
1345
|
+
| [`simulateExposureBuildUp`](api/facade-exposure-build-up.md#simulateexposurebuildup) | Runtime |
|
|
1346
|
+
| [`SimulateExposureBuildUpFrame`](api/facade-exposure-build-up.md#simulateexposurebuildupframe) | Type |
|
|
1347
|
+
| [`SimulateExposureBuildUpOptions`](api/facade-exposure-build-up.md#simulateexposurebuildupoptions) | Type |
|
|
1348
|
+
| [`SimulateExposureBuildUpResult`](api/facade-exposure-build-up.md#simulateexposurebuildupresult) | Type |
|
|
1349
|
+
|
|
1317
1350
|
## facade/fast-methods.ts
|
|
1318
1351
|
|
|
1319
1352
|
[Detailed contracts](api/facade-fast-methods.md) · [Source](https://github.com/Photivra/photivra/blob/main/src/facade/fast-methods.ts)
|
|
@@ -1323,6 +1356,44 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
|
|
|
1323
1356
|
| [`SIMULATE_CAPTURE_FAST_METHODS`](api/facade-fast-methods.md#simulate_capture_fast_methods) | Runtime |
|
|
1324
1357
|
| [`SimulateCaptureFastMethod`](api/facade-fast-methods.md#simulatecapturefastmethod) | Type |
|
|
1325
1358
|
|
|
1359
|
+
## facade/image-plane-enable.ts
|
|
1360
|
+
|
|
1361
|
+
[Detailed contracts](api/facade-image-plane-enable.md) · [Source](https://github.com/Photivra/photivra/blob/main/src/facade/image-plane-enable.ts)
|
|
1362
|
+
|
|
1363
|
+
| Export | Kind |
|
|
1364
|
+
| --- | --- |
|
|
1365
|
+
| [`prepareImagePlaneScenes`](api/facade-image-plane-enable.md#prepareimageplanescenes) | Runtime |
|
|
1366
|
+
|
|
1367
|
+
## facade/mixed-illumination.ts
|
|
1368
|
+
|
|
1369
|
+
[Detailed contracts](api/facade-mixed-illumination.md) · [Source](https://github.com/Photivra/photivra/blob/main/src/facade/mixed-illumination.ts)
|
|
1370
|
+
|
|
1371
|
+
| Export | Kind |
|
|
1372
|
+
| --- | --- |
|
|
1373
|
+
| [`MixedIllumination`](api/facade-mixed-illumination.md#mixedillumination) | Type |
|
|
1374
|
+
| [`MixedIlluminationBlend`](api/facade-mixed-illumination.md#mixedilluminationblend) | Type |
|
|
1375
|
+
| [`MixedIlluminationPoint`](api/facade-mixed-illumination.md#mixedilluminationpoint) | Type |
|
|
1376
|
+
| [`MixedIlluminationPointSource`](api/facade-mixed-illumination.md#mixedilluminationpointsource) | Type |
|
|
1377
|
+
| [`MixedIlluminationSource`](api/facade-mixed-illumination.md#mixedilluminationsource) | Type |
|
|
1378
|
+
|
|
1379
|
+
## facade/representative-light-sources.ts
|
|
1380
|
+
|
|
1381
|
+
[Detailed contracts](api/facade-representative-light-sources.md) · [Source](https://github.com/Photivra/photivra/blob/main/src/facade/representative-light-sources.ts)
|
|
1382
|
+
|
|
1383
|
+
| Export | Kind |
|
|
1384
|
+
| --- | --- |
|
|
1385
|
+
| [`createRepresentativeLightSourceIlluminant`](api/facade-representative-light-sources.md#createrepresentativelightsourceilluminant) | Runtime |
|
|
1386
|
+
| [`createRepresentativeLightSourceSpectrum`](api/facade-representative-light-sources.md#createrepresentativelightsourcespectrum) | Runtime |
|
|
1387
|
+
| [`getRepresentativeLightSourceSet`](api/facade-representative-light-sources.md#getrepresentativelightsourceset) | Runtime |
|
|
1388
|
+
| [`REPRESENTATIVE_LIGHT_SOURCE_IDS`](api/facade-representative-light-sources.md#representative_light_source_ids) | Runtime |
|
|
1389
|
+
| [`REPRESENTATIVE_LIGHT_SOURCE_SET_ID`](api/facade-representative-light-sources.md#representative_light_source_set_id) | Runtime |
|
|
1390
|
+
| [`REPRESENTATIVE_LIGHT_SOURCE_SET_VERSION`](api/facade-representative-light-sources.md#representative_light_source_set_version) | Runtime |
|
|
1391
|
+
| [`REPRESENTATIVE_LIGHT_SOURCE_WAVELENGTH_RANGE_NANOMETERS`](api/facade-representative-light-sources.md#representative_light_source_wavelength_range_nanometers) | Runtime |
|
|
1392
|
+
| [`RepresentativeLightSource`](api/facade-representative-light-sources.md#representativelightsource) | Type |
|
|
1393
|
+
| [`RepresentativeLightSourceBand`](api/facade-representative-light-sources.md#representativelightsourceband) | Type |
|
|
1394
|
+
| [`RepresentativeLightSourceId`](api/facade-representative-light-sources.md#representativelightsourceid) | Type |
|
|
1395
|
+
| [`RepresentativeLightSourceSet`](api/facade-representative-light-sources.md#representativelightsourceset) | Type |
|
|
1396
|
+
|
|
1326
1397
|
## facade/scene-radiance.ts
|
|
1327
1398
|
|
|
1328
1399
|
[Detailed contracts](api/facade-scene-radiance.md) · [Source](https://github.com/Photivra/photivra/blob/main/src/facade/scene-radiance.ts)
|
|
@@ -1337,6 +1408,8 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
|
|
|
1337
1408
|
| [`CHART_SIZE_DEFAULT_SURROUND_REFLECTANCE`](api/facade-scene-radiance.md#chart_size_default_surround_reflectance) | Runtime |
|
|
1338
1409
|
| [`ChartSizeOption`](api/facade-scene-radiance.md#chartsizeoption) | Type |
|
|
1339
1410
|
| [`createPlanarSceneRadianceProvider`](api/facade-scene-radiance.md#createplanarsceneradianceprovider) | Runtime |
|
|
1411
|
+
| [`ImagePlaneImage`](api/facade-scene-radiance.md#imageplaneimage) | Type |
|
|
1412
|
+
| [`ImagePlaneScene`](api/facade-scene-radiance.md#imageplanescene) | Type |
|
|
1340
1413
|
| [`PHOTOPIC_MAXIMUM_LUMINOUS_EFFICACY_LM_PER_W`](api/facade-scene-radiance.md#photopic_maximum_luminous_efficacy_lm_per_w) | Runtime |
|
|
1341
1414
|
| [`PlanarSceneRadianceProvider`](api/facade-scene-radiance.md#planarsceneradianceprovider) | Type |
|
|
1342
1415
|
| [`resolveCaptureIlluminant`](api/facade-scene-radiance.md#resolvecaptureilluminant) | Runtime |
|
|
@@ -1563,6 +1636,14 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
|
|
|
1563
1636
|
| [`CalculateProjectedMotionBlurInput`](api/motion-projected-motion.md#calculateprojectedmotionblurinput) | Type |
|
|
1564
1637
|
| [`ProjectedMotionBlur`](api/motion-projected-motion.md#projectedmotionblur) | Type |
|
|
1565
1638
|
|
|
1639
|
+
## motion/subject-motion.ts
|
|
1640
|
+
|
|
1641
|
+
[Detailed contracts](api/motion-subject-motion.md) · [Source](https://github.com/Photivra/photivra/blob/main/src/motion/subject-motion.ts)
|
|
1642
|
+
|
|
1643
|
+
| Export | Kind |
|
|
1644
|
+
| --- | --- |
|
|
1645
|
+
| [`SimulateCaptureSubjectMotion`](api/motion-subject-motion.md#simulatecapturesubjectmotion) | Type |
|
|
1646
|
+
|
|
1566
1647
|
## motion/temporal-exposure-sum.ts
|
|
1567
1648
|
|
|
1568
1649
|
[Detailed contracts](api/motion-temporal-exposure-sum.md) · [Source](https://github.com/Photivra/photivra/blob/main/src/motion/temporal-exposure-sum.ts)
|
|
@@ -1801,6 +1882,17 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
|
|
|
1801
1882
|
| [`resolveFrontOfLensFilterTransmission`](api/optics-front-of-lens-filter.md#resolvefrontoflensfiltertransmission) | Runtime |
|
|
1802
1883
|
| [`ResolveFrontOfLensFilterTransmissionInput`](api/optics-front-of-lens-filter.md#resolvefrontoflensfiltertransmissioninput) | Type |
|
|
1803
1884
|
|
|
1885
|
+
## optics/generic-lens-lateral-color.ts
|
|
1886
|
+
|
|
1887
|
+
[Detailed contracts](api/optics-generic-lens-lateral-color.md) · [Source](https://github.com/Photivra/photivra/blob/main/src/optics/generic-lens-lateral-color.ts)
|
|
1888
|
+
|
|
1889
|
+
| Export | Kind |
|
|
1890
|
+
| --- | --- |
|
|
1891
|
+
| [`calculateLateralColorFringe`](api/optics-generic-lens-lateral-color.md#calculatelateralcolorfringe) | Runtime |
|
|
1892
|
+
| [`CalculateLateralColorFringeInput`](api/optics-generic-lens-lateral-color.md#calculatelateralcolorfringeinput) | Type |
|
|
1893
|
+
| [`LateralColorDisplacement`](api/optics-generic-lens-lateral-color.md#lateralcolordisplacement) | Type |
|
|
1894
|
+
| [`LateralColorFringe`](api/optics-generic-lens-lateral-color.md#lateralcolorfringe) | Type |
|
|
1895
|
+
|
|
1804
1896
|
## optics/generic-nd-filter-presets.ts
|
|
1805
1897
|
|
|
1806
1898
|
[Detailed contracts](api/optics-generic-nd-filter-presets.md) · [Source](https://github.com/Photivra/photivra/blob/main/src/optics/generic-nd-filter-presets.ts)
|
|
@@ -2183,6 +2275,7 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
|
|
|
2183
2275
|
|
|
2184
2276
|
| Export | Kind |
|
|
2185
2277
|
| --- | --- |
|
|
2278
|
+
| [`ChannelDispersion`](api/output-developed-lens-corrections.md#channeldispersion) | Type |
|
|
2186
2279
|
| [`GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM`](api/output-developed-lens-corrections.md#generic_lens_correction_channel_wavelengths_nm) | Runtime |
|
|
2187
2280
|
|
|
2188
2281
|
## output/generic-lens-corrections.ts
|
|
@@ -2191,6 +2284,8 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
|
|
|
2191
2284
|
|
|
2192
2285
|
| Export | Kind |
|
|
2193
2286
|
| --- | --- |
|
|
2287
|
+
| [`calculateChannelEffectiveDispersion`](api/output-generic-lens-corrections.md#calculatechanneleffectivedispersion) | Runtime |
|
|
2288
|
+
| [`CalculateChannelEffectiveDispersionInput`](api/output-generic-lens-corrections.md#calculatechanneleffectivedispersioninput) | Type |
|
|
2194
2289
|
| [`createGenericLensCorrectionProfile`](api/output-generic-lens-corrections.md#creategenericlenscorrectionprofile) | Runtime |
|
|
2195
2290
|
| [`CreateGenericLensCorrectionProfileInput`](api/output-generic-lens-corrections.md#creategenericlenscorrectionprofileinput) | Type |
|
|
2196
2291
|
|
|
@@ -2336,7 +2431,9 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
|
|
|
2336
2431
|
| [`calculatePrintPlan`](api/output-print-plan.md#calculateprintplan) | Runtime |
|
|
2337
2432
|
| [`calculatePrintSizeLimit`](api/output-print-plan.md#calculateprintsizelimit) | Runtime |
|
|
2338
2433
|
| [`parsePrintPlanInput`](api/output-print-plan.md#parseprintplaninput) | Runtime |
|
|
2434
|
+
| [`PrintContainLayout`](api/output-print-plan.md#printcontainlayout) | Type |
|
|
2339
2435
|
| [`PrintedImageSize`](api/output-print-plan.md#printedimagesize) | Type |
|
|
2436
|
+
| [`PrintFit`](api/output-print-plan.md#printfit) | Type |
|
|
2340
2437
|
| [`PrintLength`](api/output-print-plan.md#printlength) | Type |
|
|
2341
2438
|
| [`PrintNativeSource`](api/output-print-plan.md#printnativesource) | Type |
|
|
2342
2439
|
| [`PrintPlan`](api/output-print-plan.md#printplan) | Type |
|
package/docs/QUICK_START.md
CHANGED
|
@@ -88,6 +88,7 @@ modelling choices (such as "no lens PSF") as `kind: "model"`.
|
|
|
88
88
|
| `chartSize` | fill the frame | 2.3: `{ widthM, heightM?, surroundReflectance? }` places the chart at its physical size (see [Fixed-size chart](#fixed-size-chart-23)). |
|
|
89
89
|
| `illuminant` | Planckian 6500 K | Also `"cie-a"`, `{ kind: "planckian", temperatureKelvin }`, `{ kind: "daylight", correlatedColorTemperatureKelvin, basis }` or `{ kind: "spectrum", spectrum }`. |
|
|
90
90
|
| `illuminanceLux` | 21 481 (π·80 000/11.7) | See [Exposure](#exposure). |
|
|
91
|
+
| `illumination` | off | 2.22 (#550): mixed lighting, two to eight sources (`{ illuminant, illuminanceLux }`) blended across the chart by a linear gradient, a radial falloff or regions, and point sources (`points`) with inverse-square falloff, instead of `illuminant` and `illuminanceLux`. The frame center's light sets the grey card and its strongest source the `"scene-illuminant"` white balance (see [Mixed lighting](https://github.com/Photivra/photivra/blob/main/docs/MIXED_ILLUMINATION.md)). |
|
|
91
92
|
| `output.width`, `output.height` | 64 × 64 | Output pixels, 1–4094 each. The sensor has one extra site on each side for demosaicing (see `output.borderHandling`). |
|
|
92
93
|
| `output.maximumPixels` | off | Opt-in large images (2.2): the most native sensor sites the capture may allocate, up to `NATIVE_RAW_LIMITS.ceilingPixels` (2^27). Sides may then reach 16 384 sites including the margin (see [Large images](#large-images)). |
|
|
93
94
|
| `output.region` | whole frame | 2.3: `{ x, y, width, height }` renders that rectangle of the frame, byte-identical to the whole render (see [Region rendering](#region-rendering-23)). |
|
|
@@ -118,6 +119,7 @@ modelling choices (such as "no lens PSF") as `kind: "model"`.
|
|
|
118
119
|
| `motion.angularVelocityRadPerSec` | still | `{ pitch?, yaw?, roll? }` in rad/s (see [Motion](#motion)). |
|
|
119
120
|
| `motion.shutter` | `"global"` | Or `"rolling"`. |
|
|
120
121
|
| `motion.rollingShutterSkewSeconds` | the body's readout skew | Rolling only. |
|
|
122
|
+
| `motion.subject` | still subject | 2.22.0: `{ velocityMPerSec?: { x?, y? }, angularVelocityRadPerSec?, region?, fast? }`, a moving subject (see [Subject motion](#subject-motion)). |
|
|
121
123
|
| `signal`, `onProgress` | none | An `AbortSignal` cancels the running task; `onProgress` reports `raw`, `white-balance`, `development`, `diffraction-correction` and `sharpening` (#571: output rows, with those options) and `jpeg` tiles. The last event of each stage has `completedTileCount === totalTileCount`. The capture yields to the host about every 8 ms with a `MessageChannel` message (else `setImmediate`, else `setTimeout`), so other queued tasks run in between: an abort sent from another task, such as a Web Worker `message` event, takes effect at the next yield. |
|
|
122
124
|
|
|
123
125
|
Unknown fields are rejected, so a typo never silently falls back to a default.
|
|
@@ -530,6 +532,29 @@ SNR is 88.5 (38.9 dB); at ISO 6400 with 1/64 of the light, S ≈ 123 e⁻ and th
|
|
|
530
532
|
the photon shot noise grows as √S, so less light means more noise whatever the ISO. The display view
|
|
531
533
|
reports the same `sensor` record as its capture.
|
|
532
534
|
|
|
535
|
+
### Sensor profiles (#554)
|
|
536
|
+
|
|
537
|
+
`sensor.profile` takes one of eight representative sensor profile records whose values come from
|
|
538
|
+
published specifications (`SENSOR_PROFILES`, or `resolveSensorProfile(id)`; unnamed, by format,
|
|
539
|
+
resolution, architecture and era), checked strictly, so a modified copy also works. It
|
|
540
|
+
replaces the body's ISO range, readout regimes (full well, read noise of each conversion gain, the
|
|
541
|
+
dual-gain switch and the high gain's storage, ADC bit depth), readout time and fixed-pattern noise,
|
|
542
|
+
and sets the default pitch and peak QE; the characteristics above still override it one at a time.
|
|
543
|
+
|
|
544
|
+
```ts
|
|
545
|
+
import { resolveSensorProfile, simulateCapture } from "@photivra/engine";
|
|
546
|
+
const phone = await simulateCapture({ scene: { preset: "uniform" }, sensor: { profile: resolveSensorProfile("phone-50mp-stacked") } });
|
|
547
|
+
phone.sensor.fullWellElectrons; // 8 600 e⁻ at 1.197 µm
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
Each profile's base ISO is re-rated from its full well, QE and pixel area, so the same ISO reaches
|
|
551
|
+
the RAW white at the same exposure on every profile (the assumption `sensor.profile` records the
|
|
552
|
+
values and the rounding). The phone profile takes the catalogue's three phone lenses
|
|
553
|
+
(`lens: { catalogue: "pv-24-1.8-phone" }`), and they need it. With a profile, a catalogue lens is framed on
|
|
554
|
+
the profile's real sensor (the largest centred region of the output's aspect; assumption `sensor.profileView`), so it
|
|
555
|
+
shows its real field of view. Values, sources, the framing and the ISO rating:
|
|
556
|
+
[SENSOR_PROFILES.md](https://github.com/Photivra/photivra/blob/main/docs/SENSOR_PROFILES.md).
|
|
557
|
+
|
|
533
558
|
## Exposure
|
|
534
559
|
|
|
535
560
|
The facade meters the scene like a reflected-light meter:
|
|
@@ -1689,6 +1714,48 @@ const pan = await simulateCapture({
|
|
|
1689
1714
|
console.log(pan.assumptions.find((a) => a.id === "sensor.shutter")?.value, pan.exposure.settings.shutterSeconds);
|
|
1690
1715
|
```
|
|
1691
1716
|
|
|
1717
|
+
## Subject motion
|
|
1718
|
+
|
|
1719
|
+
`motion.subject` (2.22.0, #551) moves the subject on the scene plane during the exposure: the whole scene, or one
|
|
1720
|
+
rectangle of it (`region: { centerXM, centerYM, widthM, heightM }` in metres at rest) over a still background.
|
|
1721
|
+
|
|
1722
|
+
- `velocityMPerSec: { x, y }` is a translation in m/s on the plane (+x right, +y up as seen from the camera).
|
|
1723
|
+
`angularVelocityRadPerSec` adds a rotation about an axis parallel to the optical axis, counter-clockwise as seen from
|
|
1724
|
+
the camera: the optical axis for the whole scene, the region's center for a region.
|
|
1725
|
+
- The model is rigid motion of a planar layer. At time t from the first opening (camera rotation's time reference), a
|
|
1726
|
+
subject point at rest q is at R(ωt)(q − c) + c + v·t. A plane point shows the subject where one is there, else the
|
|
1727
|
+
region's rest rectangle shows a neutral 18 % background (what the subject revealed), else the still scene. The background
|
|
1728
|
+
is lit by the plane's irradiance at that point: the illuminant, or with `illumination` (#550) the blend and point sources there.
|
|
1729
|
+
- Every scene query reads the subject at its own time. The blur is therefore the same temporal midpoint quadrature as
|
|
1730
|
+
camera rotation (`sensor.temporalSampleCount`, default 8 with a moving subject), and an edge streaks over
|
|
1731
|
+
v·T·m on the sensor (m = v′/D, the thin-lens image distance over the subject distance). With few samples a long streak
|
|
1732
|
+
shows separate copies; raise the count. A test checks the length within sampling error.
|
|
1733
|
+
- A rolling shutter skews a moving subject: each row's exposure window opens later, so it sees the subject later. A test
|
|
1734
|
+
checks the skew against the site windows' start offsets.
|
|
1735
|
+
- Panning: a camera yaw of −v/D rad/s (the engine's rotation convention) follows a subject moving at v m/s in +x. The
|
|
1736
|
+
subject stays sharp and the background streaks.
|
|
1737
|
+
- It combines with `lens.diffraction`, `lens.defocus` (global shutter; their site lattices are formed at each midpoint), the
|
|
1738
|
+
lens projection, distortion and vignetting, and camera rotation. `lens.pupilSampling` is refused.
|
|
1739
|
+
- Display view: sideways motion under a global shutter only. It refuses a rotating subject and a rolling shutter with a
|
|
1740
|
+
reason; `simulateCapture` renders both.
|
|
1741
|
+
- Fast method (2.20.0 convention): with the default count and a still camera, `fast` takes one midpoint per site of the
|
|
1742
|
+
streak plus one when that is fewer than 8 (taps less than one site apart). Exact keeps 8, and an explicit
|
|
1743
|
+
`sensor.temporalSampleCount` always wins. Fast is the default for sRGB samples and the display view, exact for DNG,
|
|
1744
|
+
JPEG and PNG.
|
|
1745
|
+
- Off by default: a request without `motion.subject` is byte-identical to 2.20.0.
|
|
1746
|
+
|
|
1747
|
+
```ts
|
|
1748
|
+
import { simulateCapture } from "@photivra/engine";
|
|
1749
|
+
|
|
1750
|
+
// A subject 4 mm wide moving right at 0.2 m/s, with the camera panning to follow it.
|
|
1751
|
+
const pan = await simulateCapture({
|
|
1752
|
+
scene: { preset: "bar-groups" }, subjectDistanceM: 2, lens: { focalLengthMm: 50 },
|
|
1753
|
+
motion: { angularVelocityRadPerSec: { yaw: -0.1 }, subject: { velocityMPerSec: { x: 0.2 }, region: { centerXM: 0.002, centerYM: 0, widthM: 0.004, heightM: 0.004 } } },
|
|
1754
|
+
output: { width: 32, height: 16 }
|
|
1755
|
+
});
|
|
1756
|
+
console.log(pan.assumptions.find((a) => a.id === "motion.subject")?.value);
|
|
1757
|
+
```
|
|
1758
|
+
|
|
1692
1759
|
## Performance
|
|
1693
1760
|
|
|
1694
1761
|
Almost all of the cost is engine time per sensor site; the facade itself adds little. Since 2.0, the
|
|
@@ -2029,6 +2096,64 @@ developed image in 64-bit floats (24 bytes per pixel, 549 MiB at 24 MP), held as
|
|
|
2029
2096
|
sampled every 50 ms). The extra pass therefore costs about 5 s on one worker after the parts, against about 10 s to render
|
|
2030
2097
|
the whole frame again on one worker.
|
|
2031
2098
|
|
|
2099
|
+
### Exposure build-up (#534)
|
|
2100
|
+
|
|
2101
|
+
`simulateExposureBuildUp(options, { fractions })` renders fractions of one exposure as one accumulating process, the way a
|
|
2102
|
+
camera's live readout of a long exposure shows the light building up. Each fraction is read out and developed as its own
|
|
2103
|
+
photo, and the fraction at 1 is `simulateCapture(options)`, byte for byte (owner decision, 2026-11-06).
|
|
2104
|
+
|
|
2105
|
+
- **Signal.** Each fraction fⱼ below 1 is a capture of the same request in manual mode at the whole exposure's resolved
|
|
2106
|
+
f-number and ISO and the shutter fⱼ·t. So its expected signal at every site is integrated from the spectrum over the window
|
|
2107
|
+
[0, fⱼ·t] by the capture itself. That includes camera rotation, `motion.subject` (#551), mixed lighting (#550, which does not
|
|
2108
|
+
change over time) and, with a rolling shutter, each row's own window. No per-channel signal is precomputed (owner rule).
|
|
2109
|
+
- **Charge.** Photon and dark-charge arrivals are Poisson processes with any rate over time. Given a count n over [0, t], the
|
|
2110
|
+
count over [0, s] is Binomial(n, Λ(s)/Λ(t)), with Λ the expected count (R. G. Gallager, *Discrete Stochastic Processes*,
|
|
2111
|
+
chapter 2, sections 2.4 and 2.5, MIT OpenCourseWare 6.262, free). The whole exposure keeps its ordinary draws. Going down
|
|
2112
|
+
the fractions, each site's photo and dark counts are split: N(fⱼ) ~ Binomial(N(fⱼ₊₁), λ(fⱼ)/λ(fⱼ₊₁)), with λ the site's
|
|
2113
|
+
expected mean in each render (after PRNU and DSNU). As a result:
|
|
2114
|
+
- each fraction's counts are Poisson with that fraction's own mean, which is the mean and variance of a capture at fⱼ·t;
|
|
2115
|
+
- the counts never decrease from one fraction to the next;
|
|
2116
|
+
- two fractions' counts covary by the earlier one's mean.
|
|
2117
|
+
|
|
2118
|
+
The binomial draws are exact: inversion, and Hörmann's BTRS rejection (`src/sensor/binomial-sample.ts`).
|
|
2119
|
+
- **Quadrature.** The windows are nested, so the true means never decrease. The temporal midpoint quadrature can still give an
|
|
2120
|
+
earlier fraction a larger estimate than a later one at a site under motion. There the count is kept (probability 1), and the
|
|
2121
|
+
`exposure.buildUp` assumption counts those sites (`nonMonotoneSites`). Raise `sensor.temporalSampleCount` to remove them.
|
|
2122
|
+
- **Sensor.** Fixed-pattern noise (PRNU, DSNU, hot pixels, offsets) belongs to the sensor and is the same in every fraction.
|
|
2123
|
+
Each fraction's charge is clipped at the full well (`sensor.fullWell`), then read out once: its own read noise (the regime's
|
|
2124
|
+
components; dual gain as the ISO selects), pre-ADC and ADC saturation, and quantization at the ADC bit depth (#555; a #554
|
|
2125
|
+
sensor profile's readout feeds the same stage).
|
|
2126
|
+
- **Cost and settings.** One capture per fraction, plus about 64 bytes per native site for the chain. The request's `fast`
|
|
2127
|
+
and the format default apply to every fraction, and the build-up adds no fast method: with `fast: false` (the calculator's
|
|
2128
|
+
setting) every fraction is an exact capture. `"auto"` white balance is measured on each fraction, as its own photo.
|
|
2129
|
+
- **Limits.** On screen only: formats `"srgb-8"` and `"srgb-16"` (a fraction's JPEG, PNG or DNG is refused). 1 to 64
|
|
2130
|
+
fractions, strictly increasing, above 0 and at most 1. Each fraction's shutter fⱼ·t must be one the body can set. A
|
|
2131
|
+
fraction's realization depends on the whole list, because each step of the chain has its own seeded streams
|
|
2132
|
+
(`EXPOSURE_BUILD_UP_STREAM_BASE`). The same request and list always give the same frames.
|
|
2133
|
+
- **Result.** One frame per fraction: `{ fraction, elapsedSeconds, result }`. Below 1, `result` is the fraction capture's
|
|
2134
|
+
result (`image`, `raw`, `plan` and `request` at fⱼ·t), with the build-up's counts and the assumption `exposure.buildUp`.
|
|
2135
|
+
Its `exposure` and `sensor` are the whole exposure's reports: the camera's settings and noise figure, not the elapsed part's.
|
|
2136
|
+
|
|
2137
|
+
```ts
|
|
2138
|
+
import { simulateExposureBuildUp } from "@photivra/engine";
|
|
2139
|
+
|
|
2140
|
+
const build = await simulateExposureBuildUp({ scene: { preset: "slanted-edge" }, exposure: { mode: "manual", shutterSeconds: 1 / 15, iso: 400 },
|
|
2141
|
+
motion: { angularVelocityRadPerSec: { yaw: 0.2 } }, output: { width: 64, height: 48 } }, { fractions: [0.2, 0.4, 0.6, 0.8, 1] });
|
|
2142
|
+
for (const frame of build.frames) console.log(frame.fraction, frame.elapsedSeconds, frame.result.image.samples.length);
|
|
2143
|
+
```
|
|
2144
|
+
|
|
2145
|
+
The tests are in `test/exposure-build-up.test.ts`:
|
|
2146
|
+
|
|
2147
|
+
- The last fraction equals `simulateCapture` byte for byte: tabulated and lean paths, fixed pattern, dual gain, auto white
|
|
2148
|
+
balance and Auto ISO, camera rotation, rolling shutter, a moving subject and mixed lighting.
|
|
2149
|
+
- Each fraction's mean and variance match a single capture at f·t. Under motion this is checked column by column, and the
|
|
2150
|
+
fraction is shown to differ from the whole exposure scaled by f.
|
|
2151
|
+
- Without read noise the codes never decrease, and Cov(c₁, c₂) = Var(c₁).
|
|
2152
|
+
- Dark frames' read noise is independent between readouts.
|
|
2153
|
+
- A fraction past the full well clips.
|
|
2154
|
+
|
|
2155
|
+
`test/binomial-sample.test.ts` checks the binomial draws with chi-square tests of the probability mass function.
|
|
2156
|
+
|
|
2032
2157
|
### A Print JPEG written in strips
|
|
2033
2158
|
|
|
2034
2159
|
At 1:1 (output size equal to the crop), a Print JPEG's rows can be coded on separate workers.
|
package/docs/USAGE.md
CHANGED
|
@@ -7146,6 +7146,7 @@ console.log(png.length, same);
|
|
|
7146
7146
|
See [Print planning](https://github.com/Photivra/photivra/blob/main/docs/PRINT_PLANNING.md) for a complete, strict packed example and the exact-ratio integer policy.
|
|
7147
7147
|
|
|
7148
7148
|
- `calculatePrintPlan()` says whether the native sampling is enough, and gives a compatible minimum file raster.
|
|
7149
|
+
- `fit: { kind: "contain", paperOrientation }` (2.22.0) fits the whole image inside paper of another shape and reports the borders ([Contain fit](https://github.com/Photivra/photivra/blob/main/docs/PRINT_PLANNING.md#integer-policy-fit-and-sufficiency)).
|
|
7149
7150
|
- `calculatePrintSizeLimit()` distinguishes finite, unbounded and unavailable sampling limits.
|
|
7150
7151
|
- `calculatePrintSizeLimitWithBlurAndNoise()` (2.20.0) gives the largest print at a viewing distance that still looks sharp and clean, counting blur and noise as well as pixels, and names the limiting factor ([Print size limit with blur and noise](https://github.com/Photivra/photivra/blob/main/docs/PRINT_PLANNING.md#print-size-limit-with-blur-and-noise)). An approximation.
|
|
7151
7152
|
- Print quality and delivery are not assessed.
|
|
@@ -7512,6 +7513,26 @@ console.log(calculateGeometricFocusBreathing({
|
|
|
7512
7513
|
}).value.deltaDegrees);
|
|
7513
7514
|
```
|
|
7514
7515
|
|
|
7516
|
+
## Sensor profiles (#554)
|
|
7517
|
+
|
|
7518
|
+
`SENSOR_PROFILES` lists eight representative sensors (full frame, APS-C, medium format and a large phone sensor; BSI,
|
|
7519
|
+
stacked and front-illuminated; 2010s and 2020s) whose values come from published specifications, values derived from them
|
|
7520
|
+
or labelled design values, with no maker or product named. `simulateCapture({ sensor: { profile: record } })` simulates one
|
|
7521
|
+
(the record is checked strictly; `resolveSensorProfile(id)` finds one by id):
|
|
7522
|
+
it replaces the body's ISO range, readout regimes, readout time and fixed-pattern noise, and the #555 characteristics
|
|
7523
|
+
override it one at a time. `resolveSensorProfile(id)` returns one profile; `SENSOR_PROFILE_ISO_ANCHOR` is the ISO anchor its base ISO is re-rated from. Values, sources and the ISO
|
|
7524
|
+
rating: [SENSOR_PROFILES.md](https://github.com/Photivra/photivra/blob/main/docs/SENSOR_PROFILES.md).
|
|
7525
|
+
|
|
7526
|
+
```ts
|
|
7527
|
+
import { SENSOR_PROFILES, simulateCapture } from "@photivra/engine";
|
|
7528
|
+
|
|
7529
|
+
for (const p of SENSOR_PROFILES) {
|
|
7530
|
+
// fast: false (full quality, as the calculator asks) computes the mid-gray noise figure.
|
|
7531
|
+
const r = await simulateCapture({ scene: { preset: "uniform" }, fast: false, sensor: { profile: p }, exposure: { mode: "manual", iso: 3200, shutterSeconds: 1 / 4000 } });
|
|
7532
|
+
console.log(p.name, r.sensor.dynamicRangeStops.toFixed(1), r.sensor.midGrey?.snr.toFixed(1));
|
|
7533
|
+
}
|
|
7534
|
+
```
|
|
7535
|
+
|
|
7515
7536
|
## Generic lens catalogue (#549)
|
|
7516
7537
|
|
|
7517
7538
|
`GENERIC_LENS_CATALOGUE` lists the engine's 30 generic primes (17 professional, 13 consumer, "PV 50 1.2 Pro", "PV 50 1.8"; Photivra's
|
|
@@ -7524,7 +7545,9 @@ correction profile for `resolveLensCorrectionPlan`, which `simulateCapture` appl
|
|
|
7524
7545
|
`output.lensCorrections` (#570; never to the RAW or the DNG), and which a DNG can carry for RAW editors as optional DNG opcodes with
|
|
7525
7546
|
`output.dng.lensCorrectionOpcodes` (#587; the RAW codes unchanged). `simulateCapture` renders a catalogue lens with `lens.catalogue`
|
|
7526
7547
|
([quick start](QUICK_START.md#generic-lens-catalogue-549)); `fast: true` (or `lens: { catalogue: { kind, fast: true } }`) takes its
|
|
7527
|
-
fast method (#568), a labeled approximation that blends the PSF over a field grid, for interactive captures.
|
|
7548
|
+
fast method (#568), a labeled approximation that blends the PSF over a field grid, for interactive captures.
|
|
7549
|
+
`calculateLateralColorFringe({ lensId, activeImagingArea, pixelPitchMicrometers, fieldPosition? })` gives the lens's lateral color as a
|
|
7550
|
+
fringe width: the blue-minus-red image displacement in µm and pixels at a field position (default the corner; #580). Equations, tables and the
|
|
7528
7551
|
chart measurements: [LENS_CATALOGUE.md](https://github.com/Photivra/photivra/blob/main/docs/LENS_CATALOGUE.md).
|
|
7529
7552
|
|
|
7530
7553
|
```ts
|
|
@@ -7790,6 +7813,13 @@ including the whole-image stages `output.diffractionCorrection` and `output.shar
|
|
|
7790
7813
|
bytes as a whole-frame `simulateCapture()` and per-row progress (`diffraction-correction`, `sharpening`). See
|
|
7791
7814
|
[Quick start, Developing a capture rendered in parts](QUICK_START.md#developing-a-capture-rendered-in-parts-571).
|
|
7792
7815
|
|
|
7816
|
+
`simulateExposureBuildUp(options, { fractions })` (#534) renders fractions of one exposure as one accumulating process. Each
|
|
7817
|
+
fraction's expected signal is its own capture at the elapsed time, so camera rotation, a moving subject and mixed lighting are
|
|
7818
|
+
included. Its photo and dark counts are binomial splits of the next fraction's, so they build up in place. Each fraction is
|
|
7819
|
+
clipped at the full well, read out once with its own read noise and developed as its own photo. The fraction at 1 is
|
|
7820
|
+
`simulateCapture(options)` byte for byte. Only the sRGB sample formats are accepted (on screen). See
|
|
7821
|
+
[Quick start, Exposure build-up](QUICK_START.md#exposure-build-up-534).
|
|
7822
|
+
|
|
7793
7823
|
### Color charts (2.14)
|
|
7794
7824
|
|
|
7795
7825
|
The engine's own [color patches](https://github.com/Photivra/photivra/blob/main/docs/COLOR_PATCHES.md) are reflectance spectra lit by the capture's illuminant. Draw the
|
|
@@ -7810,6 +7840,33 @@ const sky = getColorPatchSet().patches.find((p) => p.patchId === "sky-blue");
|
|
|
7810
7840
|
console.log(tungsten.scene.parameters.patchSet, sky?.code, sky?.name, sky?.reference.cielabD65); // "PV-M1", "Midday Sky"
|
|
7811
7841
|
```
|
|
7812
7842
|
|
|
7843
|
+
### A photo as a print (2.21)
|
|
7844
|
+
|
|
7845
|
+
An [image-plane scene](https://github.com/Photivra/photivra/blob/main/docs/IMAGE_PLANE.md) (#581) puts an sRGB (8- or 16-bit) or linear image in front of the camera as a flat matte
|
|
7846
|
+
print. Each pixel becomes a smooth reflectance spectrum in [0, 1] that gives back its sRGB under CIE D65. Under other lights the print's colors are an approximation.
|
|
7847
|
+
`chartSize` sets the print's size (its height follows the image). Without it the print is fitted inside the frame. `prepareDisplayView` renders it on WebGPU in the Speed path.
|
|
7848
|
+
`prepareImagePlaneScenes()` loads the scene's module first, so bundles that never print a photo do not carry it.
|
|
7849
|
+
|
|
7850
|
+
```ts
|
|
7851
|
+
import { prepareImagePlaneScenes, simulateCapture } from "@photivra/engine";
|
|
7852
|
+
|
|
7853
|
+
await prepareImagePlaneScenes(); // loads the scene's module (a lazy chunk) once
|
|
7854
|
+
const width = 2, height = 1, data = Uint8Array.of(255, 255, 255, 30, 60, 200);
|
|
7855
|
+
const print = await simulateCapture({
|
|
7856
|
+
scene: { kind: "image-plane", sceneId: "snapshot", image: { width, height, channels: 3, encoding: "srgb-8", data }, paper: { whiteReflectance: 0.9 } },
|
|
7857
|
+
chartSize: { widthM: 0.1 },
|
|
7858
|
+
subjectDistanceM: 0.4,
|
|
7859
|
+
output: { width: 32, height: 16 }
|
|
7860
|
+
});
|
|
7861
|
+
console.log(print.scene.parameters.encoding, print.assumptions.some((a) => a.id === "scene.imagePlane")); // "srgb-8", true
|
|
7862
|
+
```
|
|
7863
|
+
|
|
7864
|
+
**Representative light sources (2.22).** The engine's own [representative light sources](https://github.com/Photivra/photivra/blob/main/docs/LIGHT_SOURCES.md) are four relative spectra
|
|
7865
|
+
designed by Photivra: a tri-phosphor fluorescent tube and white LEDs of low and high color rendering at 4000 K and a warm one at
|
|
7866
|
+
2700 K, each "representative, not a named product". `createRepresentativeLightSourceIlluminant({ sourceId })` returns an
|
|
7867
|
+
`illuminant` (`{ kind: "spectrum", spectrum, label }`); under the two 4000 K LEDs the color patches differ by up to ΔE00 5.9 while
|
|
7868
|
+
the neutrals stay neutral. Use `sensor.spectralStepNanometers: 5`. The page has a runnable example.
|
|
7869
|
+
|
|
7813
7870
|
In `aperture-priority` mode, the shutter comes from the meter (CIPA DC-004 relations S = 10/H and H = 0.65·L·t/N²) through
|
|
7814
7871
|
`resolveAperturePriorityExposureMode()`.
|
|
7815
7872
|
`exposure.standardExposureDeviationStops` is the exposure used minus the standard exposure for the metered luminance L, in stops,
|
|
@@ -7866,6 +7923,28 @@ The chart presets are also available on their own:
|
|
|
7866
7923
|
- `createPlanarSceneRadianceProvider()` turns a chart into an `evaluateRadiance` callback for the native
|
|
7867
7924
|
environment RAW contract.
|
|
7868
7925
|
|
|
7926
|
+
### Mixed lighting (2.22, #550)
|
|
7927
|
+
|
|
7928
|
+
`illumination` lights the chart with two or more sources blended across it (a linear gradient, a radial falloff or regions),
|
|
7929
|
+
for example window daylight on one side and a lamp on the other. The frame center's light sets the grey-card meter and its
|
|
7930
|
+
strongest source the `"scene-illuminant"` white balance; both are reported in `assumptions`. See
|
|
7931
|
+
[Mixed lighting](https://github.com/Photivra/photivra/blob/main/docs/MIXED_ILLUMINATION.md) for the model and every field.
|
|
7932
|
+
|
|
7933
|
+
```ts
|
|
7934
|
+
import { simulateCapture } from "@photivra/engine";
|
|
7935
|
+
|
|
7936
|
+
const lampPool = await simulateCapture({
|
|
7937
|
+
scene: { preset: "step-wedge" },
|
|
7938
|
+
exposure: { mode: "aperture-priority", metering: "grey-card" },
|
|
7939
|
+
illumination: {
|
|
7940
|
+
sources: [{ illuminant: "cie-a", illuminanceLux: 500 }, { illuminant: { kind: "planckian", temperatureKelvin: 6500 }, illuminanceLux: 20000 }],
|
|
7941
|
+
blend: { kind: "radial", radius: 0.8, stops: [0.25, 1] }
|
|
7942
|
+
},
|
|
7943
|
+
output: { width: 32, height: 16 }
|
|
7944
|
+
});
|
|
7945
|
+
console.log(lampPool.assumptions.find((a) => a.id === "output.whiteBalanceSource")?.value); // { source: 0, illuminant: "cie-a" }
|
|
7946
|
+
```
|
|
7947
|
+
|
|
7869
7948
|
### Display view of a capture
|
|
7870
7949
|
|
|
7871
7950
|
`simulateDisplayView(options, view)` takes a capture request and a display size and returns the real
|
|
@@ -8051,6 +8130,18 @@ const rolling = await simulateCapture({ scene: { preset: "checkerboard" }, motio
|
|
|
8051
8130
|
console.log(rolling.request.siteInput(7 * 8).charge.completenessProfile.startOffsetSecondsFromOpeningReference);
|
|
8052
8131
|
```
|
|
8053
8132
|
|
|
8133
|
+
`motion.subject` (2.22.0) moves the subject instead: the whole scene, or one `region` of it over a still background,
|
|
8134
|
+
at `velocityMPerSec` on the scene plane, optionally rotating at `angularVelocityRadPerSec` about the optical axis. It
|
|
8135
|
+
uses the same temporal midpoints, so a rolling shutter skews the moving subject. The display view renders sideways
|
|
8136
|
+
motion under a global shutter and refuses the rest. See [Subject motion](QUICK_START.md#subject-motion).
|
|
8137
|
+
|
|
8138
|
+
```ts
|
|
8139
|
+
import { simulateCapture } from "@photivra/engine";
|
|
8140
|
+
|
|
8141
|
+
const moving = await simulateCapture({ scene: { preset: "slanted-edge" }, motion: { subject: { velocityMPerSec: { x: 0.5 } } }, output: { width: 8, height: 8 } });
|
|
8142
|
+
console.log(moving.assumptions.find((a) => a.id === "motion.subject")?.value);
|
|
8143
|
+
```
|
|
8144
|
+
|
|
8054
8145
|
## Equivalent f-number, sensor area and blur as a share of the frame
|
|
8055
8146
|
|
|
8056
8147
|
`calculateSensorEquivalence()` says what a lens on one sensor corresponds to on a 36 × 24 mm frame (or on a `reference` you pass). Call it once per sensor to fill a lens table:
|
package/docs/api/core-version.md
CHANGED
|
@@ -11,5 +11,5 @@ version they were made with. Proof-of-concept (POC), schema and model contracts
|
|
|
11
11
|
own versions. Release checks confirm it matches package.json and the packed package.
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
|
-
ENGINE_API_VERSION = "2.
|
|
14
|
+
ENGINE_API_VERSION = "2.21.0" as const
|
|
15
15
|
```
|
|
@@ -4,11 +4,11 @@ The package and root API versions are recorded once in [Navigation](../API_REFER
|
|
|
4
4
|
|
|
5
5
|
## GENERIC_LENS_CATALOGUE
|
|
6
6
|
|
|
7
|
-
The
|
|
7
|
+
The 33 lenses: 17 professional, 13 consumer and (#554) 3 phone lenses, each set by focal length.
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
10
|
GENERIC_LENS_CATALOGUE: readonly GenericLensCatalogueEntry[] = Object.freeze([...PRO.map((row) => entry("professional", row)),
|
|
11
|
-
...CONSUMER.map((row) => entry("consumer", row))])
|
|
11
|
+
...CONSUMER.map((row) => entry("consumer", row)), ...PHONE.map((row) => entry("phone", row))])
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
## GENERIC_LENS_IDS
|
|
@@ -30,7 +30,12 @@ GENERIC_LENS_SET_BASELINES: Readonly<Record<GenericLensSet, Readonly<GenericLens
|
|
|
30
30
|
breathingFraction: -0.03, focusScatterDepthOfFocusFraction: 0.1, closeFocusFactor: 0.15 }),
|
|
31
31
|
consumer: Object.freeze({ sphericalWaves: 0.5, comaWaves: 1.0, astigmatismWaves: 0.8, fieldCurvatureWaves: 0.45, axialColorWaves: 0.4,
|
|
32
32
|
lateralColorFraction: 0.0003, distortionMagnitude: 0.03, naturalVignettingStops: 0.7, opticalVignettingStops: 1.1, veilingGlareFraction: 0.02, ghostFraction: 0.008,
|
|
33
|
-
breathingFraction: -0.08, focusScatterDepthOfFocusFraction: 0.3, closeFocusFactor: 0.5 })
|
|
33
|
+
breathingFraction: -0.08, focusScatterDepthOfFocusFraction: 0.3, closeFocusFactor: 0.5 }),
|
|
34
|
+
// #554: design values (docs/LENS_CATALOGUE.md, "Phone lenses"): the consumer baseline with the stronger fall-off, flare and distortion
|
|
35
|
+
// (corrected in software on phones) of small fixed-aperture phone lenses, and no optical-vignetting change (the aperture is fixed).
|
|
36
|
+
phone: Object.freeze({ sphericalWaves: 0.5, comaWaves: 1.0, astigmatismWaves: 0.8, fieldCurvatureWaves: 0.5, axialColorWaves: 0.3,
|
|
37
|
+
lateralColorFraction: 0.0004, distortionMagnitude: 0.03, naturalVignettingStops: 1.2, opticalVignettingStops: 0.5, veilingGlareFraction: 0.03, ghostFraction: 0.01,
|
|
38
|
+
breathingFraction: -0.05, focusScatterDepthOfFocusFraction: 0.3, closeFocusFactor: 0.5 })
|
|
34
39
|
})
|
|
35
40
|
```
|
|
36
41
|
|
|
@@ -179,10 +184,10 @@ export type GenericLensKind = "standard" | "macro" | "fisheye";
|
|
|
179
184
|
|
|
180
185
|
## GenericLensSet
|
|
181
186
|
|
|
182
|
-
|
|
187
|
+
#554: "phone", the three fixed lenses of a phone sensor profile (`sensor.profile` with `fixedLenses: "phone"`).
|
|
183
188
|
|
|
184
189
|
```ts
|
|
185
|
-
export type GenericLensSet = "professional" | "consumer";
|
|
190
|
+
export type GenericLensSet = "professional" | "consumer" | "phone";
|
|
186
191
|
```
|
|
187
192
|
|
|
188
193
|
## ResolvedGenericLens
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# equipment/sensor-profile-body.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
|
+
## SENSOR_PROFILE_ISO_ANCHOR
|
|
6
|
+
|
|
7
|
+
The engine's ISO anchor (see the module comment): white charge 60 000 e⁻ at ISO 100, 36 µm², QE 0.5.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
SENSOR_PROFILE_ISO_ANCHOR = Object.freeze({ iso: 100, fullWellElectrons: 60000, pixelAreaSquareMicrometers: 36, peakQuantumEfficiency: .5 })
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## SensorProfile
|
|
14
|
+
|
|
15
|
+
One sensor profile. Every number is per photosite unless stated; see `provenance` for each value's kind.
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
export interface SensorProfile {
|
|
19
|
+
id: string;
|
|
20
|
+
/** Format, resolution, architecture and era; no maker or product name. */
|
|
21
|
+
name: string;
|
|
22
|
+
/** A market category, not a specification: it is a label and changes nothing. */
|
|
23
|
+
marketTag: "consumer" | "professional";
|
|
24
|
+
architecture: "front-illuminated" | "back-illuminated" | "stacked";
|
|
25
|
+
sensorWidthMm: number; sensorHeightMm: number;
|
|
26
|
+
pixelsWide: number; pixelsHigh: number;
|
|
27
|
+
pixelPitchMicrometers: number;
|
|
28
|
+
/** The green channel's peak EQE (#555 `peakQuantumEfficiency`). */
|
|
29
|
+
peakQuantumEfficiency: number;
|
|
30
|
+
/** Full-well capacity, e⁻: the charge at the RAW white level at base ISO. */
|
|
31
|
+
fullWellElectrons: number;
|
|
32
|
+
/** Read noise, RMS e⁻; `highGainFromIso` null for one conversion gain throughout. */
|
|
33
|
+
readNoise: { lowGainElectrons: number; highGainElectrons: number; highGainFromIso: number | null };
|
|
34
|
+
/** Charge storage limit of the high conversion gain, e⁻ (null without one): full well × base ISO / switch ISO. */
|
|
35
|
+
highGainFullWellElectrons: number | null;
|
|
36
|
+
adcBitDepth: 12 | 14 | 16;
|
|
37
|
+
/** Re-rated base ISO (see the module comment) and the highest ISO setting. */
|
|
38
|
+
baseIso: number; maximumIso: number;
|
|
39
|
+
/** Largest analog gain relative to base ISO; above it the gain is digital. */
|
|
40
|
+
maximumAnalogGain: number;
|
|
41
|
+
/** Rolling readout, s: the top-to-bottom skew with `motion.shutter: "rolling"`. */
|
|
42
|
+
readoutSeconds: number;
|
|
43
|
+
/** Fixed-pattern noise used with `sensor.fixedPatternNoise` (EMVA 1288 Release 4.0 non-uniformities). */
|
|
44
|
+
fixedPattern: { prnu: number; dsnu: number; hotPixelFraction: number; hotPixelMultiplier: number; rowElectrons: number; columnElectrons: number };
|
|
45
|
+
/** What the profile is good for (text only). */
|
|
46
|
+
goodFor: readonly string[];
|
|
47
|
+
description: string;
|
|
48
|
+
/** "phone": the profile takes only the catalogue's phone lenses (`lens.catalogue`), which need it. */
|
|
49
|
+
fixedLenses: "phone" | null;
|
|
50
|
+
/**
|
|
51
|
+
* Each value's kind and source key (S1–S10, docs/SENSOR_PROFILES.md "Sources"); a value not listed is a design value.
|
|
52
|
+
* For example `fullWellElectrons: "published S1"` or `baseIso: "derived"`.
|
|
53
|
+
*/
|
|
54
|
+
provenance: Readonly<Partial<Record<keyof SensorProfile | "sensorSize" | "pixels" | "highGainFromIso", string>>>;
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## SensorProfileValueKind
|
|
59
|
+
|
|
60
|
+
How a profile value was obtained: copied from a source, calculated from sourced values, or Photivra's design value.
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
export type SensorProfileValueKind = "published" | "derived" | "design";
|
|
64
|
+
```
|