@photivra/engine 0.1.0 → 0.4.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 +87 -0
- package/CITATION.cff +1 -1
- package/README.md +61 -12
- package/dist/camera/equivalent-focal-length.d.ts +34 -0
- package/dist/camera/equivalent-focal-length.d.ts.map +1 -0
- package/dist/camera/equivalent-focal-length.js +33 -0
- package/dist/camera/equivalent-focal-length.js.map +1 -0
- package/dist/camera/field-of-view.d.ts +40 -3
- package/dist/camera/field-of-view.d.ts.map +1 -1
- package/dist/camera/field-of-view.js +66 -24
- package/dist/camera/field-of-view.js.map +1 -1
- package/dist/core/calculation-result.d.ts.map +1 -1
- package/dist/core/calculation-result.js +14 -2
- package/dist/core/calculation-result.js.map +1 -1
- package/dist/core/configuration-error.d.ts +9 -0
- package/dist/core/configuration-error.d.ts.map +1 -0
- package/dist/core/configuration-error.js +13 -0
- package/dist/core/configuration-error.js.map +1 -0
- package/dist/core/evidence-provenance.d.ts +40 -0
- package/dist/core/evidence-provenance.d.ts.map +1 -0
- package/dist/core/evidence-provenance.js +79 -0
- package/dist/core/evidence-provenance.js.map +1 -0
- package/dist/core/image-formation.d.ts +70 -0
- package/dist/core/image-formation.d.ts.map +1 -0
- package/dist/core/image-formation.js +335 -0
- package/dist/core/image-formation.js.map +1 -0
- package/dist/exposure/exposure.d.ts +31 -0
- package/dist/exposure/exposure.d.ts.map +1 -1
- package/dist/exposure/exposure.js +35 -0
- package/dist/exposure/exposure.js.map +1 -1
- package/dist/index.d.ts +16 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -4
- package/dist/index.js.map +1 -1
- package/dist/motion/camera-rotation.d.ts +91 -0
- package/dist/motion/camera-rotation.d.ts.map +1 -0
- package/dist/motion/camera-rotation.js +157 -0
- package/dist/motion/camera-rotation.js.map +1 -0
- package/dist/optics/focus-breathing.d.ts +73 -0
- package/dist/optics/focus-breathing.d.ts.map +1 -0
- package/dist/optics/focus-breathing.js +77 -0
- package/dist/optics/focus-breathing.js.map +1 -0
- package/dist/optics/illumination-vignetting.d.ts +59 -0
- package/dist/optics/illumination-vignetting.d.ts.map +1 -0
- package/dist/optics/illumination-vignetting.js +107 -0
- package/dist/optics/illumination-vignetting.js.map +1 -0
- package/dist/optics/lateral-chromatic-aberration.d.ts +93 -0
- package/dist/optics/lateral-chromatic-aberration.d.ts.map +1 -0
- package/dist/optics/lateral-chromatic-aberration.js +174 -0
- package/dist/optics/lateral-chromatic-aberration.js.map +1 -0
- package/dist/optics/psf-foundation.d.ts +102 -0
- package/dist/optics/psf-foundation.d.ts.map +1 -0
- package/dist/optics/psf-foundation.js +164 -0
- package/dist/optics/psf-foundation.js.map +1 -0
- package/dist/optics/radial-distortion.d.ts +91 -0
- package/dist/optics/radial-distortion.d.ts.map +1 -0
- package/dist/optics/radial-distortion.js +198 -0
- package/dist/optics/radial-distortion.js.map +1 -0
- package/dist/output/capture-geometry.d.ts +225 -0
- package/dist/output/capture-geometry.d.ts.map +1 -0
- package/dist/output/capture-geometry.js +478 -0
- package/dist/output/capture-geometry.js.map +1 -0
- package/dist/schema/validation.d.ts +1 -8
- package/dist/schema/validation.d.ts.map +1 -1
- package/dist/schema/validation.js +2 -11
- package/dist/schema/validation.js.map +1 -1
- package/dist/sensor/architecture.d.ts +40 -0
- package/dist/sensor/architecture.d.ts.map +1 -0
- package/dist/sensor/architecture.js +98 -0
- package/dist/sensor/architecture.js.map +1 -0
- package/dist/sensor/radiometry-readiness.d.ts +111 -0
- package/dist/sensor/radiometry-readiness.d.ts.map +1 -0
- package/dist/sensor/radiometry-readiness.js +294 -0
- package/dist/sensor/radiometry-readiness.js.map +1 -0
- package/dist/sensor/sensor-geometry.d.ts +99 -0
- package/dist/sensor/sensor-geometry.d.ts.map +1 -0
- package/dist/sensor/sensor-geometry.js +71 -0
- package/dist/sensor/sensor-geometry.js.map +1 -0
- package/dist/simulation/poc-simulation.d.ts +93 -1
- package/dist/simulation/poc-simulation.d.ts.map +1 -1
- package/dist/simulation/poc-simulation.js +308 -24
- package/dist/simulation/poc-simulation.js.map +1 -1
- package/docs/API_STYLE.md +35 -2
- package/docs/ARCHITECTURE.md +179 -1
- package/docs/IMAGE_FORMATION.md +142 -0
- package/docs/MOTION_AND_SIGNAL.md +54 -1
- package/docs/PHYSICS_FOUNDATION.md +190 -3
- package/docs/POC_API.md +73 -7
- package/docs/PROVENANCE.md +24 -0
- package/docs/PSF_FOUNDATION.md +133 -0
- package/docs/STABILIZATION.md +17 -4
- package/docs/USAGE.md +680 -4
- package/package.json +2 -1
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { approximationResult } from "../core/calculation-result.js";
|
|
3
|
+
import { InvalidScientificInputError, requirePositiveFinite } from "../core/validation.js";
|
|
4
|
+
function requireFinite(name, value) {
|
|
5
|
+
if (!Number.isFinite(value)) {
|
|
6
|
+
throw new InvalidScientificInputError(`${name} must be finite.`);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
function throughputFromSquaredRadius(squaredRadius, coefficients) {
|
|
10
|
+
return (1 +
|
|
11
|
+
coefficients.r2 * squaredRadius +
|
|
12
|
+
coefficients.r4 * squaredRadius * squaredRadius +
|
|
13
|
+
coefficients.r6 *
|
|
14
|
+
squaredRadius *
|
|
15
|
+
squaredRadius *
|
|
16
|
+
squaredRadius);
|
|
17
|
+
}
|
|
18
|
+
function throughputCriticalSquaredRadii(coefficients) {
|
|
19
|
+
// For s = rho²:
|
|
20
|
+
// T(s) = 1 + r2*s + r4*s² + r6*s³
|
|
21
|
+
// dT/ds = r2 + 2*r4*s + 3*r6*s²
|
|
22
|
+
const a = 3 * coefficients.r6;
|
|
23
|
+
const b = 2 * coefficients.r4;
|
|
24
|
+
const c = coefficients.r2;
|
|
25
|
+
if (a === 0) {
|
|
26
|
+
return b === 0 ? [] : [-c / b];
|
|
27
|
+
}
|
|
28
|
+
const discriminant = b * b - 4 * a * c;
|
|
29
|
+
if (discriminant < 0) {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
const root = Math.sqrt(discriminant);
|
|
33
|
+
return [(-b - root) / (2 * a), (-b + root) / (2 * a)];
|
|
34
|
+
}
|
|
35
|
+
function validateProfile(profile) {
|
|
36
|
+
requirePositiveFinite("profile.normalizationRadiusMm", profile.normalizationRadiusMm);
|
|
37
|
+
requirePositiveFinite("profile.maximumNormalizedRadius", profile.maximumNormalizedRadius);
|
|
38
|
+
requireFinite("profile.coefficients.r2", profile.coefficients.r2);
|
|
39
|
+
requireFinite("profile.coefficients.r4", profile.coefficients.r4);
|
|
40
|
+
requireFinite("profile.coefficients.r6", profile.coefficients.r6);
|
|
41
|
+
const maximumSquaredRadius = profile.maximumNormalizedRadius * profile.maximumNormalizedRadius;
|
|
42
|
+
const candidates = [
|
|
43
|
+
0,
|
|
44
|
+
maximumSquaredRadius,
|
|
45
|
+
...throughputCriticalSquaredRadii(profile.coefficients).filter((value) => value > 0 && value < maximumSquaredRadius)
|
|
46
|
+
];
|
|
47
|
+
const throughputs = candidates.map((squaredRadius) => throughputFromSquaredRadius(squaredRadius, profile.coefficients));
|
|
48
|
+
const minimumThroughputFactor = Math.min(...throughputs);
|
|
49
|
+
const maximumThroughputFactor = Math.max(...throughputs);
|
|
50
|
+
if (!Number.isFinite(minimumThroughputFactor) ||
|
|
51
|
+
minimumThroughputFactor <= 0) {
|
|
52
|
+
throw new InvalidScientificInputError("Illumination vignetting profile must keep relative linear throughput strictly greater than zero over maximumNormalizedRadius.");
|
|
53
|
+
}
|
|
54
|
+
if (!Number.isFinite(maximumThroughputFactor) ||
|
|
55
|
+
maximumThroughputFactor > 1) {
|
|
56
|
+
throw new InvalidScientificInputError("Illumination vignetting profile must not amplify relative linear throughput above the optical-axis value of 1.");
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
normalizationRadiusMm: profile.normalizationRadiusMm,
|
|
60
|
+
maximumNormalizedRadius: profile.maximumNormalizedRadius,
|
|
61
|
+
coefficients: { ...profile.coefficients },
|
|
62
|
+
minimumThroughputFactor,
|
|
63
|
+
maximumThroughputFactor
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function assumptions() {
|
|
67
|
+
return [
|
|
68
|
+
"Generic rotationally symmetric illumination-vignetting approximation evaluated from ideal image-plane field radius",
|
|
69
|
+
"Relative linear throughput is normalized to 1 on the optical axis",
|
|
70
|
+
"The model attenuates scene-linear/channel-linear signal only and must be applied before display/gamma encoding",
|
|
71
|
+
"The model changes throughput only; it does not alter field coordinates, pupil shape, PSF shape, bokeh, or focus",
|
|
72
|
+
"Mechanical/pupil vignetting and cat's-eye bokeh are separate pupil/PSF effects",
|
|
73
|
+
"The profile is a generic caller-supplied radial approximation, not calibrated radiometry or a named-lens measurement"
|
|
74
|
+
];
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Calculates generic field-dependent illumination falloff as a multiplicative
|
|
78
|
+
* linear-light throughput factor.
|
|
79
|
+
*
|
|
80
|
+
* The profile is accepted only when its relative throughput remains strictly
|
|
81
|
+
* positive and never exceeds the optical-axis normalization of 1 throughout
|
|
82
|
+
* the complete declared operating envelope.
|
|
83
|
+
*/
|
|
84
|
+
export function calculateIlluminationVignetting(input) {
|
|
85
|
+
requireFinite("imagePointMm.x", input.imagePointMm.x);
|
|
86
|
+
requireFinite("imagePointMm.y", input.imagePointMm.y);
|
|
87
|
+
const profile = validateProfile(input.profile);
|
|
88
|
+
const radiusMm = Math.hypot(input.imagePointMm.x, input.imagePointMm.y);
|
|
89
|
+
const normalizedRadius = radiusMm / profile.normalizationRadiusMm;
|
|
90
|
+
if (normalizedRadius > profile.maximumNormalizedRadius) {
|
|
91
|
+
throw new InvalidScientificInputError("imagePointMm lies outside the illumination vignetting profile maximumNormalizedRadius.");
|
|
92
|
+
}
|
|
93
|
+
const squaredRadius = normalizedRadius * normalizedRadius;
|
|
94
|
+
const linearThroughputFactor = throughputFromSquaredRadius(squaredRadius, profile.coefficients);
|
|
95
|
+
const attenuationStops = linearThroughputFactor === 1
|
|
96
|
+
? 0
|
|
97
|
+
: -Math.log2(linearThroughputFactor);
|
|
98
|
+
return approximationResult({
|
|
99
|
+
imagePointMm: { ...input.imagePointMm },
|
|
100
|
+
normalizedRadius,
|
|
101
|
+
linearThroughputFactor,
|
|
102
|
+
attenuationStops,
|
|
103
|
+
profileMinimumThroughputFactor: profile.minimumThroughputFactor,
|
|
104
|
+
profileMaximumThroughputFactor: profile.maximumThroughputFactor
|
|
105
|
+
}, "generic-radial-illumination-vignetting", "1.0.0", assumptions());
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=illumination-vignetting.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"illumination-vignetting.js","sourceRoot":"","sources":["../../src/optics/illumination-vignetting.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { type CalculationResult } from "../core/calculation-result.js";
|
|
2
|
+
import { type LensFieldPointMm, type RadialDistortionCoefficients } from "./radial-distortion.js";
|
|
3
|
+
export type LateralChromaticAberrationChannel = "red" | "green" | "blue";
|
|
4
|
+
export interface LateralChromaticAberrationProfile {
|
|
5
|
+
/** Shared physical image-plane normalization radius in millimetres. */
|
|
6
|
+
normalizationRadiusMm: number;
|
|
7
|
+
/** Shared undistorted normalized-radius operating envelope. */
|
|
8
|
+
maximumNormalizedRadius: number;
|
|
9
|
+
/**
|
|
10
|
+
* Green-reference base geometric distortion.
|
|
11
|
+
*
|
|
12
|
+
* This common mapping is included in every channel. Red/blue CA terms are
|
|
13
|
+
* added to this base, so callers should not apply a second distortion pass.
|
|
14
|
+
*/
|
|
15
|
+
baseDistortionCoefficients: RadialDistortionCoefficients;
|
|
16
|
+
/** Additional red-channel radial coefficients relative to green. */
|
|
17
|
+
redCoefficientOffset: RadialDistortionCoefficients;
|
|
18
|
+
/** Additional blue-channel radial coefficients relative to green. */
|
|
19
|
+
blueCoefficientOffset: RadialDistortionCoefficients;
|
|
20
|
+
}
|
|
21
|
+
export interface CalculateLateralChromaticAberrationMappingInput {
|
|
22
|
+
/** Ideal/undistorted image-plane point shared by all channels. */
|
|
23
|
+
imagePointMm: LensFieldPointMm;
|
|
24
|
+
profile: LateralChromaticAberrationProfile;
|
|
25
|
+
}
|
|
26
|
+
export interface CalculateInverseLateralChromaticAberrationMappingInput {
|
|
27
|
+
/**
|
|
28
|
+
* Distorted output image-plane destination. Each output channel is inverse-
|
|
29
|
+
* mapped independently to its ideal source coordinate.
|
|
30
|
+
*/
|
|
31
|
+
distortedImagePointMm: LensFieldPointMm;
|
|
32
|
+
profile: LateralChromaticAberrationProfile;
|
|
33
|
+
}
|
|
34
|
+
export interface LateralChromaticAberrationChannelMapping {
|
|
35
|
+
mappedImagePointMm: LensFieldPointMm;
|
|
36
|
+
radialScale: number;
|
|
37
|
+
combinedCoefficients: RadialDistortionCoefficients;
|
|
38
|
+
}
|
|
39
|
+
export interface InverseLateralChromaticAberrationChannelMapping {
|
|
40
|
+
sourceImagePointMm: LensFieldPointMm;
|
|
41
|
+
radialScaleAtSource: number;
|
|
42
|
+
combinedCoefficients: RadialDistortionCoefficients;
|
|
43
|
+
}
|
|
44
|
+
export interface ChannelSeparationVectorMm {
|
|
45
|
+
x: number;
|
|
46
|
+
y: number;
|
|
47
|
+
distance: number;
|
|
48
|
+
}
|
|
49
|
+
export interface LateralChromaticAberrationSeparation {
|
|
50
|
+
redGreen: ChannelSeparationVectorMm;
|
|
51
|
+
blueGreen: ChannelSeparationVectorMm;
|
|
52
|
+
redBlue: ChannelSeparationVectorMm;
|
|
53
|
+
maximumPairDistanceMm: number;
|
|
54
|
+
}
|
|
55
|
+
export interface LateralChromaticAberrationMapping {
|
|
56
|
+
direction: "undistorted-to-channel-distorted";
|
|
57
|
+
referenceChannel: "green";
|
|
58
|
+
sourceImagePointMm: LensFieldPointMm;
|
|
59
|
+
channels: {
|
|
60
|
+
red: LateralChromaticAberrationChannelMapping;
|
|
61
|
+
green: LateralChromaticAberrationChannelMapping;
|
|
62
|
+
blue: LateralChromaticAberrationChannelMapping;
|
|
63
|
+
};
|
|
64
|
+
separation: LateralChromaticAberrationSeparation;
|
|
65
|
+
}
|
|
66
|
+
export interface InverseLateralChromaticAberrationMapping {
|
|
67
|
+
direction: "distorted-output-to-channel-sources";
|
|
68
|
+
referenceChannel: "green";
|
|
69
|
+
distortedImagePointMm: LensFieldPointMm;
|
|
70
|
+
channels: {
|
|
71
|
+
red: InverseLateralChromaticAberrationChannelMapping;
|
|
72
|
+
green: InverseLateralChromaticAberrationChannelMapping;
|
|
73
|
+
blue: InverseLateralChromaticAberrationChannelMapping;
|
|
74
|
+
};
|
|
75
|
+
sourceSeparation: LateralChromaticAberrationSeparation;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Calculates generic lateral chromatic-aberration field separation by mapping
|
|
79
|
+
* one ideal image-plane point through a shared green-reference base distortion
|
|
80
|
+
* and red/blue radial coefficient offsets.
|
|
81
|
+
*
|
|
82
|
+
* This is channel-dependent field mapping, not a blur kernel.
|
|
83
|
+
*/
|
|
84
|
+
export declare function calculateLateralChromaticAberrationMapping(input: CalculateLateralChromaticAberrationMappingInput): CalculationResult<LateralChromaticAberrationMapping>;
|
|
85
|
+
/**
|
|
86
|
+
* Inverse-maps one distorted output destination independently for the
|
|
87
|
+
* representative red/green/blue channels.
|
|
88
|
+
*
|
|
89
|
+
* Renderers can use the returned per-channel ideal source coordinates for
|
|
90
|
+
* inverse sampling without inventing chromatic-aberration equations.
|
|
91
|
+
*/
|
|
92
|
+
export declare function calculateInverseLateralChromaticAberrationMapping(input: CalculateInverseLateralChromaticAberrationMappingInput): CalculationResult<InverseLateralChromaticAberrationMapping>;
|
|
93
|
+
//# sourceMappingURL=lateral-chromatic-aberration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lateral-chromatic-aberration.d.ts","sourceRoot":"","sources":["../../src/optics/lateral-chromatic-aberration.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,4BAA4B,EAElC,MAAM,wBAAwB,CAAC;AAEhC,MAAM,MAAM,iCAAiC,GAAG,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;AAEzE,MAAM,WAAW,iCAAiC;IAChD,uEAAuE;IACvE,qBAAqB,EAAE,MAAM,CAAC;IAC9B,+DAA+D;IAC/D,uBAAuB,EAAE,MAAM,CAAC;IAChC;;;;;OAKG;IACH,0BAA0B,EAAE,4BAA4B,CAAC;IACzD,oEAAoE;IACpE,oBAAoB,EAAE,4BAA4B,CAAC;IACnD,qEAAqE;IACrE,qBAAqB,EAAE,4BAA4B,CAAC;CACrD;AAED,MAAM,WAAW,+CAA+C;IAC9D,kEAAkE;IAClE,YAAY,EAAE,gBAAgB,CAAC;IAC/B,OAAO,EAAE,iCAAiC,CAAC;CAC5C;AAED,MAAM,WAAW,sDAAsD;IACrE;;;OAGG;IACH,qBAAqB,EAAE,gBAAgB,CAAC;IACxC,OAAO,EAAE,iCAAiC,CAAC;CAC5C;AAED,MAAM,WAAW,wCAAwC;IACvD,kBAAkB,EAAE,gBAAgB,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,oBAAoB,EAAE,4BAA4B,CAAC;CACpD;AAED,MAAM,WAAW,+CAA+C;IAC9D,kBAAkB,EAAE,gBAAgB,CAAC;IACrC,mBAAmB,EAAE,MAAM,CAAC;IAC5B,oBAAoB,EAAE,4BAA4B,CAAC;CACpD;AAED,MAAM,WAAW,yBAAyB;IACxC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,oCAAoC;IACnD,QAAQ,EAAE,yBAAyB,CAAC;IACpC,SAAS,EAAE,yBAAyB,CAAC;IACrC,OAAO,EAAE,yBAAyB,CAAC;IACnC,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,iCAAiC;IAChD,SAAS,EAAE,kCAAkC,CAAC;IAC9C,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,gBAAgB,CAAC;IACrC,QAAQ,EAAE;QACR,GAAG,EAAE,wCAAwC,CAAC;QAC9C,KAAK,EAAE,wCAAwC,CAAC;QAChD,IAAI,EAAE,wCAAwC,CAAC;KAChD,CAAC;IACF,UAAU,EAAE,oCAAoC,CAAC;CAClD;AAED,MAAM,WAAW,wCAAwC;IACvD,SAAS,EAAE,qCAAqC,CAAC;IACjD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,qBAAqB,EAAE,gBAAgB,CAAC;IACxC,QAAQ,EAAE;QACR,GAAG,EAAE,+CAA+C,CAAC;QACrD,KAAK,EAAE,+CAA+C,CAAC;QACvD,IAAI,EAAE,+CAA+C,CAAC;KACvD,CAAC;IACF,gBAAgB,EAAE,oCAAoC,CAAC;CACxD;AAyID;;;;;;GAMG;AACH,wBAAgB,0CAA0C,CACxD,KAAK,EAAE,+CAA+C,GACrD,iBAAiB,CAAC,iCAAiC,CAAC,CAsDtD;AAED;;;;;;GAMG;AACH,wBAAgB,iDAAiD,CAC/D,KAAK,EAAE,sDAAsD,GAC5D,iBAAiB,CAAC,wCAAwC,CAAC,CAsD7D"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { approximationResult } from "../core/calculation-result.js";
|
|
3
|
+
import { InvalidScientificInputError } from "../core/validation.js";
|
|
4
|
+
import { calculateInverseRadialDistortionMapping, calculateRadialDistortionMapping } from "./radial-distortion.js";
|
|
5
|
+
function requireFiniteCoefficients(path, coefficients) {
|
|
6
|
+
for (const key of ["k1", "k2", "k3"]) {
|
|
7
|
+
if (!Number.isFinite(coefficients[key])) {
|
|
8
|
+
throw new InvalidScientificInputError(`${path}.${key} must be finite.`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
function addCoefficients(base, offset) {
|
|
13
|
+
return {
|
|
14
|
+
k1: base.k1 + offset.k1,
|
|
15
|
+
k2: base.k2 + offset.k2,
|
|
16
|
+
k3: base.k3 + offset.k3
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function resolveProfiles(profile) {
|
|
20
|
+
requireFiniteCoefficients("profile.baseDistortionCoefficients", profile.baseDistortionCoefficients);
|
|
21
|
+
requireFiniteCoefficients("profile.redCoefficientOffset", profile.redCoefficientOffset);
|
|
22
|
+
requireFiniteCoefficients("profile.blueCoefficientOffset", profile.blueCoefficientOffset);
|
|
23
|
+
const shared = {
|
|
24
|
+
normalizationRadiusMm: profile.normalizationRadiusMm,
|
|
25
|
+
maximumNormalizedRadius: profile.maximumNormalizedRadius
|
|
26
|
+
};
|
|
27
|
+
return {
|
|
28
|
+
red: {
|
|
29
|
+
...shared,
|
|
30
|
+
coefficients: addCoefficients(profile.baseDistortionCoefficients, profile.redCoefficientOffset)
|
|
31
|
+
},
|
|
32
|
+
green: {
|
|
33
|
+
...shared,
|
|
34
|
+
coefficients: { ...profile.baseDistortionCoefficients }
|
|
35
|
+
},
|
|
36
|
+
blue: {
|
|
37
|
+
...shared,
|
|
38
|
+
coefficients: addCoefficients(profile.baseDistortionCoefficients, profile.blueCoefficientOffset)
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function withChannelContext(channel, calculate) {
|
|
43
|
+
try {
|
|
44
|
+
return calculate();
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (error instanceof InvalidScientificInputError) {
|
|
48
|
+
throw new InvalidScientificInputError(`Lateral chromatic aberration ${channel} channel: ${error.message}`);
|
|
49
|
+
}
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
function separationVector(first, second) {
|
|
54
|
+
const x = first.x - second.x;
|
|
55
|
+
const y = first.y - second.y;
|
|
56
|
+
return {
|
|
57
|
+
x,
|
|
58
|
+
y,
|
|
59
|
+
distance: Math.hypot(x, y)
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function separations(red, green, blue) {
|
|
63
|
+
const redGreen = separationVector(red, green);
|
|
64
|
+
const blueGreen = separationVector(blue, green);
|
|
65
|
+
const redBlue = separationVector(red, blue);
|
|
66
|
+
return {
|
|
67
|
+
redGreen,
|
|
68
|
+
blueGreen,
|
|
69
|
+
redBlue,
|
|
70
|
+
maximumPairDistanceMm: Math.max(redGreen.distance, blueGreen.distance, redBlue.distance)
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function assumptions() {
|
|
74
|
+
return [
|
|
75
|
+
"Generic lateral chromatic aberration is represented as independent radial field mapping for abstract red, green, and blue renderer channels",
|
|
76
|
+
"Green is the reference field mapping; red and blue coefficient offsets are added to the green-reference base distortion rather than applied as a second warp",
|
|
77
|
+
"All channel mappings share one physical normalization radius and declared operating envelope",
|
|
78
|
+
"Each combined channel radial profile must remain one-to-one over the declared operating envelope",
|
|
79
|
+
"The RGB channel labels are representative rendering channels, not calibrated wavelengths, sensor spectral responses, or CFA primaries",
|
|
80
|
+
"This model changes channel field coordinates only; it does not blur, change PSF shape, modify channel intensity, or model longitudinal chromatic aberration",
|
|
81
|
+
"Profiles are generic caller inputs and do not represent a named lens unless separately calibrated with defensible provenance"
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Calculates generic lateral chromatic-aberration field separation by mapping
|
|
86
|
+
* one ideal image-plane point through a shared green-reference base distortion
|
|
87
|
+
* and red/blue radial coefficient offsets.
|
|
88
|
+
*
|
|
89
|
+
* This is channel-dependent field mapping, not a blur kernel.
|
|
90
|
+
*/
|
|
91
|
+
export function calculateLateralChromaticAberrationMapping(input) {
|
|
92
|
+
const profiles = resolveProfiles(input.profile);
|
|
93
|
+
const red = withChannelContext("red", () => calculateRadialDistortionMapping({
|
|
94
|
+
imagePointMm: input.imagePointMm,
|
|
95
|
+
profile: profiles.red
|
|
96
|
+
})).value;
|
|
97
|
+
const green = withChannelContext("green", () => calculateRadialDistortionMapping({
|
|
98
|
+
imagePointMm: input.imagePointMm,
|
|
99
|
+
profile: profiles.green
|
|
100
|
+
})).value;
|
|
101
|
+
const blue = withChannelContext("blue", () => calculateRadialDistortionMapping({
|
|
102
|
+
imagePointMm: input.imagePointMm,
|
|
103
|
+
profile: profiles.blue
|
|
104
|
+
})).value;
|
|
105
|
+
return approximationResult({
|
|
106
|
+
direction: "undistorted-to-channel-distorted",
|
|
107
|
+
referenceChannel: "green",
|
|
108
|
+
sourceImagePointMm: { ...input.imagePointMm },
|
|
109
|
+
channels: {
|
|
110
|
+
red: {
|
|
111
|
+
mappedImagePointMm: { ...red.mappedImagePointMm },
|
|
112
|
+
radialScale: red.radialScale,
|
|
113
|
+
combinedCoefficients: { ...profiles.red.coefficients }
|
|
114
|
+
},
|
|
115
|
+
green: {
|
|
116
|
+
mappedImagePointMm: { ...green.mappedImagePointMm },
|
|
117
|
+
radialScale: green.radialScale,
|
|
118
|
+
combinedCoefficients: { ...profiles.green.coefficients }
|
|
119
|
+
},
|
|
120
|
+
blue: {
|
|
121
|
+
mappedImagePointMm: { ...blue.mappedImagePointMm },
|
|
122
|
+
radialScale: blue.radialScale,
|
|
123
|
+
combinedCoefficients: { ...profiles.blue.coefficients }
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
separation: separations(red.mappedImagePointMm, green.mappedImagePointMm, blue.mappedImagePointMm)
|
|
127
|
+
}, "generic-green-reference-lateral-chromatic-aberration", "1.0.0", assumptions());
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Inverse-maps one distorted output destination independently for the
|
|
131
|
+
* representative red/green/blue channels.
|
|
132
|
+
*
|
|
133
|
+
* Renderers can use the returned per-channel ideal source coordinates for
|
|
134
|
+
* inverse sampling without inventing chromatic-aberration equations.
|
|
135
|
+
*/
|
|
136
|
+
export function calculateInverseLateralChromaticAberrationMapping(input) {
|
|
137
|
+
const profiles = resolveProfiles(input.profile);
|
|
138
|
+
const red = withChannelContext("red", () => calculateInverseRadialDistortionMapping({
|
|
139
|
+
distortedImagePointMm: input.distortedImagePointMm,
|
|
140
|
+
profile: profiles.red
|
|
141
|
+
})).value;
|
|
142
|
+
const green = withChannelContext("green", () => calculateInverseRadialDistortionMapping({
|
|
143
|
+
distortedImagePointMm: input.distortedImagePointMm,
|
|
144
|
+
profile: profiles.green
|
|
145
|
+
})).value;
|
|
146
|
+
const blue = withChannelContext("blue", () => calculateInverseRadialDistortionMapping({
|
|
147
|
+
distortedImagePointMm: input.distortedImagePointMm,
|
|
148
|
+
profile: profiles.blue
|
|
149
|
+
})).value;
|
|
150
|
+
return approximationResult({
|
|
151
|
+
direction: "distorted-output-to-channel-sources",
|
|
152
|
+
referenceChannel: "green",
|
|
153
|
+
distortedImagePointMm: { ...input.distortedImagePointMm },
|
|
154
|
+
channels: {
|
|
155
|
+
red: {
|
|
156
|
+
sourceImagePointMm: { ...red.sourceImagePointMm },
|
|
157
|
+
radialScaleAtSource: red.radialScaleAtSource,
|
|
158
|
+
combinedCoefficients: { ...profiles.red.coefficients }
|
|
159
|
+
},
|
|
160
|
+
green: {
|
|
161
|
+
sourceImagePointMm: { ...green.sourceImagePointMm },
|
|
162
|
+
radialScaleAtSource: green.radialScaleAtSource,
|
|
163
|
+
combinedCoefficients: { ...profiles.green.coefficients }
|
|
164
|
+
},
|
|
165
|
+
blue: {
|
|
166
|
+
sourceImagePointMm: { ...blue.sourceImagePointMm },
|
|
167
|
+
radialScaleAtSource: blue.radialScaleAtSource,
|
|
168
|
+
combinedCoefficients: { ...profiles.blue.coefficients }
|
|
169
|
+
}
|
|
170
|
+
},
|
|
171
|
+
sourceSeparation: separations(red.sourceImagePointMm, green.sourceImagePointMm, blue.sourceImagePointMm)
|
|
172
|
+
}, "generic-inverse-green-reference-lateral-chromatic-aberration", "1.0.0", assumptions());
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=lateral-chromatic-aberration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lateral-chromatic-aberration.js","sourceRoot":"","sources":["../../src/optics/lateral-chromatic-aberration.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { type CalculationProvenance, type CalculationResult } from "../core/calculation-result.js";
|
|
2
|
+
import type { LensFieldPointMm } from "./radial-distortion.js";
|
|
3
|
+
export declare const PSF_FOUNDATION_VERSION: "0.1.0";
|
|
4
|
+
export type PsfContributionId = "geometric-defocus-circle" | "circular-diffraction-first-zero" | "non-circular-diffraction" | "mechanical-pupil-clipping" | "field-curvature" | "field-dependent-aberration" | "field-dependent-bokeh";
|
|
5
|
+
export type PsfContributionStatus = "implemented-diagnostic" | "reserved-contract";
|
|
6
|
+
export interface PsfContributionContract {
|
|
7
|
+
id: PsfContributionId;
|
|
8
|
+
status: PsfContributionStatus;
|
|
9
|
+
dependsOn: readonly ("field-position" | "focus-depth" | "pupil" | "wavelength")[];
|
|
10
|
+
note: string;
|
|
11
|
+
}
|
|
12
|
+
export interface PsfFoundationContract {
|
|
13
|
+
version: typeof PSF_FOUNDATION_VERSION;
|
|
14
|
+
coordinateSpace: "image-plane-metric";
|
|
15
|
+
fieldAxes: "+X right, +Y up";
|
|
16
|
+
compositionPolicy: "separate-contributions-no-combined-psf";
|
|
17
|
+
contributions: readonly PsfContributionContract[];
|
|
18
|
+
previewReferencePolicy: {
|
|
19
|
+
previewMayApproximate: true;
|
|
20
|
+
referenceMayUseHigherFidelity: true;
|
|
21
|
+
mustPreserveContributionSemantics: true;
|
|
22
|
+
};
|
|
23
|
+
notes: readonly string[];
|
|
24
|
+
}
|
|
25
|
+
export interface CalculatePsfFoundationComponentsInput {
|
|
26
|
+
focalLengthMm: number;
|
|
27
|
+
aperture: number;
|
|
28
|
+
focusDistanceM: number;
|
|
29
|
+
subjectDistanceM: number;
|
|
30
|
+
/**
|
|
31
|
+
* Image-plane field position relative to the optical axis, in millimetres.
|
|
32
|
+
* +X is right and +Y is up.
|
|
33
|
+
*/
|
|
34
|
+
fieldPointMm: LensFieldPointMm;
|
|
35
|
+
/**
|
|
36
|
+
* Physical image-plane radius used only to normalize field position for
|
|
37
|
+
* cross-case diagnostics. It is not a lens calibration or validity bound.
|
|
38
|
+
*/
|
|
39
|
+
fieldNormalizationRadiusMm: number;
|
|
40
|
+
spectralBasis: {
|
|
41
|
+
kind: "monochromatic";
|
|
42
|
+
wavelengthNm: number;
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
export interface PsfFoundationComponents {
|
|
46
|
+
context: {
|
|
47
|
+
field: {
|
|
48
|
+
imagePointMm: LensFieldPointMm;
|
|
49
|
+
radiusMm: number;
|
|
50
|
+
normalizationRadiusMm: number;
|
|
51
|
+
normalizedRadius: number;
|
|
52
|
+
};
|
|
53
|
+
depth: {
|
|
54
|
+
focusDistanceM: number;
|
|
55
|
+
subjectDistanceM: number;
|
|
56
|
+
};
|
|
57
|
+
spectralBasis: {
|
|
58
|
+
kind: "monochromatic";
|
|
59
|
+
wavelengthNm: number;
|
|
60
|
+
};
|
|
61
|
+
pupil: {
|
|
62
|
+
kind: "ideal-circular-f-number-derived";
|
|
63
|
+
apertureFNumber: number;
|
|
64
|
+
diameterMm: number;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
contributions: {
|
|
68
|
+
geometricDefocus: {
|
|
69
|
+
id: "geometric-defocus-circle";
|
|
70
|
+
diameterMm: number;
|
|
71
|
+
provenance: CalculationProvenance;
|
|
72
|
+
};
|
|
73
|
+
circularDiffraction: {
|
|
74
|
+
id: "circular-diffraction-first-zero";
|
|
75
|
+
firstZeroDiameterMicrometers: number;
|
|
76
|
+
provenance: CalculationProvenance;
|
|
77
|
+
};
|
|
78
|
+
};
|
|
79
|
+
composition: {
|
|
80
|
+
status: "not-composed";
|
|
81
|
+
note: string;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Returns the public PSF/pupil foundation contract.
|
|
86
|
+
*
|
|
87
|
+
* The contract describes current diagnostics plus reserved future contribution
|
|
88
|
+
* ownership. It does not imply that reserved contributions are implemented.
|
|
89
|
+
*/
|
|
90
|
+
export declare function getPsfFoundationContract(): PsfFoundationContract;
|
|
91
|
+
/**
|
|
92
|
+
* Evaluates the currently implemented PSF-related diagnostics in one explicit
|
|
93
|
+
* field/depth/spectral/pupil context without combining them into a synthetic
|
|
94
|
+
* PSF or blur radius.
|
|
95
|
+
*
|
|
96
|
+
* Field position is recorded even though the current defocus-circle and
|
|
97
|
+
* circular-Airy diagnostics are not field dependent. Future field-dependent
|
|
98
|
+
* contributions can consume the same context without changing these existing
|
|
99
|
+
* primitives.
|
|
100
|
+
*/
|
|
101
|
+
export declare function calculatePsfFoundationComponents(input: CalculatePsfFoundationComponentsInput): CalculationResult<PsfFoundationComponents>;
|
|
102
|
+
//# sourceMappingURL=psf-foundation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"psf-foundation.d.ts","sourceRoot":"","sources":["../../src/optics/psf-foundation.ts"],"names":[],"mappings":"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"}
|