@photivra/engine 2.20.0 → 2.22.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 (275) hide show
  1. package/CHANGELOG.md +295 -1
  2. package/CITATION.cff +1 -1
  3. package/README.md +7 -6
  4. package/SECURITY.md +1 -1
  5. package/THIRD_PARTY.md +18 -0
  6. package/dist/capture/c2pa-embed.d.ts +116 -0
  7. package/dist/capture/c2pa-embed.js +493 -0
  8. package/dist/capture/c2pa-embed.js.map +1 -0
  9. package/dist/capture/dng-import.d.ts +73 -8
  10. package/dist/capture/dng-import.js +315 -80
  11. package/dist/capture/dng-import.js.map +1 -1
  12. package/dist/capture/dng-lens-correction-opcodes.d.ts +2 -2
  13. package/dist/capture/dng-lens-correction-opcodes.js +2 -2
  14. package/dist/capture/dng-lens-correction-opcodes.js.map +1 -1
  15. package/dist/capture/dng-opcodes.d.ts +96 -0
  16. package/dist/capture/dng-opcodes.js +391 -0
  17. package/dist/capture/dng-opcodes.js.map +1 -0
  18. package/dist/capture/dng-profile-maps.d.ts +99 -0
  19. package/dist/capture/dng-profile-maps.js +381 -0
  20. package/dist/capture/dng-profile-maps.js.map +1 -0
  21. package/dist/capture/dng-stage-image.d.ts +23 -0
  22. package/dist/capture/dng-stage-image.js +65 -0
  23. package/dist/capture/dng-stage-image.js.map +1 -0
  24. package/dist/capture/exposure-build-up-stage.d.ts +16 -0
  25. package/dist/capture/exposure-build-up-stage.js +4 -0
  26. package/dist/capture/exposure-build-up-stage.js.map +1 -0
  27. package/dist/capture/jpeg-xl-tiles.d.ts +22 -0
  28. package/dist/capture/jpeg-xl-tiles.js +151 -0
  29. package/dist/capture/jpeg-xl-tiles.js.map +1 -0
  30. package/dist/capture/lossy-jpeg.d.ts +17 -0
  31. package/dist/capture/lossy-jpeg.js +369 -0
  32. package/dist/capture/lossy-jpeg.js.map +1 -0
  33. package/dist/capture/native-environment-lean.d.ts +2 -1
  34. package/dist/capture/native-environment-lean.js +41 -73
  35. package/dist/capture/native-environment-lean.js.map +1 -1
  36. package/dist/capture/native-environment-tabulated.js +4 -3
  37. package/dist/capture/native-environment-tabulated.js.map +1 -1
  38. package/dist/capture/native-raw-development.d.ts +2 -1
  39. package/dist/capture/native-raw-development.js.map +1 -1
  40. package/dist/capture/native-raw.d.ts +3 -2
  41. package/dist/capture/native-raw.js +5 -4
  42. package/dist/capture/native-raw.js.map +1 -1
  43. package/dist/capture/sensor-raw-producer.d.ts +4 -1
  44. package/dist/capture/sensor-raw-producer.js +12 -3
  45. package/dist/capture/sensor-raw-producer.js.map +1 -1
  46. package/dist/color/neighbor-highlight-reconstruction.d.ts +66 -0
  47. package/dist/color/neighbor-highlight-reconstruction.js +234 -0
  48. package/dist/color/neighbor-highlight-reconstruction.js.map +1 -0
  49. package/dist/color/sensor-color-development.d.ts +5 -0
  50. package/dist/color/sensor-color-development.js +7 -1
  51. package/dist/color/sensor-color-development.js.map +1 -1
  52. package/dist/color/spectral-spikiness.d.ts +11 -0
  53. package/dist/color/spectral-spikiness.js +23 -0
  54. package/dist/color/spectral-spikiness.js.map +1 -0
  55. package/dist/core/execution-error.d.ts +21 -0
  56. package/dist/core/execution-error.js +12 -0
  57. package/dist/core/execution-error.js.map +1 -0
  58. package/dist/core/version.d.ts +1 -1
  59. package/dist/core/version.js +1 -1
  60. package/dist/core/version.js.map +1 -1
  61. package/dist/equipment/generic-lens-catalogue.d.ts +3 -2
  62. package/dist/equipment/generic-lens-catalogue.js +18 -5
  63. package/dist/equipment/generic-lens-catalogue.js.map +1 -1
  64. package/dist/equipment/generic-tier-assets.d.ts +32 -0
  65. package/dist/equipment/generic-tier-assets.js +16 -11
  66. package/dist/equipment/generic-tier-assets.js.map +1 -1
  67. package/dist/equipment/sensor-profile-body.d.ts +78 -0
  68. package/dist/equipment/sensor-profile-body.js +77 -0
  69. package/dist/equipment/sensor-profile-body.js.map +1 -0
  70. package/dist/equipment/sensor-profiles.d.ts +8 -0
  71. package/dist/equipment/sensor-profiles.js +82 -0
  72. package/dist/equipment/sensor-profiles.js.map +1 -0
  73. package/dist/facade/catalogue-lens-signal.d.ts +28 -3
  74. package/dist/facade/catalogue-lens-signal.js +83 -78
  75. package/dist/facade/catalogue-lens-signal.js.map +1 -1
  76. package/dist/facade/chart-radiance-batch.js +2 -1
  77. package/dist/facade/chart-radiance-batch.js.map +1 -1
  78. package/dist/facade/defocus-image-space-provider.d.ts +7 -0
  79. package/dist/facade/defocus-image-space-provider.js +64 -76
  80. package/dist/facade/defocus-image-space-provider.js.map +1 -1
  81. package/dist/facade/diffraction-provider.d.ts +1 -1
  82. package/dist/facade/diffraction-provider.js +4 -4
  83. package/dist/facade/diffraction-provider.js.map +1 -1
  84. package/dist/facade/display-view-gpu-session.js +39 -5
  85. package/dist/facade/display-view-gpu-session.js.map +1 -1
  86. package/dist/facade/display-view-gpu.d.ts +19 -6
  87. package/dist/facade/display-view-gpu.js.map +1 -1
  88. package/dist/facade/display-view-lens-signal.js +25 -23
  89. package/dist/facade/display-view-lens-signal.js.map +1 -1
  90. package/dist/facade/display-view-normal.d.ts +15 -0
  91. package/dist/facade/display-view-normal.js +44 -0
  92. package/dist/facade/display-view-normal.js.map +1 -0
  93. package/dist/facade/display-view-rendering.d.ts +97 -0
  94. package/dist/facade/display-view-rendering.js +598 -0
  95. package/dist/facade/display-view-rendering.js.map +1 -0
  96. package/dist/facade/display-view.d.ts +37 -65
  97. package/dist/facade/display-view.js +152 -499
  98. package/dist/facade/display-view.js.map +1 -1
  99. package/dist/facade/expected-signal-setup.d.ts +3 -1
  100. package/dist/facade/expected-signal-setup.js +3 -3
  101. package/dist/facade/expected-signal-setup.js.map +1 -1
  102. package/dist/facade/expected-signal.d.ts +47 -14
  103. package/dist/facade/expected-signal.js +117 -112
  104. package/dist/facade/expected-signal.js.map +1 -1
  105. package/dist/facade/exposure-build-up.d.ts +70 -0
  106. package/dist/facade/exposure-build-up.js +164 -0
  107. package/dist/facade/exposure-build-up.js.map +1 -0
  108. package/dist/facade/fast-methods.d.ts +1 -1
  109. package/dist/facade/fast-methods.js +46 -9
  110. package/dist/facade/fast-methods.js.map +1 -1
  111. package/dist/facade/generic-lens-capture.d.ts +3 -3
  112. package/dist/facade/generic-lens-capture.js +3 -3
  113. package/dist/facade/generic-lens-capture.js.map +1 -1
  114. package/dist/facade/image-plane-enable.d.ts +7 -0
  115. package/dist/facade/image-plane-enable.js +17 -0
  116. package/dist/facade/image-plane-enable.js.map +1 -0
  117. package/dist/facade/image-plane-lattice-data.d.ts +6 -0
  118. package/dist/facade/image-plane-lattice-data.js +10 -0
  119. package/dist/facade/image-plane-lattice-data.js.map +1 -0
  120. package/dist/facade/image-plane-support.d.ts +33 -0
  121. package/dist/facade/image-plane-support.js +11 -0
  122. package/dist/facade/image-plane-support.js.map +1 -0
  123. package/dist/facade/image-plane.d.ts +36 -0
  124. package/dist/facade/image-plane.js +384 -0
  125. package/dist/facade/image-plane.js.map +1 -0
  126. package/dist/facade/mixed-illumination.d.ts +108 -0
  127. package/dist/facade/mixed-illumination.js +174 -0
  128. package/dist/facade/mixed-illumination.js.map +1 -0
  129. package/dist/facade/on-demand-kits.d.ts +14 -0
  130. package/dist/facade/on-demand-kits.js +36 -0
  131. package/dist/facade/on-demand-kits.js.map +1 -0
  132. package/dist/facade/opt-in-kit.d.ts +9 -0
  133. package/dist/facade/opt-in-kit.js +11 -0
  134. package/dist/facade/opt-in-kit.js.map +1 -0
  135. package/dist/facade/quality-kit.d.ts +21 -0
  136. package/dist/facade/quality-kit.js +23 -0
  137. package/dist/facade/quality-kit.js.map +1 -0
  138. package/dist/facade/quality-paths.d.ts +22 -0
  139. package/dist/facade/quality-paths.js +44 -0
  140. package/dist/facade/quality-paths.js.map +1 -0
  141. package/dist/facade/representative-light-sources.d.ts +72 -0
  142. package/dist/facade/representative-light-sources.js +124 -0
  143. package/dist/facade/representative-light-sources.js.map +1 -0
  144. package/dist/facade/scene-radiance.d.ts +145 -4
  145. package/dist/facade/scene-radiance.js +50 -13
  146. package/dist/facade/scene-radiance.js.map +1 -1
  147. package/dist/facade/simulate-capture.d.ts +68 -20
  148. package/dist/facade/simulate-capture.js +399 -146
  149. package/dist/facade/simulate-capture.js.map +1 -1
  150. package/dist/facade/simulate-display-view.d.ts +10 -12
  151. package/dist/facade/simulate-display-view.js +70 -53
  152. package/dist/facade/simulate-display-view.js.map +1 -1
  153. package/dist/gpu/chart-composite-gpu.d.ts +13 -1
  154. package/dist/gpu/chart-composite-gpu.js +48 -9
  155. package/dist/gpu/chart-composite-gpu.js.map +1 -1
  156. package/dist/gpu/chart-radiance-gpu.js +4 -30
  157. package/dist/gpu/chart-radiance-gpu.js.map +1 -1
  158. package/dist/gpu/display-view-composite-material-module.d.ts +10 -4
  159. package/dist/gpu/display-view-composite-material-module.js +39 -13
  160. package/dist/gpu/display-view-composite-material-module.js.map +1 -1
  161. package/dist/gpu/display-view-gpu-renderer.d.ts +43 -0
  162. package/dist/gpu/display-view-gpu-renderer.js +134 -0
  163. package/dist/gpu/display-view-gpu-renderer.js.map +1 -0
  164. package/dist/gpu/display-view-image-material-module.d.ts +87 -0
  165. package/dist/gpu/display-view-image-material-module.js +361 -0
  166. package/dist/gpu/display-view-image-material-module.js.map +1 -0
  167. package/dist/gpu/display-view-image-quality.d.ts +87 -0
  168. package/dist/gpu/display-view-image-quality.js +625 -0
  169. package/dist/gpu/display-view-image-quality.js.map +1 -0
  170. package/dist/gpu/display-view-quality-gpu.d.ts +80 -65
  171. package/dist/gpu/display-view-quality-gpu.js +187 -445
  172. package/dist/gpu/display-view-quality-gpu.js.map +1 -1
  173. package/dist/gpu/display-view-speed-gpu.d.ts +67 -19
  174. package/dist/gpu/display-view-speed-gpu.js +181 -105
  175. package/dist/gpu/display-view-speed-gpu.js.map +1 -1
  176. package/dist/gpu/image-kernel-accelerator.js +3 -0
  177. package/dist/gpu/image-kernel-accelerator.js.map +1 -1
  178. package/dist/gpu/image-kernel-execution.d.ts +2 -13
  179. package/dist/gpu/image-kernel-execution.js +2 -10
  180. package/dist/gpu/image-kernel-execution.js.map +1 -1
  181. package/dist/gpu/wgsl-display-view-coupled-stub.d.ts +8 -0
  182. package/dist/gpu/wgsl-display-view-coupled-stub.js +21 -0
  183. package/dist/gpu/wgsl-display-view-coupled-stub.js.map +1 -0
  184. package/dist/gpu/wgsl-display-view-coupled.d.ts +88 -0
  185. package/dist/gpu/wgsl-display-view-coupled.js +353 -0
  186. package/dist/gpu/wgsl-display-view-coupled.js.map +1 -0
  187. package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +33 -24
  188. package/dist/gpu/wgsl-display-view-quality-kernel.js +166 -146
  189. package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
  190. package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +19 -44
  191. package/dist/gpu/wgsl-display-view-speed-kernel.js +116 -18
  192. package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
  193. package/dist/gpu/wgsl-exact-sum.d.ts +20 -0
  194. package/dist/gpu/wgsl-exact-sum.js +42 -0
  195. package/dist/gpu/wgsl-exact-sum.js.map +1 -0
  196. package/dist/index.d.ts +22 -6
  197. package/dist/index.js +16 -2
  198. package/dist/index.js.map +1 -1
  199. package/dist/motion/subject-motion.d.ts +66 -0
  200. package/dist/motion/subject-motion.js +52 -0
  201. package/dist/motion/subject-motion.js.map +1 -0
  202. package/dist/optics/generic-lens-lateral-color.d.ts +83 -0
  203. package/dist/optics/generic-lens-lateral-color.js +77 -0
  204. package/dist/optics/generic-lens-lateral-color.js.map +1 -0
  205. package/dist/optics/generic-lens-psf.js +54 -46
  206. package/dist/optics/generic-lens-psf.js.map +1 -1
  207. package/dist/optics/lens-psf-profile.d.ts +7 -0
  208. package/dist/optics/lens-psf-profile.js +2 -1
  209. package/dist/optics/lens-psf-profile.js.map +1 -1
  210. package/dist/output/camera-channel-response.d.ts +50 -0
  211. package/dist/output/camera-channel-response.js +69 -0
  212. package/dist/output/camera-channel-response.js.map +1 -0
  213. package/dist/output/developed-lens-corrections.d.ts +19 -5
  214. package/dist/output/developed-lens-corrections.js +28 -9
  215. package/dist/output/developed-lens-corrections.js.map +1 -1
  216. package/dist/output/generic-lens-corrections.d.ts +41 -5
  217. package/dist/output/generic-lens-corrections.js +52 -8
  218. package/dist/output/generic-lens-corrections.js.map +1 -1
  219. package/dist/output/native-capture-sdr.js +5 -2
  220. package/dist/output/native-capture-sdr.js.map +1 -1
  221. package/dist/output/print-plan.d.ts +41 -5
  222. package/dist/output/print-plan.js +59 -14
  223. package/dist/output/print-plan.js.map +1 -1
  224. package/dist/output/sdr-rendering.d.ts +18 -1
  225. package/dist/output/sdr-rendering.js +39 -2
  226. package/dist/output/sdr-rendering.js.map +1 -1
  227. package/dist/sensor/binomial-sample.d.ts +3 -0
  228. package/dist/sensor/binomial-sample.js +86 -0
  229. package/dist/sensor/binomial-sample.js.map +1 -0
  230. package/dist/sensor/native-sensor-report.d.ts +10 -0
  231. package/dist/sensor/native-sensor-report.js +16 -0
  232. package/dist/sensor/native-sensor-report.js.map +1 -0
  233. package/dist/sensor/sensor-characteristics.d.ts +91 -13
  234. package/dist/sensor/sensor-characteristics.js +30 -15
  235. package/dist/sensor/sensor-characteristics.js.map +1 -1
  236. package/docs/AGENT_GUIDE.md +1 -0
  237. package/docs/API_REFERENCE.md +214 -4
  238. package/docs/QUICK_START.md +162 -8
  239. package/docs/USAGE.md +195 -22
  240. package/docs/api/capture-c2pa-embed.md +177 -0
  241. package/docs/api/capture-dng-import.md +86 -10
  242. package/docs/api/capture-dng-opcodes.md +93 -0
  243. package/docs/api/capture-dng-profile-maps.md +141 -0
  244. package/docs/api/capture-dng-stage-image.md +43 -0
  245. package/docs/api/capture-jpeg-xl-tiles.md +33 -0
  246. package/docs/api/capture-lossy-jpeg.md +41 -0
  247. package/docs/api/capture-native-raw-development.md +2 -1
  248. package/docs/api/core-execution-error.md +43 -0
  249. package/docs/api/core-version.md +1 -1
  250. package/docs/api/equipment-generic-lens-catalogue.md +10 -5
  251. package/docs/api/equipment-sensor-profile-body.md +64 -0
  252. package/docs/api/equipment-sensor-profiles.md +86 -0
  253. package/docs/api/exports.json +460 -5
  254. package/docs/api/facade-display-view-gpu.md +5 -2
  255. package/docs/api/facade-display-view.md +11 -15
  256. package/docs/api/facade-exposure-build-up.md +105 -0
  257. package/docs/api/facade-fast-methods.md +47 -10
  258. package/docs/api/facade-image-plane-enable.md +16 -0
  259. package/docs/api/facade-mixed-illumination.md +67 -0
  260. package/docs/api/facade-quality-paths.md +24 -0
  261. package/docs/api/facade-representative-light-sources.md +131 -0
  262. package/docs/api/facade-scene-radiance.md +54 -3
  263. package/docs/api/facade-simulate-capture.md +48 -11
  264. package/docs/api/facade-simulate-display-view.md +10 -12
  265. package/docs/api/gpu-image-kernel-execution.md +0 -36
  266. package/docs/api/motion-subject-motion.md +23 -0
  267. package/docs/api/optics-generic-lens-lateral-color.md +79 -0
  268. package/docs/api/output-camera-channel-response.md +55 -0
  269. package/docs/api/output-developed-lens-corrections.md +10 -1
  270. package/docs/api/output-generic-lens-corrections.md +40 -4
  271. package/docs/api/output-print-plan.md +35 -1
  272. package/docs/api/output-sdr-rendering.md +6 -1
  273. package/docs/api/sensor-sensor-characteristics.md +54 -2
  274. package/llms.txt +3 -0
  275. package/package.json +6 -5
