@photivra/engine 2.18.0 → 2.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +179 -1
- package/CITATION.cff +1 -1
- package/NODE_SUPPORT.md +7 -5
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +14 -10
- package/dist/capture/dng-lens-correction-opcodes.d.ts +32 -0
- package/dist/capture/dng-lens-correction-opcodes.js +119 -0
- package/dist/capture/dng-lens-correction-opcodes.js.map +1 -0
- package/dist/capture/focus-control.js +2 -75
- package/dist/capture/focus-control.js.map +1 -1
- package/dist/capture/native-environment-lean.d.ts +1 -0
- package/dist/capture/native-environment-lean.js +2 -6
- package/dist/capture/native-environment-lean.js.map +1 -1
- package/dist/capture/native-environment-optical-stage.d.ts +1 -0
- package/dist/capture/native-environment-optical-stage.js +2 -5
- package/dist/capture/native-environment-optical-stage.js.map +1 -1
- package/dist/capture/native-environment-raw.js +1 -5
- package/dist/capture/native-environment-raw.js.map +1 -1
- package/dist/capture/native-raw-dng.d.ts +6 -0
- package/dist/capture/native-raw-dng.js +16 -2
- package/dist/capture/native-raw-dng.js.map +1 -1
- package/dist/capture/release-sequence.js +2 -47
- package/dist/capture/release-sequence.js.map +1 -1
- package/dist/capture/sensor-raw-producer.js +3 -5
- package/dist/capture/sensor-raw-producer.js.map +1 -1
- package/dist/color/white-balance.d.ts +2 -0
- package/dist/color/white-balance.js +3 -56
- package/dist/color/white-balance.js.map +1 -1
- package/dist/composition/image-formation-plan.d.ts +1 -0
- package/dist/composition/image-formation-plan.js +3 -49
- package/dist/composition/image-formation-plan.js.map +1 -1
- package/dist/core/evidence-provenance.js +1 -16
- package/dist/core/evidence-provenance.js.map +1 -1
- package/dist/core/record-validation.d.ts +54 -0
- package/dist/core/record-validation.js +176 -0
- package/dist/core/record-validation.js.map +1 -1
- package/dist/core/scientific-assurance.js +1 -18
- package/dist/core/scientific-assurance.js.map +1 -1
- package/dist/core/shared-helpers.d.ts +35 -0
- package/dist/core/shared-helpers.js +38 -0
- package/dist/core/shared-helpers.js.map +1 -0
- package/dist/core/spectral-composition.js +1 -36
- package/dist/core/spectral-composition.js.map +1 -1
- package/dist/core/spectral.js +1 -19
- package/dist/core/spectral.js.map +1 -1
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/core/version.js.map +1 -1
- package/dist/equipment/exposure-capabilities.d.ts +1 -0
- package/dist/equipment/exposure-capabilities.js +2 -36
- package/dist/equipment/exposure-capabilities.js.map +1 -1
- package/dist/equipment/generic-lens-catalogue.d.ts +8 -2
- package/dist/equipment/generic-lens-catalogue.js +31 -2
- package/dist/equipment/generic-lens-catalogue.js.map +1 -1
- package/dist/equipment/generic-tier-assets.js +2 -3
- package/dist/equipment/generic-tier-assets.js.map +1 -1
- package/dist/equipment/generic-tier-presets.js +2 -3
- package/dist/equipment/generic-tier-presets.js.map +1 -1
- package/dist/equipment/iso-capabilities.js +2 -49
- package/dist/equipment/iso-capabilities.js.map +1 -1
- package/dist/equipment/metering-capabilities.js +1 -26
- package/dist/equipment/metering-capabilities.js.map +1 -1
- package/dist/equipment/release-capabilities.js +1 -53
- package/dist/equipment/release-capabilities.js.map +1 -1
- package/dist/exposure/auto-iso-slowest-shutter.js +1 -7
- package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -1
- package/dist/exposure/exposure-duration-control.js +1 -16
- package/dist/exposure/exposure-duration-control.js.map +1 -1
- package/dist/exposure/exposure-mode-resolver.js +1 -17
- package/dist/exposure/exposure-mode-resolver.js.map +1 -1
- package/dist/exposure/filtered-exposure-time.d.ts +93 -0
- package/dist/exposure/filtered-exposure-time.js +129 -0
- package/dist/exposure/filtered-exposure-time.js.map +1 -0
- package/dist/exposure/flash-ttl.d.ts +1 -0
- package/dist/exposure/flash-ttl.js +1 -1
- package/dist/exposure/flash-ttl.js.map +1 -1
- package/dist/exposure/flash.js +1 -49
- package/dist/exposure/flash.js.map +1 -1
- package/dist/exposure/metering-scene-radiance.js +1 -25
- package/dist/exposure/metering-scene-radiance.js.map +1 -1
- package/dist/exposure/metering-target.js +1 -30
- package/dist/exposure/metering-target.js.map +1 -1
- package/dist/exposure/metering-temporal.js +1 -8
- package/dist/exposure/metering-temporal.js.map +1 -1
- package/dist/exposure/metering.js +1 -72
- package/dist/exposure/metering.js.map +1 -1
- package/dist/exposure/program-line.js +1 -45
- package/dist/exposure/program-line.js.map +1 -1
- package/dist/facade/develop-joined-parts.d.ts +29 -0
- package/dist/facade/develop-joined-parts.js +33 -0
- package/dist/facade/develop-joined-parts.js.map +1 -0
- package/dist/facade/display-view-gpu.d.ts +7 -4
- package/dist/facade/display-view-gpu.js +4 -4
- package/dist/facade/display-view-gpu.js.map +1 -1
- package/dist/facade/display-view-lens-signal.d.ts +1 -1
- package/dist/facade/display-view-lens-signal.js +5 -3
- package/dist/facade/display-view-lens-signal.js.map +1 -1
- package/dist/facade/display-view.d.ts +3 -1
- package/dist/facade/display-view.js +1 -1
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/fast-methods.d.ts +50 -0
- package/dist/facade/fast-methods.js +50 -0
- package/dist/facade/fast-methods.js.map +1 -0
- package/dist/facade/fast-resolution.d.ts +12 -0
- package/dist/facade/fast-resolution.js +18 -0
- package/dist/facade/fast-resolution.js.map +1 -0
- package/dist/facade/generic-lens-capture.d.ts +6 -3
- package/dist/facade/generic-lens-capture.js +6 -3
- package/dist/facade/generic-lens-capture.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +88 -16
- package/dist/facade/simulate-capture.js +279 -153
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.d.ts +3 -2
- package/dist/facade/simulate-display-view.js +22 -12
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/chart-composite-gpu.js +1 -3
- package/dist/gpu/chart-composite-gpu.js.map +1 -1
- package/dist/gpu/chart-radiance-gpu.js +1 -5
- package/dist/gpu/chart-radiance-gpu.js.map +1 -1
- package/dist/gpu/display-view-quality-gpu.d.ts +13 -1
- package/dist/gpu/display-view-quality-gpu.js +4 -8
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/display-view-speed-gpu.js +3 -25
- package/dist/gpu/display-view-speed-gpu.js.map +1 -1
- package/dist/gpu/sensor-stage-gpu.js +1 -5
- package/dist/gpu/sensor-stage-gpu.js.map +1 -1
- package/dist/gpu/webgpu-runtime.d.ts +2 -0
- package/dist/gpu/webgpu-runtime.js +3 -0
- package/dist/gpu/webgpu-runtime.js.map +1 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/motion/camera-rotation.js +1 -6
- package/dist/motion/camera-rotation.js.map +1 -1
- package/dist/motion/capture-rotation-inverse-mapping.d.ts +12 -0
- package/dist/motion/capture-rotation-inverse-mapping.js +2 -2
- package/dist/motion/capture-rotation-inverse-mapping.js.map +1 -1
- package/dist/motion/capture-rotation-temporal-quadrature.d.ts +1 -0
- package/dist/motion/capture-rotation-temporal-quadrature.js +1 -1
- package/dist/motion/capture-rotation-temporal-quadrature.js.map +1 -1
- package/dist/motion/capture-rotation-trajectory.js +2 -17
- package/dist/motion/capture-rotation-trajectory.js.map +1 -1
- package/dist/motion/capture-translation-parallax-temporal-quadrature.d.ts +1 -0
- package/dist/motion/capture-translation-parallax-temporal-quadrature.js +3 -14
- package/dist/motion/capture-translation-parallax-temporal-quadrature.js.map +1 -1
- package/dist/motion/extended-object-projection.js +5 -40
- package/dist/motion/extended-object-projection.js.map +1 -1
- package/dist/optics/aperture-diameter.d.ts +47 -0
- package/dist/optics/aperture-diameter.js +50 -0
- package/dist/optics/aperture-diameter.js.map +1 -0
- package/dist/optics/atmospheric-haze.js +1 -7
- package/dist/optics/atmospheric-haze.js.map +1 -1
- package/dist/optics/brown-conrady-distortion.js +1 -7
- package/dist/optics/brown-conrady-distortion.js.map +1 -1
- package/dist/optics/complex-pupil-psf.d.ts +1 -0
- package/dist/optics/complex-pupil-psf.js +2 -84
- package/dist/optics/complex-pupil-psf.js.map +1 -1
- package/dist/optics/detail-limit.d.ts +182 -0
- package/dist/optics/detail-limit.js +336 -0
- package/dist/optics/detail-limit.js.map +1 -0
- package/dist/optics/focus-state.js +1 -11
- package/dist/optics/focus-state.js.map +1 -1
- package/dist/optics/front-of-lens-filter.d.ts +5 -0
- package/dist/optics/front-of-lens-filter.js +3 -65
- package/dist/optics/front-of-lens-filter.js.map +1 -1
- package/dist/optics/generic-lens-psf.d.ts +18 -2
- package/dist/optics/generic-lens-psf.js +25 -2
- package/dist/optics/generic-lens-psf.js.map +1 -1
- package/dist/optics/generic-nd-filter-presets.d.ts +12 -0
- package/dist/optics/generic-nd-filter-presets.js +48 -0
- package/dist/optics/generic-nd-filter-presets.js.map +1 -0
- package/dist/optics/illumination-vignetting.js +1 -6
- package/dist/optics/illumination-vignetting.js.map +1 -1
- package/dist/optics/lens-psf-profile.d.ts +4 -1
- package/dist/optics/lens-psf-profile.js +12 -111
- package/dist/optics/lens-psf-profile.js.map +1 -1
- package/dist/optics/longitudinal-chromatic-aberration.js +1 -7
- package/dist/optics/longitudinal-chromatic-aberration.js.map +1 -1
- package/dist/optics/pupil-vignetting.js +1 -7
- package/dist/optics/pupil-vignetting.js.map +1 -1
- package/dist/optics/radial-distortion.js +1 -6
- package/dist/optics/radial-distortion.js.map +1 -1
- package/dist/optics/scene-to-sensor-irradiance.d.ts +13 -0
- package/dist/optics/scene-to-sensor-irradiance.js +11 -96
- package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
- package/dist/optics/stray-light.d.ts +1 -0
- package/dist/optics/stray-light.js +1 -1
- package/dist/optics/stray-light.js.map +1 -1
- package/dist/optics/zernike-wavefront.js +1 -7
- package/dist/optics/zernike-wavefront.js.map +1 -1
- package/dist/output/capture-geometry.js +1 -8
- package/dist/output/capture-geometry.js.map +1 -1
- package/dist/output/crop-to-match-focal-length.d.ts +62 -0
- package/dist/output/crop-to-match-focal-length.js +63 -0
- package/dist/output/crop-to-match-focal-length.js.map +1 -0
- package/dist/output/developed-lens-corrections.d.ts +62 -0
- package/dist/output/developed-lens-corrections.js +135 -0
- package/dist/output/developed-lens-corrections.js.map +1 -0
- package/dist/output/generic-lens-corrections.d.ts +5 -7
- package/dist/output/generic-lens-corrections.js +11 -10
- package/dist/output/generic-lens-corrections.js.map +1 -1
- package/dist/output/geometric-transforms.js +1 -4
- package/dist/output/geometric-transforms.js.map +1 -1
- package/dist/output/lens-corrections.d.ts +12 -2
- package/dist/output/lens-corrections.js +22 -7
- package/dist/output/lens-corrections.js.map +1 -1
- package/dist/output/native-capture-sdr.d.ts +2 -2
- package/dist/output/native-capture-sdr.js +3 -2
- package/dist/output/native-capture-sdr.js.map +1 -1
- package/dist/output/print-contrast-reference.js +1 -1
- package/dist/output/print-contrast-reference.js.map +1 -1
- package/dist/output/print-size-blur-noise.d.ts +110 -0
- package/dist/output/print-size-blur-noise.js +138 -0
- package/dist/output/print-size-blur-noise.js.map +1 -0
- package/dist/processing/development-detail.d.ts +29 -0
- package/dist/processing/development-detail.js +128 -107
- package/dist/processing/development-detail.js.map +1 -1
- package/dist/schema/illumination-temporal.js +3 -72
- package/dist/schema/illumination-temporal.js.map +1 -1
- package/dist/schema/illumination.d.ts +1 -0
- package/dist/schema/illumination.js +2 -65
- package/dist/schema/illumination.js.map +1 -1
- package/dist/schema/scene-radiance-temporal.js +2 -14
- package/dist/schema/scene-radiance-temporal.js.map +1 -1
- package/dist/schema/scene-radiance.js +2 -60
- package/dist/schema/scene-radiance.js.map +1 -1
- package/dist/schema/validation.d.ts +2 -0
- package/dist/schema/validation.js +2 -17
- package/dist/schema/validation.js.map +1 -1
- package/dist/sensor/accumulated-charge.js +2 -59
- package/dist/sensor/accumulated-charge.js.map +1 -1
- package/dist/sensor/architecture.js +1 -16
- package/dist/sensor/architecture.js.map +1 -1
- package/dist/sensor/camera-saturation-capacity.d.ts +1 -0
- package/dist/sensor/camera-saturation-capacity.js +3 -55
- package/dist/sensor/camera-saturation-capacity.js.map +1 -1
- package/dist/sensor/capture-color-sampling-binding.js +1 -26
- package/dist/sensor/capture-color-sampling-binding.js.map +1 -1
- package/dist/sensor/capture-mode-timing.js +1 -39
- package/dist/sensor/capture-mode-timing.js.map +1 -1
- package/dist/sensor/capture-mode.js +1 -23
- package/dist/sensor/capture-mode.js.map +1 -1
- package/dist/sensor/color-sampling.js +1 -26
- package/dist/sensor/color-sampling.js.map +1 -1
- package/dist/sensor/constant-rate-temporal-integration.js +2 -46
- package/dist/sensor/constant-rate-temporal-integration.js.map +1 -1
- package/dist/sensor/dark-current.js +2 -45
- package/dist/sensor/dark-current.js.map +1 -1
- package/dist/sensor/eqe-electron-rate.js +2 -41
- package/dist/sensor/eqe-electron-rate.js.map +1 -1
- package/dist/sensor/exposure-window.d.ts +7 -0
- package/dist/sensor/exposure-window.js +9 -24
- package/dist/sensor/exposure-window.js.map +1 -1
- package/dist/sensor/iso-gain.js +1 -7
- package/dist/sensor/iso-gain.js.map +1 -1
- package/dist/sensor/iso-signal-chain.js +1 -43
- package/dist/sensor/iso-signal-chain.js.map +1 -1
- package/dist/sensor/optical-stack.js +1 -25
- package/dist/sensor/optical-stack.js.map +1 -1
- package/dist/sensor/photon-energy.js +1 -26
- package/dist/sensor/photon-energy.js.map +1 -1
- package/dist/sensor/physical-charge-capacity.js +3 -77
- package/dist/sensor/physical-charge-capacity.js.map +1 -1
- package/dist/sensor/radiometry-readiness.js +2 -23
- package/dist/sensor/radiometry-readiness.js.map +1 -1
- package/dist/sensor/raw-readout.d.ts +3 -0
- package/dist/sensor/raw-readout.js +4 -66
- package/dist/sensor/raw-readout.js.map +1 -1
- package/dist/sensor/raw-reconstruction.js +2 -64
- package/dist/sensor/raw-reconstruction.js.map +1 -1
- package/dist/sensor/readout-exposure-linkage.js +1 -9
- package/dist/sensor/readout-exposure-linkage.js.map +1 -1
- package/dist/sensor/readout-timing.js +4 -79
- package/dist/sensor/readout-timing.js.map +1 -1
- package/dist/sensor/response-application-compatibility.d.ts +2 -0
- package/dist/sensor/response-application-compatibility.js +4 -35
- package/dist/sensor/response-application-compatibility.js.map +1 -1
- package/dist/sensor/response-operating-range.d.ts +1 -0
- package/dist/sensor/response-operating-range.js +3 -79
- package/dist/sensor/response-operating-range.js.map +1 -1
- package/dist/sensor/responsivity-photocurrent.js +4 -114
- package/dist/sensor/responsivity-photocurrent.js.map +1 -1
- package/dist/sensor/sampling-aperture.js +1 -25
- package/dist/sensor/sampling-aperture.js.map +1 -1
- package/dist/sensor/sensor-characteristics.d.ts +137 -0
- package/dist/sensor/sensor-characteristics.js +115 -0
- package/dist/sensor/sensor-characteristics.js.map +1 -0
- package/dist/sensor/spatial-sample-reduction.js +3 -24
- package/dist/sensor/spatial-sample-reduction.js.map +1 -1
- package/dist/sensor/spatial-sampling-quadrature.js +1 -3
- package/dist/sensor/spatial-sampling-quadrature.js.map +1 -1
- package/dist/sensor/spatio-spectral-reduction.js +1 -18
- package/dist/sensor/spatio-spectral-reduction.js.map +1 -1
- package/dist/sensor/spectral-coverage.d.ts +10 -1
- package/dist/sensor/spectral-coverage.js +1 -1
- package/dist/sensor/spectral-coverage.js.map +1 -1
- package/dist/sensor/spectral-quadrature.js +3 -41
- package/dist/sensor/spectral-quadrature.js.map +1 -1
- package/dist/sensor/spectral-response.js +1 -47
- package/dist/sensor/spectral-response.js.map +1 -1
- package/dist/sensor/temporal-photo-signal.d.ts +3 -0
- package/dist/sensor/temporal-photo-signal.js +5 -2
- package/dist/sensor/temporal-photo-signal.js.map +1 -1
- package/dist/stabilization/camera-shake.js +1 -6
- package/dist/stabilization/camera-shake.js.map +1 -1
- package/dist/stabilization/system.d.ts +9 -0
- package/dist/stabilization/system.js +7 -86
- package/dist/stabilization/system.js.map +1 -1
- package/docs/AGENT_GUIDE.md +5 -0
- package/docs/API_REFERENCE.md +116 -3
- package/docs/QUICK_START.md +178 -14
- package/docs/USAGE.md +111 -3
- package/docs/api/capture-native-raw-dng.md +6 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/equipment-generic-lens-catalogue.md +7 -2
- package/docs/api/exports.json +203 -3
- package/docs/api/exposure-filtered-exposure-time.md +105 -0
- package/docs/api/facade-develop-joined-parts.md +32 -0
- package/docs/api/facade-display-view-gpu.md +5 -2
- package/docs/api/facade-fast-methods.md +91 -0
- package/docs/api/facade-simulate-capture.md +72 -16
- package/docs/api/facade-simulate-display-view.md +3 -2
- package/docs/api/optics-aperture-diameter.md +51 -0
- package/docs/api/optics-detail-limit.md +203 -0
- package/docs/api/optics-generic-nd-filter-presets.md +34 -0
- package/docs/api/optics-lens-psf-profile.md +3 -1
- package/docs/api/output-crop-to-match-focal-length.md +67 -0
- package/docs/api/output-developed-lens-corrections.md +12 -0
- package/docs/api/output-generic-lens-corrections.md +3 -10
- package/docs/api/output-lens-corrections.md +11 -1
- package/docs/api/output-print-size-blur-noise.md +115 -0
- package/docs/api/sensor-sensor-characteristics.md +105 -0
- package/llms.txt +5 -0
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SdrPixelCounts } from "../output/native-capture-sdr.js";
|
|
1
2
|
import { type SdrRenderingProfile } from "../output/sdr-rendering.js";
|
|
2
3
|
/** Version of the `development-detail` model (diffraction correction and unsharp mask). */
|
|
3
4
|
export declare const DEVELOPMENT_DETAIL_MODEL_VERSION: "0.1.0";
|
|
@@ -86,6 +87,15 @@ export declare function applyDiffractionCorrection(rgb: Float64Array, width: num
|
|
|
86
87
|
samples: Float64Array;
|
|
87
88
|
diagnostics: DiffractionCorrectionDiagnostics;
|
|
88
89
|
};
|
|
90
|
+
/** Runs a row-by-row detail stage to its end and returns its result. */
|
|
91
|
+
export declare function drainDetailRows<T>(rows: Generator<number, T, void>): T;
|
|
92
|
+
/**
|
|
93
|
+
* `applyDiffractionCorrection` in place, one output row per step (#571); each step yields the number of rows done. Output row y
|
|
94
|
+
* reads input rows y − R … y + R, so it waits in a ring of R + 1 rows and replaces input row y once row y + R is done. Every sample
|
|
95
|
+
* is the same sum in the same order as in a separate output image, so the result is bit-identical, with one image in memory
|
|
96
|
+
* instead of two. `rgb` must hold width × height pixels.
|
|
97
|
+
*/
|
|
98
|
+
export declare function correctDiffractionInPlace(rgb: Float64Array, width: number, height: number, filter: Pick<DiffractionCorrectionFilter, "weights" | "settings">): Generator<number, DiffractionCorrectionDiagnostics, void>;
|
|
89
99
|
/** Normalized Gaussian weights exp(−i²/(2σ²)), i = −r..r with r = ⌈3σ⌉. */
|
|
90
100
|
export declare function unsharpMaskKernel(radiusPixels: number): Float64Array;
|
|
91
101
|
/** What the unsharp mask did. Counts are samples (one per channel). */
|
|
@@ -112,6 +122,15 @@ export declare function applyUnsharpMask(encoded: Float64Array, width: number, h
|
|
|
112
122
|
samples: Float64Array;
|
|
113
123
|
diagnostics: UnsharpMaskDiagnostics;
|
|
114
124
|
};
|
|
125
|
+
/**
|
|
126
|
+
* The unsharp mask one output row per step (#571); each step yields the number of rows done. The horizontal Gaussian pass of
|
|
127
|
+
* each input row is kept in a ring of 2r + 1 rows, and `prepareRow(y)`, when given, runs once on input row y before it is
|
|
128
|
+
* first read (`renderSdrWithUnsharpMaskInPlace` encodes the row there). Output row y goes to `out`: the clipped value, or
|
|
129
|
+
* Math.round(value · scale) with a `scale`. Every sum is the same, in the same order, as in whole-image passes, so the
|
|
130
|
+
* result is bit-identical, with 2r + 1 rows of working memory instead of three images. `settings` must be parsed and the
|
|
131
|
+
* raster sizes must match.
|
|
132
|
+
*/
|
|
133
|
+
export declare function unsharpMaskRows(encoded: Float64Array, width: number, height: number, settings: UnsharpMaskSettings, out: Float64Array | Uint8Array | Uint16Array, scale?: number, prepareRow?: (y: number) => void): Generator<number, UnsharpMaskDiagnostics, void>;
|
|
115
134
|
/** SDR counters in the shape of the SDR rendering diagnostics. */
|
|
116
135
|
export interface DetailSdrCounts {
|
|
117
136
|
negative: number;
|
|
@@ -130,3 +149,13 @@ export declare function renderSdrWithUnsharpMask(rgb: Float64Array, width: numbe
|
|
|
130
149
|
counts: DetailSdrCounts;
|
|
131
150
|
sharpening: UnsharpMaskDiagnostics;
|
|
132
151
|
};
|
|
152
|
+
/**
|
|
153
|
+
* `renderSdrWithUnsharpMask` one output row per step (#571), encoding each row of `rgb` in place just before the mask first reads
|
|
154
|
+
* it (`rgb` then holds the encoded values). Bit-identical to the whole-image function, with no second image in memory. `settings`
|
|
155
|
+
* must be parsed and `rgb` must hold width × height pixels.
|
|
156
|
+
*/
|
|
157
|
+
export declare function renderSdrWithUnsharpMaskInPlace(rgb: Float64Array, width: number, height: number, profile: SdrRenderingProfile, settings: UnsharpMaskSettings): Generator<number, {
|
|
158
|
+
codes: Uint8Array | Uint16Array;
|
|
159
|
+
counts: SdrPixelCounts;
|
|
160
|
+
sharpening: UnsharpMaskDiagnostics;
|
|
161
|
+
}, void>;
|
|
@@ -12,12 +12,13 @@
|
|
|
12
12
|
* Development order with these stages: (exposure gain) → demosaic → colour and white balance → diffraction correction
|
|
13
13
|
* (linear) → rendering exposure and tone curve → gamut handling → sRGB transfer → unsharp mask → quantization (encode).
|
|
14
14
|
*
|
|
15
|
-
* Image edges are handled by clamping (edge pixels repeated). Both stages
|
|
15
|
+
* Image edges are handled by clamping (edge pixels repeated). Both stages need the whole image; since #571 they also run row by
|
|
16
|
+
* row in place (`correctDiffractionInPlace`, `renderSdrWithUnsharpMaskInPlace`), with the same sums in the same order.
|
|
16
17
|
* @see docs/DIGITAL_OPTICS_FOUNDATION.md for the equations, sources, trade-offs and limits.
|
|
17
18
|
*/
|
|
18
|
-
import { encodeSrgbComponent } from "../color/srgb-transfer.js";
|
|
19
19
|
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
20
20
|
import { InvalidScientificResultError } from "../core/validation.js";
|
|
21
|
+
import { renderSdrRunInto } from "../output/native-capture-sdr.js";
|
|
21
22
|
import { prepareSdrPixelTone } from "../output/sdr-rendering.js";
|
|
22
23
|
/** Version of the `development-detail` model (diffraction correction and unsharp mask). */
|
|
23
24
|
export const DEVELOPMENT_DETAIL_MODEL_VERSION = "0.1.0";
|
|
@@ -158,38 +159,62 @@ export function evaluateDiffractionCorrectionResponse(weights, radius, fx, fy) {
|
|
|
158
159
|
export function applyDiffractionCorrection(rgb, width, height, filter) {
|
|
159
160
|
if (rgb.length !== width * height * 3)
|
|
160
161
|
throw new InvalidConfigurationError("Diffraction correction raster size mismatch.");
|
|
161
|
-
const
|
|
162
|
+
const samples = Float64Array.from(rgb);
|
|
163
|
+
return { samples, diagnostics: drainDetailRows(correctDiffractionInPlace(samples, width, height, filter)) };
|
|
164
|
+
}
|
|
165
|
+
/** Runs a row-by-row detail stage to its end and returns its result. */
|
|
166
|
+
export function drainDetailRows(rows) {
|
|
167
|
+
let step = rows.next();
|
|
168
|
+
while (!step.done)
|
|
169
|
+
step = rows.next();
|
|
170
|
+
return step.value;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* `applyDiffractionCorrection` in place, one output row per step (#571); each step yields the number of rows done. Output row y
|
|
174
|
+
* reads input rows y − R … y + R, so it waits in a ring of R + 1 rows and replaces input row y once row y + R is done. Every sample
|
|
175
|
+
* is the same sum in the same order as in a separate output image, so the result is bit-identical, with one image in memory
|
|
176
|
+
* instead of two. `rgb` must hold width × height pixels.
|
|
177
|
+
*/
|
|
178
|
+
export function* correctDiffractionInPlace(rgb, width, height, filter) {
|
|
179
|
+
const R = filter.settings.kernelRadiusPixels, size = 2 * R + 1, w = filter.weights, rowLength = width * 3, ring = new Float64Array((R + 1) * rowLength);
|
|
162
180
|
const xs = new Int32Array(width + 2 * R);
|
|
163
181
|
for (let i = 0; i < xs.length; i++)
|
|
164
182
|
xs[i] = Math.min(width - 1, Math.max(0, i - R)) * 3;
|
|
165
183
|
let negative = 0, above = 0;
|
|
166
|
-
for (let y = 0; y
|
|
167
|
-
|
|
184
|
+
for (let y = 0; y <= height + R; y++) {
|
|
185
|
+
// Output row y − R − 1 holds the slot of row y; input row y − R − 1 is read by no later output row, so it takes its place.
|
|
186
|
+
const o = (y % (R + 1)) * rowLength;
|
|
187
|
+
if (y > R)
|
|
188
|
+
rgb.set(ring.subarray(o, o + rowLength), (y - R - 1) * rowLength);
|
|
189
|
+
if (y >= height)
|
|
190
|
+
continue;
|
|
191
|
+
ring.fill(0, o, o + rowLength);
|
|
168
192
|
for (let dy = -R; dy <= R; dy++) {
|
|
169
|
-
const row = Math.min(height - 1, Math.max(0, y + dy)) *
|
|
193
|
+
const row = Math.min(height - 1, Math.max(0, y + dy)) * rowLength, wr = (dy + R) * size;
|
|
170
194
|
for (let dx = 0; dx < size; dx++) {
|
|
171
195
|
const weight = w[wr + dx];
|
|
172
196
|
if (weight === 0)
|
|
173
197
|
continue;
|
|
174
198
|
for (let x = 0; x < width; x++) {
|
|
175
199
|
const s = row + xs[x + dx], d = o + 3 * x;
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
200
|
+
ring[d] = ring[d] + weight * rgb[s];
|
|
201
|
+
ring[d + 1] = ring[d + 1] + weight * rgb[s + 1];
|
|
202
|
+
ring[d + 2] = ring[d + 2] + weight * rgb[s + 2];
|
|
179
203
|
}
|
|
180
204
|
}
|
|
181
205
|
}
|
|
206
|
+
for (let i = o; i < o + rowLength; i++) {
|
|
207
|
+
const v = ring[i];
|
|
208
|
+
if (!Number.isFinite(v))
|
|
209
|
+
throw new InvalidScientificResultError("Diffraction correction produced a non-finite sample.");
|
|
210
|
+
if (v < 0)
|
|
211
|
+
negative++;
|
|
212
|
+
else if (v > 1)
|
|
213
|
+
above++;
|
|
214
|
+
}
|
|
215
|
+
yield y + 1;
|
|
182
216
|
}
|
|
183
|
-
|
|
184
|
-
const v = out[i];
|
|
185
|
-
if (!Number.isFinite(v))
|
|
186
|
-
throw new InvalidScientificResultError("Diffraction correction produced a non-finite sample.");
|
|
187
|
-
if (v < 0)
|
|
188
|
-
negative++;
|
|
189
|
-
else if (v > 1)
|
|
190
|
-
above++;
|
|
191
|
-
}
|
|
192
|
-
return { samples: out, diagnostics: { negativeSampleCount: negative, aboveWhiteSampleCount: above } };
|
|
217
|
+
return { negativeSampleCount: negative, aboveWhiteSampleCount: above };
|
|
193
218
|
}
|
|
194
219
|
/** Normalized Gaussian weights exp(−i²/(2σ²)), i = −r..r with r = ⌈3σ⌉. */
|
|
195
220
|
export function unsharpMaskKernel(radiusPixels) {
|
|
@@ -213,64 +238,79 @@ export function applyUnsharpMask(encoded, width, height, settings) {
|
|
|
213
238
|
const s = parseUnsharpMaskSettings(settings);
|
|
214
239
|
if (encoded.length !== width * height * 3)
|
|
215
240
|
throw new InvalidConfigurationError("Unsharp mask raster size mismatch.");
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
241
|
+
const samples = new Float64Array(encoded.length);
|
|
242
|
+
return { samples, diagnostics: drainDetailRows(unsharpMaskRows(encoded, width, height, s, samples)) };
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* The unsharp mask one output row per step (#571); each step yields the number of rows done. The horizontal Gaussian pass of
|
|
246
|
+
* each input row is kept in a ring of 2r + 1 rows, and `prepareRow(y)`, when given, runs once on input row y before it is
|
|
247
|
+
* first read (`renderSdrWithUnsharpMaskInPlace` encodes the row there). Output row y goes to `out`: the clipped value, or
|
|
248
|
+
* Math.round(value · scale) with a `scale`. Every sum is the same, in the same order, as in whole-image passes, so the
|
|
249
|
+
* result is bit-identical, with 2r + 1 rows of working memory instead of three images. `settings` must be parsed and the
|
|
250
|
+
* raster sizes must match.
|
|
251
|
+
*/
|
|
252
|
+
export function* unsharpMaskRows(encoded, width, height, settings, out, scale, prepareRow) {
|
|
253
|
+
const k = unsharpMaskKernel(settings.radiusPixels), r = (k.length - 1) / 2, span = 2 * r + 1, rowLength = width * 3, ring = new Float64Array(span * rowLength);
|
|
254
|
+
const amount = settings.amount, threshold = settings.threshold;
|
|
255
|
+
let sharpened = 0, held = 0, low = 0, high = 0, increase = 0, decrease = 0, ready = 0;
|
|
256
|
+
for (let y = 0; y < height; y++) {
|
|
257
|
+
// Rows up to y + r, prepared and horizontally blurred.
|
|
258
|
+
for (const last = Math.min(height - 1, y + r); ready <= last; ready++) {
|
|
259
|
+
prepareRow?.(ready);
|
|
260
|
+
const source = ready * rowLength, slot = (ready % span) * rowLength;
|
|
261
|
+
for (let x = 0; x < width; x++) {
|
|
262
|
+
let a = 0, b = 0, c = 0;
|
|
263
|
+
for (let i = -r; i <= r; i++) {
|
|
264
|
+
const sx = source + Math.min(width - 1, Math.max(0, x + i)) * 3, w = k[i + r];
|
|
265
|
+
a += w * encoded[sx];
|
|
266
|
+
b += w * encoded[sx + 1];
|
|
267
|
+
c += w * encoded[sx + 2];
|
|
268
|
+
}
|
|
269
|
+
ring[slot + 3 * x] = a;
|
|
270
|
+
ring[slot + 3 * x + 1] = b;
|
|
271
|
+
ring[slot + 3 * x + 2] = c;
|
|
226
272
|
}
|
|
227
|
-
horizontal[d] = a;
|
|
228
|
-
horizontal[d + 1] = b;
|
|
229
|
-
horizontal[d + 2] = c;
|
|
230
273
|
}
|
|
231
|
-
|
|
274
|
+
const o = y * rowLength;
|
|
232
275
|
for (let x = 0; x < width; x++) {
|
|
233
|
-
const d = (y * width + x) * 3;
|
|
234
276
|
let a = 0, b = 0, c = 0;
|
|
235
277
|
for (let i = -r; i <= r; i++) {
|
|
236
|
-
const sy = (Math.min(height - 1, Math.max(0, y + i)) *
|
|
237
|
-
a += w *
|
|
238
|
-
b += w *
|
|
239
|
-
c += w *
|
|
278
|
+
const sy = (Math.min(height - 1, Math.max(0, y + i)) % span) * rowLength + 3 * x, w = k[i + r];
|
|
279
|
+
a += w * ring[sy];
|
|
280
|
+
b += w * ring[sy + 1];
|
|
281
|
+
c += w * ring[sy + 2];
|
|
282
|
+
}
|
|
283
|
+
for (let channel = 0; channel < 3; channel++) {
|
|
284
|
+
const index = o + 3 * x + channel, value = encoded[index], detail = value - (channel === 0 ? a : channel === 1 ? b : c);
|
|
285
|
+
let v = value;
|
|
286
|
+
if (detail === 0 || Math.abs(detail) < threshold) {
|
|
287
|
+
if (detail !== 0)
|
|
288
|
+
held++;
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
const change = amount * detail;
|
|
292
|
+
v = value + change;
|
|
293
|
+
sharpened++;
|
|
294
|
+
if (change > increase)
|
|
295
|
+
increase = change;
|
|
296
|
+
if (-change > decrease)
|
|
297
|
+
decrease = -change;
|
|
298
|
+
if (v < 0) {
|
|
299
|
+
v = 0;
|
|
300
|
+
low++;
|
|
301
|
+
}
|
|
302
|
+
else if (v > 1) {
|
|
303
|
+
v = 1;
|
|
304
|
+
high++;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
out[index] = scale === undefined ? v : Math.round(v * scale);
|
|
240
308
|
}
|
|
241
|
-
blurred[d] = a;
|
|
242
|
-
blurred[d + 1] = b;
|
|
243
|
-
blurred[d + 2] = c;
|
|
244
|
-
}
|
|
245
|
-
const out = new Float64Array(encoded.length);
|
|
246
|
-
let sharpened = 0, held = 0, low = 0, high = 0, increase = 0, decrease = 0;
|
|
247
|
-
for (let i = 0; i < encoded.length; i++) {
|
|
248
|
-
const x = encoded[i], detail = x - blurred[i];
|
|
249
|
-
if (detail === 0 || Math.abs(detail) < s.threshold) {
|
|
250
|
-
if (detail !== 0)
|
|
251
|
-
held++;
|
|
252
|
-
out[i] = x;
|
|
253
|
-
continue;
|
|
254
|
-
}
|
|
255
|
-
const change = s.amount * detail;
|
|
256
|
-
let v = x + change;
|
|
257
|
-
sharpened++;
|
|
258
|
-
if (change > increase)
|
|
259
|
-
increase = change;
|
|
260
|
-
if (-change > decrease)
|
|
261
|
-
decrease = -change;
|
|
262
|
-
if (v < 0) {
|
|
263
|
-
v = 0;
|
|
264
|
-
low++;
|
|
265
|
-
}
|
|
266
|
-
else if (v > 1) {
|
|
267
|
-
v = 1;
|
|
268
|
-
high++;
|
|
269
309
|
}
|
|
270
|
-
|
|
310
|
+
yield y + 1;
|
|
271
311
|
}
|
|
272
|
-
return {
|
|
273
|
-
|
|
312
|
+
return { kernelRadiusPixels: r, sharpenedSampleCount: sharpened, thresholdHeldSampleCount: held, clippedLowSampleCount: low,
|
|
313
|
+
clippedHighSampleCount: high, maximumIncrease: increase, maximumDecrease: decrease };
|
|
274
314
|
}
|
|
275
315
|
/**
|
|
276
316
|
* SDR rendering with an unsharp mask between the sRGB transfer and quantization. Per pixel it runs the reference renderer's
|
|
@@ -280,46 +320,27 @@ export function applyUnsharpMask(encoded, width, height, settings) {
|
|
|
280
320
|
export function renderSdrWithUnsharpMask(rgb, width, height, profile, settings) {
|
|
281
321
|
if (rgb.length !== width * height * 3)
|
|
282
322
|
throw new InvalidConfigurationError("SDR raster size mismatch.");
|
|
283
|
-
const
|
|
323
|
+
const { codes, counts: { negative, above, changed, low, high }, sharpening } = drainDetailRows(renderSdrWithUnsharpMaskInPlace(Float64Array.from(rgb), width, height, profile, parseUnsharpMaskSettings(settings)));
|
|
324
|
+
return { codes, counts: { negative, above, changed, low, high }, sharpening };
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* `renderSdrWithUnsharpMask` one output row per step (#571), encoding each row of `rgb` in place just before the mask first reads
|
|
328
|
+
* it (`rgb` then holds the encoded values). Bit-identical to the whole-image function, with no second image in memory. `settings`
|
|
329
|
+
* must be parsed and `rgb` must hold width × height pixels.
|
|
330
|
+
*/
|
|
331
|
+
export function* renderSdrWithUnsharpMaskInPlace(rgb, width, height, profile, settings) {
|
|
332
|
+
const scale = 2 ** profile.renderingExposureEv, tone = prepareSdrPixelTone(profile), scratch = new Float64Array(3);
|
|
284
333
|
if (!Number.isFinite(scale) || scale <= 0)
|
|
285
334
|
throw new InvalidConfigurationError("Rendering scale is unrepresentable.");
|
|
286
|
-
const
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
if (
|
|
291
|
-
throw new
|
|
292
|
-
|
|
293
|
-
for (let c = 0; c < 3; c++) {
|
|
294
|
-
const exposed = c === 0 ? er : c === 1 ? eg : eb;
|
|
295
|
-
if (exposed < 0)
|
|
296
|
-
counts.negative++;
|
|
297
|
-
if (exposed > 1)
|
|
298
|
-
counts.above++;
|
|
299
|
-
let mapped = scratch[c];
|
|
300
|
-
if (mapped !== exposed)
|
|
301
|
-
counts.changed++;
|
|
302
|
-
if (mapped === 0)
|
|
303
|
-
mapped = 0;
|
|
304
|
-
if (mapped < 0 || mapped > 1) {
|
|
305
|
-
if (reject)
|
|
306
|
-
throw new InvalidConfigurationError("Tone output exceeds the declared SDR gamut range.");
|
|
307
|
-
if (mapped < 0) {
|
|
308
|
-
counts.low++;
|
|
309
|
-
mapped = 0;
|
|
310
|
-
}
|
|
311
|
-
else {
|
|
312
|
-
counts.high++;
|
|
313
|
-
mapped = 1;
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
encoded[i + c] = encodeSrgbComponent(mapped);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
const sharpened = applyUnsharpMask(encoded, width, height, settings), maximum = 2 ** profile.bitDepth - 1;
|
|
335
|
+
const counts = { negative: 0, above: 0, changed: 0, low: 0, high: 0, outOfGamut: false };
|
|
336
|
+
// The native SDR kernel per row, unquantized (maximum 0), in place: the reference renderer's operations in its order.
|
|
337
|
+
const encodeRow = (y) => {
|
|
338
|
+
renderSdrRunInto(rgb, y * width * 3, width, profile, tone, scale, 0, rgb, y * width * 3, scratch, counts);
|
|
339
|
+
if (counts.outOfGamut)
|
|
340
|
+
throw new InvalidConfigurationError("Tone output exceeds the declared SDR gamut range.");
|
|
341
|
+
};
|
|
320
342
|
const codes = profile.bitDepth === 8 ? new Uint8Array(rgb.length) : new Uint16Array(rgb.length);
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
return { codes, counts, sharpening: sharpened.diagnostics };
|
|
343
|
+
const sharpening = yield* unsharpMaskRows(rgb, width, height, settings, codes, 2 ** profile.bitDepth - 1, encodeRow);
|
|
344
|
+
return { codes, counts, sharpening };
|
|
324
345
|
}
|
|
325
346
|
//# sourceMappingURL=development-detail.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"development-detail.js","sourceRoot":"","sources":["../../src/processing/development-detail.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Development detail stages (#552), both opt-in and off by default:\n *\n * - **Diffraction correction**: a bounded, regularized (Wiener–Tikhonov) deconvolution of the developed linear RGB with the\n * diffraction OTF of an ideal circular pupil at the capture's working f-number. The OTF is zero beyond the cut-off\n * f_c = 1/(λ·N_w), so detail beyond it never reached the sensor; the filter's design gain there is 0 and it cannot, and\n * does not claim to, restore it.\n * - **Sharpening**: an unsharp mask (radius, amount, threshold) on the display-encoded values, after the tone curve and\n * gamut handling and before quantization. It raises edge contrast and creates halos; it adds no optical detail.\n *\n * Development order with these stages: (exposure gain) → demosaic → colour and white balance → diffraction correction\n * (linear) → rendering exposure and tone curve → gamut handling → sRGB transfer → unsharp mask → quantization (encode).\n *\n * Image edges are handled by clamping (edge pixels repeated). Both stages work on a whole in-memory image.\n * @see docs/DIGITAL_OPTICS_FOUNDATION.md for the equations, sources, trade-offs and limits.\n */\n\nimport { encodeSrgbComponent } from \"../color/srgb-transfer.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { InvalidScientificResultError } from \"../core/validation.js\";\nimport { prepareSdrPixelTone, type SdrRenderingProfile } from \"../output/sdr-rendering.js\";\n\n/** Version of the `development-detail` model (diffraction correction and unsharp mask). */\nexport const DEVELOPMENT_DETAIL_MODEL_VERSION = \"0.1.0\" as const;\n\n/** Unsharp mask settings: Gaussian standard deviation in output pixels, gain on the detail signal, and threshold in encoded units [0, 1]. */\nexport interface UnsharpMaskSettings { radiusPixels: number; amount: number; threshold: number }\n/** Diffraction correction settings: design wavelength, Wiener noise-to-signal regularization, gain bound and FIR radius in pixels. */\nexport interface DiffractionCorrectionSettings { wavelengthNm: number; regularization: number; maximumGain: number; kernelRadiusPixels: number }\n\n/** Accepted ranges. Bounds are engineering limits on cost and on noise gain, not quality recommendations. */\nexport const DEVELOPMENT_DETAIL_LIMITS = /* @__PURE__ */ Object.freeze({\n maximumUnsharpRadiusPixels: 8, maximumUnsharpAmount: 5,\n minimumWavelengthNm: 380, maximumWavelengthNm: 780,\n minimumRegularization: 1e-4, maximumRegularization: 1,\n maximumGain: 10, maximumKernelRadiusPixels: 16\n});\n\n/** Third dark ring of the Airy pattern, in units of λ·N (j₁,₃/π, j₁,₃ = 10.1734681…). */\nconst AIRY_THIRD_ZERO_PER_LAMBDA_N = 10.173468135062722 / Math.PI;\n\nfunction finiteIn(value: unknown, name: string, minimum: number, maximum: number, minimumExclusive = false): number {\n if (typeof value !== \"number\" || !Number.isFinite(value) || (minimumExclusive ? value <= minimum : value < minimum) || value > maximum) {\n throw new InvalidConfigurationError(name + \" must be a finite number in \" + (minimumExclusive ? \"(\" : \"[\") + minimum + \", \" + maximum + \"].\");\n }\n return value;\n}\n\n/** Validates unsharp mask settings. */\nexport function parseUnsharpMaskSettings(value: UnsharpMaskSettings, path = \"sharpening\"): UnsharpMaskSettings {\n return { radiusPixels: finiteIn(value.radiusPixels, path + \".radiusPixels\", 0, DEVELOPMENT_DETAIL_LIMITS.maximumUnsharpRadiusPixels, true),\n amount: finiteIn(value.amount, path + \".amount\", 0, DEVELOPMENT_DETAIL_LIMITS.maximumUnsharpAmount, true),\n threshold: finiteIn(value.threshold, path + \".threshold\", 0, 1) };\n}\n/** Validates diffraction correction settings. */\nexport function parseDiffractionCorrectionSettings(value: DiffractionCorrectionSettings, path = \"diffractionCorrection\"): DiffractionCorrectionSettings {\n const radius = value.kernelRadiusPixels;\n if (typeof radius !== \"number\" || !Number.isSafeInteger(radius) || radius < 1 || radius > DEVELOPMENT_DETAIL_LIMITS.maximumKernelRadiusPixels) {\n throw new InvalidConfigurationError(path + \".kernelRadiusPixels must be an integer from 1 to \" + DEVELOPMENT_DETAIL_LIMITS.maximumKernelRadiusPixels + \".\");\n }\n return { wavelengthNm: finiteIn(value.wavelengthNm, path + \".wavelengthNm\", DEVELOPMENT_DETAIL_LIMITS.minimumWavelengthNm, DEVELOPMENT_DETAIL_LIMITS.maximumWavelengthNm),\n regularization: finiteIn(value.regularization, path + \".regularization\", DEVELOPMENT_DETAIL_LIMITS.minimumRegularization, DEVELOPMENT_DETAIL_LIMITS.maximumRegularization),\n maximumGain: finiteIn(value.maximumGain, path + \".maximumGain\", 1, DEVELOPMENT_DETAIL_LIMITS.maximumGain), kernelRadiusPixels: radius };\n}\n/** Default FIR radius: the Airy pattern's third dark ring (3.2383·λ·N_w) in pixels, rounded up, within 1..16. */\nexport function defaultDiffractionCorrectionRadiusPixels(workingFNumber: number, wavelengthNm: number, pixelPitchMicrometers: number): number {\n const sites = Math.ceil(AIRY_THIRD_ZERO_PER_LAMBDA_N * wavelengthNm / 1000 * workingFNumber / pixelPitchMicrometers);\n return Math.min(DEVELOPMENT_DETAIL_LIMITS.maximumKernelRadiusPixels, Math.max(1, sites));\n}\n\n/**\n * Incoherent OTF of an ideal, aberration-free circular pupil at normalized frequency s = f/f_c:\n * H(s) = (2/π)(acos s − s·√(1 − s²)) for s < 1, and 0 for s ≥ 1 (MIT OCW 2.71, lectures 21–22; Goodman, §7.3).\n */\nexport function circularPupilDiffractionMtf(normalizedFrequency: number): number {\n const s = Math.abs(normalizedFrequency);\n if (s >= 1) return 0;\n return 2 / Math.PI * (Math.acos(s) - s * Math.sqrt(1 - s * s));\n}\n\n/** A designed diffraction correction filter and what its response does. Frequencies are in cycles per pixel. */\nexport interface DiffractionCorrectionFilter {\n settings: DiffractionCorrectionSettings;\n workingFNumber: number;\n pixelPitchMicrometers: number;\n /** f_c · pitch = pitch/(λ·N_w): the diffraction cut-off. The sampling Nyquist frequency is 0.5. */\n cutoffCyclesPerPixel: number;\n /** Fraction of the Nyquist square |fx|, |fy| ≤ 0.5 lying beyond the cut-off, where the lens transmitted nothing. */\n nyquistAreaBeyondCutoffFraction: number;\n /** Largest gain of the designed (untruncated) response: min(maximumGain, max over s of H(1+α)/(H² + α)). */\n designPeakGain: number;\n /** Largest |response| of the windowed, truncated FIR over a 1/64-cycle grid of the Nyquist square. */\n responsePeakGain: number;\n /**\n * Largest |response| of the FIR at grid frequencies beyond the cut-off (null when the whole Nyquist square is inside it).\n * The design is 0 there; a non-zero value is truncation leakage that passes noise and aliased signal, never lost detail,\n * because the optics delivered no signal at those frequencies.\n */\n responseMaximumBeyondCutoff: number | null;\n /** Row-major (2R+1)² weights summing to 1; weights[(dy + R)(2R + 1) + dx + R]. */\n weights: Float64Array;\n}\n\n/**\n * Designs the bounded Wiener–Tikhonov correction. In frequency (normalized s = ρ/f_c, ρ the radial frequency):\n * G(s) = min(maximumGain, H(s)·(1 + α)/(H(s)² + α)), G(0) = 1, and G(s) = 0 for s ≥ 1,\n * where α is the regularization (an assumed noise-to-signal power ratio, Wiener's K). The response is sampled on an M × M\n * grid (M a power of two ≥ max(64, 4(2R + 1))), inverse transformed, truncated to the (2R + 1)² window with a radial raised\n * cosine taper and renormalized to unit sum. The achieved response is then measured, not assumed.\n */\nexport function designDiffractionCorrectionFilter(input: { workingFNumber: number; pixelPitchMicrometers: number; settings: DiffractionCorrectionSettings }): DiffractionCorrectionFilter {\n const settings = parseDiffractionCorrectionSettings(input.settings);\n const workingFNumber = finiteIn(input.workingFNumber, \"workingFNumber\", 0, 1e4, true), pitch = finiteIn(input.pixelPitchMicrometers, \"pixelPitchMicrometers\", 0, 1e4, true);\n const cutoff = pitch / (settings.wavelengthNm / 1000 * workingFNumber), alpha = settings.regularization, bound = settings.maximumGain;\n const gainAt = (rho: number): number => {\n const h = circularPupilDiffractionMtf(rho / cutoff);\n return h === 0 ? 0 : Math.min(bound, h * (1 + alpha) / (h * h + alpha));\n };\n const R = settings.kernelRadiusPixels, size = 2 * R + 1;\n let M = 64;\n while (M < 4 * size) M *= 2;\n // A(ky, x) = Σ_kx G cos(2π kx x/M); h(x, y) = (1/M²) Σ_ky cos(2π ky y/M) A(ky, x). G is even in each axis, so h is real and even.\n const cosTable = new Float64Array(M * (R + 1));\n for (let k = 0; k < M; k++) for (let x = 0; x <= R; x++) cosTable[k * (R + 1) + x] = Math.cos(2 * Math.PI * k * x / M);\n const A = new Float64Array(M * (R + 1));\n for (let ky = 0; ky < M; ky++) {\n const fy = (ky < M / 2 ? ky : ky - M) / M;\n for (let kx = 0; kx < M; kx++) {\n const fx = (kx < M / 2 ? kx : kx - M) / M, g = gainAt(Math.hypot(fx, fy));\n if (g === 0) continue;\n for (let x = 0; x <= R; x++) A[ky * (R + 1) + x]! += g * cosTable[kx * (R + 1) + x]!;\n }\n }\n const quarter = new Float64Array((R + 1) * (R + 1));\n for (let y = 0; y <= R; y++) for (let x = 0; x <= R; x++) {\n let sum = 0;\n for (let ky = 0; ky < M; ky++) sum += cosTable[ky * (R + 1) + y]! * A[ky * (R + 1) + x]!;\n const r = Math.hypot(x, y), taper = r < R + 1 ? 0.5 * (1 + Math.cos(Math.PI * r / (R + 1))) : 0;\n quarter[y * (R + 1) + x] = sum / (M * M) * taper;\n }\n const weights = new Float64Array(size * size);\n let total = 0;\n for (let dy = -R; dy <= R; dy++) for (let dx = -R; dx <= R; dx++) {\n const w = quarter[Math.abs(dy) * (R + 1) + Math.abs(dx)]!;\n weights[(dy + R) * size + dx + R] = w; total += w;\n }\n if (!(total > 0) || !Number.isFinite(total)) throw new InvalidScientificResultError(\"Diffraction correction filter has no finite DC response.\");\n for (let i = 0; i < weights.length; i++) weights[i] = weights[i]! / total;\n let designPeakGain = 0;\n for (let i = 0; i <= 4096; i++) designPeakGain = Math.max(designPeakGain, gainAt(i / 4096 * cutoff));\n let responsePeakGain = 0, beyond: number | null = null, beyondCount = 0;\n const GRID = 64;\n for (let iy = 0; iy <= GRID / 2; iy++) for (let ix = 0; ix <= GRID / 2; ix++) {\n const fx = ix / GRID, fy = iy / GRID, response = Math.abs(evaluateDiffractionCorrectionResponse(weights, R, fx, fy));\n responsePeakGain = Math.max(responsePeakGain, response);\n if (Math.hypot(fx, fy) >= cutoff) beyond = Math.max(beyond ?? 0, response);\n }\n const AREA = 256;\n for (let iy = 0; iy < AREA; iy++) for (let ix = 0; ix < AREA; ix++) if (Math.hypot((ix + 0.5) / (2 * AREA), (iy + 0.5) / (2 * AREA)) >= cutoff) beyondCount++;\n return { settings, workingFNumber, pixelPitchMicrometers: pitch, cutoffCyclesPerPixel: cutoff, nyquistAreaBeyondCutoffFraction: beyondCount / (AREA * AREA),\n designPeakGain, responsePeakGain, responseMaximumBeyondCutoff: beyond, weights };\n}\n/** Frequency response Σ w(x, y)·cos(2π(fx·x + fy·y)) of a centred (2R + 1)² FIR at (fx, fy) cycles per pixel. */\nexport function evaluateDiffractionCorrectionResponse(weights: Float64Array, radius: number, fx: number, fy: number): number {\n const size = 2 * radius + 1;\n let sum = 0;\n for (let dy = -radius; dy <= radius; dy++) for (let dx = -radius; dx <= radius; dx++) sum += weights[(dy + radius) * size + dx + radius]! * Math.cos(2 * Math.PI * (fx * dx + fy * dy));\n return sum;\n}\n\n/** What the correction did to the linear samples. */\nexport interface DiffractionCorrectionDiagnostics {\n /** Corrected samples below 0 (ringing below black). They are clipped by the later gamut handling. */\n negativeSampleCount: number;\n /** Corrected samples above 1 (above the RAW white level). */\n aboveWhiteSampleCount: number;\n}\n/**\n * Applies the FIR to interleaved linear RGB (width × height × 3), each channel separately, edges clamped. Returns new samples.\n */\nexport function applyDiffractionCorrection(rgb: Float64Array, width: number, height: number, filter: Pick<DiffractionCorrectionFilter, \"weights\" | \"settings\">):\n { samples: Float64Array; diagnostics: DiffractionCorrectionDiagnostics } {\n if (rgb.length !== width * height * 3) throw new InvalidConfigurationError(\"Diffraction correction raster size mismatch.\");\n const R = filter.settings.kernelRadiusPixels, size = 2 * R + 1, w = filter.weights, out = new Float64Array(rgb.length);\n const xs = new Int32Array(width + 2 * R);\n for (let i = 0; i < xs.length; i++) xs[i] = Math.min(width - 1, Math.max(0, i - R)) * 3;\n let negative = 0, above = 0;\n for (let y = 0; y < height; y++) {\n const o = y * width * 3;\n for (let dy = -R; dy <= R; dy++) {\n const row = Math.min(height - 1, Math.max(0, y + dy)) * width * 3, wr = (dy + R) * size;\n for (let dx = 0; dx < size; dx++) {\n const weight = w[wr + dx]!;\n if (weight === 0) continue;\n for (let x = 0; x < width; x++) {\n const s = row + xs[x + dx]!, d = o + 3 * x;\n out[d] = out[d]! + weight * rgb[s]!; out[d + 1] = out[d + 1]! + weight * rgb[s + 1]!; out[d + 2] = out[d + 2]! + weight * rgb[s + 2]!;\n }\n }\n }\n }\n for (let i = 0; i < out.length; i++) {\n const v = out[i]!;\n if (!Number.isFinite(v)) throw new InvalidScientificResultError(\"Diffraction correction produced a non-finite sample.\");\n if (v < 0) negative++; else if (v > 1) above++;\n }\n return { samples: out, diagnostics: { negativeSampleCount: negative, aboveWhiteSampleCount: above } };\n}\n\n/** Normalized Gaussian weights exp(−i²/(2σ²)), i = −r..r with r = ⌈3σ⌉. */\nexport function unsharpMaskKernel(radiusPixels: number): Float64Array {\n const r = Math.ceil(3 * radiusPixels), k = new Float64Array(2 * r + 1);\n let total = 0;\n for (let i = -r; i <= r; i++) { const v = Math.exp(-i * i / (2 * radiusPixels * radiusPixels)); k[i + r] = v; total += v; }\n for (let i = 0; i < k.length; i++) k[i] = k[i]! / total;\n return k;\n}\n/** What the unsharp mask did. Counts are samples (one per channel). */\nexport interface UnsharpMaskDiagnostics {\n /** Half-width of the separable Gaussian, ⌈3σ⌉ pixels. */\n kernelRadiusPixels: number;\n /** Samples changed by the mask (|detail| ≥ threshold and detail ≠ 0). */\n sharpenedSampleCount: number;\n /** Samples left unchanged because 0 < |detail| < threshold. */\n thresholdHeldSampleCount: number;\n /** Sharpened samples clipped at 0 or 1 (the halo did not fit in the code range). */\n clippedLowSampleCount: number;\n clippedHighSampleCount: number;\n /** Largest increase and decrease amount·detail before clipping, in encoded units: the halo strength. */\n maximumIncrease: number;\n maximumDecrease: number;\n}\n/**\n * Unsharp mask on interleaved encoded RGB in [0, 1] (each channel separately, edges clamped):\n * d = x − (G_σ ∗ x); y = x + amount·d where |d| ≥ threshold, else y = x; then y is clipped to [0, 1].\n * Returns new samples.\n */\nexport function applyUnsharpMask(encoded: Float64Array, width: number, height: number, settings: UnsharpMaskSettings):\n { samples: Float64Array; diagnostics: UnsharpMaskDiagnostics } {\n const s = parseUnsharpMaskSettings(settings);\n if (encoded.length !== width * height * 3) throw new InvalidConfigurationError(\"Unsharp mask raster size mismatch.\");\n const k = unsharpMaskKernel(s.radiusPixels), r = (k.length - 1) / 2, horizontal = new Float64Array(encoded.length), blurred = new Float64Array(encoded.length);\n for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {\n const d = (y * width + x) * 3;\n let a = 0, b = 0, c = 0;\n for (let i = -r; i <= r; i++) {\n const sx = (y * width + Math.min(width - 1, Math.max(0, x + i))) * 3, w = k[i + r]!;\n a += w * encoded[sx]!; b += w * encoded[sx + 1]!; c += w * encoded[sx + 2]!;\n }\n horizontal[d] = a; horizontal[d + 1] = b; horizontal[d + 2] = c;\n }\n for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {\n const d = (y * width + x) * 3;\n let a = 0, b = 0, c = 0;\n for (let i = -r; i <= r; i++) {\n const sy = (Math.min(height - 1, Math.max(0, y + i)) * width + x) * 3, w = k[i + r]!;\n a += w * horizontal[sy]!; b += w * horizontal[sy + 1]!; c += w * horizontal[sy + 2]!;\n }\n blurred[d] = a; blurred[d + 1] = b; blurred[d + 2] = c;\n }\n const out = new Float64Array(encoded.length);\n let sharpened = 0, held = 0, low = 0, high = 0, increase = 0, decrease = 0;\n for (let i = 0; i < encoded.length; i++) {\n const x = encoded[i]!, detail = x - blurred[i]!;\n if (detail === 0 || Math.abs(detail) < s.threshold) { if (detail !== 0) held++; out[i] = x; continue; }\n const change = s.amount * detail;\n let v = x + change;\n sharpened++;\n if (change > increase) increase = change;\n if (-change > decrease) decrease = -change;\n if (v < 0) { v = 0; low++; } else if (v > 1) { v = 1; high++; }\n out[i] = v;\n }\n return { samples: out, diagnostics: { kernelRadiusPixels: r, sharpenedSampleCount: sharpened, thresholdHeldSampleCount: held, clippedLowSampleCount: low,\n clippedHighSampleCount: high, maximumIncrease: increase, maximumDecrease: decrease } };\n}\n\n/** SDR counters in the shape of the SDR rendering diagnostics. */\nexport interface DetailSdrCounts { negative: number; above: number; changed: number; low: number; high: number }\n/**\n * SDR rendering with an unsharp mask between the sRGB transfer and quantization. Per pixel it runs the reference renderer's\n * operations in its order (−0 → +0, rendering exposure 2^EV with reference white 1, the shared tone operator, gamut\n * handling, sRGB transfer); the whole encoded image is then sharpened and rounded to nearest (ties up).\n */\nexport function renderSdrWithUnsharpMask(rgb: Float64Array, width: number, height: number, profile: SdrRenderingProfile, settings: UnsharpMaskSettings):\n { codes: Uint8Array | Uint16Array; counts: DetailSdrCounts; sharpening: UnsharpMaskDiagnostics } {\n if (rgb.length !== width * height * 3) throw new InvalidConfigurationError(\"SDR raster size mismatch.\");\n const scale = 2 ** profile.renderingExposureEv, tone = prepareSdrPixelTone(profile), scratch = new Float64Array(3), encoded = new Float64Array(rgb.length);\n if (!Number.isFinite(scale) || scale <= 0) throw new InvalidConfigurationError(\"Rendering scale is unrepresentable.\");\n const reject = profile.gamutHandling === \"reject-out-of-range\", counts: DetailSdrCounts = { negative: 0, above: 0, changed: 0, low: 0, high: 0 };\n for (let i = 0; i < rgb.length; i += 3) {\n const r = rgb[i]!, g = rgb[i + 1]!, b = rgb[i + 2]!;\n const er = (r === 0 ? 0 : r) * scale, eg = (g === 0 ? 0 : g) * scale, eb = (b === 0 ? 0 : b) * scale;\n if (!Number.isFinite(er) || !Number.isFinite(eg) || !Number.isFinite(eb)) throw new InvalidScientificResultError(\"Rendering exposure produced a non-finite sample.\");\n tone(er, eg, eb, scratch);\n for (let c = 0; c < 3; c++) {\n const exposed = c === 0 ? er : c === 1 ? eg : eb;\n if (exposed < 0) counts.negative++;\n if (exposed > 1) counts.above++;\n let mapped = scratch[c]!;\n if (mapped !== exposed) counts.changed++;\n if (mapped === 0) mapped = 0;\n if (mapped < 0 || mapped > 1) {\n if (reject) throw new InvalidConfigurationError(\"Tone output exceeds the declared SDR gamut range.\");\n if (mapped < 0) { counts.low++; mapped = 0; } else { counts.high++; mapped = 1; }\n }\n encoded[i + c] = encodeSrgbComponent(mapped);\n }\n }\n const sharpened = applyUnsharpMask(encoded, width, height, settings), maximum = 2 ** profile.bitDepth - 1;\n const codes = profile.bitDepth === 8 ? new Uint8Array(rgb.length) : new Uint16Array(rgb.length);\n for (let i = 0; i < codes.length; i++) codes[i] = Math.round(sharpened.samples[i]! * maximum);\n return { codes, counts, sharpening: sharpened.diagnostics };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"development-detail.js","sourceRoot":"","sources":["../../src/processing/development-detail.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Development detail stages (#552), both opt-in and off by default:\n *\n * - **Diffraction correction**: a bounded, regularized (Wiener–Tikhonov) deconvolution of the developed linear RGB with the\n * diffraction OTF of an ideal circular pupil at the capture's working f-number. The OTF is zero beyond the cut-off\n * f_c = 1/(λ·N_w), so detail beyond it never reached the sensor; the filter's design gain there is 0 and it cannot, and\n * does not claim to, restore it.\n * - **Sharpening**: an unsharp mask (radius, amount, threshold) on the display-encoded values, after the tone curve and\n * gamut handling and before quantization. It raises edge contrast and creates halos; it adds no optical detail.\n *\n * Development order with these stages: (exposure gain) → demosaic → colour and white balance → diffraction correction\n * (linear) → rendering exposure and tone curve → gamut handling → sRGB transfer → unsharp mask → quantization (encode).\n *\n * Image edges are handled by clamping (edge pixels repeated). Both stages need the whole image; since #571 they also run row by\n * row in place (`correctDiffractionInPlace`, `renderSdrWithUnsharpMaskInPlace`), with the same sums in the same order.\n * @see docs/DIGITAL_OPTICS_FOUNDATION.md for the equations, sources, trade-offs and limits.\n */\n\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { InvalidScientificResultError } from \"../core/validation.js\";\nimport { renderSdrRunInto, type SdrPixelCounts } from \"../output/native-capture-sdr.js\";\nimport { prepareSdrPixelTone, type SdrRenderingProfile } from \"../output/sdr-rendering.js\";\n\n/** Version of the `development-detail` model (diffraction correction and unsharp mask). */\nexport const DEVELOPMENT_DETAIL_MODEL_VERSION = \"0.1.0\" as const;\n\n/** Unsharp mask settings: Gaussian standard deviation in output pixels, gain on the detail signal, and threshold in encoded units [0, 1]. */\nexport interface UnsharpMaskSettings { radiusPixels: number; amount: number; threshold: number }\n/** Diffraction correction settings: design wavelength, Wiener noise-to-signal regularization, gain bound and FIR radius in pixels. */\nexport interface DiffractionCorrectionSettings { wavelengthNm: number; regularization: number; maximumGain: number; kernelRadiusPixels: number }\n\n/** Accepted ranges. Bounds are engineering limits on cost and on noise gain, not quality recommendations. */\nexport const DEVELOPMENT_DETAIL_LIMITS = /* @__PURE__ */ Object.freeze({\n maximumUnsharpRadiusPixels: 8, maximumUnsharpAmount: 5,\n minimumWavelengthNm: 380, maximumWavelengthNm: 780,\n minimumRegularization: 1e-4, maximumRegularization: 1,\n maximumGain: 10, maximumKernelRadiusPixels: 16\n});\n\n/** Third dark ring of the Airy pattern, in units of λ·N (j₁,₃/π, j₁,₃ = 10.1734681…). */\nconst AIRY_THIRD_ZERO_PER_LAMBDA_N = 10.173468135062722 / Math.PI;\n\nfunction finiteIn(value: unknown, name: string, minimum: number, maximum: number, minimumExclusive = false): number {\n if (typeof value !== \"number\" || !Number.isFinite(value) || (minimumExclusive ? value <= minimum : value < minimum) || value > maximum) {\n throw new InvalidConfigurationError(name + \" must be a finite number in \" + (minimumExclusive ? \"(\" : \"[\") + minimum + \", \" + maximum + \"].\");\n }\n return value;\n}\n\n/** Validates unsharp mask settings. */\nexport function parseUnsharpMaskSettings(value: UnsharpMaskSettings, path = \"sharpening\"): UnsharpMaskSettings {\n return { radiusPixels: finiteIn(value.radiusPixels, path + \".radiusPixels\", 0, DEVELOPMENT_DETAIL_LIMITS.maximumUnsharpRadiusPixels, true),\n amount: finiteIn(value.amount, path + \".amount\", 0, DEVELOPMENT_DETAIL_LIMITS.maximumUnsharpAmount, true),\n threshold: finiteIn(value.threshold, path + \".threshold\", 0, 1) };\n}\n/** Validates diffraction correction settings. */\nexport function parseDiffractionCorrectionSettings(value: DiffractionCorrectionSettings, path = \"diffractionCorrection\"): DiffractionCorrectionSettings {\n const radius = value.kernelRadiusPixels;\n if (typeof radius !== \"number\" || !Number.isSafeInteger(radius) || radius < 1 || radius > DEVELOPMENT_DETAIL_LIMITS.maximumKernelRadiusPixels) {\n throw new InvalidConfigurationError(path + \".kernelRadiusPixels must be an integer from 1 to \" + DEVELOPMENT_DETAIL_LIMITS.maximumKernelRadiusPixels + \".\");\n }\n return { wavelengthNm: finiteIn(value.wavelengthNm, path + \".wavelengthNm\", DEVELOPMENT_DETAIL_LIMITS.minimumWavelengthNm, DEVELOPMENT_DETAIL_LIMITS.maximumWavelengthNm),\n regularization: finiteIn(value.regularization, path + \".regularization\", DEVELOPMENT_DETAIL_LIMITS.minimumRegularization, DEVELOPMENT_DETAIL_LIMITS.maximumRegularization),\n maximumGain: finiteIn(value.maximumGain, path + \".maximumGain\", 1, DEVELOPMENT_DETAIL_LIMITS.maximumGain), kernelRadiusPixels: radius };\n}\n/** Default FIR radius: the Airy pattern's third dark ring (3.2383·λ·N_w) in pixels, rounded up, within 1..16. */\nexport function defaultDiffractionCorrectionRadiusPixels(workingFNumber: number, wavelengthNm: number, pixelPitchMicrometers: number): number {\n const sites = Math.ceil(AIRY_THIRD_ZERO_PER_LAMBDA_N * wavelengthNm / 1000 * workingFNumber / pixelPitchMicrometers);\n return Math.min(DEVELOPMENT_DETAIL_LIMITS.maximumKernelRadiusPixels, Math.max(1, sites));\n}\n\n/**\n * Incoherent OTF of an ideal, aberration-free circular pupil at normalized frequency s = f/f_c:\n * H(s) = (2/π)(acos s − s·√(1 − s²)) for s < 1, and 0 for s ≥ 1 (MIT OCW 2.71, lectures 21–22; Goodman, §7.3).\n */\nexport function circularPupilDiffractionMtf(normalizedFrequency: number): number {\n const s = Math.abs(normalizedFrequency);\n if (s >= 1) return 0;\n return 2 / Math.PI * (Math.acos(s) - s * Math.sqrt(1 - s * s));\n}\n\n/** A designed diffraction correction filter and what its response does. Frequencies are in cycles per pixel. */\nexport interface DiffractionCorrectionFilter {\n settings: DiffractionCorrectionSettings;\n workingFNumber: number;\n pixelPitchMicrometers: number;\n /** f_c · pitch = pitch/(λ·N_w): the diffraction cut-off. The sampling Nyquist frequency is 0.5. */\n cutoffCyclesPerPixel: number;\n /** Fraction of the Nyquist square |fx|, |fy| ≤ 0.5 lying beyond the cut-off, where the lens transmitted nothing. */\n nyquistAreaBeyondCutoffFraction: number;\n /** Largest gain of the designed (untruncated) response: min(maximumGain, max over s of H(1+α)/(H² + α)). */\n designPeakGain: number;\n /** Largest |response| of the windowed, truncated FIR over a 1/64-cycle grid of the Nyquist square. */\n responsePeakGain: number;\n /**\n * Largest |response| of the FIR at grid frequencies beyond the cut-off (null when the whole Nyquist square is inside it).\n * The design is 0 there; a non-zero value is truncation leakage that passes noise and aliased signal, never lost detail,\n * because the optics delivered no signal at those frequencies.\n */\n responseMaximumBeyondCutoff: number | null;\n /** Row-major (2R+1)² weights summing to 1; weights[(dy + R)(2R + 1) + dx + R]. */\n weights: Float64Array;\n}\n\n/**\n * Designs the bounded Wiener–Tikhonov correction. In frequency (normalized s = ρ/f_c, ρ the radial frequency):\n * G(s) = min(maximumGain, H(s)·(1 + α)/(H(s)² + α)), G(0) = 1, and G(s) = 0 for s ≥ 1,\n * where α is the regularization (an assumed noise-to-signal power ratio, Wiener's K). The response is sampled on an M × M\n * grid (M a power of two ≥ max(64, 4(2R + 1))), inverse transformed, truncated to the (2R + 1)² window with a radial raised\n * cosine taper and renormalized to unit sum. The achieved response is then measured, not assumed.\n */\nexport function designDiffractionCorrectionFilter(input: { workingFNumber: number; pixelPitchMicrometers: number; settings: DiffractionCorrectionSettings }): DiffractionCorrectionFilter {\n const settings = parseDiffractionCorrectionSettings(input.settings);\n const workingFNumber = finiteIn(input.workingFNumber, \"workingFNumber\", 0, 1e4, true), pitch = finiteIn(input.pixelPitchMicrometers, \"pixelPitchMicrometers\", 0, 1e4, true);\n const cutoff = pitch / (settings.wavelengthNm / 1000 * workingFNumber), alpha = settings.regularization, bound = settings.maximumGain;\n const gainAt = (rho: number): number => {\n const h = circularPupilDiffractionMtf(rho / cutoff);\n return h === 0 ? 0 : Math.min(bound, h * (1 + alpha) / (h * h + alpha));\n };\n const R = settings.kernelRadiusPixels, size = 2 * R + 1;\n let M = 64;\n while (M < 4 * size) M *= 2;\n // A(ky, x) = Σ_kx G cos(2π kx x/M); h(x, y) = (1/M²) Σ_ky cos(2π ky y/M) A(ky, x). G is even in each axis, so h is real and even.\n const cosTable = new Float64Array(M * (R + 1));\n for (let k = 0; k < M; k++) for (let x = 0; x <= R; x++) cosTable[k * (R + 1) + x] = Math.cos(2 * Math.PI * k * x / M);\n const A = new Float64Array(M * (R + 1));\n for (let ky = 0; ky < M; ky++) {\n const fy = (ky < M / 2 ? ky : ky - M) / M;\n for (let kx = 0; kx < M; kx++) {\n const fx = (kx < M / 2 ? kx : kx - M) / M, g = gainAt(Math.hypot(fx, fy));\n if (g === 0) continue;\n for (let x = 0; x <= R; x++) A[ky * (R + 1) + x]! += g * cosTable[kx * (R + 1) + x]!;\n }\n }\n const quarter = new Float64Array((R + 1) * (R + 1));\n for (let y = 0; y <= R; y++) for (let x = 0; x <= R; x++) {\n let sum = 0;\n for (let ky = 0; ky < M; ky++) sum += cosTable[ky * (R + 1) + y]! * A[ky * (R + 1) + x]!;\n const r = Math.hypot(x, y), taper = r < R + 1 ? 0.5 * (1 + Math.cos(Math.PI * r / (R + 1))) : 0;\n quarter[y * (R + 1) + x] = sum / (M * M) * taper;\n }\n const weights = new Float64Array(size * size);\n let total = 0;\n for (let dy = -R; dy <= R; dy++) for (let dx = -R; dx <= R; dx++) {\n const w = quarter[Math.abs(dy) * (R + 1) + Math.abs(dx)]!;\n weights[(dy + R) * size + dx + R] = w; total += w;\n }\n if (!(total > 0) || !Number.isFinite(total)) throw new InvalidScientificResultError(\"Diffraction correction filter has no finite DC response.\");\n for (let i = 0; i < weights.length; i++) weights[i] = weights[i]! / total;\n let designPeakGain = 0;\n for (let i = 0; i <= 4096; i++) designPeakGain = Math.max(designPeakGain, gainAt(i / 4096 * cutoff));\n let responsePeakGain = 0, beyond: number | null = null, beyondCount = 0;\n const GRID = 64;\n for (let iy = 0; iy <= GRID / 2; iy++) for (let ix = 0; ix <= GRID / 2; ix++) {\n const fx = ix / GRID, fy = iy / GRID, response = Math.abs(evaluateDiffractionCorrectionResponse(weights, R, fx, fy));\n responsePeakGain = Math.max(responsePeakGain, response);\n if (Math.hypot(fx, fy) >= cutoff) beyond = Math.max(beyond ?? 0, response);\n }\n const AREA = 256;\n for (let iy = 0; iy < AREA; iy++) for (let ix = 0; ix < AREA; ix++) if (Math.hypot((ix + 0.5) / (2 * AREA), (iy + 0.5) / (2 * AREA)) >= cutoff) beyondCount++;\n return { settings, workingFNumber, pixelPitchMicrometers: pitch, cutoffCyclesPerPixel: cutoff, nyquistAreaBeyondCutoffFraction: beyondCount / (AREA * AREA),\n designPeakGain, responsePeakGain, responseMaximumBeyondCutoff: beyond, weights };\n}\n/** Frequency response Σ w(x, y)·cos(2π(fx·x + fy·y)) of a centred (2R + 1)² FIR at (fx, fy) cycles per pixel. */\nexport function evaluateDiffractionCorrectionResponse(weights: Float64Array, radius: number, fx: number, fy: number): number {\n const size = 2 * radius + 1;\n let sum = 0;\n for (let dy = -radius; dy <= radius; dy++) for (let dx = -radius; dx <= radius; dx++) sum += weights[(dy + radius) * size + dx + radius]! * Math.cos(2 * Math.PI * (fx * dx + fy * dy));\n return sum;\n}\n\n/** What the correction did to the linear samples. */\nexport interface DiffractionCorrectionDiagnostics {\n /** Corrected samples below 0 (ringing below black). They are clipped by the later gamut handling. */\n negativeSampleCount: number;\n /** Corrected samples above 1 (above the RAW white level). */\n aboveWhiteSampleCount: number;\n}\n/**\n * Applies the FIR to interleaved linear RGB (width × height × 3), each channel separately, edges clamped. Returns new samples.\n */\nexport function applyDiffractionCorrection(rgb: Float64Array, width: number, height: number, filter: Pick<DiffractionCorrectionFilter, \"weights\" | \"settings\">):\n { samples: Float64Array; diagnostics: DiffractionCorrectionDiagnostics } {\n if (rgb.length !== width * height * 3) throw new InvalidConfigurationError(\"Diffraction correction raster size mismatch.\");\n const samples = Float64Array.from(rgb);\n return { samples, diagnostics: drainDetailRows(correctDiffractionInPlace(samples, width, height, filter)) };\n}\n/** Runs a row-by-row detail stage to its end and returns its result. */\nexport function drainDetailRows<T>(rows: Generator<number, T, void>): T {\n let step = rows.next();\n while (!step.done) step = rows.next();\n return step.value;\n}\n/**\n * `applyDiffractionCorrection` in place, one output row per step (#571); each step yields the number of rows done. Output row y\n * reads input rows y − R … y + R, so it waits in a ring of R + 1 rows and replaces input row y once row y + R is done. Every sample\n * is the same sum in the same order as in a separate output image, so the result is bit-identical, with one image in memory\n * instead of two. `rgb` must hold width × height pixels.\n */\nexport function* correctDiffractionInPlace(rgb: Float64Array, width: number, height: number, filter: Pick<DiffractionCorrectionFilter, \"weights\" | \"settings\">):\n Generator<number, DiffractionCorrectionDiagnostics, void> {\n const R = filter.settings.kernelRadiusPixels, size = 2 * R + 1, w = filter.weights, rowLength = width * 3, ring = new Float64Array((R + 1) * rowLength);\n const xs = new Int32Array(width + 2 * R);\n for (let i = 0; i < xs.length; i++) xs[i] = Math.min(width - 1, Math.max(0, i - R)) * 3;\n let negative = 0, above = 0;\n for (let y = 0; y <= height + R; y++) {\n // Output row y − R − 1 holds the slot of row y; input row y − R − 1 is read by no later output row, so it takes its place.\n const o = (y % (R + 1)) * rowLength;\n if (y > R) rgb.set(ring.subarray(o, o + rowLength), (y - R - 1) * rowLength);\n if (y >= height) continue;\n ring.fill(0, o, o + rowLength);\n for (let dy = -R; dy <= R; dy++) {\n const row = Math.min(height - 1, Math.max(0, y + dy)) * rowLength, wr = (dy + R) * size;\n for (let dx = 0; dx < size; dx++) {\n const weight = w[wr + dx]!;\n if (weight === 0) continue;\n for (let x = 0; x < width; x++) {\n const s = row + xs[x + dx]!, d = o + 3 * x;\n ring[d] = ring[d]! + weight * rgb[s]!; ring[d + 1] = ring[d + 1]! + weight * rgb[s + 1]!; ring[d + 2] = ring[d + 2]! + weight * rgb[s + 2]!;\n }\n }\n }\n for (let i = o; i < o + rowLength; i++) {\n const v = ring[i]!;\n if (!Number.isFinite(v)) throw new InvalidScientificResultError(\"Diffraction correction produced a non-finite sample.\");\n if (v < 0) negative++; else if (v > 1) above++;\n }\n yield y + 1;\n }\n return { negativeSampleCount: negative, aboveWhiteSampleCount: above };\n}\n\n/** Normalized Gaussian weights exp(−i²/(2σ²)), i = −r..r with r = ⌈3σ⌉. */\nexport function unsharpMaskKernel(radiusPixels: number): Float64Array {\n const r = Math.ceil(3 * radiusPixels), k = new Float64Array(2 * r + 1);\n let total = 0;\n for (let i = -r; i <= r; i++) { const v = Math.exp(-i * i / (2 * radiusPixels * radiusPixels)); k[i + r] = v; total += v; }\n for (let i = 0; i < k.length; i++) k[i] = k[i]! / total;\n return k;\n}\n/** What the unsharp mask did. Counts are samples (one per channel). */\nexport interface UnsharpMaskDiagnostics {\n /** Half-width of the separable Gaussian, ⌈3σ⌉ pixels. */\n kernelRadiusPixels: number;\n /** Samples changed by the mask (|detail| ≥ threshold and detail ≠ 0). */\n sharpenedSampleCount: number;\n /** Samples left unchanged because 0 < |detail| < threshold. */\n thresholdHeldSampleCount: number;\n /** Sharpened samples clipped at 0 or 1 (the halo did not fit in the code range). */\n clippedLowSampleCount: number;\n clippedHighSampleCount: number;\n /** Largest increase and decrease amount·detail before clipping, in encoded units: the halo strength. */\n maximumIncrease: number;\n maximumDecrease: number;\n}\n/**\n * Unsharp mask on interleaved encoded RGB in [0, 1] (each channel separately, edges clamped):\n * d = x − (G_σ ∗ x); y = x + amount·d where |d| ≥ threshold, else y = x; then y is clipped to [0, 1].\n * Returns new samples.\n */\nexport function applyUnsharpMask(encoded: Float64Array, width: number, height: number, settings: UnsharpMaskSettings):\n { samples: Float64Array; diagnostics: UnsharpMaskDiagnostics } {\n const s = parseUnsharpMaskSettings(settings);\n if (encoded.length !== width * height * 3) throw new InvalidConfigurationError(\"Unsharp mask raster size mismatch.\");\n const samples = new Float64Array(encoded.length);\n return { samples, diagnostics: drainDetailRows(unsharpMaskRows(encoded, width, height, s, samples)) };\n}\n/**\n * The unsharp mask one output row per step (#571); each step yields the number of rows done. The horizontal Gaussian pass of\n * each input row is kept in a ring of 2r + 1 rows, and `prepareRow(y)`, when given, runs once on input row y before it is\n * first read (`renderSdrWithUnsharpMaskInPlace` encodes the row there). Output row y goes to `out`: the clipped value, or\n * Math.round(value · scale) with a `scale`. Every sum is the same, in the same order, as in whole-image passes, so the\n * result is bit-identical, with 2r + 1 rows of working memory instead of three images. `settings` must be parsed and the\n * raster sizes must match.\n */\nexport function* unsharpMaskRows(encoded: Float64Array, width: number, height: number, settings: UnsharpMaskSettings, out: Float64Array | Uint8Array | Uint16Array,\n scale?: number, prepareRow?: (y: number) => void): Generator<number, UnsharpMaskDiagnostics, void> {\n const k = unsharpMaskKernel(settings.radiusPixels), r = (k.length - 1) / 2, span = 2 * r + 1, rowLength = width * 3, ring = new Float64Array(span * rowLength);\n const amount = settings.amount, threshold = settings.threshold;\n let sharpened = 0, held = 0, low = 0, high = 0, increase = 0, decrease = 0, ready = 0;\n for (let y = 0; y < height; y++) {\n // Rows up to y + r, prepared and horizontally blurred.\n for (const last = Math.min(height - 1, y + r); ready <= last; ready++) {\n prepareRow?.(ready);\n const source = ready * rowLength, slot = (ready % span) * rowLength;\n for (let x = 0; x < width; x++) {\n let a = 0, b = 0, c = 0;\n for (let i = -r; i <= r; i++) {\n const sx = source + Math.min(width - 1, Math.max(0, x + i)) * 3, w = k[i + r]!;\n a += w * encoded[sx]!; b += w * encoded[sx + 1]!; c += w * encoded[sx + 2]!;\n }\n ring[slot + 3 * x] = a; ring[slot + 3 * x + 1] = b; ring[slot + 3 * x + 2] = c;\n }\n }\n const o = y * rowLength;\n for (let x = 0; x < width; x++) {\n let a = 0, b = 0, c = 0;\n for (let i = -r; i <= r; i++) {\n const sy = (Math.min(height - 1, Math.max(0, y + i)) % span) * rowLength + 3 * x, w = k[i + r]!;\n a += w * ring[sy]!; b += w * ring[sy + 1]!; c += w * ring[sy + 2]!;\n }\n for (let channel = 0; channel < 3; channel++) {\n const index = o + 3 * x + channel, value = encoded[index]!, detail = value - (channel === 0 ? a : channel === 1 ? b : c);\n let v = value;\n if (detail === 0 || Math.abs(detail) < threshold) { if (detail !== 0) held++; }\n else {\n const change = amount * detail;\n v = value + change;\n sharpened++;\n if (change > increase) increase = change;\n if (-change > decrease) decrease = -change;\n if (v < 0) { v = 0; low++; } else if (v > 1) { v = 1; high++; }\n }\n out[index] = scale === undefined ? v : Math.round(v * scale);\n }\n }\n yield y + 1;\n }\n return { kernelRadiusPixels: r, sharpenedSampleCount: sharpened, thresholdHeldSampleCount: held, clippedLowSampleCount: low,\n clippedHighSampleCount: high, maximumIncrease: increase, maximumDecrease: decrease };\n}\n\n/** SDR counters in the shape of the SDR rendering diagnostics. */\nexport interface DetailSdrCounts { negative: number; above: number; changed: number; low: number; high: number }\n/**\n * SDR rendering with an unsharp mask between the sRGB transfer and quantization. Per pixel it runs the reference renderer's\n * operations in its order (−0 → +0, rendering exposure 2^EV with reference white 1, the shared tone operator, gamut\n * handling, sRGB transfer); the whole encoded image is then sharpened and rounded to nearest (ties up).\n */\nexport function renderSdrWithUnsharpMask(rgb: Float64Array, width: number, height: number, profile: SdrRenderingProfile, settings: UnsharpMaskSettings):\n { codes: Uint8Array | Uint16Array; counts: DetailSdrCounts; sharpening: UnsharpMaskDiagnostics } {\n if (rgb.length !== width * height * 3) throw new InvalidConfigurationError(\"SDR raster size mismatch.\");\n const { codes, counts: { negative, above, changed, low, high }, sharpening } =\n drainDetailRows(renderSdrWithUnsharpMaskInPlace(Float64Array.from(rgb), width, height, profile, parseUnsharpMaskSettings(settings)));\n return { codes, counts: { negative, above, changed, low, high }, sharpening };\n}\n/**\n * `renderSdrWithUnsharpMask` one output row per step (#571), encoding each row of `rgb` in place just before the mask first reads\n * it (`rgb` then holds the encoded values). Bit-identical to the whole-image function, with no second image in memory. `settings`\n * must be parsed and `rgb` must hold width × height pixels.\n */\nexport function* renderSdrWithUnsharpMaskInPlace(rgb: Float64Array, width: number, height: number, profile: SdrRenderingProfile, settings: UnsharpMaskSettings):\n Generator<number, { codes: Uint8Array | Uint16Array; counts: SdrPixelCounts; sharpening: UnsharpMaskDiagnostics }, void> {\n const scale = 2 ** profile.renderingExposureEv, tone = prepareSdrPixelTone(profile), scratch = new Float64Array(3);\n if (!Number.isFinite(scale) || scale <= 0) throw new InvalidConfigurationError(\"Rendering scale is unrepresentable.\");\n const counts: SdrPixelCounts = { negative: 0, above: 0, changed: 0, low: 0, high: 0, outOfGamut: false };\n // The native SDR kernel per row, unquantized (maximum 0), in place: the reference renderer's operations in its order.\n const encodeRow = (y: number): void => {\n renderSdrRunInto(rgb, y * width * 3, width, profile, tone, scale, 0, rgb, y * width * 3, scratch, counts);\n if (counts.outOfGamut) throw new InvalidConfigurationError(\"Tone output exceeds the declared SDR gamut range.\");\n };\n const codes = profile.bitDepth === 8 ? new Uint8Array(rgb.length) : new Uint16Array(rgb.length);\n const sharpening = yield* unsharpMaskRows(rgb, width, height, settings, codes, 2 ** profile.bitDepth - 1, encodeRow);\n return { codes, counts, sharpening };\n}\n"]}
|
|
@@ -12,48 +12,10 @@
|
|
|
12
12
|
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
13
13
|
import { parseEvidenceList } from "../core/evidence-provenance.js";
|
|
14
14
|
import { InvalidScientificInputError, requirePositiveInteger } from "../core/validation.js";
|
|
15
|
-
import { requireInputKeys,
|
|
15
|
+
import { requireInputKeys, rejectUnknownVariantFields, requireFixedFieldsWhenPresent, requireConfigRecord as requireRecord, requireTrimmedConfigString as requireNonEmptyString, requireConfigFinite as requireFinite, parseConfigScientificStatus as parseScientificStatus, requireConfigFiniteAboveZero as requirePositiveFinite, requireConfigFiniteAtLeastZero as requireNonNegativeFinite } from "../core/record-validation.js";
|
|
16
|
+
import { parseUncertainty } from "../optics/lens-psf-profile.js";
|
|
17
|
+
import { positiveModulo } from "../optics/complex-pupil-psf.js";
|
|
16
18
|
export const SCENE_ILLUMINATION_TEMPORAL_PROFILE_SCHEMA_VERSION = "0.1.0";
|
|
17
|
-
function requireRecord(value, path, allowedKeys) {
|
|
18
|
-
if (typeof value !== "object" ||
|
|
19
|
-
value === null ||
|
|
20
|
-
Array.isArray(value)) {
|
|
21
|
-
throw new InvalidConfigurationError(path + " must be an object.");
|
|
22
|
-
}
|
|
23
|
-
if (allowedKeys !== null)
|
|
24
|
-
rejectUnknownFields(value, allowedKeys, path);
|
|
25
|
-
return value;
|
|
26
|
-
}
|
|
27
|
-
function requireNonEmptyString(value, path) {
|
|
28
|
-
if (typeof value !== "string" ||
|
|
29
|
-
value.trim().length === 0) {
|
|
30
|
-
throw new InvalidConfigurationError(path + " must be a non-empty string.");
|
|
31
|
-
}
|
|
32
|
-
return value.trim();
|
|
33
|
-
}
|
|
34
|
-
function requireFinite(value, path) {
|
|
35
|
-
if (typeof value !== "number" ||
|
|
36
|
-
!Number.isFinite(value)) {
|
|
37
|
-
throw new InvalidConfigurationError(path + " must be finite.");
|
|
38
|
-
}
|
|
39
|
-
return value;
|
|
40
|
-
}
|
|
41
|
-
function requirePositiveFinite(value, path) {
|
|
42
|
-
const parsed = requireFinite(value, path);
|
|
43
|
-
if (parsed <= 0) {
|
|
44
|
-
throw new InvalidConfigurationError(path +
|
|
45
|
-
" must be greater than zero.");
|
|
46
|
-
}
|
|
47
|
-
return parsed;
|
|
48
|
-
}
|
|
49
|
-
function requireNonNegativeFinite(value, path) {
|
|
50
|
-
const parsed = requireFinite(value, path);
|
|
51
|
-
if (parsed < 0) {
|
|
52
|
-
throw new InvalidConfigurationError(path +
|
|
53
|
-
" must be greater than or equal to zero.");
|
|
54
|
-
}
|
|
55
|
-
return parsed;
|
|
56
|
-
}
|
|
57
19
|
function requireReusableNumericEvidence(value, path) {
|
|
58
20
|
const evidence = parseEvidenceList(value, path);
|
|
59
21
|
if (!evidence.some((entry) => entry.reuseStatus ===
|
|
@@ -65,31 +27,6 @@ function requireReusableNumericEvidence(value, path) {
|
|
|
65
27
|
}
|
|
66
28
|
return evidence;
|
|
67
29
|
}
|
|
68
|
-
function parseScientificStatus(value, path) {
|
|
69
|
-
if (value !== "calibrated" &&
|
|
70
|
-
value !== "approximation") {
|
|
71
|
-
throw new InvalidConfigurationError(path + " is invalid.");
|
|
72
|
-
}
|
|
73
|
-
return value;
|
|
74
|
-
}
|
|
75
|
-
function parseUncertainty(value, path) {
|
|
76
|
-
const record = requireRecord(value, path, null);
|
|
77
|
-
rejectUnknownVariantFields(record, "kind", UNCERTAINTY_VARIANT_FIELDS, path);
|
|
78
|
-
if (record.kind === "relative") {
|
|
79
|
-
return {
|
|
80
|
-
kind: "relative",
|
|
81
|
-
fraction: requireNonNegativeFinite(record.fraction, path + ".fraction"),
|
|
82
|
-
basis: requireNonEmptyString(record.basis, path + ".basis")
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
if (record.kind === "not-quantified") {
|
|
86
|
-
return {
|
|
87
|
-
kind: "not-quantified",
|
|
88
|
-
limitation: requireNonEmptyString(record.limitation, path + ".limitation")
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
throw new InvalidConfigurationError(path + ".kind is invalid.");
|
|
92
|
-
}
|
|
93
30
|
function parseRegistrationUncertainty(value, path) {
|
|
94
31
|
const record = requireRecord(value, path, null);
|
|
95
32
|
rejectUnknownVariantFields(record, "kind", { "absolute-seconds": ["kind", "plusMinusSeconds", "basis"], "not-quantified": ["kind", "limitation"] }, path);
|
|
@@ -394,12 +331,6 @@ function interpolateWaveform(samples, timeSeconds) {
|
|
|
394
331
|
return last
|
|
395
332
|
.relativeMagnitudeMultiplier;
|
|
396
333
|
}
|
|
397
|
-
function positiveModulo(value, modulus) {
|
|
398
|
-
const remainder = value % modulus;
|
|
399
|
-
return remainder < 0
|
|
400
|
-
? remainder + modulus
|
|
401
|
-
: remainder;
|
|
402
|
-
}
|
|
403
334
|
function evaluateWaveform(waveform, waveformTimeSeconds) {
|
|
404
335
|
if (waveform.kind ===
|
|
405
336
|
"aperiodic-relative-multiplier") {
|