@photivra/engine 2.15.0 → 2.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -1
- package/CITATION.cff +1 -1
- package/README.md +6 -6
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +14 -0
- package/dist/capture/native-environment-lean.js +1 -1
- package/dist/capture/native-environment-lean.js.map +1 -1
- package/dist/capture/native-raw-development.d.ts +15 -0
- package/dist/capture/native-raw-development.js +12 -6
- package/dist/capture/native-raw-development.js.map +1 -1
- package/dist/capture/raw-development-kernel.d.ts +10 -3
- package/dist/capture/raw-development-kernel.js +25 -6
- package/dist/capture/raw-development-kernel.js.map +1 -1
- package/dist/core/internal-radiance-evaluator.d.ts +5 -2
- package/dist/core/internal-radiance-evaluator.js.map +1 -1
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/core/version.js.map +1 -1
- package/dist/facade/defocus-image-space-provider.d.ts +109 -0
- package/dist/facade/defocus-image-space-provider.js +359 -0
- package/dist/facade/defocus-image-space-provider.js.map +1 -0
- package/dist/facade/diffraction-provider.d.ts +16 -0
- package/dist/facade/diffraction-provider.js +3 -3
- package/dist/facade/diffraction-provider.js.map +1 -1
- package/dist/facade/display-view.d.ts +21 -0
- package/dist/facade/display-view.js +66 -8
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +111 -2
- package/dist/facade/simulate-capture.js +220 -44
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.js +7 -3
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/display-view-quality-gpu.d.ts +2 -0
- package/dist/gpu/display-view-quality-gpu.js +57 -13
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/display-view-speed-gpu.d.ts +2 -0
- package/dist/gpu/display-view-speed-gpu.js +45 -16
- package/dist/gpu/display-view-speed-gpu.js.map +1 -1
- package/dist/gpu/image-kernel-accelerator.js +3 -0
- package/dist/gpu/image-kernel-accelerator.js.map +1 -1
- package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +4 -0
- package/dist/gpu/wgsl-display-view-quality-kernel.js +53 -2
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-speed-kernel.d.ts +15 -0
- package/dist/gpu/wgsl-display-view-speed-kernel.js +42 -1
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/optics/defocus-disc-kernel.d.ts +3 -2
- package/dist/optics/defocus-disc-kernel.js +3 -2
- package/dist/optics/defocus-disc-kernel.js.map +1 -1
- package/dist/optics/sensor-environment-query.d.ts +31 -0
- package/dist/optics/sensor-environment-query.js +45 -13
- package/dist/optics/sensor-environment-query.js.map +1 -1
- package/dist/processing/development-detail.d.ts +132 -0
- package/dist/processing/development-detail.js +325 -0
- package/dist/processing/development-detail.js.map +1 -0
- package/docs/API_REFERENCE.md +6 -1
- package/docs/QUICK_START.md +190 -12
- package/docs/USAGE.md +14 -3
- package/docs/api/capture-native-raw-development.md +16 -0
- package/docs/api/core-version.md +1 -1
- package/docs/api/exports.json +27 -2
- package/docs/api/facade-simulate-capture.md +128 -3
- package/docs/api/optics-defocus-disc-kernel.md +3 -2
- package/package.json +1 -1
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* Development detail stages (#552), both opt-in and off by default:
|
|
4
|
+
*
|
|
5
|
+
* - **Diffraction correction**: a bounded, regularized (Wiener–Tikhonov) deconvolution of the developed linear RGB with the
|
|
6
|
+
* diffraction OTF of an ideal circular pupil at the capture's working f-number. The OTF is zero beyond the cut-off
|
|
7
|
+
* f_c = 1/(λ·N_w), so detail beyond it never reached the sensor; the filter's design gain there is 0 and it cannot, and
|
|
8
|
+
* does not claim to, restore it.
|
|
9
|
+
* - **Sharpening**: an unsharp mask (radius, amount, threshold) on the display-encoded values, after the tone curve and
|
|
10
|
+
* gamut handling and before quantization. It raises edge contrast and creates halos; it adds no optical detail.
|
|
11
|
+
*
|
|
12
|
+
* Development order with these stages: (exposure gain) → demosaic → colour and white balance → diffraction correction
|
|
13
|
+
* (linear) → rendering exposure and tone curve → gamut handling → sRGB transfer → unsharp mask → quantization (encode).
|
|
14
|
+
*
|
|
15
|
+
* Image edges are handled by clamping (edge pixels repeated). Both stages work on a whole in-memory image.
|
|
16
|
+
* @see docs/DIGITAL_OPTICS_FOUNDATION.md for the equations, sources, trade-offs and limits.
|
|
17
|
+
*/
|
|
18
|
+
import { encodeSrgbComponent } from "../color/srgb-transfer.js";
|
|
19
|
+
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
20
|
+
import { InvalidScientificResultError } from "../core/validation.js";
|
|
21
|
+
import { prepareSdrPixelTone } from "../output/sdr-rendering.js";
|
|
22
|
+
/** Version of the `development-detail` model (diffraction correction and unsharp mask). */
|
|
23
|
+
export const DEVELOPMENT_DETAIL_MODEL_VERSION = "0.1.0";
|
|
24
|
+
/** Accepted ranges. Bounds are engineering limits on cost and on noise gain, not quality recommendations. */
|
|
25
|
+
export const DEVELOPMENT_DETAIL_LIMITS = /* @__PURE__ */ Object.freeze({
|
|
26
|
+
maximumUnsharpRadiusPixels: 8, maximumUnsharpAmount: 5,
|
|
27
|
+
minimumWavelengthNm: 380, maximumWavelengthNm: 780,
|
|
28
|
+
minimumRegularization: 1e-4, maximumRegularization: 1,
|
|
29
|
+
maximumGain: 10, maximumKernelRadiusPixels: 16
|
|
30
|
+
});
|
|
31
|
+
/** Third dark ring of the Airy pattern, in units of λ·N (j₁,₃/π, j₁,₃ = 10.1734681…). */
|
|
32
|
+
const AIRY_THIRD_ZERO_PER_LAMBDA_N = 10.173468135062722 / Math.PI;
|
|
33
|
+
function finiteIn(value, name, minimum, maximum, minimumExclusive = false) {
|
|
34
|
+
if (typeof value !== "number" || !Number.isFinite(value) || (minimumExclusive ? value <= minimum : value < minimum) || value > maximum) {
|
|
35
|
+
throw new InvalidConfigurationError(name + " must be a finite number in " + (minimumExclusive ? "(" : "[") + minimum + ", " + maximum + "].");
|
|
36
|
+
}
|
|
37
|
+
return value;
|
|
38
|
+
}
|
|
39
|
+
/** Validates unsharp mask settings. */
|
|
40
|
+
export function parseUnsharpMaskSettings(value, path = "sharpening") {
|
|
41
|
+
return { radiusPixels: finiteIn(value.radiusPixels, path + ".radiusPixels", 0, DEVELOPMENT_DETAIL_LIMITS.maximumUnsharpRadiusPixels, true),
|
|
42
|
+
amount: finiteIn(value.amount, path + ".amount", 0, DEVELOPMENT_DETAIL_LIMITS.maximumUnsharpAmount, true),
|
|
43
|
+
threshold: finiteIn(value.threshold, path + ".threshold", 0, 1) };
|
|
44
|
+
}
|
|
45
|
+
/** Validates diffraction correction settings. */
|
|
46
|
+
export function parseDiffractionCorrectionSettings(value, path = "diffractionCorrection") {
|
|
47
|
+
const radius = value.kernelRadiusPixels;
|
|
48
|
+
if (typeof radius !== "number" || !Number.isSafeInteger(radius) || radius < 1 || radius > DEVELOPMENT_DETAIL_LIMITS.maximumKernelRadiusPixels) {
|
|
49
|
+
throw new InvalidConfigurationError(path + ".kernelRadiusPixels must be an integer from 1 to " + DEVELOPMENT_DETAIL_LIMITS.maximumKernelRadiusPixels + ".");
|
|
50
|
+
}
|
|
51
|
+
return { wavelengthNm: finiteIn(value.wavelengthNm, path + ".wavelengthNm", DEVELOPMENT_DETAIL_LIMITS.minimumWavelengthNm, DEVELOPMENT_DETAIL_LIMITS.maximumWavelengthNm),
|
|
52
|
+
regularization: finiteIn(value.regularization, path + ".regularization", DEVELOPMENT_DETAIL_LIMITS.minimumRegularization, DEVELOPMENT_DETAIL_LIMITS.maximumRegularization),
|
|
53
|
+
maximumGain: finiteIn(value.maximumGain, path + ".maximumGain", 1, DEVELOPMENT_DETAIL_LIMITS.maximumGain), kernelRadiusPixels: radius };
|
|
54
|
+
}
|
|
55
|
+
/** Default FIR radius: the Airy pattern's third dark ring (3.2383·λ·N_w) in pixels, rounded up, within 1..16. */
|
|
56
|
+
export function defaultDiffractionCorrectionRadiusPixels(workingFNumber, wavelengthNm, pixelPitchMicrometers) {
|
|
57
|
+
const sites = Math.ceil(AIRY_THIRD_ZERO_PER_LAMBDA_N * wavelengthNm / 1000 * workingFNumber / pixelPitchMicrometers);
|
|
58
|
+
return Math.min(DEVELOPMENT_DETAIL_LIMITS.maximumKernelRadiusPixels, Math.max(1, sites));
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Incoherent OTF of an ideal, aberration-free circular pupil at normalized frequency s = f/f_c:
|
|
62
|
+
* H(s) = (2/π)(acos s − s·√(1 − s²)) for s < 1, and 0 for s ≥ 1 (MIT OCW 2.71, lectures 21–22; Goodman, §7.3).
|
|
63
|
+
*/
|
|
64
|
+
export function circularPupilDiffractionMtf(normalizedFrequency) {
|
|
65
|
+
const s = Math.abs(normalizedFrequency);
|
|
66
|
+
if (s >= 1)
|
|
67
|
+
return 0;
|
|
68
|
+
return 2 / Math.PI * (Math.acos(s) - s * Math.sqrt(1 - s * s));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Designs the bounded Wiener–Tikhonov correction. In frequency (normalized s = ρ/f_c, ρ the radial frequency):
|
|
72
|
+
* G(s) = min(maximumGain, H(s)·(1 + α)/(H(s)² + α)), G(0) = 1, and G(s) = 0 for s ≥ 1,
|
|
73
|
+
* where α is the regularization (an assumed noise-to-signal power ratio, Wiener's K). The response is sampled on an M × M
|
|
74
|
+
* grid (M a power of two ≥ max(64, 4(2R + 1))), inverse transformed, truncated to the (2R + 1)² window with a radial raised
|
|
75
|
+
* cosine taper and renormalized to unit sum. The achieved response is then measured, not assumed.
|
|
76
|
+
*/
|
|
77
|
+
export function designDiffractionCorrectionFilter(input) {
|
|
78
|
+
const settings = parseDiffractionCorrectionSettings(input.settings);
|
|
79
|
+
const workingFNumber = finiteIn(input.workingFNumber, "workingFNumber", 0, 1e4, true), pitch = finiteIn(input.pixelPitchMicrometers, "pixelPitchMicrometers", 0, 1e4, true);
|
|
80
|
+
const cutoff = pitch / (settings.wavelengthNm / 1000 * workingFNumber), alpha = settings.regularization, bound = settings.maximumGain;
|
|
81
|
+
const gainAt = (rho) => {
|
|
82
|
+
const h = circularPupilDiffractionMtf(rho / cutoff);
|
|
83
|
+
return h === 0 ? 0 : Math.min(bound, h * (1 + alpha) / (h * h + alpha));
|
|
84
|
+
};
|
|
85
|
+
const R = settings.kernelRadiusPixels, size = 2 * R + 1;
|
|
86
|
+
let M = 64;
|
|
87
|
+
while (M < 4 * size)
|
|
88
|
+
M *= 2;
|
|
89
|
+
// A(ky, x) = Σ_kx G cos(2π kx x/M); h(x, y) = (1/M²) Σ_ky cos(2π ky y/M) A(ky, x). G is even in each axis, so h is real and even.
|
|
90
|
+
const cosTable = new Float64Array(M * (R + 1));
|
|
91
|
+
for (let k = 0; k < M; k++)
|
|
92
|
+
for (let x = 0; x <= R; x++)
|
|
93
|
+
cosTable[k * (R + 1) + x] = Math.cos(2 * Math.PI * k * x / M);
|
|
94
|
+
const A = new Float64Array(M * (R + 1));
|
|
95
|
+
for (let ky = 0; ky < M; ky++) {
|
|
96
|
+
const fy = (ky < M / 2 ? ky : ky - M) / M;
|
|
97
|
+
for (let kx = 0; kx < M; kx++) {
|
|
98
|
+
const fx = (kx < M / 2 ? kx : kx - M) / M, g = gainAt(Math.hypot(fx, fy));
|
|
99
|
+
if (g === 0)
|
|
100
|
+
continue;
|
|
101
|
+
for (let x = 0; x <= R; x++)
|
|
102
|
+
A[ky * (R + 1) + x] += g * cosTable[kx * (R + 1) + x];
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const quarter = new Float64Array((R + 1) * (R + 1));
|
|
106
|
+
for (let y = 0; y <= R; y++)
|
|
107
|
+
for (let x = 0; x <= R; x++) {
|
|
108
|
+
let sum = 0;
|
|
109
|
+
for (let ky = 0; ky < M; ky++)
|
|
110
|
+
sum += cosTable[ky * (R + 1) + y] * A[ky * (R + 1) + x];
|
|
111
|
+
const r = Math.hypot(x, y), taper = r < R + 1 ? 0.5 * (1 + Math.cos(Math.PI * r / (R + 1))) : 0;
|
|
112
|
+
quarter[y * (R + 1) + x] = sum / (M * M) * taper;
|
|
113
|
+
}
|
|
114
|
+
const weights = new Float64Array(size * size);
|
|
115
|
+
let total = 0;
|
|
116
|
+
for (let dy = -R; dy <= R; dy++)
|
|
117
|
+
for (let dx = -R; dx <= R; dx++) {
|
|
118
|
+
const w = quarter[Math.abs(dy) * (R + 1) + Math.abs(dx)];
|
|
119
|
+
weights[(dy + R) * size + dx + R] = w;
|
|
120
|
+
total += w;
|
|
121
|
+
}
|
|
122
|
+
if (!(total > 0) || !Number.isFinite(total))
|
|
123
|
+
throw new InvalidScientificResultError("Diffraction correction filter has no finite DC response.");
|
|
124
|
+
for (let i = 0; i < weights.length; i++)
|
|
125
|
+
weights[i] = weights[i] / total;
|
|
126
|
+
let designPeakGain = 0;
|
|
127
|
+
for (let i = 0; i <= 4096; i++)
|
|
128
|
+
designPeakGain = Math.max(designPeakGain, gainAt(i / 4096 * cutoff));
|
|
129
|
+
let responsePeakGain = 0, beyond = null, beyondCount = 0;
|
|
130
|
+
const GRID = 64;
|
|
131
|
+
for (let iy = 0; iy <= GRID / 2; iy++)
|
|
132
|
+
for (let ix = 0; ix <= GRID / 2; ix++) {
|
|
133
|
+
const fx = ix / GRID, fy = iy / GRID, response = Math.abs(evaluateDiffractionCorrectionResponse(weights, R, fx, fy));
|
|
134
|
+
responsePeakGain = Math.max(responsePeakGain, response);
|
|
135
|
+
if (Math.hypot(fx, fy) >= cutoff)
|
|
136
|
+
beyond = Math.max(beyond ?? 0, response);
|
|
137
|
+
}
|
|
138
|
+
const AREA = 256;
|
|
139
|
+
for (let iy = 0; iy < AREA; iy++)
|
|
140
|
+
for (let ix = 0; ix < AREA; ix++)
|
|
141
|
+
if (Math.hypot((ix + 0.5) / (2 * AREA), (iy + 0.5) / (2 * AREA)) >= cutoff)
|
|
142
|
+
beyondCount++;
|
|
143
|
+
return { settings, workingFNumber, pixelPitchMicrometers: pitch, cutoffCyclesPerPixel: cutoff, nyquistAreaBeyondCutoffFraction: beyondCount / (AREA * AREA),
|
|
144
|
+
designPeakGain, responsePeakGain, responseMaximumBeyondCutoff: beyond, weights };
|
|
145
|
+
}
|
|
146
|
+
/** Frequency response Σ w(x, y)·cos(2π(fx·x + fy·y)) of a centred (2R + 1)² FIR at (fx, fy) cycles per pixel. */
|
|
147
|
+
export function evaluateDiffractionCorrectionResponse(weights, radius, fx, fy) {
|
|
148
|
+
const size = 2 * radius + 1;
|
|
149
|
+
let sum = 0;
|
|
150
|
+
for (let dy = -radius; dy <= radius; dy++)
|
|
151
|
+
for (let dx = -radius; dx <= radius; dx++)
|
|
152
|
+
sum += weights[(dy + radius) * size + dx + radius] * Math.cos(2 * Math.PI * (fx * dx + fy * dy));
|
|
153
|
+
return sum;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Applies the FIR to interleaved linear RGB (width × height × 3), each channel separately, edges clamped. Returns new samples.
|
|
157
|
+
*/
|
|
158
|
+
export function applyDiffractionCorrection(rgb, width, height, filter) {
|
|
159
|
+
if (rgb.length !== width * height * 3)
|
|
160
|
+
throw new InvalidConfigurationError("Diffraction correction raster size mismatch.");
|
|
161
|
+
const R = filter.settings.kernelRadiusPixels, size = 2 * R + 1, w = filter.weights, out = new Float64Array(rgb.length);
|
|
162
|
+
const xs = new Int32Array(width + 2 * R);
|
|
163
|
+
for (let i = 0; i < xs.length; i++)
|
|
164
|
+
xs[i] = Math.min(width - 1, Math.max(0, i - R)) * 3;
|
|
165
|
+
let negative = 0, above = 0;
|
|
166
|
+
for (let y = 0; y < height; y++) {
|
|
167
|
+
const o = y * width * 3;
|
|
168
|
+
for (let dy = -R; dy <= R; dy++) {
|
|
169
|
+
const row = Math.min(height - 1, Math.max(0, y + dy)) * width * 3, wr = (dy + R) * size;
|
|
170
|
+
for (let dx = 0; dx < size; dx++) {
|
|
171
|
+
const weight = w[wr + dx];
|
|
172
|
+
if (weight === 0)
|
|
173
|
+
continue;
|
|
174
|
+
for (let x = 0; x < width; x++) {
|
|
175
|
+
const s = row + xs[x + dx], d = o + 3 * x;
|
|
176
|
+
out[d] = out[d] + weight * rgb[s];
|
|
177
|
+
out[d + 1] = out[d + 1] + weight * rgb[s + 1];
|
|
178
|
+
out[d + 2] = out[d + 2] + weight * rgb[s + 2];
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
for (let i = 0; i < out.length; i++) {
|
|
184
|
+
const v = out[i];
|
|
185
|
+
if (!Number.isFinite(v))
|
|
186
|
+
throw new InvalidScientificResultError("Diffraction correction produced a non-finite sample.");
|
|
187
|
+
if (v < 0)
|
|
188
|
+
negative++;
|
|
189
|
+
else if (v > 1)
|
|
190
|
+
above++;
|
|
191
|
+
}
|
|
192
|
+
return { samples: out, diagnostics: { negativeSampleCount: negative, aboveWhiteSampleCount: above } };
|
|
193
|
+
}
|
|
194
|
+
/** Normalized Gaussian weights exp(−i²/(2σ²)), i = −r..r with r = ⌈3σ⌉. */
|
|
195
|
+
export function unsharpMaskKernel(radiusPixels) {
|
|
196
|
+
const r = Math.ceil(3 * radiusPixels), k = new Float64Array(2 * r + 1);
|
|
197
|
+
let total = 0;
|
|
198
|
+
for (let i = -r; i <= r; i++) {
|
|
199
|
+
const v = Math.exp(-i * i / (2 * radiusPixels * radiusPixels));
|
|
200
|
+
k[i + r] = v;
|
|
201
|
+
total += v;
|
|
202
|
+
}
|
|
203
|
+
for (let i = 0; i < k.length; i++)
|
|
204
|
+
k[i] = k[i] / total;
|
|
205
|
+
return k;
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Unsharp mask on interleaved encoded RGB in [0, 1] (each channel separately, edges clamped):
|
|
209
|
+
* d = x − (G_σ ∗ x); y = x + amount·d where |d| ≥ threshold, else y = x; then y is clipped to [0, 1].
|
|
210
|
+
* Returns new samples.
|
|
211
|
+
*/
|
|
212
|
+
export function applyUnsharpMask(encoded, width, height, settings) {
|
|
213
|
+
const s = parseUnsharpMaskSettings(settings);
|
|
214
|
+
if (encoded.length !== width * height * 3)
|
|
215
|
+
throw new InvalidConfigurationError("Unsharp mask raster size mismatch.");
|
|
216
|
+
const k = unsharpMaskKernel(s.radiusPixels), r = (k.length - 1) / 2, horizontal = new Float64Array(encoded.length), blurred = new Float64Array(encoded.length);
|
|
217
|
+
for (let y = 0; y < height; y++)
|
|
218
|
+
for (let x = 0; x < width; x++) {
|
|
219
|
+
const d = (y * width + x) * 3;
|
|
220
|
+
let a = 0, b = 0, c = 0;
|
|
221
|
+
for (let i = -r; i <= r; i++) {
|
|
222
|
+
const sx = (y * width + Math.min(width - 1, Math.max(0, x + i))) * 3, w = k[i + r];
|
|
223
|
+
a += w * encoded[sx];
|
|
224
|
+
b += w * encoded[sx + 1];
|
|
225
|
+
c += w * encoded[sx + 2];
|
|
226
|
+
}
|
|
227
|
+
horizontal[d] = a;
|
|
228
|
+
horizontal[d + 1] = b;
|
|
229
|
+
horizontal[d + 2] = c;
|
|
230
|
+
}
|
|
231
|
+
for (let y = 0; y < height; y++)
|
|
232
|
+
for (let x = 0; x < width; x++) {
|
|
233
|
+
const d = (y * width + x) * 3;
|
|
234
|
+
let a = 0, b = 0, c = 0;
|
|
235
|
+
for (let i = -r; i <= r; i++) {
|
|
236
|
+
const sy = (Math.min(height - 1, Math.max(0, y + i)) * width + x) * 3, w = k[i + r];
|
|
237
|
+
a += w * horizontal[sy];
|
|
238
|
+
b += w * horizontal[sy + 1];
|
|
239
|
+
c += w * horizontal[sy + 2];
|
|
240
|
+
}
|
|
241
|
+
blurred[d] = a;
|
|
242
|
+
blurred[d + 1] = b;
|
|
243
|
+
blurred[d + 2] = c;
|
|
244
|
+
}
|
|
245
|
+
const out = new Float64Array(encoded.length);
|
|
246
|
+
let sharpened = 0, held = 0, low = 0, high = 0, increase = 0, decrease = 0;
|
|
247
|
+
for (let i = 0; i < encoded.length; i++) {
|
|
248
|
+
const x = encoded[i], detail = x - blurred[i];
|
|
249
|
+
if (detail === 0 || Math.abs(detail) < s.threshold) {
|
|
250
|
+
if (detail !== 0)
|
|
251
|
+
held++;
|
|
252
|
+
out[i] = x;
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
const change = s.amount * detail;
|
|
256
|
+
let v = x + change;
|
|
257
|
+
sharpened++;
|
|
258
|
+
if (change > increase)
|
|
259
|
+
increase = change;
|
|
260
|
+
if (-change > decrease)
|
|
261
|
+
decrease = -change;
|
|
262
|
+
if (v < 0) {
|
|
263
|
+
v = 0;
|
|
264
|
+
low++;
|
|
265
|
+
}
|
|
266
|
+
else if (v > 1) {
|
|
267
|
+
v = 1;
|
|
268
|
+
high++;
|
|
269
|
+
}
|
|
270
|
+
out[i] = v;
|
|
271
|
+
}
|
|
272
|
+
return { samples: out, diagnostics: { kernelRadiusPixels: r, sharpenedSampleCount: sharpened, thresholdHeldSampleCount: held, clippedLowSampleCount: low,
|
|
273
|
+
clippedHighSampleCount: high, maximumIncrease: increase, maximumDecrease: decrease } };
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* SDR rendering with an unsharp mask between the sRGB transfer and quantization. Per pixel it runs the reference renderer's
|
|
277
|
+
* operations in its order (−0 → +0, rendering exposure 2^EV with reference white 1, the shared tone operator, gamut
|
|
278
|
+
* handling, sRGB transfer); the whole encoded image is then sharpened and rounded to nearest (ties up).
|
|
279
|
+
*/
|
|
280
|
+
export function renderSdrWithUnsharpMask(rgb, width, height, profile, settings) {
|
|
281
|
+
if (rgb.length !== width * height * 3)
|
|
282
|
+
throw new InvalidConfigurationError("SDR raster size mismatch.");
|
|
283
|
+
const scale = 2 ** profile.renderingExposureEv, tone = prepareSdrPixelTone(profile), scratch = new Float64Array(3), encoded = new Float64Array(rgb.length);
|
|
284
|
+
if (!Number.isFinite(scale) || scale <= 0)
|
|
285
|
+
throw new InvalidConfigurationError("Rendering scale is unrepresentable.");
|
|
286
|
+
const reject = profile.gamutHandling === "reject-out-of-range", counts = { negative: 0, above: 0, changed: 0, low: 0, high: 0 };
|
|
287
|
+
for (let i = 0; i < rgb.length; i += 3) {
|
|
288
|
+
const r = rgb[i], g = rgb[i + 1], b = rgb[i + 2];
|
|
289
|
+
const er = (r === 0 ? 0 : r) * scale, eg = (g === 0 ? 0 : g) * scale, eb = (b === 0 ? 0 : b) * scale;
|
|
290
|
+
if (!Number.isFinite(er) || !Number.isFinite(eg) || !Number.isFinite(eb))
|
|
291
|
+
throw new InvalidScientificResultError("Rendering exposure produced a non-finite sample.");
|
|
292
|
+
tone(er, eg, eb, scratch);
|
|
293
|
+
for (let c = 0; c < 3; c++) {
|
|
294
|
+
const exposed = c === 0 ? er : c === 1 ? eg : eb;
|
|
295
|
+
if (exposed < 0)
|
|
296
|
+
counts.negative++;
|
|
297
|
+
if (exposed > 1)
|
|
298
|
+
counts.above++;
|
|
299
|
+
let mapped = scratch[c];
|
|
300
|
+
if (mapped !== exposed)
|
|
301
|
+
counts.changed++;
|
|
302
|
+
if (mapped === 0)
|
|
303
|
+
mapped = 0;
|
|
304
|
+
if (mapped < 0 || mapped > 1) {
|
|
305
|
+
if (reject)
|
|
306
|
+
throw new InvalidConfigurationError("Tone output exceeds the declared SDR gamut range.");
|
|
307
|
+
if (mapped < 0) {
|
|
308
|
+
counts.low++;
|
|
309
|
+
mapped = 0;
|
|
310
|
+
}
|
|
311
|
+
else {
|
|
312
|
+
counts.high++;
|
|
313
|
+
mapped = 1;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
encoded[i + c] = encodeSrgbComponent(mapped);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
const sharpened = applyUnsharpMask(encoded, width, height, settings), maximum = 2 ** profile.bitDepth - 1;
|
|
320
|
+
const codes = profile.bitDepth === 8 ? new Uint8Array(rgb.length) : new Uint16Array(rgb.length);
|
|
321
|
+
for (let i = 0; i < codes.length; i++)
|
|
322
|
+
codes[i] = Math.round(sharpened.samples[i] * maximum);
|
|
323
|
+
return { codes, counts, sharpening: sharpened.diagnostics };
|
|
324
|
+
}
|
|
325
|
+
//# sourceMappingURL=development-detail.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"development-detail.js","sourceRoot":"","sources":["../../src/processing/development-detail.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Development detail stages (#552), both opt-in and off by default:\n *\n * - **Diffraction correction**: a bounded, regularized (Wiener–Tikhonov) deconvolution of the developed linear RGB with the\n * diffraction OTF of an ideal circular pupil at the capture's working f-number. The OTF is zero beyond the cut-off\n * f_c = 1/(λ·N_w), so detail beyond it never reached the sensor; the filter's design gain there is 0 and it cannot, and\n * does not claim to, restore it.\n * - **Sharpening**: an unsharp mask (radius, amount, threshold) on the display-encoded values, after the tone curve and\n * gamut handling and before quantization. It raises edge contrast and creates halos; it adds no optical detail.\n *\n * Development order with these stages: (exposure gain) → demosaic → colour and white balance → diffraction correction\n * (linear) → rendering exposure and tone curve → gamut handling → sRGB transfer → unsharp mask → quantization (encode).\n *\n * Image edges are handled by clamping (edge pixels repeated). Both stages work on a whole in-memory image.\n * @see docs/DIGITAL_OPTICS_FOUNDATION.md for the equations, sources, trade-offs and limits.\n */\n\nimport { encodeSrgbComponent } from \"../color/srgb-transfer.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { InvalidScientificResultError } from \"../core/validation.js\";\nimport { prepareSdrPixelTone, type SdrRenderingProfile } from \"../output/sdr-rendering.js\";\n\n/** Version of the `development-detail` model (diffraction correction and unsharp mask). */\nexport const DEVELOPMENT_DETAIL_MODEL_VERSION = \"0.1.0\" as const;\n\n/** Unsharp mask settings: Gaussian standard deviation in output pixels, gain on the detail signal, and threshold in encoded units [0, 1]. */\nexport interface UnsharpMaskSettings { radiusPixels: number; amount: number; threshold: number }\n/** Diffraction correction settings: design wavelength, Wiener noise-to-signal regularization, gain bound and FIR radius in pixels. */\nexport interface DiffractionCorrectionSettings { wavelengthNm: number; regularization: number; maximumGain: number; kernelRadiusPixels: number }\n\n/** Accepted ranges. Bounds are engineering limits on cost and on noise gain, not quality recommendations. */\nexport const DEVELOPMENT_DETAIL_LIMITS = /* @__PURE__ */ Object.freeze({\n maximumUnsharpRadiusPixels: 8, maximumUnsharpAmount: 5,\n minimumWavelengthNm: 380, maximumWavelengthNm: 780,\n minimumRegularization: 1e-4, maximumRegularization: 1,\n maximumGain: 10, maximumKernelRadiusPixels: 16\n});\n\n/** Third dark ring of the Airy pattern, in units of λ·N (j₁,₃/π, j₁,₃ = 10.1734681…). */\nconst AIRY_THIRD_ZERO_PER_LAMBDA_N = 10.173468135062722 / Math.PI;\n\nfunction finiteIn(value: unknown, name: string, minimum: number, maximum: number, minimumExclusive = false): number {\n if (typeof value !== \"number\" || !Number.isFinite(value) || (minimumExclusive ? value <= minimum : value < minimum) || value > maximum) {\n throw new InvalidConfigurationError(name + \" must be a finite number in \" + (minimumExclusive ? \"(\" : \"[\") + minimum + \", \" + maximum + \"].\");\n }\n return value;\n}\n\n/** Validates unsharp mask settings. */\nexport function parseUnsharpMaskSettings(value: UnsharpMaskSettings, path = \"sharpening\"): UnsharpMaskSettings {\n return { radiusPixels: finiteIn(value.radiusPixels, path + \".radiusPixels\", 0, DEVELOPMENT_DETAIL_LIMITS.maximumUnsharpRadiusPixels, true),\n amount: finiteIn(value.amount, path + \".amount\", 0, DEVELOPMENT_DETAIL_LIMITS.maximumUnsharpAmount, true),\n threshold: finiteIn(value.threshold, path + \".threshold\", 0, 1) };\n}\n/** Validates diffraction correction settings. */\nexport function parseDiffractionCorrectionSettings(value: DiffractionCorrectionSettings, path = \"diffractionCorrection\"): DiffractionCorrectionSettings {\n const radius = value.kernelRadiusPixels;\n if (typeof radius !== \"number\" || !Number.isSafeInteger(radius) || radius < 1 || radius > DEVELOPMENT_DETAIL_LIMITS.maximumKernelRadiusPixels) {\n throw new InvalidConfigurationError(path + \".kernelRadiusPixels must be an integer from 1 to \" + DEVELOPMENT_DETAIL_LIMITS.maximumKernelRadiusPixels + \".\");\n }\n return { wavelengthNm: finiteIn(value.wavelengthNm, path + \".wavelengthNm\", DEVELOPMENT_DETAIL_LIMITS.minimumWavelengthNm, DEVELOPMENT_DETAIL_LIMITS.maximumWavelengthNm),\n regularization: finiteIn(value.regularization, path + \".regularization\", DEVELOPMENT_DETAIL_LIMITS.minimumRegularization, DEVELOPMENT_DETAIL_LIMITS.maximumRegularization),\n maximumGain: finiteIn(value.maximumGain, path + \".maximumGain\", 1, DEVELOPMENT_DETAIL_LIMITS.maximumGain), kernelRadiusPixels: radius };\n}\n/** Default FIR radius: the Airy pattern's third dark ring (3.2383·λ·N_w) in pixels, rounded up, within 1..16. */\nexport function defaultDiffractionCorrectionRadiusPixels(workingFNumber: number, wavelengthNm: number, pixelPitchMicrometers: number): number {\n const sites = Math.ceil(AIRY_THIRD_ZERO_PER_LAMBDA_N * wavelengthNm / 1000 * workingFNumber / pixelPitchMicrometers);\n return Math.min(DEVELOPMENT_DETAIL_LIMITS.maximumKernelRadiusPixels, Math.max(1, sites));\n}\n\n/**\n * Incoherent OTF of an ideal, aberration-free circular pupil at normalized frequency s = f/f_c:\n * H(s) = (2/π)(acos s − s·√(1 − s²)) for s < 1, and 0 for s ≥ 1 (MIT OCW 2.71, lectures 21–22; Goodman, §7.3).\n */\nexport function circularPupilDiffractionMtf(normalizedFrequency: number): number {\n const s = Math.abs(normalizedFrequency);\n if (s >= 1) return 0;\n return 2 / Math.PI * (Math.acos(s) - s * Math.sqrt(1 - s * s));\n}\n\n/** A designed diffraction correction filter and what its response does. Frequencies are in cycles per pixel. */\nexport interface DiffractionCorrectionFilter {\n settings: DiffractionCorrectionSettings;\n workingFNumber: number;\n pixelPitchMicrometers: number;\n /** f_c · pitch = pitch/(λ·N_w): the diffraction cut-off. The sampling Nyquist frequency is 0.5. */\n cutoffCyclesPerPixel: number;\n /** Fraction of the Nyquist square |fx|, |fy| ≤ 0.5 lying beyond the cut-off, where the lens transmitted nothing. */\n nyquistAreaBeyondCutoffFraction: number;\n /** Largest gain of the designed (untruncated) response: min(maximumGain, max over s of H(1+α)/(H² + α)). */\n designPeakGain: number;\n /** Largest |response| of the windowed, truncated FIR over a 1/64-cycle grid of the Nyquist square. */\n responsePeakGain: number;\n /**\n * Largest |response| of the FIR at grid frequencies beyond the cut-off (null when the whole Nyquist square is inside it).\n * The design is 0 there; a non-zero value is truncation leakage that passes noise and aliased signal, never lost detail,\n * because the optics delivered no signal at those frequencies.\n */\n responseMaximumBeyondCutoff: number | null;\n /** Row-major (2R+1)² weights summing to 1; weights[(dy + R)(2R + 1) + dx + R]. */\n weights: Float64Array;\n}\n\n/**\n * Designs the bounded Wiener–Tikhonov correction. In frequency (normalized s = ρ/f_c, ρ the radial frequency):\n * G(s) = min(maximumGain, H(s)·(1 + α)/(H(s)² + α)), G(0) = 1, and G(s) = 0 for s ≥ 1,\n * where α is the regularization (an assumed noise-to-signal power ratio, Wiener's K). The response is sampled on an M × M\n * grid (M a power of two ≥ max(64, 4(2R + 1))), inverse transformed, truncated to the (2R + 1)² window with a radial raised\n * cosine taper and renormalized to unit sum. The achieved response is then measured, not assumed.\n */\nexport function designDiffractionCorrectionFilter(input: { workingFNumber: number; pixelPitchMicrometers: number; settings: DiffractionCorrectionSettings }): DiffractionCorrectionFilter {\n const settings = parseDiffractionCorrectionSettings(input.settings);\n const workingFNumber = finiteIn(input.workingFNumber, \"workingFNumber\", 0, 1e4, true), pitch = finiteIn(input.pixelPitchMicrometers, \"pixelPitchMicrometers\", 0, 1e4, true);\n const cutoff = pitch / (settings.wavelengthNm / 1000 * workingFNumber), alpha = settings.regularization, bound = settings.maximumGain;\n const gainAt = (rho: number): number => {\n const h = circularPupilDiffractionMtf(rho / cutoff);\n return h === 0 ? 0 : Math.min(bound, h * (1 + alpha) / (h * h + alpha));\n };\n const R = settings.kernelRadiusPixels, size = 2 * R + 1;\n let M = 64;\n while (M < 4 * size) M *= 2;\n // A(ky, x) = Σ_kx G cos(2π kx x/M); h(x, y) = (1/M²) Σ_ky cos(2π ky y/M) A(ky, x). G is even in each axis, so h is real and even.\n const cosTable = new Float64Array(M * (R + 1));\n for (let k = 0; k < M; k++) for (let x = 0; x <= R; x++) cosTable[k * (R + 1) + x] = Math.cos(2 * Math.PI * k * x / M);\n const A = new Float64Array(M * (R + 1));\n for (let ky = 0; ky < M; ky++) {\n const fy = (ky < M / 2 ? ky : ky - M) / M;\n for (let kx = 0; kx < M; kx++) {\n const fx = (kx < M / 2 ? kx : kx - M) / M, g = gainAt(Math.hypot(fx, fy));\n if (g === 0) continue;\n for (let x = 0; x <= R; x++) A[ky * (R + 1) + x]! += g * cosTable[kx * (R + 1) + x]!;\n }\n }\n const quarter = new Float64Array((R + 1) * (R + 1));\n for (let y = 0; y <= R; y++) for (let x = 0; x <= R; x++) {\n let sum = 0;\n for (let ky = 0; ky < M; ky++) sum += cosTable[ky * (R + 1) + y]! * A[ky * (R + 1) + x]!;\n const r = Math.hypot(x, y), taper = r < R + 1 ? 0.5 * (1 + Math.cos(Math.PI * r / (R + 1))) : 0;\n quarter[y * (R + 1) + x] = sum / (M * M) * taper;\n }\n const weights = new Float64Array(size * size);\n let total = 0;\n for (let dy = -R; dy <= R; dy++) for (let dx = -R; dx <= R; dx++) {\n const w = quarter[Math.abs(dy) * (R + 1) + Math.abs(dx)]!;\n weights[(dy + R) * size + dx + R] = w; total += w;\n }\n if (!(total > 0) || !Number.isFinite(total)) throw new InvalidScientificResultError(\"Diffraction correction filter has no finite DC response.\");\n for (let i = 0; i < weights.length; i++) weights[i] = weights[i]! / total;\n let designPeakGain = 0;\n for (let i = 0; i <= 4096; i++) designPeakGain = Math.max(designPeakGain, gainAt(i / 4096 * cutoff));\n let responsePeakGain = 0, beyond: number | null = null, beyondCount = 0;\n const GRID = 64;\n for (let iy = 0; iy <= GRID / 2; iy++) for (let ix = 0; ix <= GRID / 2; ix++) {\n const fx = ix / GRID, fy = iy / GRID, response = Math.abs(evaluateDiffractionCorrectionResponse(weights, R, fx, fy));\n responsePeakGain = Math.max(responsePeakGain, response);\n if (Math.hypot(fx, fy) >= cutoff) beyond = Math.max(beyond ?? 0, response);\n }\n const AREA = 256;\n for (let iy = 0; iy < AREA; iy++) for (let ix = 0; ix < AREA; ix++) if (Math.hypot((ix + 0.5) / (2 * AREA), (iy + 0.5) / (2 * AREA)) >= cutoff) beyondCount++;\n return { settings, workingFNumber, pixelPitchMicrometers: pitch, cutoffCyclesPerPixel: cutoff, nyquistAreaBeyondCutoffFraction: beyondCount / (AREA * AREA),\n designPeakGain, responsePeakGain, responseMaximumBeyondCutoff: beyond, weights };\n}\n/** Frequency response Σ w(x, y)·cos(2π(fx·x + fy·y)) of a centred (2R + 1)² FIR at (fx, fy) cycles per pixel. */\nexport function evaluateDiffractionCorrectionResponse(weights: Float64Array, radius: number, fx: number, fy: number): number {\n const size = 2 * radius + 1;\n let sum = 0;\n for (let dy = -radius; dy <= radius; dy++) for (let dx = -radius; dx <= radius; dx++) sum += weights[(dy + radius) * size + dx + radius]! * Math.cos(2 * Math.PI * (fx * dx + fy * dy));\n return sum;\n}\n\n/** What the correction did to the linear samples. */\nexport interface DiffractionCorrectionDiagnostics {\n /** Corrected samples below 0 (ringing below black). They are clipped by the later gamut handling. */\n negativeSampleCount: number;\n /** Corrected samples above 1 (above the RAW white level). */\n aboveWhiteSampleCount: number;\n}\n/**\n * Applies the FIR to interleaved linear RGB (width × height × 3), each channel separately, edges clamped. Returns new samples.\n */\nexport function applyDiffractionCorrection(rgb: Float64Array, width: number, height: number, filter: Pick<DiffractionCorrectionFilter, \"weights\" | \"settings\">):\n { samples: Float64Array; diagnostics: DiffractionCorrectionDiagnostics } {\n if (rgb.length !== width * height * 3) throw new InvalidConfigurationError(\"Diffraction correction raster size mismatch.\");\n const R = filter.settings.kernelRadiusPixels, size = 2 * R + 1, w = filter.weights, out = new Float64Array(rgb.length);\n const xs = new Int32Array(width + 2 * R);\n for (let i = 0; i < xs.length; i++) xs[i] = Math.min(width - 1, Math.max(0, i - R)) * 3;\n let negative = 0, above = 0;\n for (let y = 0; y < height; y++) {\n const o = y * width * 3;\n for (let dy = -R; dy <= R; dy++) {\n const row = Math.min(height - 1, Math.max(0, y + dy)) * width * 3, wr = (dy + R) * size;\n for (let dx = 0; dx < size; dx++) {\n const weight = w[wr + dx]!;\n if (weight === 0) continue;\n for (let x = 0; x < width; x++) {\n const s = row + xs[x + dx]!, d = o + 3 * x;\n out[d] = out[d]! + weight * rgb[s]!; out[d + 1] = out[d + 1]! + weight * rgb[s + 1]!; out[d + 2] = out[d + 2]! + weight * rgb[s + 2]!;\n }\n }\n }\n }\n for (let i = 0; i < out.length; i++) {\n const v = out[i]!;\n if (!Number.isFinite(v)) throw new InvalidScientificResultError(\"Diffraction correction produced a non-finite sample.\");\n if (v < 0) negative++; else if (v > 1) above++;\n }\n return { samples: out, diagnostics: { negativeSampleCount: negative, aboveWhiteSampleCount: above } };\n}\n\n/** Normalized Gaussian weights exp(−i²/(2σ²)), i = −r..r with r = ⌈3σ⌉. */\nexport function unsharpMaskKernel(radiusPixels: number): Float64Array {\n const r = Math.ceil(3 * radiusPixels), k = new Float64Array(2 * r + 1);\n let total = 0;\n for (let i = -r; i <= r; i++) { const v = Math.exp(-i * i / (2 * radiusPixels * radiusPixels)); k[i + r] = v; total += v; }\n for (let i = 0; i < k.length; i++) k[i] = k[i]! / total;\n return k;\n}\n/** What the unsharp mask did. Counts are samples (one per channel). */\nexport interface UnsharpMaskDiagnostics {\n /** Half-width of the separable Gaussian, ⌈3σ⌉ pixels. */\n kernelRadiusPixels: number;\n /** Samples changed by the mask (|detail| ≥ threshold and detail ≠ 0). */\n sharpenedSampleCount: number;\n /** Samples left unchanged because 0 < |detail| < threshold. */\n thresholdHeldSampleCount: number;\n /** Sharpened samples clipped at 0 or 1 (the halo did not fit in the code range). */\n clippedLowSampleCount: number;\n clippedHighSampleCount: number;\n /** Largest increase and decrease amount·detail before clipping, in encoded units: the halo strength. */\n maximumIncrease: number;\n maximumDecrease: number;\n}\n/**\n * Unsharp mask on interleaved encoded RGB in [0, 1] (each channel separately, edges clamped):\n * d = x − (G_σ ∗ x); y = x + amount·d where |d| ≥ threshold, else y = x; then y is clipped to [0, 1].\n * Returns new samples.\n */\nexport function applyUnsharpMask(encoded: Float64Array, width: number, height: number, settings: UnsharpMaskSettings):\n { samples: Float64Array; diagnostics: UnsharpMaskDiagnostics } {\n const s = parseUnsharpMaskSettings(settings);\n if (encoded.length !== width * height * 3) throw new InvalidConfigurationError(\"Unsharp mask raster size mismatch.\");\n const k = unsharpMaskKernel(s.radiusPixels), r = (k.length - 1) / 2, horizontal = new Float64Array(encoded.length), blurred = new Float64Array(encoded.length);\n for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {\n const d = (y * width + x) * 3;\n let a = 0, b = 0, c = 0;\n for (let i = -r; i <= r; i++) {\n const sx = (y * width + Math.min(width - 1, Math.max(0, x + i))) * 3, w = k[i + r]!;\n a += w * encoded[sx]!; b += w * encoded[sx + 1]!; c += w * encoded[sx + 2]!;\n }\n horizontal[d] = a; horizontal[d + 1] = b; horizontal[d + 2] = c;\n }\n for (let y = 0; y < height; y++) for (let x = 0; x < width; x++) {\n const d = (y * width + x) * 3;\n let a = 0, b = 0, c = 0;\n for (let i = -r; i <= r; i++) {\n const sy = (Math.min(height - 1, Math.max(0, y + i)) * width + x) * 3, w = k[i + r]!;\n a += w * horizontal[sy]!; b += w * horizontal[sy + 1]!; c += w * horizontal[sy + 2]!;\n }\n blurred[d] = a; blurred[d + 1] = b; blurred[d + 2] = c;\n }\n const out = new Float64Array(encoded.length);\n let sharpened = 0, held = 0, low = 0, high = 0, increase = 0, decrease = 0;\n for (let i = 0; i < encoded.length; i++) {\n const x = encoded[i]!, detail = x - blurred[i]!;\n if (detail === 0 || Math.abs(detail) < s.threshold) { if (detail !== 0) held++; out[i] = x; continue; }\n const change = s.amount * detail;\n let v = x + change;\n sharpened++;\n if (change > increase) increase = change;\n if (-change > decrease) decrease = -change;\n if (v < 0) { v = 0; low++; } else if (v > 1) { v = 1; high++; }\n out[i] = v;\n }\n return { samples: out, diagnostics: { kernelRadiusPixels: r, sharpenedSampleCount: sharpened, thresholdHeldSampleCount: held, clippedLowSampleCount: low,\n clippedHighSampleCount: high, maximumIncrease: increase, maximumDecrease: decrease } };\n}\n\n/** SDR counters in the shape of the SDR rendering diagnostics. */\nexport interface DetailSdrCounts { negative: number; above: number; changed: number; low: number; high: number }\n/**\n * SDR rendering with an unsharp mask between the sRGB transfer and quantization. Per pixel it runs the reference renderer's\n * operations in its order (−0 → +0, rendering exposure 2^EV with reference white 1, the shared tone operator, gamut\n * handling, sRGB transfer); the whole encoded image is then sharpened and rounded to nearest (ties up).\n */\nexport function renderSdrWithUnsharpMask(rgb: Float64Array, width: number, height: number, profile: SdrRenderingProfile, settings: UnsharpMaskSettings):\n { codes: Uint8Array | Uint16Array; counts: DetailSdrCounts; sharpening: UnsharpMaskDiagnostics } {\n if (rgb.length !== width * height * 3) throw new InvalidConfigurationError(\"SDR raster size mismatch.\");\n const scale = 2 ** profile.renderingExposureEv, tone = prepareSdrPixelTone(profile), scratch = new Float64Array(3), encoded = new Float64Array(rgb.length);\n if (!Number.isFinite(scale) || scale <= 0) throw new InvalidConfigurationError(\"Rendering scale is unrepresentable.\");\n const reject = profile.gamutHandling === \"reject-out-of-range\", counts: DetailSdrCounts = { negative: 0, above: 0, changed: 0, low: 0, high: 0 };\n for (let i = 0; i < rgb.length; i += 3) {\n const r = rgb[i]!, g = rgb[i + 1]!, b = rgb[i + 2]!;\n const er = (r === 0 ? 0 : r) * scale, eg = (g === 0 ? 0 : g) * scale, eb = (b === 0 ? 0 : b) * scale;\n if (!Number.isFinite(er) || !Number.isFinite(eg) || !Number.isFinite(eb)) throw new InvalidScientificResultError(\"Rendering exposure produced a non-finite sample.\");\n tone(er, eg, eb, scratch);\n for (let c = 0; c < 3; c++) {\n const exposed = c === 0 ? er : c === 1 ? eg : eb;\n if (exposed < 0) counts.negative++;\n if (exposed > 1) counts.above++;\n let mapped = scratch[c]!;\n if (mapped !== exposed) counts.changed++;\n if (mapped === 0) mapped = 0;\n if (mapped < 0 || mapped > 1) {\n if (reject) throw new InvalidConfigurationError(\"Tone output exceeds the declared SDR gamut range.\");\n if (mapped < 0) { counts.low++; mapped = 0; } else { counts.high++; mapped = 1; }\n }\n encoded[i + c] = encodeSrgbComponent(mapped);\n }\n }\n const sharpened = applyUnsharpMask(encoded, width, height, settings), maximum = 2 ** profile.bitDepth - 1;\n const codes = profile.bitDepth === 8 ? new Uint8Array(rgb.length) : new Uint16Array(rgb.length);\n for (let i = 0; i < codes.length; i++) codes[i] = Math.round(sharpened.samples[i]! * maximum);\n return { codes, counts, sharpening: sharpened.diagnostics };\n}\n"]}
|
package/docs/API_REFERENCE.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Public API reference
|
|
2
2
|
|
|
3
|
-
Generated from the exact root exports for **@photivra/engine 2.
|
|
3
|
+
Generated from the exact root exports for **@photivra/engine 2.16.0 / root contract 2.16.0** by `node scripts/generate-api-reference.mjs`. No module deep import is supported.
|
|
4
4
|
|
|
5
5
|
Use [the developer guide](https://github.com/Photivra/photivra/blob/main/docs/DEVELOPERS.md) for executable paths and scientific boundaries. Types are contracts, not runtime validation: parse untrusted data, preserve units/reference frames, read assessment blockers, and retain evidence/limitations. Optional properties do not imply a universal default. Model/schema IDs remain independent of the distribution.
|
|
6
6
|
|
|
@@ -223,6 +223,7 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
|
|
|
223
223
|
| Export | Kind |
|
|
224
224
|
| --- | --- |
|
|
225
225
|
| [`createNativeRawDevelopmentTask`](api/capture-native-raw-development.md#createnativerawdevelopmenttask) | Runtime |
|
|
226
|
+
| [`NATIVE_RAW_DEVELOPMENT_EXPOSURE_RANGE_STOPS`](api/capture-native-raw-development.md#native_raw_development_exposure_range_stops) | Runtime |
|
|
226
227
|
| [`NativeRawDevelopmentInput`](api/capture-native-raw-development.md#nativerawdevelopmentinput) | Type |
|
|
227
228
|
| [`NativeRawDevelopmentOutput`](api/capture-native-raw-development.md#nativerawdevelopmentoutput) | Type |
|
|
228
229
|
| [`NativeRawDevelopmentProvider`](api/capture-native-raw-development.md#nativerawdevelopmentprovider) | Type |
|
|
@@ -1268,6 +1269,7 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
|
|
|
1268
1269
|
|
|
1269
1270
|
| Export | Kind |
|
|
1270
1271
|
| --- | --- |
|
|
1272
|
+
| [`DisplayViewHighlightDiagnostics`](api/facade-simulate-capture.md#displayviewhighlightdiagnostics) | Type |
|
|
1271
1273
|
| [`DisplayViewResult`](api/facade-simulate-capture.md#displayviewresult) | Type |
|
|
1272
1274
|
| [`IdealReferenceResult`](api/facade-simulate-capture.md#idealreferenceresult) | Type |
|
|
1273
1275
|
| [`serializeSimulatedCaptureDngFromParts`](api/facade-simulate-capture.md#serializesimulatedcapturedngfromparts) | Runtime |
|
|
@@ -1289,6 +1291,8 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
|
|
|
1289
1291
|
| [`SimulateCaptureCostOption`](api/facade-simulate-capture.md#simulatecapturecostoption) | Type |
|
|
1290
1292
|
| [`SimulateCaptureDemosaic`](api/facade-simulate-capture.md#simulatecapturedemosaic) | Type |
|
|
1291
1293
|
| [`SimulateCaptureDemosaicRecord`](api/facade-simulate-capture.md#simulatecapturedemosaicrecord) | Type |
|
|
1294
|
+
| [`SimulateCaptureDetailDiagnostics`](api/facade-simulate-capture.md#simulatecapturedetaildiagnostics) | Type |
|
|
1295
|
+
| [`SimulateCaptureDiffractionCorrection`](api/facade-simulate-capture.md#simulatecapturediffractioncorrection) | Type |
|
|
1292
1296
|
| [`SimulateCaptureDngPart`](api/facade-simulate-capture.md#simulatecapturedngpart) | Type |
|
|
1293
1297
|
| [`SimulateCaptureExposureMode`](api/facade-simulate-capture.md#simulatecaptureexposuremode) | Type |
|
|
1294
1298
|
| [`SimulateCaptureFastOption`](api/facade-simulate-capture.md#simulatecapturefastoption) | Type |
|
|
@@ -1302,6 +1306,7 @@ Start here to find a function by task. [Using Photivra from an agent](AGENT_GUID
|
|
|
1302
1306
|
| [`SimulateCaptureProgress`](api/facade-simulate-capture.md#simulatecaptureprogress) | Type |
|
|
1303
1307
|
| [`SimulateCaptureResult`](api/facade-simulate-capture.md#simulatecaptureresult) | Type |
|
|
1304
1308
|
| [`SimulateCaptureSensorAntiAliasing`](api/facade-simulate-capture.md#simulatecapturesensorantialiasing) | Type |
|
|
1309
|
+
| [`SimulateCaptureSharpening`](api/facade-simulate-capture.md#simulatecapturesharpening) | Type |
|
|
1305
1310
|
| [`SimulateCaptureWhiteBalance`](api/facade-simulate-capture.md#simulatecapturewhitebalance) | Type |
|
|
1306
1311
|
| [`SimulateDisplayViewOptions`](api/facade-simulate-capture.md#simulatedisplayviewoptions) | Type |
|
|
1307
1312
|
| [`simulateIdealReference`](api/facade-simulate-capture.md#simulateidealreference) | Runtime |
|