@@ -0,0 +1,93 @@
1
+ # capture/dng-opcodes.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## applyDngOpcodes
6
+
7
+ Applies an opcode list to a stage image in place (WarpRectilinear returns a new buffer in `image.data`). Returns one assumption
8
+ per opcode: what was applied, or why it was not.
9
+
10
+ ```ts
11
+ export function applyDngOpcodes(image: DngStageImage, opcodes: readonly DngOpcode[], options: ApplyDngOpcodesOptions): string[];
12
+ ```
13
+
14
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
15
+
16
+ ## ApplyDngOpcodesOptions
17
+
18
+ Options of {@link applyDngOpcodes}.
19
+
20
+ ```ts
21
+ export interface ApplyDngOpcodesOptions {
22
+ /** 1, 2 or 3: the list's stage (for the report). */
23
+ stage: 1 | 2 | 3;
24
+ /**
25
+ * WarpRectilinear resampling: false (full quality) uses cubic convolution (Keys, a = −0.75, the DNG SDK's kernel); true uses
26
+ * bilinear interpolation (fast). Registered as "dngImport.warpRectilinear" in SIMULATE_CAPTURE_FAST_METHODS.
27
+ */
28
+ fast: boolean;
29
+ /** 1, or 0.5 for a half-size image: pixel-coordinate parameters are scaled; pitched areas and bad pixels are then not applied. */
30
+ scale?: 1 | 0.5;
31
+ }
32
+ ```
33
+
34
+ ## DngOpcode
35
+
36
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
37
+
38
+ ```ts
39
+ export interface DngOpcode {
40
+ id: number;
41
+ /** The specification's name, or "Unknown". */
42
+ name: string;
43
+ dngVersion: [number, number, number, number];
44
+ /** Flag bit 0: a reader may skip it. */
45
+ optional: boolean;
46
+ /** Flag bit 1: may be skipped for preview-quality rendering. */
47
+ previewSkip: boolean;
48
+ /** Decoded parameters of an applied opcode; null for one this reader does not apply. */
49
+ parameters: DngOpcodeParameters | null;
50
+ }
51
+ ```
52
+
53
+ ## DngOpcodeArea
54
+
55
+ The rectangle, plane range and pitch an area-based opcode acts on (stage pixel coordinates, bottom and right exclusive).
56
+
57
+ ```ts
58
+ export interface DngOpcodeArea { top: number; left: number; bottom: number; right: number; plane: number; planes: number; rowPitch: number; colPitch: number }
59
+ ```
60
+
61
+ ## DngOpcodeParameters
62
+
63
+ Opcode parameters, by opcode.
64
+
65
+ ```ts
66
+ export type DngOpcodeParameters =
67
+ | { opcode: "WarpRectilinear"; planes: { radial: [number, number, number, number]; tangential: [number, number] }[]; centerX: number; centerY: number }
68
+ | { opcode: "FixVignetteRadial"; coefficients: [number, number, number, number, number]; centerX: number; centerY: number }
69
+ | { opcode: "FixBadPixelsConstant"; constant: number; bayerPhase: number }
70
+ | { opcode: "FixBadPixelsList"; bayerPhase: number; points: [number, number][]; rects: [number, number, number, number][] }
71
+ | { opcode: "MapTable"; area: DngOpcodeArea; table: Uint16Array }
72
+ | { opcode: "MapPolynomial"; area: DngOpcodeArea; coefficients: number[] }
73
+ | { opcode: "GainMap"; area: DngOpcodeArea; pointsV: number; pointsH: number; spacingV: number; spacingH: number; originV: number; originH: number; mapPlanes: number; gains: Float32Array }
74
+ | { opcode: "DeltaPerRow" | "DeltaPerColumn" | "ScalePerRow" | "ScalePerColumn"; area: DngOpcodeArea; values: Float32Array };
75
+ ```
76
+
77
+ ## DngStageImage
78
+
79
+ A stage image: interleaved (row, column, plane), normalized values.
80
+
81
+ ```ts
82
+ export interface DngStageImage { width: number; height: number; planes: number; data: Float32Array }
83
+ ```
84
+
85
+ ## parseDngOpcodeList
86
+
87
+ Parses an opcode list (the tag's bytes). Every count and length is checked against the bytes before allocation.
88
+
89
+ ```ts
90
+ export function parseDngOpcodeList(bytes: Uint8Array): DngOpcode[];
91
+ ```
92
+
93
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
@@ -0,0 +1,141 @@
1
+ # capture/dng-profile-maps.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## applyDngProfileGainTableMap
6
+
7
+ Applies ProfileGainTableMap(2): at each pixel the weight t = clamp(w·(R, G, B, min, max)·2^BaselineExposure, 0, 1)^γ selects a
8
+ gain by linear interpolation at t·N in the table (index clamped to N − 1, SDK), bilinear between map points at the pixel centre's
9
+ relative position (held at the map's edges); R, G and B are multiplied by it.
10
+
11
+ ```ts
12
+ export function applyDngProfileGainTableMap(input: ApplyDngProfileGainTableMapInput): void;
13
+ ```
14
+
15
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
16
+
17
+ ## ApplyDngProfileGainTableMapInput
18
+
19
+ Options of {@link applyDngProfileGainTableMap}.
20
+
21
+ ```ts
22
+ export interface ApplyDngProfileGainTableMapInput {
23
+ /** Linear ProPhoto (RIMM, D50) RGB, white-balanced, 1 = clipping white; changed in place. */
24
+ image: DngRgbImage;
25
+ map: DngProfileGainTableMap;
26
+ /** BaselineExposure (stops) not yet applied to `image`: the weight is scaled by 2^stops, as the SDK does. Default 0. */
27
+ baselineExposureStops?: number;
28
+ /** Where `image` sits in the stage-3 (ActiveArea) image the map spans. Default: `image` is the whole stage-3 image. */
29
+ region?: { x: number; y: number; stageWidth: number; stageHeight: number };
30
+ /** Keep values above 1 (true) or clip to 0..1 (false, the SDR default). */
31
+ overrange?: boolean;
32
+ }
33
+ ```
34
+
35
+ ## applyDngRgbTables
36
+
37
+ Applies RGBTables: each table maps ProPhoto RGB into its own primaries (clipped to 0..1, the clipped-off part kept and added back
38
+ when GamutExtension is "extend"), encodes it with the table's gamma, interpolates tetrahedrally in the 3D table, decodes and maps
39
+ back (SDK `RefRGBtoRGBTable3D`). The background table (empty name) applies to the whole image. Weighted sum: each masked table's
40
+ result weighted by its mask, the background (the background table's result, or the input) by 1 − min(1, Σ masks), normalized.
41
+ Sequential: each table in file order, blended into the running image by its mask (the background table by 1 − min(1, Σ other masks)).
42
+ A table whose mask is not supplied is skipped (as the SDK skips a name with no SemanticMask); the returned notes say so.
43
+
44
+ ```ts
45
+ export function applyDngRgbTables(input: ApplyDngRgbTablesInput): string[];
46
+ ```
47
+
48
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
49
+
50
+ ## ApplyDngRgbTablesInput
51
+
52
+ Options of {@link applyDngRgbTables}.
53
+
54
+ ```ts
55
+ export interface ApplyDngRgbTablesInput {
56
+ /** ProPhoto-primaries RGB after the tone curve, 0..1; changed in place. */
57
+ image: DngRgbImage;
58
+ tables: DngRgbTables;
59
+ /** Semantic masks by name (values 0..1, any size; stretched over the image with bilinear interpolation). */
60
+ masks?: readonly { name: string; width: number; height: number; data: Float32Array }[];
61
+ }
62
+ ```
63
+
64
+ ## DngProfileGainTableMap
65
+
66
+ A parsed ProfileGainTableMap(2): N gains at each of V × H map points, decoded to 32-bit floats.
67
+
68
+ ```ts
69
+ export interface DngProfileGainTableMap {
70
+ /** 1 for ProfileGainTableMap (52525), 2 for ProfileGainTableMap2 (52544). */
71
+ version: 1 | 2;
72
+ pointsV: number; pointsH: number; spacingV: number; spacingH: number; originV: number; originH: number;
73
+ /** MapPointsN: the table length at each map point. */
74
+ tablePoints: number;
75
+ /** MapInputWeights for red, green, blue, min(R,G,B) and max(R,G,B). */
76
+ inputWeights: [number, number, number, number, number];
77
+ /** PGTM2: the weight's gamma (1 for version 1). */
78
+ gamma: number;
79
+ /** Gains, row-major (v, h, table point). */
80
+ gains: Float32Array;
81
+ }
82
+ ```
83
+
84
+ ## DngRgbImage
85
+
86
+ An RGB image, interleaved (row, column, channel).
87
+
88
+ ```ts
89
+ export interface DngRgbImage { width: number; height: number; data: Float32Array }
90
+ ```
91
+
92
+ ## DngRgbTable
93
+
94
+ One table of RGBTables: a 3D table of `divisions`³ RGB samples (16-bit, 65535 = 1).
95
+
96
+ ```ts
97
+ export interface DngRgbTable {
98
+ /** TableSemanticName: the semantic mask it applies within; "" for the background table (the whole image). */
99
+ semanticName: string;
100
+ divisions: number;
101
+ gamma: "linear" | "srgb" | "1.8" | "2.2" | "rec2020";
102
+ primaries: "srgb" | "adobe-rgb" | "display-p3" | "rec2020" | "prophoto";
103
+ gamut: "clip" | "extend";
104
+ /** RGB per node, red slowest then green then blue, length divisions³ × 3. */
105
+ samples: Uint16Array;
106
+ }
107
+ ```
108
+
109
+ ## DngRgbTables
110
+
111
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
112
+
113
+ ```ts
114
+ export interface DngRgbTables { compositeMethod: "weighted-sum" | "sequential"; tables: DngRgbTable[] }
115
+ ```
116
+
117
+ ## parseDngProfileGainTableMap
118
+
119
+ Parses ProfileGainTableMap (version 1) or ProfileGainTableMap2 (version 2). Layout: MapPointsV, MapPointsH (LONG), MapSpacingV,
120
+ MapSpacingH, MapOriginV, MapOriginH (DOUBLE), MapPointsN (LONG), MapInputWeights (5 FLOAT); version 2 adds DataType (LONG: 0 uint8,
121
+ 1 uint16, 2 float16, 3 float32), Gamma, GainMin, GainMax (FLOAT); then the gains. Integer gains are GainMin + (x/max)·(GainMax −
122
+ GainMin). A single map point on an axis has spacing 1 and origin 0 (SDK).
123
+
124
+ ```ts
125
+ export function parseDngProfileGainTableMap(bytes: Uint8Array, littleEndian: boolean, version: 1 | 2): DngProfileGainTableMap;
126
+ ```
127
+
128
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
129
+
130
+ ## parseDngRgbTables
131
+
132
+ Parses RGBTables (52543): table count (LONG, at most 20; 0 means none), CompositeMethod (LONG: 0 weighted sum, 1 sequential), then
133
+ per table LengthTableSemanticName (SHORT), the name's bytes, Divisions, PixelType (0 uint8, 1 uint16, 2 float32), GammaEncoding
134
+ (0 linear, 1 sRGB, 2 γ 1.8, 3 γ 2.2, 4 Rec. 2020), ColorPrimaries (0 sRGB, 1 Adobe RGB, 2 P3, 3 Rec. 2020, 4 ProPhoto) and
135
+ GamutExtension (0 clip, 1 extend), each a BYTE, then Divisions³ RGB samples (uint8 scaled by 256, float scaled by 65535).
136
+
137
+ ```ts
138
+ export function parseDngRgbTables(bytes: Uint8Array, littleEndian: boolean): DngRgbTables | null;
139
+ ```
140
+
141
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
@@ -0,0 +1,43 @@
1
+ # capture/dng-stage-image.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## developDngStageImage
6
+
7
+ Builds the stage-2 (CFA) or stage-3 (LinearRaw) image of an imported DNG, normalized 0..1, interleaved by sample.
8
+
9
+ ```ts
10
+ export function developDngStageImage(raw: ImportedDngRaw, options: DevelopDngStageImageOptions = {}): DngStageImageResult;
11
+ ```
12
+
13
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
14
+
15
+ ## DevelopDngStageImageOptions
16
+
17
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
18
+
19
+ ```ts
20
+ export interface DevelopDngStageImageOptions {
21
+ /** The output the development is for ("srgb-8" or "srgb-16" on screen; "dng", "jpeg" or "png" exports). Default "srgb-8" (fast; owner, 2026-11-11). */
22
+ format?: "srgb-8" | "srgb-16" | "dng" | "jpeg" | "png";
23
+ /** The speed-versus-accuracy choice (WarpRectilinear resampling); default from `format` (fast on screen, full for exports). */
24
+ fast?: boolean;
25
+ /** Apply the opcode lists (default true). */
26
+ opcodes?: boolean;
27
+ }
28
+ ```
29
+
30
+ ## DngStageImageResult
31
+
32
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
33
+
34
+ ```ts
35
+ export interface DngStageImageResult extends DngStageImage {
36
+ /** 3 for LinearRaw (every opcode list applied), 2 for a CFA mosaic (OpcodeList3 left to the caller). */
37
+ stage: 2 | 3;
38
+ /** The resolved `fast` and where it came from. */
39
+ fast: { requested: boolean; defaultedFrom: "request" | "format" };
40
+ /** What was applied and what was not. */
41
+ assumptions: string[];
42
+ }
43
+ ```
@@ -0,0 +1,33 @@
1
+ # capture/jpeg-xl-tiles.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## createJxlOxideDngDecoder
6
+
7
+ A {@link DngJpegXlDecoder} over jxl-oxide-wasm: `forceSrgb` off (the samples stay in the codestream's encoding), one render per
8
+ tile, and the image object freed after it. Usage: `import init, * as jxl from "jxl-oxide-wasm"; await init();
9
+ createJxlOxideDngDecoder(jxl)`.
10
+
11
+ ```ts
12
+ export function createJxlOxideDngDecoder(module: JxlOxideModule): DngJpegXlDecoder;
13
+ ```
14
+
15
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
16
+
17
+ ## DngJpegXlDecoder
18
+
19
+ Decodes one JPEG XL codestream (a DNG strip or tile) to PNG bytes in the codestream's own colour encoding (no colour conversion).
20
+
21
+ ```ts
22
+ export type DngJpegXlDecoder = (codestream: Uint8Array) => Uint8Array | Promise<Uint8Array>;
23
+ ```
24
+
25
+ ## JxlOxideModule
26
+
27
+ The part of the jxl-oxide-wasm module the adapter uses (its `JxlImage` class, after the module's `init`).
28
+
29
+ ```ts
30
+ export interface JxlOxideModule {
31
+ JxlImage: new () => { feedBytes(bytes: Uint8Array): unknown; tryInit(): boolean; render(): { encodeToPng(): Uint8Array }; forceSrgb: boolean; free(): void };
32
+ }
33
+ ```
@@ -0,0 +1,41 @@
1
+ # capture/lossy-jpeg.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## LossyJpegImage
6
+
7
+ Decoded samples, interleaved by component within each line (line, x, component), 0..255.
8
+
9
+ ```ts
10
+ export interface LossyJpegImage {
11
+ width: number;
12
+ height: number;
13
+ components: number;
14
+ /** "ycbcr-to-rgb" when three YCbCr components were converted to RGB; "none" otherwise. */
15
+ colorTransform: "ycbcr-to-rgb" | "none";
16
+ samples: Uint8Array;
17
+ }
18
+ ```
19
+
20
+ ## LossyJpegLimits
21
+
22
+ The exact contract is declared below; use the domain guide for assumptions and staged integration.
23
+
24
+ ```ts
25
+ export interface LossyJpegLimits {
26
+ /** Largest width x height x components accepted, checked before any sample buffer is allocated. */
27
+ maximumSamples: number;
28
+ /** An abbreviated table-specification stream (SOI, DQT/DHT, EOI) read before `bytes`, as DNG's JPEGTables tag (347) supplies. */
29
+ tables?: Uint8Array;
30
+ }
31
+ ```
32
+
33
+ ## parseLossyJpeg
34
+
35
+ Decodes one DCT JPEG stream (see the module notes). Every length, count and table index is checked against the buffer.
36
+
37
+ ```ts
38
+ export function parseLossyJpeg(bytes: Uint8Array, limits: LossyJpegLimits): LossyJpegImage;
39
+ ```
40
+
41
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
@@ -136,7 +136,8 @@ RAW saturation handled before white balance. Counts are pixels or samples of the
136
136
 
137
137
  ```ts
138
138
  export interface RawHighlightDiagnostics {
139
- mode: RawHighlightMode;
139
+ /** #602: "reconstruct-neighbors" from `simulateCapture`'s whole-image neighbor reconstruction. */
140
+ mode: RawHighlightMode | "reconstruct-neighbors";
140
141
  /** Output pixels with at least one contributing site at or above its white level, or flagged as saturated. */
141
142
  saturatedPixelCount: number;
142
143
  /** `clip`: WB-scaled samples clipped at the smallest gain. Always 0 for `reconstruct`. */
@@ -0,0 +1,43 @@
1
+ # core/execution-error.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## ImageKernelExecutionError
6
+
7
+ Domain error for getting and running a backend. Input validation still throws the engine's existing errors.
8
+
9
+ ```ts
10
+ export class ImageKernelExecutionError extends Error {
11
+ readonly code = "IMAGE_KERNEL_EXECUTION";
12
+ readonly reason: ImageKernelFailureReason;
13
+
14
+ constructor(reason: ImageKernelFailureReason, message: string) {
15
+ super(message);
16
+ this.name = "ImageKernelExecutionError";
17
+ this.reason = reason;
18
+ }
19
+ }
20
+ ```
21
+
22
+ ## ImageKernelFailureReason
23
+
24
+ Failure classes reported by {@link ImageKernelExecutionError}. `conformance-failed` (#481): a device failed the display view's
25
+ run-time self-check with `gpu: "require"`. `quality-code-unavailable` (2.22.0, #594): on-demand full-quality code could not be
26
+ loaded (for example a failed chunk download); `prepareQualityPaths()` rejects with it, a display-view render with `gpu: "require"`
27
+ fails with it (with `gpu: "auto"` it runs the CPU reference), and a CPU request whose full-quality or opt-in code does not load
28
+ fails with it (nothing is remembered: the next request tries again).
29
+
30
+ ```ts
31
+ export type ImageKernelFailureReason = ImageKernelFallbackReason | "device-lost" | "out-of-memory" | "gpu-validation" |
32
+ "aborted" | "disposed" | "busy" | "unsupported-on-backend" | "unsupported-float32-domain" | "indeterminate-float32-classification" | "conformance-failed" |
33
+ "quality-code-unavailable";
34
+ ```
35
+
36
+ ## ImageKernelFallbackReason
37
+
38
+ Why a requested `webgpu-f32` backend ran the CPU reference instead. `webgpu-disabled`: `setWebGpuEnabled(false)` in this realm.
39
+
40
+ ```ts
41
+ export type ImageKernelFallbackReason = "webgpu-unavailable" | "webgpu-disabled" | "adapter-unavailable" | "device-request-failed" |
42
+ "insufficient-limits" | "unsupported-platform-endianness";
43
+ ```
@@ -11,5 +11,5 @@ version they were made with. Proof-of-concept (POC), schema and model contracts
11
11
  own versions. Release checks confirm it matches package.json and the packed package.
12
12
 
13
13
  ```ts
14
- ENGINE_API_VERSION = "2.20.0" as const
14
+ ENGINE_API_VERSION = "2.22.0" as const
15
15
  ```
@@ -4,11 +4,11 @@ The package and root API versions are recorded once in [Navigation](../API_REFER
4
4
 
5
5
  ## GENERIC_LENS_CATALOGUE
6
6
 
7
- The 30 lenses: 17 professional, then 13 consumer, each set by focal length.
7
+ The 33 lenses: 17 professional, 13 consumer and (#554) 3 phone lenses, each set by focal length.
8
8
 
9
9
  ```ts
10
10
  GENERIC_LENS_CATALOGUE: readonly GenericLensCatalogueEntry[] = Object.freeze([...PRO.map((row) => entry("professional", row)),
11
- ...CONSUMER.map((row) => entry("consumer", row))])
11
+ ...CONSUMER.map((row) => entry("consumer", row)), ...PHONE.map((row) => entry("phone", row))])
12
12
  ```
13
13
 
14
14
  ## GENERIC_LENS_IDS
@@ -30,7 +30,12 @@ GENERIC_LENS_SET_BASELINES: Readonly<Record<GenericLensSet, Readonly<GenericLens
30
30
  breathingFraction: -0.03, focusScatterDepthOfFocusFraction: 0.1, closeFocusFactor: 0.15 }),
31
31
  consumer: Object.freeze({ sphericalWaves: 0.5, comaWaves: 1.0, astigmatismWaves: 0.8, fieldCurvatureWaves: 0.45, axialColorWaves: 0.4,
32
32
  lateralColorFraction: 0.0003, distortionMagnitude: 0.03, naturalVignettingStops: 0.7, opticalVignettingStops: 1.1, veilingGlareFraction: 0.02, ghostFraction: 0.008,
33
- breathingFraction: -0.08, focusScatterDepthOfFocusFraction: 0.3, closeFocusFactor: 0.5 })
33
+ breathingFraction: -0.08, focusScatterDepthOfFocusFraction: 0.3, closeFocusFactor: 0.5 }),
34
+ // #554: design values (docs/LENS_CATALOGUE.md, "Phone lenses"): the consumer baseline with the stronger fall-off, flare and distortion
35
+ // (corrected in software on phones) of small fixed-aperture phone lenses, and no optical-vignetting change (the aperture is fixed).
36
+ phone: Object.freeze({ sphericalWaves: 0.5, comaWaves: 1.0, astigmatismWaves: 0.8, fieldCurvatureWaves: 0.5, axialColorWaves: 0.3,
37
+ lateralColorFraction: 0.0004, distortionMagnitude: 0.03, naturalVignettingStops: 1.2, opticalVignettingStops: 0.5, veilingGlareFraction: 0.03, ghostFraction: 0.01,
38
+ breathingFraction: -0.05, focusScatterDepthOfFocusFraction: 0.3, closeFocusFactor: 0.5 })
34
39
  })
35
40
  ```
36
41
 
@@ -179,10 +184,10 @@ export type GenericLensKind = "standard" | "macro" | "fisheye";
179
184
 
180
185
  ## GenericLensSet
181
186
 
182
- The exact contract is declared below; use the domain guide for assumptions and staged integration.
187
+ #554: "phone", the three fixed lenses of a phone sensor profile (`sensor.profile` with `fixedLenses: "phone"`).
183
188
 
184
189
  ```ts
185
- export type GenericLensSet = "professional" | "consumer";
190
+ export type GenericLensSet = "professional" | "consumer" | "phone";
186
191
  ```
187
192
 
188
193
  ## ResolvedGenericLens
@@ -0,0 +1,64 @@
1
+ # equipment/sensor-profile-body.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## SENSOR_PROFILE_ISO_ANCHOR
6
+
7
+ The engine's ISO anchor (see the module comment): white charge 60 000 e⁻ at ISO 100, 36 µm², QE 0.5.
8
+
9
+ ```ts
10
+ SENSOR_PROFILE_ISO_ANCHOR = Object.freeze({ iso: 100, fullWellElectrons: 60000, pixelAreaSquareMicrometers: 36, peakQuantumEfficiency: .5 })
11
+ ```
12
+
13
+ ## SensorProfile
14
+
15
+ One sensor profile. Every number is per photosite unless stated; see `provenance` for each value's kind.
16
+
17
+ ```ts
18
+ export interface SensorProfile {
19
+ id: string;
20
+ /** Format, resolution, architecture and era; no maker or product name. */
21
+ name: string;
22
+ /** A market category, not a specification: it is a label and changes nothing. */
23
+ marketTag: "consumer" | "professional";
24
+ architecture: "front-illuminated" | "back-illuminated" | "stacked";
25
+ sensorWidthMm: number; sensorHeightMm: number;
26
+ pixelsWide: number; pixelsHigh: number;
27
+ pixelPitchMicrometers: number;
28
+ /** The green channel's peak EQE (#555 `peakQuantumEfficiency`). */
29
+ peakQuantumEfficiency: number;
30
+ /** Full-well capacity, e⁻: the charge at the RAW white level at base ISO. */
31
+ fullWellElectrons: number;
32
+ /** Read noise, RMS e⁻; `highGainFromIso` null for one conversion gain throughout. */
33
+ readNoise: { lowGainElectrons: number; highGainElectrons: number; highGainFromIso: number | null };
34
+ /** Charge storage limit of the high conversion gain, e⁻ (null without one): full well × base ISO / switch ISO. */
35
+ highGainFullWellElectrons: number | null;
36
+ adcBitDepth: 12 | 14 | 16;
37
+ /** Re-rated base ISO (see the module comment) and the highest ISO setting. */
38
+ baseIso: number; maximumIso: number;
39
+ /** Largest analog gain relative to base ISO; above it the gain is digital. */
40
+ maximumAnalogGain: number;
41
+ /** Rolling readout, s: the top-to-bottom skew with `motion.shutter: "rolling"`. */
42
+ readoutSeconds: number;
43
+ /** Fixed-pattern noise used with `sensor.fixedPatternNoise` (EMVA 1288 Release 4.0 non-uniformities). */
44
+ fixedPattern: { prnu: number; dsnu: number; hotPixelFraction: number; hotPixelMultiplier: number; rowElectrons: number; columnElectrons: number };
45
+ /** What the profile is good for (text only). */
46
+ goodFor: readonly string[];
47
+ description: string;
48
+ /** "phone": the profile takes only the catalogue's phone lenses (`lens.catalogue`), which need it. */
49
+ fixedLenses: "phone" | null;
50
+ /**
51
+ * Each value's kind and source key (S1–S10, docs/SENSOR_PROFILES.md "Sources"); a value not listed is a design value.
52
+ * For example `fullWellElectrons: "published S1"` or `baseIso: "derived"`.
53
+ */
54
+ provenance: Readonly<Partial<Record<keyof SensorProfile | "sensorSize" | "pixels" | "highGainFromIso", string>>>;
55
+ }
56
+ ```
57
+
58
+ ## SensorProfileValueKind
59
+
60
+ How a profile value was obtained: copied from a source, calculated from sourced values, or Photivra's design value.
61
+
62
+ ```ts
63
+ export type SensorProfileValueKind = "published" | "derived" | "design";
64
+ ```
@@ -0,0 +1,86 @@
1
+ # equipment/sensor-profiles.ts public contracts
2
+
3
+ The package and root API versions are recorded once in [Navigation](../API_REFERENCE.md) · [Developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md). Generated signatures retain independent schema/model versions. Only the exports listed here are root-package contracts; module-local helpers are not supported deep imports.
4
+
5
+ ## resolveSensorProfile
6
+
7
+ The profile record with this id (for `sensor.profile`); a clear error listing the ids otherwise.
8
+
9
+ ```ts
10
+ export function resolveSensorProfile(id: unknown): SensorProfile;
11
+ ```
12
+
13
+ This call is synchronous; any supplied provider must follow its explicit synchronous contract.
14
+
15
+ ## SENSOR_PROFILE_IDS
16
+
17
+ The profile ids, in catalogue order.
18
+
19
+ ```ts
20
+ SENSOR_PROFILE_IDS: readonly string[] = Object.freeze(SENSOR_PROFILES.map((p) => p.id))
21
+ ```
22
+
23
+ ## SENSOR_PROFILES
24
+
25
+ The catalogue, in the issue's order. Frozen.
26
+
27
+ ```ts
28
+ SENSOR_PROFILES: readonly SensorProfile[] = Object.freeze(([
29
+ { id: "full-frame-24mp-bsi-2020s", name: "Full frame 24 MP BSI, 2020s", marketTag: "consumer", architecture: "back-illuminated",
30
+ sensorWidthMm: 36.07, sensorHeightMm: 24.01, pixelsWide: 6072, pixelsHigh: 4042, pixelPitchMicrometers: 5.94, peakQuantumEfficiency: .8,
31
+ fullWellElectrons: 100000, readNoise: { lowGainElectrons: 6.4, highGainElectrons: 1.1, highGainFromIso: 500 }, highGainFullWellElectrons: 20000,
32
+ adcBitDepth: 14, baseIso: 100, maximumIso: 51200, maximumAnalogGain: 64, readoutSeconds: .05, fixedPattern: BSI,
33
+ goodFor: ["landscape", "portrait", "travel"], description: "Large pixels and a deep full well for wide dynamic range at base ISO; a moderate readout shows some rolling-shutter skew.",
34
+ fixedLenses: null, provenance: { ...sourced("S1", "pixels", "pixelPitchMicrometers", "peakQuantumEfficiency", "fullWellElectrons", "readNoise", "adcBitDepth"),
35
+ sensorSize: "derived", architecture: "published S1", highGainFromIso: "derived S1", highGainFullWellElectrons: "derived", baseIso: "derived" } },
36
+ { id: "full-frame-45mp-stacked-2020s", name: "Full frame 45 MP stacked, 2020s", marketTag: "professional", architecture: "stacked",
37
+ sensorWidthMm: 36, sensorHeightMm: 24, pixelsWide: 8256, pixelsHigh: 5504, pixelPitchMicrometers: 4.36, peakQuantumEfficiency: .8,
38
+ fullWellElectrons: 69000, readNoise: { lowGainElectrons: 3.5, highGainElectrons: 1.5, highGainFromIso: 250 }, highGainFullWellElectrons: 22080,
39
+ adcBitDepth: 14, baseIso: 80, maximumIso: 25600, maximumAnalogGain: 64, readoutSeconds: .005, fixedPattern: BSI,
40
+ goodFor: ["landscape", "studio", "wildlife"], description: "High resolution with a fast stacked readout: fine detail for landscape and studio work, little rolling-shutter skew for action.",
41
+ fixedLenses: null, provenance: { pixelPitchMicrometers: "derived", fullWellElectrons: "design (S2 scaled by pixel area)", readNoise: "design (S2)", highGainFromIso: "design (S2)",
42
+ highGainFullWellElectrons: "derived", baseIso: "derived" } },
43
+ { id: "full-frame-24mp-stacked-sports-2020s", name: "Full frame 24 MP stacked (sports), 2020s", marketTag: "professional", architecture: "stacked",
44
+ sensorWidthMm: 36, sensorHeightMm: 24, pixelsWide: 6000, pixelsHigh: 4000, pixelPitchMicrometers: 6, peakQuantumEfficiency: .8,
45
+ fullWellElectrons: 80000, readNoise: { lowGainElectrons: 3, highGainElectrons: 1, highGainFromIso: 400 }, highGainFullWellElectrons: 25000,
46
+ adcBitDepth: 14, baseIso: 125, maximumIso: 102400, maximumAnalogGain: 64, readoutSeconds: .0025, fixedPattern: BSI,
47
+ goodFor: ["sports", "wildlife", "journalism"], description: "The fastest readout and low high-gain read noise: almost no rolling-shutter skew and clean high ISO, at a moderate resolution.",
48
+ fixedLenses: null, provenance: { pixelPitchMicrometers: "derived", highGainFullWellElectrons: "derived", baseIso: "derived" } },
49
+ { id: "full-frame-24mp-fsi-2010s", name: "Full frame 24 MP front-illuminated, 2010s", marketTag: "professional", architecture: "front-illuminated",
50
+ sensorWidthMm: 36.01, sensorHeightMm: 24.07, pixelsWide: 6032, pixelsHigh: 4032, pixelPitchMicrometers: 5.97, peakQuantumEfficiency: .53,
51
+ fullWellElectrons: 76000, readNoise: { lowGainElectrons: 4, highGainElectrons: 4, highGainFromIso: null }, highGainFullWellElectrons: null,
52
+ adcBitDepth: 14, baseIso: 80, maximumIso: 25600, maximumAnalogGain: 64, readoutSeconds: .08, fixedPattern: FSI,
53
+ goodFor: ["portrait", "landscape", "journalism"], description: "An older professional sensor: lower QE and one conversion gain, so a modern consumer BSI sensor beats it at high ISO; slow readout.",
54
+ fixedLenses: null, provenance: { ...sourced("S3", "pixels", "pixelPitchMicrometers", "peakQuantumEfficiency", "fullWellElectrons", "adcBitDepth"),
55
+ sensorSize: "derived", architecture: "derived S4", baseIso: "derived" } },
56
+ { id: "aps-c-24mp-fsi-2020s", name: "APS-C 24 MP front-illuminated, 2020s", marketTag: "consumer", architecture: "front-illuminated",
57
+ sensorWidthMm: 23.55, sensorHeightMm: 15.73, pixelsWide: 6024, pixelsHigh: 4024, pixelPitchMicrometers: 3.91, peakQuantumEfficiency: .55,
58
+ fullWellElectrons: 36000, readNoise: { lowGainElectrons: 2.7, highGainElectrons: 2.7, highGainFromIso: null }, highGainFullWellElectrons: null,
59
+ adcBitDepth: 14, baseIso: 80, maximumIso: 25600, maximumAnalogGain: 64, readoutSeconds: .06, fixedPattern: FSI,
60
+ goodFor: ["travel", "family", "learning"], description: "A smaller, front-illuminated sensor: less light per pixel, a slower readout and more rolling-shutter skew.",
61
+ fixedLenses: null, provenance: { ...sourced("S5", "pixels", "pixelPitchMicrometers", "architecture", "fullWellElectrons", "readNoise", "adcBitDepth"), sensorSize: "derived", baseIso: "derived" } },
62
+ { id: "aps-c-26mp-bsi-2020s", name: "APS-C 26 MP BSI, 2020s", marketTag: "professional", architecture: "back-illuminated",
63
+ sensorWidthMm: 23.49, sensorHeightMm: 15.7, pixelsWide: 6248, pixelsHigh: 4176, pixelPitchMicrometers: 3.76, peakQuantumEfficiency: .8,
64
+ fullWellElectrons: 50000, readNoise: { lowGainElectrons: 3.3, highGainElectrons: 1, highGainFromIso: 250 }, highGainFullWellElectrons: 16000,
65
+ adcBitDepth: 14, baseIso: 80, maximumIso: 25600, maximumAnalogGain: 64, readoutSeconds: .025, fixedPattern: BSI,
66
+ goodFor: ["wildlife", "sports", "travel"], description: "A BSI APS-C sensor with dual conversion gain whose readout beats the consumer full-frame profile's.",
67
+ fixedLenses: null, provenance: { ...sourced("S6", "pixels", "pixelPitchMicrometers", "peakQuantumEfficiency", "fullWellElectrons", "readNoise"), sensorSize: "derived",
68
+ architecture: "published S6", highGainFromIso: "design (S7)", highGainFullWellElectrons: "derived", baseIso: "derived" } },
69
+ { id: "medium-format-100mp-bsi-16bit", name: "Medium format 100 MP BSI, 16-bit", marketTag: "professional", architecture: "back-illuminated",
70
+ sensorWidthMm: 43.856, sensorHeightMm: 32.9, pixelsWide: 11656, pixelsHigh: 8750, pixelPitchMicrometers: 3.76, peakQuantumEfficiency: .8,
71
+ fullWellElectrons: 50300, readNoise: { lowGainElectrons: 3.3, highGainElectrons: 1, highGainFromIso: 250 }, highGainFullWellElectrons: 16096,
72
+ adcBitDepth: 16, baseIso: 80, maximumIso: 12800, maximumAnalogGain: 64, readoutSeconds: .25, fixedPattern: BSI,
73
+ goodFor: ["studio", "landscape", "portrait"], description: "A large sensor with 100 MP and a 16-bit RAW; a slow readout makes its electronic shutter unsuited to motion.",
74
+ fixedLenses: null, provenance: { ...sourced("S7", "pixels", "pixelPitchMicrometers", "sensorSize", "fullWellElectrons", "readNoise", "adcBitDepth"), architecture: "published S8",
75
+ highGainFromIso: "derived S7", highGainFullWellElectrons: "derived", baseIso: "derived" } },
76
+ { id: "phone-50mp-stacked", name: "Phone, large, 50 MP stacked", marketTag: "consumer", architecture: "stacked",
77
+ sensorWidthMm: 9.844, sensorHeightMm: 7.431, pixelsWide: 8192, pixelsHigh: 6144, pixelPitchMicrometers: 1.197, peakQuantumEfficiency: .75,
78
+ fullWellElectrons: 8600, readNoise: { lowGainElectrons: 3, highGainElectrons: 1.4, highGainFromIso: 160 }, highGainFullWellElectrons: 2150,
79
+ adcBitDepth: 12, baseIso: 40, maximumIso: 6400, maximumAnalogGain: 64, readoutSeconds: 1 / 30,
80
+ fixedPattern: { prnu: .008, dsnu: .3, hotPixelFraction: 2e-4, hotPixelMultiplier: 50, rowElectrons: .2, columnElectrons: .2 },
81
+ goodFor: ["everyday", "travel", "family"], description: "A large phone sensor: tiny pixels with a small full well, so less dynamic range and more noise per pixel; simulated as a single frame (no multi-frame stacking).",
82
+ fixedLenses: "phone", provenance: { ...sourced("S9", "pixels", "pixelPitchMicrometers", "sensorSize", "architecture", "adcBitDepth"), fullWellElectrons: "design (S10 scaled by pixel area)",
83
+ readNoise: "high gain published S10, low gain design", highGainFullWellElectrons: "derived", readoutSeconds: "derived S9 (1 / full-resolution frame rate)",
84
+ fixedPattern: "PRNU, row and column published S10; the rest design", baseIso: "derived" } }
85
+ ] satisfies SensorProfile[]).map((p) => Object.freeze(p)))
86
+ ```