@photivra/engine 0.1.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/CITATION.cff +23 -0
- package/LICENSE +201 -0
- package/NOTICE +9 -0
- package/README.md +235 -0
- package/dist/camera/field-of-view.d.ts +37 -0
- package/dist/camera/field-of-view.d.ts.map +1 -0
- package/dist/camera/field-of-view.js +47 -0
- package/dist/camera/field-of-view.js.map +1 -0
- package/dist/camera/projected-object-size.d.ts +34 -0
- package/dist/camera/projected-object-size.d.ts.map +1 -0
- package/dist/camera/projected-object-size.js +57 -0
- package/dist/camera/projected-object-size.js.map +1 -0
- package/dist/core/calculation-result.d.ts +125 -0
- package/dist/core/calculation-result.d.ts.map +1 -0
- package/dist/core/calculation-result.js +175 -0
- package/dist/core/calculation-result.js.map +1 -0
- package/dist/core/validation.d.ts +30 -0
- package/dist/core/validation.d.ts.map +1 -0
- package/dist/core/validation.js +45 -0
- package/dist/core/validation.js.map +1 -0
- package/dist/exposure/exposure.d.ts +50 -0
- package/dist/exposure/exposure.d.ts.map +1 -0
- package/dist/exposure/exposure.js +62 -0
- package/dist/exposure/exposure.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/motion/projected-motion.d.ts +41 -0
- package/dist/motion/projected-motion.d.ts.map +1 -0
- package/dist/motion/projected-motion.js +84 -0
- package/dist/motion/projected-motion.js.map +1 -0
- package/dist/optics/aperture.d.ts +39 -0
- package/dist/optics/aperture.d.ts.map +1 -0
- package/dist/optics/aperture.js +62 -0
- package/dist/optics/aperture.js.map +1 -0
- package/dist/optics/circle-of-confusion.d.ts +23 -0
- package/dist/optics/circle-of-confusion.d.ts.map +1 -0
- package/dist/optics/circle-of-confusion.js +32 -0
- package/dist/optics/circle-of-confusion.js.map +1 -0
- package/dist/optics/depth-of-field.d.ts +53 -0
- package/dist/optics/depth-of-field.d.ts.map +1 -0
- package/dist/optics/depth-of-field.js +81 -0
- package/dist/optics/depth-of-field.js.map +1 -0
- package/dist/optics/diffraction.d.ts +26 -0
- package/dist/optics/diffraction.d.ts.map +1 -0
- package/dist/optics/diffraction.js +21 -0
- package/dist/optics/diffraction.js.map +1 -0
- package/dist/optics/thin-lens.d.ts +30 -0
- package/dist/optics/thin-lens.d.ts.map +1 -0
- package/dist/optics/thin-lens.js +33 -0
- package/dist/optics/thin-lens.js.map +1 -0
- package/dist/output/crop.d.ts +27 -0
- package/dist/output/crop.d.ts.map +1 -0
- package/dist/output/crop.js +26 -0
- package/dist/output/crop.js.map +1 -0
- package/dist/output/subject-framing-crop.d.ts +36 -0
- package/dist/output/subject-framing-crop.d.ts.map +1 -0
- package/dist/output/subject-framing-crop.js +42 -0
- package/dist/output/subject-framing-crop.js.map +1 -0
- package/dist/schema/camera.d.ts +34 -0
- package/dist/schema/camera.d.ts.map +1 -0
- package/dist/schema/camera.js +3 -0
- package/dist/schema/camera.js.map +1 -0
- package/dist/schema/scene.d.ts +69 -0
- package/dist/schema/scene.d.ts.map +1 -0
- package/dist/schema/scene.js +3 -0
- package/dist/schema/scene.js.map +1 -0
- package/dist/schema/validation.d.ts +25 -0
- package/dist/schema/validation.d.ts.map +1 -0
- package/dist/schema/validation.js +180 -0
- package/dist/schema/validation.js.map +1 -0
- package/dist/sensor/pixel-pitch.d.ts +21 -0
- package/dist/sensor/pixel-pitch.d.ts.map +1 -0
- package/dist/sensor/pixel-pitch.js +19 -0
- package/dist/sensor/pixel-pitch.js.map +1 -0
- package/dist/sensor/signal-noise.d.ts +37 -0
- package/dist/sensor/signal-noise.d.ts.map +1 -0
- package/dist/sensor/signal-noise.js +53 -0
- package/dist/sensor/signal-noise.js.map +1 -0
- package/dist/simulation/poc-simulation.d.ts +264 -0
- package/dist/simulation/poc-simulation.d.ts.map +1 -0
- package/dist/simulation/poc-simulation.js +449 -0
- package/dist/simulation/poc-simulation.js.map +1 -0
- package/dist/stabilization/camera-shake.d.ts +41 -0
- package/dist/stabilization/camera-shake.d.ts.map +1 -0
- package/dist/stabilization/camera-shake.js +79 -0
- package/dist/stabilization/camera-shake.js.map +1 -0
- package/docs/API_STYLE.md +65 -0
- package/docs/ARCHITECTURE.md +51 -0
- package/docs/MOTION_AND_SIGNAL.md +53 -0
- package/docs/PHYSICS_FOUNDATION.md +92 -0
- package/docs/POC_API.md +210 -0
- package/docs/PROVENANCE.md +87 -0
- package/docs/STABILIZATION.md +78 -0
- package/docs/USAGE.md +538 -0
- package/package.json +91 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type CalculationResult } from "../core/calculation-result.js";
|
|
2
|
+
export interface CalculateSubjectFramingCropInput {
|
|
3
|
+
/** Source image width in pixels. */
|
|
4
|
+
pixelWidth: number;
|
|
5
|
+
/** Source image height in pixels. */
|
|
6
|
+
pixelHeight: number;
|
|
7
|
+
/** Projected subject height in source-image pixels. */
|
|
8
|
+
subjectHeightPixels: number;
|
|
9
|
+
/** Desired subject height as a fraction of output frame height, >0 and <=1. */
|
|
10
|
+
targetSubjectHeightFraction: number;
|
|
11
|
+
}
|
|
12
|
+
export interface SubjectFramingCrop {
|
|
13
|
+
cropFactor: number;
|
|
14
|
+
pixelWidth: number;
|
|
15
|
+
pixelHeight: number;
|
|
16
|
+
megapixels: number;
|
|
17
|
+
/**
|
|
18
|
+
* Actual subject height divided by cropped frame height.
|
|
19
|
+
* Values greater than 1 mean the subject is taller than the output frame.
|
|
20
|
+
*/
|
|
21
|
+
subjectHeightFraction: number;
|
|
22
|
+
/** Whether the projected subject exceeds the cropped frame height. */
|
|
23
|
+
subjectClipped: boolean;
|
|
24
|
+
/** Whether achieving the target required an additional crop. */
|
|
25
|
+
cropped: boolean;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Calculates the same-aspect-ratio crop needed for a subject to occupy a
|
|
29
|
+
* requested fraction of frame height.
|
|
30
|
+
*
|
|
31
|
+
* This calculation assumes a centered/positionable crop with enough spatial
|
|
32
|
+
* margin around the subject. It does not validate subject position against
|
|
33
|
+
* image edges.
|
|
34
|
+
*/
|
|
35
|
+
export declare function calculateSubjectFramingCrop(input: CalculateSubjectFramingCropInput): CalculationResult<SubjectFramingCrop>;
|
|
36
|
+
//# sourceMappingURL=subject-framing-crop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subject-framing-crop.d.ts","sourceRoot":"","sources":["../../src/output/subject-framing-crop.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,+BAA+B,CAAC;AAOvC,MAAM,WAAW,gCAAgC;IAC/C,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,qCAAqC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,uDAAuD;IACvD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,+EAA+E;IAC/E,2BAA2B,EAAE,MAAM,CAAC;CACrC;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB;;;OAGG;IACH,qBAAqB,EAAE,MAAM,CAAC;IAC9B,sEAAsE;IACtE,cAAc,EAAE,OAAO,CAAC;IACxB,gEAAgE;IAChE,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CACzC,KAAK,EAAE,gCAAgC,GACtC,iBAAiB,CAAC,kBAAkB,CAAC,CAoDvC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { calculatedResult } from "../core/calculation-result.js";
|
|
3
|
+
import { InvalidScientificInputError, requirePositiveFinite, requirePositiveInteger } from "../core/validation.js";
|
|
4
|
+
/**
|
|
5
|
+
* Calculates the same-aspect-ratio crop needed for a subject to occupy a
|
|
6
|
+
* requested fraction of frame height.
|
|
7
|
+
*
|
|
8
|
+
* This calculation assumes a centered/positionable crop with enough spatial
|
|
9
|
+
* margin around the subject. It does not validate subject position against
|
|
10
|
+
* image edges.
|
|
11
|
+
*/
|
|
12
|
+
export function calculateSubjectFramingCrop(input) {
|
|
13
|
+
requirePositiveInteger("pixelWidth", input.pixelWidth);
|
|
14
|
+
requirePositiveInteger("pixelHeight", input.pixelHeight);
|
|
15
|
+
requirePositiveFinite("subjectHeightPixels", input.subjectHeightPixels);
|
|
16
|
+
requirePositiveFinite("targetSubjectHeightFraction", input.targetSubjectHeightFraction);
|
|
17
|
+
if (input.targetSubjectHeightFraction > 1) {
|
|
18
|
+
throw new InvalidScientificInputError("targetSubjectHeightFraction must be less than or equal to 1.");
|
|
19
|
+
}
|
|
20
|
+
const requestedCropHeight = input.subjectHeightPixels / input.targetSubjectHeightFraction;
|
|
21
|
+
const cropFactor = requestedCropHeight >= input.pixelHeight
|
|
22
|
+
? 1
|
|
23
|
+
: input.pixelHeight / requestedCropHeight;
|
|
24
|
+
const pixelHeight = Math.max(1, Math.floor(input.pixelHeight / cropFactor));
|
|
25
|
+
const pixelWidth = Math.max(1, Math.floor(input.pixelWidth / cropFactor));
|
|
26
|
+
const subjectHeightFraction = input.subjectHeightPixels / pixelHeight;
|
|
27
|
+
return calculatedResult({
|
|
28
|
+
cropFactor,
|
|
29
|
+
pixelWidth,
|
|
30
|
+
pixelHeight,
|
|
31
|
+
megapixels: (pixelWidth * pixelHeight) / 1_000_000,
|
|
32
|
+
subjectHeightFraction,
|
|
33
|
+
subjectClipped: subjectHeightFraction > 1,
|
|
34
|
+
cropped: cropFactor > 1
|
|
35
|
+
}, "subject-height-framing-crop", "1.0.0", [
|
|
36
|
+
"Original aspect ratio is preserved",
|
|
37
|
+
"Crop can be positioned around the subject",
|
|
38
|
+
"Subject edge/margin constraints are not modeled",
|
|
39
|
+
"subjectHeightFraction may exceed 1 when the subject is clipped by frame height"
|
|
40
|
+
]);
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=subject-framing-crop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"subject-framing-crop.js","sourceRoot":"","sources":["../../src/output/subject-framing-crop.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC,OAAO,EACL,gBAAgB,EAEjB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,uBAAuB,CAAC;AA6B/B;;;;;;;GAOG;AACH,MAAM,UAAU,2BAA2B,CACzC,KAAuC;IAEvC,sBAAsB,CAAC,YAAY,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IACvD,sBAAsB,CAAC,aAAa,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;IACzD,qBAAqB,CAAC,qBAAqB,EAAE,KAAK,CAAC,mBAAmB,CAAC,CAAC;IACxE,qBAAqB,CACnB,6BAA6B,EAC7B,KAAK,CAAC,2BAA2B,CAClC,CAAC;IAEF,IAAI,KAAK,CAAC,2BAA2B,GAAG,CAAC,EAAE,CAAC;QAC1C,MAAM,IAAI,2BAA2B,CACnC,8DAA8D,CAC/D,CAAC;IACJ,CAAC;IAED,MAAM,mBAAmB,GACvB,KAAK,CAAC,mBAAmB,GAAG,KAAK,CAAC,2BAA2B,CAAC;IAChE,MAAM,UAAU,GACd,mBAAmB,IAAI,KAAK,CAAC,WAAW;QACtC,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,KAAK,CAAC,WAAW,GAAG,mBAAmB,CAAC;IAE9C,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAC1B,CAAC,EACD,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,GAAG,UAAU,CAAC,CAC3C,CAAC;IACF,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CACzB,CAAC,EACD,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,GAAG,UAAU,CAAC,CAC1C,CAAC;IACF,MAAM,qBAAqB,GACzB,KAAK,CAAC,mBAAmB,GAAG,WAAW,CAAC;IAE1C,OAAO,gBAAgB,CACrB;QACE,UAAU;QACV,UAAU;QACV,WAAW;QACX,UAAU,EAAE,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,SAAS;QAClD,qBAAqB;QACrB,cAAc,EAAE,qBAAqB,GAAG,CAAC;QACzC,OAAO,EAAE,UAAU,GAAG,CAAC;KACxB,EACD,6BAA6B,EAC7B,OAAO,EACP;QACE,oCAAoC;QACpC,2CAA2C;QAC3C,iDAAiD;QACjD,gFAAgF;KACjF,CACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export interface SensorConfiguration {
|
|
2
|
+
widthMm: number;
|
|
3
|
+
heightMm: number;
|
|
4
|
+
pixelWidth: number;
|
|
5
|
+
pixelHeight: number;
|
|
6
|
+
}
|
|
7
|
+
export interface LensConfiguration {
|
|
8
|
+
focalLengthMm: number;
|
|
9
|
+
aperture: number;
|
|
10
|
+
}
|
|
11
|
+
export interface ExposureConfiguration {
|
|
12
|
+
shutterSeconds: number;
|
|
13
|
+
iso: number;
|
|
14
|
+
}
|
|
15
|
+
export interface FocusConfiguration {
|
|
16
|
+
focusDistanceM: number;
|
|
17
|
+
}
|
|
18
|
+
export type CameraSupport = "handheld" | "braced" | "monopod" | "tripod";
|
|
19
|
+
export interface StabilizationConfiguration {
|
|
20
|
+
bodyEnabled: boolean;
|
|
21
|
+
lensEnabled: boolean;
|
|
22
|
+
support: CameraSupport;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Renderer-independent camera configuration consumed by simulation modules.
|
|
26
|
+
*/
|
|
27
|
+
export interface CameraConfiguration {
|
|
28
|
+
sensor: SensorConfiguration;
|
|
29
|
+
lens: LensConfiguration;
|
|
30
|
+
exposure: ExposureConfiguration;
|
|
31
|
+
focus: FocusConfiguration;
|
|
32
|
+
stabilization?: StabilizationConfiguration;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=camera.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"camera.d.ts","sourceRoot":"","sources":["../../src/schema/camera.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,MAAM,aAAa,GAAG,UAAU,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEzE,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,aAAa,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,IAAI,EAAE,iBAAiB,CAAC;IACxB,QAAQ,EAAE,qBAAqB,CAAC;IAChC,KAAK,EAAE,kBAAkB,CAAC;IAC1B,aAAa,CAAC,EAAE,0BAA0B,CAAC;CAC5C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"camera.js","sourceRoot":"","sources":["../../src/schema/camera.ts"],"names":[],"mappings":"AAAA,sCAAsC"}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Three-dimensional value in scene/world coordinates.
|
|
3
|
+
*/
|
|
4
|
+
export interface Vector3 {
|
|
5
|
+
x: number;
|
|
6
|
+
y: number;
|
|
7
|
+
z: number;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Describes how scene-linear image values relate to physical light.
|
|
11
|
+
*/
|
|
12
|
+
export type SceneRadiometry = {
|
|
13
|
+
/**
|
|
14
|
+
* Relative linear values preserve light ratios but are not absolutely
|
|
15
|
+
* calibrated for photon-count calculations.
|
|
16
|
+
*/
|
|
17
|
+
kind: "relative-linear";
|
|
18
|
+
referenceValue: number;
|
|
19
|
+
} | {
|
|
20
|
+
/**
|
|
21
|
+
* Scene package includes an absolute luminance anchor that can support
|
|
22
|
+
* calibrated photometric exposure work. Luminance alone is not
|
|
23
|
+
* sufficient to derive photon counts; photon simulation additionally
|
|
24
|
+
* requires defensible spectral and optical calibration.
|
|
25
|
+
*/
|
|
26
|
+
kind: "absolute-luminance";
|
|
27
|
+
referenceValue: number;
|
|
28
|
+
referenceLuminanceCdM2: number;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Motion attached to an independently controllable scene object or region.
|
|
32
|
+
*/
|
|
33
|
+
export interface SceneMotion {
|
|
34
|
+
/** World-space linear velocity in metres per second. */
|
|
35
|
+
linearVelocityMps: Vector3;
|
|
36
|
+
/** Optional world-space angular velocity in radians per second. */
|
|
37
|
+
angularVelocityRadPerSec?: Vector3;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Semantic object/region within a curated synthetic scene.
|
|
41
|
+
*/
|
|
42
|
+
export interface SceneObject {
|
|
43
|
+
id: string;
|
|
44
|
+
label?: string;
|
|
45
|
+
/** Representative distance from the camera origin in metres. */
|
|
46
|
+
distanceM: number;
|
|
47
|
+
motion?: SceneMotion;
|
|
48
|
+
tags?: readonly string[];
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Capabilities intentionally advertised by a scene package.
|
|
52
|
+
*/
|
|
53
|
+
export type SceneCapability = "scene-linear-rgb" | "metric-depth" | "segmentation" | "occlusion-layers" | "motion-vectors" | "focus-targets" | "multi-resolution-layers" | "absolute-radiometry";
|
|
54
|
+
/**
|
|
55
|
+
* Versioned, renderer-independent description of a curated synthetic scene.
|
|
56
|
+
*
|
|
57
|
+
* Asset locations are intentionally not part of this scientific schema;
|
|
58
|
+
* callers may bind the metadata to their own asset systems separately.
|
|
59
|
+
*/
|
|
60
|
+
export interface SceneDefinition {
|
|
61
|
+
schemaVersion: string;
|
|
62
|
+
id: string;
|
|
63
|
+
version: string;
|
|
64
|
+
radiometry: SceneRadiometry;
|
|
65
|
+
capabilities: readonly SceneCapability[];
|
|
66
|
+
objects: readonly SceneObject[];
|
|
67
|
+
focusTargetIds?: readonly string[];
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=scene.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scene.d.ts","sourceRoot":"","sources":["../../src/schema/scene.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GACvB;IACE;;;OAGG;IACH,IAAI,EAAE,iBAAiB,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;CACxB,GACD;IACE;;;;;OAKG;IACH,IAAI,EAAE,oBAAoB,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,sBAAsB,EAAE,MAAM,CAAC;CAChC,CAAC;AAEN;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,wDAAwD;IACxD,iBAAiB,EAAE,OAAO,CAAC;IAC3B,mEAAmE;IACnE,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GACvB,kBAAkB,GAClB,cAAc,GACd,cAAc,GACd,kBAAkB,GAClB,gBAAgB,GAChB,eAAe,GACf,yBAAyB,GACzB,qBAAqB,CAAC;AAE1B;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,eAAe,CAAC;IAC5B,YAAY,EAAE,SAAS,eAAe,EAAE,CAAC;IACzC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IAChC,cAAc,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scene.js","sourceRoot":"","sources":["../../src/schema/scene.ts"],"names":[],"mappings":"AAAA,sCAAsC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { CameraConfiguration } from "./camera.js";
|
|
2
|
+
import type { SceneDefinition } from "./scene.js";
|
|
3
|
+
/**
|
|
4
|
+
* Error thrown when external configuration data does not match a public
|
|
5
|
+
* runtime schema.
|
|
6
|
+
*/
|
|
7
|
+
export declare class InvalidConfigurationError extends TypeError {
|
|
8
|
+
readonly code = "INVALID_CONFIGURATION";
|
|
9
|
+
constructor(message: string);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Parses and validates an untrusted camera configuration.
|
|
13
|
+
*
|
|
14
|
+
* @param value Unknown external data.
|
|
15
|
+
* @returns The original value narrowed to CameraConfiguration.
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseCameraConfiguration(value: unknown): CameraConfiguration;
|
|
18
|
+
/**
|
|
19
|
+
* Parses and validates an untrusted renderer-independent scene definition.
|
|
20
|
+
*
|
|
21
|
+
* @param value Unknown external data.
|
|
22
|
+
* @returns The original value narrowed to SceneDefinition.
|
|
23
|
+
*/
|
|
24
|
+
export declare function parseSceneDefinition(value: unknown): SceneDefinition;
|
|
25
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/schema/validation.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,mBAAmB,EAEpB,MAAM,aAAa,CAAC;AACrB,OAAO,KAAK,EAEV,eAAe,EAChB,MAAM,YAAY,CAAC;AAsBpB;;;GAGG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;IACtD,QAAQ,CAAC,IAAI,2BAA2B;gBAE5B,OAAO,EAAE,MAAM;CAI5B;AAkGD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,GACb,mBAAmB,CAwCrB;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,eAAe,CAuFpE"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
const CAMERA_SUPPORTS = new Set([
|
|
3
|
+
"handheld",
|
|
4
|
+
"braced",
|
|
5
|
+
"monopod",
|
|
6
|
+
"tripod"
|
|
7
|
+
]);
|
|
8
|
+
const SCENE_CAPABILITIES = new Set([
|
|
9
|
+
"scene-linear-rgb",
|
|
10
|
+
"metric-depth",
|
|
11
|
+
"segmentation",
|
|
12
|
+
"occlusion-layers",
|
|
13
|
+
"motion-vectors",
|
|
14
|
+
"focus-targets",
|
|
15
|
+
"multi-resolution-layers",
|
|
16
|
+
"absolute-radiometry"
|
|
17
|
+
]);
|
|
18
|
+
/**
|
|
19
|
+
* Error thrown when external configuration data does not match a public
|
|
20
|
+
* runtime schema.
|
|
21
|
+
*/
|
|
22
|
+
export class InvalidConfigurationError extends TypeError {
|
|
23
|
+
code = "INVALID_CONFIGURATION";
|
|
24
|
+
constructor(message) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = "InvalidConfigurationError";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function requireRecord(value, path) {
|
|
30
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
31
|
+
throw new InvalidConfigurationError(`${path} must be an object.`);
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
function requireString(record, key, path) {
|
|
36
|
+
const value = record[key];
|
|
37
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
38
|
+
throw new InvalidConfigurationError(`${path}.${key} must be a non-empty string.`);
|
|
39
|
+
}
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
function requireBoolean(record, key, path) {
|
|
43
|
+
const value = record[key];
|
|
44
|
+
if (typeof value !== "boolean") {
|
|
45
|
+
throw new InvalidConfigurationError(`${path}.${key} must be a boolean.`);
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
function requirePositiveNumber(record, key, path) {
|
|
50
|
+
const value = record[key];
|
|
51
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
52
|
+
throw new InvalidConfigurationError(`${path}.${key} must be a finite number greater than zero.`);
|
|
53
|
+
}
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
function requirePositiveIntegerValue(record, key, path) {
|
|
57
|
+
const value = record[key];
|
|
58
|
+
if (!Number.isSafeInteger(value) || value <= 0) {
|
|
59
|
+
throw new InvalidConfigurationError(`${path}.${key} must be a positive safe integer.`);
|
|
60
|
+
}
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
function assertVector3(value, path) {
|
|
64
|
+
const vector = requireRecord(value, path);
|
|
65
|
+
for (const component of ["x", "y", "z"]) {
|
|
66
|
+
const coordinate = vector[component];
|
|
67
|
+
if (typeof coordinate !== "number" || !Number.isFinite(coordinate)) {
|
|
68
|
+
throw new InvalidConfigurationError(`${path}.${component} must be a finite number.`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function assertStringArray(value, path) {
|
|
73
|
+
if (!Array.isArray(value) ||
|
|
74
|
+
value.some((entry) => typeof entry !== "string" || entry.trim().length === 0)) {
|
|
75
|
+
throw new InvalidConfigurationError(`${path} must be an array of non-empty strings.`);
|
|
76
|
+
}
|
|
77
|
+
return value;
|
|
78
|
+
}
|
|
79
|
+
function assertUniqueStrings(values, path) {
|
|
80
|
+
const normalized = values.map((value) => value.trim());
|
|
81
|
+
if (new Set(normalized).size !== normalized.length) {
|
|
82
|
+
throw new InvalidConfigurationError(`${path} must not contain duplicates.`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Parses and validates an untrusted camera configuration.
|
|
87
|
+
*
|
|
88
|
+
* @param value Unknown external data.
|
|
89
|
+
* @returns The original value narrowed to CameraConfiguration.
|
|
90
|
+
*/
|
|
91
|
+
export function parseCameraConfiguration(value) {
|
|
92
|
+
const camera = requireRecord(value, "camera");
|
|
93
|
+
const sensor = requireRecord(camera.sensor, "camera.sensor");
|
|
94
|
+
requirePositiveNumber(sensor, "widthMm", "camera.sensor");
|
|
95
|
+
requirePositiveNumber(sensor, "heightMm", "camera.sensor");
|
|
96
|
+
requirePositiveIntegerValue(sensor, "pixelWidth", "camera.sensor");
|
|
97
|
+
requirePositiveIntegerValue(sensor, "pixelHeight", "camera.sensor");
|
|
98
|
+
const lens = requireRecord(camera.lens, "camera.lens");
|
|
99
|
+
requirePositiveNumber(lens, "focalLengthMm", "camera.lens");
|
|
100
|
+
requirePositiveNumber(lens, "aperture", "camera.lens");
|
|
101
|
+
const exposure = requireRecord(camera.exposure, "camera.exposure");
|
|
102
|
+
requirePositiveNumber(exposure, "shutterSeconds", "camera.exposure");
|
|
103
|
+
requirePositiveNumber(exposure, "iso", "camera.exposure");
|
|
104
|
+
const focus = requireRecord(camera.focus, "camera.focus");
|
|
105
|
+
requirePositiveNumber(focus, "focusDistanceM", "camera.focus");
|
|
106
|
+
if (camera.stabilization !== undefined) {
|
|
107
|
+
const stabilization = requireRecord(camera.stabilization, "camera.stabilization");
|
|
108
|
+
requireBoolean(stabilization, "bodyEnabled", "camera.stabilization");
|
|
109
|
+
requireBoolean(stabilization, "lensEnabled", "camera.stabilization");
|
|
110
|
+
const support = requireString(stabilization, "support", "camera.stabilization");
|
|
111
|
+
if (!CAMERA_SUPPORTS.has(support)) {
|
|
112
|
+
throw new InvalidConfigurationError("camera.stabilization.support is invalid.");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Parses and validates an untrusted renderer-independent scene definition.
|
|
119
|
+
*
|
|
120
|
+
* @param value Unknown external data.
|
|
121
|
+
* @returns The original value narrowed to SceneDefinition.
|
|
122
|
+
*/
|
|
123
|
+
export function parseSceneDefinition(value) {
|
|
124
|
+
const scene = requireRecord(value, "scene");
|
|
125
|
+
requireString(scene, "schemaVersion", "scene");
|
|
126
|
+
requireString(scene, "id", "scene");
|
|
127
|
+
requireString(scene, "version", "scene");
|
|
128
|
+
const radiometry = requireRecord(scene.radiometry, "scene.radiometry");
|
|
129
|
+
const radiometryKind = requireString(radiometry, "kind", "scene.radiometry");
|
|
130
|
+
requirePositiveNumber(radiometry, "referenceValue", "scene.radiometry");
|
|
131
|
+
if (radiometryKind === "absolute-luminance") {
|
|
132
|
+
requirePositiveNumber(radiometry, "referenceLuminanceCdM2", "scene.radiometry");
|
|
133
|
+
}
|
|
134
|
+
else if (radiometryKind !== "relative-linear") {
|
|
135
|
+
throw new InvalidConfigurationError("scene.radiometry.kind is invalid.");
|
|
136
|
+
}
|
|
137
|
+
const capabilities = assertStringArray(scene.capabilities, "scene.capabilities");
|
|
138
|
+
assertUniqueStrings(capabilities, "scene.capabilities");
|
|
139
|
+
for (const capability of capabilities) {
|
|
140
|
+
if (!SCENE_CAPABILITIES.has(capability)) {
|
|
141
|
+
throw new InvalidConfigurationError(`Unknown scene capability: ${capability}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
if (!Array.isArray(scene.objects)) {
|
|
145
|
+
throw new InvalidConfigurationError("scene.objects must be an array.");
|
|
146
|
+
}
|
|
147
|
+
const objectIds = [];
|
|
148
|
+
scene.objects.forEach((objectValue, index) => {
|
|
149
|
+
const path = `scene.objects[${index}]`;
|
|
150
|
+
const object = requireRecord(objectValue, path);
|
|
151
|
+
objectIds.push(requireString(object, "id", path));
|
|
152
|
+
requirePositiveNumber(object, "distanceM", path);
|
|
153
|
+
if (object.label !== undefined && typeof object.label !== "string") {
|
|
154
|
+
throw new InvalidConfigurationError(`${path}.label must be a string.`);
|
|
155
|
+
}
|
|
156
|
+
if (object.tags !== undefined) {
|
|
157
|
+
assertStringArray(object.tags, `${path}.tags`);
|
|
158
|
+
}
|
|
159
|
+
if (object.motion !== undefined) {
|
|
160
|
+
const motion = requireRecord(object.motion, `${path}.motion`);
|
|
161
|
+
assertVector3(motion.linearVelocityMps, `${path}.motion.linearVelocityMps`);
|
|
162
|
+
if (motion.angularVelocityRadPerSec !== undefined) {
|
|
163
|
+
assertVector3(motion.angularVelocityRadPerSec, `${path}.motion.angularVelocityRadPerSec`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
assertUniqueStrings(objectIds, "scene.objects[].id");
|
|
168
|
+
if (scene.focusTargetIds !== undefined) {
|
|
169
|
+
const focusTargetIds = assertStringArray(scene.focusTargetIds, "scene.focusTargetIds");
|
|
170
|
+
assertUniqueStrings(focusTargetIds, "scene.focusTargetIds");
|
|
171
|
+
const knownIds = new Set(objectIds);
|
|
172
|
+
for (const focusTargetId of focusTargetIds) {
|
|
173
|
+
if (!knownIds.has(focusTargetId)) {
|
|
174
|
+
throw new InvalidConfigurationError(`Unknown scene focus target: ${focusTargetId}`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return value;
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=validation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/schema/validation.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type CalculationResult } from "../core/calculation-result.js";
|
|
2
|
+
export interface CalculatePixelPitchInput {
|
|
3
|
+
/** Physical sensor width in millimetres. */
|
|
4
|
+
sensorWidthMm: number;
|
|
5
|
+
/** Horizontal active pixel count. */
|
|
6
|
+
pixelWidth: number;
|
|
7
|
+
}
|
|
8
|
+
export interface PixelPitch {
|
|
9
|
+
/** Horizontal pixel pitch in micrometres. */
|
|
10
|
+
micrometers: number;
|
|
11
|
+
/** Horizontal pixel pitch in millimetres. */
|
|
12
|
+
millimeters: number;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Calculates horizontal pixel pitch from active sensor width and pixel count.
|
|
16
|
+
*
|
|
17
|
+
* @param input Sensor width and horizontal active pixel count.
|
|
18
|
+
* @returns Pixel pitch.
|
|
19
|
+
*/
|
|
20
|
+
export declare function calculatePixelPitch(input: CalculatePixelPitchInput): CalculationResult<PixelPitch>;
|
|
21
|
+
//# sourceMappingURL=pixel-pitch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pixel-pitch.d.ts","sourceRoot":"","sources":["../../src/sensor/pixel-pitch.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,+BAA+B,CAAC;AAMvC,MAAM,WAAW,wBAAwB;IACvC,4CAA4C;IAC5C,aAAa,EAAE,MAAM,CAAC;IACtB,qCAAqC;IACrC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAC;IACpB,6CAA6C;IAC7C,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,wBAAwB,GAC9B,iBAAiB,CAAC,UAAU,CAAC,CAc/B"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { calculatedResult } from "../core/calculation-result.js";
|
|
3
|
+
import { requirePositiveFinite, requirePositiveInteger } from "../core/validation.js";
|
|
4
|
+
/**
|
|
5
|
+
* Calculates horizontal pixel pitch from active sensor width and pixel count.
|
|
6
|
+
*
|
|
7
|
+
* @param input Sensor width and horizontal active pixel count.
|
|
8
|
+
* @returns Pixel pitch.
|
|
9
|
+
*/
|
|
10
|
+
export function calculatePixelPitch(input) {
|
|
11
|
+
requirePositiveFinite("sensorWidthMm", input.sensorWidthMm);
|
|
12
|
+
requirePositiveInteger("pixelWidth", input.pixelWidth);
|
|
13
|
+
const millimeters = input.sensorWidthMm / input.pixelWidth;
|
|
14
|
+
return calculatedResult({
|
|
15
|
+
millimeters,
|
|
16
|
+
micrometers: millimeters * 1000
|
|
17
|
+
}, "pixel-pitch", "1.0.0");
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=pixel-pitch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pixel-pitch.js","sourceRoot":"","sources":["../../src/sensor/pixel-pitch.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC,OAAO,EACL,gBAAgB,EAEjB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,uBAAuB,CAAC;AAgB/B;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAA+B;IAE/B,qBAAqB,CAAC,eAAe,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;IAC5D,sBAAsB,CAAC,YAAY,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAEvD,MAAM,WAAW,GAAG,KAAK,CAAC,aAAa,GAAG,KAAK,CAAC,UAAU,CAAC;IAE3D,OAAO,gBAAgB,CACrB;QACE,WAAW;QACX,WAAW,EAAE,WAAW,GAAG,IAAI;KAChC,EACD,aAAa,EACb,OAAO,CACR,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type CalculationResult } from "../core/calculation-result.js";
|
|
2
|
+
export interface CalculatePhotoelectronsInput {
|
|
3
|
+
/** Mean photons incident on the photosite/measurement region. */
|
|
4
|
+
incidentPhotons: number;
|
|
5
|
+
/** Quantum efficiency from 0 to 1 inclusive. */
|
|
6
|
+
quantumEfficiency: number;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Converts mean incident photon count to mean photoelectron count.
|
|
10
|
+
*
|
|
11
|
+
* This is an expectation value; the stochastic shot-noise realization is not
|
|
12
|
+
* generated by this deterministic calculation.
|
|
13
|
+
*/
|
|
14
|
+
export declare function calculatePhotoelectrons(input: CalculatePhotoelectronsInput): CalculationResult<number>;
|
|
15
|
+
export interface CalculateSignalToNoiseInput {
|
|
16
|
+
/** Mean signal in electrons. */
|
|
17
|
+
signalElectrons: number;
|
|
18
|
+
/** RMS read noise in electrons. */
|
|
19
|
+
readNoiseElectrons: number;
|
|
20
|
+
}
|
|
21
|
+
export interface SignalToNoise {
|
|
22
|
+
shotNoiseStdElectrons: number;
|
|
23
|
+
combinedNoiseStdElectrons: number;
|
|
24
|
+
snrLinear: number;
|
|
25
|
+
/** Null when the signal is zero because -Infinity dB is not JSON-safe. */
|
|
26
|
+
snrDb: number | null;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Calculates expected SNR from Poisson shot noise and independent RMS read
|
|
30
|
+
* noise.
|
|
31
|
+
*
|
|
32
|
+
* Variances are combined as signalElectrons + readNoiseElectrons^2.
|
|
33
|
+
* Dark current, fixed-pattern noise, quantization, clipping, and downstream
|
|
34
|
+
* processing are intentionally outside this primitive.
|
|
35
|
+
*/
|
|
36
|
+
export declare function calculateSignalToNoise(input: CalculateSignalToNoiseInput): CalculationResult<SignalToNoise>;
|
|
37
|
+
//# sourceMappingURL=signal-noise.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal-noise.d.ts","sourceRoot":"","sources":["../../src/sensor/signal-noise.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,+BAA+B,CAAC;AAWvC,MAAM,WAAW,4BAA4B;IAC3C,iEAAiE;IACjE,eAAe,EAAE,MAAM,CAAC;IACxB,gDAAgD;IAChD,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,4BAA4B,GAClC,iBAAiB,CAAC,MAAM,CAAC,CAmB3B;AAED,MAAM,WAAW,2BAA2B;IAC1C,gCAAgC;IAChC,eAAe,EAAE,MAAM,CAAC;IACxB,mCAAmC;IACnC,kBAAkB,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,qBAAqB,EAAE,MAAM,CAAC;IAC9B,yBAAyB,EAAE,MAAM,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,0EAA0E;IAC1E,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,2BAA2B,GACjC,iBAAiB,CAAC,aAAa,CAAC,CA8BlC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
import { calculatedResult } from "../core/calculation-result.js";
|
|
3
|
+
import { InvalidScientificInputError } from "../core/validation.js";
|
|
4
|
+
function requireNonNegativeFinite(name, value) {
|
|
5
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
6
|
+
throw new InvalidScientificInputError(`${name} must be a finite number greater than or equal to zero.`);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Converts mean incident photon count to mean photoelectron count.
|
|
11
|
+
*
|
|
12
|
+
* This is an expectation value; the stochastic shot-noise realization is not
|
|
13
|
+
* generated by this deterministic calculation.
|
|
14
|
+
*/
|
|
15
|
+
export function calculatePhotoelectrons(input) {
|
|
16
|
+
requireNonNegativeFinite("incidentPhotons", input.incidentPhotons);
|
|
17
|
+
if (!Number.isFinite(input.quantumEfficiency) ||
|
|
18
|
+
input.quantumEfficiency < 0 ||
|
|
19
|
+
input.quantumEfficiency > 1) {
|
|
20
|
+
throw new InvalidScientificInputError("quantumEfficiency must be a finite number from 0 through 1.");
|
|
21
|
+
}
|
|
22
|
+
return calculatedResult(input.incidentPhotons * input.quantumEfficiency, "mean-photoelectron-conversion", "1.0.0", ["Quantum efficiency is supplied by the caller"]);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Calculates expected SNR from Poisson shot noise and independent RMS read
|
|
26
|
+
* noise.
|
|
27
|
+
*
|
|
28
|
+
* Variances are combined as signalElectrons + readNoiseElectrons^2.
|
|
29
|
+
* Dark current, fixed-pattern noise, quantization, clipping, and downstream
|
|
30
|
+
* processing are intentionally outside this primitive.
|
|
31
|
+
*/
|
|
32
|
+
export function calculateSignalToNoise(input) {
|
|
33
|
+
requireNonNegativeFinite("signalElectrons", input.signalElectrons);
|
|
34
|
+
requireNonNegativeFinite("readNoiseElectrons", input.readNoiseElectrons);
|
|
35
|
+
const shotNoiseStdElectrons = Math.sqrt(input.signalElectrons);
|
|
36
|
+
const combinedNoiseStdElectrons = Math.sqrt(input.signalElectrons +
|
|
37
|
+
input.readNoiseElectrons * input.readNoiseElectrons);
|
|
38
|
+
const snrLinear = combinedNoiseStdElectrons === 0
|
|
39
|
+
? 0
|
|
40
|
+
: input.signalElectrons / combinedNoiseStdElectrons;
|
|
41
|
+
const snrDb = snrLinear === 0 ? null : 20 * Math.log10(snrLinear);
|
|
42
|
+
return calculatedResult({
|
|
43
|
+
shotNoiseStdElectrons,
|
|
44
|
+
combinedNoiseStdElectrons,
|
|
45
|
+
snrLinear,
|
|
46
|
+
snrDb
|
|
47
|
+
}, "poisson-shot-plus-read-noise-snr", "1.0.0", [
|
|
48
|
+
"Photon shot noise is Poisson",
|
|
49
|
+
"Read noise is supplied as independent RMS electrons",
|
|
50
|
+
"Dark current and fixed-pattern noise are not included"
|
|
51
|
+
]);
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=signal-noise.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"signal-noise.js","sourceRoot":"","sources":["../../src/sensor/signal-noise.ts"],"names":[],"mappings":"AAAA,sCAAsC;AAEtC,OAAO,EACL,gBAAgB,EAEjB,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,2BAA2B,EAAE,MAAM,uBAAuB,CAAC;AAEpE,SAAS,wBAAwB,CAAC,IAAY,EAAE,KAAa;IAC3D,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACzC,MAAM,IAAI,2BAA2B,CACnC,GAAG,IAAI,yDAAyD,CACjE,CAAC;IACJ,CAAC;AACH,CAAC;AASD;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAAmC;IAEnC,wBAAwB,CAAC,iBAAiB,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;IAEnE,IACE,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,iBAAiB,CAAC;QACzC,KAAK,CAAC,iBAAiB,GAAG,CAAC;QAC3B,KAAK,CAAC,iBAAiB,GAAG,CAAC,EAC3B,CAAC;QACD,MAAM,IAAI,2BAA2B,CACnC,6DAA6D,CAC9D,CAAC;IACJ,CAAC;IAED,OAAO,gBAAgB,CACrB,KAAK,CAAC,eAAe,GAAG,KAAK,CAAC,iBAAiB,EAC/C,+BAA+B,EAC/B,OAAO,EACP,CAAC,8CAA8C,CAAC,CACjD,CAAC;AACJ,CAAC;AAiBD;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CACpC,KAAkC;IAElC,wBAAwB,CAAC,iBAAiB,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;IACnE,wBAAwB,CAAC,oBAAoB,EAAE,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAEzE,MAAM,qBAAqB,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IAC/D,MAAM,yBAAyB,GAAG,IAAI,CAAC,IAAI,CACzC,KAAK,CAAC,eAAe;QACnB,KAAK,CAAC,kBAAkB,GAAG,KAAK,CAAC,kBAAkB,CACtD,CAAC;IACF,MAAM,SAAS,GACb,yBAAyB,KAAK,CAAC;QAC7B,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,KAAK,CAAC,eAAe,GAAG,yBAAyB,CAAC;IACxD,MAAM,KAAK,GAAG,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IAElE,OAAO,gBAAgB,CACrB;QACE,qBAAqB;QACrB,yBAAyB;QACzB,SAAS;QACT,KAAK;KACN,EACD,kCAAkC,EAClC,OAAO,EACP;QACE,8BAA8B;QAC9B,qDAAqD;QACrD,uDAAuD;KACxD,CACF,CAAC;AACJ,CAAC"}
|