@photivra/engine 2.16.0 → 2.18.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 +104 -1
- package/CITATION.cff +1 -1
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +24 -0
- package/dist/camera/lens-projection.d.ts +5 -0
- package/dist/camera/lens-projection.js +10 -0
- package/dist/camera/lens-projection.js.map +1 -1
- package/dist/camera/sensor-equivalence.d.ts +156 -0
- package/dist/camera/sensor-equivalence.js +208 -0
- package/dist/camera/sensor-equivalence.js.map +1 -0
- package/dist/capture/native-environment-tabulated.d.ts +5 -0
- package/dist/capture/native-environment-tabulated.js +1 -1
- package/dist/capture/native-environment-tabulated.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 +227 -0
- package/dist/equipment/generic-lens-catalogue.js +243 -0
- package/dist/equipment/generic-lens-catalogue.js.map +1 -0
- package/dist/equipment/ideal-lens.d.ts +25 -0
- package/dist/equipment/ideal-lens.js +21 -0
- package/dist/equipment/ideal-lens.js.map +1 -0
- package/dist/facade/catalogue-lens-signal.d.ts +94 -0
- package/dist/facade/catalogue-lens-signal.js +362 -0
- package/dist/facade/catalogue-lens-signal.js.map +1 -0
- package/dist/facade/defocus-bokeh-zones.d.ts +33 -0
- package/dist/facade/defocus-bokeh-zones.js +53 -0
- package/dist/facade/defocus-bokeh-zones.js.map +1 -0
- package/dist/facade/defocus-image-space-provider.d.ts +9 -0
- package/dist/facade/defocus-image-space-provider.js +101 -9
- package/dist/facade/defocus-image-space-provider.js.map +1 -1
- package/dist/facade/diffraction-provider.d.ts +29 -0
- package/dist/facade/diffraction-provider.js +117 -11
- package/dist/facade/diffraction-provider.js.map +1 -1
- package/dist/facade/display-view-gpu-session.js +4 -0
- package/dist/facade/display-view-gpu-session.js.map +1 -1
- package/dist/facade/display-view-gpu.d.ts +10 -0
- package/dist/facade/display-view-gpu.js.map +1 -1
- package/dist/facade/display-view-lens-signal.d.ts +61 -0
- package/dist/facade/display-view-lens-signal.js +502 -0
- package/dist/facade/display-view-lens-signal.js.map +1 -0
- package/dist/facade/expected-signal-setup.d.ts +15 -0
- package/dist/facade/expected-signal-setup.js +19 -2
- package/dist/facade/expected-signal-setup.js.map +1 -1
- package/dist/facade/expected-signal.d.ts +13 -0
- package/dist/facade/expected-signal.js +155 -1
- package/dist/facade/expected-signal.js.map +1 -1
- package/dist/facade/generic-lens-capture.d.ts +7 -0
- package/dist/facade/generic-lens-capture.js +9 -0
- package/dist/facade/generic-lens-capture.js.map +1 -0
- package/dist/facade/large-defocus-lattice.d.ts +39 -0
- package/dist/facade/large-defocus-lattice.js +64 -0
- package/dist/facade/large-defocus-lattice.js.map +1 -0
- package/dist/facade/scene-radiance.d.ts +5 -0
- package/dist/facade/scene-radiance.js +5 -2
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +67 -7
- package/dist/facade/simulate-capture.js +400 -64
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.js +23 -17
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/display-view-quality-gpu.d.ts +0 -9
- package/dist/gpu/display-view-quality-gpu.js +6 -0
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/display-view-speed-gpu.js +2 -0
- package/dist/gpu/display-view-speed-gpu.js.map +1 -1
- package/dist/index.d.ts +6 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/optics/defocus-bokeh-kernel.d.ts +207 -0
- package/dist/optics/defocus-bokeh-kernel.js +380 -0
- package/dist/optics/defocus-bokeh-kernel.js.map +1 -0
- package/dist/optics/defocus-disc-kernel.d.ts +41 -19
- package/dist/optics/defocus-disc-kernel.js +44 -23
- package/dist/optics/defocus-disc-kernel.js.map +1 -1
- package/dist/optics/generic-lens-psf.d.ts +68 -0
- package/dist/optics/generic-lens-psf.js +337 -0
- package/dist/optics/generic-lens-psf.js.map +1 -0
- package/dist/optics/illumination-vignetting.d.ts +6 -0
- package/dist/optics/illumination-vignetting.js +24 -7
- package/dist/optics/illumination-vignetting.js.map +1 -1
- package/dist/optics/sensor-environment-query.d.ts +6 -0
- package/dist/optics/sensor-environment-query.js +13 -6
- package/dist/optics/sensor-environment-query.js.map +1 -1
- package/dist/optics/site-kernel-runs.d.ts +77 -0
- package/dist/optics/site-kernel-runs.js +200 -0
- package/dist/optics/site-kernel-runs.js.map +1 -0
- package/dist/output/generic-lens-corrections.d.ts +24 -0
- package/dist/output/generic-lens-corrections.js +51 -0
- package/dist/output/generic-lens-corrections.js.map +1 -0
- package/docs/AGENT_GUIDE.md +4 -0
- package/docs/API_REFERENCE.md +77 -1
- package/docs/QUICK_START.md +305 -17
- package/docs/USAGE.md +98 -5
- package/docs/api/camera-sensor-equivalence.md +209 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/equipment-generic-lens-catalogue.md +281 -0
- package/docs/api/equipment-ideal-lens.md +26 -0
- package/docs/api/exports.json +202 -2
- package/docs/api/facade-simulate-capture.md +48 -4
- package/docs/api/optics-defocus-bokeh-kernel.md +145 -0
- package/docs/api/optics-defocus-disc-kernel.md +22 -15
- package/docs/api/optics-sensor-environment-query.md +6 -0
- package/docs/api/output-generic-lens-corrections.md +39 -0
- package/llms.txt +3 -0
- package/package.json +1 -1
package/docs/USAGE.md
CHANGED
|
@@ -374,6 +374,8 @@ The actual focal length stays the physical optical quantity used by projection a
|
|
|
374
374
|
- Focus distance is not part of conventional 35 mm-equivalent focal length. A later digital/output crop and the output resolution are also excluded.
|
|
375
375
|
- If a product wants to describe the final digital framing, it should label it as output framing. It should not silently replace the optical focal length or the capture-equivalent focal length.
|
|
376
376
|
|
|
377
|
+
To render a lens by its physical focal length on any sensor, give `simulateCapture()` or the display view `lens.ideal: { focalLengthMm }` with any `lens.fNumber` within `IDEAL_LENS_LIMITS` (f/0.5–f/128, 1–4000 mm; optional `widestFNumber` and `narrowestFNumber` narrow the f-number range for the automatic exposure modes): an aberration-free thin lens that is not scaled by the crop factor, unlike `lens.prime` (see docs/QUICK_START.md, "Ideal lens at any focal length and f-number").
|
|
378
|
+
|
|
377
379
|
`calculateImagingAreaMetrics()` provides the same canonical diagonal crop-factor calculation, independent of raster density, so other engine modules do not need to repeat that formula.
|
|
378
380
|
|
|
379
381
|
## Focus-breathing projection
|
|
@@ -1455,7 +1457,7 @@ console.log(blur.value.diameterMm);
|
|
|
1455
1457
|
|
|
1456
1458
|
You supply the circle-of-confusion criterion used for depth of field (DOF). These are ideal geometric thin-lens models. They do not include diffraction or real-lens aberrations in the DOF criterion.
|
|
1457
1459
|
|
|
1458
|
-
`simulateCapture` and the display view render the defocus blur disc with `lens.defocus: { model: "geometric-disc" }`, up to a kernel radius of `DEFOCUS_DISC_KERNEL_MAXIMUM_RADIUS_SITES` (
|
|
1460
|
+
`simulateCapture` and the display view render the defocus blur disc with `lens.defocus: { model: "geometric-disc" }`, up to a kernel radius of `DEFOCUS_DISC_KERNEL_MAXIMUM_RADIUS_SITES` (512 sites, a disc 1025 sites across, 6.15 mm at 6 µm; 64 sites before 2.17.0) and refuse a larger one. Up to 64 sites the disc is convolved directly, as before; above it, by runs of equal kernel weights (#564), the same sums at a cost that grows with the radius instead of its square ([Quick start, Large defocus discs](QUICK_START.md#large-defocus-discs-564)). Use `calculateDefocusDiscKernelRadius()` to check a request before rendering it. It runs the capture's own resolution and limit test, so it agrees with the capture's refusal, and `convolution` says which method renders it. Pass the f-number the capture uses (in an automatic exposure mode, the resolved one) and the sensor's pixel pitch:
|
|
1459
1461
|
|
|
1460
1462
|
```ts
|
|
1461
1463
|
import { calculateDefocusDiscKernelRadius } from "@photivra/engine";
|
|
@@ -1463,13 +1465,14 @@ import { calculateDefocusDiscKernelRadius } from "@photivra/engine";
|
|
|
1463
1465
|
const check = calculateDefocusDiscKernelRadius({
|
|
1464
1466
|
focalLengthMm: 50,
|
|
1465
1467
|
aperture: 1.4,
|
|
1466
|
-
focusDistanceM:
|
|
1468
|
+
focusDistanceM: 0.25,
|
|
1467
1469
|
subjectDistanceM: 2,
|
|
1468
1470
|
pixelPitchMicrometers: 6
|
|
1469
1471
|
});
|
|
1470
1472
|
|
|
1471
|
-
console.log(check.value.withinLimit); // false: lens.defocus refuses this request
|
|
1472
|
-
console.log(check.value.kernelRadiusSites, check.value.excessKernelRadiusSites); //
|
|
1473
|
+
console.log(check.value.withinLimit); // false: lens.defocus refuses this request (a 7.81 mm blur, 1,303 sites across)
|
|
1474
|
+
console.log(check.value.kernelRadiusSites, check.value.excessKernelRadiusSites); // 651, 139
|
|
1475
|
+
// Focused at 1 m instead: 157 sites across, radius 78, withinLimit true, convolution "runs".
|
|
1473
1476
|
```
|
|
1474
1477
|
|
|
1475
1478
|
See [Physics Foundation](https://github.com/Photivra/photivra/blob/main/docs/PHYSICS_FOUNDATION.md#thin-lens-focus-and-depth-of-field).
|
|
@@ -7470,6 +7473,29 @@ console.log(calculateGeometricFocusBreathing({
|
|
|
7470
7473
|
}).value.deltaDegrees);
|
|
7471
7474
|
```
|
|
7472
7475
|
|
|
7476
|
+
## Generic lens catalogue (#549)
|
|
7477
|
+
|
|
7478
|
+
`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
|
|
7479
|
+
own designs, not real products). `resolveGenericLens` resolves one for a sensor, an f-number and a focus distance: its focal length
|
|
7480
|
+
(scaled by the crop factor), aperture range, close focus, projection and its character, Seidel aberrations, axial and lateral
|
|
7481
|
+
colour, distortion and vignetting profiles, flare, breathing and focus scatter, each a labelled approximation.
|
|
7482
|
+
`simulateGenericLensFocusError` draws a seeded autofocus error; `createGenericLensCorrectionProfile` builds the lens's generic
|
|
7483
|
+
correction profile for `resolveLensCorrectionPlan`. `simulateCapture` renders a catalogue lens with `lens.catalogue`
|
|
7484
|
+
([quick start](QUICK_START.md#generic-lens-catalogue-549)); `fast: true` (or `lens: { catalogue: { kind, fast: true } }`) takes its
|
|
7485
|
+
fast method (#568), a labeled approximation that blends the PSF over a field grid, for interactive captures. Equations, tables and the
|
|
7486
|
+
chart measurements: [LENS_CATALOGUE.md](https://github.com/Photivra/photivra/blob/main/docs/LENS_CATALOGUE.md).
|
|
7487
|
+
|
|
7488
|
+
```ts
|
|
7489
|
+
import { GENERIC_LENS_CATALOGUE, resolveGenericLens, simulateGenericLensFocusError } from "@photivra/engine";
|
|
7490
|
+
|
|
7491
|
+
const frame = { widthMm: 36, heightMm: 24 };
|
|
7492
|
+
const pro = resolveGenericLens({ lensId: "pv-50-1.2-pro", activeImagingArea: frame, fNumber: 2 });
|
|
7493
|
+
const consumer = resolveGenericLens({ lensId: "pv-50-1.8", activeImagingArea: frame, fNumber: 2 });
|
|
7494
|
+
console.log(GENERIC_LENS_CATALOGUE.length, pro.character.aberrations.comaWaves < consumer.character.aberrations.comaWaves); // 30 true
|
|
7495
|
+
const shot = simulateGenericLensFocusError({ lensId: "pv-50-1.8", activeImagingArea: frame, fNumber: 1.8, focusDistanceM: 2, seed: 1, shotIndex: 0 });
|
|
7496
|
+
console.log(shot.focusDistanceM, shot.imageSideErrorMicrometers);
|
|
7497
|
+
```
|
|
7498
|
+
|
|
7473
7499
|
## Parallel native environment RAW (row-band partitions, 2.0)
|
|
7474
7500
|
|
|
7475
7501
|
`createNativeEnvironmentRawTask` runs on one thread. For several cores, split the capture into row bands with
|
|
@@ -7814,6 +7840,20 @@ region that have a clipped contributing site. `simulateCapture` counts the same
|
|
|
7814
7840
|
are equal where every site is far from white. Counts of regions that tile a view add up to the whole view's count. See
|
|
7815
7841
|
[Highlight count](https://github.com/Photivra/photivra/blob/main/docs/DISPLAY_VIEW.md#highlight-count-533).
|
|
7816
7842
|
|
|
7843
|
+
From #563 the view renders `lens.projection`, `lens.distortion`, `lens.vignetting` and camera rotation (global
|
|
7844
|
+
shutter), in focus and with `lens.defocus` out of focus, with each real site's expected electrons as the capture's. These
|
|
7845
|
+
requests run on the CPU (`gpu: "auto"` falls back labelled `unsupported-on-backend`); rotation with a rolling shutter is
|
|
7846
|
+
refused. See [Lens projection, distortion, vignetting and rotation](https://github.com/Photivra/photivra/blob/main/docs/DISPLAY_VIEW.md#lens-projection-distortion-vignetting-and-rotation).
|
|
7847
|
+
|
|
7848
|
+
```ts
|
|
7849
|
+
import { simulateDisplayView } from "@photivra/engine";
|
|
7850
|
+
|
|
7851
|
+
const fisheye = await simulateDisplayView({ scene: { preset: "siemens-star" }, sensor: { pixelPitchMicrometers: 3 }, output: { width: 120, height: 80 },
|
|
7852
|
+
lens: { projection: "fisheye-equisolid-angle", vignetting: { normalizationRadiusMm: 0.25, maximumNormalizedRadius: 1, coefficients: { r2: -0.4, r4: 0, r6: 0 } } } },
|
|
7853
|
+
{ width: 30, height: 20 });
|
|
7854
|
+
console.log(fisheye.kind, fisheye.assumptions.find((a) => a.id === "lens.vignetting")?.kind); // "display-view" "model"
|
|
7855
|
+
```
|
|
7856
|
+
|
|
7817
7857
|
#### Rendering a prepared view on the GPU (2.11, #481)
|
|
7818
7858
|
|
|
7819
7859
|
`prepareDisplayView(request, { gpu })` chooses the renderer: `"off"` (the default) is the CPU reference, `"auto"` the GPU
|
|
@@ -7923,7 +7963,7 @@ console.log(barrel.request.siteInput(0).environment.lensFieldMapping?.distortion
|
|
|
7923
7963
|
lattice, and the capture convolves the scene radiance with it, which is a geometric approximation. With
|
|
7924
7964
|
`lens.diffraction`, each spectral node's diffraction kernel is convolved with the disc, which is a further
|
|
7925
7965
|
approximation. The `lens.defocus` assumption records the diameter in µm and in sites, the kernel radius and the
|
|
7926
|
-
execution. `simulateDisplayView`
|
|
7966
|
+
execution. `simulateDisplayView` renders it too, also with projection, distortion, vignetting and rotation (#563). See [Optics](QUICK_START.md#optics).
|
|
7927
7967
|
|
|
7928
7968
|
```ts
|
|
7929
7969
|
import { simulateCapture } from "@photivra/engine";
|
|
@@ -7937,6 +7977,21 @@ const blurred = await simulateCapture({
|
|
|
7937
7977
|
console.log(blurred.assumptions.find((a) => a.id === "lens.defocus")?.value); // diameterSites ≈ 5.95, kernelRadiusSites 3
|
|
7938
7978
|
```
|
|
7939
7979
|
|
|
7980
|
+
Bokeh character (#553): `lens.defocus` also takes `blades` (a polygonal or rounded-blade opening), `sphericalAberration`
|
|
7981
|
+
(primary W040: a soft-edged blur on one side of the focus plane and a bright-rimmed one on the other) and `catsEye`
|
|
7982
|
+
(mechanical vignetting by field position; `simulateCapture` only). Each is off by default. `calculateDefocusBokehKernel`
|
|
7983
|
+
returns the kernel without rendering. See [Bokeh character](QUICK_START.md#bokeh-character-553).
|
|
7984
|
+
|
|
7985
|
+
```ts
|
|
7986
|
+
import { calculateDefocusBokehKernel } from "@photivra/engine";
|
|
7987
|
+
|
|
7988
|
+
const kernel = calculateDefocusBokehKernel({
|
|
7989
|
+
focalLengthMm: 50, aperture: 4, focusDistanceM: 1.4, subjectDistanceM: 2, pixelPitchMicrometers: 6,
|
|
7990
|
+
bokeh: { blades: { count: 5 }, sphericalAberration: { w040Micrometers: 0.5, atFNumber: 4 } }
|
|
7991
|
+
});
|
|
7992
|
+
console.log(kernel.value.side, kernel.value.kernelRadiusSites, kernel.value.method); // "behind-focus", 17, "pupil-ray-binning"
|
|
7993
|
+
```
|
|
7994
|
+
|
|
7940
7995
|
`motion.angularVelocityRadPerSec` rotates the camera during the exposure (8 time midpoints by
|
|
7941
7996
|
default). `motion.shutter: "rolling"` derives each site's exposure window from the body's rolling
|
|
7942
7997
|
readout, with `resolveElectronicRollingShutterExposureSchedule`. See [Motion](QUICK_START.md#motion).
|
|
@@ -7947,3 +8002,41 @@ import { simulateCapture } from "@photivra/engine";
|
|
|
7947
8002
|
const rolling = await simulateCapture({ scene: { preset: "checkerboard" }, motion: { angularVelocityRadPerSec: { pitch: 0.02 }, shutter: "rolling" }, output: { width: 6, height: 6 } });
|
|
7948
8003
|
console.log(rolling.request.siteInput(7 * 8).charge.completenessProfile.startOffsetSecondsFromOpeningReference);
|
|
7949
8004
|
```
|
|
8005
|
+
|
|
8006
|
+
## Equivalent f-number, sensor area and blur as a share of the frame
|
|
8007
|
+
|
|
8008
|
+
`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:
|
|
8009
|
+
|
|
8010
|
+
```ts
|
|
8011
|
+
import { calculateBlurShareOfFrame, calculateSensorEquivalence } from "@photivra/engine";
|
|
8012
|
+
|
|
8013
|
+
const apsC = calculateSensorEquivalence({
|
|
8014
|
+
sensor: { widthMm: 23.5, heightMm: 15.6 },
|
|
8015
|
+
focalLengthMm: 50,
|
|
8016
|
+
fNumber: 2
|
|
8017
|
+
}).value;
|
|
8018
|
+
|
|
8019
|
+
console.log(apsC.cropFactor); // ≈ 1.53
|
|
8020
|
+
console.log(apsC.equivalentFocalLengthMm); // ≈ 76.7 (framing)
|
|
8021
|
+
console.log(apsC.equivalentFNumber); // ≈ 3.07 (depth of field, blur and total light; not exposure)
|
|
8022
|
+
console.log(apsC.exposureFNumber); // 2 (exposure does not change with sensor size)
|
|
8023
|
+
console.log(apsC.areaRatio); // ≈ 0.42 of the full-frame area
|
|
8024
|
+
console.log(apsC.totalLightDifferenceStops); // ≈ -1.24 stops at the same f-number and shutter
|
|
8025
|
+
console.log(apsC.basis.equivalentFNumber.kind); // "approximation"
|
|
8026
|
+
|
|
8027
|
+
const share = calculateBlurShareOfFrame({
|
|
8028
|
+
blurCircleMm: 0.03,
|
|
8029
|
+
sensor: { widthMm: 36, heightMm: 24 },
|
|
8030
|
+
print: { widthMm: 300, heightMm: 200 }
|
|
8031
|
+
}).value;
|
|
8032
|
+
|
|
8033
|
+
console.log(share.shareOfDiagonal); // ≈ 0.00069 of the frame diagonal
|
|
8034
|
+
console.log(share.print?.blurOnPrintMm); // 0.25 mm on the print
|
|
8035
|
+
```
|
|
8036
|
+
|
|
8037
|
+
- `basis` gives, for every number, `kind: "exact"` or `"approximation"` and the conditions. Crop factor, equivalent focal length, area ratio, total-light difference, entrance pupil and angles of view are exact under their stated conditions.
|
|
8038
|
+
- The equivalent f-number (crop factor × f-number) assumes the same framing, subject distance and output size. It is a thin-lens approximation that is exact only when the focus distance is much greater than the focal length; at close focus the blur shares match at `k·N·(s − f)/(s − k·f)`.
|
|
8039
|
+
- Exposure uses the f-number on the lens. Equivalence never changes shutter, ISO or exposure.
|
|
8040
|
+
- Equal shares of the frame diagonal look the same when images are shown at the same size. `calculateBlurShareOfFrame()` does not judge whether a blur is visible.
|
|
8041
|
+
|
|
8042
|
+
See [Physics Foundation](https://github.com/Photivra/photivra/blob/main/docs/PHYSICS_FOUNDATION.md#sensor-equivalence-f-number-area-and-blur-share).
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# camera/sensor-equivalence.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
|
+
## BlurShareOfFrame
|
|
6
|
+
|
|
7
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
export interface BlurShareOfFrame {
|
|
11
|
+
/** Blur-circle diameter on the sensor in millimetres. */
|
|
12
|
+
blurCircleMm: number;
|
|
13
|
+
/** Sensor diagonal in millimetres. */
|
|
14
|
+
frameDiagonalMm: number;
|
|
15
|
+
/** Blur diameter / frame diagonal. Equal shares look the same at the same output size. */
|
|
16
|
+
shareOfDiagonal: number;
|
|
17
|
+
/** Blur diameter / frame width. */
|
|
18
|
+
shareOfWidth: number;
|
|
19
|
+
/** Blur diameter / frame height. */
|
|
20
|
+
shareOfHeight: number;
|
|
21
|
+
/** Blur on the print; present only when a print size was given. */
|
|
22
|
+
print?: BlurShareOfPrint;
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## BlurShareOfPrint
|
|
27
|
+
|
|
28
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
export interface BlurShareOfPrint {
|
|
32
|
+
/** Linear enlargement from sensor to print: the smaller of the long-side and short-side ratios. */
|
|
33
|
+
enlargement: number;
|
|
34
|
+
/** Blur-circle diameter on the print in millimetres: blurCircleMm × enlargement. */
|
|
35
|
+
blurOnPrintMm: number;
|
|
36
|
+
/** Size of the printed image in millimetres (equal to the print when the aspect ratios match). */
|
|
37
|
+
imageLongSideMm: number;
|
|
38
|
+
imageShortSideMm: number;
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## calculateBlurShareOfFrame
|
|
43
|
+
|
|
44
|
+
A blur circle on the sensor as a share of the frame (diagonal, width and height) and, when a
|
|
45
|
+
print size is given, its diameter on that print.
|
|
46
|
+
|
|
47
|
+
Equal shares of the diagonal look the same when the images are shown at the same size, which
|
|
48
|
+
is how "same framing" comparisons across sensor sizes are stated. Every value is an exact ratio
|
|
49
|
+
or scaling; whether a blur is visible depends on viewing distance and acuity, which this
|
|
50
|
+
function does not judge.
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
export function calculateBlurShareOfFrame(
|
|
54
|
+
input: CalculateBlurShareOfFrameInput
|
|
55
|
+
): CalculationResult<BlurShareOfFrame>;
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
This call is synchronous; any supplied provider must follow its explicit synchronous contract.
|
|
59
|
+
|
|
60
|
+
## CalculateBlurShareOfFrameInput
|
|
61
|
+
|
|
62
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
export interface CalculateBlurShareOfFrameInput {
|
|
66
|
+
/** Blur-circle diameter on the sensor in millimetres (for example from calculateDefocusCircle). Zero is allowed. */
|
|
67
|
+
blurCircleMm: number;
|
|
68
|
+
/** Physical active imaging area of the sensor. */
|
|
69
|
+
sensor: SensorImagingArea;
|
|
70
|
+
/**
|
|
71
|
+
* Optional print size. The whole frame is enlarged to fit inside it without cropping, long side
|
|
72
|
+
* to long side.
|
|
73
|
+
*/
|
|
74
|
+
print?: { widthMm: number; heightMm: number };
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## calculateSensorEquivalence
|
|
79
|
+
|
|
80
|
+
Equivalence of a lens (focal length, f-number) on one sensor against a reference format,
|
|
81
|
+
36 × 24 mm by default: crop factor, equivalent focal length, equivalent f-number for depth of
|
|
82
|
+
field, blur and total light, the unchanged f-number for exposure, sensor area ratio, total-light
|
|
83
|
+
difference in stops and the angles of view.
|
|
84
|
+
|
|
85
|
+
Call it once per sensor for a lens table. Every quantity carries a `basis` entry saying whether it
|
|
86
|
+
is exact under its stated conditions or an approximation. The equivalent f-number is an
|
|
87
|
+
approximation (exact when the focus distance is much greater than the focal length) and never an
|
|
88
|
+
exposure setting: exposure uses `exposureFNumber`, the f-number on the lens.
|
|
89
|
+
|
|
90
|
+
Example: 50 mm f/2 on a 23.5 × 15.6 mm sensor gives crop factor ≈ 1.53, ≈ 77 mm and ≈ f/3.1.
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
export function calculateSensorEquivalence(
|
|
94
|
+
input: CalculateSensorEquivalenceInput
|
|
95
|
+
): CalculationResult<SensorEquivalence>;
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
This call is synchronous; any supplied provider must follow its explicit synchronous contract.
|
|
99
|
+
|
|
100
|
+
## CalculateSensorEquivalenceInput
|
|
101
|
+
|
|
102
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
export interface CalculateSensorEquivalenceInput {
|
|
106
|
+
/** Physical active imaging area of the sensor the lens is used on. */
|
|
107
|
+
sensor: SensorImagingArea;
|
|
108
|
+
/** Physical focal length of the lens in millimetres. */
|
|
109
|
+
focalLengthMm: number;
|
|
110
|
+
/** f-number set on the lens (N = focal length / entrance-pupil diameter). */
|
|
111
|
+
fNumber: number;
|
|
112
|
+
/** Format to compare against. Defaults to the 36 × 24 mm frame. */
|
|
113
|
+
reference?: SensorImagingArea;
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## EquivalenceExactness
|
|
118
|
+
|
|
119
|
+
Whether a value is exact under its stated conditions or an approximation.
|
|
120
|
+
|
|
121
|
+
```ts
|
|
122
|
+
export type EquivalenceExactness = "exact" | "approximation";
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## EquivalenceValueBasis
|
|
126
|
+
|
|
127
|
+
How one equivalence quantity was obtained, and under which conditions it holds.
|
|
128
|
+
|
|
129
|
+
```ts
|
|
130
|
+
export interface EquivalenceValueBasis {
|
|
131
|
+
/** `"exact"` under the stated conditions, or `"approximation"`. */
|
|
132
|
+
kind: EquivalenceExactness;
|
|
133
|
+
/** Plain statement of the definition and its conditions. */
|
|
134
|
+
statement: string;
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## SensorEquivalence
|
|
139
|
+
|
|
140
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
141
|
+
|
|
142
|
+
```ts
|
|
143
|
+
export interface SensorEquivalence {
|
|
144
|
+
/** Physical focal length supplied by the caller, in millimetres. */
|
|
145
|
+
focalLengthMm: number;
|
|
146
|
+
/** f-number supplied by the caller. */
|
|
147
|
+
fNumber: number;
|
|
148
|
+
/** Sensor active imaging area used. */
|
|
149
|
+
sensor: SensorImagingArea;
|
|
150
|
+
/** Reference format used (36 × 24 mm unless the caller passed one). */
|
|
151
|
+
reference: SensorImagingArea;
|
|
152
|
+
/** Sensor diagonal in millimetres. */
|
|
153
|
+
sensorDiagonalMm: number;
|
|
154
|
+
/** Reference diagonal in millimetres. */
|
|
155
|
+
referenceDiagonalMm: number;
|
|
156
|
+
/** Diagonal crop factor: reference diagonal / sensor diagonal. Greater than 1 for a smaller sensor. */
|
|
157
|
+
cropFactor: number;
|
|
158
|
+
/** Focal length on the reference format with the same diagonal angle of view: cropFactor × focalLengthMm. */
|
|
159
|
+
equivalentFocalLengthMm: number;
|
|
160
|
+
/**
|
|
161
|
+
* f-number on the reference format that gives the same depth of field, background blur and total
|
|
162
|
+
* light for the same framing and output size: cropFactor × fNumber. Not an exposure setting.
|
|
163
|
+
*/
|
|
164
|
+
equivalentFNumber: number;
|
|
165
|
+
/** f-number that sets exposure (image brightness): the f-number on the lens, unchanged by sensor size. */
|
|
166
|
+
exposureFNumber: number;
|
|
167
|
+
/** Entrance-pupil diameter f/N in millimetres; the equivalent lens has the same diameter. */
|
|
168
|
+
entrancePupilDiameterMm: number;
|
|
169
|
+
/** Sensor area / reference area. Less than 1 for a smaller sensor. */
|
|
170
|
+
areaRatio: number;
|
|
171
|
+
/**
|
|
172
|
+
* log2(areaRatio): total light collected by the sensor relative to the reference at the same
|
|
173
|
+
* f-number, shutter time and scene, in stops. Negative for a smaller sensor.
|
|
174
|
+
*/
|
|
175
|
+
totalLightDifferenceStops: number;
|
|
176
|
+
/** Angles of view of this lens on the sensor (the equivalent lens gives the same diagonal angle). */
|
|
177
|
+
fieldOfView: SensorEquivalenceFieldOfView;
|
|
178
|
+
/** Exact or approximated, with the conditions, for every quantity above. */
|
|
179
|
+
basis: Readonly<Record<SensorEquivalenceQuantity, EquivalenceValueBasis>>;
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## SensorEquivalenceFieldOfView
|
|
184
|
+
|
|
185
|
+
Angles of view of an ideal rectilinear lens focused at infinity, in degrees.
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
export interface SensorEquivalenceFieldOfView {
|
|
189
|
+
horizontalDegrees: number;
|
|
190
|
+
verticalDegrees: number;
|
|
191
|
+
diagonalDegrees: number;
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## SensorEquivalenceQuantity
|
|
196
|
+
|
|
197
|
+
Quantities of a sensor equivalence result that carry an exactness statement.
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
export type SensorEquivalenceQuantity =
|
|
201
|
+
| "cropFactor"
|
|
202
|
+
| "equivalentFocalLengthMm"
|
|
203
|
+
| "equivalentFNumber"
|
|
204
|
+
| "exposureFNumber"
|
|
205
|
+
| "entrancePupilDiameterMm"
|
|
206
|
+
| "areaRatio"
|
|
207
|
+
| "totalLightDifferenceStops"
|
|
208
|
+
| "fieldOfView";
|
|
209
|
+
```
|
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.18.0" as const
|
|
15
15
|
```
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
# equipment/generic-lens-catalogue.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
|
+
## GENERIC_LENS_CATALOGUE
|
|
6
|
+
|
|
7
|
+
The 30 lenses: 17 professional, then 13 consumer, each set by focal length.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
GENERIC_LENS_CATALOGUE: readonly GenericLensCatalogueEntry[] = Object.freeze([...PRO.map((row) => entry("professional", row)),
|
|
11
|
+
...CONSUMER.map((row) => entry("consumer", row))])
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## GENERIC_LENS_IDS
|
|
15
|
+
|
|
16
|
+
The catalogue ids, in catalogue order.
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
GENERIC_LENS_IDS: readonly string[] = Object.freeze(GENERIC_LENS_CATALOGUE.map((lens) => lens.lensId))
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## GENERIC_LENS_SET_BASELINES
|
|
23
|
+
|
|
24
|
+
The set baselines (owner's table, #549), each lens's starting point before its adjustments.
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
GENERIC_LENS_SET_BASELINES: Readonly<Record<GenericLensSet, Readonly<GenericLensCharacterValues>>> = Object.freeze({
|
|
28
|
+
professional: Object.freeze({ sphericalWaves: 0.25, comaWaves: 0.35, astigmatismWaves: 0.3, fieldCurvatureWaves: 0.15, axialColorWaves: 0.15,
|
|
29
|
+
lateralColorFraction: 0.00008, distortionMagnitude: 0.01, naturalVignettingStops: 0.4, opticalVignettingStops: 0.6, veilingGlareFraction: 0.005, ghostFraction: 0.002,
|
|
30
|
+
breathingFraction: -0.03, focusScatterDepthOfFocusFraction: 0.1, closeFocusFactor: 0.15 }),
|
|
31
|
+
consumer: Object.freeze({ sphericalWaves: 0.5, comaWaves: 1.0, astigmatismWaves: 0.8, fieldCurvatureWaves: 0.45, axialColorWaves: 0.4,
|
|
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 })
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## GenericLensAdjustments
|
|
38
|
+
|
|
39
|
+
Per-lens multipliers of the set baseline (missing: 1). `distortion` is signed and required: −1 barrel, +1 pincushion at the baseline.
|
|
40
|
+
|
|
41
|
+
```ts
|
|
42
|
+
export type GenericLensAdjustments = { distortion: number } & Partial<Record<"spherical" | "coma" | "astigmatism" | "fieldCurvature" | "axialColor" | "lateralColor" |
|
|
43
|
+
"vignetting" | "strayLight" | "breathing" | "focusScatter" | "closeFocus", number>>;
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## GenericLensCatalogueEntry
|
|
47
|
+
|
|
48
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
export interface GenericLensCatalogueEntry {
|
|
52
|
+
/** Catalogue id, for example "pv-50-1.2-pro". */
|
|
53
|
+
lensId: string;
|
|
54
|
+
/** Display name, for example "PV 50 1.2 Pro". */
|
|
55
|
+
name: string;
|
|
56
|
+
set: GenericLensSet;
|
|
57
|
+
kind: GenericLensKind;
|
|
58
|
+
/** Full-frame-equivalent focal length (36 × 24 mm reference). */
|
|
59
|
+
equivalentFocalLengthMm: number;
|
|
60
|
+
widestFNumber: number;
|
|
61
|
+
narrowestFNumber: number;
|
|
62
|
+
/** Largest magnification: 0.15 (the generic rule) or 1 for the professional macro. */
|
|
63
|
+
maximumMagnification: number;
|
|
64
|
+
projection: LensProjectionModel;
|
|
65
|
+
/** Fisheyes only: a circular image inside the frame, or one that fills it. */
|
|
66
|
+
fisheyeImage?: "circular" | "full-frame";
|
|
67
|
+
adjustments: Readonly<GenericLensAdjustments>;
|
|
68
|
+
/** Why this lens departs from its set's baseline. */
|
|
69
|
+
note: string;
|
|
70
|
+
/**
|
|
71
|
+
* A short plain-English summary of the lens's character (at most 40 characters, US spelling), written from its adjustments and
|
|
72
|
+
* `note`, for lists and pickers. It describes only what the catalogue models (bokeh shape is not modeled: the defocus is a disc).
|
|
73
|
+
*/
|
|
74
|
+
summary: string;
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## GenericLensCharacter
|
|
79
|
+
|
|
80
|
+
A catalogue lens's character resolved for a sensor, an f-number and a focus distance. Aberrations in waves at 550 nm.
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
export interface GenericLensCharacter {
|
|
84
|
+
fNumber: number;
|
|
85
|
+
focusDistanceM: number | null;
|
|
86
|
+
/** Thin-lens magnification at the focus distance (0 at infinity). */
|
|
87
|
+
magnification: number;
|
|
88
|
+
/** 1 + c·m/m_max. */
|
|
89
|
+
closeFocusGain: number;
|
|
90
|
+
/** Pupil-radius ratio N0/N. */
|
|
91
|
+
pupilScale: number;
|
|
92
|
+
/** Resolved Seidel coefficients at the frame corner (H = 1), waves at 550 nm; `balancingDefocusWaves` = −spherical. */
|
|
93
|
+
aberrations: { sphericalWaves: number; balancingDefocusWaves: number; comaWaves: number; astigmatismWaves: number; fieldCurvatureWaves: number; axialColorWaves: number };
|
|
94
|
+
/** Lateral colour: F-to-C image-radius difference at the corner, fraction of the half-diagonal and micrometres on this sensor. */
|
|
95
|
+
lateralColor: { fraction: number; cornerMicrometers: number };
|
|
96
|
+
/** Radial distortion profile (normalised to the half-diagonal, valid to 1.25 of it, beyond the corner) and its corner value (fraction; negative = barrel). */
|
|
97
|
+
distortion: { profile: RadialDistortionProfile; corner: number };
|
|
98
|
+
/** Relative illumination profile (normalised to the half-diagonal, valid to 1.25 of it) and the corner fall-off in stops. */
|
|
99
|
+
vignetting: { profile: IlluminationVignettingProfile; cornerStops: number };
|
|
100
|
+
strayLight: { veilingGlareFraction: number; ghostFraction: number };
|
|
101
|
+
/** Field-of-view change at this focus and at minimum focus, beyond thin-lens unit focusing (fraction, negative = narrower). */
|
|
102
|
+
breathing: { fieldOfViewChange: number; atMinimumFocus: number };
|
|
103
|
+
/** Standard deviation of the autofocus error as a fraction of the depth of focus 2·N·c. */
|
|
104
|
+
focusScatterDepthOfFocusFraction: number;
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## GenericLensCharacterValues
|
|
109
|
+
|
|
110
|
+
The character of a set, at each lens's widest aperture. Aberrations are in waves at 550 nm at the frame corner.
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
export interface GenericLensCharacterValues {
|
|
114
|
+
/** Spherical aberration W040 (balanced by W020 = −W040): centre sharpness. */
|
|
115
|
+
sphericalWaves: number;
|
|
116
|
+
/** Coma W131 at H = 1: the comet-shaped point images of night photography. */
|
|
117
|
+
comaWaves: number;
|
|
118
|
+
/** Astigmatism W222 at H = 1: edge and corner sharpness. */
|
|
119
|
+
astigmatismWaves: number;
|
|
120
|
+
/** Field curvature W220 at H = 1: edge and corner sharpness. */
|
|
121
|
+
fieldCurvatureWaves: number;
|
|
122
|
+
/** Axial colour: the F-to-C difference of the defocus W020, waves at 550 nm. */
|
|
123
|
+
axialColorWaves: number;
|
|
124
|
+
/** Lateral colour: the F-to-C difference of the image radius at the corner, as a fraction of the half-diagonal. */
|
|
125
|
+
lateralColorFraction: number;
|
|
126
|
+
/** Corner distortion magnitude |k1| (fraction); each lens gives the sign (barrel −, pincushion +). */
|
|
127
|
+
distortionMagnitude: number;
|
|
128
|
+
/** Corner fall-off in stops that stays when stopped down (natural vignetting). */
|
|
129
|
+
naturalVignettingStops: number;
|
|
130
|
+
/** Extra corner fall-off in stops at the widest aperture (optical vignetting); gone two stops down. */
|
|
131
|
+
opticalVignettingStops: number;
|
|
132
|
+
/** Veiling glare: the fraction of the image's mean irradiance spread as a uniform veil. */
|
|
133
|
+
veilingGlareFraction: number;
|
|
134
|
+
/** Peak ghost power as a fraction of a bright source's power on the axis. */
|
|
135
|
+
ghostFraction: number;
|
|
136
|
+
/** Field-of-view change between infinity and minimum focus as a fraction (negative: narrower), beyond thin-lens unit focusing. */
|
|
137
|
+
breathingFraction: number;
|
|
138
|
+
/** Standard deviation of the autofocus error as a fraction of the depth of focus 2·N·c. */
|
|
139
|
+
focusScatterDepthOfFocusFraction: number;
|
|
140
|
+
/** c in the close-focus factor 1 + c·m/m_max. */
|
|
141
|
+
closeFocusFactor: number;
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## GenericLensFocusError
|
|
146
|
+
|
|
147
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
export interface GenericLensFocusError {
|
|
151
|
+
lensId: string;
|
|
152
|
+
/** The intended and the sampled focus distance, m (the sampled one held within minimum focus and a far limit). */
|
|
153
|
+
intendedFocusDistanceM: number;
|
|
154
|
+
focusDistanceM: number;
|
|
155
|
+
/** The standard normal draw z and the image-side focus error z·σ·2Nc, µm (positive: a longer image distance, so focused nearer). */
|
|
156
|
+
standardNormal: number;
|
|
157
|
+
imageSideErrorMicrometers: number;
|
|
158
|
+
/** σ (fraction of the depth of focus) and the depth of focus 2·N·c, µm. */
|
|
159
|
+
depthOfFocusFraction: number;
|
|
160
|
+
depthOfFocusMicrometers: number;
|
|
161
|
+
/** True when the sampled image distance was held at the minimum-focus or the far (1000 m) limit. */
|
|
162
|
+
held: "minimum-focus" | "far-limit" | null;
|
|
163
|
+
scientificStatus: "approximation";
|
|
164
|
+
}
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## GenericLensKind
|
|
168
|
+
|
|
169
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
export type GenericLensKind = "standard" | "macro" | "fisheye";
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## GenericLensSet
|
|
176
|
+
|
|
177
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
export type GenericLensSet = "professional" | "consumer";
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## ResolvedGenericLens
|
|
184
|
+
|
|
185
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
export interface ResolvedGenericLens {
|
|
189
|
+
lensId: string;
|
|
190
|
+
name: string;
|
|
191
|
+
set: GenericLensSet;
|
|
192
|
+
kind: GenericLensKind;
|
|
193
|
+
equivalentFocalLengthMm: number;
|
|
194
|
+
/** Physical focal length on this sensor: equivalent ÷ crop factor. */
|
|
195
|
+
focalLengthMm: number;
|
|
196
|
+
cropFactor35Mm: number;
|
|
197
|
+
/** Half-diagonal of the sensor, mm: field height H = 1 and ρ = 1 of the profiles. */
|
|
198
|
+
halfDiagonalMm: number;
|
|
199
|
+
widestFNumber: number;
|
|
200
|
+
narrowestFNumber: number;
|
|
201
|
+
/** Marked third-stop f-numbers within the range. */
|
|
202
|
+
fNumbers: readonly number[];
|
|
203
|
+
/** max(0.10 m, f·(1 + 1/m_max)), metres from the lens (thin lens). */
|
|
204
|
+
minimumFocusDistanceM: number;
|
|
205
|
+
maximumMagnification: number;
|
|
206
|
+
projection: LensProjectionModel;
|
|
207
|
+
fisheyeImage: "circular" | "full-frame" | null;
|
|
208
|
+
/** Fisheyes: the radius of the 180° image circle on this sensor, 2·f·sin 45° (equisolid), mm. Null otherwise. */
|
|
209
|
+
imageCircleRadiusMm: number | null;
|
|
210
|
+
character: GenericLensCharacter;
|
|
211
|
+
note: string;
|
|
212
|
+
/** The catalogue entry's `summary`: a short plain-English description of the lens's character (at most 40 characters). */
|
|
213
|
+
summary: string;
|
|
214
|
+
evidence: readonly EvidenceProvenance[];
|
|
215
|
+
scientificStatus: "approximation";
|
|
216
|
+
limitations: readonly string[];
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## resolveGenericLens
|
|
221
|
+
|
|
222
|
+
Resolves a catalogue lens for a sensor: its focal length (equivalent ÷ the diagonal crop factor), f-number range, close focus
|
|
223
|
+
and projection, and its character (aberrations, colour, distortion, vignetting, flare, breathing and focus scatter) at the
|
|
224
|
+
given f-number and focus distance. Every value is a labelled approximation of a generic lens, not a measured product.
|
|
225
|
+
|
|
226
|
+
```ts
|
|
227
|
+
export function resolveGenericLens(input: ResolveGenericLensInput): ResolvedGenericLens;
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
This call is synchronous; any supplied provider must follow its explicit synchronous contract.
|
|
231
|
+
|
|
232
|
+
## ResolveGenericLensInput
|
|
233
|
+
|
|
234
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
235
|
+
|
|
236
|
+
```ts
|
|
237
|
+
export interface ResolveGenericLensInput {
|
|
238
|
+
/** A catalogue id (`GENERIC_LENS_IDS`). */
|
|
239
|
+
lensId: string;
|
|
240
|
+
/** The sensor's active capture area, mm. */
|
|
241
|
+
activeImagingArea: { widthMm: number; heightMm: number };
|
|
242
|
+
/** Default: the widest aperture. Must lie in the lens's range. */
|
|
243
|
+
fNumber?: number;
|
|
244
|
+
/** Default: infinity. At least the minimum focus distance. */
|
|
245
|
+
focusDistanceM?: number;
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## simulateGenericLensFocusError
|
|
250
|
+
|
|
251
|
+
Focus consistency (#549): a seeded, reproducible autofocus error for one shot. The image-side error is z·σ·2Nc, with z a
|
|
252
|
+
standard normal draw from (seed, shotIndex) (MurmurHash3 stream seed, xorshift32 and Box–Muller, the generators of the
|
|
253
|
+
capture's sensor noise), σ the lens's focus scatter as a fraction of the depth of focus 2·N·c, and
|
|
254
|
+
c the circle of confusion. The thin-lens image distance of the intended focus moves by that error and the focus distance is
|
|
255
|
+
read back from it. An approximation of autofocus variation; off unless called (or `simulateCapture`'s `lens.focusScatter`).
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
export function simulateGenericLensFocusError(input: SimulateGenericLensFocusErrorInput): GenericLensFocusError;
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
This call is synchronous; any supplied provider must follow its explicit synchronous contract.
|
|
262
|
+
|
|
263
|
+
## SimulateGenericLensFocusErrorInput
|
|
264
|
+
|
|
265
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
export interface SimulateGenericLensFocusErrorInput {
|
|
269
|
+
lensId: string;
|
|
270
|
+
activeImagingArea: { widthMm: number; heightMm: number };
|
|
271
|
+
fNumber: number;
|
|
272
|
+
/** The intended focus distance, m. */
|
|
273
|
+
focusDistanceM: number;
|
|
274
|
+
/** uint32 seed of the shooting session. */
|
|
275
|
+
seed: number;
|
|
276
|
+
/** uint32 index of the shot in the session. Default 0. */
|
|
277
|
+
shotIndex?: number;
|
|
278
|
+
/** Circle of confusion for the depth of focus, mm. Default: the sensor diagonal / 1500. */
|
|
279
|
+
circleOfConfusionMm?: number;
|
|
280
|
+
}
|
|
281
|
+
```
|