@photivra/engine 2.18.0 → 2.19.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.
Files changed (261) hide show
  1. package/CHANGELOG.md +68 -1
  2. package/CITATION.cff +1 -1
  3. package/README.md +6 -6
  4. package/SECURITY.md +1 -1
  5. package/dist/capture/focus-control.js +2 -75
  6. package/dist/capture/focus-control.js.map +1 -1
  7. package/dist/capture/native-environment-lean.d.ts +1 -0
  8. package/dist/capture/native-environment-lean.js +1 -1
  9. package/dist/capture/native-environment-lean.js.map +1 -1
  10. package/dist/capture/native-environment-optical-stage.d.ts +1 -0
  11. package/dist/capture/native-environment-optical-stage.js +2 -5
  12. package/dist/capture/native-environment-optical-stage.js.map +1 -1
  13. package/dist/capture/native-environment-raw.js +1 -5
  14. package/dist/capture/native-environment-raw.js.map +1 -1
  15. package/dist/capture/release-sequence.js +2 -47
  16. package/dist/capture/release-sequence.js.map +1 -1
  17. package/dist/color/white-balance.d.ts +2 -0
  18. package/dist/color/white-balance.js +3 -56
  19. package/dist/color/white-balance.js.map +1 -1
  20. package/dist/composition/image-formation-plan.d.ts +1 -0
  21. package/dist/composition/image-formation-plan.js +3 -49
  22. package/dist/composition/image-formation-plan.js.map +1 -1
  23. package/dist/core/evidence-provenance.js +1 -16
  24. package/dist/core/evidence-provenance.js.map +1 -1
  25. package/dist/core/record-validation.d.ts +54 -0
  26. package/dist/core/record-validation.js +176 -0
  27. package/dist/core/record-validation.js.map +1 -1
  28. package/dist/core/scientific-assurance.js +1 -18
  29. package/dist/core/scientific-assurance.js.map +1 -1
  30. package/dist/core/shared-helpers.d.ts +33 -0
  31. package/dist/core/shared-helpers.js +34 -0
  32. package/dist/core/shared-helpers.js.map +1 -0
  33. package/dist/core/spectral-composition.js +1 -36
  34. package/dist/core/spectral-composition.js.map +1 -1
  35. package/dist/core/spectral.js +1 -19
  36. package/dist/core/spectral.js.map +1 -1
  37. package/dist/core/version.d.ts +1 -1
  38. package/dist/core/version.js +1 -1
  39. package/dist/core/version.js.map +1 -1
  40. package/dist/equipment/exposure-capabilities.d.ts +1 -0
  41. package/dist/equipment/exposure-capabilities.js +2 -36
  42. package/dist/equipment/exposure-capabilities.js.map +1 -1
  43. package/dist/equipment/iso-capabilities.js +2 -49
  44. package/dist/equipment/iso-capabilities.js.map +1 -1
  45. package/dist/equipment/metering-capabilities.js +1 -26
  46. package/dist/equipment/metering-capabilities.js.map +1 -1
  47. package/dist/equipment/release-capabilities.js +1 -53
  48. package/dist/equipment/release-capabilities.js.map +1 -1
  49. package/dist/exposure/auto-iso-slowest-shutter.js +1 -7
  50. package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -1
  51. package/dist/exposure/exposure-duration-control.js +1 -16
  52. package/dist/exposure/exposure-duration-control.js.map +1 -1
  53. package/dist/exposure/exposure-mode-resolver.js +1 -17
  54. package/dist/exposure/exposure-mode-resolver.js.map +1 -1
  55. package/dist/exposure/filtered-exposure-time.d.ts +93 -0
  56. package/dist/exposure/filtered-exposure-time.js +129 -0
  57. package/dist/exposure/filtered-exposure-time.js.map +1 -0
  58. package/dist/exposure/flash-ttl.d.ts +1 -0
  59. package/dist/exposure/flash-ttl.js +1 -1
  60. package/dist/exposure/flash-ttl.js.map +1 -1
  61. package/dist/exposure/flash.js +1 -49
  62. package/dist/exposure/flash.js.map +1 -1
  63. package/dist/exposure/metering-scene-radiance.js +1 -25
  64. package/dist/exposure/metering-scene-radiance.js.map +1 -1
  65. package/dist/exposure/metering-target.js +1 -30
  66. package/dist/exposure/metering-target.js.map +1 -1
  67. package/dist/exposure/metering-temporal.js +1 -8
  68. package/dist/exposure/metering-temporal.js.map +1 -1
  69. package/dist/exposure/metering.js +1 -72
  70. package/dist/exposure/metering.js.map +1 -1
  71. package/dist/exposure/program-line.js +1 -45
  72. package/dist/exposure/program-line.js.map +1 -1
  73. package/dist/facade/develop-joined-parts.d.ts +29 -0
  74. package/dist/facade/develop-joined-parts.js +33 -0
  75. package/dist/facade/develop-joined-parts.js.map +1 -0
  76. package/dist/facade/generic-lens-capture.d.ts +4 -2
  77. package/dist/facade/generic-lens-capture.js +4 -2
  78. package/dist/facade/generic-lens-capture.js.map +1 -1
  79. package/dist/facade/simulate-capture.d.ts +37 -5
  80. package/dist/facade/simulate-capture.js +153 -96
  81. package/dist/facade/simulate-capture.js.map +1 -1
  82. package/dist/gpu/chart-composite-gpu.js +1 -3
  83. package/dist/gpu/chart-composite-gpu.js.map +1 -1
  84. package/dist/gpu/chart-radiance-gpu.js +1 -5
  85. package/dist/gpu/chart-radiance-gpu.js.map +1 -1
  86. package/dist/gpu/display-view-quality-gpu.d.ts +13 -1
  87. package/dist/gpu/display-view-quality-gpu.js +4 -8
  88. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  89. package/dist/gpu/display-view-speed-gpu.js +3 -25
  90. package/dist/gpu/display-view-speed-gpu.js.map +1 -1
  91. package/dist/gpu/sensor-stage-gpu.js +1 -5
  92. package/dist/gpu/sensor-stage-gpu.js.map +1 -1
  93. package/dist/gpu/webgpu-runtime.d.ts +2 -0
  94. package/dist/gpu/webgpu-runtime.js +3 -0
  95. package/dist/gpu/webgpu-runtime.js.map +1 -1
  96. package/dist/index.d.ts +4 -1
  97. package/dist/index.js +3 -0
  98. package/dist/index.js.map +1 -1
  99. package/dist/motion/camera-rotation.js +1 -6
  100. package/dist/motion/camera-rotation.js.map +1 -1
  101. package/dist/motion/capture-rotation-inverse-mapping.d.ts +12 -0
  102. package/dist/motion/capture-rotation-inverse-mapping.js +2 -2
  103. package/dist/motion/capture-rotation-inverse-mapping.js.map +1 -1
  104. package/dist/motion/capture-rotation-temporal-quadrature.d.ts +1 -0
  105. package/dist/motion/capture-rotation-temporal-quadrature.js +1 -1
  106. package/dist/motion/capture-rotation-temporal-quadrature.js.map +1 -1
  107. package/dist/motion/capture-rotation-trajectory.js +2 -17
  108. package/dist/motion/capture-rotation-trajectory.js.map +1 -1
  109. package/dist/motion/capture-translation-parallax-temporal-quadrature.d.ts +1 -0
  110. package/dist/motion/capture-translation-parallax-temporal-quadrature.js +3 -14
  111. package/dist/motion/capture-translation-parallax-temporal-quadrature.js.map +1 -1
  112. package/dist/motion/extended-object-projection.js +5 -40
  113. package/dist/motion/extended-object-projection.js.map +1 -1
  114. package/dist/optics/atmospheric-haze.js +1 -7
  115. package/dist/optics/atmospheric-haze.js.map +1 -1
  116. package/dist/optics/brown-conrady-distortion.js +1 -7
  117. package/dist/optics/brown-conrady-distortion.js.map +1 -1
  118. package/dist/optics/complex-pupil-psf.d.ts +1 -0
  119. package/dist/optics/complex-pupil-psf.js +2 -84
  120. package/dist/optics/complex-pupil-psf.js.map +1 -1
  121. package/dist/optics/focus-state.js +1 -11
  122. package/dist/optics/focus-state.js.map +1 -1
  123. package/dist/optics/front-of-lens-filter.d.ts +5 -0
  124. package/dist/optics/front-of-lens-filter.js +3 -65
  125. package/dist/optics/front-of-lens-filter.js.map +1 -1
  126. package/dist/optics/generic-nd-filter-presets.d.ts +12 -0
  127. package/dist/optics/generic-nd-filter-presets.js +48 -0
  128. package/dist/optics/generic-nd-filter-presets.js.map +1 -0
  129. package/dist/optics/illumination-vignetting.js +1 -6
  130. package/dist/optics/illumination-vignetting.js.map +1 -1
  131. package/dist/optics/lens-psf-profile.d.ts +4 -1
  132. package/dist/optics/lens-psf-profile.js +12 -111
  133. package/dist/optics/lens-psf-profile.js.map +1 -1
  134. package/dist/optics/longitudinal-chromatic-aberration.js +1 -7
  135. package/dist/optics/longitudinal-chromatic-aberration.js.map +1 -1
  136. package/dist/optics/pupil-vignetting.js +1 -7
  137. package/dist/optics/pupil-vignetting.js.map +1 -1
  138. package/dist/optics/radial-distortion.js +1 -6
  139. package/dist/optics/radial-distortion.js.map +1 -1
  140. package/dist/optics/scene-to-sensor-irradiance.js +4 -96
  141. package/dist/optics/scene-to-sensor-irradiance.js.map +1 -1
  142. package/dist/optics/stray-light.d.ts +1 -0
  143. package/dist/optics/stray-light.js +1 -1
  144. package/dist/optics/stray-light.js.map +1 -1
  145. package/dist/optics/zernike-wavefront.js +1 -7
  146. package/dist/optics/zernike-wavefront.js.map +1 -1
  147. package/dist/output/capture-geometry.js +1 -8
  148. package/dist/output/capture-geometry.js.map +1 -1
  149. package/dist/output/developed-lens-corrections.d.ts +62 -0
  150. package/dist/output/developed-lens-corrections.js +135 -0
  151. package/dist/output/developed-lens-corrections.js.map +1 -0
  152. package/dist/output/generic-lens-corrections.d.ts +2 -6
  153. package/dist/output/generic-lens-corrections.js +3 -6
  154. package/dist/output/generic-lens-corrections.js.map +1 -1
  155. package/dist/output/geometric-transforms.js +1 -4
  156. package/dist/output/geometric-transforms.js.map +1 -1
  157. package/dist/output/native-capture-sdr.d.ts +2 -2
  158. package/dist/output/native-capture-sdr.js +3 -2
  159. package/dist/output/native-capture-sdr.js.map +1 -1
  160. package/dist/processing/development-detail.d.ts +29 -0
  161. package/dist/processing/development-detail.js +128 -107
  162. package/dist/processing/development-detail.js.map +1 -1
  163. package/dist/schema/illumination-temporal.js +3 -72
  164. package/dist/schema/illumination-temporal.js.map +1 -1
  165. package/dist/schema/illumination.d.ts +1 -0
  166. package/dist/schema/illumination.js +2 -65
  167. package/dist/schema/illumination.js.map +1 -1
  168. package/dist/schema/scene-radiance-temporal.js +2 -14
  169. package/dist/schema/scene-radiance-temporal.js.map +1 -1
  170. package/dist/schema/scene-radiance.js +2 -60
  171. package/dist/schema/scene-radiance.js.map +1 -1
  172. package/dist/schema/validation.d.ts +2 -0
  173. package/dist/schema/validation.js +2 -17
  174. package/dist/schema/validation.js.map +1 -1
  175. package/dist/sensor/accumulated-charge.js +2 -59
  176. package/dist/sensor/accumulated-charge.js.map +1 -1
  177. package/dist/sensor/architecture.js +1 -16
  178. package/dist/sensor/architecture.js.map +1 -1
  179. package/dist/sensor/camera-saturation-capacity.d.ts +1 -0
  180. package/dist/sensor/camera-saturation-capacity.js +3 -55
  181. package/dist/sensor/camera-saturation-capacity.js.map +1 -1
  182. package/dist/sensor/capture-color-sampling-binding.js +1 -26
  183. package/dist/sensor/capture-color-sampling-binding.js.map +1 -1
  184. package/dist/sensor/capture-mode-timing.js +1 -39
  185. package/dist/sensor/capture-mode-timing.js.map +1 -1
  186. package/dist/sensor/capture-mode.js +1 -23
  187. package/dist/sensor/capture-mode.js.map +1 -1
  188. package/dist/sensor/color-sampling.js +1 -26
  189. package/dist/sensor/color-sampling.js.map +1 -1
  190. package/dist/sensor/constant-rate-temporal-integration.js +2 -46
  191. package/dist/sensor/constant-rate-temporal-integration.js.map +1 -1
  192. package/dist/sensor/dark-current.js +2 -45
  193. package/dist/sensor/dark-current.js.map +1 -1
  194. package/dist/sensor/eqe-electron-rate.js +2 -35
  195. package/dist/sensor/eqe-electron-rate.js.map +1 -1
  196. package/dist/sensor/exposure-window.d.ts +5 -0
  197. package/dist/sensor/exposure-window.js +7 -22
  198. package/dist/sensor/exposure-window.js.map +1 -1
  199. package/dist/sensor/iso-gain.js +1 -7
  200. package/dist/sensor/iso-gain.js.map +1 -1
  201. package/dist/sensor/iso-signal-chain.js +1 -43
  202. package/dist/sensor/iso-signal-chain.js.map +1 -1
  203. package/dist/sensor/optical-stack.js +1 -25
  204. package/dist/sensor/optical-stack.js.map +1 -1
  205. package/dist/sensor/photon-energy.js +1 -26
  206. package/dist/sensor/photon-energy.js.map +1 -1
  207. package/dist/sensor/physical-charge-capacity.js +3 -77
  208. package/dist/sensor/physical-charge-capacity.js.map +1 -1
  209. package/dist/sensor/radiometry-readiness.js +2 -23
  210. package/dist/sensor/radiometry-readiness.js.map +1 -1
  211. package/dist/sensor/raw-readout.d.ts +3 -0
  212. package/dist/sensor/raw-readout.js +4 -66
  213. package/dist/sensor/raw-readout.js.map +1 -1
  214. package/dist/sensor/raw-reconstruction.js +2 -64
  215. package/dist/sensor/raw-reconstruction.js.map +1 -1
  216. package/dist/sensor/readout-exposure-linkage.js +1 -9
  217. package/dist/sensor/readout-exposure-linkage.js.map +1 -1
  218. package/dist/sensor/readout-timing.js +3 -57
  219. package/dist/sensor/readout-timing.js.map +1 -1
  220. package/dist/sensor/response-application-compatibility.d.ts +2 -0
  221. package/dist/sensor/response-application-compatibility.js +3 -29
  222. package/dist/sensor/response-application-compatibility.js.map +1 -1
  223. package/dist/sensor/response-operating-range.d.ts +1 -0
  224. package/dist/sensor/response-operating-range.js +3 -79
  225. package/dist/sensor/response-operating-range.js.map +1 -1
  226. package/dist/sensor/responsivity-photocurrent.js +4 -108
  227. package/dist/sensor/responsivity-photocurrent.js.map +1 -1
  228. package/dist/sensor/sampling-aperture.js +1 -25
  229. package/dist/sensor/sampling-aperture.js.map +1 -1
  230. package/dist/sensor/spatial-sample-reduction.js +2 -19
  231. package/dist/sensor/spatial-sample-reduction.js.map +1 -1
  232. package/dist/sensor/spatial-sampling-quadrature.js +1 -3
  233. package/dist/sensor/spatial-sampling-quadrature.js.map +1 -1
  234. package/dist/sensor/spatio-spectral-reduction.js +1 -18
  235. package/dist/sensor/spatio-spectral-reduction.js.map +1 -1
  236. package/dist/sensor/spectral-coverage.d.ts +10 -1
  237. package/dist/sensor/spectral-coverage.js +1 -1
  238. package/dist/sensor/spectral-coverage.js.map +1 -1
  239. package/dist/sensor/spectral-quadrature.js +2 -35
  240. package/dist/sensor/spectral-quadrature.js.map +1 -1
  241. package/dist/sensor/spectral-response.js +1 -47
  242. package/dist/sensor/spectral-response.js.map +1 -1
  243. package/dist/stabilization/camera-shake.js +1 -6
  244. package/dist/stabilization/camera-shake.js.map +1 -1
  245. package/dist/stabilization/system.js +2 -80
  246. package/dist/stabilization/system.js.map +1 -1
  247. package/docs/AGENT_GUIDE.md +1 -0
  248. package/docs/API_REFERENCE.md +41 -2
  249. package/docs/QUICK_START.md +90 -8
  250. package/docs/USAGE.md +47 -2
  251. package/docs/api/core-version.md +1 -1
  252. package/docs/api/exports.json +53 -3
  253. package/docs/api/exposure-filtered-exposure-time.md +105 -0
  254. package/docs/api/facade-develop-joined-parts.md +32 -0
  255. package/docs/api/facade-simulate-capture.md +36 -5
  256. package/docs/api/optics-generic-nd-filter-presets.md +34 -0
  257. package/docs/api/optics-lens-psf-profile.md +3 -1
  258. package/docs/api/output-developed-lens-corrections.md +12 -0
  259. package/docs/api/output-generic-lens-corrections.md +0 -9
  260. package/llms.txt +1 -0
  261. package/package.json +1 -1
