@photivra/engine 2.19.0 → 2.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +289 -1
- package/CITATION.cff +1 -1
- package/NODE_SUPPORT.md +7 -5
- package/README.md +7 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +21 -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/exposure-build-up-stage.d.ts +16 -0
- package/dist/capture/exposure-build-up-stage.js +4 -0
- package/dist/capture/exposure-build-up-stage.js.map +1 -0
- package/dist/capture/native-environment-lean.d.ts +2 -1
- package/dist/capture/native-environment-lean.js +42 -78
- package/dist/capture/native-environment-lean.js.map +1 -1
- package/dist/capture/native-environment-tabulated.js +4 -3
- package/dist/capture/native-environment-tabulated.js.map +1 -1
- package/dist/capture/native-raw-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/native-raw.d.ts +3 -2
- package/dist/capture/native-raw.js +5 -4
- package/dist/capture/native-raw.js.map +1 -1
- package/dist/capture/sensor-raw-producer.d.ts +4 -1
- package/dist/capture/sensor-raw-producer.js +15 -8
- package/dist/capture/sensor-raw-producer.js.map +1 -1
- package/dist/core/shared-helpers.d.ts +2 -0
- package/dist/core/shared-helpers.js +4 -0
- package/dist/core/shared-helpers.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 +11 -4
- package/dist/equipment/generic-lens-catalogue.js +49 -7
- package/dist/equipment/generic-lens-catalogue.js.map +1 -1
- package/dist/equipment/generic-tier-assets.d.ts +32 -0
- package/dist/equipment/generic-tier-assets.js +18 -14
- 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/sensor-profile-body.d.ts +78 -0
- package/dist/equipment/sensor-profile-body.js +77 -0
- package/dist/equipment/sensor-profile-body.js.map +1 -0
- package/dist/equipment/sensor-profiles.d.ts +8 -0
- package/dist/equipment/sensor-profiles.js +82 -0
- package/dist/equipment/sensor-profiles.js.map +1 -0
- package/dist/facade/catalogue-lens-signal.d.ts +2 -2
- package/dist/facade/catalogue-lens-signal.js +58 -71
- package/dist/facade/catalogue-lens-signal.js.map +1 -1
- package/dist/facade/chart-radiance-batch.js +2 -1
- package/dist/facade/chart-radiance-batch.js.map +1 -1
- package/dist/facade/defocus-image-space-provider.d.ts +7 -0
- package/dist/facade/defocus-image-space-provider.js +64 -76
- package/dist/facade/defocus-image-space-provider.js.map +1 -1
- package/dist/facade/display-view-gpu-session.js +2 -0
- package/dist/facade/display-view-gpu-session.js.map +1 -1
- package/dist/facade/display-view-gpu.d.ts +13 -5
- 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 +30 -26
- 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 +6 -24
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/expected-signal-setup.d.ts +3 -1
- package/dist/facade/expected-signal-setup.js +1 -1
- package/dist/facade/expected-signal-setup.js.map +1 -1
- package/dist/facade/expected-signal.d.ts +47 -14
- package/dist/facade/expected-signal.js +117 -112
- package/dist/facade/expected-signal.js.map +1 -1
- package/dist/facade/exposure-build-up.d.ts +70 -0
- package/dist/facade/exposure-build-up.js +164 -0
- package/dist/facade/exposure-build-up.js.map +1 -0
- package/dist/facade/fast-methods.d.ts +50 -0
- package/dist/facade/fast-methods.js +58 -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 +5 -4
- package/dist/facade/generic-lens-capture.js +5 -4
- package/dist/facade/generic-lens-capture.js.map +1 -1
- package/dist/facade/image-plane-enable.d.ts +7 -0
- package/dist/facade/image-plane-enable.js +17 -0
- package/dist/facade/image-plane-enable.js.map +1 -0
- package/dist/facade/image-plane-lattice-data.d.ts +6 -0
- package/dist/facade/image-plane-lattice-data.js +10 -0
- package/dist/facade/image-plane-lattice-data.js.map +1 -0
- package/dist/facade/image-plane-support.d.ts +33 -0
- package/dist/facade/image-plane-support.js +11 -0
- package/dist/facade/image-plane-support.js.map +1 -0
- package/dist/facade/image-plane.d.ts +36 -0
- package/dist/facade/image-plane.js +353 -0
- package/dist/facade/image-plane.js.map +1 -0
- package/dist/facade/mixed-illumination.d.ts +108 -0
- package/dist/facade/mixed-illumination.js +174 -0
- package/dist/facade/mixed-illumination.js.map +1 -0
- package/dist/facade/representative-light-sources.d.ts +72 -0
- package/dist/facade/representative-light-sources.js +124 -0
- package/dist/facade/representative-light-sources.js.map +1 -0
- package/dist/facade/scene-radiance.d.ts +129 -4
- package/dist/facade/scene-radiance.js +48 -13
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +88 -19
- package/dist/facade/simulate-capture.js +374 -145
- 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 +36 -18
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/chart-composite-gpu.d.ts +13 -1
- package/dist/gpu/chart-composite-gpu.js +48 -9
- package/dist/gpu/chart-composite-gpu.js.map +1 -1
- package/dist/gpu/chart-radiance-gpu.js +4 -30
- package/dist/gpu/chart-radiance-gpu.js.map +1 -1
- package/dist/gpu/display-view-composite-material-module.d.ts +10 -4
- package/dist/gpu/display-view-composite-material-module.js +39 -13
- package/dist/gpu/display-view-composite-material-module.js.map +1 -1
- package/dist/gpu/display-view-image-material-module.d.ts +86 -0
- package/dist/gpu/display-view-image-material-module.js +339 -0
- package/dist/gpu/display-view-image-material-module.js.map +1 -0
- package/dist/gpu/display-view-image-quality.d.ts +45 -0
- package/dist/gpu/display-view-image-quality.js +380 -0
- package/dist/gpu/display-view-image-quality.js.map +1 -0
- package/dist/gpu/display-view-quality-gpu.d.ts +67 -1
- package/dist/gpu/display-view-quality-gpu.js +136 -205
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/display-view-speed-gpu.d.ts +51 -3
- package/dist/gpu/display-view-speed-gpu.js +131 -93
- package/dist/gpu/display-view-speed-gpu.js.map +1 -1
- package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +29 -15
- package/dist/gpu/wgsl-display-view-quality-kernel.js +58 -36
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +21 -6
- package/dist/gpu/wgsl-display-view-speed-kernel.js +46 -11
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
- package/dist/gpu/wgsl-exact-sum.d.ts +20 -0
- package/dist/gpu/wgsl-exact-sum.js +42 -0
- package/dist/gpu/wgsl-exact-sum.js.map +1 -0
- package/dist/index.d.ts +18 -3
- package/dist/index.js +12 -1
- package/dist/index.js.map +1 -1
- package/dist/motion/subject-motion.d.ts +66 -0
- package/dist/motion/subject-motion.js +52 -0
- package/dist/motion/subject-motion.js.map +1 -0
- package/dist/optics/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/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/generic-lens-lateral-color.d.ts +83 -0
- package/dist/optics/generic-lens-lateral-color.js +77 -0
- package/dist/optics/generic-lens-lateral-color.js.map +1 -0
- package/dist/optics/generic-lens-psf.d.ts +18 -2
- package/dist/optics/generic-lens-psf.js +79 -48
- package/dist/optics/generic-lens-psf.js.map +1 -1
- package/dist/optics/lens-psf-profile.d.ts +7 -0
- package/dist/optics/lens-psf-profile.js +2 -1
- package/dist/optics/lens-psf-profile.js.map +1 -1
- package/dist/optics/scene-to-sensor-irradiance.d.ts +13 -0
- package/dist/optics/scene-to-sensor-irradiance.js +7 -0
- package/dist/optics/scene-to-sensor-irradiance.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 +19 -5
- package/dist/output/developed-lens-corrections.js +30 -11
- package/dist/output/developed-lens-corrections.js.map +1 -1
- package/dist/output/generic-lens-corrections.d.ts +41 -3
- package/dist/output/generic-lens-corrections.js +57 -9
- package/dist/output/generic-lens-corrections.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/print-contrast-reference.js +1 -1
- package/dist/output/print-contrast-reference.js.map +1 -1
- package/dist/output/print-plan.d.ts +41 -5
- package/dist/output/print-plan.js +59 -14
- package/dist/output/print-plan.js.map +1 -1
- package/dist/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/sensor/binomial-sample.d.ts +3 -0
- package/dist/sensor/binomial-sample.js +86 -0
- package/dist/sensor/binomial-sample.js.map +1 -0
- package/dist/sensor/eqe-electron-rate.js +1 -7
- package/dist/sensor/eqe-electron-rate.js.map +1 -1
- package/dist/sensor/exposure-window.d.ts +2 -0
- package/dist/sensor/exposure-window.js +2 -2
- package/dist/sensor/exposure-window.js.map +1 -1
- package/dist/sensor/readout-timing.js +2 -23
- package/dist/sensor/readout-timing.js.map +1 -1
- package/dist/sensor/response-application-compatibility.js +1 -6
- package/dist/sensor/response-application-compatibility.js.map +1 -1
- package/dist/sensor/responsivity-photocurrent.js +1 -7
- package/dist/sensor/responsivity-photocurrent.js.map +1 -1
- package/dist/sensor/sensor-characteristics.d.ts +147 -0
- package/dist/sensor/sensor-characteristics.js +126 -0
- package/dist/sensor/sensor-characteristics.js.map +1 -0
- package/dist/sensor/spatial-sample-reduction.js +1 -5
- package/dist/sensor/spatial-sample-reduction.js.map +1 -1
- package/dist/sensor/spectral-quadrature.js +1 -6
- package/dist/sensor/spectral-quadrature.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/system.d.ts +9 -0
- package/dist/stabilization/system.js +5 -6
- package/dist/stabilization/system.js.map +1 -1
- package/docs/AGENT_GUIDE.md +5 -0
- package/docs/API_REFERENCE.md +173 -2
- package/docs/QUICK_START.md +213 -6
- package/docs/USAGE.md +157 -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 +17 -7
- package/docs/api/equipment-sensor-profile-body.md +64 -0
- package/docs/api/equipment-sensor-profiles.md +86 -0
- package/docs/api/exports.json +357 -2
- package/docs/api/facade-display-view-gpu.md +5 -2
- package/docs/api/facade-exposure-build-up.md +105 -0
- package/docs/api/facade-fast-methods.md +99 -0
- package/docs/api/facade-image-plane-enable.md +16 -0
- package/docs/api/facade-mixed-illumination.md +67 -0
- package/docs/api/facade-representative-light-sources.md +131 -0
- package/docs/api/facade-scene-radiance.md +54 -3
- package/docs/api/facade-simulate-capture.md +60 -15
- package/docs/api/facade-simulate-display-view.md +3 -2
- package/docs/api/motion-subject-motion.md +23 -0
- package/docs/api/optics-aperture-diameter.md +51 -0
- package/docs/api/optics-detail-limit.md +203 -0
- package/docs/api/optics-generic-lens-lateral-color.md +79 -0
- package/docs/api/output-crop-to-match-focal-length.md +67 -0
- package/docs/api/output-developed-lens-corrections.md +10 -1
- package/docs/api/output-generic-lens-corrections.md +40 -2
- package/docs/api/output-lens-corrections.md +11 -1
- package/docs/api/output-print-plan.md +35 -1
- package/docs/api/output-print-size-blur-noise.md +115 -0
- package/docs/api/sensor-sensor-characteristics.md +105 -0
- package/llms.txt +6 -0
- package/package.json +1 -1
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Sensor profile catalogue (#554): eight representative, unnamed sensors whose values come from manufacturer-published
|
|
4
|
+
* specifications and openly published papers, or are labelled design values inside the range those sources support.
|
|
5
|
+
* Profiles are named by format, resolution, architecture and era; the market tag is a label only and enters no equation.
|
|
6
|
+
*
|
|
7
|
+
* A capture takes a profile record, `simulateCapture({ sensor: { profile: SENSOR_PROFILES[i] } })`; this module is data only
|
|
8
|
+
* and is never imported by the capture, so an application's capture worker does not bundle it (the record is checked by
|
|
9
|
+
* `parseSensorProfile` in `sensor-profile-body.ts`).
|
|
10
|
+
* @see docs/SENSOR_PROFILES.md for every value's source or design basis.
|
|
11
|
+
*/
|
|
12
|
+
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
13
|
+
const BSI = { prnu: .004, dsnu: .15, hotPixelFraction: 5e-5, hotPixelMultiplier: 30, rowElectrons: .15, columnElectrons: .1 };
|
|
14
|
+
const FSI = { prnu: .006, dsnu: .2, hotPixelFraction: 1e-4, hotPixelMultiplier: 40, rowElectrons: .3, columnElectrons: .2 };
|
|
15
|
+
const sourced = (key, ...fields) => Object.fromEntries(fields.map((f) => [f, "published " + key]));
|
|
16
|
+
/** The catalogue, in the issue's order. Frozen. */
|
|
17
|
+
export const SENSOR_PROFILES = /* @__PURE__ */ Object.freeze([
|
|
18
|
+
{ id: "full-frame-24mp-bsi-2020s", name: "Full frame 24 MP BSI, 2020s", marketTag: "consumer", architecture: "back-illuminated",
|
|
19
|
+
sensorWidthMm: 36.07, sensorHeightMm: 24.01, pixelsWide: 6072, pixelsHigh: 4042, pixelPitchMicrometers: 5.94, peakQuantumEfficiency: .8,
|
|
20
|
+
fullWellElectrons: 100000, readNoise: { lowGainElectrons: 6.4, highGainElectrons: 1.1, highGainFromIso: 500 }, highGainFullWellElectrons: 20000,
|
|
21
|
+
adcBitDepth: 14, baseIso: 100, maximumIso: 51200, maximumAnalogGain: 64, readoutSeconds: .05, fixedPattern: BSI,
|
|
22
|
+
goodFor: ["landscape", "portrait", "travel"], description: "Large pixels and a deep full well for wide dynamic range at base ISO; a moderate readout shows some rolling-shutter skew.",
|
|
23
|
+
fixedLenses: null, provenance: { ...sourced("S1", "pixels", "pixelPitchMicrometers", "peakQuantumEfficiency", "fullWellElectrons", "readNoise", "adcBitDepth"),
|
|
24
|
+
sensorSize: "derived", architecture: "published S1", highGainFromIso: "derived S1", highGainFullWellElectrons: "derived", baseIso: "derived" } },
|
|
25
|
+
{ id: "full-frame-45mp-stacked-2020s", name: "Full frame 45 MP stacked, 2020s", marketTag: "professional", architecture: "stacked",
|
|
26
|
+
sensorWidthMm: 36, sensorHeightMm: 24, pixelsWide: 8256, pixelsHigh: 5504, pixelPitchMicrometers: 4.36, peakQuantumEfficiency: .8,
|
|
27
|
+
fullWellElectrons: 69000, readNoise: { lowGainElectrons: 3.5, highGainElectrons: 1.5, highGainFromIso: 250 }, highGainFullWellElectrons: 22080,
|
|
28
|
+
adcBitDepth: 14, baseIso: 80, maximumIso: 25600, maximumAnalogGain: 64, readoutSeconds: .005, fixedPattern: BSI,
|
|
29
|
+
goodFor: ["landscape", "studio", "wildlife"], description: "High resolution with a fast stacked readout: fine detail for landscape and studio work, little rolling-shutter skew for action.",
|
|
30
|
+
fixedLenses: null, provenance: { pixelPitchMicrometers: "derived", fullWellElectrons: "design (S2 scaled by pixel area)", readNoise: "design (S2)", highGainFromIso: "design (S2)",
|
|
31
|
+
highGainFullWellElectrons: "derived", baseIso: "derived" } },
|
|
32
|
+
{ id: "full-frame-24mp-stacked-sports-2020s", name: "Full frame 24 MP stacked (sports), 2020s", marketTag: "professional", architecture: "stacked",
|
|
33
|
+
sensorWidthMm: 36, sensorHeightMm: 24, pixelsWide: 6000, pixelsHigh: 4000, pixelPitchMicrometers: 6, peakQuantumEfficiency: .8,
|
|
34
|
+
fullWellElectrons: 80000, readNoise: { lowGainElectrons: 3, highGainElectrons: 1, highGainFromIso: 400 }, highGainFullWellElectrons: 25000,
|
|
35
|
+
adcBitDepth: 14, baseIso: 125, maximumIso: 102400, maximumAnalogGain: 64, readoutSeconds: .0025, fixedPattern: BSI,
|
|
36
|
+
goodFor: ["sports", "wildlife", "journalism"], description: "The fastest readout and low high-gain read noise: almost no rolling-shutter skew and clean high ISO, at a moderate resolution.",
|
|
37
|
+
fixedLenses: null, provenance: { pixelPitchMicrometers: "derived", highGainFullWellElectrons: "derived", baseIso: "derived" } },
|
|
38
|
+
{ id: "full-frame-24mp-fsi-2010s", name: "Full frame 24 MP front-illuminated, 2010s", marketTag: "professional", architecture: "front-illuminated",
|
|
39
|
+
sensorWidthMm: 36.01, sensorHeightMm: 24.07, pixelsWide: 6032, pixelsHigh: 4032, pixelPitchMicrometers: 5.97, peakQuantumEfficiency: .53,
|
|
40
|
+
fullWellElectrons: 76000, readNoise: { lowGainElectrons: 4, highGainElectrons: 4, highGainFromIso: null }, highGainFullWellElectrons: null,
|
|
41
|
+
adcBitDepth: 14, baseIso: 80, maximumIso: 25600, maximumAnalogGain: 64, readoutSeconds: .08, fixedPattern: FSI,
|
|
42
|
+
goodFor: ["portrait", "landscape", "journalism"], description: "An older professional sensor: lower QE and one conversion gain, so a modern consumer BSI sensor beats it at high ISO; slow readout.",
|
|
43
|
+
fixedLenses: null, provenance: { ...sourced("S3", "pixels", "pixelPitchMicrometers", "peakQuantumEfficiency", "fullWellElectrons", "adcBitDepth"),
|
|
44
|
+
sensorSize: "derived", architecture: "derived S4", baseIso: "derived" } },
|
|
45
|
+
{ id: "aps-c-24mp-fsi-2020s", name: "APS-C 24 MP front-illuminated, 2020s", marketTag: "consumer", architecture: "front-illuminated",
|
|
46
|
+
sensorWidthMm: 23.55, sensorHeightMm: 15.73, pixelsWide: 6024, pixelsHigh: 4024, pixelPitchMicrometers: 3.91, peakQuantumEfficiency: .55,
|
|
47
|
+
fullWellElectrons: 36000, readNoise: { lowGainElectrons: 2.7, highGainElectrons: 2.7, highGainFromIso: null }, highGainFullWellElectrons: null,
|
|
48
|
+
adcBitDepth: 14, baseIso: 80, maximumIso: 25600, maximumAnalogGain: 64, readoutSeconds: .06, fixedPattern: FSI,
|
|
49
|
+
goodFor: ["travel", "family", "learning"], description: "A smaller, front-illuminated sensor: less light per pixel, a slower readout and more rolling-shutter skew.",
|
|
50
|
+
fixedLenses: null, provenance: { ...sourced("S5", "pixels", "pixelPitchMicrometers", "architecture", "fullWellElectrons", "readNoise", "adcBitDepth"), sensorSize: "derived", baseIso: "derived" } },
|
|
51
|
+
{ id: "aps-c-26mp-bsi-2020s", name: "APS-C 26 MP BSI, 2020s", marketTag: "professional", architecture: "back-illuminated",
|
|
52
|
+
sensorWidthMm: 23.49, sensorHeightMm: 15.7, pixelsWide: 6248, pixelsHigh: 4176, pixelPitchMicrometers: 3.76, peakQuantumEfficiency: .8,
|
|
53
|
+
fullWellElectrons: 50000, readNoise: { lowGainElectrons: 3.3, highGainElectrons: 1, highGainFromIso: 250 }, highGainFullWellElectrons: 16000,
|
|
54
|
+
adcBitDepth: 14, baseIso: 80, maximumIso: 25600, maximumAnalogGain: 64, readoutSeconds: .025, fixedPattern: BSI,
|
|
55
|
+
goodFor: ["wildlife", "sports", "travel"], description: "A BSI APS-C sensor with dual conversion gain whose readout beats the consumer full-frame profile's.",
|
|
56
|
+
fixedLenses: null, provenance: { ...sourced("S6", "pixels", "pixelPitchMicrometers", "peakQuantumEfficiency", "fullWellElectrons", "readNoise"), sensorSize: "derived",
|
|
57
|
+
architecture: "published S6", highGainFromIso: "design (S7)", highGainFullWellElectrons: "derived", baseIso: "derived" } },
|
|
58
|
+
{ id: "medium-format-100mp-bsi-16bit", name: "Medium format 100 MP BSI, 16-bit", marketTag: "professional", architecture: "back-illuminated",
|
|
59
|
+
sensorWidthMm: 43.856, sensorHeightMm: 32.9, pixelsWide: 11656, pixelsHigh: 8750, pixelPitchMicrometers: 3.76, peakQuantumEfficiency: .8,
|
|
60
|
+
fullWellElectrons: 50300, readNoise: { lowGainElectrons: 3.3, highGainElectrons: 1, highGainFromIso: 250 }, highGainFullWellElectrons: 16096,
|
|
61
|
+
adcBitDepth: 16, baseIso: 80, maximumIso: 12800, maximumAnalogGain: 64, readoutSeconds: .25, fixedPattern: BSI,
|
|
62
|
+
goodFor: ["studio", "landscape", "portrait"], description: "A large sensor with 100 MP and a 16-bit RAW; a slow readout makes its electronic shutter unsuited to motion.",
|
|
63
|
+
fixedLenses: null, provenance: { ...sourced("S7", "pixels", "pixelPitchMicrometers", "sensorSize", "fullWellElectrons", "readNoise", "adcBitDepth"), architecture: "published S8",
|
|
64
|
+
highGainFromIso: "derived S7", highGainFullWellElectrons: "derived", baseIso: "derived" } },
|
|
65
|
+
{ id: "phone-50mp-stacked", name: "Phone, large, 50 MP stacked", marketTag: "consumer", architecture: "stacked",
|
|
66
|
+
sensorWidthMm: 9.844, sensorHeightMm: 7.431, pixelsWide: 8192, pixelsHigh: 6144, pixelPitchMicrometers: 1.197, peakQuantumEfficiency: .75,
|
|
67
|
+
fullWellElectrons: 8600, readNoise: { lowGainElectrons: 3, highGainElectrons: 1.4, highGainFromIso: 160 }, highGainFullWellElectrons: 2150,
|
|
68
|
+
adcBitDepth: 12, baseIso: 40, maximumIso: 6400, maximumAnalogGain: 64, readoutSeconds: 1 / 30,
|
|
69
|
+
fixedPattern: { prnu: .008, dsnu: .3, hotPixelFraction: 2e-4, hotPixelMultiplier: 50, rowElectrons: .2, columnElectrons: .2 },
|
|
70
|
+
goodFor: ["everyday", "travel", "family"], description: "A large phone sensor: tiny pixels with a small full well, so less dynamic range and more noise per pixel; simulated as a single frame (no multi-frame stacking).",
|
|
71
|
+
fixedLenses: "phone", provenance: { ...sourced("S9", "pixels", "pixelPitchMicrometers", "sensorSize", "architecture", "adcBitDepth"), fullWellElectrons: "design (S10 scaled by pixel area)",
|
|
72
|
+
readNoise: "high gain published S10, low gain design", highGainFullWellElectrons: "derived", readoutSeconds: "derived S9 (1 / full-resolution frame rate)",
|
|
73
|
+
fixedPattern: "PRNU, row and column published S10; the rest design", baseIso: "derived" } }
|
|
74
|
+
].map((p) => Object.freeze(p)));
|
|
75
|
+
/** The profile ids, in catalogue order. */
|
|
76
|
+
export const SENSOR_PROFILE_IDS = /* @__PURE__ */ Object.freeze(SENSOR_PROFILES.map((p) => p.id));
|
|
77
|
+
/** The profile record with this id (for `sensor.profile`); a clear error listing the ids otherwise. */
|
|
78
|
+
export function resolveSensorProfile(id) {
|
|
79
|
+
return SENSOR_PROFILES.find((p) => p.id === id) ?? fail("resolveSensorProfile: the id must be one of " + SENSOR_PROFILE_IDS.join(", ") + ".");
|
|
80
|
+
}
|
|
81
|
+
function fail(message) { throw new InvalidConfigurationError(message); }
|
|
82
|
+
//# sourceMappingURL=sensor-profiles.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sensor-profiles.js","sourceRoot":"","sources":["../../src/equipment/sensor-profiles.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Sensor profile catalogue (#554): eight representative, unnamed sensors whose values come from manufacturer-published\n * specifications and openly published papers, or are labelled design values inside the range those sources support.\n * Profiles are named by format, resolution, architecture and era; the market tag is a label only and enters no equation.\n *\n * A capture takes a profile record, `simulateCapture({ sensor: { profile: SENSOR_PROFILES[i] } })`; this module is data only\n * and is never imported by the capture, so an application's capture worker does not bundle it (the record is checked by\n * `parseSensorProfile` in `sensor-profile-body.ts`).\n * @see docs/SENSOR_PROFILES.md for every value's source or design basis.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport type { SensorProfile } from \"./sensor-profile-body.js\";\n\nexport type { SensorProfile, SensorProfileValueKind } from \"./sensor-profile-body.js\";\n\nconst BSI = { prnu: .004, dsnu: .15, hotPixelFraction: 5e-5, hotPixelMultiplier: 30, rowElectrons: .15, columnElectrons: .1 };\nconst FSI = { prnu: .006, dsnu: .2, hotPixelFraction: 1e-4, hotPixelMultiplier: 40, rowElectrons: .3, columnElectrons: .2 };\nconst sourced = (key: string, ...fields: string[]): Record<string, string> => Object.fromEntries(fields.map((f) => [f, \"published \" + key]));\n\n/** The catalogue, in the issue's order. Frozen. */\nexport const SENSOR_PROFILES: readonly SensorProfile[] = /* @__PURE__ */ Object.freeze(([\n { id: \"full-frame-24mp-bsi-2020s\", name: \"Full frame 24 MP BSI, 2020s\", marketTag: \"consumer\", architecture: \"back-illuminated\",\n sensorWidthMm: 36.07, sensorHeightMm: 24.01, pixelsWide: 6072, pixelsHigh: 4042, pixelPitchMicrometers: 5.94, peakQuantumEfficiency: .8,\n fullWellElectrons: 100000, readNoise: { lowGainElectrons: 6.4, highGainElectrons: 1.1, highGainFromIso: 500 }, highGainFullWellElectrons: 20000,\n adcBitDepth: 14, baseIso: 100, maximumIso: 51200, maximumAnalogGain: 64, readoutSeconds: .05, fixedPattern: BSI,\n goodFor: [\"landscape\", \"portrait\", \"travel\"], description: \"Large pixels and a deep full well for wide dynamic range at base ISO; a moderate readout shows some rolling-shutter skew.\",\n fixedLenses: null, provenance: { ...sourced(\"S1\", \"pixels\", \"pixelPitchMicrometers\", \"peakQuantumEfficiency\", \"fullWellElectrons\", \"readNoise\", \"adcBitDepth\"),\n sensorSize: \"derived\", architecture: \"published S1\", highGainFromIso: \"derived S1\", highGainFullWellElectrons: \"derived\", baseIso: \"derived\" } },\n { id: \"full-frame-45mp-stacked-2020s\", name: \"Full frame 45 MP stacked, 2020s\", marketTag: \"professional\", architecture: \"stacked\",\n sensorWidthMm: 36, sensorHeightMm: 24, pixelsWide: 8256, pixelsHigh: 5504, pixelPitchMicrometers: 4.36, peakQuantumEfficiency: .8,\n fullWellElectrons: 69000, readNoise: { lowGainElectrons: 3.5, highGainElectrons: 1.5, highGainFromIso: 250 }, highGainFullWellElectrons: 22080,\n adcBitDepth: 14, baseIso: 80, maximumIso: 25600, maximumAnalogGain: 64, readoutSeconds: .005, fixedPattern: BSI,\n goodFor: [\"landscape\", \"studio\", \"wildlife\"], description: \"High resolution with a fast stacked readout: fine detail for landscape and studio work, little rolling-shutter skew for action.\",\n fixedLenses: null, provenance: { pixelPitchMicrometers: \"derived\", fullWellElectrons: \"design (S2 scaled by pixel area)\", readNoise: \"design (S2)\", highGainFromIso: \"design (S2)\",\n highGainFullWellElectrons: \"derived\", baseIso: \"derived\" } },\n { id: \"full-frame-24mp-stacked-sports-2020s\", name: \"Full frame 24 MP stacked (sports), 2020s\", marketTag: \"professional\", architecture: \"stacked\",\n sensorWidthMm: 36, sensorHeightMm: 24, pixelsWide: 6000, pixelsHigh: 4000, pixelPitchMicrometers: 6, peakQuantumEfficiency: .8,\n fullWellElectrons: 80000, readNoise: { lowGainElectrons: 3, highGainElectrons: 1, highGainFromIso: 400 }, highGainFullWellElectrons: 25000,\n adcBitDepth: 14, baseIso: 125, maximumIso: 102400, maximumAnalogGain: 64, readoutSeconds: .0025, fixedPattern: BSI,\n goodFor: [\"sports\", \"wildlife\", \"journalism\"], description: \"The fastest readout and low high-gain read noise: almost no rolling-shutter skew and clean high ISO, at a moderate resolution.\",\n fixedLenses: null, provenance: { pixelPitchMicrometers: \"derived\", highGainFullWellElectrons: \"derived\", baseIso: \"derived\" } },\n { id: \"full-frame-24mp-fsi-2010s\", name: \"Full frame 24 MP front-illuminated, 2010s\", marketTag: \"professional\", architecture: \"front-illuminated\",\n sensorWidthMm: 36.01, sensorHeightMm: 24.07, pixelsWide: 6032, pixelsHigh: 4032, pixelPitchMicrometers: 5.97, peakQuantumEfficiency: .53,\n fullWellElectrons: 76000, readNoise: { lowGainElectrons: 4, highGainElectrons: 4, highGainFromIso: null }, highGainFullWellElectrons: null,\n adcBitDepth: 14, baseIso: 80, maximumIso: 25600, maximumAnalogGain: 64, readoutSeconds: .08, fixedPattern: FSI,\n goodFor: [\"portrait\", \"landscape\", \"journalism\"], description: \"An older professional sensor: lower QE and one conversion gain, so a modern consumer BSI sensor beats it at high ISO; slow readout.\",\n fixedLenses: null, provenance: { ...sourced(\"S3\", \"pixels\", \"pixelPitchMicrometers\", \"peakQuantumEfficiency\", \"fullWellElectrons\", \"adcBitDepth\"),\n sensorSize: \"derived\", architecture: \"derived S4\", baseIso: \"derived\" } },\n { id: \"aps-c-24mp-fsi-2020s\", name: \"APS-C 24 MP front-illuminated, 2020s\", marketTag: \"consumer\", architecture: \"front-illuminated\",\n sensorWidthMm: 23.55, sensorHeightMm: 15.73, pixelsWide: 6024, pixelsHigh: 4024, pixelPitchMicrometers: 3.91, peakQuantumEfficiency: .55,\n fullWellElectrons: 36000, readNoise: { lowGainElectrons: 2.7, highGainElectrons: 2.7, highGainFromIso: null }, highGainFullWellElectrons: null,\n adcBitDepth: 14, baseIso: 80, maximumIso: 25600, maximumAnalogGain: 64, readoutSeconds: .06, fixedPattern: FSI,\n goodFor: [\"travel\", \"family\", \"learning\"], description: \"A smaller, front-illuminated sensor: less light per pixel, a slower readout and more rolling-shutter skew.\",\n fixedLenses: null, provenance: { ...sourced(\"S5\", \"pixels\", \"pixelPitchMicrometers\", \"architecture\", \"fullWellElectrons\", \"readNoise\", \"adcBitDepth\"), sensorSize: \"derived\", baseIso: \"derived\" } },\n { id: \"aps-c-26mp-bsi-2020s\", name: \"APS-C 26 MP BSI, 2020s\", marketTag: \"professional\", architecture: \"back-illuminated\",\n sensorWidthMm: 23.49, sensorHeightMm: 15.7, pixelsWide: 6248, pixelsHigh: 4176, pixelPitchMicrometers: 3.76, peakQuantumEfficiency: .8,\n fullWellElectrons: 50000, readNoise: { lowGainElectrons: 3.3, highGainElectrons: 1, highGainFromIso: 250 }, highGainFullWellElectrons: 16000,\n adcBitDepth: 14, baseIso: 80, maximumIso: 25600, maximumAnalogGain: 64, readoutSeconds: .025, fixedPattern: BSI,\n goodFor: [\"wildlife\", \"sports\", \"travel\"], description: \"A BSI APS-C sensor with dual conversion gain whose readout beats the consumer full-frame profile's.\",\n fixedLenses: null, provenance: { ...sourced(\"S6\", \"pixels\", \"pixelPitchMicrometers\", \"peakQuantumEfficiency\", \"fullWellElectrons\", \"readNoise\"), sensorSize: \"derived\",\n architecture: \"published S6\", highGainFromIso: \"design (S7)\", highGainFullWellElectrons: \"derived\", baseIso: \"derived\" } },\n { id: \"medium-format-100mp-bsi-16bit\", name: \"Medium format 100 MP BSI, 16-bit\", marketTag: \"professional\", architecture: \"back-illuminated\",\n sensorWidthMm: 43.856, sensorHeightMm: 32.9, pixelsWide: 11656, pixelsHigh: 8750, pixelPitchMicrometers: 3.76, peakQuantumEfficiency: .8,\n fullWellElectrons: 50300, readNoise: { lowGainElectrons: 3.3, highGainElectrons: 1, highGainFromIso: 250 }, highGainFullWellElectrons: 16096,\n adcBitDepth: 16, baseIso: 80, maximumIso: 12800, maximumAnalogGain: 64, readoutSeconds: .25, fixedPattern: BSI,\n goodFor: [\"studio\", \"landscape\", \"portrait\"], description: \"A large sensor with 100 MP and a 16-bit RAW; a slow readout makes its electronic shutter unsuited to motion.\",\n fixedLenses: null, provenance: { ...sourced(\"S7\", \"pixels\", \"pixelPitchMicrometers\", \"sensorSize\", \"fullWellElectrons\", \"readNoise\", \"adcBitDepth\"), architecture: \"published S8\",\n highGainFromIso: \"derived S7\", highGainFullWellElectrons: \"derived\", baseIso: \"derived\" } },\n { id: \"phone-50mp-stacked\", name: \"Phone, large, 50 MP stacked\", marketTag: \"consumer\", architecture: \"stacked\",\n sensorWidthMm: 9.844, sensorHeightMm: 7.431, pixelsWide: 8192, pixelsHigh: 6144, pixelPitchMicrometers: 1.197, peakQuantumEfficiency: .75,\n fullWellElectrons: 8600, readNoise: { lowGainElectrons: 3, highGainElectrons: 1.4, highGainFromIso: 160 }, highGainFullWellElectrons: 2150,\n adcBitDepth: 12, baseIso: 40, maximumIso: 6400, maximumAnalogGain: 64, readoutSeconds: 1 / 30,\n fixedPattern: { prnu: .008, dsnu: .3, hotPixelFraction: 2e-4, hotPixelMultiplier: 50, rowElectrons: .2, columnElectrons: .2 },\n goodFor: [\"everyday\", \"travel\", \"family\"], description: \"A large phone sensor: tiny pixels with a small full well, so less dynamic range and more noise per pixel; simulated as a single frame (no multi-frame stacking).\",\n fixedLenses: \"phone\", provenance: { ...sourced(\"S9\", \"pixels\", \"pixelPitchMicrometers\", \"sensorSize\", \"architecture\", \"adcBitDepth\"), fullWellElectrons: \"design (S10 scaled by pixel area)\",\n readNoise: \"high gain published S10, low gain design\", highGainFullWellElectrons: \"derived\", readoutSeconds: \"derived S9 (1 / full-resolution frame rate)\",\n fixedPattern: \"PRNU, row and column published S10; the rest design\", baseIso: \"derived\" } }\n] satisfies SensorProfile[]).map((p) => Object.freeze(p)));\n\n/** The profile ids, in catalogue order. */\nexport const SENSOR_PROFILE_IDS: readonly string[] = /* @__PURE__ */ Object.freeze(SENSOR_PROFILES.map((p) => p.id));\n\n/** The profile record with this id (for `sensor.profile`); a clear error listing the ids otherwise. */\nexport function resolveSensorProfile(id: unknown): SensorProfile {\n return SENSOR_PROFILES.find((p) => p.id === id) ?? fail(\"resolveSensorProfile: the id must be one of \" + SENSOR_PROFILE_IDS.join(\", \") + \".\");\n}\nfunction fail(message: string): never { throw new InvalidConfigurationError(message); }\n"]}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { type IlluminationVignettingProfile } from "../optics/illumination-vignetting.js";
|
|
2
2
|
import { type ResolveLensSampledPsfInput } from "../optics/lens-psf-profile.js";
|
|
3
3
|
import type { DisplayViewScene } from "./expected-signal.js";
|
|
4
|
-
/** Fewest grid cells between neighboring profile field nodes. */
|
|
5
|
-
export declare const CATALOGUE_FAST_GRID_MINIMUM_SUBDIVISIONS =
|
|
4
|
+
/** Fewest grid cells between neighboring profile field nodes (10 since #589: with 4, 0.4 % of quick frames failed the run-time check). */
|
|
5
|
+
export declare const CATALOGUE_FAST_GRID_MINIMUM_SUBDIVISIONS = 10;
|
|
6
6
|
/** Largest move of a node kernel's intensity centroid across one grid cell, in sites (sets finer grids where lateral color moves the PSF). */
|
|
7
7
|
export declare const CATALOGUE_FAST_GRID_CENTROID_STEP_SITES = 0.05;
|
|
8
8
|
/**
|
|
@@ -18,9 +18,11 @@
|
|
|
18
18
|
* The grid subdivides each interval between the profile's field nodes into at least GRID_MINIMUM_SUBDIVISIONS cells, and into
|
|
19
19
|
* more where the node kernels' intensity centroids move by more than GRID_CENTROID_STEP_SITES sites across the interval (lateral
|
|
20
20
|
* colour moves the PSF across the frame; a blend of two shifted kernels is not the shifted kernel). Since the blend is linear,
|
|
21
|
-
* the channel response
|
|
22
|
-
*
|
|
23
|
-
*
|
|
21
|
+
* the channel response folds into the grid kernels once per grid point and node, and each tap's signal is integrated from its
|
|
22
|
+
* material's spectrum when the site is read (2.21.0; no material is folded into a kernel):
|
|
23
|
+
* μ̃_c(q) = Σ_k F(s_k)·Σ_λ L_{m(s_k)}(λ)·Σ_g b_g(q)·C_{g,c}(k, λ), C_{g,c}(k, λ) = A_c(λ)·K_{g,λ}(k),
|
|
24
|
+
* the blend taken along y once per grid column and native row, so a site costs (2R + 1)² taps of N nodes each instead of
|
|
25
|
+
* (2R + 1)² scene queries per spectral node. Its error against the exact method is
|
|
24
26
|
* bounded by ‖K̃ − K‖₁ times the brightest tap's signal; tests measure both (test/simulate-capture-lens-catalogue-fast.test.ts).
|
|
25
27
|
*
|
|
26
28
|
* The scene points. The mapping is the environment's (`undistortedImagePoint`, `lensProjectionDirectionValue`, the planar
|
|
@@ -32,9 +34,9 @@ import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
|
32
34
|
import { insideRadialImageCircle, lensProjectionDirectionValue } from "../camera/lens-projection.js";
|
|
33
35
|
import { resolveProjectionDistanceMm } from "../motion/camera-rotation.js";
|
|
34
36
|
import { prepareIlluminationVignettingThroughput } from "../optics/illumination-vignetting.js";
|
|
35
|
-
import { resolveLensSampledPsfKernel } from "../optics/lens-psf-profile.js";
|
|
36
|
-
/** Fewest grid cells between neighboring profile field nodes. */
|
|
37
|
-
export const CATALOGUE_FAST_GRID_MINIMUM_SUBDIVISIONS =
|
|
37
|
+
import { kernelCentroid, resolveLensSampledPsfKernel } from "../optics/lens-psf-profile.js";
|
|
38
|
+
/** Fewest grid cells between neighboring profile field nodes (10 since #589: with 4, 0.4 % of quick frames failed the run-time check). */
|
|
39
|
+
export const CATALOGUE_FAST_GRID_MINIMUM_SUBDIVISIONS = 10;
|
|
38
40
|
/** Largest move of a node kernel's intensity centroid across one grid cell, in sites (sets finer grids where lateral color moves the PSF). */
|
|
39
41
|
export const CATALOGUE_FAST_GRID_CENTROID_STEP_SITES = 0.05;
|
|
40
42
|
/**
|
|
@@ -45,17 +47,6 @@ export const CATALOGUE_FAST_VERIFICATION_TOLERANCE = 0.005;
|
|
|
45
47
|
/** Largest cache window, in lattice points; a larger window is evaluated without caching. */
|
|
46
48
|
const WINDOW_LIMIT = 1 << 22;
|
|
47
49
|
const NEWTON_ITERATIONS = 60;
|
|
48
|
-
/** A kernel's intensity centroid, in samples. */
|
|
49
|
-
function centroid(values, width) {
|
|
50
|
-
let sum = 0, x = 0, y = 0;
|
|
51
|
-
for (let i = 0; i < values.length; i++) {
|
|
52
|
-
const v = values[i];
|
|
53
|
-
sum += v;
|
|
54
|
-
x += v * (i % width);
|
|
55
|
-
y += v * Math.floor(i / width);
|
|
56
|
-
}
|
|
57
|
-
return { x: x / sum, y: y / sum };
|
|
58
|
-
}
|
|
59
50
|
/**
|
|
60
51
|
* The grid axes: each interval between neighboring profile field nodes split into max(GRID_MINIMUM_SUBDIVISIONS,
|
|
61
52
|
* ⌈Δc / GRID_CENTROID_STEP_SITES⌉) equal cells, Δc the largest centroid move between the interval's two nodes over the other
|
|
@@ -66,7 +57,7 @@ export function catalogueFastGridAxes(psf) {
|
|
|
66
57
|
const centroids = new Map();
|
|
67
58
|
for (const node of profile.nodes) {
|
|
68
59
|
const c = node.coordinate;
|
|
69
|
-
centroids.set(c.fieldXmm + ":" + c.fieldYmm + ":" + c.wavelengthNm,
|
|
60
|
+
centroids.set(c.fieldXmm + ":" + c.fieldYmm + ":" + c.wavelengthNm, kernelCentroid(node.kernel));
|
|
70
61
|
}
|
|
71
62
|
const at = (x, y, nm) => {
|
|
72
63
|
const c = centroids.get(x + ":" + y + ":" + nm);
|
|
@@ -122,18 +113,12 @@ export function createCatalogueLensSignal(given) {
|
|
|
122
113
|
throw new InvalidConfigurationError("lens.catalogue fast: the PSF profile's kernels differ in size.");
|
|
123
114
|
// Lattice reach of the taps around a site: x from −cx … kw − 1 − cx (as c_x − k_x), y from −cy … kh − 1 − cy.
|
|
124
115
|
const reachLeft = kw - 1 - cx, reachRight = cx, reachUp = cy, reachDown = kh - 1 - cy;
|
|
125
|
-
//
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
scene.materials.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
materialClass[index] = 1 + spectral.length;
|
|
132
|
-
spectral.push(m.radiance);
|
|
133
|
-
} });
|
|
134
|
-
const classes = 1 + spectral.length, classRadiance = [scene.whiteRadiance, ...spectral];
|
|
135
|
-
const tableSize = 3 * classes * taps;
|
|
136
|
-
// W_{c,m}(k) = Σ_λ A_c(λ)·L_m(λ)·K_λ(k) for the kernels of one field point.
|
|
116
|
+
// Each material's spectral radiance at the nodes (a neutral one: its reflectance times the white radiance). Only the spectra are
|
|
117
|
+
// kept: a site's signal is integrated from its taps' material spectra when it is read, so no material is folded into a kernel.
|
|
118
|
+
const M = scene.materials.length;
|
|
119
|
+
const spectra = scene.materials.map((m) => ("neutral" in m ? Float64Array.from(scene.whiteRadiance, (w) => m.neutral * w) : Float64Array.from(m.radiance)));
|
|
120
|
+
const tableSize = 3 * taps * nodes;
|
|
121
|
+
// A_c(λ_n)·K_n(k) for the kernels of one field point, at ((c·taps + k)·nodes + n): the channel response per node, no material.
|
|
137
122
|
const foldKernels = (fieldXmm, fieldYmm) => {
|
|
138
123
|
const table = new Float64Array(tableSize);
|
|
139
124
|
for (let n = 0; n < nodes; n++) {
|
|
@@ -142,15 +127,8 @@ export function createCatalogueLensSignal(given) {
|
|
|
142
127
|
throw new InvalidConfigurationError("lens.catalogue fast: a resolved kernel differs in size.");
|
|
143
128
|
for (let c = 0; c < 3; c++) {
|
|
144
129
|
const a = A[c][n];
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
for (let cl = 0; cl < classes; cl++) {
|
|
148
|
-
const weight = a * classRadiance[cl][n], base = (c * classes + cl) * taps;
|
|
149
|
-
if (weight === 0)
|
|
150
|
-
continue;
|
|
151
|
-
for (let k = 0; k < taps; k++)
|
|
152
|
-
table[base + k] = table[base + k] + weight * kernel[k];
|
|
153
|
-
}
|
|
130
|
+
for (let k = 0; k < taps; k++)
|
|
131
|
+
table[(c * taps + k) * nodes + n] = a * kernel[k];
|
|
154
132
|
}
|
|
155
133
|
}
|
|
156
134
|
return table;
|
|
@@ -279,40 +257,39 @@ export function createCatalogueLensSignal(given) {
|
|
|
279
257
|
winFactor[q * w + p] = lf;
|
|
280
258
|
}
|
|
281
259
|
};
|
|
282
|
-
|
|
283
|
-
|
|
260
|
+
// A site's kernel is the bilinear blend of its cell's four grid kernels. Blended along y first, once per grid column and native
|
|
261
|
+
// row (every site of a row has the same fy): R_i(y) = (1 − fy)·T(i, j) + fy·T(i, j + 1), kept for the row the sites are on.
|
|
262
|
+
const rowTables = [], rowOf = new Int32Array(GX).fill(-1);
|
|
263
|
+
const rowTable = (i, y) => {
|
|
264
|
+
let table = rowTables[i];
|
|
265
|
+
if (!table)
|
|
266
|
+
table = rowTables[i] = new Float64Array(tableSize);
|
|
267
|
+
if (rowOf[i] !== y) {
|
|
268
|
+
const j = cellY[y], fy = weightY[y], a = tableAt(i, j), b = tableAt(i, j + 1);
|
|
269
|
+
for (let e = 0; e < tableSize; e++)
|
|
270
|
+
table[e] = (1 - fy) * a[e] + fy * b[e];
|
|
271
|
+
rowOf[i] = y;
|
|
272
|
+
}
|
|
273
|
+
return table;
|
|
274
|
+
};
|
|
284
275
|
const site = (x, y) => {
|
|
285
276
|
const c = channelAt(x, y);
|
|
286
|
-
let
|
|
277
|
+
let r0, r1, fx;
|
|
287
278
|
if (input.exactKernels) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
b00 = 1;
|
|
291
|
-
b10 = b01 = b11 = 0;
|
|
279
|
+
r0 = r1 = foldKernels((x + 0.5 - W / 2) * mmPerSite, -(y + 0.5 - H / 2) * mmPerSite);
|
|
280
|
+
fx = 0;
|
|
292
281
|
}
|
|
293
282
|
else {
|
|
294
|
-
const i = cellX[x]
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
cellJ = j;
|
|
299
|
-
c00 = tableAt(i, j);
|
|
300
|
-
c10 = tableAt(i + 1, j);
|
|
301
|
-
c01 = tableAt(i, j + 1);
|
|
302
|
-
c11 = tableAt(i + 1, j + 1);
|
|
303
|
-
}
|
|
304
|
-
t00 = c00;
|
|
305
|
-
t10 = c10;
|
|
306
|
-
t01 = c01;
|
|
307
|
-
t11 = c11;
|
|
308
|
-
b00 = (1 - fx) * (1 - fy);
|
|
309
|
-
b10 = fx * (1 - fy);
|
|
310
|
-
b01 = (1 - fx) * fy;
|
|
311
|
-
b11 = fx * fy;
|
|
283
|
+
const i = cellX[x];
|
|
284
|
+
r0 = rowTable(i, y);
|
|
285
|
+
r1 = rowTable(i + 1, y);
|
|
286
|
+
fx = weightX[x];
|
|
312
287
|
}
|
|
313
288
|
const inside = ww > 0 && x - reachLeft >= wx0 && y - reachUp >= wy0 && x + reachRight < wx0 + ww && y + reachDown < wy0 + wh;
|
|
314
|
-
const channelBase = c *
|
|
315
|
-
|
|
289
|
+
const channelBase = c * taps * nodes;
|
|
290
|
+
// Each tap's signal is integrated from its material's spectrum against the two row-blended kernels per node; the blend along x
|
|
291
|
+
// is linear, so it is applied to the two sums.
|
|
292
|
+
let s0 = 0, s1 = 0;
|
|
316
293
|
for (let ky = 0, k = 0; ky < kh; ky++) {
|
|
317
294
|
const q = y + ky - cy;
|
|
318
295
|
for (let kx = 0; kx < kw; kx++, k++) {
|
|
@@ -330,12 +307,22 @@ export function createCatalogueLensSignal(given) {
|
|
|
330
307
|
}
|
|
331
308
|
if (f === 0)
|
|
332
309
|
continue;
|
|
333
|
-
const
|
|
334
|
-
|
|
335
|
-
|
|
310
|
+
const L = spectra[m], at = channelBase + k * nodes;
|
|
311
|
+
let w0 = 0, w1 = 0;
|
|
312
|
+
if (fx === 0)
|
|
313
|
+
for (let n = 0; n < nodes; n++)
|
|
314
|
+
w0 += r0[at + n] * L[n];
|
|
315
|
+
else
|
|
316
|
+
for (let n = 0; n < nodes; n++) {
|
|
317
|
+
const l = L[n];
|
|
318
|
+
w0 += r0[at + n] * l;
|
|
319
|
+
w1 += r1[at + n] * l;
|
|
320
|
+
}
|
|
321
|
+
s0 += f * w0;
|
|
322
|
+
s1 += f * w1;
|
|
336
323
|
}
|
|
337
324
|
}
|
|
338
|
-
return
|
|
325
|
+
return fx === 0 ? s0 : (1 - fx) * s0 + fx * s1;
|
|
339
326
|
};
|
|
340
327
|
return {
|
|
341
328
|
window, site,
|
|
@@ -349,7 +336,7 @@ export function catalogueFastAssumptions(run) {
|
|
|
349
336
|
return [{ id: "raw.expectedSignal", value: { method: "tabulated-expected-electrons-catalogue-psf-field-grid", verificationSites: 4, tolerance,
|
|
350
337
|
verificationRelativeDifference: run.verificationRelativeDifference },
|
|
351
338
|
explanation: "Each native site's expected photo electrons are tabulated: the sum over the PSF taps of the field factor (vignetting × distortion area factor) times the " +
|
|
352
|
-
"grid-blended kernel
|
|
339
|
+
"grid-blended kernel per spectral node times the channel response, integrated with each tap's material spectrum (see fast.lens.catalogue). The sensor stage (dark charge, Poisson draws, read noise, ADC) " +
|
|
353
340
|
"is the native one with each site's own seeds. One site of each CFA phase ran the exact chain (the environment PSF quadrature): its readout was reproduced bit for bit and " +
|
|
354
341
|
"its electrons agreed with the fast ones within the tolerance (verificationRelativeDifference: the largest difference there)." },
|
|
355
342
|
{ id: "fast.lens.catalogue", value: { method: "field-grid-bilinear-psf", gridColumns: run.columns, gridRows: run.rows, gridPointsResolved: run.resolved,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"catalogue-lens-signal.js","sourceRoot":"","sources":["../../src/facade/catalogue-lens-signal.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * #568: the fast method of `simulateCapture`'s `lens.catalogue` (requested with `fast: true` or `lens.catalogue.fast`): each\n * native site's expected photo electrons from tables instead of the environment PSF quadrature's per-tap scene queries.\n * Loaded on demand with the catalogue (`generic-lens-capture.ts`), so a capture without a catalogue lens never loads it.\n *\n * What the exact method computes. The environment PSF quadrature forms, for site q of CFA channel c,\n * μ_c(q) = Σ_λ A_c(λ) Σ_k K_{q,λ}(k)·F(s_k)·L(D(s_k), λ), s_k = q + (c_x − k_x, k_y − c_y)·p,\n * with K_{q,λ} the catalogue PSF resolved at the site's own field point (`resolveLensSampledPsfKernel`: the profile's 5 × 5 field\n * nodes × 5 wavelength nodes interpolated with centroid alignment; row index k_y increases with image +Y, `sensorPsfSourcePoint`),\n * F = T·A the field factor at the tap point (vignetting throughput × distortion area factor), D the scene ray (inverse radial\n * distortion, then the rectilinear or equisolid projection at the thin-lens image distance) and A_c(λ) the electrons per unit\n * spectral radiance on the axis (the tabulated path's channel response). Every tap of a site-centre query is a lattice point.\n *\n * The approximation (the only one). Resolving K_{q,λ} at every site and spectral node is what makes the exact method slow, so the\n * fast method resolves it exactly on a field grid and blends it bilinearly between the four grid points around each site:\n * K̃_{q,λ} = Σ_g b_g(q)·K_{g,λ}.\n * The grid subdivides each interval between the profile's field nodes into at least GRID_MINIMUM_SUBDIVISIONS cells, and into\n * more where the node kernels' intensity centroids move by more than GRID_CENTROID_STEP_SITES sites across the interval (lateral\n * colour moves the PSF across the frame; a blend of two shifted kernels is not the shifted kernel). Since the blend is linear,\n * the channel response and the material radiance fold into the grid kernels once per grid point:\n * μ̃_c(q) = Σ_k F(s_k)·Σ_g b_g(q)·W_{g,c,m(s_k)}(k), W_{g,c,m}(k) = Σ_λ A_c(λ)·L_m(λ)·K_{g,λ}(k),\n * so a site costs (2R + 1)² taps instead of (2R + 1)² scene queries per spectral node. Its error against the exact method is\n * bounded by ‖K̃ − K‖₁ times the brightest tap's signal; tests measure both (test/simulate-capture-lens-catalogue-fast.test.ts).\n *\n * The scene points. The mapping is the environment's (`undistortedImagePoint`, `lensProjectionDirectionValue`, the planar\n * provider's plane point), with the radial inverse solved by Newton's method instead of bisection: both converge to the same\n * root to rounding. Outside a circular fisheye's image circle (`outsideImageCircle: \"backward-ray\"`) and beyond the horizon the\n * scene radiance is zero, as in the capture.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { insideRadialImageCircle, lensProjectionDirectionValue } from \"../camera/lens-projection.js\";\nimport { resolveProjectionDistanceMm } from \"../motion/camera-rotation.js\";\nimport { prepareIlluminationVignettingThroughput, type IlluminationVignettingProfile } from \"../optics/illumination-vignetting.js\";\nimport { resolveLensSampledPsfKernel, type ResolveLensSampledPsfInput } from \"../optics/lens-psf-profile.js\";\nimport type { DisplayViewScene } from \"./expected-signal.js\";\n\n/** Fewest grid cells between neighboring profile field nodes. */\nexport const CATALOGUE_FAST_GRID_MINIMUM_SUBDIVISIONS = 4;\n/** Largest move of a node kernel's intensity centroid across one grid cell, in sites (sets finer grids where lateral color moves the PSF). */\nexport const CATALOGUE_FAST_GRID_CENTROID_STEP_SITES = 0.05;\n/**\n * Largest relative difference between the fast and the exact expected electrons that the run-time verification (one site per\n * CFA phase through the exact chain, `renderTabulatedNativeRaw`) accepts; above it the capture runs the exact method.\n */\nexport const CATALOGUE_FAST_VERIFICATION_TOLERANCE = 0.005;\n/** Largest cache window, in lattice points; a larger window is evaluated without caching. */\nconst WINDOW_LIMIT = 1 << 22;\nconst NEWTON_ITERATIONS = 60;\n\nexport interface CatalogueLensSignalInput {\n nativeWidth: number;\n nativeHeight: number;\n /** Site pitch, µm. */\n pitch: number;\n /** The catalogue PSF's optical state (profile, focal length, focus, f-number, defocus), as the site inputs' `psf.configuration.psf`. */\n psf: Omit<ResolveLensSampledPsfInput, \"fieldPointMm\" | \"wavelengthNm\">;\n /** Spectral nodes and, per CFA channel (red, green, blue), the expected electrons per unit spectral radiance at each node. */\n wavelengthsNm: readonly number[];\n electronsPerRadiance: readonly [Float64Array, Float64Array, Float64Array];\n /** The scene's chart materials (the tabulated path's material tables). */\n scene: Extract<DisplayViewScene, { kind: \"materials\" }>;\n /** CFA channel index (0 red, 1 green, 2 blue) of native site (x, y). */\n channelAt(x: number, y: number): number;\n /** The radial distortion profile (r_d = r·(1 + k1ρ² + k2ρ⁴ + k3ρ⁶), ρ = r / normalizationRadiusMm); absent without distortion. */\n distortion?: { normalizationRadiusMm: number; coefficients: { k1: number; k2: number; k3: number } };\n vignetting?: IlluminationVignettingProfile;\n projection: \"rectilinear\" | \"fisheye-equisolid-angle\";\n /** True for a catalogue fisheye: a sensor point outside the image circle looks straight back and sees no scene. */\n outsideImageCircleSeesNothing: boolean;\n focalLengthMm: number;\n focusDistanceM: number;\n /** The planar provider's plane distance and frame-unit scale. */\n distanceM: number;\n metresPerFrameUnit: number;\n /** Internal test hook: resolve every site's kernels exactly (the exact method's sum, slowly) instead of blending the grid. */\n exactKernels?: boolean;\n}\n\nexport interface CatalogueLensSignal {\n /** Starts a cache window of native sites [x0, x1) × [y0, y1) (see `DisplayViewExpectedSignal.window`). */\n window(x0: number, y0: number, x1: number, y1: number): void;\n /** Expected photo electrons of native site (x, y) for its own CFA channel. */\n site(x: number, y: number): number;\n /** Scene material lookups so far. */\n readonly lookups: number;\n /** Grid points per axis (columns, rows) and how many were resolved so far. */\n readonly grid: { columns: number; rows: number; resolved: number };\n}\n\n/** A kernel's intensity centroid, in samples. */\nfunction centroid(values: readonly number[], width: number): { x: number; y: number } {\n let sum = 0, x = 0, y = 0;\n for (let i = 0; i < values.length; i++) { const v = values[i]!; sum += v; x += v * (i % width); y += v * Math.floor(i / width); }\n return { x: x / sum, y: y / sum };\n}\n\n/**\n * The grid axes: each interval between neighboring profile field nodes split into max(GRID_MINIMUM_SUBDIVISIONS,\n * ⌈Δc / GRID_CENTROID_STEP_SITES⌉) equal cells, Δc the largest centroid move between the interval's two nodes over the other\n * axis's nodes and the wavelength nodes.\n */\nexport function catalogueFastGridAxes(psf: CatalogueLensSignalInput[\"psf\"]): { x: number[]; y: number[] } {\n const profile = psf.profile, axes = profile.axes, fx = axes.fieldXmm, fy = axes.fieldYmm, wl = axes.wavelengthNm;\n const centroids = new Map<string, { x: number; y: number }>();\n for (const node of profile.nodes) {\n const c = node.coordinate;\n centroids.set(c.fieldXmm + \":\" + c.fieldYmm + \":\" + c.wavelengthNm, centroid(node.kernel.normalizedIntensity, node.kernel.widthSamples));\n }\n const at = (x: number, y: number, nm: number): { x: number; y: number } => {\n const c = centroids.get(x + \":\" + y + \":\" + nm);\n if (!c) throw new InvalidConfigurationError(\"lens.catalogue fast: the PSF profile lacks a field node.\");\n return c;\n };\n const axis = (own: readonly number[], other: readonly number[], along: boolean): number[] => {\n const out: number[] = [];\n for (let i = 0; i + 1 < own.length; i++) {\n let move = 0;\n for (const o of other) for (const nm of wl) {\n const a = along ? at(own[i]!, o, nm) : at(o, own[i]!, nm), b = along ? at(own[i + 1]!, o, nm) : at(o, own[i + 1]!, nm);\n move = Math.max(move, Math.hypot(a.x - b.x, a.y - b.y));\n }\n const cells = Math.max(CATALOGUE_FAST_GRID_MINIMUM_SUBDIVISIONS, Math.ceil(move / CATALOGUE_FAST_GRID_CENTROID_STEP_SITES - 1e-9));\n for (let k = 0; k < cells; k++) out.push(own[i]! + (own[i + 1]! - own[i]!) * k / cells);\n }\n out.push(own[own.length - 1]!);\n return out;\n };\n return { x: axis(fx, fy, true), y: axis(fy, fx, false) };\n}\n\n/** The cell [i, i + 1] of an ascending axis holding v (clamped to the ends) and v's weight toward i + 1. */\nfunction cellOf(axis: readonly number[], v: number): { i: number; f: number } {\n let lo = 0, hi = axis.length - 2;\n while (lo < hi) { const mid = (lo + hi + 1) >> 1; if (axis[mid]! <= v) lo = mid; else hi = mid - 1; }\n const f = (v - axis[lo]!) / (axis[lo + 1]! - axis[lo]!);\n return { i: lo, f: Math.min(1, Math.max(0, f)) };\n}\n\n/**\n * Internal test hook (never set by the engine): `exactKernels` makes the facade's signals resolve every site's kernels exactly;\n * `onInput` receives each input the facade builds, so a test can compare the grid blend with the exact sum site by site.\n */\nexport interface CatalogueLensSignalTestHook { exactKernels?: boolean; onInput?(input: CatalogueLensSignalInput): void }\nlet testHook: CatalogueLensSignalTestHook | undefined;\nexport function setCatalogueLensSignalTestHook(hook: CatalogueLensSignalTestHook | undefined): void { testHook = hook; }\n\n/** The fast method's expected signal (see the module comment). */\nexport function createCatalogueLensSignal(given: CatalogueLensSignalInput): CatalogueLensSignal {\n testHook?.onInput?.(given);\n const input = testHook?.exactKernels ? { ...given, exactKernels: true } : given;\n const { nativeWidth: W, nativeHeight: H, psf, wavelengthsNm, electronsPerRadiance: A, scene, channelAt } = input;\n const mmPerSite = input.pitch / 1000, nodes = wavelengthsNm.length;\n const first = psf.profile.nodes[0];\n if (!first) throw new InvalidConfigurationError(\"lens.catalogue fast: the PSF profile has no nodes.\");\n const kw = first.kernel.widthSamples, kh = first.kernel.heightSamples, cx = first.kernel.centerSampleX, cy = first.kernel.centerSampleY, taps = kw * kh;\n for (const node of psf.profile.nodes) if (node.kernel.widthSamples !== kw || node.kernel.heightSamples !== kh || node.kernel.centerSampleX !== cx || node.kernel.centerSampleY !== cy)\n throw new InvalidConfigurationError(\"lens.catalogue fast: the PSF profile's kernels differ in size.\");\n // Lattice reach of the taps around a site: x from −cx … kw − 1 − cx (as c_x − k_x), y from −cy … kh − 1 − cy.\n const reachLeft = kw - 1 - cx, reachRight = cx, reachUp = cy, reachDown = kh - 1 - cy;\n // Materials: neutral ones by reflectance (class 0, the white radiance), spectral ones by class 1 + index.\n const M = scene.materials.length, reflectance = new Float64Array(M), materialClass = new Int32Array(M);\n const spectral: Float64Array[] = [];\n scene.materials.forEach((m, index) => { if (\"neutral\" in m) reflectance[index] = m.neutral; else { materialClass[index] = 1 + spectral.length; spectral.push(m.radiance); } });\n const classes = 1 + spectral.length, classRadiance = [scene.whiteRadiance, ...spectral];\n const tableSize = 3 * classes * taps;\n // W_{c,m}(k) = Σ_λ A_c(λ)·L_m(λ)·K_λ(k) for the kernels of one field point.\n const foldKernels = (fieldXmm: number, fieldYmm: number): Float64Array => {\n const table = new Float64Array(tableSize);\n for (let n = 0; n < nodes; n++) {\n const kernel = resolveLensSampledPsfKernel({ ...psf, wavelengthNm: wavelengthsNm[n]!, fieldPointMm: { x: fieldXmm, y: fieldYmm } }).normalizedIntensity;\n if (kernel.length !== taps) throw new InvalidConfigurationError(\"lens.catalogue fast: a resolved kernel differs in size.\");\n for (let c = 0; c < 3; c++) {\n const a = A[c]![n]!;\n if (a === 0) continue;\n for (let cl = 0; cl < classes; cl++) {\n const weight = a * classRadiance[cl]![n]!, base = (c * classes + cl) * taps;\n if (weight === 0) continue;\n for (let k = 0; k < taps; k++) table[base + k] = table[base + k]! + weight * kernel[k]!;\n }\n }\n }\n return table;\n };\n // The field grid, resolved lazily (a region render resolves only the grid points around it).\n const axes = catalogueFastGridAxes(psf), gx = axes.x, gy = axes.y, GX = gx.length;\n const tables: (Float64Array | undefined)[] = new Array(GX * gy.length);\n let resolved = 0;\n const tableAt = (i: number, j: number): Float64Array => {\n const k = j * GX + i;\n let t = tables[k];\n if (!t) { t = foldKernels(gx[i]!, gy[j]!); tables[k] = t; resolved++; }\n return t;\n };\n // Each native column's and row's grid cell and blend weight (field +Y up: row y sits at −(y + ½ − H/2)·p).\n const cellX = new Int32Array(W), weightX = new Float64Array(W), cellY = new Int32Array(H), weightY = new Float64Array(H);\n for (let x = 0; x < W; x++) { const c = cellOf(gx, (x + 0.5 - W / 2) * mmPerSite); cellX[x] = c.i; weightX[x] = c.f; }\n for (let y = 0; y < H; y++) { const c = cellOf(gy, -(y + 0.5 - H / 2) * mmPerSite); cellY[y] = c.i; weightY[y] = c.f; }\n\n // The scene point and field factor of a native sensor point (mm, +Y down); factor 0 where the scene radiance is zero.\n const pd = resolveProjectionDistanceMm(input.focalLengthMm, input.focusDistanceM).projectionDistanceMm;\n const planeScale = input.distanceM / input.metresPerFrameUnit;\n const throughputAt = input.vignetting ? prepareIlluminationVignettingThroughput(input.vignetting) : undefined;\n const distortion = input.distortion, rectilinear = input.projection === \"rectilinear\";\n let mu = 0, mv = 0, mf = 0;\n const map = (xMm: number, yMm: number): void => {\n const imageX = xMm, imageY = -yMm;\n let idealX = imageX, idealY = imageY, area = 1;\n if (distortion) {\n const { k1, k2, k3 } = distortion.coefficients, norm = distortion.normalizationRadiusMm, rd = Math.hypot(imageX, imageY) / norm;\n let rho = rd;\n if (rd > 0) {\n // Newton's method on ρ·s(ρ) = ρ_d (monotonic over the profile's validated range), from ρ = ρ_d.\n let converged = false;\n for (let it = 0; it < NEWTON_ITERATIONS; it++) {\n const r2 = rho * rho, value = rho * (1 + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2) - rd, slope = 1 + 3 * k1 * r2 + 5 * k2 * r2 * r2 + 7 * k3 * r2 * r2 * r2;\n if (!(slope > 0)) break;\n const step = value / slope;\n rho -= step;\n if (Math.abs(step) <= 4 * Number.EPSILON * rho) { converged = true; break; }\n }\n if (!converged || !(rho > 0)) throw new InvalidConfigurationError(\"lens.catalogue fast: the inverse distortion did not converge.\");\n }\n const r2 = rho * rho, s = 1 + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2, derivative = 1 + 3 * k1 * r2 + 5 * k2 * r2 * r2 + 7 * k3 * r2 * r2 * r2;\n if (rd > 0) { idealX = imageX / s; idealY = imageY / s; }\n area = 1 / (s * derivative);\n if (!(area > 0) || !Number.isFinite(area)) throw new InvalidConfigurationError(\"lens.catalogue fast: the distortion area factor must stay finite and positive.\");\n }\n const factor = (throughputAt ? throughputAt({ x: imageX, y: imageY }) : 1) * area;\n if (rectilinear) { mu = idealX / pd * planeScale; mv = idealY / pd * planeScale; mf = factor; return; }\n if (input.outsideImageCircleSeesNothing && !insideRadialImageCircle(input.projection, pd, Math.hypot(idealX, idealY))) { mf = 0; return; }\n const d = lensProjectionDirectionValue(input.projection, pd, idealX, idealY);\n if (!(d.z > 0)) { mf = 0; return; }\n mu = d.x * (input.distanceM / d.z) / input.metresPerFrameUnit; mv = d.y * (input.distanceM / d.z) / input.metresPerFrameUnit; mf = factor;\n };\n let lookups = 0;\n let lm = 0, lf = 0;\n /** The material and field factor of lattice point (p, q) (sites from the top-left site, the lattice formula). */\n const lattice = (p: number, q: number): void => {\n map((p + 0.5 - W / 2) * mmPerSite, (q + 0.5 - H / 2) * mmPerSite);\n lf = mf;\n if (mf === 0) { lm = 0; return; }\n const m = scene.materialAt(mu, mv);\n if (!(m >= 0 && m < M) || !Number.isInteger(m)) throw new InvalidConfigurationError(\"Unknown chart material.\");\n lm = m; lookups++;\n };\n // The window's lattice: points [wx0, wx0 + ww) × [wy0, wy0 + wh).\n let wx0 = 0, wy0 = 0, ww = 0, wh = 0, winMaterial = new Int32Array(0), winFactor = new Float64Array(0);\n const window = (x0: number, y0: number, x1: number, y1: number): void => {\n const w = x1 - x0 + reachLeft + reachRight, h = y1 - y0 + reachUp + reachDown;\n if (!(w > 0 && h > 0) || w * h > WINDOW_LIMIT) { ww = 0; wh = 0; return; }\n wx0 = x0 - reachLeft; wy0 = y0 - reachUp; ww = w; wh = h;\n if (winMaterial.length < w * h) { winMaterial = new Int32Array(w * h); winFactor = new Float64Array(w * h); }\n for (let q = 0; q < h; q++) for (let p = 0; p < w; p++) { lattice(wx0 + p, wy0 + q); winMaterial[q * w + p] = lm; winFactor[q * w + p] = lf; }\n };\n const kernelOf = new Float64Array(tableSize);\n let cellI = -1, cellJ = -1, c00: Float64Array = kernelOf, c10: Float64Array = kernelOf, c01: Float64Array = kernelOf, c11: Float64Array = kernelOf;\n const site = (x: number, y: number): number => {\n const c = channelAt(x, y);\n let t00: Float64Array, t10: Float64Array, t01: Float64Array, t11: Float64Array, b00: number, b10: number, b01: number, b11: number;\n if (input.exactKernels) {\n kernelOf.set(foldKernels((x + 0.5 - W / 2) * mmPerSite, -(y + 0.5 - H / 2) * mmPerSite));\n t00 = t10 = t01 = t11 = kernelOf; b00 = 1; b10 = b01 = b11 = 0;\n } else {\n const i = cellX[x]!, j = cellY[y]!, fx = weightX[x]!, fy = weightY[y]!;\n // Neighboring sites share a cell: its four tables are kept from the last site.\n if (i !== cellI || j !== cellJ) { cellI = i; cellJ = j; c00 = tableAt(i, j); c10 = tableAt(i + 1, j); c01 = tableAt(i, j + 1); c11 = tableAt(i + 1, j + 1); }\n t00 = c00; t10 = c10; t01 = c01; t11 = c11;\n b00 = (1 - fx) * (1 - fy); b10 = fx * (1 - fy); b01 = (1 - fx) * fy; b11 = fx * fy;\n }\n const inside = ww > 0 && x - reachLeft >= wx0 && y - reachUp >= wy0 && x + reachRight < wx0 + ww && y + reachDown < wy0 + wh;\n const channelBase = c * classes * taps;\n let sum = 0;\n for (let ky = 0, k = 0; ky < kh; ky++) {\n const q = y + ky - cy;\n for (let kx = 0; kx < kw; kx++, k++) {\n const p = x + cx - kx;\n let m: number, f: number;\n if (inside) { const at = (q - wy0) * ww + p - wx0; m = winMaterial[at]!; f = winFactor[at]!; } else { lattice(p, q); m = lm; f = lf; }\n if (f === 0) continue;\n const cl = materialClass[m]!, at = channelBase + cl * taps + k;\n const w = b00 * t00[at]! + b10 * t10[at]! + b01 * t01[at]! + b11 * t11[at]!;\n sum += f * (cl === 0 ? reflectance[m]! * w : w);\n }\n }\n return sum;\n };\n return {\n window, site,\n get lookups(): number { return lookups; },\n get grid(): { columns: number; rows: number; resolved: number } { return { columns: GX, rows: gy.length, resolved }; }\n };\n}\n\n/** The `raw.expectedSignal` and `fast.lens.catalogue` assumptions of a capture the fast method rendered (kept here, out of the capture bundle's entry). */\nexport function catalogueFastAssumptions(run: { columns: number; rows: number; resolved: number; verificationRelativeDifference: number }):\n { id: string; value: Record<string, unknown>; explanation: string }[] {\n const tolerance = CATALOGUE_FAST_VERIFICATION_TOLERANCE, cells = CATALOGUE_FAST_GRID_MINIMUM_SUBDIVISIONS, step = CATALOGUE_FAST_GRID_CENTROID_STEP_SITES;\n return [{ id: \"raw.expectedSignal\", value: { method: \"tabulated-expected-electrons-catalogue-psf-field-grid\", verificationSites: 4, tolerance,\n verificationRelativeDifference: run.verificationRelativeDifference },\n explanation: \"Each native site's expected photo electrons are tabulated: the sum over the PSF taps of the field factor (vignetting × distortion area factor) times the \" +\n \"grid-blended kernel folded with the channel response and the material radiance (see fast.lens.catalogue). The sensor stage (dark charge, Poisson draws, read noise, ADC) \" +\n \"is the native one with each site's own seeds. One site of each CFA phase ran the exact chain (the environment PSF quadrature): its readout was reproduced bit for bit and \" +\n \"its electrons agreed with the fast ones within the tolerance (verificationRelativeDifference: the largest difference there).\" },\n { id: \"fast.lens.catalogue\", value: { method: \"field-grid-bilinear-psf\", gridColumns: run.columns, gridRows: run.rows, gridPointsResolved: run.resolved,\n minimumSubdivisions: cells, centroidStepSites: step, verificationTolerance: tolerance },\n explanation: \"Fast method (#568), an approximation of the exact one: the catalogue PSF is resolved exactly on a field grid (each interval between the profile's field nodes \" +\n \"split into at least \" + cells + \" cells, more where the node kernels' centroids move by over \" + step + \" site per cell), and each site takes the bilinear blend of its \" +\n \"four grid points' kernels instead of its own. Its error is at most the kernels' L1 difference times the brightest tap's signal; docs/LENS_CATALOGUE.md (\\\"Fast method\\\") \" +\n \"records it measured against the exact method. Distortion, vignetting and projection are the exact method's (the radial inverse by Newton's method, equal to rounding).\" }];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"catalogue-lens-signal.js","sourceRoot":"","sources":["../../src/facade/catalogue-lens-signal.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * #568: the fast method of `simulateCapture`'s `lens.catalogue` (requested with `fast: true` or `lens.catalogue.fast`): each\n * native site's expected photo electrons from tables instead of the environment PSF quadrature's per-tap scene queries.\n * Loaded on demand with the catalogue (`generic-lens-capture.ts`), so a capture without a catalogue lens never loads it.\n *\n * What the exact method computes. The environment PSF quadrature forms, for site q of CFA channel c,\n * μ_c(q) = Σ_λ A_c(λ) Σ_k K_{q,λ}(k)·F(s_k)·L(D(s_k), λ), s_k = q + (c_x − k_x, k_y − c_y)·p,\n * with K_{q,λ} the catalogue PSF resolved at the site's own field point (`resolveLensSampledPsfKernel`: the profile's 5 × 5 field\n * nodes × 5 wavelength nodes interpolated with centroid alignment; row index k_y increases with image +Y, `sensorPsfSourcePoint`),\n * F = T·A the field factor at the tap point (vignetting throughput × distortion area factor), D the scene ray (inverse radial\n * distortion, then the rectilinear or equisolid projection at the thin-lens image distance) and A_c(λ) the electrons per unit\n * spectral radiance on the axis (the tabulated path's channel response). Every tap of a site-centre query is a lattice point.\n *\n * The approximation (the only one). Resolving K_{q,λ} at every site and spectral node is what makes the exact method slow, so the\n * fast method resolves it exactly on a field grid and blends it bilinearly between the four grid points around each site:\n * K̃_{q,λ} = Σ_g b_g(q)·K_{g,λ}.\n * The grid subdivides each interval between the profile's field nodes into at least GRID_MINIMUM_SUBDIVISIONS cells, and into\n * more where the node kernels' intensity centroids move by more than GRID_CENTROID_STEP_SITES sites across the interval (lateral\n * colour moves the PSF across the frame; a blend of two shifted kernels is not the shifted kernel). Since the blend is linear,\n * the channel response folds into the grid kernels once per grid point and node, and each tap's signal is integrated from its\n * material's spectrum when the site is read (2.21.0; no material is folded into a kernel):\n * μ̃_c(q) = Σ_k F(s_k)·Σ_λ L_{m(s_k)}(λ)·Σ_g b_g(q)·C_{g,c}(k, λ), C_{g,c}(k, λ) = A_c(λ)·K_{g,λ}(k),\n * the blend taken along y once per grid column and native row, so a site costs (2R + 1)² taps of N nodes each instead of\n * (2R + 1)² scene queries per spectral node. Its error against the exact method is\n * bounded by ‖K̃ − K‖₁ times the brightest tap's signal; tests measure both (test/simulate-capture-lens-catalogue-fast.test.ts).\n *\n * The scene points. The mapping is the environment's (`undistortedImagePoint`, `lensProjectionDirectionValue`, the planar\n * provider's plane point), with the radial inverse solved by Newton's method instead of bisection: both converge to the same\n * root to rounding. Outside a circular fisheye's image circle (`outsideImageCircle: \"backward-ray\"`) and beyond the horizon the\n * scene radiance is zero, as in the capture.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { insideRadialImageCircle, lensProjectionDirectionValue } from \"../camera/lens-projection.js\";\nimport { resolveProjectionDistanceMm } from \"../motion/camera-rotation.js\";\nimport { prepareIlluminationVignettingThroughput, type IlluminationVignettingProfile } from \"../optics/illumination-vignetting.js\";\nimport { kernelCentroid, resolveLensSampledPsfKernel, type ResolveLensSampledPsfInput } from \"../optics/lens-psf-profile.js\";\nimport type { DisplayViewScene } from \"./expected-signal.js\";\n\n/** Fewest grid cells between neighboring profile field nodes (10 since #589: with 4, 0.4 % of quick frames failed the run-time check). */\nexport const CATALOGUE_FAST_GRID_MINIMUM_SUBDIVISIONS = 10;\n/** Largest move of a node kernel's intensity centroid across one grid cell, in sites (sets finer grids where lateral color moves the PSF). */\nexport const CATALOGUE_FAST_GRID_CENTROID_STEP_SITES = 0.05;\n/**\n * Largest relative difference between the fast and the exact expected electrons that the run-time verification (one site per\n * CFA phase through the exact chain, `renderTabulatedNativeRaw`) accepts; above it the capture runs the exact method.\n */\nexport const CATALOGUE_FAST_VERIFICATION_TOLERANCE = 0.005;\n/** Largest cache window, in lattice points; a larger window is evaluated without caching. */\nconst WINDOW_LIMIT = 1 << 22;\nconst NEWTON_ITERATIONS = 60;\n\nexport interface CatalogueLensSignalInput {\n nativeWidth: number;\n nativeHeight: number;\n /** Site pitch, µm. */\n pitch: number;\n /** The catalogue PSF's optical state (profile, focal length, focus, f-number, defocus), as the site inputs' `psf.configuration.psf`. */\n psf: Omit<ResolveLensSampledPsfInput, \"fieldPointMm\" | \"wavelengthNm\">;\n /** Spectral nodes and, per CFA channel (red, green, blue), the expected electrons per unit spectral radiance at each node. */\n wavelengthsNm: readonly number[];\n electronsPerRadiance: readonly [Float64Array, Float64Array, Float64Array];\n /** The scene's chart materials (the tabulated path's material tables). */\n scene: Extract<DisplayViewScene, { kind: \"materials\" }>;\n /** CFA channel index (0 red, 1 green, 2 blue) of native site (x, y). */\n channelAt(x: number, y: number): number;\n /** The radial distortion profile (r_d = r·(1 + k1ρ² + k2ρ⁴ + k3ρ⁶), ρ = r / normalizationRadiusMm); absent without distortion. */\n distortion?: { normalizationRadiusMm: number; coefficients: { k1: number; k2: number; k3: number } };\n vignetting?: IlluminationVignettingProfile;\n projection: \"rectilinear\" | \"fisheye-equisolid-angle\";\n /** True for a catalogue fisheye: a sensor point outside the image circle looks straight back and sees no scene. */\n outsideImageCircleSeesNothing: boolean;\n focalLengthMm: number;\n focusDistanceM: number;\n /** The planar provider's plane distance and frame-unit scale. */\n distanceM: number;\n metresPerFrameUnit: number;\n /** Internal test hook: resolve every site's kernels exactly (the exact method's sum, slowly) instead of blending the grid. */\n exactKernels?: boolean;\n}\n\nexport interface CatalogueLensSignal {\n /** Starts a cache window of native sites [x0, x1) × [y0, y1) (see `DisplayViewExpectedSignal.window`). */\n window(x0: number, y0: number, x1: number, y1: number): void;\n /** Expected photo electrons of native site (x, y) for its own CFA channel. */\n site(x: number, y: number): number;\n /** Scene material lookups so far. */\n readonly lookups: number;\n /** Grid points per axis (columns, rows) and how many were resolved so far. */\n readonly grid: { columns: number; rows: number; resolved: number };\n}\n\n/**\n * The grid axes: each interval between neighboring profile field nodes split into max(GRID_MINIMUM_SUBDIVISIONS,\n * ⌈Δc / GRID_CENTROID_STEP_SITES⌉) equal cells, Δc the largest centroid move between the interval's two nodes over the other\n * axis's nodes and the wavelength nodes.\n */\nexport function catalogueFastGridAxes(psf: CatalogueLensSignalInput[\"psf\"]): { x: number[]; y: number[] } {\n const profile = psf.profile, axes = profile.axes, fx = axes.fieldXmm, fy = axes.fieldYmm, wl = axes.wavelengthNm;\n const centroids = new Map<string, { x: number; y: number }>();\n for (const node of profile.nodes) {\n const c = node.coordinate;\n centroids.set(c.fieldXmm + \":\" + c.fieldYmm + \":\" + c.wavelengthNm, kernelCentroid(node.kernel));\n }\n const at = (x: number, y: number, nm: number): { x: number; y: number } => {\n const c = centroids.get(x + \":\" + y + \":\" + nm);\n if (!c) throw new InvalidConfigurationError(\"lens.catalogue fast: the PSF profile lacks a field node.\");\n return c;\n };\n const axis = (own: readonly number[], other: readonly number[], along: boolean): number[] => {\n const out: number[] = [];\n for (let i = 0; i + 1 < own.length; i++) {\n let move = 0;\n for (const o of other) for (const nm of wl) {\n const a = along ? at(own[i]!, o, nm) : at(o, own[i]!, nm), b = along ? at(own[i + 1]!, o, nm) : at(o, own[i + 1]!, nm);\n move = Math.max(move, Math.hypot(a.x - b.x, a.y - b.y));\n }\n const cells = Math.max(CATALOGUE_FAST_GRID_MINIMUM_SUBDIVISIONS, Math.ceil(move / CATALOGUE_FAST_GRID_CENTROID_STEP_SITES - 1e-9));\n for (let k = 0; k < cells; k++) out.push(own[i]! + (own[i + 1]! - own[i]!) * k / cells);\n }\n out.push(own[own.length - 1]!);\n return out;\n };\n return { x: axis(fx, fy, true), y: axis(fy, fx, false) };\n}\n\n/** The cell [i, i + 1] of an ascending axis holding v (clamped to the ends) and v's weight toward i + 1. */\nfunction cellOf(axis: readonly number[], v: number): { i: number; f: number } {\n let lo = 0, hi = axis.length - 2;\n while (lo < hi) { const mid = (lo + hi + 1) >> 1; if (axis[mid]! <= v) lo = mid; else hi = mid - 1; }\n const f = (v - axis[lo]!) / (axis[lo + 1]! - axis[lo]!);\n return { i: lo, f: Math.min(1, Math.max(0, f)) };\n}\n\n/**\n * Internal test hook (never set by the engine): `exactKernels` makes the facade's signals resolve every site's kernels exactly;\n * `onInput` receives each input the facade builds, so a test can compare the grid blend with the exact sum site by site.\n */\nexport interface CatalogueLensSignalTestHook { exactKernels?: boolean; onInput?(input: CatalogueLensSignalInput): void }\nlet testHook: CatalogueLensSignalTestHook | undefined;\nexport function setCatalogueLensSignalTestHook(hook: CatalogueLensSignalTestHook | undefined): void { testHook = hook; }\n\n/** The fast method's expected signal (see the module comment). */\nexport function createCatalogueLensSignal(given: CatalogueLensSignalInput): CatalogueLensSignal {\n testHook?.onInput?.(given);\n const input = testHook?.exactKernels ? { ...given, exactKernels: true } : given;\n const { nativeWidth: W, nativeHeight: H, psf, wavelengthsNm, electronsPerRadiance: A, scene, channelAt } = input;\n const mmPerSite = input.pitch / 1000, nodes = wavelengthsNm.length;\n const first = psf.profile.nodes[0];\n if (!first) throw new InvalidConfigurationError(\"lens.catalogue fast: the PSF profile has no nodes.\");\n const kw = first.kernel.widthSamples, kh = first.kernel.heightSamples, cx = first.kernel.centerSampleX, cy = first.kernel.centerSampleY, taps = kw * kh;\n for (const node of psf.profile.nodes) if (node.kernel.widthSamples !== kw || node.kernel.heightSamples !== kh || node.kernel.centerSampleX !== cx || node.kernel.centerSampleY !== cy)\n throw new InvalidConfigurationError(\"lens.catalogue fast: the PSF profile's kernels differ in size.\");\n // Lattice reach of the taps around a site: x from −cx … kw − 1 − cx (as c_x − k_x), y from −cy … kh − 1 − cy.\n const reachLeft = kw - 1 - cx, reachRight = cx, reachUp = cy, reachDown = kh - 1 - cy;\n // Each material's spectral radiance at the nodes (a neutral one: its reflectance times the white radiance). Only the spectra are\n // kept: a site's signal is integrated from its taps' material spectra when it is read, so no material is folded into a kernel.\n const M = scene.materials.length;\n const spectra = scene.materials.map((m) => (\"neutral\" in m ? Float64Array.from(scene.whiteRadiance, (w) => m.neutral * w) : Float64Array.from(m.radiance)));\n const tableSize = 3 * taps * nodes;\n // A_c(λ_n)·K_n(k) for the kernels of one field point, at ((c·taps + k)·nodes + n): the channel response per node, no material.\n const foldKernels = (fieldXmm: number, fieldYmm: number): Float64Array => {\n const table = new Float64Array(tableSize);\n for (let n = 0; n < nodes; n++) {\n const kernel = resolveLensSampledPsfKernel({ ...psf, wavelengthNm: wavelengthsNm[n]!, fieldPointMm: { x: fieldXmm, y: fieldYmm } }).normalizedIntensity;\n if (kernel.length !== taps) throw new InvalidConfigurationError(\"lens.catalogue fast: a resolved kernel differs in size.\");\n for (let c = 0; c < 3; c++) {\n const a = A[c]![n]!;\n for (let k = 0; k < taps; k++) table[(c * taps + k) * nodes + n] = a * kernel[k]!;\n }\n }\n return table;\n };\n // The field grid, resolved lazily (a region render resolves only the grid points around it).\n const axes = catalogueFastGridAxes(psf), gx = axes.x, gy = axes.y, GX = gx.length;\n const tables: (Float64Array | undefined)[] = new Array(GX * gy.length);\n let resolved = 0;\n const tableAt = (i: number, j: number): Float64Array => {\n const k = j * GX + i;\n let t = tables[k];\n if (!t) { t = foldKernels(gx[i]!, gy[j]!); tables[k] = t; resolved++; }\n return t;\n };\n // Each native column's and row's grid cell and blend weight (field +Y up: row y sits at −(y + ½ − H/2)·p).\n const cellX = new Int32Array(W), weightX = new Float64Array(W), cellY = new Int32Array(H), weightY = new Float64Array(H);\n for (let x = 0; x < W; x++) { const c = cellOf(gx, (x + 0.5 - W / 2) * mmPerSite); cellX[x] = c.i; weightX[x] = c.f; }\n for (let y = 0; y < H; y++) { const c = cellOf(gy, -(y + 0.5 - H / 2) * mmPerSite); cellY[y] = c.i; weightY[y] = c.f; }\n\n // The scene point and field factor of a native sensor point (mm, +Y down); factor 0 where the scene radiance is zero.\n const pd = resolveProjectionDistanceMm(input.focalLengthMm, input.focusDistanceM).projectionDistanceMm;\n const planeScale = input.distanceM / input.metresPerFrameUnit;\n const throughputAt = input.vignetting ? prepareIlluminationVignettingThroughput(input.vignetting) : undefined;\n const distortion = input.distortion, rectilinear = input.projection === \"rectilinear\";\n let mu = 0, mv = 0, mf = 0;\n const map = (xMm: number, yMm: number): void => {\n const imageX = xMm, imageY = -yMm;\n let idealX = imageX, idealY = imageY, area = 1;\n if (distortion) {\n const { k1, k2, k3 } = distortion.coefficients, norm = distortion.normalizationRadiusMm, rd = Math.hypot(imageX, imageY) / norm;\n let rho = rd;\n if (rd > 0) {\n // Newton's method on ρ·s(ρ) = ρ_d (monotonic over the profile's validated range), from ρ = ρ_d.\n let converged = false;\n for (let it = 0; it < NEWTON_ITERATIONS; it++) {\n const r2 = rho * rho, value = rho * (1 + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2) - rd, slope = 1 + 3 * k1 * r2 + 5 * k2 * r2 * r2 + 7 * k3 * r2 * r2 * r2;\n if (!(slope > 0)) break;\n const step = value / slope;\n rho -= step;\n if (Math.abs(step) <= 4 * Number.EPSILON * rho) { converged = true; break; }\n }\n if (!converged || !(rho > 0)) throw new InvalidConfigurationError(\"lens.catalogue fast: the inverse distortion did not converge.\");\n }\n const r2 = rho * rho, s = 1 + k1 * r2 + k2 * r2 * r2 + k3 * r2 * r2 * r2, derivative = 1 + 3 * k1 * r2 + 5 * k2 * r2 * r2 + 7 * k3 * r2 * r2 * r2;\n if (rd > 0) { idealX = imageX / s; idealY = imageY / s; }\n area = 1 / (s * derivative);\n if (!(area > 0) || !Number.isFinite(area)) throw new InvalidConfigurationError(\"lens.catalogue fast: the distortion area factor must stay finite and positive.\");\n }\n const factor = (throughputAt ? throughputAt({ x: imageX, y: imageY }) : 1) * area;\n if (rectilinear) { mu = idealX / pd * planeScale; mv = idealY / pd * planeScale; mf = factor; return; }\n if (input.outsideImageCircleSeesNothing && !insideRadialImageCircle(input.projection, pd, Math.hypot(idealX, idealY))) { mf = 0; return; }\n const d = lensProjectionDirectionValue(input.projection, pd, idealX, idealY);\n if (!(d.z > 0)) { mf = 0; return; }\n mu = d.x * (input.distanceM / d.z) / input.metresPerFrameUnit; mv = d.y * (input.distanceM / d.z) / input.metresPerFrameUnit; mf = factor;\n };\n let lookups = 0;\n let lm = 0, lf = 0;\n /** The material and field factor of lattice point (p, q) (sites from the top-left site, the lattice formula). */\n const lattice = (p: number, q: number): void => {\n map((p + 0.5 - W / 2) * mmPerSite, (q + 0.5 - H / 2) * mmPerSite);\n lf = mf;\n if (mf === 0) { lm = 0; return; }\n const m = scene.materialAt(mu, mv);\n if (!(m >= 0 && m < M) || !Number.isInteger(m)) throw new InvalidConfigurationError(\"Unknown chart material.\");\n lm = m; lookups++;\n };\n // The window's lattice: points [wx0, wx0 + ww) × [wy0, wy0 + wh).\n let wx0 = 0, wy0 = 0, ww = 0, wh = 0, winMaterial = new Int32Array(0), winFactor = new Float64Array(0);\n const window = (x0: number, y0: number, x1: number, y1: number): void => {\n const w = x1 - x0 + reachLeft + reachRight, h = y1 - y0 + reachUp + reachDown;\n if (!(w > 0 && h > 0) || w * h > WINDOW_LIMIT) { ww = 0; wh = 0; return; }\n wx0 = x0 - reachLeft; wy0 = y0 - reachUp; ww = w; wh = h;\n if (winMaterial.length < w * h) { winMaterial = new Int32Array(w * h); winFactor = new Float64Array(w * h); }\n for (let q = 0; q < h; q++) for (let p = 0; p < w; p++) { lattice(wx0 + p, wy0 + q); winMaterial[q * w + p] = lm; winFactor[q * w + p] = lf; }\n };\n // A site's kernel is the bilinear blend of its cell's four grid kernels. Blended along y first, once per grid column and native\n // row (every site of a row has the same fy): R_i(y) = (1 − fy)·T(i, j) + fy·T(i, j + 1), kept for the row the sites are on.\n const rowTables: Float64Array[] = [], rowOf = new Int32Array(GX).fill(-1);\n const rowTable = (i: number, y: number): Float64Array => {\n let table = rowTables[i];\n if (!table) table = rowTables[i] = new Float64Array(tableSize);\n if (rowOf[i] !== y) {\n const j = cellY[y]!, fy = weightY[y]!, a = tableAt(i, j), b = tableAt(i, j + 1);\n for (let e = 0; e < tableSize; e++) table[e] = (1 - fy) * a[e]! + fy * b[e]!;\n rowOf[i] = y;\n }\n return table;\n };\n const site = (x: number, y: number): number => {\n const c = channelAt(x, y);\n let r0: Float64Array, r1: Float64Array, fx: number;\n if (input.exactKernels) { r0 = r1 = foldKernels((x + 0.5 - W / 2) * mmPerSite, -(y + 0.5 - H / 2) * mmPerSite); fx = 0; }\n else { const i = cellX[x]!; r0 = rowTable(i, y); r1 = rowTable(i + 1, y); fx = weightX[x]!; }\n const inside = ww > 0 && x - reachLeft >= wx0 && y - reachUp >= wy0 && x + reachRight < wx0 + ww && y + reachDown < wy0 + wh;\n const channelBase = c * taps * nodes;\n // Each tap's signal is integrated from its material's spectrum against the two row-blended kernels per node; the blend along x\n // is linear, so it is applied to the two sums.\n let s0 = 0, s1 = 0;\n for (let ky = 0, k = 0; ky < kh; ky++) {\n const q = y + ky - cy;\n for (let kx = 0; kx < kw; kx++, k++) {\n const p = x + cx - kx;\n let m: number, f: number;\n if (inside) { const at = (q - wy0) * ww + p - wx0; m = winMaterial[at]!; f = winFactor[at]!; } else { lattice(p, q); m = lm; f = lf; }\n if (f === 0) continue;\n const L = spectra[m]!, at = channelBase + k * nodes;\n let w0 = 0, w1 = 0;\n if (fx === 0) for (let n = 0; n < nodes; n++) w0 += r0[at + n]! * L[n]!;\n else for (let n = 0; n < nodes; n++) { const l = L[n]!; w0 += r0[at + n]! * l; w1 += r1[at + n]! * l; }\n s0 += f * w0; s1 += f * w1;\n }\n }\n return fx === 0 ? s0 : (1 - fx) * s0 + fx * s1;\n };\n return {\n window, site,\n get lookups(): number { return lookups; },\n get grid(): { columns: number; rows: number; resolved: number } { return { columns: GX, rows: gy.length, resolved }; }\n };\n}\n\n/** The `raw.expectedSignal` and `fast.lens.catalogue` assumptions of a capture the fast method rendered (kept here, out of the capture bundle's entry). */\nexport function catalogueFastAssumptions(run: { columns: number; rows: number; resolved: number; verificationRelativeDifference: number }):\n { id: string; value: Record<string, unknown>; explanation: string }[] {\n const tolerance = CATALOGUE_FAST_VERIFICATION_TOLERANCE, cells = CATALOGUE_FAST_GRID_MINIMUM_SUBDIVISIONS, step = CATALOGUE_FAST_GRID_CENTROID_STEP_SITES;\n return [{ id: \"raw.expectedSignal\", value: { method: \"tabulated-expected-electrons-catalogue-psf-field-grid\", verificationSites: 4, tolerance,\n verificationRelativeDifference: run.verificationRelativeDifference },\n explanation: \"Each native site's expected photo electrons are tabulated: the sum over the PSF taps of the field factor (vignetting × distortion area factor) times the \" +\n \"grid-blended kernel per spectral node times the channel response, integrated with each tap's material spectrum (see fast.lens.catalogue). The sensor stage (dark charge, Poisson draws, read noise, ADC) \" +\n \"is the native one with each site's own seeds. One site of each CFA phase ran the exact chain (the environment PSF quadrature): its readout was reproduced bit for bit and \" +\n \"its electrons agreed with the fast ones within the tolerance (verificationRelativeDifference: the largest difference there).\" },\n { id: \"fast.lens.catalogue\", value: { method: \"field-grid-bilinear-psf\", gridColumns: run.columns, gridRows: run.rows, gridPointsResolved: run.resolved,\n minimumSubdivisions: cells, centroidStepSites: step, verificationTolerance: tolerance },\n explanation: \"Fast method (#568), an approximation of the exact one: the catalogue PSF is resolved exactly on a field grid (each interval between the profile's field nodes \" +\n \"split into at least \" + cells + \" cells, more where the node kernels' centroids move by over \" + step + \" site per cell), and each site takes the bilinear blend of its \" +\n \"four grid points' kernels instead of its own. Its error is at most the kernels' L1 difference times the brightest tap's signal; docs/LENS_CATALOGUE.md (\\\"Fast method\\\") \" +\n \"records it measured against the exact method. Distortion, vignetting and projection are the exact method's (the radial inverse by Newton's method, equal to rounding).\" }];\n}\n"]}
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
* Every value comes from the chart provider's own `radianceAt` (createPlanarSceneRadianceProvider): a
|
|
9
9
|
* Lambertian reflectance ρ(λ) under the scaled illuminant, L = ρ·k·S(λ)/π. The spectral integral of a
|
|
10
10
|
* query is Σ_j w_cj·L(u, v, λ_j), summed in wavelength order. Charts are uniform-illuminant Lambertian
|
|
11
|
-
* patterns, so a query's integral depends only on its material: the GPU stage
|
|
11
|
+
* patterns, so a query's integral depends only on its material: the GPU stage classifies the material and (2.21.0) the host
|
|
12
|
+
* integrates that material's spectrum against the weights per query.
|
|
12
13
|
*
|
|
13
14
|
* The query shape mirrors a batched provider (one call, queries in, results in the same order). Points
|
|
14
15
|
* are frame units (u right, v up; see chart-scenes.ts); `mapEnvironmentDirectionsToChartPoints` converts
|