@photivra/engine 2.18.0 → 2.20.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 +179 -1
- package/CITATION.cff +1 -1
- package/NODE_SUPPORT.md +7 -5
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +14 -10
- package/dist/capture/dng-lens-correction-opcodes.d.ts +32 -0
- package/dist/capture/dng-lens-correction-opcodes.js +119 -0
- package/dist/capture/dng-lens-correction-opcodes.js.map +1 -0
- package/dist/capture/focus-control.js +2 -75
- package/dist/capture/focus-control.js.map +1 -1
- package/dist/capture/native-environment-lean.d.ts +1 -0
- package/dist/capture/native-environment-lean.js +2 -6
- package/dist/capture/native-environment-lean.js.map +1 -1
- package/dist/capture/native-environment-optical-stage.d.ts +1 -0
- package/dist/capture/native-environment-optical-stage.js +2 -5
- package/dist/capture/native-environment-optical-stage.js.map +1 -1
- package/dist/capture/native-environment-raw.js +1 -5
- package/dist/capture/native-environment-raw.js.map +1 -1
- package/dist/capture/native-raw-dng.d.ts +6 -0
- package/dist/capture/native-raw-dng.js +16 -2
- package/dist/capture/native-raw-dng.js.map +1 -1
- package/dist/capture/release-sequence.js +2 -47
- package/dist/capture/release-sequence.js.map +1 -1
- package/dist/capture/sensor-raw-producer.js +3 -5
- package/dist/capture/sensor-raw-producer.js.map +1 -1
- package/dist/color/white-balance.d.ts +2 -0
- package/dist/color/white-balance.js +3 -56
- package/dist/color/white-balance.js.map +1 -1
- package/dist/composition/image-formation-plan.d.ts +1 -0
- package/dist/composition/image-formation-plan.js +3 -49
- package/dist/composition/image-formation-plan.js.map +1 -1
- package/dist/core/evidence-provenance.js +1 -16
- package/dist/core/evidence-provenance.js.map +1 -1
- package/dist/core/record-validation.d.ts +54 -0
- package/dist/core/record-validation.js +176 -0
- package/dist/core/record-validation.js.map +1 -1
- package/dist/core/scientific-assurance.js +1 -18
- package/dist/core/scientific-assurance.js.map +1 -1
- package/dist/core/shared-helpers.d.ts +35 -0
- package/dist/core/shared-helpers.js +38 -0
- package/dist/core/shared-helpers.js.map +1 -0
- package/dist/core/spectral-composition.js +1 -36
- package/dist/core/spectral-composition.js.map +1 -1
- package/dist/core/spectral.js +1 -19
- package/dist/core/spectral.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/exposure-capabilities.d.ts +1 -0
- package/dist/equipment/exposure-capabilities.js +2 -36
- package/dist/equipment/exposure-capabilities.js.map +1 -1
- package/dist/equipment/generic-lens-catalogue.d.ts +8 -2
- package/dist/equipment/generic-lens-catalogue.js +31 -2
- package/dist/equipment/generic-lens-catalogue.js.map +1 -1
- package/dist/equipment/generic-tier-assets.js +2 -3
- package/dist/equipment/generic-tier-assets.js.map +1 -1
- package/dist/equipment/generic-tier-presets.js +2 -3
- package/dist/equipment/generic-tier-presets.js.map +1 -1
- package/dist/equipment/iso-capabilities.js +2 -49
- package/dist/equipment/iso-capabilities.js.map +1 -1
- package/dist/equipment/metering-capabilities.js +1 -26
- package/dist/equipment/metering-capabilities.js.map +1 -1
- package/dist/equipment/release-capabilities.js +1 -53
- package/dist/equipment/release-capabilities.js.map +1 -1
- package/dist/exposure/auto-iso-slowest-shutter.js +1 -7
- package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -1
- package/dist/exposure/exposure-duration-control.js +1 -16
- package/dist/exposure/exposure-duration-control.js.map +1 -1
- package/dist/exposure/exposure-mode-resolver.js +1 -17
- package/dist/exposure/exposure-mode-resolver.js.map +1 -1
- package/dist/exposure/filtered-exposure-time.d.ts +93 -0
- package/dist/exposure/filtered-exposure-time.js +129 -0
- package/dist/exposure/filtered-exposure-time.js.map +1 -0
- package/dist/exposure/flash-ttl.d.ts +1 -0
- package/dist/exposure/flash-ttl.js +1 -1
- package/dist/exposure/flash-ttl.js.map +1 -1
- package/dist/exposure/flash.js +1 -49
- package/dist/exposure/flash.js.map +1 -1
- package/dist/exposure/metering-scene-radiance.js +1 -25
- package/dist/exposure/metering-scene-radiance.js.map +1 -1
- package/dist/exposure/metering-target.js +1 -30
- package/dist/exposure/metering-target.js.map +1 -1
- package/dist/exposure/metering-temporal.js +1 -8
- package/dist/exposure/metering-temporal.js.map +1 -1
- package/dist/exposure/metering.js +1 -72
- package/dist/exposure/metering.js.map +1 -1
- package/dist/exposure/program-line.js +1 -45
- package/dist/exposure/program-line.js.map +1 -1
- package/dist/facade/develop-joined-parts.d.ts +29 -0
- package/dist/facade/develop-joined-parts.js +33 -0
- package/dist/facade/develop-joined-parts.js.map +1 -0
- package/dist/facade/display-view-gpu.d.ts +7 -4
- package/dist/facade/display-view-gpu.js +4 -4
- package/dist/facade/display-view-gpu.js.map +1 -1
- package/dist/facade/display-view-lens-signal.d.ts +1 -1
- package/dist/facade/display-view-lens-signal.js +5 -3
- package/dist/facade/display-view-lens-signal.js.map +1 -1
- package/dist/facade/display-view.d.ts +3 -1
- package/dist/facade/display-view.js +1 -1
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/fast-methods.d.ts +50 -0
- package/dist/facade/fast-methods.js +50 -0
- package/dist/facade/fast-methods.js.map +1 -0
- package/dist/facade/fast-resolution.d.ts +12 -0
- package/dist/facade/fast-resolution.js +18 -0
- package/dist/facade/fast-resolution.js.map +1 -0
- package/dist/facade/generic-lens-capture.d.ts +6 -3
- package/dist/facade/generic-lens-capture.js +6 -3
- package/dist/facade/generic-lens-capture.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +88 -16
- package/dist/facade/simulate-capture.js +279 -153
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.d.ts +3 -2
- package/dist/facade/simulate-display-view.js +22 -12
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/chart-composite-gpu.js +1 -3
- package/dist/gpu/chart-composite-gpu.js.map +1 -1
- package/dist/gpu/chart-radiance-gpu.js +1 -5
- package/dist/gpu/chart-radiance-gpu.js.map +1 -1
- package/dist/gpu/display-view-quality-gpu.d.ts +13 -1
- package/dist/gpu/display-view-quality-gpu.js +4 -8
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/display-view-speed-gpu.js +3 -25
- package/dist/gpu/display-view-speed-gpu.js.map +1 -1
- package/dist/gpu/sensor-stage-gpu.js +1 -5
- package/dist/gpu/sensor-stage-gpu.js.map +1 -1
- package/dist/gpu/webgpu-runtime.d.ts +2 -0
- package/dist/gpu/webgpu-runtime.js +3 -0
- package/dist/gpu/webgpu-runtime.js.map +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/motion/camera-rotation.js +1 -6
- package/dist/motion/camera-rotation.js.map +1 -1
- package/dist/motion/capture-rotation-inverse-mapping.d.ts +12 -0
- package/dist/motion/capture-rotation-inverse-mapping.js +2 -2
- package/dist/motion/capture-rotation-inverse-mapping.js.map +1 -1
- package/dist/motion/capture-rotation-temporal-quadrature.d.ts +1 -0
- package/dist/motion/capture-rotation-temporal-quadrature.js +1 -1
- package/dist/motion/capture-rotation-temporal-quadrature.js.map +1 -1
- package/dist/motion/capture-rotation-trajectory.js +2 -17
- package/dist/motion/capture-rotation-trajectory.js.map +1 -1
- package/dist/motion/capture-translation-parallax-temporal-quadrature.d.ts +1 -0
- package/dist/motion/capture-translation-parallax-temporal-quadrature.js +3 -14
- package/dist/motion/capture-translation-parallax-temporal-quadrature.js.map +1 -1
- package/dist/motion/extended-object-projection.js +5 -40
- package/dist/motion/extended-object-projection.js.map +1 -1
- package/dist/optics/aperture-diameter.d.ts +47 -0
- package/dist/optics/aperture-diameter.js +50 -0
- package/dist/optics/aperture-diameter.js.map +1 -0
- package/dist/optics/atmospheric-haze.js +1 -7
- package/dist/optics/atmospheric-haze.js.map +1 -1
- package/dist/optics/brown-conrady-distortion.js +1 -7
- package/dist/optics/brown-conrady-distortion.js.map +1 -1
- package/dist/optics/complex-pupil-psf.d.ts +1 -0
- package/dist/optics/complex-pupil-psf.js +2 -84
- package/dist/optics/complex-pupil-psf.js.map +1 -1
- package/dist/optics/detail-limit.d.ts +182 -0
- package/dist/optics/detail-limit.js +336 -0
- package/dist/optics/detail-limit.js.map +1 -0
- package/dist/optics/focus-state.js +1 -11
- package/dist/optics/focus-state.js.map +1 -1
- package/dist/optics/front-of-lens-filter.d.ts +5 -0
- package/dist/optics/front-of-lens-filter.js +3 -65
- package/dist/optics/front-of-lens-filter.js.map +1 -1
- package/dist/optics/generic-lens-psf.d.ts +18 -2
- package/dist/optics/generic-lens-psf.js +25 -2
- package/dist/optics/generic-lens-psf.js.map +1 -1
- package/dist/optics/generic-nd-filter-presets.d.ts +12 -0
- package/dist/optics/generic-nd-filter-presets.js +48 -0
- package/dist/optics/generic-nd-filter-presets.js.map +1 -0
- package/dist/optics/illumination-vignetting.js +1 -6
- package/dist/optics/illumination-vignetting.js.map +1 -1
- package/dist/optics/lens-psf-profile.d.ts +4 -1
- package/dist/optics/lens-psf-profile.js +12 -111
- package/dist/optics/lens-psf-profile.js.map +1 -1
- package/dist/optics/longitudinal-chromatic-aberration.js +1 -7
- package/dist/optics/longitudinal-chromatic-aberration.js.map +1 -1
- package/dist/optics/pupil-vignetting.js +1 -7
- package/dist/optics/pupil-vignetting.js.map +1 -1
- package/dist/optics/radial-distortion.js +1 -6
- package/dist/optics/radial-distortion.js.map +1 -1
- package/dist/optics/scene-to-sensor-irradiance.d.ts +13 -0
- package/dist/optics/scene-to-sensor-irradiance.js +11 -96
- package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
- package/dist/optics/stray-light.d.ts +1 -0
- package/dist/optics/stray-light.js +1 -1
- package/dist/optics/stray-light.js.map +1 -1
- package/dist/optics/zernike-wavefront.js +1 -7
- package/dist/optics/zernike-wavefront.js.map +1 -1
- package/dist/output/capture-geometry.js +1 -8
- package/dist/output/capture-geometry.js.map +1 -1
- package/dist/output/crop-to-match-focal-length.d.ts +62 -0
- package/dist/output/crop-to-match-focal-length.js +63 -0
- package/dist/output/crop-to-match-focal-length.js.map +1 -0
- package/dist/output/developed-lens-corrections.d.ts +62 -0
- package/dist/output/developed-lens-corrections.js +135 -0
- package/dist/output/developed-lens-corrections.js.map +1 -0
- package/dist/output/generic-lens-corrections.d.ts +5 -7
- package/dist/output/generic-lens-corrections.js +11 -10
- package/dist/output/generic-lens-corrections.js.map +1 -1
- package/dist/output/geometric-transforms.js +1 -4
- package/dist/output/geometric-transforms.js.map +1 -1
- package/dist/output/lens-corrections.d.ts +12 -2
- package/dist/output/lens-corrections.js +22 -7
- package/dist/output/lens-corrections.js.map +1 -1
- package/dist/output/native-capture-sdr.d.ts +2 -2
- package/dist/output/native-capture-sdr.js +3 -2
- package/dist/output/native-capture-sdr.js.map +1 -1
- package/dist/output/print-contrast-reference.js +1 -1
- package/dist/output/print-contrast-reference.js.map +1 -1
- package/dist/output/print-size-blur-noise.d.ts +110 -0
- package/dist/output/print-size-blur-noise.js +138 -0
- package/dist/output/print-size-blur-noise.js.map +1 -0
- package/dist/processing/development-detail.d.ts +29 -0
- package/dist/processing/development-detail.js +128 -107
- package/dist/processing/development-detail.js.map +1 -1
- package/dist/schema/illumination-temporal.js +3 -72
- package/dist/schema/illumination-temporal.js.map +1 -1
- package/dist/schema/illumination.d.ts +1 -0
- package/dist/schema/illumination.js +2 -65
- package/dist/schema/illumination.js.map +1 -1
- package/dist/schema/scene-radiance-temporal.js +2 -14
- package/dist/schema/scene-radiance-temporal.js.map +1 -1
- package/dist/schema/scene-radiance.js +2 -60
- package/dist/schema/scene-radiance.js.map +1 -1
- package/dist/schema/validation.d.ts +2 -0
- package/dist/schema/validation.js +2 -17
- package/dist/schema/validation.js.map +1 -1
- package/dist/sensor/accumulated-charge.js +2 -59
- package/dist/sensor/accumulated-charge.js.map +1 -1
- package/dist/sensor/architecture.js +1 -16
- package/dist/sensor/architecture.js.map +1 -1
- package/dist/sensor/camera-saturation-capacity.d.ts +1 -0
- package/dist/sensor/camera-saturation-capacity.js +3 -55
- package/dist/sensor/camera-saturation-capacity.js.map +1 -1
- package/dist/sensor/capture-color-sampling-binding.js +1 -26
- package/dist/sensor/capture-color-sampling-binding.js.map +1 -1
- package/dist/sensor/capture-mode-timing.js +1 -39
- package/dist/sensor/capture-mode-timing.js.map +1 -1
- package/dist/sensor/capture-mode.js +1 -23
- package/dist/sensor/capture-mode.js.map +1 -1
- package/dist/sensor/color-sampling.js +1 -26
- package/dist/sensor/color-sampling.js.map +1 -1
- package/dist/sensor/constant-rate-temporal-integration.js +2 -46
- package/dist/sensor/constant-rate-temporal-integration.js.map +1 -1
- package/dist/sensor/dark-current.js +2 -45
- package/dist/sensor/dark-current.js.map +1 -1
- package/dist/sensor/eqe-electron-rate.js +2 -41
- package/dist/sensor/eqe-electron-rate.js.map +1 -1
- package/dist/sensor/exposure-window.d.ts +7 -0
- package/dist/sensor/exposure-window.js +9 -24
- package/dist/sensor/exposure-window.js.map +1 -1
- package/dist/sensor/iso-gain.js +1 -7
- package/dist/sensor/iso-gain.js.map +1 -1
- package/dist/sensor/iso-signal-chain.js +1 -43
- package/dist/sensor/iso-signal-chain.js.map +1 -1
- package/dist/sensor/optical-stack.js +1 -25
- package/dist/sensor/optical-stack.js.map +1 -1
- package/dist/sensor/photon-energy.js +1 -26
- package/dist/sensor/photon-energy.js.map +1 -1
- package/dist/sensor/physical-charge-capacity.js +3 -77
- package/dist/sensor/physical-charge-capacity.js.map +1 -1
- package/dist/sensor/radiometry-readiness.js +2 -23
- package/dist/sensor/radiometry-readiness.js.map +1 -1
- package/dist/sensor/raw-readout.d.ts +3 -0
- package/dist/sensor/raw-readout.js +4 -66
- package/dist/sensor/raw-readout.js.map +1 -1
- package/dist/sensor/raw-reconstruction.js +2 -64
- package/dist/sensor/raw-reconstruction.js.map +1 -1
- package/dist/sensor/readout-exposure-linkage.js +1 -9
- package/dist/sensor/readout-exposure-linkage.js.map +1 -1
- package/dist/sensor/readout-timing.js +4 -79
- package/dist/sensor/readout-timing.js.map +1 -1
- package/dist/sensor/response-application-compatibility.d.ts +2 -0
- package/dist/sensor/response-application-compatibility.js +4 -35
- package/dist/sensor/response-application-compatibility.js.map +1 -1
- package/dist/sensor/response-operating-range.d.ts +1 -0
- package/dist/sensor/response-operating-range.js +3 -79
- package/dist/sensor/response-operating-range.js.map +1 -1
- package/dist/sensor/responsivity-photocurrent.js +4 -114
- package/dist/sensor/responsivity-photocurrent.js.map +1 -1
- package/dist/sensor/sampling-aperture.js +1 -25
- package/dist/sensor/sampling-aperture.js.map +1 -1
- package/dist/sensor/sensor-characteristics.d.ts +137 -0
- package/dist/sensor/sensor-characteristics.js +115 -0
- package/dist/sensor/sensor-characteristics.js.map +1 -0
- package/dist/sensor/spatial-sample-reduction.js +3 -24
- package/dist/sensor/spatial-sample-reduction.js.map +1 -1
- package/dist/sensor/spatial-sampling-quadrature.js +1 -3
- package/dist/sensor/spatial-sampling-quadrature.js.map +1 -1
- package/dist/sensor/spatio-spectral-reduction.js +1 -18
- package/dist/sensor/spatio-spectral-reduction.js.map +1 -1
- package/dist/sensor/spectral-coverage.d.ts +10 -1
- package/dist/sensor/spectral-coverage.js +1 -1
- package/dist/sensor/spectral-coverage.js.map +1 -1
- package/dist/sensor/spectral-quadrature.js +3 -41
- package/dist/sensor/spectral-quadrature.js.map +1 -1
- package/dist/sensor/spectral-response.js +1 -47
- package/dist/sensor/spectral-response.js.map +1 -1
- package/dist/sensor/temporal-photo-signal.d.ts +3 -0
- package/dist/sensor/temporal-photo-signal.js +5 -2
- package/dist/sensor/temporal-photo-signal.js.map +1 -1
- package/dist/stabilization/camera-shake.js +1 -6
- package/dist/stabilization/camera-shake.js.map +1 -1
- package/dist/stabilization/system.d.ts +9 -0
- package/dist/stabilization/system.js +7 -86
- package/dist/stabilization/system.js.map +1 -1
- package/docs/AGENT_GUIDE.md +5 -0
- package/docs/API_REFERENCE.md +116 -3
- package/docs/QUICK_START.md +178 -14
- package/docs/USAGE.md +111 -3
- package/docs/api/capture-native-raw-dng.md +6 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/equipment-generic-lens-catalogue.md +7 -2
- package/docs/api/exports.json +203 -3
- package/docs/api/exposure-filtered-exposure-time.md +105 -0
- package/docs/api/facade-develop-joined-parts.md +32 -0
- package/docs/api/facade-display-view-gpu.md +5 -2
- package/docs/api/facade-fast-methods.md +91 -0
- package/docs/api/facade-simulate-capture.md +72 -16
- package/docs/api/facade-simulate-display-view.md +3 -2
- package/docs/api/optics-aperture-diameter.md +51 -0
- package/docs/api/optics-detail-limit.md +203 -0
- package/docs/api/optics-generic-nd-filter-presets.md +34 -0
- package/docs/api/optics-lens-psf-profile.md +3 -1
- package/docs/api/output-crop-to-match-focal-length.md +67 -0
- package/docs/api/output-developed-lens-corrections.md +12 -0
- package/docs/api/output-generic-lens-corrections.md +3 -10
- package/docs/api/output-lens-corrections.md +11 -1
- package/docs/api/output-print-size-blur-noise.md +115 -0
- package/docs/api/sensor-sensor-characteristics.md +105 -0
- package/llms.txt +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# optics/detail-limit.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
|
+
## calculateDetailLimit
|
|
6
|
+
|
|
7
|
+
Which limits detail for given settings: the lens (diffraction at this f-number, aberrations, defocus) or the sensor's pixel
|
|
8
|
+
pitch. Returns the optics' resolution (MTF50 and the diffraction cut-off, cycles/mm), the sampling's (Nyquist frequency
|
|
9
|
+
and, with a Bayer filter, the red and blue sites'), the system MTF50 (optics × pixel aperture), the f-number from which
|
|
10
|
+
diffraction limits on this sensor, and which one limits.
|
|
11
|
+
|
|
12
|
+
The sensor limits when the optics still pass 50 % contrast at the Nyquist frequency 1/(2p); otherwise the lens does,
|
|
13
|
+
through diffraction when its wavefront error is within the Maréchal criterion (RMS ≤ λ/14), else through aberrations or
|
|
14
|
+
defocus. The optics MTF is the exact scalar OTF of the stated wavefront at one wavelength; the decision rule and the
|
|
15
|
+
catalogue lenses' wavefronts are approximations.
|
|
16
|
+
|
|
17
|
+
Example: an ideal 50 mm lens at f/22 on a 36 × 24 mm, 6000-pixel-wide sensor (6 µm) is limited by diffraction (optics
|
|
18
|
+
MTF50 ≈ 33 cycles/mm against a Nyquist frequency of ≈ 83); at f/4 the pixel pitch limits.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
export function calculateDetailLimit(input: CalculateDetailLimitInput): CalculationResult<DetailLimit>;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This call is synchronous; any supplied provider must follow its explicit synchronous contract.
|
|
25
|
+
|
|
26
|
+
## CalculateDetailLimitInput
|
|
27
|
+
|
|
28
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
export interface CalculateDetailLimitInput {
|
|
32
|
+
/** Active imaging area and horizontal pixel count; pitch = widthMm / pixelWidth. */
|
|
33
|
+
sensor: {
|
|
34
|
+
widthMm: number;
|
|
35
|
+
heightMm: number;
|
|
36
|
+
pixelWidth: number;
|
|
37
|
+
/** Default `"bayer"`. `"none"`: every site records full detail (a monochrome sensor). */
|
|
38
|
+
colorFilterArray?: "bayer" | "none";
|
|
39
|
+
};
|
|
40
|
+
lens: DetailLimitIdealLens | DetailLimitCatalogueLens;
|
|
41
|
+
/** f-number set on the lens. */
|
|
42
|
+
fNumber: number;
|
|
43
|
+
/**
|
|
44
|
+
* Focus and subject distances from the lens in meters. Omitted: focused at infinity, subject in focus. `subjectDistanceM`
|
|
45
|
+
* omitted: the subject is at the focus distance.
|
|
46
|
+
*/
|
|
47
|
+
focus?: { focusDistanceM: number; subjectDistanceM?: number };
|
|
48
|
+
/** Field height as a fraction of the half-diagonal, 0 (center, default) to 1 (corner). Only aberrations depend on it. */
|
|
49
|
+
fieldPosition?: number;
|
|
50
|
+
/** Wavelength in nanometres, 380–780. Default 550. */
|
|
51
|
+
wavelengthNm?: number;
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## DETAIL_LIMIT_LIMITS
|
|
56
|
+
|
|
57
|
+
Limits of `calculateDetailLimit`: the wavelength range and the largest wavefront (sum of the magnitudes of its terms, in
|
|
58
|
+
waves) whose transfer function is integrated. Beyond it the blur is geometric and the quadrature would grow without
|
|
59
|
+
bound; use `calculateDefocusCircle` for such a blur.
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
DETAIL_LIMIT_LIMITS = Object.freeze({ minimumWavelengthNm: 380, maximumWavelengthNm: 780, maximumWavefrontWaves: 64 })
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## DetailLimit
|
|
66
|
+
|
|
67
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
export interface DetailLimit {
|
|
71
|
+
/** The lens or the sensor. */
|
|
72
|
+
limitedBy: DetailLimitedBy;
|
|
73
|
+
/** Diffraction, aberrations or defocus (the lens), or the pixel pitch (the sensor). */
|
|
74
|
+
limitingFactor: DetailLimitingFactor;
|
|
75
|
+
/** Optics MTF50 / Nyquist frequency: at least 1 when the sensor limits. */
|
|
76
|
+
opticsToNyquistRatio: number;
|
|
77
|
+
fNumber: number;
|
|
78
|
+
wavelengthNm: number;
|
|
79
|
+
/** Physical focal length used (the catalogue lens's on this sensor), mm. */
|
|
80
|
+
focalLengthMm: number;
|
|
81
|
+
fieldPosition: number;
|
|
82
|
+
optics: DetailLimitOptics;
|
|
83
|
+
sampling: DetailLimitSampling;
|
|
84
|
+
system: DetailLimitSystem;
|
|
85
|
+
/**
|
|
86
|
+
* The f-number at which an aberration-free lens in focus has its MTF50 at the Nyquist frequency (2·p·0.40397/λ at
|
|
87
|
+
* infinity focus): from it on, diffraction limits detail on this sensor whatever the lens.
|
|
88
|
+
*/
|
|
89
|
+
diffractionLimitedFNumber: number;
|
|
90
|
+
basis: Readonly<Record<"optics" | "sampling" | "system" | "limitedBy", DetailLimitBasis>>;
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## DetailLimitBasis
|
|
95
|
+
|
|
96
|
+
Whether a quantity is exact under its stated conditions or an approximation.
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
export interface DetailLimitBasis { kind: "exact" | "approximation"; statement: string }
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## DetailLimitCatalogueLens
|
|
103
|
+
|
|
104
|
+
A generic catalogue lens (`GENERIC_LENS_IDS`), resolved for the sensor as `resolveGenericLens` does.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
export interface DetailLimitCatalogueLens {
|
|
108
|
+
kind: "catalogue";
|
|
109
|
+
lensId: string;
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## DetailLimitedBy
|
|
114
|
+
|
|
115
|
+
Which component limits detail.
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
export type DetailLimitedBy = "lens" | "sensor";
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## DetailLimitIdealLens
|
|
122
|
+
|
|
123
|
+
A lens with no aberrations (diffraction only).
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
export interface DetailLimitIdealLens {
|
|
127
|
+
kind: "ideal";
|
|
128
|
+
/** Physical focal length in millimeters (for the working f-number and the defocus of a subject off the focus distance). */
|
|
129
|
+
focalLengthMm: number;
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## DetailLimitingFactor
|
|
134
|
+
|
|
135
|
+
What limits it: the lens's diffraction, aberrations or defocus, or the sensor's pixel pitch.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
export type DetailLimitingFactor = "diffraction" | "aberrations" | "defocus" | "pixel-pitch";
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## DetailLimitOptics
|
|
142
|
+
|
|
143
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
export interface DetailLimitOptics {
|
|
147
|
+
/** Working f-number N_w = N·v/f (thin-lens image distance v; N at infinity focus). */
|
|
148
|
+
workingFNumber: number;
|
|
149
|
+
/** Diffraction cut-off 1/(λ·N_w) in cycles/mm: no optics passes detail beyond it. */
|
|
150
|
+
cutoffCyclesPerMm: number;
|
|
151
|
+
/** Frequency where the optics' MTF falls to 0.5, the lower of the two directions, cycles/mm. */
|
|
152
|
+
mtf50CyclesPerMm: number;
|
|
153
|
+
/** MTF50 for detail along the radius from the image center (tangential), cycles/mm. */
|
|
154
|
+
tangentialMtf50CyclesPerMm: number;
|
|
155
|
+
/** MTF50 for detail across the radius (sagittal), cycles/mm. Equal to the tangential value at the center. */
|
|
156
|
+
sagittalMtf50CyclesPerMm: number;
|
|
157
|
+
/** MTF50 of an aberration-free lens in focus at the same working f-number (0.40397·cut-off), cycles/mm. */
|
|
158
|
+
diffractionMtf50CyclesPerMm: number;
|
|
159
|
+
/** Optics MTF at the sensor's Nyquist frequency (lower direction). */
|
|
160
|
+
mtfAtNyquist: number;
|
|
161
|
+
/** RMS wavefront error in waves at the wavelength: of the aberrations, of the defocus, and of both together. */
|
|
162
|
+
rmsWavefrontErrorWaves: { aberrations: number; defocus: number; total: number };
|
|
163
|
+
/** Whether the total RMS wavefront error is within the Maréchal criterion λ/14. */
|
|
164
|
+
withinMarechalCriterion: boolean;
|
|
165
|
+
/** Geometric blur-circle diameter of the defocus on the sensor, mm (0 in focus). */
|
|
166
|
+
defocusBlurDiameterMm: number;
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## DetailLimitSampling
|
|
171
|
+
|
|
172
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
export interface DetailLimitSampling {
|
|
176
|
+
colorFilterArray: "bayer" | "none";
|
|
177
|
+
/** Pixel pitch in micrometres: widthMm / pixelWidth. */
|
|
178
|
+
pixelPitchMicrometers: number;
|
|
179
|
+
/** 1/(2p), cycles/mm: the finest detail the pixel grid records without aliasing (Bayer green sites too, along rows and columns). */
|
|
180
|
+
nyquistCyclesPerMm: number;
|
|
181
|
+
/** Bayer only: 1/(4p), the Nyquist frequency of the red and blue sites. Null without a color filter array. */
|
|
182
|
+
redBlueNyquistCyclesPerMm: number | null;
|
|
183
|
+
/** Pixel aperture MTF at the Nyquist frequency: sinc(1/2) = 2/π. */
|
|
184
|
+
pixelApertureMtfAtNyquist: number;
|
|
185
|
+
/** Frequency where the pixel aperture MTF falls to 0.5 (0.60335/p), cycles/mm: beyond the Nyquist frequency. */
|
|
186
|
+
pixelApertureMtf50CyclesPerMm: number;
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## DetailLimitSystem
|
|
191
|
+
|
|
192
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
export interface DetailLimitSystem {
|
|
196
|
+
/** MTF50 of optics × pixel aperture (lower direction), cycles/mm. Detail above the Nyquist frequency aliases. */
|
|
197
|
+
mtf50CyclesPerMm: number;
|
|
198
|
+
/** The same in line pairs per picture height (cycles/mm × heightMm). */
|
|
199
|
+
mtf50LinePairsPerPictureHeight: number;
|
|
200
|
+
/** System MTF at the Nyquist frequency (lower direction). */
|
|
201
|
+
mtfAtNyquist: number;
|
|
202
|
+
}
|
|
203
|
+
```
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# optics/generic-nd-filter-presets.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_ND_FILTER_PROFILES
|
|
6
|
+
|
|
7
|
+
Frozen generic ND filter profiles keyed by stops: `GENERIC_ND_FILTER_PROFILES[10]` transmits
|
|
8
|
+
exactly 1/1024. Spectrally flat over 380–780 nm (air), idealized (`scientificStatus:
|
|
9
|
+
"approximation"`), unbranded. 1/125 s with the 10-stop preset gives 1024/125 ≈ 8.19 s in
|
|
10
|
+
`calculateFilteredExposureTime`.
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
GENERIC_ND_FILTER_PROFILES: Readonly<Record<GenericNdFilterStops, FrontOfLensFilterProfile>> = freezeOwnedData({
|
|
14
|
+
3: idealNdProfile(3),
|
|
15
|
+
6: idealNdProfile(6),
|
|
16
|
+
10: idealNdProfile(10)
|
|
17
|
+
})
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## GENERIC_ND_FILTER_STOPS
|
|
21
|
+
|
|
22
|
+
Attenuations, in stops, of the generic ND presets.
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
GENERIC_ND_FILTER_STOPS = Object.freeze([3, 6, 10] as const)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## GenericNdFilterStops
|
|
29
|
+
|
|
30
|
+
One of the generic ND preset attenuations: 3, 6 or 10 stops.
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
export type GenericNdFilterStops = (typeof GENERIC_ND_FILTER_STOPS)[number];
|
|
34
|
+
```
|
|
@@ -200,7 +200,9 @@ Kernel interpolation between regular-grid nodes.
|
|
|
200
200
|
- `rotationally-symmetric-radial-field-centroid-aligned`: for a rotationally symmetric lens. Nodes
|
|
201
201
|
are declared only along the +X field axis (fieldYmm = [0], fieldXmm >= 0). A query at field
|
|
202
202
|
(x, y) is interpolated, with centroid alignment, at radius hypot(x, y). The kernel is then
|
|
203
|
-
rotated by atan2(y, x) about its centre
|
|
203
|
+
rotated by atan2(y, x) about its centre, with the kernel row index increasing with image +Y (the
|
|
204
|
+
convention the environment PSF quadrature applies), so an asymmetric kernel points the same way
|
|
205
|
+
as in the 2-D grid modes. The sample pitch must be square.
|
|
204
206
|
Shifts and rotations use bilinear resampling. This smooths the kernel slightly (by at most about
|
|
205
207
|
one sample). Samples outside the kernel are zero, and the result is renormalised to unit energy.
|
|
206
208
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# output/crop-to-match-focal-length.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
|
+
## calculateCropToMatchFocalLength
|
|
6
|
+
|
|
7
|
+
The centred crop that makes an image from a shorter lens match a longer lens's framing from the
|
|
8
|
+
same position: crop factor target ÷ focal length, and the pixels and megapixels left.
|
|
9
|
+
|
|
10
|
+
Exact for ideal rectilinear lenses focused at infinity, to whole pixels (the crop is
|
|
11
|
+
`calculateCenteredCrop`'s, which keeps the aspect ratio). At close focus the matching factor is
|
|
12
|
+
larger, F·(d − f)/(f·(d − F)) at object distance d (thin lens); that, distortion and focus
|
|
13
|
+
breathing are not modelled. The crop has the longer lens's framing and perspective but fewer
|
|
14
|
+
pixels: it records no more detail than the cropped part of the shorter lens's image.
|
|
15
|
+
|
|
16
|
+
Example: a 24 MP (6000 × 4000) image at 50 mm cropped to match 100 mm: factor 2, 3000 × 2000,
|
|
17
|
+
6 MP.
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
export function calculateCropToMatchFocalLength(
|
|
21
|
+
input: CalculateCropToMatchFocalLengthInput
|
|
22
|
+
): CalculationResult<CropToMatchFocalLength>;
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
This call is synchronous; any supplied provider must follow its explicit synchronous contract.
|
|
26
|
+
|
|
27
|
+
## CalculateCropToMatchFocalLengthInput
|
|
28
|
+
|
|
29
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
export interface CalculateCropToMatchFocalLengthInput {
|
|
33
|
+
/** Focal length the image was taken with, in millimetres. */
|
|
34
|
+
focalLengthMm: number;
|
|
35
|
+
/** Longer focal length whose framing the crop should match, in millimetres. At least `focalLengthMm`. */
|
|
36
|
+
targetFocalLengthMm: number;
|
|
37
|
+
/** Image width in pixels (for example the sensor's output width). */
|
|
38
|
+
pixelWidth: number;
|
|
39
|
+
/** Image height in pixels. */
|
|
40
|
+
pixelHeight: number;
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## CropToMatchFocalLength
|
|
45
|
+
|
|
46
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
export interface CropToMatchFocalLength {
|
|
50
|
+
/** Focal length supplied by the caller, in millimetres. */
|
|
51
|
+
focalLengthMm: number;
|
|
52
|
+
/** Target focal length supplied by the caller, in millimetres. */
|
|
53
|
+
targetFocalLengthMm: number;
|
|
54
|
+
/** Linear crop factor targetFocalLengthMm / focalLengthMm (1 means no crop). */
|
|
55
|
+
cropFactor: number;
|
|
56
|
+
/** Width left after the centred crop, in pixels (as `calculateCenteredCrop`). */
|
|
57
|
+
pixelWidth: number;
|
|
58
|
+
/** Height left after the centred crop, in pixels. */
|
|
59
|
+
pixelHeight: number;
|
|
60
|
+
/** Pixels left, in millions. */
|
|
61
|
+
megapixels: number;
|
|
62
|
+
/** Megapixels of the uncropped image. */
|
|
63
|
+
sourceMegapixels: number;
|
|
64
|
+
/** Fraction of the original pixel area retained, about 1 / cropFactor². */
|
|
65
|
+
retainedAreaFraction: number;
|
|
66
|
+
}
|
|
67
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# output/developed-lens-corrections.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_CORRECTION_CHANNEL_WAVELENGTHS_NM
|
|
6
|
+
|
|
7
|
+
Effective wavelengths of the red, green and blue channels for the lateral-colour map, nm: a generic approximation of where
|
|
8
|
+
a camera's channel responses peak, not a measured camera.
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM: Readonly<Record<LensCorrectionChannel, number>> = Object.freeze({ red: 610, green: 540, blue: 465 })
|
|
12
|
+
```
|
|
@@ -6,7 +6,9 @@ The package and root API versions are recorded once in [Navigation](../API_REFER
|
|
|
6
6
|
|
|
7
7
|
Builds the catalogue lens's generic correction profile for one camera state: a radial distortion map (the lens's own
|
|
8
8
|
r_d = r(1 + k1ρ²), read at each ideal destination point), per-channel lateral-colour scales 1 + L·D(λ_channel), and the
|
|
9
|
-
relative-illumination gain at the state's aperture (strength 1).
|
|
9
|
+
relative-illumination gain at the state's aperture (strength 1). From profile version 1.1.0 (#584) the vignetting component
|
|
10
|
+
(version 1.1.0) carries the distortion's area factor (`distortionAreaFactor`): its gain is det J(ρ_i)/E(ρ_s) at the capture's
|
|
11
|
+
sensor point, so it flattens a flat-field capture. The result passes `parseGenericLensCorrectionProfile`.
|
|
10
12
|
|
|
11
13
|
```ts
|
|
12
14
|
export function createGenericLensCorrectionProfile(input: CreateGenericLensCorrectionProfileInput): GenericLensCorrectionProfile;
|
|
@@ -28,12 +30,3 @@ export interface CreateGenericLensCorrectionProfileInput {
|
|
|
28
30
|
state: OpticalProfileState;
|
|
29
31
|
}
|
|
30
32
|
```
|
|
31
|
-
|
|
32
|
-
## GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM
|
|
33
|
-
|
|
34
|
-
Effective wavelengths of the red, green and blue channels for the lateral-colour map, nm: a generic approximation of where
|
|
35
|
-
a camera's channel responses peak, not a measured camera.
|
|
36
|
-
|
|
37
|
-
```ts
|
|
38
|
-
GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM: Readonly<Record<LensCorrectionChannel, number>> = Object.freeze({ red: 610, green: 540, blue: 465 })
|
|
39
|
-
```
|
|
@@ -31,7 +31,9 @@ This call is synchronous; any supplied provider must follow its explicit synchro
|
|
|
31
31
|
## calculatePeripheralIlluminationCorrection
|
|
32
32
|
|
|
33
33
|
Applies a gain after capture and reports the variance before clipping. It does not rewrite
|
|
34
|
-
the photon or SNR (signal-to-noise ratio) history of the capture.
|
|
34
|
+
the photon or SNR (signal-to-noise ratio) history of the capture. The gain is T^(−strength), T the relative illumination at
|
|
35
|
+
`imagePointMm`: E(ρ) of the component's profile, divided by det J(ρ_i) when the component has a `distortionAreaFactor` (#584;
|
|
36
|
+
then `imagePointMm` is the capture's sensor point, where the capture applied both).
|
|
35
37
|
|
|
36
38
|
```ts
|
|
37
39
|
export function calculatePeripheralIlluminationCorrection(input: {
|
|
@@ -77,6 +79,14 @@ export interface IlluminationLensCorrection extends CorrectionComponentIdentity
|
|
|
77
79
|
kind: "peripheral-illumination";
|
|
78
80
|
profile: IlluminationVignettingProfile;
|
|
79
81
|
strength: number;
|
|
82
|
+
/**
|
|
83
|
+
* Optional (#584): the capture's radial distortion r_d = r(1 + k1ρ² + k2ρ⁴ + k3ρ⁶), whose area factor 1/det J the capture also
|
|
84
|
+
* multiplied into the irradiance (det J = (1 + k1ρ_i² + k2ρ_i⁴ + k3ρ_i⁶)(1 + 3k1ρ_i² + 5k2ρ_i⁴ + 7k3ρ_i⁶) at the ideal point ρ_i that
|
|
85
|
+
* the lens images to the sensor point). With it, the relative illumination corrected is E(ρ_s)/det J(ρ_i), evaluated at the
|
|
86
|
+
* capture's sensor (source) point, so strength 1 flattens a flat field. It holds whether or not the distortion itself is corrected.
|
|
87
|
+
* Without it the gain is 1/E alone, as before.
|
|
88
|
+
*/
|
|
89
|
+
distortionAreaFactor?: RadialDistortionProfile;
|
|
80
90
|
}
|
|
81
91
|
```
|
|
82
92
|
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# output/print-size-blur-noise.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
|
+
## calculatePrintSizeLimitWithBlurAndNoise
|
|
6
|
+
|
|
7
|
+
Calculates the largest print, at a fixed viewing distance, at which the capture still looks sharp and clean, and names
|
|
8
|
+
the factor that limits it: pixels, blur or noise. An approximation throughout; the equations are exact under its model.
|
|
9
|
+
|
|
10
|
+
With θ the criterion angle, d the viewing distance, s = 2 d tan(θ/2) the length θ covers on the print, N_w the raster
|
|
11
|
+
width in pixels, b the blur diameter in pixels and SNR_px the per-pixel SNR (print width W, height in proportion):
|
|
12
|
+
- pixels: a print pixel (W / N_w) may cover at most s, so W ≤ N_w·s (as `calculatePrintPlan` with
|
|
13
|
+
`angular-pixel-pitch`, before its whole-pixel rounding);
|
|
14
|
+
- blur: the blur on the print (b·W / N_w) may cover at most s, so W ≤ N_w·s / b: the criterion of an acceptable circle
|
|
15
|
+
of confusion with the eye's resolution as the limit;
|
|
16
|
+
- noise: the eye averages the (W / N_w) wide pixels inside s, so the visible SNR is SNR_px·(N_w·s / W) while a pixel is
|
|
17
|
+
smaller than s (the mean of n independent values has 1/√n of their standard deviation); W ≤ N_w·s·SNR_px / SNR_min,
|
|
18
|
+
and noise never limits when SNR_px ≥ SNR_min.
|
|
19
|
+
One arcminute is normal (20/20) acuity under optimal conditions (Z. Alomar, K. D. Bojikian, "Visual Acuity Testing in
|
|
20
|
+
Adults", EyeWiki, American Academy of Ophthalmology, https://eyewiki.aao.org/Visual_Acuity_Testing_in_Adults, last edited
|
|
21
|
+
June 16, 2026); the SNR threshold has no default and is the caller's. Blur parts add in root-sum-square, which is exact
|
|
22
|
+
for Gaussian spots (variances of independent displacements add: C. M. Grinstead, J. L. Snell, "Introduction to
|
|
23
|
+
Probability", 2nd rev. ed., AMS 1997, sec. 6.2, free under the GNU FDL) and an approximation for discs and Airy patterns.
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
export function calculatePrintSizeLimitWithBlurAndNoise(value: PrintSizeLimitWithBlurAndNoiseInput): CalculationResult<PrintSizeLimitWithBlurAndNoise>;
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
This call is synchronous; any supplied provider must follow its explicit synchronous contract.
|
|
30
|
+
|
|
31
|
+
## PrintBlurInput
|
|
32
|
+
|
|
33
|
+
The capture's blur at the sensor plane, as blur-spot diameters in millimetres.
|
|
34
|
+
`components` combines the parts by root-sum-square (an approximation, see the docs); `measured-total` is one measured diameter.
|
|
35
|
+
A zero component is allowed and means that part is left out. Engine helpers give the parts: `calculateDefocusCircle` (defocus),
|
|
36
|
+
`calculateAiryDisk` (diffraction, micrometres), `calculateProjectedMotionBlur` (motion length).
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
export type PrintBlurInput =
|
|
40
|
+
| { kind: "components"; lensMm?: number; defocusMm?: number; diffractionMm?: number; motionMm?: number }
|
|
41
|
+
| { kind: "measured-total"; diameterMm: number };
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## PrintImageMm
|
|
45
|
+
|
|
46
|
+
A print size in millimetres at the raster's aspect ratio.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
export interface PrintImageMm { width: number; height: number }
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## PrintNoiseInput
|
|
53
|
+
|
|
54
|
+
The noise of one pixel of the printed raster, for the tone the caller cares about (for example mid-gray).
|
|
55
|
+
`shot-and-read-noise` uses the engine's noise model (`calculateSignalToNoise`: Poisson shot noise plus RMS read noise);
|
|
56
|
+
`pixel-snr` is a per-pixel signal-to-noise ratio the caller already has. `capture-noise-figure` (2.20.0) takes a capture's own
|
|
57
|
+
mid-gray noise figure (#535: `result.sensor.midGrey` of a `simulateCapture` with `fast: false`), whose SNR S/σ_total counts photon
|
|
58
|
+
shot, read, dark-current, quantization and any fixed-pattern noise; it is one sensor site's, so it applies as the pixel SNR when one
|
|
59
|
+
printed pixel is one capture pixel. A clipped figure (the patch at white) is refused.
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
export type PrintNoiseInput =
|
|
63
|
+
| { kind: "shot-and-read-noise"; signalElectrons: number; readNoiseElectrons: number }
|
|
64
|
+
| { kind: "pixel-snr"; snrLinear: number }
|
|
65
|
+
| { kind: "capture-noise-figure"; midGrey: NonNullable<SimulateCaptureSensorReport["midGrey"]> };
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## PrintSharpCleanCriterion
|
|
69
|
+
|
|
70
|
+
What "looks sharp and clean" means: the smallest angle the viewer resolves, and the lowest signal-to-noise ratio the viewer
|
|
71
|
+
accepts after the eye averages over that angle. Both are the caller's choice; one arcminute is normal (20/20) acuity.
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
export interface PrintSharpCleanCriterion {
|
|
75
|
+
kind: "visual-resolution";
|
|
76
|
+
arcminutes: number;
|
|
77
|
+
minimumVisibleSnr: number;
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## PrintSizeLimitWithBlurAndNoise
|
|
82
|
+
|
|
83
|
+
Result. Each factor's own limit is the largest print it allows on its own; null means that factor never limits.
|
|
84
|
+
`limitingFactor` names the smallest; on a tie the order is pixels, blur, noise.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
export interface PrintSizeLimitWithBlurAndNoise {
|
|
88
|
+
engineApiVersion: string;
|
|
89
|
+
status: "finite" | "no-positive-size";
|
|
90
|
+
limitingFactor: "pixels" | "blur" | "noise";
|
|
91
|
+
maximumImageMm: PrintImageMm;
|
|
92
|
+
limits: { pixels: PrintImageMm; blur: PrintImageMm | null; noise: PrintImageMm | null };
|
|
93
|
+
viewingDistanceMm: number;
|
|
94
|
+
/** The criterion angle as a length on the print at the viewing distance: 2 d tan(θ/2). */
|
|
95
|
+
resolvedSpotMm: number;
|
|
96
|
+
blur: { diameterMm: number; diameterPixels: number; largestComponent: "lens" | "defocus" | "diffraction" | "motion" | "measured-total" | null };
|
|
97
|
+
noise: { pixelSnr: number; visibleSnrAtMaximum: number };
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## PrintSizeLimitWithBlurAndNoiseInput
|
|
102
|
+
|
|
103
|
+
Input. The raster is the retained image that is printed (after any crop), with square pixels; `pixelPitchMm` is the
|
|
104
|
+
sensor length one of those pixels covers. The print keeps the raster's aspect ratio. The viewing distance is fixed.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
export interface PrintSizeLimitWithBlurAndNoiseInput {
|
|
108
|
+
raster: RasterDimensions;
|
|
109
|
+
pixelPitchMm: number;
|
|
110
|
+
viewingDistance: PrintLength;
|
|
111
|
+
criterion: PrintSharpCleanCriterion;
|
|
112
|
+
blur: PrintBlurInput;
|
|
113
|
+
noise: PrintNoiseInput;
|
|
114
|
+
}
|
|
115
|
+
```
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# sensor/sensor-characteristics.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
|
+
## SimulateCaptureNoiseComponents
|
|
6
|
+
|
|
7
|
+
One RMS noise component of the mid-grey figure, e⁻ (input-referred).
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
export interface SimulateCaptureNoiseComponents {
|
|
11
|
+
/** Photon shot noise √S (Poisson). */
|
|
12
|
+
photonShot: number;
|
|
13
|
+
/** Read noise of the capture's readout regime. */
|
|
14
|
+
read: number;
|
|
15
|
+
/** Dark-current shot noise √(D·t), D = 1 e⁻/s at 20 °C. */
|
|
16
|
+
darkCurrent: number;
|
|
17
|
+
/** ADC quantization, one code (and one more after a digital gain) as a uniform 1/12 code² variance, in e⁻. */
|
|
18
|
+
quantization: number;
|
|
19
|
+
/** With `sensor.fixedPatternNoise`: √((PRNU·S)² + (DSNU·D·t)² + row² + column²), hot pixels excluded; a spatial, not temporal, noise. */
|
|
20
|
+
fixedPattern?: number;
|
|
21
|
+
/** √ of the sum of the squares. */
|
|
22
|
+
total: number;
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## SimulateCaptureSensorCharacteristics
|
|
27
|
+
|
|
28
|
+
#555: sensor characteristics a capture can set one at a time (`simulateCapture({ sensor })`). Each is off by default: an
|
|
29
|
+
omitted field keeps the camera body's value, and a request with none of them is byte-identical to one before 2.20.0.
|
|
30
|
+
Every value set is listed in the result's `assumptions`.
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
export interface SimulateCaptureSensorCharacteristics {
|
|
34
|
+
/**
|
|
35
|
+
* Peak external quantum efficiency of the green channel (its value at 555 nm), 0.05–0.9. Default 0.5, the facade's
|
|
36
|
+
* idealized sensor. Every channel's EQE curve is multiplied by `peakQuantumEfficiency / 0.5`, so the curves keep their
|
|
37
|
+
* shape (and the colour matrix is unchanged) and the collected charge scales in proportion.
|
|
38
|
+
*/
|
|
39
|
+
peakQuantumEfficiency?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Full-well capacity: the charge, e⁻, that reaches the RAW white level at base ISO. Default: the body's base-ISO saturation
|
|
42
|
+
* at 6 µm, scaled by the pixel area (the `sensor.isoAreaNormalization` approximation). `link` `"pixel-area"` (default)
|
|
43
|
+
* scales `electrons`, the full well at `referencePixelPitchMicrometers` (default 6), by (pitch / reference)²; `"none"`
|
|
44
|
+
* uses `electrons` at any pitch. The result is 100 e⁻ to 1e10 e⁻.
|
|
45
|
+
*/
|
|
46
|
+
fullWell?: { electrons: number; link?: "pixel-area" | "none"; referencePixelPitchMicrometers?: number };
|
|
47
|
+
/**
|
|
48
|
+
* Read noise, RMS e⁻ (0–100), of the low and the high conversion gain, and the dual-gain switch: the ISO setting from
|
|
49
|
+
* which the high conversion gain is used (any ISO from 100; above the body's highest ISO, the low gain is used throughout).
|
|
50
|
+
* An omitted field keeps the body's value. Needs readout regimes that declare their exposure index (the tiers do).
|
|
51
|
+
*/
|
|
52
|
+
readNoise?: { lowGainElectrons?: number; highGainElectrons?: number; highGainFromIso?: number };
|
|
53
|
+
/** Readout time, s (1e-6 to 1): the rolling shutter's top-to-bottom skew. Default: the body's (tiers 8, 15 and 30 ms). */
|
|
54
|
+
readoutSeconds?: number;
|
|
55
|
+
/**
|
|
56
|
+
* ADC bit depth: 12, 14 or 16. Default: the body's (consumer 12, the others 14). The black level scales with the code range
|
|
57
|
+
* (×2^Δbits), and the conversion gain follows so the same charge reaches the same fraction of the range.
|
|
58
|
+
*/
|
|
59
|
+
adcBitDepth?: 12 | 14 | 16;
|
|
60
|
+
/**
|
|
61
|
+
* Opt-in (advanced): the body's synthetic fixed-pattern noise profile (PRNU, DSNU, hot pixels, row and column offsets; EMVA
|
|
62
|
+
* 1288 Release 4.0 non-uniformities), the same pattern in every capture. Default false. Not in `simulateDisplayView`.
|
|
63
|
+
*/
|
|
64
|
+
fixedPatternNoise?: boolean;
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## SimulateCaptureSensorReport
|
|
69
|
+
|
|
70
|
+
#555, #535: the capture's sensor characteristics as used, and the figures calculated from them (reported, not settable).
|
|
71
|
+
Model figures from the engine's own noise model, not measurements.
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
export interface SimulateCaptureSensorReport {
|
|
75
|
+
pixelPitchMicrometers: number;
|
|
76
|
+
/** The green channel's peak EQE (at 555 nm). */
|
|
77
|
+
peakQuantumEfficiency: number;
|
|
78
|
+
/** Full-well capacity, e⁻: the base-ISO saturation charge (`sensor.capacityElectrons`). */
|
|
79
|
+
fullWellElectrons: number;
|
|
80
|
+
/** Read noise, RMS e⁻, of each conversion gain, and the ISO from which the high gain is used (null: one gain throughout). */
|
|
81
|
+
readNoise: { lowGainElectrons: number; highGainElectrons: number; highGainFromIso: number | null };
|
|
82
|
+
/** The body's rolling readout skew, s (null when it declares no rolling readout). */
|
|
83
|
+
readoutSeconds: number | null;
|
|
84
|
+
/** ADC bit depth of the capture's readout regime. */
|
|
85
|
+
adcBitDepth: number;
|
|
86
|
+
fixedPatternNoise: boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Engineering dynamic range at the capture's ISO, stops: log₂(S_sat / σ_floor), with S_sat the charge at the RAW white
|
|
89
|
+
* level (full well over the analog gain, limited by the charge capacity and the ADC range) and σ_floor the read noise and
|
|
90
|
+
* ADC quantization added in quadrature (the temporal dark noise of EMVA 1288 Release 4.0 without dark current). An ideal
|
|
91
|
+
* noiseless readout is limited by the ADC alone: log₂((white − black)·√12) for unit digital gain.
|
|
92
|
+
*/
|
|
93
|
+
dynamicRangeStops: number;
|
|
94
|
+
/** The same per ISO setting of the body, in its order. */
|
|
95
|
+
dynamicRangeByIso: readonly { iso: number; stops: number }[];
|
|
96
|
+
/**
|
|
97
|
+
* #535: the expected signal-to-noise ratio of an 18 % Lambertian grey patch under the request's illuminant and illuminance
|
|
98
|
+
* at the capture's exposure, ISO and sensor, on the green channel next to the optical axis (no vignetting, PSF or motion).
|
|
99
|
+
* `snr` = S/σ_total and `snrDecibels` = 20·log₁₀(snr). `clipped`: S is at or above the white level, where the RAW clips and
|
|
100
|
+
* the figure no longer describes the image. Computed only when the request resolves to exact (`fast: false`, the default for
|
|
101
|
+
* DNG, JPEG and PNG exports; omitted for a fast request).
|
|
102
|
+
*/
|
|
103
|
+
midGrey?: { reflectance: 0.18; channel: "green"; signalElectrons: number; noiseElectrons: SimulateCaptureNoiseComponents; snr: number; snrDecibels: number; clipped: boolean };
|
|
104
|
+
}
|
|
105
|
+
```
|
package/llms.txt
CHANGED
|
@@ -26,8 +26,13 @@
|
|
|
26
26
|
- [calculateEquivalentFocalLength35Mm()](https://github.com/Photivra/photivra/blob/main/docs/api/camera-equivalent-focal-length.md#calculateequivalentfocallength35mm): crop factor and 35 mm-equivalent focal length
|
|
27
27
|
- [calculateSensorEquivalence()](https://github.com/Photivra/photivra/blob/main/docs/api/camera-sensor-equivalence.md#calculatesensorequivalence): equivalent focal length and f-number, sensor area ratio and angles of view of a lens on one sensor, each marked exact or approximated
|
|
28
28
|
- [calculateBlurShareOfFrame()](https://github.com/Photivra/photivra/blob/main/docs/api/camera-sensor-equivalence.md#calculateblurshareofframe): a blur circle as a share of the frame diagonal, or its size on a print
|
|
29
|
+
- [calculatePrintSizeLimitWithBlurAndNoise()](https://github.com/Photivra/photivra/blob/main/docs/api/output-print-size-blur-noise.md#calculateprintsizelimitwithblurandnoise): the largest print at a viewing distance that still looks sharp and clean, counting blur and noise as well as pixels (an approximation)
|
|
29
30
|
- [calculateCenteredCrop()](https://github.com/Photivra/photivra/blob/main/docs/api/output-crop.md#calculatecenteredcrop): pixels left after a centred crop
|
|
31
|
+
- [calculateCropToMatchFocalLength()](https://github.com/Photivra/photivra/blob/main/docs/api/output-crop-to-match-focal-length.md#calculatecroptomatchfocallength): crop factor and pixels left when cropping a shorter lens's image to match a longer focal length
|
|
32
|
+
- [calculateApertureDiameter()](https://github.com/Photivra/photivra/blob/main/docs/api/optics-aperture-diameter.md#calculateaperturediameter): entrance-pupil (aperture) diameter in mm, focal length ÷ f-number
|
|
30
33
|
- [calculateProjectedMotionBlur()](https://github.com/Photivra/photivra/blob/main/docs/api/motion-projected-motion.md#calculateprojectedmotionblur): how far a moving subject smears on the sensor during the shutter time
|
|
34
|
+
- [calculateFilteredExposureTime()](https://github.com/Photivra/photivra/blob/main/docs/api/exposure-filtered-exposure-time.md#calculatefilteredexposuretime): shutter time that keeps the same exposure with an ND (or any front-of-lens) filter, and the nearest shutter-dial value; `GENERIC_ND_FILTER_PROFILES` gives idealized 3-, 6- and 10-stop filters (exactly 2^-N)
|
|
35
|
+
- [calculateDetailLimit()](https://github.com/Photivra/photivra/blob/main/docs/api/optics-detail-limit.md#calculatedetaillimit): which limits detail for given settings, the lens (diffraction, aberrations, defocus) or the sensor's pixel pitch, with the optics MTF50, the Nyquist frequency and the system MTF50
|
|
31
36
|
- [calculateAiryDisk()](https://github.com/Photivra/photivra/blob/main/docs/api/optics-diffraction.md#calculateairydisk): diffraction spot size from f-number and wavelength
|
|
32
37
|
- [calculateSignalToNoise()](https://github.com/Photivra/photivra/blob/main/docs/api/sensor-signal-noise.md#calculatesignaltonoise): expected SNR from signal and read noise in electrons
|
|
33
38
|
- [calculateChromaticityFromCorrelatedColorTemperature()](https://github.com/Photivra/photivra/blob/main/docs/api/color-illuminants.md#calculatechromaticityfromcorrelatedcolortemperature): white point for a colour temperature in kelvin
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@photivra/engine",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.20.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Scientific TypeScript engine for camera optics, exposure, focus, motion, sensor sampling, diffraction, and digital imaging calculations.",
|
|
6
6
|
"type": "module",
|