@@ -0,0 +1,135 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * @internal `output.lensCorrections` (#570): a catalogue lens's generic correction profile (`createGenericLensCorrectionProfile`)
4
+ * applied to the developed linear RGB of `simulateCapture`, after demosaic, colour and white balance and before the tone curve.
5
+ * The RAW and the DNG are never corrected.
6
+ *
7
+ * For an output pixel at p (pixels from the frame centre, +y up) and channel c, the source point is read by inverse mapping,
8
+ * as the profile composes it (lateral colour, then distortion):
9
+ * q = m · a_c · p, s = q · (1 + k1·|q|²/R²),
10
+ * with a_c = 1 + L·D(λ_c) the channel's lateral-colour scale, k1 the lens's corner distortion, R the half-diagonal and m ≤ 1 the
11
+ * fill factor (the largest that keeps every output pixel centre's source inside the frame for every channel, so the output keeps
12
+ * its size with no missing pixels; the image is magnified by 1/m). The channel is sampled bilinearly at s.
13
+ *
14
+ * Vignetting is corrected to a flat field: the sample is multiplied by det J(ρ_i) / E(ρ_s), the inverse of the relative
15
+ * illumination the capture delivered at s: the lens's E(ρ) = (1 − bρ²)² at the sensor point and its distortion's area factor
16
+ * 1/det J, det J = (1 + k1ρ_i²)(1 + 3k1ρ_i²) at the ideal point ρ_i that the lens images to ρ_s (the capture's
17
+ * "inverse-distortion-jacobian"; a relative illumination measured on a flat field contains it too). The profile's vignetting
18
+ * component holds E only; without the area factor a PV 16 2.8 corner would come out about 18 % too bright.
19
+ * Every position is computed in whole-frame pixel coordinates, so a region renders the same values as the whole frame.
20
+ */
21
+ import { InvalidScientificResultError } from "../core/validation.js";
22
+ /**
23
+ * Effective wavelengths of the red, green and blue channels for the lateral-colour map, nm: a generic approximation of where
24
+ * a camera's channel responses peak, not a measured camera.
25
+ */
26
+ export const GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM = /* @__PURE__ */ Object.freeze({ red: 610, green: 540, blue: 465 });
27
+ /** @internal The lateral-colour scale of one channel, 1 + L·D(λ_channel) (`D` the catalogue's normalized dispersion). */
28
+ export function genericLensChannelScale(fraction, dispersion, channel) {
29
+ return 1 + fraction * dispersion(GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM[channel]);
30
+ }
31
+ /** Source position (pixel-centre coordinates of the whole frame, unclamped) of output pixel (x, y), channel c. */
32
+ function sourceOf(c, m, x, y, channel, out) {
33
+ const a = m * c.scales[channel], px = (x + 0.5 - c.width / 2) * a, py = (c.height / 2 - y - 0.5) * a;
34
+ const s = 1 + c.k1 * (px * px + py * py) / (c.radius * c.radius);
35
+ out[0] = px * s + c.width / 2 - 0.5;
36
+ out[1] = c.height / 2 - 0.5 - py * s;
37
+ }
38
+ /** Calls `visit` for every pixel centre on the border of `r`. */
39
+ function border(r, visit) {
40
+ for (let x = r.x; x < r.x + r.width; x++) {
41
+ visit(x, r.y);
42
+ visit(x, r.y + r.height - 1);
43
+ }
44
+ for (let y = r.y; y < r.y + r.height; y++) {
45
+ visit(r.x, y);
46
+ visit(r.x + r.width - 1, y);
47
+ }
48
+ }
49
+ /**
50
+ * @internal Prepares the correction of a W × H frame. `lens` is the resolved catalogue lens (`resolveGenericLens`) on the
51
+ * frame; `dispersion` is `genericLensDispersion`. The mapping's source radius grows with the output radius (the catalogue's
52
+ * distortion is monotonic over the frame), so the sources of the border pixel centres bound every source, and the fill factor
53
+ * is found by bisection on them.
54
+ */
55
+ export function prepareDevelopedLensCorrection(lens, selection, width, height, pitchMm, dispersion) {
56
+ const fraction = selection.lateralColor ? lens.character.lateralColor.fraction : 0;
57
+ const c = { width, height, radius: lens.halfDiagonalMm / pitchMm,
58
+ k1: selection.distortion ? lens.character.distortion.corner : 0, lensK1: lens.character.distortion.corner,
59
+ scales: [genericLensChannelScale(fraction, dispersion, "red"), genericLensChannelScale(fraction, dispersion, "green"), genericLensChannelScale(fraction, dispersion, "blue")], fill: 1 };
60
+ const p = new Float64Array(2), frame = { x: 0, y: 0, width, height };
61
+ const fits = (m) => {
62
+ let ok = true;
63
+ border(frame, (x, y) => {
64
+ for (let k = 0; k < 3 && ok; k++) {
65
+ sourceOf(c, m, x, y, k, p);
66
+ ok = p[0] >= 0 && p[0] <= width - 1 && p[1] >= 0 && p[1] <= height - 1;
67
+ }
68
+ });
69
+ return ok;
70
+ };
71
+ if (!fits(1)) {
72
+ let lo = 0.5, hi = 1;
73
+ for (let i = 0; i < 52; i++) {
74
+ const mid = (lo + hi) / 2;
75
+ if (fits(mid))
76
+ lo = mid;
77
+ else
78
+ hi = mid;
79
+ }
80
+ c.fill = lo;
81
+ }
82
+ return c;
83
+ }
84
+ /**
85
+ * @internal The developed pixels (a rectangle of the whole frame) that the corrected pixels of `region` read: the bounding box of
86
+ * the region border's sources over the three channels, with their bilinear neighbours, one pixel of margin, clipped to the frame.
87
+ */
88
+ export function developedLensCorrectionSourceRect(c, region) {
89
+ const p = new Float64Array(2);
90
+ let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;
91
+ border(region, (x, y) => {
92
+ for (let k = 0; k < 3; k++) {
93
+ sourceOf(c, c.fill, x, y, k, p);
94
+ x0 = Math.min(x0, p[0]);
95
+ x1 = Math.max(x1, p[0]);
96
+ y0 = Math.min(y0, p[1]);
97
+ y1 = Math.max(y1, p[1]);
98
+ }
99
+ });
100
+ const left = Math.max(0, Math.floor(x0) - 1), top = Math.max(0, Math.floor(y0) - 1);
101
+ return { x: left, y: top, width: Math.min(c.width - 1, Math.floor(x1) + 2) - left + 1, height: Math.min(c.height - 1, Math.floor(y1) + 2) - top + 1 };
102
+ }
103
+ /**
104
+ * @internal Corrects developed linear RGB (`rgb`, interleaved, the frame's pixels in `source`) into the pixels of `region`, both
105
+ * rectangles of the whole frame. `cornerStops` is the lens's vignetting at the capture's f-number, or null to leave it.
106
+ * Positions outside the frame (none after the fill, up to rounding) are clamped to the edge pixel centres.
107
+ */
108
+ export function applyDevelopedLensCorrection(c, rgb, source, region, cornerStops) {
109
+ const out = new Float64Array(region.width * region.height * 3), W = c.width, H = c.height, cx = W / 2 - 0.5, cy = H / 2 - 0.5, r2 = c.radius * c.radius;
110
+ const b = cornerStops === null ? 0 : 1 - 2 ** (-cornerStops / 2), k1 = c.k1, lensK1 = c.lensK1;
111
+ for (let y = 0, o = 0; y < region.height; y++)
112
+ for (let x = 0; x < region.width; x++)
113
+ for (let k = 0; k < 3; k++, o++) {
114
+ // sourceOf, inlined (a call per sample is about 5 times slower here): the same arithmetic, so the region bounds hold.
115
+ const a = c.fill * c.scales[k], px = (region.x + x + 0.5 - W / 2) * a, py = (H / 2 - (region.y + y) - 0.5) * a, f = 1 + k1 * (px * px + py * py) / r2;
116
+ const su = px * f + W / 2 - 0.5, sv = H / 2 - 0.5 - py * f;
117
+ const u = Math.min(W - 1, Math.max(0, su)), v = Math.min(H - 1, Math.max(0, sv));
118
+ const i = Math.floor(u), j = Math.floor(v), dx = u - i, dy = v - j, i1 = dx === 0 ? i : i + 1, j1 = dy === 0 ? j : j + 1;
119
+ if (i < source.x || j < source.y || i1 >= source.x + source.width || j1 >= source.y + source.height)
120
+ throw new InvalidScientificResultError("Lens correction read outside the developed rectangle.");
121
+ const row = (j - source.y) * source.width, row1 = (j1 - source.y) * source.width, a0 = i - source.x, a1 = i1 - source.x;
122
+ let value = (1 - dy) * ((1 - dx) * rgb[(row + a0) * 3 + k] + dx * rgb[(row + a1) * 3 + k]) + dy * ((1 - dx) * rgb[(row1 + a0) * 3 + k] + dx * rgb[(row1 + a1) * 3 + k]);
123
+ if (b !== 0) {
124
+ // ρ_s², then ρ_i from ρ_s = ρ_i(1 + k1ρ_i²) by Newton's method (monotonic over the frame; converged to rounding in 4 steps).
125
+ const s2 = ((su - cx) * (su - cx) + (sv - cy) * (sv - cy)) / r2, rs = Math.sqrt(s2), e = 1 - b * s2;
126
+ let ri = rs;
127
+ for (let n = 0; n < 4; n++)
128
+ ri -= (ri * (1 + lensK1 * ri * ri) - rs) / (1 + 3 * lensK1 * ri * ri);
129
+ value *= (1 + lensK1 * ri * ri) * (1 + 3 * lensK1 * ri * ri) / (e * e);
130
+ }
131
+ out[o] = value;
132
+ }
133
+ return out;
134
+ }
135
+ //# sourceMappingURL=developed-lens-corrections.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"developed-lens-corrections.js","sourceRoot":"","sources":["../../src/output/developed-lens-corrections.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * @internal `output.lensCorrections` (#570): a catalogue lens's generic correction profile (`createGenericLensCorrectionProfile`)\n * applied to the developed linear RGB of `simulateCapture`, after demosaic, colour and white balance and before the tone curve.\n * The RAW and the DNG are never corrected.\n *\n * For an output pixel at p (pixels from the frame centre, +y up) and channel c, the source point is read by inverse mapping,\n * as the profile composes it (lateral colour, then distortion):\n * q = m · a_c · p, s = q · (1 + k1·|q|²/R²),\n * with a_c = 1 + L·D(λ_c) the channel's lateral-colour scale, k1 the lens's corner distortion, R the half-diagonal and m ≤ 1 the\n * fill factor (the largest that keeps every output pixel centre's source inside the frame for every channel, so the output keeps\n * its size with no missing pixels; the image is magnified by 1/m). The channel is sampled bilinearly at s.\n *\n * Vignetting is corrected to a flat field: the sample is multiplied by det J(ρ_i) / E(ρ_s), the inverse of the relative\n * illumination the capture delivered at s: the lens's E(ρ) = (1 − bρ²)² at the sensor point and its distortion's area factor\n * 1/det J, det J = (1 + k1ρ_i²)(1 + 3k1ρ_i²) at the ideal point ρ_i that the lens images to ρ_s (the capture's\n * \"inverse-distortion-jacobian\"; a relative illumination measured on a flat field contains it too). The profile's vignetting\n * component holds E only; without the area factor a PV 16 2.8 corner would come out about 18 % too bright.\n * Every position is computed in whole-frame pixel coordinates, so a region renders the same values as the whole frame.\n */\n\nimport { InvalidScientificResultError } from \"../core/validation.js\";\nimport type { LensCorrectionChannel } from \"./lens-corrections.js\";\n\n/**\n * Effective wavelengths of the red, green and blue channels for the lateral-colour map, nm: a generic approximation of where\n * a camera's channel responses peak, not a measured camera.\n */\nexport const GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM: Readonly<Record<LensCorrectionChannel, number>> = /* @__PURE__ */ Object.freeze({ red: 610, green: 540, blue: 465 });\n\n/** @internal The lateral-colour scale of one channel, 1 + L·D(λ_channel) (`D` the catalogue's normalized dispersion). */\nexport function genericLensChannelScale(fraction: number, dispersion: (wavelengthNm: number) => number, channel: LensCorrectionChannel): number {\n return 1 + fraction * dispersion(GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM[channel]);\n}\n\n/** @internal Which corrections run. */\nexport interface DevelopedLensCorrectionSelection { distortion: boolean; lateralColor: boolean; vignetting: boolean }\n/** @internal A prepared correction for one frame (pixel units). */\nexport interface DevelopedLensCorrection {\n width: number; height: number;\n /** Half-diagonal R in pixels. */\n radius: number;\n /** k1 (0 when distortion is off), the lens's own k1, and the red, green and blue scales a_c (1 when lateral colour is off). */\n k1: number; lensK1: number; scales: readonly [number, number, number];\n /** Fill factor m ≤ 1 (1/m is the magnification). */\n fill: number;\n}\ntype Rect = { x: number; y: number; width: number; height: number };\n\n/** Source position (pixel-centre coordinates of the whole frame, unclamped) of output pixel (x, y), channel c. */\nfunction sourceOf(c: DevelopedLensCorrection, m: number, x: number, y: number, channel: number, out: Float64Array): void {\n const a = m * c.scales[channel]!, px = (x + 0.5 - c.width / 2) * a, py = (c.height / 2 - y - 0.5) * a;\n const s = 1 + c.k1 * (px * px + py * py) / (c.radius * c.radius);\n out[0] = px * s + c.width / 2 - 0.5; out[1] = c.height / 2 - 0.5 - py * s;\n}\n/** Calls `visit` for every pixel centre on the border of `r`. */\nfunction border(r: Rect, visit: (x: number, y: number) => void): void {\n for (let x = r.x; x < r.x + r.width; x++) { visit(x, r.y); visit(x, r.y + r.height - 1); }\n for (let y = r.y; y < r.y + r.height; y++) { visit(r.x, y); visit(r.x + r.width - 1, y); }\n}\n\n/**\n * @internal Prepares the correction of a W × H frame. `lens` is the resolved catalogue lens (`resolveGenericLens`) on the\n * frame; `dispersion` is `genericLensDispersion`. The mapping's source radius grows with the output radius (the catalogue's\n * distortion is monotonic over the frame), so the sources of the border pixel centres bound every source, and the fill factor\n * is found by bisection on them.\n */\nexport function prepareDevelopedLensCorrection(lens: { halfDiagonalMm: number; character: { distortion: { corner: number }; lateralColor: { fraction: number } } },\n selection: DevelopedLensCorrectionSelection, width: number, height: number, pitchMm: number, dispersion: (wavelengthNm: number) => number): DevelopedLensCorrection {\n const fraction = selection.lateralColor ? lens.character.lateralColor.fraction : 0;\n const c: DevelopedLensCorrection = { width, height, radius: lens.halfDiagonalMm / pitchMm,\n k1: selection.distortion ? lens.character.distortion.corner : 0, lensK1: lens.character.distortion.corner,\n scales: [genericLensChannelScale(fraction, dispersion, \"red\"), genericLensChannelScale(fraction, dispersion, \"green\"), genericLensChannelScale(fraction, dispersion, \"blue\")], fill: 1 };\n const p = new Float64Array(2), frame = { x: 0, y: 0, width, height };\n const fits = (m: number): boolean => {\n let ok = true;\n border(frame, (x, y) => {\n for (let k = 0; k < 3 && ok; k++) { sourceOf(c, m, x, y, k, p); ok = p[0]! >= 0 && p[0]! <= width - 1 && p[1]! >= 0 && p[1]! <= height - 1; }\n });\n return ok;\n };\n if (!fits(1)) {\n let lo = 0.5, hi = 1;\n for (let i = 0; i < 52; i++) { const mid = (lo + hi) / 2; if (fits(mid)) lo = mid; else hi = mid; }\n c.fill = lo;\n }\n return c;\n}\n\n/**\n * @internal The developed pixels (a rectangle of the whole frame) that the corrected pixels of `region` read: the bounding box of\n * the region border's sources over the three channels, with their bilinear neighbours, one pixel of margin, clipped to the frame.\n */\nexport function developedLensCorrectionSourceRect(c: DevelopedLensCorrection, region: Rect): Rect {\n const p = new Float64Array(2);\n let x0 = Infinity, y0 = Infinity, x1 = -Infinity, y1 = -Infinity;\n border(region, (x, y) => {\n for (let k = 0; k < 3; k++) {\n sourceOf(c, c.fill, x, y, k, p);\n x0 = Math.min(x0, p[0]!); x1 = Math.max(x1, p[0]!); y0 = Math.min(y0, p[1]!); y1 = Math.max(y1, p[1]!);\n }\n });\n const left = Math.max(0, Math.floor(x0) - 1), top = Math.max(0, Math.floor(y0) - 1);\n return { x: left, y: top, width: Math.min(c.width - 1, Math.floor(x1) + 2) - left + 1, height: Math.min(c.height - 1, Math.floor(y1) + 2) - top + 1 };\n}\n\n/**\n * @internal Corrects developed linear RGB (`rgb`, interleaved, the frame's pixels in `source`) into the pixels of `region`, both\n * rectangles of the whole frame. `cornerStops` is the lens's vignetting at the capture's f-number, or null to leave it.\n * Positions outside the frame (none after the fill, up to rounding) are clamped to the edge pixel centres.\n */\nexport function applyDevelopedLensCorrection(c: DevelopedLensCorrection, rgb: Float64Array, source: Rect, region: Rect, cornerStops: number | null): Float64Array {\n const out = new Float64Array(region.width * region.height * 3), W = c.width, H = c.height, cx = W / 2 - 0.5, cy = H / 2 - 0.5, r2 = c.radius * c.radius;\n const b = cornerStops === null ? 0 : 1 - 2 ** (-cornerStops / 2), k1 = c.k1, lensK1 = c.lensK1;\n for (let y = 0, o = 0; y < region.height; y++) for (let x = 0; x < region.width; x++) for (let k = 0; k < 3; k++, o++) {\n // sourceOf, inlined (a call per sample is about 5 times slower here): the same arithmetic, so the region bounds hold.\n const a = c.fill * c.scales[k]!, px = (region.x + x + 0.5 - W / 2) * a, py = (H / 2 - (region.y + y) - 0.5) * a, f = 1 + k1 * (px * px + py * py) / r2;\n const su = px * f + W / 2 - 0.5, sv = H / 2 - 0.5 - py * f;\n const u = Math.min(W - 1, Math.max(0, su)), v = Math.min(H - 1, Math.max(0, sv));\n const i = Math.floor(u), j = Math.floor(v), dx = u - i, dy = v - j, i1 = dx === 0 ? i : i + 1, j1 = dy === 0 ? j : j + 1;\n if (i < source.x || j < source.y || i1 >= source.x + source.width || j1 >= source.y + source.height)\n throw new InvalidScientificResultError(\"Lens correction read outside the developed rectangle.\");\n const row = (j - source.y) * source.width, row1 = (j1 - source.y) * source.width, a0 = i - source.x, a1 = i1 - source.x;\n let value = (1 - dy) * ((1 - dx) * rgb[(row + a0) * 3 + k]! + dx * rgb[(row + a1) * 3 + k]!) + dy * ((1 - dx) * rgb[(row1 + a0) * 3 + k]! + dx * rgb[(row1 + a1) * 3 + k]!);\n if (b !== 0) {\n // ρ_s², then ρ_i from ρ_s = ρ_i(1 + k1ρ_i²) by Newton's method (monotonic over the frame; converged to rounding in 4 steps).\n const s2 = ((su - cx) * (su - cx) + (sv - cy) * (sv - cy)) / r2, rs = Math.sqrt(s2), e = 1 - b * s2;\n let ri = rs;\n for (let n = 0; n < 4; n++) ri -= (ri * (1 + lensK1 * ri * ri) - rs) / (1 + 3 * lensK1 * ri * ri);\n value *= (1 + lensK1 * ri * ri) * (1 + 3 * lensK1 * ri * ri) / (e * e);\n }\n out[o] = value;\n }\n return out;\n}\n"]}
@@ -1,10 +1,6 @@
1
1
  import { type OpticalProfileState } from "../optics/profile-contract.js";
2
- import { type GenericLensCorrectionProfile, type LensCorrectionChannel } from "./lens-corrections.js";
3
- /**
4
- * Effective wavelengths of the red, green and blue channels for the lateral-colour map, nm: a generic approximation of where
5
- * a camera's channel responses peak, not a measured camera.
6
- */
7
- export declare const GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM: Readonly<Record<LensCorrectionChannel, number>>;
2
+ import { type GenericLensCorrectionProfile } from "./lens-corrections.js";
3
+ export { GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM } from "./developed-lens-corrections.js";
8
4
  export interface CreateGenericLensCorrectionProfileInput {
9
5
  /** A catalogue id (`GENERIC_LENS_IDS`); `state.lensId` must be the same id. */
10
6
  lensId: string;
@@ -11,11 +11,8 @@ import { requireInputKeys, requireInputObject } from "../core/record-validation.
11
11
  import { parseOpticalProfileState } from "../optics/profile-contract.js";
12
12
  import { GENERIC_LENS_EVIDENCE, genericLensDispersion, resolveGenericLens } from "../equipment/generic-lens-catalogue.js";
13
13
  import { parseGenericLensCorrectionProfile } from "./lens-corrections.js";
14
- /**
15
- * Effective wavelengths of the red, green and blue channels for the lateral-colour map, nm: a generic approximation of where
16
- * a camera's channel responses peak, not a measured camera.
17
- */
18
- export const GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM = /* @__PURE__ */ Object.freeze({ red: 610, green: 540, blue: 465 });
14
+ import { genericLensChannelScale } from "./developed-lens-corrections.js";
15
+ export { GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM } from "./developed-lens-corrections.js";
19
16
  /**
20
17
  * Builds the catalogue lens's generic correction profile for one camera state: a radial distortion map (the lens's own
21
18
  * r_d = r(1 + k1ρ²), read at each ideal destination point), per-channel lateral-colour scales 1 + L·D(λ_channel), and the
@@ -32,7 +29,7 @@ export function createGenericLensCorrectionProfile(input) {
32
29
  throw new InvalidScientificInputError("state.focalLengthMm must be the " + lens.name + "'s focal length on this sensor, " + lens.focalLengthMm + " mm.");
33
30
  const c = lens.character, domain = "reconstructed-linear";
34
31
  const identity = { version: "1.0.0", domain, availability: "toggle", defaultEnabled: true, dependencies: [], requiredByStabilizationModes: [] };
35
- const scale = (channel) => 1 + c.lateralColor.fraction * genericLensDispersion(GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM[channel]);
32
+ const scale = (channel) => genericLensChannelScale(c.lateralColor.fraction, genericLensDispersion, channel);
36
33
  const affine = (channel) => ({ id: lens.lensId + ":lateral-ca:" + channel, version: "1.0.0", domain, purpose: "lateral-ca", kind: "affine",
37
34
  matrix: [scale(channel), 0, 0, scale(channel)], offsetMm: { x: 0, y: 0 } });
38
35
  return parseGenericLensCorrectionProfile({ schemaVersion: "0.1.0", id: "photivra-" + lens.lensId + ":corrections", version: "1.0.0", state,
@@ -1 +1 @@
1
- {"version":3,"file":"generic-lens-corrections.js","sourceRoot":"","sources":["../../src/output/generic-lens-corrections.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * The generic correction profile of a catalogue lens (#549), for the post-capture lens corrections\n * (`resolveLensCorrectionPlan`, `calculateLensCorrectedCapture`): a distortion map, a lateral-colour map per channel and a\n * peripheral-illumination gain, each from the lens's own character (`resolveGenericLens`). Consumer lenses rely on it more:\n * their uncorrected distortion and vignetting are larger, so the same profile corrects more. Like every catalogue number it is\n * a labelled approximation, bound to one exact camera state (`OpticalProfileState`).\n */\n\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport { requireInputKeys, requireInputObject } from \"../core/record-validation.js\";\nimport { parseOpticalProfileState, type OpticalProfileState } from \"../optics/profile-contract.js\";\nimport { GENERIC_LENS_EVIDENCE, genericLensDispersion, resolveGenericLens } from \"../equipment/generic-lens-catalogue.js\";\nimport { parseGenericLensCorrectionProfile, type GenericLensCorrectionProfile, type LensCorrectionChannel } from \"./lens-corrections.js\";\nimport type { AffineGeometricTransform } from \"./geometric-transforms.js\";\n\n/**\n * Effective wavelengths of the red, green and blue channels for the lateral-colour map, nm: a generic approximation of where\n * a camera's channel responses peak, not a measured camera.\n */\nexport const GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM: Readonly<Record<LensCorrectionChannel, number>> = /* @__PURE__ */ Object.freeze({ red: 610, green: 540, blue: 465 });\n\nexport interface CreateGenericLensCorrectionProfileInput {\n /** A catalogue id (`GENERIC_LENS_IDS`); `state.lensId` must be the same id. */\n lensId: string;\n /** The sensor's active capture area, mm (the lens is scaled to it as in `resolveGenericLens`). */\n activeImagingArea: { widthMm: number; heightMm: number };\n /** The exact camera state the profile applies to: its focal length must be the lens's on this sensor, its aperture in range. */\n state: OpticalProfileState;\n}\n\n/**\n * Builds the catalogue lens's generic correction profile for one camera state: a radial distortion map (the lens's own\n * r_d = r(1 + k1ρ²), read at each ideal destination point), per-channel lateral-colour scales 1 + L·D(λ_channel), and the\n * relative-illumination gain at the state's aperture (strength 1). The result passes `parseGenericLensCorrectionProfile`.\n */\nexport function createGenericLensCorrectionProfile(input: CreateGenericLensCorrectionProfileInput): GenericLensCorrectionProfile {\n requireInputObject(input, \"createGenericLensCorrectionProfile input\");\n requireInputKeys(input, \"createGenericLensCorrectionProfile\", [\"lensId\", \"activeImagingArea\", \"state\"]);\n const state = parseOpticalProfileState(input.state);\n const lens = resolveGenericLens({ lensId: input.lensId, activeImagingArea: input.activeImagingArea, fNumber: state.aperture });\n if (state.lensId !== lens.lensId) throw new InvalidScientificInputError(\"state.lensId must be the catalogue id \" + lens.lensId + \".\");\n if (Math.abs(state.focalLengthMm - lens.focalLengthMm) > 1e-9 * lens.focalLengthMm)\n throw new InvalidScientificInputError(\"state.focalLengthMm must be the \" + lens.name + \"'s focal length on this sensor, \" + lens.focalLengthMm + \" mm.\");\n const c = lens.character, domain = \"reconstructed-linear\" as const;\n const identity = { version: \"1.0.0\", domain, availability: \"toggle\" as const, defaultEnabled: true, dependencies: [] as string[], requiredByStabilizationModes: [] as string[] };\n const scale = (channel: LensCorrectionChannel): number => 1 + c.lateralColor.fraction * genericLensDispersion(GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM[channel]);\n const affine = (channel: LensCorrectionChannel): AffineGeometricTransform => ({ id: lens.lensId + \":lateral-ca:\" + channel, version: \"1.0.0\", domain, purpose: \"lateral-ca\" as const, kind: \"affine\" as const,\n matrix: [scale(channel), 0, 0, scale(channel)] as [number, number, number, number], offsetMm: { x: 0, y: 0 } });\n return parseGenericLensCorrectionProfile({ schemaVersion: \"0.1.0\", id: \"photivra-\" + lens.lensId + \":corrections\", version: \"1.0.0\", state,\n evidence: { kind: \"generic-parametric\", basis: \"Generic catalogue lens \" + lens.name + \" (#549): the inverse of its own distortion, lateral colour and vignetting.\",\n residualNote: \"Generic approximation, not a measured lens; axial colour, coma and the other aberrations stay.\", sources: [...GENERIC_LENS_EVIDENCE] },\n components: [\n { ...identity, id: \"distortion\", kind: \"geometry\", residualNote: \"Corrects the lens's radial distortion r_d = r(1 + k1ρ²); the stretched corners lose resolution.\",\n transform: { id: lens.lensId + \":distortion\", version: \"1.0.0\", domain, purpose: \"distortion\", kind: \"radial\",\n profile: { normalizationRadiusMm: lens.halfDiagonalMm, maximumNormalizedRadius: 1.05, coefficients: { k1: c.distortion.corner, k2: 0, k3: 0 } } } },\n { ...identity, id: \"lateral-ca\", kind: \"lateral-ca\", residualNote: \"Registers the channels at one effective wavelength each; colour inside a channel's band and axial colour stay.\",\n transforms: { red: affine(\"red\"), green: affine(\"green\"), blue: affine(\"blue\") } },\n { ...identity, id: \"vignetting\", kind: \"peripheral-illumination\", residualNote: \"Gain 1/E at the profile's aperture; it raises corner noise with the signal.\",\n profile: { ...c.vignetting.profile, maximumNormalizedRadius: 1.05 }, strength: 1 }\n ] });\n}\n"]}
1
+ {"version":3,"file":"generic-lens-corrections.js","sourceRoot":"","sources":["../../src/output/generic-lens-corrections.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * The generic correction profile of a catalogue lens (#549), for the post-capture lens corrections\n * (`resolveLensCorrectionPlan`, `calculateLensCorrectedCapture`): a distortion map, a lateral-colour map per channel and a\n * peripheral-illumination gain, each from the lens's own character (`resolveGenericLens`). Consumer lenses rely on it more:\n * their uncorrected distortion and vignetting are larger, so the same profile corrects more. Like every catalogue number it is\n * a labelled approximation, bound to one exact camera state (`OpticalProfileState`).\n */\n\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport { requireInputKeys, requireInputObject } from \"../core/record-validation.js\";\nimport { parseOpticalProfileState, type OpticalProfileState } from \"../optics/profile-contract.js\";\nimport { GENERIC_LENS_EVIDENCE, genericLensDispersion, resolveGenericLens } from \"../equipment/generic-lens-catalogue.js\";\nimport { parseGenericLensCorrectionProfile, type GenericLensCorrectionProfile, type LensCorrectionChannel } from \"./lens-corrections.js\";\nimport type { AffineGeometricTransform } from \"./geometric-transforms.js\";\nimport { genericLensChannelScale } from \"./developed-lens-corrections.js\";\n\nexport { GENERIC_LENS_CORRECTION_CHANNEL_WAVELENGTHS_NM } from \"./developed-lens-corrections.js\";\n\nexport interface CreateGenericLensCorrectionProfileInput {\n /** A catalogue id (`GENERIC_LENS_IDS`); `state.lensId` must be the same id. */\n lensId: string;\n /** The sensor's active capture area, mm (the lens is scaled to it as in `resolveGenericLens`). */\n activeImagingArea: { widthMm: number; heightMm: number };\n /** The exact camera state the profile applies to: its focal length must be the lens's on this sensor, its aperture in range. */\n state: OpticalProfileState;\n}\n\n/**\n * Builds the catalogue lens's generic correction profile for one camera state: a radial distortion map (the lens's own\n * r_d = r(1 + k1ρ²), read at each ideal destination point), per-channel lateral-colour scales 1 + L·D(λ_channel), and the\n * relative-illumination gain at the state's aperture (strength 1). The result passes `parseGenericLensCorrectionProfile`.\n */\nexport function createGenericLensCorrectionProfile(input: CreateGenericLensCorrectionProfileInput): GenericLensCorrectionProfile {\n requireInputObject(input, \"createGenericLensCorrectionProfile input\");\n requireInputKeys(input, \"createGenericLensCorrectionProfile\", [\"lensId\", \"activeImagingArea\", \"state\"]);\n const state = parseOpticalProfileState(input.state);\n const lens = resolveGenericLens({ lensId: input.lensId, activeImagingArea: input.activeImagingArea, fNumber: state.aperture });\n if (state.lensId !== lens.lensId) throw new InvalidScientificInputError(\"state.lensId must be the catalogue id \" + lens.lensId + \".\");\n if (Math.abs(state.focalLengthMm - lens.focalLengthMm) > 1e-9 * lens.focalLengthMm)\n throw new InvalidScientificInputError(\"state.focalLengthMm must be the \" + lens.name + \"'s focal length on this sensor, \" + lens.focalLengthMm + \" mm.\");\n const c = lens.character, domain = \"reconstructed-linear\" as const;\n const identity = { version: \"1.0.0\", domain, availability: \"toggle\" as const, defaultEnabled: true, dependencies: [] as string[], requiredByStabilizationModes: [] as string[] };\n const scale = (channel: LensCorrectionChannel): number => genericLensChannelScale(c.lateralColor.fraction, genericLensDispersion, channel);\n const affine = (channel: LensCorrectionChannel): AffineGeometricTransform => ({ id: lens.lensId + \":lateral-ca:\" + channel, version: \"1.0.0\", domain, purpose: \"lateral-ca\" as const, kind: \"affine\" as const,\n matrix: [scale(channel), 0, 0, scale(channel)] as [number, number, number, number], offsetMm: { x: 0, y: 0 } });\n return parseGenericLensCorrectionProfile({ schemaVersion: \"0.1.0\", id: \"photivra-\" + lens.lensId + \":corrections\", version: \"1.0.0\", state,\n evidence: { kind: \"generic-parametric\", basis: \"Generic catalogue lens \" + lens.name + \" (#549): the inverse of its own distortion, lateral colour and vignetting.\",\n residualNote: \"Generic approximation, not a measured lens; axial colour, coma and the other aberrations stay.\", sources: [...GENERIC_LENS_EVIDENCE] },\n components: [\n { ...identity, id: \"distortion\", kind: \"geometry\", residualNote: \"Corrects the lens's radial distortion r_d = r(1 + k1ρ²); the stretched corners lose resolution.\",\n transform: { id: lens.lensId + \":distortion\", version: \"1.0.0\", domain, purpose: \"distortion\", kind: \"radial\",\n profile: { normalizationRadiusMm: lens.halfDiagonalMm, maximumNormalizedRadius: 1.05, coefficients: { k1: c.distortion.corner, k2: 0, k3: 0 } } } },\n { ...identity, id: \"lateral-ca\", kind: \"lateral-ca\", residualNote: \"Registers the channels at one effective wavelength each; colour inside a channel's band and axial colour stay.\",\n transforms: { red: affine(\"red\"), green: affine(\"green\"), blue: affine(\"blue\") } },\n { ...identity, id: \"vignetting\", kind: \"peripheral-illumination\", residualNote: \"Gain 1/E at the profile's aperture; it raises corner noise with the signal.\",\n profile: { ...c.vignetting.profile, maximumNormalizedRadius: 1.05 }, strength: 1 }\n ] });\n}\n"]}
@@ -23,10 +23,7 @@ import { choice, finite, list, positive, record, textValue } from "../optics/pro
23
23
  import { requireInputObject, requireInputKeys } from "../core/record-validation.js";
