@photivra/engine 2.19.0 → 2.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +112 -1
- package/CITATION.cff +1 -1
- package/NODE_SUPPORT.md +7 -5
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +14 -10
- package/dist/capture/dng-lens-correction-opcodes.d.ts +32 -0
- package/dist/capture/dng-lens-correction-opcodes.js +119 -0
- package/dist/capture/dng-lens-correction-opcodes.js.map +1 -0
- package/dist/capture/native-environment-lean.js +1 -5
- package/dist/capture/native-environment-lean.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/sensor-raw-producer.js +3 -5
- 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 +8 -2
- package/dist/equipment/generic-lens-catalogue.js +31 -2
- package/dist/equipment/generic-lens-catalogue.js.map +1 -1
- package/dist/equipment/generic-tier-assets.js +2 -3
- package/dist/equipment/generic-tier-assets.js.map +1 -1
- package/dist/equipment/generic-tier-presets.js +2 -3
- package/dist/equipment/generic-tier-presets.js.map +1 -1
- package/dist/facade/display-view-gpu.d.ts +7 -4
- package/dist/facade/display-view-gpu.js +4 -4
- package/dist/facade/display-view-gpu.js.map +1 -1
- package/dist/facade/display-view-lens-signal.d.ts +1 -1
- package/dist/facade/display-view-lens-signal.js +5 -3
- package/dist/facade/display-view-lens-signal.js.map +1 -1
- package/dist/facade/display-view.d.ts +3 -1
- package/dist/facade/display-view.js +1 -1
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/fast-methods.d.ts +50 -0
- package/dist/facade/fast-methods.js +50 -0
- package/dist/facade/fast-methods.js.map +1 -0
- package/dist/facade/fast-resolution.d.ts +12 -0
- package/dist/facade/fast-resolution.js +18 -0
- package/dist/facade/fast-resolution.js.map +1 -0
- package/dist/facade/generic-lens-capture.d.ts +3 -2
- package/dist/facade/generic-lens-capture.js +3 -2
- package/dist/facade/generic-lens-capture.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +51 -11
- package/dist/facade/simulate-capture.js +144 -75
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.d.ts +3 -2
- package/dist/facade/simulate-display-view.js +22 -12
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/optics/aperture-diameter.d.ts +47 -0
- package/dist/optics/aperture-diameter.js +50 -0
- package/dist/optics/aperture-diameter.js.map +1 -0
- package/dist/optics/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-psf.d.ts +18 -2
- package/dist/optics/generic-lens-psf.js +25 -2
- package/dist/optics/generic-lens-psf.js.map +1 -1
- package/dist/optics/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.js +2 -2
- package/dist/output/developed-lens-corrections.js.map +1 -1
- package/dist/output/generic-lens-corrections.d.ts +3 -1
- package/dist/output/generic-lens-corrections.js +8 -4
- 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-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/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 +137 -0
- package/dist/sensor/sensor-characteristics.js +115 -0
- package/dist/sensor/sensor-characteristics.js.map +1 -0
- package/dist/sensor/spatial-sample-reduction.js +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 +4 -0
- package/docs/API_REFERENCE.md +76 -2
- package/docs/QUICK_START.md +88 -6
- package/docs/USAGE.md +65 -2
- package/docs/api/capture-native-raw-dng.md +6 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/equipment-generic-lens-catalogue.md +7 -2
- package/docs/api/exports.json +152 -2
- package/docs/api/facade-display-view-gpu.md +5 -2
- package/docs/api/facade-fast-methods.md +91 -0
- package/docs/api/facade-simulate-capture.md +36 -11
- package/docs/api/facade-simulate-display-view.md +3 -2
- package/docs/api/optics-aperture-diameter.md +51 -0
- package/docs/api/optics-detail-limit.md +203 -0
- package/docs/api/output-crop-to-match-focal-length.md +67 -0
- package/docs/api/output-generic-lens-corrections.md +3 -1
- package/docs/api/output-lens-corrections.md +11 -1
- package/docs/api/output-print-size-blur-noise.md +115 -0
- package/docs/api/sensor-sensor-characteristics.md +105 -0
- package/llms.txt +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# facade/fast-methods.ts public contracts
|
|
2
|
+
|
|
3
|
+
The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
|
|
4
|
+
|
|
5
|
+
## SIMULATE_CAPTURE_FAST_METHODS
|
|
6
|
+
|
|
7
|
+
2.20.0: every speed-versus-accuracy choice of the capture facade and the display view (see the module comment).
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
SIMULATE_CAPTURE_FAST_METHODS: readonly SimulateCaptureFastMethod[] = Object.freeze([
|
|
11
|
+
{ id: "lens.catalogue/field-grid", override: "lens.catalogue", methods: "fast-and-exact", runs: ["simulateCapture"],
|
|
12
|
+
fast: "#568: the PSF blended over a field grid and tabulated expected electrons, verified per CFA phase against the exact chain",
|
|
13
|
+
exact: "the environment PSF quadrature with the PSF resolved at each query point", followsFormatDefault: true,
|
|
14
|
+
resolver: { file: CAPTURE, call: "resolveFast(catalogueFast, fastTop)" }, report: { applied: "lens.catalogue", assumption: "fast.lens.catalogue" } },
|
|
15
|
+
{ id: "lens.catalogue/cats-eye", override: "lens.catalogue", methods: "fast-and-exact", runs: ["simulateCapture", "simulateDisplayView", "prepareDisplayView"],
|
|
16
|
+
fast: "#567: with lens.defocus, the lens's bokeh without its cat's eye (the axis kernel everywhere)",
|
|
17
|
+
exact: "the cat's eye shaped at each field point (fast wides and normals)", followsFormatDefault: true,
|
|
18
|
+
resolver: { file: CAPTURE, call: "resolveFast(catalogueFast, fastTop)" }, report: { applied: "lens.catalogue", assumption: "fast.lens.catalogue.catsEye" } },
|
|
19
|
+
{ id: "lens.catalogue/view-psf-zones", override: "lens.catalogue", methods: "fast-and-exact", runs: ["simulateDisplayView", "prepareDisplayView"],
|
|
20
|
+
fast: "#569 (H1): 64 field zones along the longer side, each with the PSF at its centre",
|
|
21
|
+
exact: "one zone per site: the PSF at each site centre", followsFormatDefault: true,
|
|
22
|
+
resolver: { file: CAPTURE, call: "resolveFast(catalogueFast, fastTop)" }, report: { applied: "lens.catalogue", assumption: "view.lensPsf" } },
|
|
23
|
+
{ id: "lens.diffraction/rolling-shutter-taps", override: "lens.diffraction", methods: "fast-and-exact", runs: ["simulateCapture"],
|
|
24
|
+
fast: "#565: under rolling-shutter rotation, each PSF tap reads the scene at its own source point",
|
|
25
|
+
exact: "each tap reads the scene at its site-lattice point (ties on chart boundaries resolve as on the lattice)", followsFormatDefault: true,
|
|
26
|
+
resolver: { file: CAPTURE, call: "resolveFast(diffractionFast, fastTop)" }, report: { applied: "lens.diffraction", assumption: "fast.lens.diffraction" } },
|
|
27
|
+
{ id: "output.demosaic.ahd/view-low-signal-correction", override: "output.demosaic", methods: "fast-and-exact", runs: ["simulateDisplayView", "prepareDisplayView"],
|
|
28
|
+
fast: "(H3) 2048 draws per correction state, applied where a pixel's linear noise exceeds 0.01",
|
|
29
|
+
exact: "8192 draws per state, at the same 0.01 threshold", followsFormatDefault: true,
|
|
30
|
+
resolver: { file: CAPTURE, call: "resolveFast(demosaicFast, fastTop)" }, report: { applied: "output.demosaic.ahd", assumption: "view.demosaic" } },
|
|
31
|
+
{ id: "view.uniform-pixel-sampling", override: "fast", methods: "fast-and-exact", runs: ["simulateDisplayView", "prepareDisplayView"],
|
|
32
|
+
fast: "(H2) a quarter of the samples per axis in display pixels whose pilot finds no detail",
|
|
33
|
+
exact: "the full samples per axis in every display pixel", followsFormatDefault: true,
|
|
34
|
+
resolver: { file: CAPTURE, call: "uniformSampling: fastTop" }, report: { assumption: "view.estimation" } },
|
|
35
|
+
{ id: "prepareDisplayView.gpu", override: "fast", methods: "fast-and-exact", runs: ["prepareDisplayView"],
|
|
36
|
+
fast: "gpu \"auto\": WebGPU float32 where a conforming device supports the request (a bounded approximation), else the CPU reference",
|
|
37
|
+
exact: "gpu \"off\": the CPU reference; an explicit gpu wins", followsFormatDefault: true,
|
|
38
|
+
resolver: { file: VIEW, call: "parsePrepareDisplayViewOptions(prepareOptions, (options as { fast?: unknown } | null | undefined)?.fast !== false)" },
|
|
39
|
+
report: { field: "diagnostics.fast.execution" } },
|
|
40
|
+
{ id: "sensor.midGrey", override: "fast", methods: "fast-and-exact", runs: ["simulateCapture", "simulateDisplayView", "prepareDisplayView"],
|
|
41
|
+
fast: "#535: the mid-gray noise figure is not computed (about 0.3 ms)", exact: "result.sensor.midGrey is computed", followsFormatDefault: true,
|
|
42
|
+
resolver: { file: CAPTURE, call: "fastTop ? undefined : 0" }, report: { field: "sensor.midGrey" } },
|
|
43
|
+
{ id: "lens.diffraction", override: "lens.diffraction", methods: "exact-only", runs: ["simulateCapture", "simulateDisplayView", "prepareDisplayView"],
|
|
44
|
+
fast: "", exact: "the diffraction kernels (a fast method only under rolling-shutter rotation, above)", followsFormatDefault: true,
|
|
45
|
+
resolver: { file: CAPTURE, call: "resolveFast(diffractionFast, fastTop)" }, report: { applied: "lens.diffraction" } },
|
|
46
|
+
{ id: "sensor.antiAliasing", override: "sensor.antiAliasing", methods: "exact-only", runs: ["simulateCapture", "simulateDisplayView", "prepareDisplayView"],
|
|
47
|
+
fast: "", exact: "the point-splitting filter", followsFormatDefault: true,
|
|
48
|
+
resolver: { file: CAPTURE, call: "fastInUse" }, report: { applied: "sensor.antiAliasing" } },
|
|
49
|
+
{ id: "sensor.pixelAperture.box", override: "sensor.pixelAperture", methods: "exact-only", runs: ["simulateCapture", "simulateDisplayView", "prepareDisplayView"],
|
|
50
|
+
fast: "", exact: "the N × N midpoint rule", followsFormatDefault: true,
|
|
51
|
+
resolver: { file: CAPTURE, call: "fastInUse" }, report: { applied: "sensor.pixelAperture.box" } },
|
|
52
|
+
{ id: "output.demosaic.ahd", override: "output.demosaic", methods: "exact-only", runs: ["simulateCapture"],
|
|
53
|
+
fast: "", exact: "the AHD demosaic of the capture (a fast method only in the display view's correction, above)", followsFormatDefault: true,
|
|
54
|
+
resolver: { file: CAPTURE, call: "resolveFast(demosaicFast, fastTop)" }, report: { applied: "output.demosaic.ahd" } }
|
|
55
|
+
] as const satisfies readonly SimulateCaptureFastMethod[])
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## SimulateCaptureFastMethod
|
|
59
|
+
|
|
60
|
+
One speed-versus-accuracy choice of {@link SIMULATE_CAPTURE_FAST_METHODS} (2.20.0).
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
export interface SimulateCaptureFastMethod {
|
|
64
|
+
/** A short id: the option path and, where one option has several fast methods, the method. */
|
|
65
|
+
id: string;
|
|
66
|
+
/**
|
|
67
|
+
* The request path whose record takes the `fast` override (`allowKeys` path in simulate-capture.ts), or "fast" when only the
|
|
68
|
+
* top-level flag sets it (a choice with no option record of its own).
|
|
69
|
+
*/
|
|
70
|
+
override: string;
|
|
71
|
+
/** "fast-and-exact": both methods exist; "exact-only": the record takes `fast`, but there is no fast method yet (reports false). */
|
|
72
|
+
methods: "fast-and-exact" | "exact-only";
|
|
73
|
+
/** Where it applies. */
|
|
74
|
+
runs: readonly ("simulateCapture" | "simulateDisplayView" | "prepareDisplayView")[];
|
|
75
|
+
/** The fast method and the exact one, in words (empty for exact-only). */
|
|
76
|
+
fast: string;
|
|
77
|
+
exact: string;
|
|
78
|
+
/** Always true: no choice keeps its own default (the test checks it). */
|
|
79
|
+
followsFormatDefault: true;
|
|
80
|
+
/**
|
|
81
|
+
* The resolver call in the source (`resolveFast(<record flag>, fastTop)`, or `fastTop` itself for a top-level choice), which the
|
|
82
|
+
* test finds in the named file.
|
|
83
|
+
*/
|
|
84
|
+
resolver: { file: string; call: string };
|
|
85
|
+
/**
|
|
86
|
+
* How the result reports it: the `diagnostics.fast.applied` key (true where the fast method ran), an assumption id the fast
|
|
87
|
+
* method adds, or another result field. At least one; the test finds each assumption id in the source.
|
|
88
|
+
*/
|
|
89
|
+
report: { applied?: string; assumption?: string; field?: string };
|
|
90
|
+
}
|
|
91
|
+
```
|
|
@@ -66,6 +66,8 @@ export interface DisplayViewResult {
|
|
|
66
66
|
/** #516: as in the capture result. */
|
|
67
67
|
isoControl: "manual" | "automatic"; autoIso?: SimulateCaptureAutoIsoReport };
|
|
68
68
|
scene: SimulateCaptureResult["scene"];
|
|
69
|
+
/** #555, #535: as in the capture result. */
|
|
70
|
+
sensor: SimulateCaptureSensorReport;
|
|
69
71
|
/**
|
|
70
72
|
* Work done: real pixels sampled, real-site expectations evaluated, scene queries, display pixels sampled in full (detail) or with
|
|
71
73
|
* a quarter of the samples per axis (uniform), display pixels whose value includes the AHD low-signal correction, and wall-clock
|
|
@@ -425,6 +427,12 @@ One part of a whole-frame DNG (#391): a region render's packed RAW and where its
|
|
|
425
427
|
export interface SimulateCaptureDngPart {
|
|
426
428
|
raw: Pick<NativeRawOutput, "codes" | "blackLevels" | "digitalSaturationCodes" | "saturationFlags" | "effectiveSaturationCodes">;
|
|
427
429
|
region: { nativeRect: { x: number; y: number; width: number; height: number } };
|
|
430
|
+
/**
|
|
431
|
+
* 2.20.0: true when a fast method ran for this part (`Object.values(result.diagnostics.fast.applied).includes(true)`). The join
|
|
432
|
+
* refuses such a part unless its own request resolves to fast too (a DNG, JPEG or PNG is exact by default): render the parts with
|
|
433
|
+
* the same `fast` as the join, or with `fast: false`.
|
|
434
|
+
*/
|
|
435
|
+
fast?: boolean;
|
|
428
436
|
}
|
|
429
437
|
```
|
|
430
438
|
|
|
@@ -450,8 +458,10 @@ export type SimulateCaptureFastOption = typeof SIMULATE_CAPTURE_FAST_OPTIONS[num
|
|
|
450
458
|
|
|
451
459
|
```ts
|
|
452
460
|
export interface SimulateCaptureFastReport {
|
|
453
|
-
/**
|
|
461
|
+
/** 2.20.0: the resolved top-level value (`fast`, else the format default), or true when any option's record sets `fast: true`. */
|
|
454
462
|
requested: boolean;
|
|
463
|
+
/** 2.20.0: where the top-level value came from: the request's `fast`, or the format default (see {@link SimulateCaptureOptions.fast}). */
|
|
464
|
+
defaultedFrom: "request" | "format";
|
|
455
465
|
/** Each fast-capable option that ran, in {@link SIMULATE_CAPTURE_FAST_OPTIONS} order: true where its fast method ran. */
|
|
456
466
|
applied: Readonly<Partial<Record<SimulateCaptureFastOption, boolean>>>;
|
|
457
467
|
/** `engine-reference` when every option ran exact; `approximation-with-a-priori-bound` when any fast method ran (the WebGPU execution record's values). */
|
|
@@ -462,6 +472,9 @@ export interface SimulateCaptureFastReport {
|
|
|
462
472
|
## SimulateCaptureLensCatalogueRecord
|
|
463
473
|
|
|
464
474
|
`lens.catalogue` as a record (#568): the lens id as `kind` and the `fast` override (see {@link SimulateCaptureOptions.fast}).
|
|
475
|
+
Fast: the tabulated fast method (#568), and with `lens.defocus` the lens's bokeh without its cat's eye (#567; fast wides and
|
|
476
|
+
normals only), whose field-varying PSF needs a finer fast grid (measured 2.2–3.4× slower). Exact: the per-site PSF with the
|
|
477
|
+
cat's eye. Blades and spherical aberration always apply (they cost nothing).
|
|
465
478
|
|
|
466
479
|
```ts
|
|
467
480
|
export interface SimulateCaptureLensCatalogueRecord { kind: string; fast?: boolean }
|
|
@@ -557,9 +570,9 @@ export interface SimulateCaptureOptions {
|
|
|
557
570
|
* factor and it replaces the tier lens's focal length and f-number range (the transmission is kept). The capture renders the
|
|
558
571
|
* lens's character, all labelled approximations: its PSF (diffraction with the lens's Seidel aberrations, axial and lateral
|
|
559
572
|
* colour, at the resolved f-number and focus) through the environment PSF quadrature, its radial distortion, its vignetting
|
|
560
|
-
* and, for the fisheyes, the equisolid projection. Flare
|
|
573
|
+
* and, for the fisheyes, the equisolid projection. Flare and focus breathing are not rendered. Not combinable
|
|
561
574
|
* with `prime`, `focalLengthMm`, `projection`, `distortion`, `vignetting`, `psf`, `diffraction` or `pupilSampling` (the
|
|
562
|
-
* lens sets them); `defocus` convolves the
|
|
575
|
+
* lens sets them); `defocus` convolves the lens's bokeh kernel (#567) into its PSF. The display view renders it with the PSF taken per field zone (#569; docs/DISPLAY_VIEW.md). Default off.
|
|
563
576
|
* #568: the record form `{ kind: <lens id>, fast }` overrides the top-level `fast` flag for this option; the fast method
|
|
564
577
|
* blends the PSF between exactly resolved kernels on a field grid and tabulates each site's expected signal
|
|
565
578
|
* (docs/LENS_CATALOGUE.md, "Fast method").
|
|
@@ -700,7 +713,7 @@ export interface SimulateCaptureOptions {
|
|
|
700
713
|
exposureCompensationStops?: number;
|
|
701
714
|
/** Default "scene-illuminant". */
|
|
702
715
|
whiteBalance?: SimulateCaptureWhiteBalance;
|
|
703
|
-
/** JPEG constant quantization step 1-255 (not a manufacturer 'quality'). Default 2. */
|
|
716
|
+
/** JPEG constant quantization step 1-255 (not a manufacturer 'quality'). Default 1 (2.20.0; 2 before), the full-quality export. */
|
|
704
717
|
jpegQuantizationStep?: number;
|
|
705
718
|
/** PNG only. Default "srgb". */
|
|
706
719
|
colorEncoding?: SimulateCaptureColorEncoding;
|
|
@@ -715,8 +728,13 @@ export interface SimulateCaptureOptions {
|
|
|
715
728
|
demosaic?: SimulateCaptureDemosaic | SimulateCaptureDemosaicRecord;
|
|
716
729
|
/** Default "margin". */
|
|
717
730
|
borderHandling?: SimulateCaptureBorderHandling;
|
|
718
|
-
/**
|
|
719
|
-
|
|
731
|
+
/**
|
|
732
|
+
* DNG only: optional capture-event time (YYYY-MM-DDTHH:mm:ss.sssZ); without it no date tags are written. Opt-in (#587), with
|
|
733
|
+
* `lens.catalogue`: `lensCorrectionOpcodes` writes the selected corrections of {@link SimulateCaptureLensCorrections} into the DNG as
|
|
734
|
+
* optional DNG opcodes in OpcodeList3 (FixVignetteRadial, WarpRectilinear with the zoom-to-fill; docs/NATIVE_RAW_AND_PRINT.md), for RAW editors
|
|
735
|
+
* that apply them; the RAW codes are unchanged. Whole frame only (no `region`). All off (or omitted): the same DNG bytes as without it.
|
|
736
|
+
*/
|
|
737
|
+
dng?: { capturedAtUtc?: string; lensCorrectionOpcodes?: SimulateCaptureLensCorrections };
|
|
720
738
|
/**
|
|
721
739
|
* Opt-in (2.4) text written into the file for formats "jpeg", "png" and "dng": the caller's description,
|
|
722
740
|
* software, copyright and comment, unchanged (JPEG XMP + COM, PNG tEXt/iTXt, DNG TIFF tags + XMP; see the
|
|
@@ -768,7 +786,8 @@ export interface SimulateCaptureOptions {
|
|
|
768
786
|
};
|
|
769
787
|
/** Camera motion and shutter type. Default: still camera, global electronic shutter. */
|
|
770
788
|
motion?: SimulateCaptureMotion;
|
|
771
|
-
sensor
|
|
789
|
+
/** #555: plus the sensor characteristics ({@link SimulateCaptureSensorCharacteristics}), each off by default. */
|
|
790
|
+
sensor?: SimulateCaptureSensorCharacteristics & {
|
|
772
791
|
/**
|
|
773
792
|
* Pixel pitch in micrometres, 0.5–1000. Default 6. The simulated sensor is output width × pitch wide. 2.5: above 100 µm the
|
|
774
793
|
* capture is a downsized view of a whole frame (for example 36 mm / 48 pixels = 750 µm), recorded in the assumption
|
|
@@ -795,10 +814,14 @@ export interface SimulateCaptureOptions {
|
|
|
795
814
|
pixelAperture?: SimulateCapturePixelAperture;
|
|
796
815
|
};
|
|
797
816
|
/**
|
|
798
|
-
* Exact or fast (#395). `false
|
|
799
|
-
*
|
|
800
|
-
*
|
|
801
|
-
*
|
|
817
|
+
* Exact or fast (#395). `false`: every option runs its exact method, the engine reference. `true`: each option listed in
|
|
818
|
+
* {@link SIMULATE_CAPTURE_FAST_OPTIONS} runs its fast method where it has one, a measured, bounded approximation of the exact
|
|
819
|
+
* one; an option without a fast method runs exact. An option's own record `fast` overrides this for that option only.
|
|
820
|
+
* Omitted (2.20.0), the format decides: fast for `output.format` "srgb-8" and "srgb-16" and for the display view, exact for
|
|
821
|
+
* "dng", "jpeg" and "png" (exports) and for `simulateIdealReference`. `diagnostics.fast` reports the resolved value, where it
|
|
822
|
+
* came from and what ran. Fast methods: `lens.catalogue` (#568, #567) and `lens.diffraction` under rolling-shutter rotation
|
|
823
|
+
* (#565); in the display view also its catalogue PSF zones, its sampling of uniform pixels and its AHD low-signal correction
|
|
824
|
+
* (docs/DISPLAY_VIEW.md). The exact result also carries `result.sensor.midGrey` (#535). Calculators should pass `fast: false`.
|
|
802
825
|
*/
|
|
803
826
|
fast?: boolean;
|
|
804
827
|
/** Aborting cancels the running task; the promise rejects with a domain error and returns no partial image. */
|
|
@@ -994,6 +1017,8 @@ export interface SimulateCaptureResult {
|
|
|
994
1017
|
limitations: readonly string[];
|
|
995
1018
|
};
|
|
996
1019
|
assumptions: readonly SimulateCaptureAssumption[];
|
|
1020
|
+
/** #555, #535: the sensor characteristics used, the calculated dynamic range and (when the request resolves to exact) the mid-grey noise figure. */
|
|
1021
|
+
sensor: SimulateCaptureSensorReport;
|
|
997
1022
|
}
|
|
998
1023
|
```
|
|
999
1024
|
|
|
@@ -65,9 +65,10 @@ responses through the environment chain, the diffraction kernels, readout moment
|
|
|
65
65
|
about 0.3–1.3 s per call on a 24 MP request; a region costs only its own pixels. Errors in the request are raised here;
|
|
66
66
|
errors in the view by `render`.
|
|
67
67
|
|
|
68
|
-
2.11 (#481): `prepareOptions.gpu` chooses the renderer: `"off"`
|
|
68
|
+
2.11 (#481): `prepareOptions.gpu` chooses the renderer: `"off"` is the CPU reference, today's bytes; `"auto"` renders
|
|
69
69
|
on WebGPU when a conforming device is available and the request is supported, else on the CPU, labelled; `"require"` fails
|
|
70
|
-
instead of falling back (`ImageKernelExecutionError`, acquisition failures here, later ones from `render`).
|
|
70
|
+
instead of falling back (`ImageKernelExecutionError`, acquisition failures here, later ones from `render`). 2.20.0: the default is
|
|
71
|
+
`"auto"` when the request resolves to fast (the view's default) and `"off"` with `fast: false`. `gpuDevice` is a
|
|
71
72
|
WebGPU entry point or an `ImageKernelAccelerator` whose device the view shares; without it the engine acquires one device
|
|
72
73
|
for the prepared view (the request's `signal` aborts the acquisition) and `dispose()` releases it. The realm's
|
|
73
74
|
`setWebGpuEnabled(false)` vetoes both GPU modes (`webgpu-disabled`). See docs/DISPLAY_VIEW.md, "Rendering on the GPU".
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# optics/aperture-diameter.ts public contracts
|
|
2
|
+
|
|
3
|
+
The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
|
|
4
|
+
|
|
5
|
+
## ApertureDiameter
|
|
6
|
+
|
|
7
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
export interface ApertureDiameter {
|
|
11
|
+
/** Focal length supplied by the caller, in millimetres. */
|
|
12
|
+
focalLengthMm: number;
|
|
13
|
+
/** f-number supplied by the caller. */
|
|
14
|
+
fNumber: number;
|
|
15
|
+
/** Entrance-pupil diameter D = focalLengthMm / fNumber, in millimetres. */
|
|
16
|
+
entrancePupilDiameterMm: number;
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## calculateApertureDiameter
|
|
21
|
+
|
|
22
|
+
Entrance-pupil (aperture) diameter in millimetres: focal length ÷ f-number.
|
|
23
|
+
|
|
24
|
+
Exact by the definition of the f-number for an ideal lens focused at infinity. It is the
|
|
25
|
+
entrance pupil (the aperture stop as seen from the front), not the diaphragm's physical
|
|
26
|
+
opening. Close focus (working f-number N·(1 + m/P) with pupil magnification P) and focal-length
|
|
27
|
+
changes while focusing are not modelled.
|
|
28
|
+
|
|
29
|
+
Example: 50 mm at f/2 gives 25 mm; at f/4, 12.5 mm (half the diameter, a quarter of the area:
|
|
30
|
+
two stops).
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
export function calculateApertureDiameter(
|
|
34
|
+
input: CalculateApertureDiameterInput
|
|
35
|
+
): CalculationResult<ApertureDiameter>;
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
This call is synchronous; any supplied provider must follow its explicit synchronous contract.
|
|
39
|
+
|
|
40
|
+
## CalculateApertureDiameterInput
|
|
41
|
+
|
|
42
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
export interface CalculateApertureDiameterInput {
|
|
46
|
+
/** Focal length of the lens in millimetres (the physical focal length, not a 35 mm equivalent). */
|
|
47
|
+
focalLengthMm: number;
|
|
48
|
+
/** f-number set on the lens (N = focal length / entrance-pupil diameter). */
|
|
49
|
+
fNumber: number;
|
|
50
|
+
}
|
|
51
|
+
```
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# optics/detail-limit.ts public contracts
|
|
2
|
+
|
|
3
|
+
The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
|
|
4
|
+
|
|
5
|
+
## calculateDetailLimit
|
|
6
|
+
|
|
7
|
+
Which limits detail for given settings: the lens (diffraction at this f-number, aberrations, defocus) or the sensor's pixel
|
|
8
|
+
pitch. Returns the optics' resolution (MTF50 and the diffraction cut-off, cycles/mm), the sampling's (Nyquist frequency
|
|
9
|
+
and, with a Bayer filter, the red and blue sites'), the system MTF50 (optics × pixel aperture), the f-number from which
|
|
10
|
+
diffraction limits on this sensor, and which one limits.
|
|
11
|
+
|
|
12
|
+
The sensor limits when the optics still pass 50 % contrast at the Nyquist frequency 1/(2p); otherwise the lens does,
|
|
13
|
+
through diffraction when its wavefront error is within the Maréchal criterion (RMS ≤ λ/14), else through aberrations or
|
|
14
|
+
defocus. The optics MTF is the exact scalar OTF of the stated wavefront at one wavelength; the decision rule and the
|
|
15
|
+
catalogue lenses' wavefronts are approximations.
|
|
16
|
+
|
|
17
|
+
Example: an ideal 50 mm lens at f/22 on a 36 × 24 mm, 6000-pixel-wide sensor (6 µm) is limited by diffraction (optics
|
|
18
|
+
MTF50 ≈ 33 cycles/mm against a Nyquist frequency of ≈ 83); at f/4 the pixel pitch limits.
|
|
19
|
+
|
|
20
|
+
```ts
|
|
21
|
+
export function calculateDetailLimit(input: CalculateDetailLimitInput): CalculationResult<DetailLimit>;
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
This call is synchronous; any supplied provider must follow its explicit synchronous contract.
|
|
25
|
+
|
|
26
|
+
## CalculateDetailLimitInput
|
|
27
|
+
|
|
28
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
export interface CalculateDetailLimitInput {
|
|
32
|
+
/** Active imaging area and horizontal pixel count; pitch = widthMm / pixelWidth. */
|
|
33
|
+
sensor: {
|
|
34
|
+
widthMm: number;
|
|
35
|
+
heightMm: number;
|
|
36
|
+
pixelWidth: number;
|
|
37
|
+
/** Default `"bayer"`. `"none"`: every site records full detail (a monochrome sensor). */
|
|
38
|
+
colorFilterArray?: "bayer" | "none";
|
|
39
|
+
};
|
|
40
|
+
lens: DetailLimitIdealLens | DetailLimitCatalogueLens;
|
|
41
|
+
/** f-number set on the lens. */
|
|
42
|
+
fNumber: number;
|
|
43
|
+
/**
|
|
44
|
+
* Focus and subject distances from the lens in meters. Omitted: focused at infinity, subject in focus. `subjectDistanceM`
|
|
45
|
+
* omitted: the subject is at the focus distance.
|
|
46
|
+
*/
|
|
47
|
+
focus?: { focusDistanceM: number; subjectDistanceM?: number };
|
|
48
|
+
/** Field height as a fraction of the half-diagonal, 0 (center, default) to 1 (corner). Only aberrations depend on it. */
|
|
49
|
+
fieldPosition?: number;
|
|
50
|
+
/** Wavelength in nanometres, 380–780. Default 550. */
|
|
51
|
+
wavelengthNm?: number;
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## DETAIL_LIMIT_LIMITS
|
|
56
|
+
|
|
57
|
+
Limits of `calculateDetailLimit`: the wavelength range and the largest wavefront (sum of the magnitudes of its terms, in
|
|
58
|
+
waves) whose transfer function is integrated. Beyond it the blur is geometric and the quadrature would grow without
|
|
59
|
+
bound; use `calculateDefocusCircle` for such a blur.
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
DETAIL_LIMIT_LIMITS = Object.freeze({ minimumWavelengthNm: 380, maximumWavelengthNm: 780, maximumWavefrontWaves: 64 })
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## DetailLimit
|
|
66
|
+
|
|
67
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
export interface DetailLimit {
|
|
71
|
+
/** The lens or the sensor. */
|
|
72
|
+
limitedBy: DetailLimitedBy;
|
|
73
|
+
/** Diffraction, aberrations or defocus (the lens), or the pixel pitch (the sensor). */
|
|
74
|
+
limitingFactor: DetailLimitingFactor;
|
|
75
|
+
/** Optics MTF50 / Nyquist frequency: at least 1 when the sensor limits. */
|
|
76
|
+
opticsToNyquistRatio: number;
|
|
77
|
+
fNumber: number;
|
|
78
|
+
wavelengthNm: number;
|
|
79
|
+
/** Physical focal length used (the catalogue lens's on this sensor), mm. */
|
|
80
|
+
focalLengthMm: number;
|
|
81
|
+
fieldPosition: number;
|
|
82
|
+
optics: DetailLimitOptics;
|
|
83
|
+
sampling: DetailLimitSampling;
|
|
84
|
+
system: DetailLimitSystem;
|
|
85
|
+
/**
|
|
86
|
+
* The f-number at which an aberration-free lens in focus has its MTF50 at the Nyquist frequency (2·p·0.40397/λ at
|
|
87
|
+
* infinity focus): from it on, diffraction limits detail on this sensor whatever the lens.
|
|
88
|
+
*/
|
|
89
|
+
diffractionLimitedFNumber: number;
|
|
90
|
+
basis: Readonly<Record<"optics" | "sampling" | "system" | "limitedBy", DetailLimitBasis>>;
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## DetailLimitBasis
|
|
95
|
+
|
|
96
|
+
Whether a quantity is exact under its stated conditions or an approximation.
|
|
97
|
+
|
|
98
|
+
```ts
|
|
99
|
+
export interface DetailLimitBasis { kind: "exact" | "approximation"; statement: string }
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## DetailLimitCatalogueLens
|
|
103
|
+
|
|
104
|
+
A generic catalogue lens (`GENERIC_LENS_IDS`), resolved for the sensor as `resolveGenericLens` does.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
export interface DetailLimitCatalogueLens {
|
|
108
|
+
kind: "catalogue";
|
|
109
|
+
lensId: string;
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## DetailLimitedBy
|
|
114
|
+
|
|
115
|
+
Which component limits detail.
|
|
116
|
+
|
|
117
|
+
```ts
|
|
118
|
+
export type DetailLimitedBy = "lens" | "sensor";
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## DetailLimitIdealLens
|
|
122
|
+
|
|
123
|
+
A lens with no aberrations (diffraction only).
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
export interface DetailLimitIdealLens {
|
|
127
|
+
kind: "ideal";
|
|
128
|
+
/** Physical focal length in millimeters (for the working f-number and the defocus of a subject off the focus distance). */
|
|
129
|
+
focalLengthMm: number;
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## DetailLimitingFactor
|
|
134
|
+
|
|
135
|
+
What limits it: the lens's diffraction, aberrations or defocus, or the sensor's pixel pitch.
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
export type DetailLimitingFactor = "diffraction" | "aberrations" | "defocus" | "pixel-pitch";
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## DetailLimitOptics
|
|
142
|
+
|
|
143
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
export interface DetailLimitOptics {
|
|
147
|
+
/** Working f-number N_w = N·v/f (thin-lens image distance v; N at infinity focus). */
|
|
148
|
+
workingFNumber: number;
|
|
149
|
+
/** Diffraction cut-off 1/(λ·N_w) in cycles/mm: no optics passes detail beyond it. */
|
|
150
|
+
cutoffCyclesPerMm: number;
|
|
151
|
+
/** Frequency where the optics' MTF falls to 0.5, the lower of the two directions, cycles/mm. */
|
|
152
|
+
mtf50CyclesPerMm: number;
|
|
153
|
+
/** MTF50 for detail along the radius from the image center (tangential), cycles/mm. */
|
|
154
|
+
tangentialMtf50CyclesPerMm: number;
|
|
155
|
+
/** MTF50 for detail across the radius (sagittal), cycles/mm. Equal to the tangential value at the center. */
|
|
156
|
+
sagittalMtf50CyclesPerMm: number;
|
|
157
|
+
/** MTF50 of an aberration-free lens in focus at the same working f-number (0.40397·cut-off), cycles/mm. */
|
|
158
|
+
diffractionMtf50CyclesPerMm: number;
|
|
159
|
+
/** Optics MTF at the sensor's Nyquist frequency (lower direction). */
|
|
160
|
+
mtfAtNyquist: number;
|
|
161
|
+
/** RMS wavefront error in waves at the wavelength: of the aberrations, of the defocus, and of both together. */
|
|
162
|
+
rmsWavefrontErrorWaves: { aberrations: number; defocus: number; total: number };
|
|
163
|
+
/** Whether the total RMS wavefront error is within the Maréchal criterion λ/14. */
|
|
164
|
+
withinMarechalCriterion: boolean;
|
|
165
|
+
/** Geometric blur-circle diameter of the defocus on the sensor, mm (0 in focus). */
|
|
166
|
+
defocusBlurDiameterMm: number;
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## DetailLimitSampling
|
|
171
|
+
|
|
172
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
export interface DetailLimitSampling {
|
|
176
|
+
colorFilterArray: "bayer" | "none";
|
|
177
|
+
/** Pixel pitch in micrometres: widthMm / pixelWidth. */
|
|
178
|
+
pixelPitchMicrometers: number;
|
|
179
|
+
/** 1/(2p), cycles/mm: the finest detail the pixel grid records without aliasing (Bayer green sites too, along rows and columns). */
|
|
180
|
+
nyquistCyclesPerMm: number;
|
|
181
|
+
/** Bayer only: 1/(4p), the Nyquist frequency of the red and blue sites. Null without a color filter array. */
|
|
182
|
+
redBlueNyquistCyclesPerMm: number | null;
|
|
183
|
+
/** Pixel aperture MTF at the Nyquist frequency: sinc(1/2) = 2/π. */
|
|
184
|
+
pixelApertureMtfAtNyquist: number;
|
|
185
|
+
/** Frequency where the pixel aperture MTF falls to 0.5 (0.60335/p), cycles/mm: beyond the Nyquist frequency. */
|
|
186
|
+
pixelApertureMtf50CyclesPerMm: number;
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## DetailLimitSystem
|
|
191
|
+
|
|
192
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
export interface DetailLimitSystem {
|
|
196
|
+
/** MTF50 of optics × pixel aperture (lower direction), cycles/mm. Detail above the Nyquist frequency aliases. */
|
|
197
|
+
mtf50CyclesPerMm: number;
|
|
198
|
+
/** The same in line pairs per picture height (cycles/mm × heightMm). */
|
|
199
|
+
mtf50LinePairsPerPictureHeight: number;
|
|
200
|
+
/** System MTF at the Nyquist frequency (lower direction). */
|
|
201
|
+
mtfAtNyquist: number;
|
|
202
|
+
}
|
|
203
|
+
```
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# output/crop-to-match-focal-length.ts public contracts
|
|
2
|
+
|
|
3
|
+
The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
|
|
4
|
+
|
|
5
|
+
## calculateCropToMatchFocalLength
|
|
6
|
+
|
|
7
|
+
The centred crop that makes an image from a shorter lens match a longer lens's framing from the
|
|
8
|
+
same position: crop factor target ÷ focal length, and the pixels and megapixels left.
|
|
9
|
+
|
|
10
|
+
Exact for ideal rectilinear lenses focused at infinity, to whole pixels (the crop is
|
|
11
|
+
`calculateCenteredCrop`'s, which keeps the aspect ratio). At close focus the matching factor is
|
|
12
|
+
larger, F·(d − f)/(f·(d − F)) at object distance d (thin lens); that, distortion and focus
|
|
13
|
+
breathing are not modelled. The crop has the longer lens's framing and perspective but fewer
|
|
14
|
+
pixels: it records no more detail than the cropped part of the shorter lens's image.
|
|
15
|
+
|
|
16
|
+
Example: a 24 MP (6000 × 4000) image at 50 mm cropped to match 100 mm: factor 2, 3000 × 2000,
|
|
17
|
+
6 MP.
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
export function calculateCropToMatchFocalLength(
|
|
21
|
+
input: CalculateCropToMatchFocalLengthInput
|
|
22
|
+
): CalculationResult<CropToMatchFocalLength>;
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
This call is synchronous; any supplied provider must follow its explicit synchronous contract.
|
|
26
|
+
|
|
27
|
+
## CalculateCropToMatchFocalLengthInput
|
|
28
|
+
|
|
29
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
export interface CalculateCropToMatchFocalLengthInput {
|
|
33
|
+
/** Focal length the image was taken with, in millimetres. */
|
|
34
|
+
focalLengthMm: number;
|
|
35
|
+
/** Longer focal length whose framing the crop should match, in millimetres. At least `focalLengthMm`. */
|
|
36
|
+
targetFocalLengthMm: number;
|
|
37
|
+
/** Image width in pixels (for example the sensor's output width). */
|
|
38
|
+
pixelWidth: number;
|
|
39
|
+
/** Image height in pixels. */
|
|
40
|
+
pixelHeight: number;
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## CropToMatchFocalLength
|
|
45
|
+
|
|
46
|
+
The exact contract is declared below; use the domain guide for assumptions and staged integration.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
export interface CropToMatchFocalLength {
|
|
50
|
+
/** Focal length supplied by the caller, in millimetres. */
|
|
51
|
+
focalLengthMm: number;
|
|
52
|
+
/** Target focal length supplied by the caller, in millimetres. */
|
|
53
|
+
targetFocalLengthMm: number;
|
|
54
|
+
/** Linear crop factor targetFocalLengthMm / focalLengthMm (1 means no crop). */
|
|
55
|
+
cropFactor: number;
|
|
56
|
+
/** Width left after the centred crop, in pixels (as `calculateCenteredCrop`). */
|
|
57
|
+
pixelWidth: number;
|
|
58
|
+
/** Height left after the centred crop, in pixels. */
|
|
59
|
+
pixelHeight: number;
|
|
60
|
+
/** Pixels left, in millions. */
|
|
61
|
+
megapixels: number;
|
|
62
|
+
/** Megapixels of the uncropped image. */
|
|
63
|
+
sourceMegapixels: number;
|
|
64
|
+
/** Fraction of the original pixel area retained, about 1 / cropFactor². */
|
|
65
|
+
retainedAreaFraction: number;
|
|
66
|
+
}
|
|
67
|
+
```
|
|
@@ -6,7 +6,9 @@ The package and root API versions are recorded once in [Navigation](../API_REFER
|
|
|
6
6
|
|
|
7
7
|
Builds the catalogue lens's generic correction profile for one camera state: a radial distortion map (the lens's own
|
|
8
8
|
r_d = r(1 + k1ρ²), read at each ideal destination point), per-channel lateral-colour scales 1 + L·D(λ_channel), and the
|
|
9
|
-
relative-illumination gain at the state's aperture (strength 1).
|
|
9
|
+
relative-illumination gain at the state's aperture (strength 1). From profile version 1.1.0 (#584) the vignetting component
|
|
10
|
+
(version 1.1.0) carries the distortion's area factor (`distortionAreaFactor`): its gain is det J(ρ_i)/E(ρ_s) at the capture's
|
|
11
|
+
sensor point, so it flattens a flat-field capture. The result passes `parseGenericLensCorrectionProfile`.
|
|
10
12
|
|
|
11
13
|
```ts
|
|
12
14
|
export function createGenericLensCorrectionProfile(input: CreateGenericLensCorrectionProfileInput): GenericLensCorrectionProfile;
|
|
@@ -31,7 +31,9 @@ This call is synchronous; any supplied provider must follow its explicit synchro
|
|
|
31
31
|
## calculatePeripheralIlluminationCorrection
|
|
32
32
|
|
|
33
33
|
Applies a gain after capture and reports the variance before clipping. It does not rewrite
|
|
34
|
-
the photon or SNR (signal-to-noise ratio) history of the capture.
|
|
34
|
+
the photon or SNR (signal-to-noise ratio) history of the capture. The gain is T^(−strength), T the relative illumination at
|
|
35
|
+
`imagePointMm`: E(ρ) of the component's profile, divided by det J(ρ_i) when the component has a `distortionAreaFactor` (#584;
|
|
36
|
+
then `imagePointMm` is the capture's sensor point, where the capture applied both).
|
|
35
37
|
|
|
36
38
|
```ts
|
|
37
39
|
export function calculatePeripheralIlluminationCorrection(input: {
|
|
@@ -77,6 +79,14 @@ export interface IlluminationLensCorrection extends CorrectionComponentIdentity
|
|
|
77
79
|
kind: "peripheral-illumination";
|
|
78
80
|
profile: IlluminationVignettingProfile;
|
|
79
81
|
strength: number;
|
|
82
|
+
/**
|
|
83
|
+
* Optional (#584): the capture's radial distortion r_d = r(1 + k1ρ² + k2ρ⁴ + k3ρ⁶), whose area factor 1/det J the capture also
|
|
84
|
+
* multiplied into the irradiance (det J = (1 + k1ρ_i² + k2ρ_i⁴ + k3ρ_i⁶)(1 + 3k1ρ_i² + 5k2ρ_i⁴ + 7k3ρ_i⁶) at the ideal point ρ_i that
|
|
85
|
+
* the lens images to the sensor point). With it, the relative illumination corrected is E(ρ_s)/det J(ρ_i), evaluated at the
|
|
86
|
+
* capture's sensor (source) point, so strength 1 flattens a flat field. It holds whether or not the distortion itself is corrected.
|
|
87
|
+
* Without it the gain is 1/E alone, as before.
|
|
88
|
+
*/
|
|
89
|
+
distortionAreaFactor?: RadialDistortionProfile;
|
|
80
90
|
}
|
|
81
91
|
```
|
|
82
92
|
|