24
24
  import { brownConradyForwardValue, brownConradyInverseValue, validateBrownConradyDistortionProfile } from "../optics/brown-conrady-distortion.js";
25
25
  import { lensProjectionRemapValue, lensProjectionSignedAxisAngle, parseLensProjectionModel } from "../camera/lens-projection.js";
26
- function point(value) {
27
- const p = record(value, ["x", "y"]);
28
- return { x: finite(p.x, "x"), y: finite(p.y, "y") };
29
- }
26
+ import { point } from "../optics/stray-light.js";
30
27
  function multiply(a, b) {
31
28
  return [a[0] * b[0] + a[1] * b[2], a[0] * b[1] + a[1] * b[3],
32
29
  a[2] * b[0] + a[3] * b[2], a[2] * b[1] + a[3] * b[3]];
@@ -1 +1 @@
1
- {"version":3,"file":"geometric-transforms.js","sourceRoot":"","sources":["../../src/output/geometric-transforms.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Geometric image transforms (warps) such as distortion correction and projection changes, and\n * a resampler that applies them.\n *\n * A mapping goes from destination (output) points to source (captured) points. Preparing a\n * mapping fixes it for one still or frame; a change in time needs a new preparation.\n *\n * Transform kinds:\n * - `affine` and `radial` (schema 0.1.0, unchanged).\n * - From mapping schema 0.2.0: `brown-conrady` (radial plus tangential lens distortion) and\n * `projection-remap` (a change of ideal projection, for example fisheye capture to a\n * rectilinear or equirectangular view).\n *\n * A mapping that uses only 0.1.0 kinds keeps schema and model version 0.1.0 and gives identical output.\n * @see docs/DIGITAL_OPTICS_FOUNDATION.md for equations, coordinate/unit conventions, blockers and\n * support limits.\n */\n\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n calculateRadialDistortionMapping, calculateInverseRadialDistortionMapping,\n validateRadialDistortionProfile, type RadialDistortionProfile, type LensFieldPointMm\n} from \"../optics/radial-distortion.js\";\nimport { choice, finite, list, positive, record, textValue } from \"../optics/profile-contract.js\";\nimport { requireInputObject, requireInputKeys } from \"../core/record-validation.js\";\nimport {\n brownConradyForwardValue, brownConradyInverseValue, validateBrownConradyDistortionProfile,\n type BrownConradyDistortionProfile, type ValidatedBrownConradyDistortionProfile\n} from \"../optics/brown-conrady-distortion.js\";\nimport {\n lensProjectionRemapValue, lensProjectionSignedAxisAngle, parseLensProjectionModel,\n type LensProjectionModel\n} from \"../camera/lens-projection.js\";\n\n/** A domain change separates maps: only adjacent maps in the same domain share one resampling pass. */\nexport type GeometricImageDomain = \"raw-channel\" | \"reconstructed-linear\" | \"processed-output\";\n/** Jacobian (derivative) d(source mm)/d(destination mm), in row-major order. */\nexport type GeometricJacobian = readonly [number, number, number, number];\ninterface TransformIdentity {\n id: string;\n version: string;\n domain: GeometricImageDomain;\n purpose: \"distortion\" | \"breathing\" | \"digital-stabilization\" | \"orientation-crop\" | \"lateral-ca\" | \"projection\";\n}\n/** Explicit destination-to-source affine map, which can include rotation, shear and offset. */\nexport interface AffineGeometricTransform extends TransformIdentity {\n kind: \"affine\";\n matrix: GeometricJacobian;\n offsetMm: LensFieldPointMm;\n}\n/** Radial distortion. A correction reads the physically distorted data at the position mapped from each ideal (undistorted) destination point. */\nexport interface RadialGeometricTransform extends TransformIdentity {\n kind: \"radial\";\n profile: RadialDistortionProfile;\n}\n/** Schema 0.2.0: Brown-Conrady distortion. A correction reads the distorted data at the position mapped from each ideal destination point. */\nexport interface BrownConradyGeometricTransform extends TransformIdentity {\n kind: \"brown-conrady\";\n profile: BrownConradyDistortionProfile;\n}\n/** One ideal projection and its mapping scale (focal length, or mm per radian for equirectangular). */\nexport interface GeometricProjectionDeclaration {\n projectionModel: LensProjectionModel;\n focalLengthMm: number;\n}\n/**\n * Schema 0.2.0: change of projection. Each destination point becomes a scene direction (using\n * the destination projection), then a source point (using the source projection). Purpose must\n * be \"projection\".\n */\nexport interface ProjectionRemapGeometricTransform extends TransformIdentity {\n kind: \"projection-remap\";\n destinationProjection: GeometricProjectionDeclaration;\n sourceProjection: GeometricProjectionDeclaration;\n}\nexport type DigitalGeometricTransform =\n | AffineGeometricTransform\n | RadialGeometricTransform\n | BrownConradyGeometricTransform\n | ProjectionRemapGeometricTransform;\n/** Serializable prepared mapping. Maps are applied in the listed order, from destination towards source. */\nexport interface PreparedGeometricMapping {\n /** 0.1.0 when only affine and radial kinds are used; 0.2.0 when a kind added in 2.0 is present. */\n schemaVersion: \"0.1.0\" | \"0.2.0\";\n frameTimeSeconds: number;\n transforms: readonly DigitalGeometricTransform[];\n semanticKey: string;\n groups: readonly { domain: GeometricImageDomain; componentIds: readonly string[] }[];\n}\n/** A regular grid of pixel centres on the optical plane: +X right, +Y up; rows run top to bottom. */\nexport interface GeometricRaster {\n width: number;\n height: number;\n centerMm: LensFieldPointMm;\n pitchMm: number;\n}\n/** The resampling filter, chosen independently of the mapping. Any prefiltering (anti-aliasing) is the caller's job. */\nexport interface GeometricResampler {\n id: string;\n version: string;\n filter: \"nearest\" | \"bilinear\";\n antialias: \"none\" | \"source-prefiltered\";\n}\nexport interface GeometricMappingPoint {\n sourcePointMm: LensFieldPointMm;\n jacobian: GeometricJacobian;\n determinant: number;\n principalStretches: readonly [number, number];\n anisotropy: number;\n components: readonly { id: string; sourcePointMm: LensFieldPointMm; jacobian: GeometricJacobian }[];\n}\nexport interface GeometricSamplingPlan {\n mapping: PreparedGeometricMapping;\n sourceRaster: GeometricRaster;\n destinationRaster: GeometricRaster;\n resampler: GeometricResampler;\n points: readonly GeometricMappingPoint[];\n /** True where the selected filter has full source support around the pixel centre. Edges are not extended. */\n validSourceMask: readonly boolean[];\n /** Largest axis-aligned rectangle of valid pixels (half-open). Null if there is none. */\n jointCrop: { x: number; y: number; width: number; height: number } | null;\n requiresPrefilter: boolean;\n canResample: boolean;\n physicalCapturedFovDegrees: { horizontal: number; vertical: number };\n /** Angular extent of the mapped pixel-centre rays kept by the joint crop. It is not the edge-to-edge FOV. */\n retainedSampleRayEnvelopeDegrees: { minX: number; maxX: number; minY: number; maxY: number } | null;\n}\n\nfunction point(value: unknown): LensFieldPointMm {\n const p = record(value, [\"x\", \"y\"]);\n return { x: finite(p.x, \"x\"), y: finite(p.y, \"y\") };\n}\nfunction multiply(a: GeometricJacobian, b: GeometricJacobian): GeometricJacobian {\n return [a[0]*b[0]+a[1]*b[2], a[0]*b[1]+a[1]*b[3],\n a[2]*b[0]+a[3]*b[2], a[2]*b[1]+a[3]*b[3]];\n}\nfunction determinant(m: GeometricJacobian): number { return m[0]*m[3]-m[1]*m[2]; }\nfunction inverse(m: GeometricJacobian): GeometricJacobian {\n const d = determinant(m);\n if (!Number.isFinite(d) || Math.abs(d) < 1e-12) throw new InvalidScientificInputError(\"Singular or ill-conditioned affine map.\");\n return [m[3]/d, -m[1]/d, -m[2]/d, m[0]/d];\n}\nfunction affine(p: LensFieldPointMm, m: GeometricJacobian, o: LensFieldPointMm): LensFieldPointMm {\n return { x: m[0]*p.x+m[1]*p.y+o.x, y: m[2]*p.x+m[3]*p.y+o.y };\n}\n/** Strictly parses transform data. Unknown kinds or fields and profiles that cannot be inverted are rejected. */\nexport function parseDigitalGeometricTransform(value: unknown): DigitalGeometricTransform {\n const r = record(value, [\"id\", \"version\", \"domain\", \"purpose\", \"kind\", \"matrix\", \"offsetMm\", \"profile\",\n \"destinationProjection\", \"sourceProjection\"]);\n const identity = {\n id: textValue(r.id), version: textValue(r.version),\n domain: choice(r.domain, [\"raw-channel\", \"reconstructed-linear\", \"processed-output\"]),\n purpose: choice(r.purpose, [\"distortion\", \"breathing\", \"digital-stabilization\", \"orientation-crop\", \"lateral-ca\", \"projection\"])\n };\n const kind = choice(r.kind, [\"affine\", \"radial\", \"brown-conrady\", \"projection-remap\"]);\n if ((kind === \"projection-remap\") !== (identity.purpose === \"projection\")) {\n throw new InvalidScientificInputError(\"Purpose \\\"projection\\\" is used exactly by projection-remap transforms.\");\n }\n if (kind !== \"projection-remap\" && (r.destinationProjection !== undefined || r.sourceProjection !== undefined)) {\n throw new InvalidScientificInputError(\"Only projection-remap transforms carry projection declarations.\");\n }\n if (kind === \"projection-remap\") {\n if (r.matrix !== undefined || r.offsetMm !== undefined || r.profile !== undefined) {\n throw new InvalidScientificInputError(\"Projection remap cannot contain affine or distortion fields.\");\n }\n const projection = (v: unknown): GeometricProjectionDeclaration => {\n const d = record(v, [\"projectionModel\", \"focalLengthMm\"]);\n return { projectionModel: parseLensProjectionModel(d.projectionModel), focalLengthMm: positive(d.focalLengthMm, \"focalLengthMm\") };\n };\n return { ...identity, kind, destinationProjection: projection(r.destinationProjection), sourceProjection: projection(r.sourceProjection) };\n }\n if (kind === \"brown-conrady\") {\n if (r.matrix !== undefined || r.offsetMm !== undefined) throw new InvalidScientificInputError(\"Brown-Conrady map cannot contain affine fields.\");\n const p = record(r.profile, [\"normalizationRadiusMm\", \"maximumNormalizedRadius\", \"coefficients\"]);\n const c = record(p.coefficients, [\"k1\", \"k2\", \"k3\", \"p1\", \"p2\"]);\n const profile = { normalizationRadiusMm: positive(p.normalizationRadiusMm, \"normalizationRadiusMm\"),\n maximumNormalizedRadius: positive(p.maximumNormalizedRadius, \"maximumNormalizedRadius\"),\n coefficients: { k1: finite(c.k1, \"k1\"), k2: finite(c.k2, \"k2\"), k3: finite(c.k3, \"k3\"), p1: finite(c.p1, \"p1\"), p2: finite(c.p2, \"p2\") } };\n validateBrownConradyDistortionProfile(profile);\n return { ...identity, kind, profile };\n }\n if (kind === \"affine\") {\n if (r.profile !== undefined) throw new InvalidScientificInputError(\"Affine map cannot contain radial profile.\");\n const entries = list(r.matrix, 4).map((v) => finite(v, \"matrix\"));\n if (entries.length !== 4) throw new InvalidScientificInputError(\"Matrix must have four entries.\");\n const matrix = entries as [number, number, number, number];\n inverse(matrix);\n return { ...identity, kind, matrix, offsetMm: point(r.offsetMm) };\n }\n if (r.matrix !== undefined || r.offsetMm !== undefined) throw new InvalidScientificInputError(\"Radial map cannot contain affine fields.\");\n const p = record(r.profile, [\"normalizationRadiusMm\", \"maximumNormalizedRadius\", \"coefficients\"]);\n const c = record(p.coefficients, [\"k1\", \"k2\", \"k3\"]);\n const profile = { normalizationRadiusMm: positive(p.normalizationRadiusMm, \"normalizationRadiusMm\"),\n maximumNormalizedRadius: positive(p.maximumNormalizedRadius, \"maximumNormalizedRadius\"),\n coefficients: { k1: finite(c.k1, \"k1\"), k2: finite(c.k2, \"k2\"), k3: finite(c.k3, \"k3\") } };\n validateRadialDistortionProfile(profile);\n return { ...identity, kind, profile };\n}\n\n/** Prepares a deterministic mapping for one still or frame. A change in time needs a new preparation. */\nexport function prepareGeometricMapping(input: {\n transforms: readonly DigitalGeometricTransform[]; frameTimeSeconds: number;\n}): CalculationResult<PreparedGeometricMapping> {\n requireInputKeys(input, \"prepareGeometricMapping\", [\"transforms\", \"frameTimeSeconds\"]);\n const time = finite(input.frameTimeSeconds, \"frameTimeSeconds\");\n if (time < 0) throw new InvalidScientificInputError(\"Time is seconds from exposure start.\");\n const transforms = list(input.transforms, 32).map(parseDigitalGeometricTransform);\n if (new Set(transforms.map((t) => t.id)).size !== transforms.length) throw new InvalidScientificInputError(\"Duplicate transform IDs.\");\n const groups: { domain: GeometricImageDomain; componentIds: string[] }[] = [];\n for (const t of transforms) {\n const last = groups.at(-1);\n if (last?.domain === t.domain) last.componentIds.push(t.id);\n else groups.push({ domain: t.domain, componentIds: [t.id] });\n }\n const version = transforms.some((t) => t.kind === \"brown-conrady\" || t.kind === \"projection-remap\") ? \"0.2.0\" as const : \"0.1.0\" as const;\n return approximationResult({ schemaVersion: version, frameTimeSeconds: time, transforms,\n semanticKey: JSON.stringify({ frameTimeSeconds: time, transforms }), groups },\n \"composed-digital-geometry\", version, [\"Explicit inverse maps; incompatible domains require separate image stages\", \"No physical PSF, photon or noise-history recalculation\"]);\n}\n\n/**\n * Re-validates a prepared mapping from its defining fields (transforms and frame time). Its derived\n * fields (schema version, semantic key, groups) are recomputed, never trusted.\n */\nfunction reprepareGeometricMapping(mapping: PreparedGeometricMapping): PreparedGeometricMapping {\n requireInputObject(mapping, \"prepareGeometricMapping input\");\n return prepareGeometricMapping({ transforms: mapping.transforms, frameTimeSeconds: mapping.frameTimeSeconds }).value;\n}\n\n/** Maps a display point to the capture, with the full local derivatives in each direction. */\nexport function calculateComposedGeometricMapping(input: {\n mapping: PreparedGeometricMapping; destinationPointMm: LensFieldPointMm;\n}): CalculationResult<GeometricMappingPoint> {\n requireInputKeys(input, \"calculateComposedGeometricMapping\", [\"mapping\", \"destinationPointMm\"]);\n const mapping = reprepareGeometricMapping(input.mapping);\n return calculatePreparedGeometricMappingPoint(mapping, input.destinationPointMm);\n}\n\n/** Only a mapping parsed and copied within this call; prepared state from outside is never accepted. */\nconst VALIDATED_BROWN_CONRADY = new WeakMap<BrownConradyGeometricTransform, ValidatedBrownConradyDistortionProfile>();\n/** Validates a parsed transform's profile once per prepared mapping (the transform object is call-local). */\nfunction validatedBrownConrady(t: BrownConradyGeometricTransform): ValidatedBrownConradyDistortionProfile {\n let v = VALIDATED_BROWN_CONRADY.get(t);\n if (v === undefined) { v = validateBrownConradyDistortionProfile(t.profile); VALIDATED_BROWN_CONRADY.set(t, v); }\n return v;\n}\n\nfunction calculatePreparedGeometricMappingPoint(\n mapping: PreparedGeometricMapping,\n destinationPointMm: LensFieldPointMm\n): CalculationResult<GeometricMappingPoint> {\n let p = point(destinationPointMm);\n let j: GeometricJacobian = [1, 0, 0, 1];\n const components: GeometricMappingPoint[\"components\"][number][] = [];\n for (const t of mapping.transforms) {\n let local: GeometricJacobian;\n if (t.kind === \"affine\") {\n // Give each point its own result arrays when one mapping serves many points.\n local = [...t.matrix];\n p = affine(p, local, t.offsetMm);\n } else if (t.kind === \"brown-conrady\") {\n const mapped = brownConradyForwardValue(validatedBrownConrady(t), p);\n local = [...mapped.jacobian];\n p = mapped.mappedImagePointMm;\n } else if (t.kind === \"projection-remap\") {\n const mapped = lensProjectionRemapValue(t.destinationProjection.projectionModel, t.destinationProjection.focalLengthMm,\n t.sourceProjection.projectionModel, t.sourceProjection.focalLengthMm, p.x, p.y);\n local = [...mapped.jacobian];\n p = { ...mapped.sourcePointMm };\n } else {\n const { k1, k2, k3 } = t.profile.coefficients;\n const r2 = (p.x*p.x+p.y*p.y)/t.profile.normalizationRadiusMm**2;\n const s = 1+k1*r2+k2*r2*r2+k3*r2*r2*r2;\n const d = 2*(k1+2*k2*r2+3*k3*r2*r2)/t.profile.normalizationRadiusMm**2;\n local = [s+d*p.x*p.x, d*p.x*p.y, d*p.x*p.y, s+d*p.y*p.y];\n p = calculateRadialDistortionMapping({ imagePointMm: p, profile: t.profile }).value.mappedImagePointMm;\n }\n j = multiply(local, j);\n components.push({ id: t.id, sourcePointMm: { ...p }, jacobian: local });\n }\n // Singular values from the eigenvalues of the symmetric matrix JᵀJ. Computing the smaller one as\n // det/smax avoids cancellation error.\n const a = j[0]*j[0]+j[2]*j[2], b = j[0]*j[1]+j[2]*j[3], c = j[1]*j[1]+j[3]*j[3];\n const maximum = Math.sqrt((a+c+Math.hypot(a-c, 2*b))/2);\n const det = determinant(j), minimum = Math.abs(det)/maximum;\n return approximationResult({ sourcePointMm: p, jacobian: j, determinant: det,\n principalStretches: [maximum, minimum], anisotropy: maximum/minimum, components },\n \"composed-digital-geometry-point\", \"0.1.0\", [\"Jacobian warps already-formed blur and noise; it creates no captured detail\"]);\n}\n\n/** Maps a capture point to the display, for focus, metering and overlays. It inverts the same maps. */\nexport function calculateForwardGeometricMapping(input: {\n mapping: PreparedGeometricMapping; sourcePointMm: LensFieldPointMm;\n}): CalculationResult<LensFieldPointMm> {\n requireInputKeys(input, \"calculateForwardGeometricMapping\", [\"mapping\", \"sourcePointMm\"]);\n let p = point(input.sourcePointMm);\n const mapping = reprepareGeometricMapping(input.mapping);\n for (const t of [...mapping.transforms].reverse()) {\n p = t.kind === \"affine\"\n ? affine({ x: p.x-t.offsetMm.x, y: p.y-t.offsetMm.y }, inverse(t.matrix), { x: 0, y: 0 })\n : t.kind === \"brown-conrady\"\n ? brownConradyInverseValue(validatedBrownConrady(t), p)\n : t.kind === \"projection-remap\"\n ? lensProjectionRemapValue(t.sourceProjection.projectionModel, t.sourceProjection.focalLengthMm,\n t.destinationProjection.projectionModel, t.destinationProjection.focalLengthMm, p.x, p.y).sourcePointMm\n : calculateInverseRadialDistortionMapping({ distortedImagePointMm: p, profile: t.profile }).value.sourceImagePointMm;\n }\n return approximationResult(p, \"forward-digital-geometry-bridge\", \"0.1.0\", [\"Inverse of the authoritative destination-to-source geometry\"]);\n}\n\n/** @internal Shared lattice validation for downstream correction consumers. */\nexport function parseGeometricRaster(value: unknown): GeometricRaster {\n const r = record(value, [\"width\", \"height\", \"centerMm\", \"pitchMm\"]);\n const width = positive(r.width, \"width\"), height = positive(r.height, \"height\");\n if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width*height > 65536) {\n throw new InvalidScientificInputError(\"Raster must have at most 65536 integral samples.\");\n }\n return { width, height, centerMm: point(r.centerMm), pitchMm: positive(r.pitchMm, \"pitchMm\") };\n}\nfunction pixelPoint(r: GeometricRaster, x: number, y: number): LensFieldPointMm {\n return { x: r.centerMm.x+(x+.5-r.width/2)*r.pitchMm, y: r.centerMm.y-(y+.5-r.height/2)*r.pitchMm };\n}\nfunction pixelCoordinate(r: GeometricRaster, p: LensFieldPointMm): LensFieldPointMm {\n return { x: (p.x-r.centerMm.x)/r.pitchMm+r.width/2-.5, y: (r.centerMm.y-p.y)/r.pitchMm+r.height/2-.5 };\n}\nfunction supported(r: GeometricRaster, p: LensFieldPointMm, filter: GeometricResampler[\"filter\"]): boolean {\n const q = pixelCoordinate(r, p);\n return filter === \"nearest\"\n ? Math.round(q.x) >= 0 && Math.round(q.x) < r.width && Math.round(q.y) >= 0 && Math.round(q.y) < r.height\n : q.x >= 0 && q.x <= r.width-1 && q.y >= 0 && q.y <= r.height-1;\n}\n/** @internal Shared deterministic crop for a validated pixel mask. */\nexport function calculateValidSourceCrop(mask: readonly boolean[], width: number, height: number): GeometricSamplingPlan[\"jointCrop\"] {\n const heights = Array<number>(width).fill(0);\n let best: GeometricSamplingPlan[\"jointCrop\"] = null, area = 0;\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) heights[x] = mask[y*width+x] ? heights[x]!+1 : 0;\n const stack: number[] = [];\n for (let x = 0; x <= width; x++) {\n const h = x === width ? 0 : heights[x]!;\n while (stack.length && heights[stack.at(-1)!]! > h) {\n const index = stack.pop()!, rectangleHeight = heights[index]!;\n const left = stack.length ? stack.at(-1)!+1 : 0, rectangleWidth = x-left;\n if (rectangleHeight*rectangleWidth > area) {\n area = rectangleHeight*rectangleWidth;\n best = { x: left, y: y-rectangleHeight+1, width: rectangleWidth, height: rectangleHeight };\n }\n }\n stack.push(x);\n }\n }\n return best;\n}\n\n/** Composes compatible inverse maps, then resolves one shared plan for validity, crop and anti-aliasing (AA). */\nexport function calculateGeometricSamplingPlan(input: {\n mapping: PreparedGeometricMapping; sourceRaster: GeometricRaster; destinationRaster: GeometricRaster;\n resampler: GeometricResampler; physicalProjectionDistanceMm: number;\n /**\n * Ideal projection of the captured (source) image, used for the reported angles. Omitted or\n * \"rectilinear\" keeps the existing atan(mm / projection distance) angles exactly.\n */\n sourceProjectionModel?: LensProjectionModel;\n}): CalculationResult<GeometricSamplingPlan> {\n requireInputKeys(input, \"calculateGeometricSamplingPlan\", [\"mapping\", \"sourceRaster\", \"destinationRaster\", \"resampler\", \"physicalProjectionDistanceMm\", \"sourceProjectionModel\"]);\n const mapping = reprepareGeometricMapping(input.mapping);\n if (mapping.groups.length > 1) throw new InvalidScientificInputError(\"Separate incompatible-domain groups before resampling.\");\n const source = parseGeometricRaster(input.sourceRaster), destination = parseGeometricRaster(input.destinationRaster);\n const r = record(input.resampler, [\"id\", \"version\", \"filter\", \"antialias\"]);\n const resampler: GeometricResampler = { id: textValue(r.id), version: textValue(r.version),\n filter: choice(r.filter, [\"nearest\", \"bilinear\"]), antialias: choice(r.antialias, [\"none\", \"source-prefiltered\"]) };\n const projection = positive(input.physicalProjectionDistanceMm, \"physicalProjectionDistanceMm\");\n const points: GeometricMappingPoint[] = [], mask: boolean[] = [];\n for (let y = 0; y < destination.height; y++) for (let x = 0; x < destination.width; x++) {\n const mapped = calculatePreparedGeometricMappingPoint(mapping, pixelPoint(destination, x, y)).value;\n points.push(mapped); mask.push(supported(source, mapped.sourcePointMm, resampler.filter));\n }\n const requiresPrefilter = points.some((p) => p.principalStretches[0]*destination.pitchMm/source.pitchMm > 1+1e-12);\n const crop = calculateValidSourceCrop(mask, destination.width, destination.height);\n const sourceModel = input.sourceProjectionModel === undefined ? \"rectilinear\" : parseLensProjectionModel(input.sourceProjectionModel);\n const angle = sourceModel === \"rectilinear\"\n ? (mm: number): number => Math.atan(mm/projection)*180/Math.PI\n : (mm: number): number => lensProjectionSignedAxisAngle(sourceModel, projection, mm)*180/Math.PI;\n const retained = points.filter((_, i) => crop !== null && i%destination.width >= crop.x &&\n i%destination.width < crop.x+crop.width && Math.floor(i/destination.width) >= crop.y &&\n Math.floor(i/destination.width) < crop.y+crop.height).map((p) => p.sourcePointMm);\n const envelope = retained.length ? { minX: Math.min(...retained.map((p) => angle(p.x))),\n maxX: Math.max(...retained.map((p) => angle(p.x))), minY: Math.min(...retained.map((p) => angle(p.y))),\n maxY: Math.max(...retained.map((p) => angle(p.y))) } : null;\n return approximationResult({ mapping, sourceRaster: source, destinationRaster: destination, resampler, points,\n validSourceMask: mask, jointCrop: crop, requiresPrefilter,\n canResample: !requiresPrefilter || resampler.antialias === \"source-prefiltered\",\n physicalCapturedFovDegrees: {\n horizontal: angle(source.centerMm.x+source.width*source.pitchMm/2)-angle(source.centerMm.x-source.width*source.pitchMm/2),\n vertical: angle(source.centerMm.y+source.height*source.pitchMm/2)-angle(source.centerMm.y-source.height*source.pitchMm/2)\n }, retainedSampleRayEnvelopeDegrees: envelope },\n \"joint-geometric-sampling-plan\", \"0.1.0\", [\"Mask and crop are exact for this pixel-center lattice/filter, not continuous between-pixel support\", \"Compression requires explicitly source-prefiltered data; this API does not implement that prefilter\", \"No temporal trajectory crop guarantee\"]);\n}\n\n/** Resamples an already-formed single-channel image once. A null output sample marks missing source data. */\nexport function calculateGeometricResampling(input: {\n plan: GeometricSamplingPlan; sourceSamples: readonly number[];\n}): CalculationResult<{ samples: readonly (number | null)[]; resamplePasses: 1; resampler: GeometricResampler }> {\n requireInputKeys(input, \"calculateGeometricResampling\", [\"plan\", \"sourceSamples\"]);\n const prior = input.plan;\n requireInputObject(prior, \"calculateGeometricResampling plan\");\n // Re-plan from the defining fields; the derived plan fields are recomputed, never trusted.\n const plan = calculateGeometricSamplingPlan({ mapping: prior.mapping, sourceRaster: prior.sourceRaster,\n destinationRaster: prior.destinationRaster, resampler: prior.resampler, physicalProjectionDistanceMm: 1 }).value;\n if (!plan.canResample) throw new InvalidScientificInputError(\"Compression requires source-prefiltered data.\");\n const source = list(input.sourceSamples, 65536).map((v) => finite(v, \"source sample\"));\n if (source.length !== plan.sourceRaster.width*plan.sourceRaster.height) throw new InvalidScientificInputError(\"Source sample count mismatch.\");\n const samples = plan.points.map((p, index): number | null => {\n if (!plan.validSourceMask[index]) return null;\n const q = pixelCoordinate(plan.sourceRaster, p.sourcePointMm), width = plan.sourceRaster.width;\n if (plan.resampler.filter === \"nearest\") return source[Math.round(q.y)*width+Math.round(q.x)]!;\n const x = Math.floor(q.x), y = Math.floor(q.y), dx = q.x-x, dy = q.y-y;\n // A centre exactly on the boundary needs no missing neighbour. The border is never extended.\n const x1 = dx === 0 ? x : x+1, y1 = dy === 0 ? y : y+1;\n return (1-dy)*((1-dx)*source[y*width+x]!+dx*source[y*width+x1]!) +\n dy*((1-dx)*source[y1*width+x]!+dx*source[y1*width+x1]!);\n });\n return approximationResult({ samples, resamplePasses: 1, resampler: plan.resampler },\n \"single-pass-geometric-resampling\", \"0.1.0\", [\"Warps captured blur/noise; does not recalculate the optical PSF\"]);\n}\n"]}
1
+ {"version":3,"file":"geometric-transforms.js","sourceRoot":"","sources":["../../src/output/geometric-transforms.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Geometric image transforms (warps) such as distortion correction and projection changes, and\n * a resampler that applies them.\n *\n * A mapping goes from destination (output) points to source (captured) points. Preparing a\n * mapping fixes it for one still or frame; a change in time needs a new preparation.\n *\n * Transform kinds:\n * - `affine` and `radial` (schema 0.1.0, unchanged).\n * - From mapping schema 0.2.0: `brown-conrady` (radial plus tangential lens distortion) and\n * `projection-remap` (a change of ideal projection, for example fisheye capture to a\n * rectilinear or equirectangular view).\n *\n * A mapping that uses only 0.1.0 kinds keeps schema and model version 0.1.0 and gives identical output.\n * @see docs/DIGITAL_OPTICS_FOUNDATION.md for equations, coordinate/unit conventions, blockers and\n * support limits.\n */\n\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidScientificInputError } from \"../core/validation.js\";\nimport {\n calculateRadialDistortionMapping, calculateInverseRadialDistortionMapping,\n validateRadialDistortionProfile, type RadialDistortionProfile, type LensFieldPointMm\n} from \"../optics/radial-distortion.js\";\nimport { choice, finite, list, positive, record, textValue } from \"../optics/profile-contract.js\";\nimport { requireInputObject, requireInputKeys } from \"../core/record-validation.js\";\nimport {\n brownConradyForwardValue, brownConradyInverseValue, validateBrownConradyDistortionProfile,\n type BrownConradyDistortionProfile, type ValidatedBrownConradyDistortionProfile\n} from \"../optics/brown-conrady-distortion.js\";\nimport {\n lensProjectionRemapValue, lensProjectionSignedAxisAngle, parseLensProjectionModel,\n type LensProjectionModel\n} from \"../camera/lens-projection.js\";\nimport { point } from \"../optics/stray-light.js\";\n\n/** A domain change separates maps: only adjacent maps in the same domain share one resampling pass. */\nexport type GeometricImageDomain = \"raw-channel\" | \"reconstructed-linear\" | \"processed-output\";\n/** Jacobian (derivative) d(source mm)/d(destination mm), in row-major order. */\nexport type GeometricJacobian = readonly [number, number, number, number];\ninterface TransformIdentity {\n id: string;\n version: string;\n domain: GeometricImageDomain;\n purpose: \"distortion\" | \"breathing\" | \"digital-stabilization\" | \"orientation-crop\" | \"lateral-ca\" | \"projection\";\n}\n/** Explicit destination-to-source affine map, which can include rotation, shear and offset. */\nexport interface AffineGeometricTransform extends TransformIdentity {\n kind: \"affine\";\n matrix: GeometricJacobian;\n offsetMm: LensFieldPointMm;\n}\n/** Radial distortion. A correction reads the physically distorted data at the position mapped from each ideal (undistorted) destination point. */\nexport interface RadialGeometricTransform extends TransformIdentity {\n kind: \"radial\";\n profile: RadialDistortionProfile;\n}\n/** Schema 0.2.0: Brown-Conrady distortion. A correction reads the distorted data at the position mapped from each ideal destination point. */\nexport interface BrownConradyGeometricTransform extends TransformIdentity {\n kind: \"brown-conrady\";\n profile: BrownConradyDistortionProfile;\n}\n/** One ideal projection and its mapping scale (focal length, or mm per radian for equirectangular). */\nexport interface GeometricProjectionDeclaration {\n projectionModel: LensProjectionModel;\n focalLengthMm: number;\n}\n/**\n * Schema 0.2.0: change of projection. Each destination point becomes a scene direction (using\n * the destination projection), then a source point (using the source projection). Purpose must\n * be \"projection\".\n */\nexport interface ProjectionRemapGeometricTransform extends TransformIdentity {\n kind: \"projection-remap\";\n destinationProjection: GeometricProjectionDeclaration;\n sourceProjection: GeometricProjectionDeclaration;\n}\nexport type DigitalGeometricTransform =\n | AffineGeometricTransform\n | RadialGeometricTransform\n | BrownConradyGeometricTransform\n | ProjectionRemapGeometricTransform;\n/** Serializable prepared mapping. Maps are applied in the listed order, from destination towards source. */\nexport interface PreparedGeometricMapping {\n /** 0.1.0 when only affine and radial kinds are used; 0.2.0 when a kind added in 2.0 is present. */\n schemaVersion: \"0.1.0\" | \"0.2.0\";\n frameTimeSeconds: number;\n transforms: readonly DigitalGeometricTransform[];\n semanticKey: string;\n groups: readonly { domain: GeometricImageDomain; componentIds: readonly string[] }[];\n}\n/** A regular grid of pixel centres on the optical plane: +X right, +Y up; rows run top to bottom. */\nexport interface GeometricRaster {\n width: number;\n height: number;\n centerMm: LensFieldPointMm;\n pitchMm: number;\n}\n/** The resampling filter, chosen independently of the mapping. Any prefiltering (anti-aliasing) is the caller's job. */\nexport interface GeometricResampler {\n id: string;\n version: string;\n filter: \"nearest\" | \"bilinear\";\n antialias: \"none\" | \"source-prefiltered\";\n}\nexport interface GeometricMappingPoint {\n sourcePointMm: LensFieldPointMm;\n jacobian: GeometricJacobian;\n determinant: number;\n principalStretches: readonly [number, number];\n anisotropy: number;\n components: readonly { id: string; sourcePointMm: LensFieldPointMm; jacobian: GeometricJacobian }[];\n}\nexport interface GeometricSamplingPlan {\n mapping: PreparedGeometricMapping;\n sourceRaster: GeometricRaster;\n destinationRaster: GeometricRaster;\n resampler: GeometricResampler;\n points: readonly GeometricMappingPoint[];\n /** True where the selected filter has full source support around the pixel centre. Edges are not extended. */\n validSourceMask: readonly boolean[];\n /** Largest axis-aligned rectangle of valid pixels (half-open). Null if there is none. */\n jointCrop: { x: number; y: number; width: number; height: number } | null;\n requiresPrefilter: boolean;\n canResample: boolean;\n physicalCapturedFovDegrees: { horizontal: number; vertical: number };\n /** Angular extent of the mapped pixel-centre rays kept by the joint crop. It is not the edge-to-edge FOV. */\n retainedSampleRayEnvelopeDegrees: { minX: number; maxX: number; minY: number; maxY: number } | null;\n}\n\nfunction multiply(a: GeometricJacobian, b: GeometricJacobian): GeometricJacobian {\n return [a[0]*b[0]+a[1]*b[2], a[0]*b[1]+a[1]*b[3],\n a[2]*b[0]+a[3]*b[2], a[2]*b[1]+a[3]*b[3]];\n}\nfunction determinant(m: GeometricJacobian): number { return m[0]*m[3]-m[1]*m[2]; }\nfunction inverse(m: GeometricJacobian): GeometricJacobian {\n const d = determinant(m);\n if (!Number.isFinite(d) || Math.abs(d) < 1e-12) throw new InvalidScientificInputError(\"Singular or ill-conditioned affine map.\");\n return [m[3]/d, -m[1]/d, -m[2]/d, m[0]/d];\n}\nfunction affine(p: LensFieldPointMm, m: GeometricJacobian, o: LensFieldPointMm): LensFieldPointMm {\n return { x: m[0]*p.x+m[1]*p.y+o.x, y: m[2]*p.x+m[3]*p.y+o.y };\n}\n/** Strictly parses transform data. Unknown kinds or fields and profiles that cannot be inverted are rejected. */\nexport function parseDigitalGeometricTransform(value: unknown): DigitalGeometricTransform {\n const r = record(value, [\"id\", \"version\", \"domain\", \"purpose\", \"kind\", \"matrix\", \"offsetMm\", \"profile\",\n \"destinationProjection\", \"sourceProjection\"]);\n const identity = {\n id: textValue(r.id), version: textValue(r.version),\n domain: choice(r.domain, [\"raw-channel\", \"reconstructed-linear\", \"processed-output\"]),\n purpose: choice(r.purpose, [\"distortion\", \"breathing\", \"digital-stabilization\", \"orientation-crop\", \"lateral-ca\", \"projection\"])\n };\n const kind = choice(r.kind, [\"affine\", \"radial\", \"brown-conrady\", \"projection-remap\"]);\n if ((kind === \"projection-remap\") !== (identity.purpose === \"projection\")) {\n throw new InvalidScientificInputError(\"Purpose \\\"projection\\\" is used exactly by projection-remap transforms.\");\n }\n if (kind !== \"projection-remap\" && (r.destinationProjection !== undefined || r.sourceProjection !== undefined)) {\n throw new InvalidScientificInputError(\"Only projection-remap transforms carry projection declarations.\");\n }\n if (kind === \"projection-remap\") {\n if (r.matrix !== undefined || r.offsetMm !== undefined || r.profile !== undefined) {\n throw new InvalidScientificInputError(\"Projection remap cannot contain affine or distortion fields.\");\n }\n const projection = (v: unknown): GeometricProjectionDeclaration => {\n const d = record(v, [\"projectionModel\", \"focalLengthMm\"]);\n return { projectionModel: parseLensProjectionModel(d.projectionModel), focalLengthMm: positive(d.focalLengthMm, \"focalLengthMm\") };\n };\n return { ...identity, kind, destinationProjection: projection(r.destinationProjection), sourceProjection: projection(r.sourceProjection) };\n }\n if (kind === \"brown-conrady\") {\n if (r.matrix !== undefined || r.offsetMm !== undefined) throw new InvalidScientificInputError(\"Brown-Conrady map cannot contain affine fields.\");\n const p = record(r.profile, [\"normalizationRadiusMm\", \"maximumNormalizedRadius\", \"coefficients\"]);\n const c = record(p.coefficients, [\"k1\", \"k2\", \"k3\", \"p1\", \"p2\"]);\n const profile = { normalizationRadiusMm: positive(p.normalizationRadiusMm, \"normalizationRadiusMm\"),\n maximumNormalizedRadius: positive(p.maximumNormalizedRadius, \"maximumNormalizedRadius\"),\n coefficients: { k1: finite(c.k1, \"k1\"), k2: finite(c.k2, \"k2\"), k3: finite(c.k3, \"k3\"), p1: finite(c.p1, \"p1\"), p2: finite(c.p2, \"p2\") } };\n validateBrownConradyDistortionProfile(profile);\n return { ...identity, kind, profile };\n }\n if (kind === \"affine\") {\n if (r.profile !== undefined) throw new InvalidScientificInputError(\"Affine map cannot contain radial profile.\");\n const entries = list(r.matrix, 4).map((v) => finite(v, \"matrix\"));\n if (entries.length !== 4) throw new InvalidScientificInputError(\"Matrix must have four entries.\");\n const matrix = entries as [number, number, number, number];\n inverse(matrix);\n return { ...identity, kind, matrix, offsetMm: point(r.offsetMm) };\n }\n if (r.matrix !== undefined || r.offsetMm !== undefined) throw new InvalidScientificInputError(\"Radial map cannot contain affine fields.\");\n const p = record(r.profile, [\"normalizationRadiusMm\", \"maximumNormalizedRadius\", \"coefficients\"]);\n const c = record(p.coefficients, [\"k1\", \"k2\", \"k3\"]);\n const profile = { normalizationRadiusMm: positive(p.normalizationRadiusMm, \"normalizationRadiusMm\"),\n maximumNormalizedRadius: positive(p.maximumNormalizedRadius, \"maximumNormalizedRadius\"),\n coefficients: { k1: finite(c.k1, \"k1\"), k2: finite(c.k2, \"k2\"), k3: finite(c.k3, \"k3\") } };\n validateRadialDistortionProfile(profile);\n return { ...identity, kind, profile };\n}\n\n/** Prepares a deterministic mapping for one still or frame. A change in time needs a new preparation. */\nexport function prepareGeometricMapping(input: {\n transforms: readonly DigitalGeometricTransform[]; frameTimeSeconds: number;\n}): CalculationResult<PreparedGeometricMapping> {\n requireInputKeys(input, \"prepareGeometricMapping\", [\"transforms\", \"frameTimeSeconds\"]);\n const time = finite(input.frameTimeSeconds, \"frameTimeSeconds\");\n if (time < 0) throw new InvalidScientificInputError(\"Time is seconds from exposure start.\");\n const transforms = list(input.transforms, 32).map(parseDigitalGeometricTransform);\n if (new Set(transforms.map((t) => t.id)).size !== transforms.length) throw new InvalidScientificInputError(\"Duplicate transform IDs.\");\n const groups: { domain: GeometricImageDomain; componentIds: string[] }[] = [];\n for (const t of transforms) {\n const last = groups.at(-1);\n if (last?.domain === t.domain) last.componentIds.push(t.id);\n else groups.push({ domain: t.domain, componentIds: [t.id] });\n }\n const version = transforms.some((t) => t.kind === \"brown-conrady\" || t.kind === \"projection-remap\") ? \"0.2.0\" as const : \"0.1.0\" as const;\n return approximationResult({ schemaVersion: version, frameTimeSeconds: time, transforms,\n semanticKey: JSON.stringify({ frameTimeSeconds: time, transforms }), groups },\n \"composed-digital-geometry\", version, [\"Explicit inverse maps; incompatible domains require separate image stages\", \"No physical PSF, photon or noise-history recalculation\"]);\n}\n\n/**\n * Re-validates a prepared mapping from its defining fields (transforms and frame time). Its derived\n * fields (schema version, semantic key, groups) are recomputed, never trusted.\n */\nfunction reprepareGeometricMapping(mapping: PreparedGeometricMapping): PreparedGeometricMapping {\n requireInputObject(mapping, \"prepareGeometricMapping input\");\n return prepareGeometricMapping({ transforms: mapping.transforms, frameTimeSeconds: mapping.frameTimeSeconds }).value;\n}\n\n/** Maps a display point to the capture, with the full local derivatives in each direction. */\nexport function calculateComposedGeometricMapping(input: {\n mapping: PreparedGeometricMapping; destinationPointMm: LensFieldPointMm;\n}): CalculationResult<GeometricMappingPoint> {\n requireInputKeys(input, \"calculateComposedGeometricMapping\", [\"mapping\", \"destinationPointMm\"]);\n const mapping = reprepareGeometricMapping(input.mapping);\n return calculatePreparedGeometricMappingPoint(mapping, input.destinationPointMm);\n}\n\n/** Only a mapping parsed and copied within this call; prepared state from outside is never accepted. */\nconst VALIDATED_BROWN_CONRADY = new WeakMap<BrownConradyGeometricTransform, ValidatedBrownConradyDistortionProfile>();\n/** Validates a parsed transform's profile once per prepared mapping (the transform object is call-local). */\nfunction validatedBrownConrady(t: BrownConradyGeometricTransform): ValidatedBrownConradyDistortionProfile {\n let v = VALIDATED_BROWN_CONRADY.get(t);\n if (v === undefined) { v = validateBrownConradyDistortionProfile(t.profile); VALIDATED_BROWN_CONRADY.set(t, v); }\n return v;\n}\n\nfunction calculatePreparedGeometricMappingPoint(\n mapping: PreparedGeometricMapping,\n destinationPointMm: LensFieldPointMm\n): CalculationResult<GeometricMappingPoint> {\n let p = point(destinationPointMm);\n let j: GeometricJacobian = [1, 0, 0, 1];\n const components: GeometricMappingPoint[\"components\"][number][] = [];\n for (const t of mapping.transforms) {\n let local: GeometricJacobian;\n if (t.kind === \"affine\") {\n // Give each point its own result arrays when one mapping serves many points.\n local = [...t.matrix];\n p = affine(p, local, t.offsetMm);\n } else if (t.kind === \"brown-conrady\") {\n const mapped = brownConradyForwardValue(validatedBrownConrady(t), p);\n local = [...mapped.jacobian];\n p = mapped.mappedImagePointMm;\n } else if (t.kind === \"projection-remap\") {\n const mapped = lensProjectionRemapValue(t.destinationProjection.projectionModel, t.destinationProjection.focalLengthMm,\n t.sourceProjection.projectionModel, t.sourceProjection.focalLengthMm, p.x, p.y);\n local = [...mapped.jacobian];\n p = { ...mapped.sourcePointMm };\n } else {\n const { k1, k2, k3 } = t.profile.coefficients;\n const r2 = (p.x*p.x+p.y*p.y)/t.profile.normalizationRadiusMm**2;\n const s = 1+k1*r2+k2*r2*r2+k3*r2*r2*r2;\n const d = 2*(k1+2*k2*r2+3*k3*r2*r2)/t.profile.normalizationRadiusMm**2;\n local = [s+d*p.x*p.x, d*p.x*p.y, d*p.x*p.y, s+d*p.y*p.y];\n p = calculateRadialDistortionMapping({ imagePointMm: p, profile: t.profile }).value.mappedImagePointMm;\n }\n j = multiply(local, j);\n components.push({ id: t.id, sourcePointMm: { ...p }, jacobian: local });\n }\n // Singular values from the eigenvalues of the symmetric matrix JᵀJ. Computing the smaller one as\n // det/smax avoids cancellation error.\n const a = j[0]*j[0]+j[2]*j[2], b = j[0]*j[1]+j[2]*j[3], c = j[1]*j[1]+j[3]*j[3];\n const maximum = Math.sqrt((a+c+Math.hypot(a-c, 2*b))/2);\n const det = determinant(j), minimum = Math.abs(det)/maximum;\n return approximationResult({ sourcePointMm: p, jacobian: j, determinant: det,\n principalStretches: [maximum, minimum], anisotropy: maximum/minimum, components },\n \"composed-digital-geometry-point\", \"0.1.0\", [\"Jacobian warps already-formed blur and noise; it creates no captured detail\"]);\n}\n\n/** Maps a capture point to the display, for focus, metering and overlays. It inverts the same maps. */\nexport function calculateForwardGeometricMapping(input: {\n mapping: PreparedGeometricMapping; sourcePointMm: LensFieldPointMm;\n}): CalculationResult<LensFieldPointMm> {\n requireInputKeys(input, \"calculateForwardGeometricMapping\", [\"mapping\", \"sourcePointMm\"]);\n let p = point(input.sourcePointMm);\n const mapping = reprepareGeometricMapping(input.mapping);\n for (const t of [...mapping.transforms].reverse()) {\n p = t.kind === \"affine\"\n ? affine({ x: p.x-t.offsetMm.x, y: p.y-t.offsetMm.y }, inverse(t.matrix), { x: 0, y: 0 })\n : t.kind === \"brown-conrady\"\n ? brownConradyInverseValue(validatedBrownConrady(t), p)\n : t.kind === \"projection-remap\"\n ? lensProjectionRemapValue(t.sourceProjection.projectionModel, t.sourceProjection.focalLengthMm,\n t.destinationProjection.projectionModel, t.destinationProjection.focalLengthMm, p.x, p.y).sourcePointMm\n : calculateInverseRadialDistortionMapping({ distortedImagePointMm: p, profile: t.profile }).value.sourceImagePointMm;\n }\n return approximationResult(p, \"forward-digital-geometry-bridge\", \"0.1.0\", [\"Inverse of the authoritative destination-to-source geometry\"]);\n}\n\n/** @internal Shared lattice validation for downstream correction consumers. */\nexport function parseGeometricRaster(value: unknown): GeometricRaster {\n const r = record(value, [\"width\", \"height\", \"centerMm\", \"pitchMm\"]);\n const width = positive(r.width, \"width\"), height = positive(r.height, \"height\");\n if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width*height > 65536) {\n throw new InvalidScientificInputError(\"Raster must have at most 65536 integral samples.\");\n }\n return { width, height, centerMm: point(r.centerMm), pitchMm: positive(r.pitchMm, \"pitchMm\") };\n}\nfunction pixelPoint(r: GeometricRaster, x: number, y: number): LensFieldPointMm {\n return { x: r.centerMm.x+(x+.5-r.width/2)*r.pitchMm, y: r.centerMm.y-(y+.5-r.height/2)*r.pitchMm };\n}\nfunction pixelCoordinate(r: GeometricRaster, p: LensFieldPointMm): LensFieldPointMm {\n return { x: (p.x-r.centerMm.x)/r.pitchMm+r.width/2-.5, y: (r.centerMm.y-p.y)/r.pitchMm+r.height/2-.5 };\n}\nfunction supported(r: GeometricRaster, p: LensFieldPointMm, filter: GeometricResampler[\"filter\"]): boolean {\n const q = pixelCoordinate(r, p);\n return filter === \"nearest\"\n ? Math.round(q.x) >= 0 && Math.round(q.x) < r.width && Math.round(q.y) >= 0 && Math.round(q.y) < r.height\n : q.x >= 0 && q.x <= r.width-1 && q.y >= 0 && q.y <= r.height-1;\n}\n/** @internal Shared deterministic crop for a validated pixel mask. */\nexport function calculateValidSourceCrop(mask: readonly boolean[], width: number, height: number): GeometricSamplingPlan[\"jointCrop\"] {\n const heights = Array<number>(width).fill(0);\n let best: GeometricSamplingPlan[\"jointCrop\"] = null, area = 0;\n for (let y = 0; y < height; y++) {\n for (let x = 0; x < width; x++) heights[x] = mask[y*width+x] ? heights[x]!+1 : 0;\n const stack: number[] = [];\n for (let x = 0; x <= width; x++) {\n const h = x === width ? 0 : heights[x]!;\n while (stack.length && heights[stack.at(-1)!]! > h) {\n const index = stack.pop()!, rectangleHeight = heights[index]!;\n const left = stack.length ? stack.at(-1)!+1 : 0, rectangleWidth = x-left;\n if (rectangleHeight*rectangleWidth > area) {\n area = rectangleHeight*rectangleWidth;\n best = { x: left, y: y-rectangleHeight+1, width: rectangleWidth, height: rectangleHeight };\n }\n }\n stack.push(x);\n }\n }\n return best;\n}\n\n/** Composes compatible inverse maps, then resolves one shared plan for validity, crop and anti-aliasing (AA). */\nexport function calculateGeometricSamplingPlan(input: {\n mapping: PreparedGeometricMapping; sourceRaster: GeometricRaster; destinationRaster: GeometricRaster;\n resampler: GeometricResampler; physicalProjectionDistanceMm: number;\n /**\n * Ideal projection of the captured (source) image, used for the reported angles. Omitted or\n * \"rectilinear\" keeps the existing atan(mm / projection distance) angles exactly.\n */\n sourceProjectionModel?: LensProjectionModel;\n}): CalculationResult<GeometricSamplingPlan> {\n requireInputKeys(input, \"calculateGeometricSamplingPlan\", [\"mapping\", \"sourceRaster\", \"destinationRaster\", \"resampler\", \"physicalProjectionDistanceMm\", \"sourceProjectionModel\"]);\n const mapping = reprepareGeometricMapping(input.mapping);\n if (mapping.groups.length > 1) throw new InvalidScientificInputError(\"Separate incompatible-domain groups before resampling.\");\n const source = parseGeometricRaster(input.sourceRaster), destination = parseGeometricRaster(input.destinationRaster);\n const r = record(input.resampler, [\"id\", \"version\", \"filter\", \"antialias\"]);\n const resampler: GeometricResampler = { id: textValue(r.id), version: textValue(r.version),\n filter: choice(r.filter, [\"nearest\", \"bilinear\"]), antialias: choice(r.antialias, [\"none\", \"source-prefiltered\"]) };\n const projection = positive(input.physicalProjectionDistanceMm, \"physicalProjectionDistanceMm\");\n const points: GeometricMappingPoint[] = [], mask: boolean[] = [];\n for (let y = 0; y < destination.height; y++) for (let x = 0; x < destination.width; x++) {\n const mapped = calculatePreparedGeometricMappingPoint(mapping, pixelPoint(destination, x, y)).value;\n points.push(mapped); mask.push(supported(source, mapped.sourcePointMm, resampler.filter));\n }\n const requiresPrefilter = points.some((p) => p.principalStretches[0]*destination.pitchMm/source.pitchMm > 1+1e-12);\n const crop = calculateValidSourceCrop(mask, destination.width, destination.height);\n const sourceModel = input.sourceProjectionModel === undefined ? \"rectilinear\" : parseLensProjectionModel(input.sourceProjectionModel);\n const angle = sourceModel === \"rectilinear\"\n ? (mm: number): number => Math.atan(mm/projection)*180/Math.PI\n : (mm: number): number => lensProjectionSignedAxisAngle(sourceModel, projection, mm)*180/Math.PI;\n const retained = points.filter((_, i) => crop !== null && i%destination.width >= crop.x &&\n i%destination.width < crop.x+crop.width && Math.floor(i/destination.width) >= crop.y &&\n Math.floor(i/destination.width) < crop.y+crop.height).map((p) => p.sourcePointMm);\n const envelope = retained.length ? { minX: Math.min(...retained.map((p) => angle(p.x))),\n maxX: Math.max(...retained.map((p) => angle(p.x))), minY: Math.min(...retained.map((p) => angle(p.y))),\n maxY: Math.max(...retained.map((p) => angle(p.y))) } : null;\n return approximationResult({ mapping, sourceRaster: source, destinationRaster: destination, resampler, points,\n validSourceMask: mask, jointCrop: crop, requiresPrefilter,\n canResample: !requiresPrefilter || resampler.antialias === \"source-prefiltered\",\n physicalCapturedFovDegrees: {\n horizontal: angle(source.centerMm.x+source.width*source.pitchMm/2)-angle(source.centerMm.x-source.width*source.pitchMm/2),\n vertical: angle(source.centerMm.y+source.height*source.pitchMm/2)-angle(source.centerMm.y-source.height*source.pitchMm/2)\n }, retainedSampleRayEnvelopeDegrees: envelope },\n \"joint-geometric-sampling-plan\", \"0.1.0\", [\"Mask and crop are exact for this pixel-center lattice/filter, not continuous between-pixel support\", \"Compression requires explicitly source-prefiltered data; this API does not implement that prefilter\", \"No temporal trajectory crop guarantee\"]);\n}\n\n/** Resamples an already-formed single-channel image once. A null output sample marks missing source data. */\nexport function calculateGeometricResampling(input: {\n plan: GeometricSamplingPlan; sourceSamples: readonly number[];\n}): CalculationResult<{ samples: readonly (number | null)[]; resamplePasses: 1; resampler: GeometricResampler }> {\n requireInputKeys(input, \"calculateGeometricResampling\", [\"plan\", \"sourceSamples\"]);\n const prior = input.plan;\n requireInputObject(prior, \"calculateGeometricResampling plan\");\n // Re-plan from the defining fields; the derived plan fields are recomputed, never trusted.\n const plan = calculateGeometricSamplingPlan({ mapping: prior.mapping, sourceRaster: prior.sourceRaster,\n destinationRaster: prior.destinationRaster, resampler: prior.resampler, physicalProjectionDistanceMm: 1 }).value;\n if (!plan.canResample) throw new InvalidScientificInputError(\"Compression requires source-prefiltered data.\");\n const source = list(input.sourceSamples, 65536).map((v) => finite(v, \"source sample\"));\n if (source.length !== plan.sourceRaster.width*plan.sourceRaster.height) throw new InvalidScientificInputError(\"Source sample count mismatch.\");\n const samples = plan.points.map((p, index): number | null => {\n if (!plan.validSourceMask[index]) return null;\n const q = pixelCoordinate(plan.sourceRaster, p.sourcePointMm), width = plan.sourceRaster.width;\n if (plan.resampler.filter === \"nearest\") return source[Math.round(q.y)*width+Math.round(q.x)]!;\n const x = Math.floor(q.x), y = Math.floor(q.y), dx = q.x-x, dy = q.y-y;\n // A centre exactly on the boundary needs no missing neighbour. The border is never extended.\n const x1 = dx === 0 ? x : x+1, y1 = dy === 0 ? y : y+1;\n return (1-dy)*((1-dx)*source[y*width+x]!+dx*source[y*width+x1]!) +\n dy*((1-dx)*source[y1*width+x]!+dx*source[y1*width+x1]!);\n });\n return approximationResult({ samples, resamplePasses: 1, resampler: plan.resampler },\n \"single-pass-geometric-resampling\", \"0.1.0\", [\"Warps captured blur/noise; does not recalculate the optical PSF\"]);\n}\n"]}
@@ -118,11 +118,11 @@ export interface SdrPixelCounts {
118
118
  * For `pixelCount` interleaved linear-sRGB pixels starting at `source[start]`, it runs exactly the
119
119
  * reference (`calculateSdrRendering`) operations in the same order per pixel: -0 → +0 → rendering
120
120
  * exposure → shared tone operator → gamut → sRGB transfer → quantization (nearest, ties up) into
121
- * `codes[outOffset]`. A gamut rejection is delayed via `counts.outOfGamut`, because the reference
121
+ * `codes[outOffset]` (with `maximum` 0, the encoded value unquantized). A gamut rejection is delayed via `counts.outOfGamut`, because the reference
122
122
  * finishes the whole tone pass (and its exposure errors) first. `scratch` must hold at least 3
123
123
  * values. Not exported from the package.
124
124
  */
125
- export declare function renderSdrRunInto(source: Float32Array | Float64Array, start: number, pixelCount: number, profile: SdrRenderingProfile, tone: SdrPixelTone, scale: number, maximum: number, codes: Uint8Array | Uint16Array, outOffset: number, scratch: Float64Array, counts: SdrPixelCounts): void;
125
+ export declare function renderSdrRunInto(source: Float32Array | Float64Array, start: number, pixelCount: number, profile: SdrRenderingProfile, tone: SdrPixelTone, scale: number, maximum: number, codes: Uint8Array | Uint16Array | Float64Array, outOffset: number, scratch: Float64Array, counts: SdrPixelCounts): void;
126
126
  /** The reference's output-encoding declaration for the given code maximum. */
127
127
  export declare function sdrOutputEncoding(maximum: number): SdrRenderingResult["outputEncoding"];
128
128
  /** A single-use task. No partial output is ever returned, and cancel or dispose stops a late result from being published. */
@@ -182,7 +182,7 @@ export function nativeSrgb8QuantizerForTest() {
182
182
  * For `pixelCount` interleaved linear-sRGB pixels starting at `source[start]`, it runs exactly the
183
183
  * reference (`calculateSdrRendering`) operations in the same order per pixel: -0 → +0 → rendering
184
184
  * exposure → shared tone operator → gamut → sRGB transfer → quantization (nearest, ties up) into
185
- * `codes[outOffset]`. A gamut rejection is delayed via `counts.outOfGamut`, because the reference
185
+ * `codes[outOffset]` (with `maximum` 0, the encoded value unquantized). A gamut rejection is delayed via `counts.outOfGamut`, because the reference
186
186
  * finishes the whole tone pass (and its exposure errors) first. `scratch` must hold at least 3
187
187
  * values. Not exported from the package.
188
188
  */
@@ -221,7 +221,8 @@ export function renderSdrRunInto(source, start, pixelCount, profile, tone, scale
221
221
  }
222
222
  }
223
223
  // Encoded values are in [0,1]: Math.round is exact nearest-ties-up here.
224
- codes[o] = quantizer ? quantizeSrgb8(quantizer, linear) : Math.round(encodeSrgbComponent(linear) * maximum);
224
+ // maximum 0 (#571): the encoded value itself, unquantized, into a Float64Array (the unsharp mask's input).
225
+ codes[o] = quantizer ? quantizeSrgb8(quantizer, linear) : maximum === 0 ? encodeSrgbComponent(linear) : Math.round(encodeSrgbComponent(linear) * maximum);
225
226
  }
226
227
  }
227
228
  counts.negative += negative;