@photivra/engine 2.12.0 → 2.14.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 +144 -1
- package/CITATION.cff +1 -1
- package/README.md +7 -7
- package/SECURITY.md +1 -1
- package/THIRD_PARTY.md +3 -0
- package/dist/camera/lens-projection.js +2 -1
- package/dist/camera/lens-projection.js.map +1 -1
- package/dist/capture/dng-import.js +1 -1
- package/dist/capture/dng-import.js.map +1 -1
- package/dist/capture/export-jpeg.d.ts +1 -1
- package/dist/capture/export-jpeg.js +6 -4
- package/dist/capture/export-jpeg.js.map +1 -1
- package/dist/capture/host-yield-cadence.js +1 -1
- package/dist/capture/host-yield-cadence.js.map +1 -1
- package/dist/capture/native-environment-optical-stage.js +1 -1
- package/dist/capture/native-environment-optical-stage.js.map +1 -1
- package/dist/capture/native-raw.js +1 -1
- package/dist/capture/native-raw.js.map +1 -1
- package/dist/capture/packed-sensor-raw.js +3 -3
- package/dist/capture/packed-sensor-raw.js.map +1 -1
- package/dist/capture/sensor-raw-producer.js +1 -1
- package/dist/capture/sensor-raw-producer.js.map +1 -1
- package/dist/color/capture-color.js +3 -3
- package/dist/color/capture-color.js.map +1 -1
- package/dist/color/chromatic-adaptation.js +2 -2
- package/dist/color/chromatic-adaptation.js.map +1 -1
- package/dist/color/color-matching-functions.js +1 -1
- package/dist/color/color-matching-functions.js.map +1 -1
- package/dist/color/colorimetry-math.js +2 -2
- package/dist/color/colorimetry-math.js.map +1 -1
- package/dist/color/illuminants.js +3 -3
- package/dist/color/illuminants.js.map +1 -1
- package/dist/color/rgb-color-spaces.js +1 -1
- package/dist/color/rgb-color-spaces.js.map +1 -1
- package/dist/color/sensor-color-development.js +1 -1
- package/dist/color/sensor-color-development.js.map +1 -1
- package/dist/core/record-validation.js +3 -3
- package/dist/core/record-validation.js.map +1 -1
- package/dist/core/version.d.ts +1 -1
- package/dist/core/version.js +1 -1
- package/dist/core/version.js.map +1 -1
- package/dist/equipment/generic-prime-lenses.js +3 -3
- package/dist/equipment/generic-prime-lenses.js.map +1 -1
- package/dist/equipment/generic-tier-assets.d.ts +23 -1
- package/dist/equipment/generic-tier-assets.js +58 -30
- package/dist/equipment/generic-tier-assets.js.map +1 -1
- package/dist/equipment/generic-tier-presets.d.ts +6 -3
- package/dist/equipment/generic-tier-presets.js +26 -13
- package/dist/equipment/generic-tier-presets.js.map +1 -1
- package/dist/exposure/auto-iso-slowest-shutter.d.ts +120 -0
- package/dist/exposure/auto-iso-slowest-shutter.js +217 -0
- package/dist/exposure/auto-iso-slowest-shutter.js.map +1 -0
- package/dist/exposure/exposure-mode-resolver.d.ts +57 -1
- package/dist/exposure/exposure-mode-resolver.js +238 -33
- package/dist/exposure/exposure-mode-resolver.js.map +1 -1
- package/dist/exposure/flash-ttl.js +1 -1
- package/dist/exposure/flash-ttl.js.map +1 -1
- package/dist/exposure/flash.js +1 -1
- package/dist/exposure/flash.js.map +1 -1
- package/dist/facade/chart-material-layers.js +2 -2
- package/dist/facade/chart-material-layers.js.map +1 -1
- package/dist/facade/chart-radiance-batch.js +1 -1
- package/dist/facade/chart-radiance-batch.js.map +1 -1
- package/dist/facade/chart-scenes.d.ts +45 -8
- package/dist/facade/chart-scenes.js +101 -44
- package/dist/facade/chart-scenes.js.map +1 -1
- package/dist/facade/color-patches.d.ts +129 -0
- package/dist/facade/color-patches.js +209 -0
- package/dist/facade/color-patches.js.map +1 -0
- package/dist/facade/diffraction-provider.js +12 -13
- package/dist/facade/diffraction-provider.js.map +1 -1
- package/dist/facade/display-view-gpu.js +1 -1
- package/dist/facade/display-view-gpu.js.map +1 -1
- package/dist/facade/display-view.d.ts +27 -0
- package/dist/facade/display-view.js +234 -65
- package/dist/facade/display-view.js.map +1 -1
- package/dist/facade/scene-radiance.js +2 -2
- package/dist/facade/scene-radiance.js.map +1 -1
- package/dist/facade/simulate-capture.d.ts +139 -12
- package/dist/facade/simulate-capture.js +209 -36
- package/dist/facade/simulate-capture.js.map +1 -1
- package/dist/facade/simulate-display-view.js +2 -2
- package/dist/facade/simulate-display-view.js.map +1 -1
- package/dist/gpu/chart-composite-gpu.js +2 -2
- package/dist/gpu/chart-composite-gpu.js.map +1 -1
- package/dist/gpu/display-view-composite-material-module.js +1 -1
- package/dist/gpu/display-view-composite-material-module.js.map +1 -1
- package/dist/gpu/display-view-quality-gpu.d.ts +2 -1
- package/dist/gpu/display-view-quality-gpu.js +24 -14
- package/dist/gpu/display-view-quality-gpu.js.map +1 -1
- package/dist/gpu/image-kernel-execution.js +2 -2
- package/dist/gpu/image-kernel-execution.js.map +1 -1
- package/dist/gpu/temporal-sum-gpu.js +23 -23
- package/dist/gpu/webgpu-runtime.js +1 -1
- package/dist/gpu/webgpu-runtime.js.map +1 -1
- package/dist/gpu/wgsl-chart-composite-kernel.js +318 -318
- package/dist/gpu/wgsl-chart-composite-kernel.js.map +1 -1
- package/dist/gpu/wgsl-chart-kernel.js +117 -117
- package/dist/gpu/wgsl-chart-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-quality-kernel.d.ts +13 -1
- package/dist/gpu/wgsl-display-view-quality-kernel.js +522 -488
- package/dist/gpu/wgsl-display-view-quality-kernel.js.map +1 -1
- package/dist/gpu/wgsl-display-view-speed-kernel.js +248 -248
- package/dist/gpu/wgsl-display-view-speed-kernel.js.map +1 -1
- package/dist/gpu/wgsl-kernels.js +203 -203
- package/dist/gpu/wgsl-kernels.js.map +1 -1
- package/dist/gpu/wgsl-sensor-kernel.js +302 -302
- package/dist/gpu/wgsl-sensor-kernel.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/motion/temporal-exposure-sum.js +1 -1
- package/dist/motion/temporal-exposure-sum.js.map +1 -1
- package/dist/optics/diffraction-psf-profile.js +4 -4
- package/dist/optics/diffraction-psf-profile.js.map +1 -1
- package/dist/optics/front-of-lens-filter.js +1 -1
- package/dist/optics/front-of-lens-filter.js.map +1 -1
- package/dist/optics/irradiance-convolution.d.ts +10 -0
- package/dist/optics/irradiance-convolution.js +49 -14
- package/dist/optics/irradiance-convolution.js.map +1 -1
- package/dist/output/native-capture-sdr.js +1 -1
- package/dist/output/native-capture-sdr.js.map +1 -1
- package/dist/output/png-encoder.js +2 -2
- package/dist/output/png-encoder.js.map +1 -1
- package/dist/output/print-jpeg.js +13 -10
- package/dist/output/print-jpeg.js.map +1 -1
- package/dist/output/text-metadata.d.ts +22 -2
- package/dist/output/text-metadata.js +58 -9
- package/dist/output/text-metadata.js.map +1 -1
- package/dist/sensor/fixed-pattern-noise.js +1 -1
- package/dist/sensor/fixed-pattern-noise.js.map +1 -1
- package/dist/sensor/readout-exposure-linkage.js +1 -1
- package/dist/sensor/readout-exposure-linkage.js.map +1 -1
- package/docs/API_REFERENCE.md +49 -1
- package/docs/QUICK_START.md +104 -9
- package/docs/USAGE.md +241 -2
- package/docs/api/core-version.md +1 -1
- package/docs/api/equipment-generic-tier-assets.md +41 -1
- package/docs/api/equipment-generic-tier-presets.md +11 -3
- package/docs/api/exports.json +172 -2
- package/docs/api/exposure-auto-iso-slowest-shutter.md +139 -0
- package/docs/api/exposure-exposure-mode-resolver.md +68 -0
- package/docs/api/facade-chart-scenes.md +36 -5
- package/docs/api/facade-color-patches.md +150 -0
- package/docs/api/facade-simulate-capture.md +140 -9
- package/docs/api/output-text-metadata.md +34 -0
- package/llms.txt +1 -0
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generic-tier-presets.js","sourceRoot":"","sources":["../../src/equipment/generic-tier-presets.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Generic tier presets: a catalog of exact preset versions for bodies and lenses.\n *\n * There is no hidden \"latest\" alias and no default sensor size. By default the lens tier matches the\n * body tier, but mixing tiers is a normal, supported choice.\n * @see docs/GENERIC_TIER_PRESETS.md for the equations, units, coordinate conventions, known blockers\n * and support limits.\n */\n\nimport { freezeOwnedData } from \"../core/owned-data.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { choice, record, parseOpticalProfileState, type OpticalProfileState, type GenericOpticalEvidence } from \"../optics/profile-contract.js\";\nimport { GENERIC_TIER_ASSETS, type GenericEquipmentTier, type GenericEquipmentTierPreset } from \"./generic-tier-assets.js\";\nimport { resolveGenericEquipmentExposureCapabilities } from \"./exposure-capabilities.js\";\nimport { parseGenericLensCorrectionProfile, type GenericLensCorrectionProfile } from \"../output/lens-corrections.js\";\nimport { parseLensStrayLightProfile, type LensStrayLightProfile } from \"../optics/stray-light.js\";\nimport { parseLensComplexPupilProfile, type LensComplexPupilProfile } from \"../optics/complex-pupil-psf.js\";\nimport { parseSceneToSensorIrradianceProfile, type SceneToSensorIrradianceProfile } from \"../optics/scene-to-sensor-irradiance.js\";\nimport type { RadialDistortionProfile } from \"../optics/radial-distortion.js\";\nimport type { LateralChromaticAberrationProfile } from \"../optics/lateral-chromatic-aberration.js\";\nimport type { IlluminationVignettingProfile } from \"../optics/illumination-vignetting.js\";\n\nexport const GENERIC_EQUIPMENT_TIER_PRESET_VERSION = \"2.0.0\" as const;\nexport const GENERIC_EQUIPMENT_TIERS = Object.freeze([\"consumer\", \"prosumer\", \"professional\"] as const);\nexport interface GenericEquipmentTierProfileReference { profileId: string; profileVersion: string }\nexport interface GenericEquipmentTierSelection {\n schemaVersion: \"0.1.0\"; presetVersion: typeof GENERIC_EQUIPMENT_TIER_PRESET_VERSION;\n bodyTier: GenericEquipmentTier; lensTier: GenericEquipmentTier;\n bodyPresetId: string; lensPresetId: string;\n profileReferences: readonly GenericEquipmentTierProfileReference[];\n sensorFormatSelectedByTier: false; scientificTierMultiplierApplied: false;\n}\nexport interface ResolvedGenericTierLensProfiles {\n selection: GenericEquipmentTierSelection;\n state: OpticalProfileState;\n radial: RadialDistortionProfile;\n lateralCa: LateralChromaticAberrationProfile;\n vignetting: IlluminationVignettingProfile;\n breathingProjectionScale: number;\n transmission: SceneToSensorIrradianceProfile;\n pupils: readonly LensComplexPupilProfile[];\n stray: LensStrayLightProfile;\n correction: GenericLensCorrectionProfile;\n scientificStatus: \"approximation\";\n}\nconst catalog = freezeOwnedData(GENERIC_TIER_ASSETS);\nfunction preset(tier: GenericEquipmentTier): GenericEquipmentTierPreset {\n return catalog.find((p) => p.tier === tier)!;\n}\n/** Catalog of exact versions. No hidden \"latest\" alias and no default sensor size. */\nexport function resolveGenericEquipmentTierCatalog(input: { presetVersion: string }): readonly GenericEquipmentTierPreset[] {\n const r = record(input, [\"presetVersion\"]);\n if (r.presetVersion !== GENERIC_EQUIPMENT_TIER_PRESET_VERSION) throw new InvalidConfigurationError(\"Unknown equipment preset version; historical definitions may not silently fall back.\");\n return catalog;\n}\nfunction bodyReferences(p: GenericEquipmentTierPreset): GenericEquipmentTierProfileReference[] {\n const b = p.body;\n return [b.exposure, b.iso, b.signalChain, b.release, b.focus, b.stabilization, b.whiteBalance, b.meteringCapabilities, b.flashSync]\n .map((profile) => ({ profileId: profile.profileId, profileVersion: profile.profileVersion }))\n .concat([...b.readout, b.readoutTiming, b.rollingReadoutTiming, b.fixedPatternNoise, ...b.metering]\n .map((a) => ({ profileId: a.assetId, profileVersion: a.assetVersion })));\n}\n/** The lens tier matches the body tier by default. Mixing tiers is a normal, supported choice. */\nexport function createGenericEquipmentTierSelection(input: {\n presetVersion: string; bodyTier: GenericEquipmentTier; lensTier?: GenericEquipmentTier;\n}): GenericEquipmentTierSelection {\n const r = record(input, [\"presetVersion\", \"bodyTier\", \"lensTier\"]);\n resolveGenericEquipmentTierCatalog({ presetVersion: r.presetVersion as string });\n const bodyTier = choice(r.bodyTier, GENERIC_EQUIPMENT_TIERS), lensTier = choice(r.lensTier === undefined ? bodyTier : r.lensTier, GENERIC_EQUIPMENT_TIERS);\n const body = preset(bodyTier), lens = preset(lensTier);\n return freezeOwnedData({ schemaVersion: \"0.1.0\", presetVersion: GENERIC_EQUIPMENT_TIER_PRESET_VERSION, bodyTier, lensTier,\n bodyPresetId: body.presetId+\":body\", lensPresetId: lens.presetId+\":lens\",\n profileReferences: [...bodyReferences(body), { profileId: lens.lens.exposure.profileId, profileVersion: lens.lens.exposure.profileVersion }, ...lens.lens.profileReferences],\n sensorFormatSelectedByTier: false, scientificTierMultiplierApplied: false });\n}\n/** On a saved simulation, rejects profile versions that are stale, missing or retuned, instead of picking today's defaults. */\nexport function parseGenericEquipmentTierSelection(value: unknown): GenericEquipmentTierSelection {\n const r = record(value, [\"schemaVersion\", \"presetVersion\", \"bodyTier\", \"lensTier\", \"bodyPresetId\", \"lensPresetId\", \"profileReferences\",\n \"sensorFormatSelectedByTier\", \"scientificTierMultiplierApplied\"]);\n const expected = createGenericEquipmentTierSelection({ presetVersion: r.presetVersion as string,\n bodyTier: r.bodyTier as GenericEquipmentTier, lensTier: choice(r.lensTier, GENERIC_EQUIPMENT_TIERS) });\n if (r.schemaVersion !== expected.schemaVersion || r.bodyPresetId !== expected.bodyPresetId || r.lensPresetId !== expected.lensPresetId ||\n r.sensorFormatSelectedByTier !== false || r.scientificTierMultiplierApplied !== false || !Array.isArray(r.profileReferences) ||\n r.profileReferences.length !== expected.profileReferences.length) throw new InvalidConfigurationError(\"Saved equipment selection differs from its exact preset manifest.\");\n for (let i = 0; i < expected.profileReferences.length; i++) {\n const ref = record(r.profileReferences[i], [\"profileId\", \"profileVersion\"]), actual = expected.profileReferences[i]!;\n if (ref.profileId !== actual.profileId || ref.profileVersion !== actual.profileVersion) throw new InvalidConfigurationError(\"Saved equipment profile reference drift.\");\n }\n return expected;\n}\n/** Ties the finite matched-reference optical data to one exact generic selected state. Never extrapolates. */\nexport function resolveGenericEquipmentTierLensProfiles(input: { selection: GenericEquipmentTierSelection; state: OpticalProfileState }): ResolvedGenericTierLensProfiles {\n const request = record(input, [\"selection\", \"state\"]), selection = parseGenericEquipmentTierSelection(request.selection), state = parseOpticalProfileState(request.state);\n const b = preset(selection.bodyTier), l = preset(selection.lensTier), a = l.lens.matchedReference;\n if (state.bodyId !== b.body.exposure.profileId.replace(/:exposure$/, \"\") || state.bodyVersion !== selection.presetVersion ||\n state.lensId !== l.lens.exposure.profileId.replace(/:exposure$/, \"\") || state.lensVersion !== selection.presetVersion ||\n state.focalLengthMm !== a.focalLengthMm || state.aperture !== a.aperture || state.focusDistanceM !== a.focusDistanceM ||\n state.captureMode !== \"still\" || state.stabilizationMode !== \"off\") throw new InvalidConfigurationError(\"No optical reference slice for this exact generic system/acquisition state.\");\n resolveGenericEquipmentExposureCapabilities({ bodyProfile: b.body.exposure, lensProfile: l.lens.exposure, selectedFocalLengthMm: state.focalLengthMm });\n const id = state.lensId, version = state.lensVersion, evidence = l.evidence;\n const opticalEvidence: GenericOpticalEvidence = { kind: \"generic-parametric\", basis: \"Photivra-owned educational tier design values, not measured calibration.\",\n residualNote: \"Finite matched-reference approximation; no branded signature or aggregate physical error bound.\", sources: evidence };\n const coeff = (k1: number): RadialDistortionProfile => ({ normalizationRadiusMm: a.normalizationRadiusMm, maximumNormalizedRadius: 1.1,\n coefficients: { k1, k2: 0, k3: 0 } });\n const radial = coeff(a.distortionK1), lateralCa: LateralChromaticAberrationProfile = { normalizationRadiusMm: a.normalizationRadiusMm,\n maximumNormalizedRadius: 1.1, baseDistortionCoefficients: radial.coefficients,\n redCoefficientOffset: { k1: a.lateralCaK1Offset, k2: 0, k3: 0 }, blueCoefficientOffset: { k1: -a.lateralCaK1Offset, k2: 0, k3: 0 } };\n const vignetting: IlluminationVignettingProfile = { normalizationRadiusMm: a.normalizationRadiusMm, maximumNormalizedRadius: 1.1,\n coefficients: { r2: a.vignettingR2, r4: 0, r6: 0 } };\n const identity = { version, domain: \"reconstructed-linear\" as const, availability: \"toggle\" as const, defaultEnabled: true,\n dependencies: [], requiredByStabilizationModes: [], residualNote: opticalEvidence.residualNote };\n const correction = parseGenericLensCorrectionProfile({ schemaVersion: \"0.1.0\", id: id+\":correction\", version, state, evidence: opticalEvidence,\n components: [{ ...identity, id: \"geometry\", kind: \"geometry\", transform: { id: id+\":radial-map\", version,\n domain: \"reconstructed-linear\", purpose: \"distortion\", kind: \"radial\", profile: radial } },\n { ...identity, id: \"lateral-ca\", kind: \"lateral-ca\", transforms: Object.fromEntries([\"red\", \"green\", \"blue\"].map((channel, i) => [channel, {\n id: id+\":ca-map:\"+channel, version, domain: \"reconstructed-linear\", purpose: \"lateral-ca\", kind: \"radial\",\n profile: coeff([a.lateralCaK1Offset*a.correctionCaFraction, 0, -a.lateralCaK1Offset*a.correctionCaFraction][i]!) }])) },\n { ...identity, id: \"gain\", kind: \"peripheral-illumination\", profile: vignetting, strength: a.correctionGainStrength }] });\n const stray = parseLensStrayLightProfile({ schemaVersion: \"0.1.0\", id: id+\":stray\", version, state, evidence: opticalEvidence,\n interaction: \"lens-reflections\", wavelengthNm: a.wavelengthNm, wavelengthBasis: \"vacuum\", maximumSourceAngleDegrees: 60,\n referenceEntranceAreaMm2: Math.PI*(50/4/2)**2, responses: [\n { id: \"ghost\", kind: \"ghost\", centroidMmPerDegree: [-.02,0,0,-.02], offsetMm: { x: 1, y: 0 }, sigmaMm: .5, axisPowerFraction: a.ghostPowerFraction, angularSlope: 1 },\n { id: \"veil\", kind: \"veil\", centroidMmPerDegree: [0,0,0,0], offsetMm: { x: 0, y: 0 }, sigmaMm: 10, axisPowerFraction: a.veilPowerFraction, angularSlope: 0 }] });\n const transmission = parseSceneToSensorIrradianceProfile({ schemaVersion: \"0.1.0\", profileId: id+\":transmission\", profileVersion: version, lensProfileId: id,\n scientificStatus: \"approximation\", applicability: { focalLengthMm: { minimum: 50, maximum: 50 }, nominalFNumber: { minimum: 4, maximum: 4 },\n focus: { kind: \"finite-distance-range\", objectDistanceM: { minimum: 5, maximum: 5 } } }, transmission: { kind: \"spectral-transmission\", scientificStatus: \"approximation\",\n wavelengthBasis: \"vacuum\", samples: { value: [{ wavelengthNanometers: 380, linearTransmissionFactor: a.transmission },\n { wavelengthNanometers: 780, linearTransmissionFactor: a.transmission }], evidence }, uncertainty: { kind: \"not-quantified\", limitation: \"Synthetic spectrally flat reference over 380-780 nm.\" } },\n distortionAreaMappingOwnership: \"not-applied-by-bridge\", psfRedistributionOwnership: \"downstream-normalized-energy-redistribution\", sensorOpticalStackIncluded: false,\n strayLightIncluded: false, polarizationModeled: false, wavelengthChangingBehaviorModeled: false, volumetricScatteringModeled: false, evidence, limitations: l.lens.limitations });\n const pupils = [0,1,2].flatMap((field) => [-20,0,20].map((defocus, index) => parseLensComplexPupilProfile({ schemaVersion: \"0.1.0\",\n profileId: id+\":pupil:\"+field+\":\"+index, profileVersion: version, scientificStatus: \"approximation\", opticalDomain: \"lens-primary-optical-path-only\",\n representation: \"complex-pupil-amplitude-plus-opd\", pupilCoordinateSystem: \"pupil-plane-metric-aligned-to-image-plane\", imageFieldAxes: \"+X right, +Y up\",\n amplitudeMeaning: \"relative-complex-pupil-amplitude-shape\", wavefrontMeaning: \"optical-path-difference-micrometers\",\n throughputOwnership: \"separate-relative-pupil-throughput-factor\", kernelEnergyNormalization: \"unit-energy-shape\", propagationModel: \"scalar-fraunhofer-discrete-reference\",\n context: { focalLengthMm: 50, focus: { kind: \"finite\", distanceM: 5 }, apertureFNumber: 4, fieldPointMm: [{x:0,y:0},{x:10,y:6},{x:18,y:12}][field],\n wavelengthNm: 550, signedDefocusImagePlaneMicrometers: defocus }, responseIncludes: { diffraction: true, aberration: true, defocus: true, pupilClippingShape: true },\n relativePupilThroughputFactor: a.pupilThroughputByField[field], grid: { widthSamples: 5, heightSamples: 5, pupilSamplePitchMmX: 3.125, pupilSamplePitchMmY: 3.125,\n centerSampleX: 2, centerSampleY: 2, relativeAmplitude: a.pupilAmplitudeByField[field], opticalPathDifferenceMicrometers: a.pupilOpdMicrometersByDefocus[index] },\n sensorOpticalStackIncluded: false, sensorSamplingIncluded: false, reconstructionIncluded: false, strayLightIncluded: false, evidence,\n uncertainty: { kind: \"not-quantified\", limitation: \"Coarse independently designed pupil-grid reference only.\" }, limitations: l.lens.limitations })));\n return freezeOwnedData({ selection, state, radial, lateralCa, vignetting, breathingProjectionScale: a.breathingProjectionScale, transmission, pupils, stray, correction,\n scientificStatus: \"approximation\" });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"generic-tier-presets.js","sourceRoot":"","sources":["../../src/equipment/generic-tier-presets.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Generic tier presets: a catalog of exact preset versions for bodies and lenses.\n *\n * There is no hidden \"latest\" alias and no default sensor size. By default the lens tier matches the\n * body tier, but mixing tiers is a normal, supported choice.\n * @see docs/GENERIC_TIER_PRESETS.md for the equations, units, coordinate conventions, known blockers\n * and support limits.\n */\n\nimport { freezeOwnedData } from \"../core/owned-data.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { choice, record, parseOpticalProfileState, type OpticalProfileState, type GenericOpticalEvidence } from \"../optics/profile-contract.js\";\nimport { genericTierAssets, type GenericEquipmentTier, type GenericEquipmentTierPreset, type GenericTierPresetVersion } from \"./generic-tier-assets.js\";\nimport { resolveGenericEquipmentExposureCapabilities } from \"./exposure-capabilities.js\";\nimport { parseGenericLensCorrectionProfile, type GenericLensCorrectionProfile } from \"../output/lens-corrections.js\";\nimport { parseLensStrayLightProfile, type LensStrayLightProfile } from \"../optics/stray-light.js\";\nimport { parseLensComplexPupilProfile, type LensComplexPupilProfile } from \"../optics/complex-pupil-psf.js\";\nimport { parseSceneToSensorIrradianceProfile, type SceneToSensorIrradianceProfile } from \"../optics/scene-to-sensor-irradiance.js\";\nimport type { RadialDistortionProfile } from \"../optics/radial-distortion.js\";\nimport type { LateralChromaticAberrationProfile } from \"../optics/lateral-chromatic-aberration.js\";\nimport type { IlluminationVignettingProfile } from \"../optics/illumination-vignetting.js\";\n\n/** The current preset version (2.14: 2.1.0, third-stop ISO settings). */\nexport const GENERIC_EQUIPMENT_TIER_PRESET_VERSION = \"2.1.0\" as const;\n/** Every exact preset version that resolves, oldest first. A version is never retuned in place (`test/generic-tier-presets.test.ts`). */\nexport const GENERIC_EQUIPMENT_TIER_PRESET_VERSIONS = /* @__PURE__ */ Object.freeze([\"2.0.0\", \"2.1.0\"] as const);\nexport const GENERIC_EQUIPMENT_TIERS = /* @__PURE__ */ Object.freeze([\"consumer\", \"prosumer\", \"professional\"] as const);\nexport interface GenericEquipmentTierProfileReference { profileId: string; profileVersion: string }\nexport interface GenericEquipmentTierSelection {\n schemaVersion: \"0.1.0\"; presetVersion: GenericTierPresetVersion;\n bodyTier: GenericEquipmentTier; lensTier: GenericEquipmentTier;\n bodyPresetId: string; lensPresetId: string;\n profileReferences: readonly GenericEquipmentTierProfileReference[];\n sensorFormatSelectedByTier: false; scientificTierMultiplierApplied: false;\n}\nexport interface ResolvedGenericTierLensProfiles {\n selection: GenericEquipmentTierSelection;\n state: OpticalProfileState;\n radial: RadialDistortionProfile;\n lateralCa: LateralChromaticAberrationProfile;\n vignetting: IlluminationVignettingProfile;\n breathingProjectionScale: number;\n transmission: SceneToSensorIrradianceProfile;\n pupils: readonly LensComplexPupilProfile[];\n stray: LensStrayLightProfile;\n correction: GenericLensCorrectionProfile;\n scientificStatus: \"approximation\";\n}\nconst catalogs = new Map<GenericTierPresetVersion, readonly GenericEquipmentTierPreset[]>();\nfunction catalogOf(version: GenericTierPresetVersion): readonly GenericEquipmentTierPreset[] {\n let catalog = catalogs.get(version);\n if (!catalog) catalogs.set(version, catalog = freezeOwnedData(genericTierAssets(version)));\n return catalog;\n}\nfunction preset(version: GenericTierPresetVersion, tier: GenericEquipmentTier): GenericEquipmentTierPreset {\n return catalogOf(version).find((p) => p.tier === tier)!;\n}\nfunction exactVersion(value: unknown): GenericTierPresetVersion {\n if (!(GENERIC_EQUIPMENT_TIER_PRESET_VERSIONS as readonly unknown[]).includes(value)) {\n throw new InvalidConfigurationError(\"Unknown equipment preset version; historical definitions may not silently fall back.\");\n }\n return value as GenericTierPresetVersion;\n}\n/** Catalog of exact versions. No hidden \"latest\" alias and no default sensor size. */\nexport function resolveGenericEquipmentTierCatalog(input: { presetVersion: string }): readonly GenericEquipmentTierPreset[] {\n const r = record(input, [\"presetVersion\"]);\n return catalogOf(exactVersion(r.presetVersion));\n}\nfunction bodyReferences(p: GenericEquipmentTierPreset): GenericEquipmentTierProfileReference[] {\n const b = p.body;\n return [b.exposure, b.iso, b.signalChain, b.release, b.focus, b.stabilization, b.whiteBalance, b.meteringCapabilities, b.flashSync]\n .map((profile) => ({ profileId: profile.profileId, profileVersion: profile.profileVersion }))\n .concat([...b.readout, b.readoutTiming, b.rollingReadoutTiming, b.fixedPatternNoise, ...b.metering]\n .map((a) => ({ profileId: a.assetId, profileVersion: a.assetVersion })));\n}\n/** The lens tier matches the body tier by default. Mixing tiers is a normal, supported choice. */\nexport function createGenericEquipmentTierSelection(input: {\n presetVersion: string; bodyTier: GenericEquipmentTier; lensTier?: GenericEquipmentTier;\n}): GenericEquipmentTierSelection {\n const r = record(input, [\"presetVersion\", \"bodyTier\", \"lensTier\"]);\n const presetVersion = exactVersion(r.presetVersion);\n const bodyTier = choice(r.bodyTier, GENERIC_EQUIPMENT_TIERS), lensTier = choice(r.lensTier === undefined ? bodyTier : r.lensTier, GENERIC_EQUIPMENT_TIERS);\n const body = preset(presetVersion, bodyTier), lens = preset(presetVersion, lensTier);\n return freezeOwnedData({ schemaVersion: \"0.1.0\", presetVersion, bodyTier, lensTier,\n bodyPresetId: body.presetId+\":body\", lensPresetId: lens.presetId+\":lens\",\n profileReferences: [...bodyReferences(body), { profileId: lens.lens.exposure.profileId, profileVersion: lens.lens.exposure.profileVersion }, ...lens.lens.profileReferences],\n sensorFormatSelectedByTier: false, scientificTierMultiplierApplied: false });\n}\n/** On a saved simulation, rejects profile versions that are stale, missing or retuned, instead of picking today's defaults. */\nexport function parseGenericEquipmentTierSelection(value: unknown): GenericEquipmentTierSelection {\n const r = record(value, [\"schemaVersion\", \"presetVersion\", \"bodyTier\", \"lensTier\", \"bodyPresetId\", \"lensPresetId\", \"profileReferences\",\n \"sensorFormatSelectedByTier\", \"scientificTierMultiplierApplied\"]);\n const expected = createGenericEquipmentTierSelection({ presetVersion: r.presetVersion as string,\n bodyTier: r.bodyTier as GenericEquipmentTier, lensTier: choice(r.lensTier, GENERIC_EQUIPMENT_TIERS) });\n if (r.schemaVersion !== expected.schemaVersion || r.bodyPresetId !== expected.bodyPresetId || r.lensPresetId !== expected.lensPresetId ||\n r.sensorFormatSelectedByTier !== false || r.scientificTierMultiplierApplied !== false || !Array.isArray(r.profileReferences) ||\n r.profileReferences.length !== expected.profileReferences.length) throw new InvalidConfigurationError(\"Saved equipment selection differs from its exact preset manifest.\");\n for (let i = 0; i < expected.profileReferences.length; i++) {\n const ref = record(r.profileReferences[i], [\"profileId\", \"profileVersion\"]), actual = expected.profileReferences[i]!;\n if (ref.profileId !== actual.profileId || ref.profileVersion !== actual.profileVersion) throw new InvalidConfigurationError(\"Saved equipment profile reference drift.\");\n }\n return expected;\n}\n/** Ties the finite matched-reference optical data to one exact generic selected state. Never extrapolates. */\nexport function resolveGenericEquipmentTierLensProfiles(input: { selection: GenericEquipmentTierSelection; state: OpticalProfileState }): ResolvedGenericTierLensProfiles {\n const request = record(input, [\"selection\", \"state\"]), selection = parseGenericEquipmentTierSelection(request.selection), state = parseOpticalProfileState(request.state);\n const b = preset(selection.presetVersion, selection.bodyTier), l = preset(selection.presetVersion, selection.lensTier), a = l.lens.matchedReference;\n if (state.bodyId !== b.body.exposure.profileId.replace(/:exposure$/, \"\") || state.bodyVersion !== selection.presetVersion ||\n state.lensId !== l.lens.exposure.profileId.replace(/:exposure$/, \"\") || state.lensVersion !== selection.presetVersion ||\n state.focalLengthMm !== a.focalLengthMm || state.aperture !== a.aperture || state.focusDistanceM !== a.focusDistanceM ||\n state.captureMode !== \"still\" || state.stabilizationMode !== \"off\") throw new InvalidConfigurationError(\"No optical reference slice for this exact generic system/acquisition state.\");\n resolveGenericEquipmentExposureCapabilities({ bodyProfile: b.body.exposure, lensProfile: l.lens.exposure, selectedFocalLengthMm: state.focalLengthMm });\n const id = state.lensId, version = state.lensVersion, evidence = l.evidence;\n const opticalEvidence: GenericOpticalEvidence = { kind: \"generic-parametric\", basis: \"Photivra-owned educational tier design values, not measured calibration.\",\n residualNote: \"Finite matched-reference approximation; no branded signature or aggregate physical error bound.\", sources: evidence };\n const coeff = (k1: number): RadialDistortionProfile => ({ normalizationRadiusMm: a.normalizationRadiusMm, maximumNormalizedRadius: 1.1,\n coefficients: { k1, k2: 0, k3: 0 } });\n const radial = coeff(a.distortionK1), lateralCa: LateralChromaticAberrationProfile = { normalizationRadiusMm: a.normalizationRadiusMm,\n maximumNormalizedRadius: 1.1, baseDistortionCoefficients: radial.coefficients,\n redCoefficientOffset: { k1: a.lateralCaK1Offset, k2: 0, k3: 0 }, blueCoefficientOffset: { k1: -a.lateralCaK1Offset, k2: 0, k3: 0 } };\n const vignetting: IlluminationVignettingProfile = { normalizationRadiusMm: a.normalizationRadiusMm, maximumNormalizedRadius: 1.1,\n coefficients: { r2: a.vignettingR2, r4: 0, r6: 0 } };\n const identity = { version, domain: \"reconstructed-linear\" as const, availability: \"toggle\" as const, defaultEnabled: true,\n dependencies: [], requiredByStabilizationModes: [], residualNote: opticalEvidence.residualNote };\n const correction = parseGenericLensCorrectionProfile({ schemaVersion: \"0.1.0\", id: id+\":correction\", version, state, evidence: opticalEvidence,\n components: [{ ...identity, id: \"geometry\", kind: \"geometry\", transform: { id: id+\":radial-map\", version,\n domain: \"reconstructed-linear\", purpose: \"distortion\", kind: \"radial\", profile: radial } },\n { ...identity, id: \"lateral-ca\", kind: \"lateral-ca\", transforms: Object.fromEntries([\"red\", \"green\", \"blue\"].map((channel, i) => [channel, {\n id: id+\":ca-map:\"+channel, version, domain: \"reconstructed-linear\", purpose: \"lateral-ca\", kind: \"radial\",\n profile: coeff([a.lateralCaK1Offset*a.correctionCaFraction, 0, -a.lateralCaK1Offset*a.correctionCaFraction][i]!) }])) },\n { ...identity, id: \"gain\", kind: \"peripheral-illumination\", profile: vignetting, strength: a.correctionGainStrength }] });\n const stray = parseLensStrayLightProfile({ schemaVersion: \"0.1.0\", id: id+\":stray\", version, state, evidence: opticalEvidence,\n interaction: \"lens-reflections\", wavelengthNm: a.wavelengthNm, wavelengthBasis: \"vacuum\", maximumSourceAngleDegrees: 60,\n referenceEntranceAreaMm2: Math.PI*(50/4/2)**2, responses: [\n { id: \"ghost\", kind: \"ghost\", centroidMmPerDegree: [-.02,0,0,-.02], offsetMm: { x: 1, y: 0 }, sigmaMm: .5, axisPowerFraction: a.ghostPowerFraction, angularSlope: 1 },\n { id: \"veil\", kind: \"veil\", centroidMmPerDegree: [0,0,0,0], offsetMm: { x: 0, y: 0 }, sigmaMm: 10, axisPowerFraction: a.veilPowerFraction, angularSlope: 0 }] });\n const transmission = parseSceneToSensorIrradianceProfile({ schemaVersion: \"0.1.0\", profileId: id+\":transmission\", profileVersion: version, lensProfileId: id,\n scientificStatus: \"approximation\", applicability: { focalLengthMm: { minimum: 50, maximum: 50 }, nominalFNumber: { minimum: 4, maximum: 4 },\n focus: { kind: \"finite-distance-range\", objectDistanceM: { minimum: 5, maximum: 5 } } }, transmission: { kind: \"spectral-transmission\", scientificStatus: \"approximation\",\n wavelengthBasis: \"vacuum\", samples: { value: [{ wavelengthNanometers: 380, linearTransmissionFactor: a.transmission },\n { wavelengthNanometers: 780, linearTransmissionFactor: a.transmission }], evidence }, uncertainty: { kind: \"not-quantified\", limitation: \"Synthetic spectrally flat reference over 380-780 nm.\" } },\n distortionAreaMappingOwnership: \"not-applied-by-bridge\", psfRedistributionOwnership: \"downstream-normalized-energy-redistribution\", sensorOpticalStackIncluded: false,\n strayLightIncluded: false, polarizationModeled: false, wavelengthChangingBehaviorModeled: false, volumetricScatteringModeled: false, evidence, limitations: l.lens.limitations });\n const pupils = [0,1,2].flatMap((field) => [-20,0,20].map((defocus, index) => parseLensComplexPupilProfile({ schemaVersion: \"0.1.0\",\n profileId: id+\":pupil:\"+field+\":\"+index, profileVersion: version, scientificStatus: \"approximation\", opticalDomain: \"lens-primary-optical-path-only\",\n representation: \"complex-pupil-amplitude-plus-opd\", pupilCoordinateSystem: \"pupil-plane-metric-aligned-to-image-plane\", imageFieldAxes: \"+X right, +Y up\",\n amplitudeMeaning: \"relative-complex-pupil-amplitude-shape\", wavefrontMeaning: \"optical-path-difference-micrometers\",\n throughputOwnership: \"separate-relative-pupil-throughput-factor\", kernelEnergyNormalization: \"unit-energy-shape\", propagationModel: \"scalar-fraunhofer-discrete-reference\",\n context: { focalLengthMm: 50, focus: { kind: \"finite\", distanceM: 5 }, apertureFNumber: 4, fieldPointMm: [{x:0,y:0},{x:10,y:6},{x:18,y:12}][field],\n wavelengthNm: 550, signedDefocusImagePlaneMicrometers: defocus }, responseIncludes: { diffraction: true, aberration: true, defocus: true, pupilClippingShape: true },\n relativePupilThroughputFactor: a.pupilThroughputByField[field], grid: { widthSamples: 5, heightSamples: 5, pupilSamplePitchMmX: 3.125, pupilSamplePitchMmY: 3.125,\n centerSampleX: 2, centerSampleY: 2, relativeAmplitude: a.pupilAmplitudeByField[field], opticalPathDifferenceMicrometers: a.pupilOpdMicrometersByDefocus[index] },\n sensorOpticalStackIncluded: false, sensorSamplingIncluded: false, reconstructionIncluded: false, strayLightIncluded: false, evidence,\n uncertainty: { kind: \"not-quantified\", limitation: \"Coarse independently designed pupil-grid reference only.\" }, limitations: l.lens.limitations })));\n return freezeOwnedData({ selection, state, radial, lateralCa, vignetting, breathingProjectionScale: a.breathingProjectionScale, transmission, pupils, stray, correction,\n scientificStatus: \"approximation\" });\n}\n"]}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #504: the automatic slowest preferred shutter for Auto ISO ("Auto" minimum shutter speed), derived
|
|
3
|
+
* from the engine's camera-shake model (`estimateCameraShakeBlur()`), not from the 1/focal-length rule
|
|
4
|
+
* of thumb.
|
|
5
|
+
*
|
|
6
|
+
* The camera turns at a constant angular velocity ω (yaw, pitch) during the exposure, reduced by the
|
|
7
|
+
* stabilization factor r = 2^−stops. A still point on the optical axis then moves
|
|
8
|
+
* b(t) = v·hypot(tan(r·ω_yaw·t), tan(r·ω_pitch·t)) on the sensor, where v is the projection distance (the
|
|
9
|
+
* focal length, or the thin-lens image distance with a focus distance). The derived shutter is the
|
|
10
|
+
* exposure time at which b(t) equals the caller's blur budget. b(t) grows strictly with t, so the
|
|
11
|
+
* solution is unique; the whole-stop bias (as in makers' Auto minimum-shutter settings: faster or
|
|
12
|
+
* slower in 1 EV steps) is applied after the derivation, and the optional shutter grid last.
|
|
13
|
+
*
|
|
14
|
+
* This is an approximation: the hand's motion is a constant-velocity profile supplied by the caller,
|
|
15
|
+
* and stabilization stops scale the motion by 2^−stops (the educational model of
|
|
16
|
+
* `estimateCameraShakeBlur()`, not a CIPA DC-011 rating).
|
|
17
|
+
* @see docs/USAGE.md ("Automatic slowest shutter (#504)") for the equations, the cited generic
|
|
18
|
+
* hand-shake default and the support limits.
|
|
19
|
+
*/
|
|
20
|
+
import { type CalculationResult } from "../core/calculation-result.js";
|
|
21
|
+
import type { ResolvedGenericEquipmentExposureCapabilities } from "../equipment/exposure-capabilities.js";
|
|
22
|
+
export declare const AUTO_ISO_SLOWEST_SHUTTER_MODEL_VERSION: "1.0.0";
|
|
23
|
+
/**
|
|
24
|
+
* A generic handheld angular velocity, labelled as a default and not a measurement of any camera or
|
|
25
|
+
* photographer: 4.39 °/s (≈ 0.0766 rad/s), the linear camera-motion rate Xiao, Farrell, Catrysse and
|
|
26
|
+
* Wandell use for "an average user" holding "a typical 100g mobile handset with two hands", from their
|
|
27
|
+
* earlier gyroscope measurements (F. Xiao, J. Farrell, P. Catrysse, B. Wandell, "Mobile imaging: the
|
|
28
|
+
* big challenge of the small pixel", Proc. SPIE 7250, Digital Photography V, 2009, §2; author copy:
|
|
29
|
+
* https://web.stanford.edu/~wandell/data/papers/2009-CameraShake-Xiao.pdf; they cite F. Xiao et al.,
|
|
30
|
+
* "Camera-motion and mobile imaging", Proc. SPIE 6502, 2007). The value is one rate magnitude; it is
|
|
31
|
+
* placed on the yaw axis (for small angles the blur length does not depend on the direction). A
|
|
32
|
+
* heavier camera, another grip or another photographer moves differently: pass your own profile.
|
|
33
|
+
*/
|
|
34
|
+
export declare const GENERIC_HANDHELD_ANGULAR_VELOCITY_RAD_PER_SEC: Readonly<{
|
|
35
|
+
yaw: number;
|
|
36
|
+
pitch: number;
|
|
37
|
+
}>;
|
|
38
|
+
/** The largest whole-stop bias accepted, either way (makers offer up to 2 stops faster or slower). */
|
|
39
|
+
export declare const AUTO_ISO_SLOWEST_SHUTTER_MAXIMUM_BIAS_STOPS: 2;
|
|
40
|
+
/** The blur allowed during the exposure, on the sensor. */
|
|
41
|
+
export type AutoIsoSlowestShutterBlurBudget = {
|
|
42
|
+
/** The blur trace length in pixels of the given pitch. */
|
|
43
|
+
kind: "pixels";
|
|
44
|
+
pixels: number;
|
|
45
|
+
pixelPitchMicrometers: number;
|
|
46
|
+
} | {
|
|
47
|
+
/** The blur trace length as a fraction of the image diagonal (width × height in millimetres). */
|
|
48
|
+
kind: "image-diagonal-fraction";
|
|
49
|
+
fraction: number;
|
|
50
|
+
imageWidthMm: number;
|
|
51
|
+
imageHeightMm: number;
|
|
52
|
+
};
|
|
53
|
+
export interface EstimateAutoIsoSlowestShutterInput {
|
|
54
|
+
focalLengthMm: number;
|
|
55
|
+
/** Optional focus distance in metres: the thin-lens image distance becomes the projection distance, as in `estimateCameraShakeBlur()`. */
|
|
56
|
+
focusDistanceM?: number;
|
|
57
|
+
/**
|
|
58
|
+
* The hand's constant angular velocity in radians per second, the explicit shake profile (signs as in
|
|
59
|
+
* `estimateCameraShakeBlur()`; only the magnitude matters here). At least one axis must be non-zero.
|
|
60
|
+
* `GENERIC_HANDHELD_ANGULAR_VELOCITY_RAD_PER_SEC` is a cited generic default, not a measurement of you.
|
|
61
|
+
*/
|
|
62
|
+
angularVelocityRadPerSec: {
|
|
63
|
+
yaw: number;
|
|
64
|
+
pitch: number;
|
|
65
|
+
};
|
|
66
|
+
/** Stabilization in equivalent stops (≥ 0); the motion is scaled by 2^−stops. Default 0. */
|
|
67
|
+
stabilizationStopsEquivalent?: number;
|
|
68
|
+
blurBudget: AutoIsoSlowestShutterBlurBudget;
|
|
69
|
+
/**
|
|
70
|
+
* The bias in whole stops, applied after the derivation: +1 is one stop faster (half the time), −1 one
|
|
71
|
+
* stop slower (twice the time). An integer from −2 to +2. Default 0.
|
|
72
|
+
*/
|
|
73
|
+
fasterByStops?: number;
|
|
74
|
+
/** Optional: the body's resolved shutter capability. The biased shutter then snaps to it, never longer than the biased value, and is held at its ends. */
|
|
75
|
+
shutterCapability?: ResolvedGenericEquipmentExposureCapabilities["shutter"];
|
|
76
|
+
}
|
|
77
|
+
export interface AutoIsoSlowestShutterEstimate {
|
|
78
|
+
/** The exposure time whose shake blur equals the budget, before the bias and the grid. */
|
|
79
|
+
derivedShutterSeconds: number;
|
|
80
|
+
fasterByStops: number;
|
|
81
|
+
/** derivedShutterSeconds · 2^−fasterByStops. */
|
|
82
|
+
biasedShutterSeconds: number;
|
|
83
|
+
/** The slowest preferred shutter: the biased value, snapped to `shutterCapability` when given. */
|
|
84
|
+
shutterSeconds: number;
|
|
85
|
+
shutterResolution: {
|
|
86
|
+
kind: "unconstrained" | "continuous" | "discrete";
|
|
87
|
+
selectionPolicy: "not-longer-than-target";
|
|
88
|
+
quantized: boolean;
|
|
89
|
+
clamped: false | "minimum" | "maximum";
|
|
90
|
+
};
|
|
91
|
+
blurBudgetMm: number;
|
|
92
|
+
/** `estimateCameraShakeBlur()`'s stabilized blur at the derived shutter (equal to the budget within rounding). */
|
|
93
|
+
blurAtDerivedShutterMm: number;
|
|
94
|
+
/** `estimateCameraShakeBlur()`'s stabilized blur at `shutterSeconds`. */
|
|
95
|
+
blurAtShutterMm: number;
|
|
96
|
+
/** The same blur lengths in pixels, for a pixel budget. */
|
|
97
|
+
blurBudgetPixels?: number;
|
|
98
|
+
blurAtShutterPixels?: number;
|
|
99
|
+
projectionDistanceMm: number;
|
|
100
|
+
residualMotionFactor: number;
|
|
101
|
+
angularVelocityRadPerSec: {
|
|
102
|
+
yaw: number;
|
|
103
|
+
pitch: number;
|
|
104
|
+
};
|
|
105
|
+
stabilizationStopsEquivalent: number;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Estimates the slowest preferred shutter for Auto ISO from the lens, the sensor and the hand's
|
|
109
|
+
* shake: the exposure time at which `estimateCameraShakeBlur()`'s stabilized blur equals the blur
|
|
110
|
+
* budget, then shifted by a whole-stop bias and optionally snapped to the body's shutter grid (never
|
|
111
|
+
* longer than the biased value). Longer focal lengths give faster shutters; each stabilization stop
|
|
112
|
+
* doubles the time; each bias stop halves (+1) or doubles (−1) it.
|
|
113
|
+
*
|
|
114
|
+
* An approximation: constant angular velocity (a caller profile, or the labelled generic default
|
|
115
|
+
* `GENERIC_HANDHELD_ANGULAR_VELOCITY_RAD_PER_SEC`), stabilization as 2^−stops, blur on the optical axis
|
|
116
|
+
* only. The 1/focal-length "reciprocal rule" is a rule of thumb and is not used.
|
|
117
|
+
* @param input - EstimateAutoIsoSlowestShutterInput.
|
|
118
|
+
* @returns The derived, biased and snapped shutter with the blur at each, as an approximation result.
|
|
119
|
+
*/
|
|
120
|
+
export declare function estimateAutoIsoSlowestShutter(input: EstimateAutoIsoSlowestShutterInput): CalculationResult<AutoIsoSlowestShutterEstimate>;
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* #504: the automatic slowest preferred shutter for Auto ISO ("Auto" minimum shutter speed), derived
|
|
4
|
+
* from the engine's camera-shake model (`estimateCameraShakeBlur()`), not from the 1/focal-length rule
|
|
5
|
+
* of thumb.
|
|
6
|
+
*
|
|
7
|
+
* The camera turns at a constant angular velocity ω (yaw, pitch) during the exposure, reduced by the
|
|
8
|
+
* stabilization factor r = 2^−stops. A still point on the optical axis then moves
|
|
9
|
+
* b(t) = v·hypot(tan(r·ω_yaw·t), tan(r·ω_pitch·t)) on the sensor, where v is the projection distance (the
|
|
10
|
+
* focal length, or the thin-lens image distance with a focus distance). The derived shutter is the
|
|
11
|
+
* exposure time at which b(t) equals the caller's blur budget. b(t) grows strictly with t, so the
|
|
12
|
+
* solution is unique; the whole-stop bias (as in makers' Auto minimum-shutter settings: faster or
|
|
13
|
+
* slower in 1 EV steps) is applied after the derivation, and the optional shutter grid last.
|
|
14
|
+
*
|
|
15
|
+
* This is an approximation: the hand's motion is a constant-velocity profile supplied by the caller,
|
|
16
|
+
* and stabilization stops scale the motion by 2^−stops (the educational model of
|
|
17
|
+
* `estimateCameraShakeBlur()`, not a CIPA DC-011 rating).
|
|
18
|
+
* @see docs/USAGE.md ("Automatic slowest shutter (#504)") for the equations, the cited generic
|
|
19
|
+
* hand-shake default and the support limits.
|
|
20
|
+
*/
|
|
21
|
+
import { approximationResult } from "../core/calculation-result.js";
|
|
22
|
+
import { InvalidConfigurationError } from "../core/configuration-error.js";
|
|
23
|
+
import { rejectUnknownFields, requireInputKeys, requireInputObject } from "../core/record-validation.js";
|
|
24
|
+
import { InvalidScientificInputError, InvalidScientificResultError } from "../core/validation.js";
|
|
25
|
+
import { portableHypot } from "../core/portable-math.js";
|
|
26
|
+
import { estimateCameraShakeBlur } from "../stabilization/camera-shake.js";
|
|
27
|
+
export const AUTO_ISO_SLOWEST_SHUTTER_MODEL_VERSION = "1.0.0";
|
|
28
|
+
/**
|
|
29
|
+
* A generic handheld angular velocity, labelled as a default and not a measurement of any camera or
|
|
30
|
+
* photographer: 4.39 °/s (≈ 0.0766 rad/s), the linear camera-motion rate Xiao, Farrell, Catrysse and
|
|
31
|
+
* Wandell use for "an average user" holding "a typical 100g mobile handset with two hands", from their
|
|
32
|
+
* earlier gyroscope measurements (F. Xiao, J. Farrell, P. Catrysse, B. Wandell, "Mobile imaging: the
|
|
33
|
+
* big challenge of the small pixel", Proc. SPIE 7250, Digital Photography V, 2009, §2; author copy:
|
|
34
|
+
* https://web.stanford.edu/~wandell/data/papers/2009-CameraShake-Xiao.pdf; they cite F. Xiao et al.,
|
|
35
|
+
* "Camera-motion and mobile imaging", Proc. SPIE 6502, 2007). The value is one rate magnitude; it is
|
|
36
|
+
* placed on the yaw axis (for small angles the blur length does not depend on the direction). A
|
|
37
|
+
* heavier camera, another grip or another photographer moves differently: pass your own profile.
|
|
38
|
+
*/
|
|
39
|
+
export const GENERIC_HANDHELD_ANGULAR_VELOCITY_RAD_PER_SEC =
|
|
40
|
+
/* @__PURE__ */ Object.freeze({ yaw: (4.39 * Math.PI) / 180, pitch: 0 });
|
|
41
|
+
/** The largest whole-stop bias accepted, either way (makers offer up to 2 stops faster or slower). */
|
|
42
|
+
export const AUTO_ISO_SLOWEST_SHUTTER_MAXIMUM_BIAS_STOPS = 2;
|
|
43
|
+
function requirePositive(value, path) {
|
|
44
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
|
|
45
|
+
throw new InvalidScientificInputError(path + " must be finite and greater than zero.");
|
|
46
|
+
}
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
function parseBudget(value) {
|
|
50
|
+
requireInputObject(value, "blurBudget");
|
|
51
|
+
const record = value;
|
|
52
|
+
if (record.kind === "pixels") {
|
|
53
|
+
rejectUnknownFields(record, ["kind", "pixels", "pixelPitchMicrometers"], "blurBudget");
|
|
54
|
+
const pixels = requirePositive(record.pixels, "blurBudget.pixels");
|
|
55
|
+
const pitch = requirePositive(record.pixelPitchMicrometers, "blurBudget.pixelPitchMicrometers");
|
|
56
|
+
return { budgetMm: (pixels * pitch) / 1000, budgetPixels: pixels, pixelPitchMicrometers: pitch };
|
|
57
|
+
}
|
|
58
|
+
if (record.kind === "image-diagonal-fraction") {
|
|
59
|
+
rejectUnknownFields(record, ["kind", "fraction", "imageWidthMm", "imageHeightMm"], "blurBudget");
|
|
60
|
+
const fraction = requirePositive(record.fraction, "blurBudget.fraction");
|
|
61
|
+
if (fraction >= 1)
|
|
62
|
+
throw new InvalidScientificInputError("blurBudget.fraction must be less than 1.");
|
|
63
|
+
const width = requirePositive(record.imageWidthMm, "blurBudget.imageWidthMm");
|
|
64
|
+
const height = requirePositive(record.imageHeightMm, "blurBudget.imageHeightMm");
|
|
65
|
+
return { budgetMm: fraction * portableHypot(width, height) };
|
|
66
|
+
}
|
|
67
|
+
throw new InvalidConfigurationError('blurBudget.kind must be "pixels" or "image-diagonal-fraction".');
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Solves v·hypot(tan(a·t), tan(b·t)) = budget for t, with a, b ≥ 0 the stabilized angular speeds. One
|
|
71
|
+
* axis: t = atan(budget/v)/a exactly. Two axes: bisection on the strictly increasing blur over
|
|
72
|
+
* (0, π/(2·max(a, b))), a fixed number of halvings (deterministic, to the last bit of the bracket).
|
|
73
|
+
*/
|
|
74
|
+
function solveShutterForBudget(projectionMm, a, b, budgetMm) {
|
|
75
|
+
const ratio = budgetMm / projectionMm;
|
|
76
|
+
if (a === 0 || b === 0)
|
|
77
|
+
return Math.atan(ratio) / Math.max(a, b);
|
|
78
|
+
let low = 0, high = Math.PI / 2 / Math.max(a, b);
|
|
79
|
+
for (let i = 0; i < 200 && high - low > 0; i++) {
|
|
80
|
+
const mid = low + (high - low) / 2;
|
|
81
|
+
if (mid <= low || mid >= high)
|
|
82
|
+
break;
|
|
83
|
+
if (portableHypot(Math.tan(a * mid), Math.tan(b * mid)) < ratio)
|
|
84
|
+
low = mid;
|
|
85
|
+
else
|
|
86
|
+
high = mid;
|
|
87
|
+
}
|
|
88
|
+
return low + (high - low) / 2;
|
|
89
|
+
}
|
|
90
|
+
function snapNotLonger(target, shutter) {
|
|
91
|
+
const grid = shutter.settingGrid;
|
|
92
|
+
if (grid.kind === "continuous-within-range") {
|
|
93
|
+
if (target < shutter.minimumSeconds)
|
|
94
|
+
return { shutterSeconds: shutter.minimumSeconds, kind: "continuous", quantized: false, clamped: "minimum" };
|
|
95
|
+
if (target > shutter.maximumSeconds)
|
|
96
|
+
return { shutterSeconds: shutter.maximumSeconds, kind: "continuous", quantized: false, clamped: "maximum" };
|
|
97
|
+
return { shutterSeconds: target, kind: "continuous", quantized: false, clamped: false };
|
|
98
|
+
}
|
|
99
|
+
const values = grid.values;
|
|
100
|
+
const tolerance = (x) => Number.EPSILON * 32 * Math.max(Math.abs(x), 1);
|
|
101
|
+
const exact = values.find((v) => Math.abs(v - target) <= tolerance(Math.max(v, target)));
|
|
102
|
+
if (exact !== undefined)
|
|
103
|
+
return { shutterSeconds: exact, kind: "discrete", quantized: false, clamped: false };
|
|
104
|
+
if (target < values[0])
|
|
105
|
+
return { shutterSeconds: values[0], kind: "discrete", quantized: true, clamped: "minimum" };
|
|
106
|
+
const last = values[values.length - 1];
|
|
107
|
+
if (target > last)
|
|
108
|
+
return { shutterSeconds: last, kind: "discrete", quantized: true, clamped: "maximum" };
|
|
109
|
+
let selected = values[0];
|
|
110
|
+
for (const v of values)
|
|
111
|
+
if (v <= target)
|
|
112
|
+
selected = v;
|
|
113
|
+
return { shutterSeconds: selected, kind: "discrete", quantized: true, clamped: false };
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Estimates the slowest preferred shutter for Auto ISO from the lens, the sensor and the hand's
|
|
117
|
+
* shake: the exposure time at which `estimateCameraShakeBlur()`'s stabilized blur equals the blur
|
|
118
|
+
* budget, then shifted by a whole-stop bias and optionally snapped to the body's shutter grid (never
|
|
119
|
+
* longer than the biased value). Longer focal lengths give faster shutters; each stabilization stop
|
|
120
|
+
* doubles the time; each bias stop halves (+1) or doubles (−1) it.
|
|
121
|
+
*
|
|
122
|
+
* An approximation: constant angular velocity (a caller profile, or the labelled generic default
|
|
123
|
+
* `GENERIC_HANDHELD_ANGULAR_VELOCITY_RAD_PER_SEC`), stabilization as 2^−stops, blur on the optical axis
|
|
124
|
+
* only. The 1/focal-length "reciprocal rule" is a rule of thumb and is not used.
|
|
125
|
+
* @param input - EstimateAutoIsoSlowestShutterInput.
|
|
126
|
+
* @returns The derived, biased and snapped shutter with the blur at each, as an approximation result.
|
|
127
|
+
*/
|
|
128
|
+
export function estimateAutoIsoSlowestShutter(input) {
|
|
129
|
+
requireInputKeys(input, "estimateAutoIsoSlowestShutter", ["focalLengthMm", "focusDistanceM", "angularVelocityRadPerSec", "stabilizationStopsEquivalent",
|
|
130
|
+
"blurBudget", "fasterByStops", "shutterCapability"]);
|
|
131
|
+
const focalLengthMm = requirePositive(input.focalLengthMm, "focalLengthMm");
|
|
132
|
+
requireInputObject(input.angularVelocityRadPerSec, "angularVelocityRadPerSec");
|
|
133
|
+
rejectUnknownFields(input.angularVelocityRadPerSec, ["yaw", "pitch"], "angularVelocityRadPerSec");
|
|
134
|
+
const { yaw, pitch } = input.angularVelocityRadPerSec;
|
|
135
|
+
for (const [name, v] of [["yaw", yaw], ["pitch", pitch]]) {
|
|
136
|
+
if (typeof v !== "number" || !Number.isFinite(v))
|
|
137
|
+
throw new InvalidScientificInputError("angularVelocityRadPerSec." + name + " must be finite.");
|
|
138
|
+
}
|
|
139
|
+
if (yaw === 0 && pitch === 0) {
|
|
140
|
+
throw new InvalidScientificInputError("angularVelocityRadPerSec must be non-zero on at least one axis: without shake no shutter limit follows.");
|
|
141
|
+
}
|
|
142
|
+
const stops = input.stabilizationStopsEquivalent ?? 0;
|
|
143
|
+
if (typeof stops !== "number" || !Number.isFinite(stops) || stops < 0) {
|
|
144
|
+
throw new InvalidScientificInputError("stabilizationStopsEquivalent must be finite and greater than or equal to zero.");
|
|
145
|
+
}
|
|
146
|
+
const bias = input.fasterByStops ?? 0;
|
|
147
|
+
if (typeof bias !== "number" || !Number.isInteger(bias) || Math.abs(bias) > AUTO_ISO_SLOWEST_SHUTTER_MAXIMUM_BIAS_STOPS) {
|
|
148
|
+
throw new InvalidScientificInputError("fasterByStops must be a whole number of stops from -2 to +2.");
|
|
149
|
+
}
|
|
150
|
+
const budget = parseBudget(input.blurBudget);
|
|
151
|
+
if (input.focusDistanceM !== undefined)
|
|
152
|
+
requirePositive(input.focusDistanceM, "focusDistanceM");
|
|
153
|
+
if (input.shutterCapability !== undefined) {
|
|
154
|
+
requireInputObject(input.shutterCapability, "shutterCapability");
|
|
155
|
+
const s = input.shutterCapability;
|
|
156
|
+
requirePositive(s.minimumSeconds, "shutterCapability.minimumSeconds");
|
|
157
|
+
requirePositive(s.maximumSeconds, "shutterCapability.maximumSeconds");
|
|
158
|
+
if (s.minimumSeconds > s.maximumSeconds)
|
|
159
|
+
throw new InvalidScientificInputError("shutterCapability.minimumSeconds must not exceed shutterCapability.maximumSeconds.");
|
|
160
|
+
requireInputObject(s.settingGrid, "shutterCapability.settingGrid");
|
|
161
|
+
if (s.settingGrid.kind === "discrete-values") {
|
|
162
|
+
const values = s.settingGrid.values;
|
|
163
|
+
if (!Array.isArray(values) || values.length === 0 || values.some((v, i) => typeof v !== "number" || !Number.isFinite(v) || v <= 0 || (i > 0 && v <= values[i - 1]))) {
|
|
164
|
+
throw new InvalidScientificInputError("shutterCapability.settingGrid.values must be strictly increasing positive finite seconds.");
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
else if (s.settingGrid.kind !== "continuous-within-range") {
|
|
168
|
+
throw new InvalidConfigurationError("shutterCapability.settingGrid.kind is invalid.");
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// The projection distance and the residual factor come from the shake model itself (a probe at a tiny time).
|
|
172
|
+
const shake = (shutterSeconds) => estimateCameraShakeBlur({ focalLengthMm, shutterSeconds, angularVelocityRadPerSec: { yaw, pitch },
|
|
173
|
+
stabilizationStopsEquivalent: stops, ...(input.focusDistanceM === undefined ? {} : { focusDistanceM: input.focusDistanceM }),
|
|
174
|
+
...(budget.pixelPitchMicrometers === undefined ? {} : { pixelPitchMicrometers: budget.pixelPitchMicrometers }) }).value;
|
|
175
|
+
const probe = shake(Number.MIN_VALUE);
|
|
176
|
+
const r = probe.residualMotionFactor, v = probe.projectionDistanceMm;
|
|
177
|
+
const derived = solveShutterForBudget(v, Math.abs(yaw) * r, Math.abs(pitch) * r, budget.budgetMm);
|
|
178
|
+
const biased = derived * 2 ** -bias;
|
|
179
|
+
if (!Number.isFinite(derived) || derived <= 0 || !Number.isFinite(biased) || biased <= 0) {
|
|
180
|
+
throw new InvalidScientificResultError("The derived slowest shutter is not a positive finite time.");
|
|
181
|
+
}
|
|
182
|
+
const snapped = input.shutterCapability === undefined ? undefined : snapNotLonger(biased, input.shutterCapability);
|
|
183
|
+
const shutterSeconds = snapped?.shutterSeconds ?? biased;
|
|
184
|
+
// estimateCameraShakeBlur rejects a quarter turn or more on an axis of the *unstabilized* motion, which
|
|
185
|
+
// a long biased or clamped time with stabilization can reach; the blur there is not reported.
|
|
186
|
+
const quarterTurnSafe = (t) => Math.max(Math.abs(yaw), Math.abs(pitch)) * t < Math.PI / 2;
|
|
187
|
+
if (!quarterTurnSafe(derived) || !quarterTurnSafe(shutterSeconds)) {
|
|
188
|
+
throw new InvalidScientificInputError("The shake over the slowest shutter would reach a quarter turn (|angular velocity × shutter| ≥ π/2 rad) before stabilization; the constant-velocity model does not apply.");
|
|
189
|
+
}
|
|
190
|
+
const atDerived = shake(derived).stabilized, atShutter = shake(shutterSeconds).stabilized;
|
|
191
|
+
return approximationResult({
|
|
192
|
+
derivedShutterSeconds: derived,
|
|
193
|
+
fasterByStops: bias,
|
|
194
|
+
biasedShutterSeconds: biased,
|
|
195
|
+
shutterSeconds,
|
|
196
|
+
shutterResolution: snapped === undefined
|
|
197
|
+
? { kind: "unconstrained", selectionPolicy: "not-longer-than-target", quantized: false, clamped: false }
|
|
198
|
+
: { kind: snapped.kind, selectionPolicy: "not-longer-than-target", quantized: snapped.quantized, clamped: snapped.clamped },
|
|
199
|
+
blurBudgetMm: budget.budgetMm,
|
|
200
|
+
blurAtDerivedShutterMm: atDerived.distanceMm,
|
|
201
|
+
blurAtShutterMm: atShutter.distanceMm,
|
|
202
|
+
...(budget.budgetPixels === undefined ? {} : { blurBudgetPixels: budget.budgetPixels, blurAtShutterPixels: atShutter.distancePixels }),
|
|
203
|
+
projectionDistanceMm: v,
|
|
204
|
+
residualMotionFactor: r,
|
|
205
|
+
angularVelocityRadPerSec: { yaw, pitch },
|
|
206
|
+
stabilizationStopsEquivalent: stops
|
|
207
|
+
}, "camera-shake-blur-budget-slowest-shutter", AUTO_ISO_SLOWEST_SHUTTER_MODEL_VERSION, [
|
|
208
|
+
"The slowest shutter is the exposure time at which estimateCameraShakeBlur()'s stabilized on-axis blur equals the blur budget",
|
|
209
|
+
"The hand turns the camera at a constant angular velocity during the exposure (a caller-supplied profile)",
|
|
210
|
+
"Stabilization attenuates the angular motion by 2^-stops (educational model, not a CIPA DC-011 rating)",
|
|
211
|
+
"The whole-stop bias multiplies the derived time by 2^-fasterByStops, after the derivation",
|
|
212
|
+
"Snapping to the shutter grid never lengthens the time, so the blur stays within the budget unless held at the body's fastest shutter",
|
|
213
|
+
"The 1/focal-length reciprocal rule is a rule of thumb and is not used",
|
|
214
|
+
"Roll, translation, off-axis field dependence and subject motion are not modelled"
|
|
215
|
+
]);
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=auto-iso-slowest-shutter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auto-iso-slowest-shutter.js","sourceRoot":"","sources":["../../src/exposure/auto-iso-slowest-shutter.ts"],"names":[],"mappings":"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","sourcesContent":["// SPDX-License-Identifier: Apache-2.0\n\n/**\n * #504: the automatic slowest preferred shutter for Auto ISO (\"Auto\" minimum shutter speed), derived\n * from the engine's camera-shake model (`estimateCameraShakeBlur()`), not from the 1/focal-length rule\n * of thumb.\n *\n * The camera turns at a constant angular velocity ω (yaw, pitch) during the exposure, reduced by the\n * stabilization factor r = 2^−stops. A still point on the optical axis then moves\n * b(t) = v·hypot(tan(r·ω_yaw·t), tan(r·ω_pitch·t)) on the sensor, where v is the projection distance (the\n * focal length, or the thin-lens image distance with a focus distance). The derived shutter is the\n * exposure time at which b(t) equals the caller's blur budget. b(t) grows strictly with t, so the\n * solution is unique; the whole-stop bias (as in makers' Auto minimum-shutter settings: faster or\n * slower in 1 EV steps) is applied after the derivation, and the optional shutter grid last.\n *\n * This is an approximation: the hand's motion is a constant-velocity profile supplied by the caller,\n * and stabilization stops scale the motion by 2^−stops (the educational model of\n * `estimateCameraShakeBlur()`, not a CIPA DC-011 rating).\n * @see docs/USAGE.md (\"Automatic slowest shutter (#504)\") for the equations, the cited generic\n * hand-shake default and the support limits.\n */\n\nimport { approximationResult, type CalculationResult } from \"../core/calculation-result.js\";\nimport { InvalidConfigurationError } from \"../core/configuration-error.js\";\nimport { rejectUnknownFields, requireInputKeys, requireInputObject } from \"../core/record-validation.js\";\nimport { InvalidScientificInputError, InvalidScientificResultError } from \"../core/validation.js\";\nimport { portableHypot } from \"../core/portable-math.js\";\nimport type { ResolvedGenericEquipmentExposureCapabilities } from \"../equipment/exposure-capabilities.js\";\nimport { estimateCameraShakeBlur, type CameraShakeEstimate } from \"../stabilization/camera-shake.js\";\n\nexport const AUTO_ISO_SLOWEST_SHUTTER_MODEL_VERSION = \"1.0.0\" as const;\n\n/**\n * A generic handheld angular velocity, labelled as a default and not a measurement of any camera or\n * photographer: 4.39 °/s (≈ 0.0766 rad/s), the linear camera-motion rate Xiao, Farrell, Catrysse and\n * Wandell use for \"an average user\" holding \"a typical 100g mobile handset with two hands\", from their\n * earlier gyroscope measurements (F. Xiao, J. Farrell, P. Catrysse, B. Wandell, \"Mobile imaging: the\n * big challenge of the small pixel\", Proc. SPIE 7250, Digital Photography V, 2009, §2; author copy:\n * https://web.stanford.edu/~wandell/data/papers/2009-CameraShake-Xiao.pdf; they cite F. Xiao et al.,\n * \"Camera-motion and mobile imaging\", Proc. SPIE 6502, 2007). The value is one rate magnitude; it is\n * placed on the yaw axis (for small angles the blur length does not depend on the direction). A\n * heavier camera, another grip or another photographer moves differently: pass your own profile.\n */\nexport const GENERIC_HANDHELD_ANGULAR_VELOCITY_RAD_PER_SEC: Readonly<{ yaw: number; pitch: number }> =\n /* @__PURE__ */ Object.freeze({ yaw: (4.39 * Math.PI) / 180, pitch: 0 });\n\n/** The largest whole-stop bias accepted, either way (makers offer up to 2 stops faster or slower). */\nexport const AUTO_ISO_SLOWEST_SHUTTER_MAXIMUM_BIAS_STOPS = 2 as const;\n\n/** The blur allowed during the exposure, on the sensor. */\nexport type AutoIsoSlowestShutterBlurBudget =\n | {\n /** The blur trace length in pixels of the given pitch. */\n kind: \"pixels\";\n pixels: number;\n pixelPitchMicrometers: number;\n }\n | {\n /** The blur trace length as a fraction of the image diagonal (width × height in millimetres). */\n kind: \"image-diagonal-fraction\";\n fraction: number;\n imageWidthMm: number;\n imageHeightMm: number;\n };\n\nexport interface EstimateAutoIsoSlowestShutterInput {\n focalLengthMm: number;\n /** Optional focus distance in metres: the thin-lens image distance becomes the projection distance, as in `estimateCameraShakeBlur()`. */\n focusDistanceM?: number;\n /**\n * The hand's constant angular velocity in radians per second, the explicit shake profile (signs as in\n * `estimateCameraShakeBlur()`; only the magnitude matters here). At least one axis must be non-zero.\n * `GENERIC_HANDHELD_ANGULAR_VELOCITY_RAD_PER_SEC` is a cited generic default, not a measurement of you.\n */\n angularVelocityRadPerSec: { yaw: number; pitch: number };\n /** Stabilization in equivalent stops (≥ 0); the motion is scaled by 2^−stops. Default 0. */\n stabilizationStopsEquivalent?: number;\n blurBudget: AutoIsoSlowestShutterBlurBudget;\n /**\n * The bias in whole stops, applied after the derivation: +1 is one stop faster (half the time), −1 one\n * stop slower (twice the time). An integer from −2 to +2. Default 0.\n */\n fasterByStops?: number;\n /** Optional: the body's resolved shutter capability. The biased shutter then snaps to it, never longer than the biased value, and is held at its ends. */\n shutterCapability?: ResolvedGenericEquipmentExposureCapabilities[\"shutter\"];\n}\n\nexport interface AutoIsoSlowestShutterEstimate {\n /** The exposure time whose shake blur equals the budget, before the bias and the grid. */\n derivedShutterSeconds: number;\n fasterByStops: number;\n /** derivedShutterSeconds · 2^−fasterByStops. */\n biasedShutterSeconds: number;\n /** The slowest preferred shutter: the biased value, snapped to `shutterCapability` when given. */\n shutterSeconds: number;\n shutterResolution: {\n kind: \"unconstrained\" | \"continuous\" | \"discrete\";\n selectionPolicy: \"not-longer-than-target\";\n quantized: boolean;\n clamped: false | \"minimum\" | \"maximum\";\n };\n blurBudgetMm: number;\n /** `estimateCameraShakeBlur()`'s stabilized blur at the derived shutter (equal to the budget within rounding). */\n blurAtDerivedShutterMm: number;\n /** `estimateCameraShakeBlur()`'s stabilized blur at `shutterSeconds`. */\n blurAtShutterMm: number;\n /** The same blur lengths in pixels, for a pixel budget. */\n blurBudgetPixels?: number;\n blurAtShutterPixels?: number;\n projectionDistanceMm: number;\n residualMotionFactor: number;\n angularVelocityRadPerSec: { yaw: number; pitch: number };\n stabilizationStopsEquivalent: number;\n}\n\nfunction requirePositive(value: unknown, path: string): number {\n if (typeof value !== \"number\" || !Number.isFinite(value) || value <= 0) {\n throw new InvalidScientificInputError(path + \" must be finite and greater than zero.\");\n }\n return value;\n}\n\nfunction parseBudget(value: unknown): { budgetMm: number; budgetPixels?: number; pixelPitchMicrometers?: number } {\n requireInputObject(value, \"blurBudget\");\n const record = value as Record<string, unknown>;\n if (record.kind === \"pixels\") {\n rejectUnknownFields(record, [\"kind\", \"pixels\", \"pixelPitchMicrometers\"], \"blurBudget\");\n const pixels = requirePositive(record.pixels, \"blurBudget.pixels\");\n const pitch = requirePositive(record.pixelPitchMicrometers, \"blurBudget.pixelPitchMicrometers\");\n return { budgetMm: (pixels * pitch) / 1000, budgetPixels: pixels, pixelPitchMicrometers: pitch };\n }\n if (record.kind === \"image-diagonal-fraction\") {\n rejectUnknownFields(record, [\"kind\", \"fraction\", \"imageWidthMm\", \"imageHeightMm\"], \"blurBudget\");\n const fraction = requirePositive(record.fraction, \"blurBudget.fraction\");\n if (fraction >= 1) throw new InvalidScientificInputError(\"blurBudget.fraction must be less than 1.\");\n const width = requirePositive(record.imageWidthMm, \"blurBudget.imageWidthMm\");\n const height = requirePositive(record.imageHeightMm, \"blurBudget.imageHeightMm\");\n return { budgetMm: fraction * portableHypot(width, height) };\n }\n throw new InvalidConfigurationError('blurBudget.kind must be \"pixels\" or \"image-diagonal-fraction\".');\n}\n\n/**\n * Solves v·hypot(tan(a·t), tan(b·t)) = budget for t, with a, b ≥ 0 the stabilized angular speeds. One\n * axis: t = atan(budget/v)/a exactly. Two axes: bisection on the strictly increasing blur over\n * (0, π/(2·max(a, b))), a fixed number of halvings (deterministic, to the last bit of the bracket).\n */\nfunction solveShutterForBudget(projectionMm: number, a: number, b: number, budgetMm: number): number {\n const ratio = budgetMm / projectionMm;\n if (a === 0 || b === 0) return Math.atan(ratio) / Math.max(a, b);\n let low = 0, high = Math.PI / 2 / Math.max(a, b);\n for (let i = 0; i < 200 && high - low > 0; i++) {\n const mid = low + (high - low) / 2;\n if (mid <= low || mid >= high) break;\n if (portableHypot(Math.tan(a * mid), Math.tan(b * mid)) < ratio) low = mid;\n else high = mid;\n }\n return low + (high - low) / 2;\n}\n\nfunction snapNotLonger(\n target: number,\n shutter: ResolvedGenericEquipmentExposureCapabilities[\"shutter\"]\n): { shutterSeconds: number; kind: \"continuous\" | \"discrete\"; quantized: boolean; clamped: false | \"minimum\" | \"maximum\" } {\n const grid = shutter.settingGrid;\n if (grid.kind === \"continuous-within-range\") {\n if (target < shutter.minimumSeconds) return { shutterSeconds: shutter.minimumSeconds, kind: \"continuous\", quantized: false, clamped: \"minimum\" };\n if (target > shutter.maximumSeconds) return { shutterSeconds: shutter.maximumSeconds, kind: \"continuous\", quantized: false, clamped: \"maximum\" };\n return { shutterSeconds: target, kind: \"continuous\", quantized: false, clamped: false };\n }\n const values = grid.values;\n const tolerance = (x: number): number => Number.EPSILON * 32 * Math.max(Math.abs(x), 1);\n const exact = values.find((v) => Math.abs(v - target) <= tolerance(Math.max(v, target)));\n if (exact !== undefined) return { shutterSeconds: exact, kind: \"discrete\", quantized: false, clamped: false };\n if (target < values[0]!) return { shutterSeconds: values[0]!, kind: \"discrete\", quantized: true, clamped: \"minimum\" };\n const last = values[values.length - 1]!;\n if (target > last) return { shutterSeconds: last, kind: \"discrete\", quantized: true, clamped: \"maximum\" };\n let selected = values[0]!;\n for (const v of values) if (v <= target) selected = v;\n return { shutterSeconds: selected, kind: \"discrete\", quantized: true, clamped: false };\n}\n\n/**\n * Estimates the slowest preferred shutter for Auto ISO from the lens, the sensor and the hand's\n * shake: the exposure time at which `estimateCameraShakeBlur()`'s stabilized blur equals the blur\n * budget, then shifted by a whole-stop bias and optionally snapped to the body's shutter grid (never\n * longer than the biased value). Longer focal lengths give faster shutters; each stabilization stop\n * doubles the time; each bias stop halves (+1) or doubles (−1) it.\n *\n * An approximation: constant angular velocity (a caller profile, or the labelled generic default\n * `GENERIC_HANDHELD_ANGULAR_VELOCITY_RAD_PER_SEC`), stabilization as 2^−stops, blur on the optical axis\n * only. The 1/focal-length \"reciprocal rule\" is a rule of thumb and is not used.\n * @param input - EstimateAutoIsoSlowestShutterInput.\n * @returns The derived, biased and snapped shutter with the blur at each, as an approximation result.\n */\nexport function estimateAutoIsoSlowestShutter(\n input: EstimateAutoIsoSlowestShutterInput\n): CalculationResult<AutoIsoSlowestShutterEstimate> {\n requireInputKeys(input, \"estimateAutoIsoSlowestShutter\", [\"focalLengthMm\", \"focusDistanceM\", \"angularVelocityRadPerSec\", \"stabilizationStopsEquivalent\",\n \"blurBudget\", \"fasterByStops\", \"shutterCapability\"]);\n const focalLengthMm = requirePositive(input.focalLengthMm, \"focalLengthMm\");\n requireInputObject(input.angularVelocityRadPerSec, \"angularVelocityRadPerSec\");\n rejectUnknownFields(input.angularVelocityRadPerSec, [\"yaw\", \"pitch\"], \"angularVelocityRadPerSec\");\n const { yaw, pitch } = input.angularVelocityRadPerSec;\n for (const [name, v] of [[\"yaw\", yaw], [\"pitch\", pitch]] as const) {\n if (typeof v !== \"number\" || !Number.isFinite(v)) throw new InvalidScientificInputError(\"angularVelocityRadPerSec.\" + name + \" must be finite.\");\n }\n if (yaw === 0 && pitch === 0) {\n throw new InvalidScientificInputError(\"angularVelocityRadPerSec must be non-zero on at least one axis: without shake no shutter limit follows.\");\n }\n const stops = input.stabilizationStopsEquivalent ?? 0;\n if (typeof stops !== \"number\" || !Number.isFinite(stops) || stops < 0) {\n throw new InvalidScientificInputError(\"stabilizationStopsEquivalent must be finite and greater than or equal to zero.\");\n }\n const bias = input.fasterByStops ?? 0;\n if (typeof bias !== \"number\" || !Number.isInteger(bias) || Math.abs(bias) > AUTO_ISO_SLOWEST_SHUTTER_MAXIMUM_BIAS_STOPS) {\n throw new InvalidScientificInputError(\"fasterByStops must be a whole number of stops from -2 to +2.\");\n }\n const budget = parseBudget(input.blurBudget);\n if (input.focusDistanceM !== undefined) requirePositive(input.focusDistanceM, \"focusDistanceM\");\n if (input.shutterCapability !== undefined) {\n requireInputObject(input.shutterCapability, \"shutterCapability\");\n const s = input.shutterCapability;\n requirePositive(s.minimumSeconds, \"shutterCapability.minimumSeconds\");\n requirePositive(s.maximumSeconds, \"shutterCapability.maximumSeconds\");\n if (s.minimumSeconds > s.maximumSeconds) throw new InvalidScientificInputError(\"shutterCapability.minimumSeconds must not exceed shutterCapability.maximumSeconds.\");\n requireInputObject(s.settingGrid, \"shutterCapability.settingGrid\");\n if (s.settingGrid.kind === \"discrete-values\") {\n const values = s.settingGrid.values;\n if (!Array.isArray(values) || values.length === 0 || values.some((v, i) => typeof v !== \"number\" || !Number.isFinite(v) || v <= 0 || (i > 0 && v <= values[i - 1]!))) {\n throw new InvalidScientificInputError(\"shutterCapability.settingGrid.values must be strictly increasing positive finite seconds.\");\n }\n } else if (s.settingGrid.kind !== \"continuous-within-range\") {\n throw new InvalidConfigurationError(\"shutterCapability.settingGrid.kind is invalid.\");\n }\n }\n\n // The projection distance and the residual factor come from the shake model itself (a probe at a tiny time).\n const shake = (shutterSeconds: number): CameraShakeEstimate => estimateCameraShakeBlur({ focalLengthMm, shutterSeconds, angularVelocityRadPerSec: { yaw, pitch },\n stabilizationStopsEquivalent: stops, ...(input.focusDistanceM === undefined ? {} : { focusDistanceM: input.focusDistanceM }),\n ...(budget.pixelPitchMicrometers === undefined ? {} : { pixelPitchMicrometers: budget.pixelPitchMicrometers }) }).value;\n const probe = shake(Number.MIN_VALUE);\n const r = probe.residualMotionFactor, v = probe.projectionDistanceMm;\n const derived = solveShutterForBudget(v, Math.abs(yaw) * r, Math.abs(pitch) * r, budget.budgetMm);\n const biased = derived * 2 ** -bias;\n if (!Number.isFinite(derived) || derived <= 0 || !Number.isFinite(biased) || biased <= 0) {\n throw new InvalidScientificResultError(\"The derived slowest shutter is not a positive finite time.\");\n }\n const snapped = input.shutterCapability === undefined ? undefined : snapNotLonger(biased, input.shutterCapability);\n const shutterSeconds = snapped?.shutterSeconds ?? biased;\n // estimateCameraShakeBlur rejects a quarter turn or more on an axis of the *unstabilized* motion, which\n // a long biased or clamped time with stabilization can reach; the blur there is not reported.\n const quarterTurnSafe = (t: number): boolean => Math.max(Math.abs(yaw), Math.abs(pitch)) * t < Math.PI / 2;\n if (!quarterTurnSafe(derived) || !quarterTurnSafe(shutterSeconds)) {\n throw new InvalidScientificInputError(\n \"The shake over the slowest shutter would reach a quarter turn (|angular velocity × shutter| ≥ π/2 rad) before stabilization; the constant-velocity model does not apply.\");\n }\n const atDerived = shake(derived).stabilized, atShutter = shake(shutterSeconds).stabilized;\n\n return approximationResult<AutoIsoSlowestShutterEstimate>({\n derivedShutterSeconds: derived,\n fasterByStops: bias,\n biasedShutterSeconds: biased,\n shutterSeconds,\n shutterResolution: snapped === undefined\n ? { kind: \"unconstrained\", selectionPolicy: \"not-longer-than-target\", quantized: false, clamped: false }\n : { kind: snapped.kind, selectionPolicy: \"not-longer-than-target\", quantized: snapped.quantized, clamped: snapped.clamped },\n blurBudgetMm: budget.budgetMm,\n blurAtDerivedShutterMm: atDerived.distanceMm,\n blurAtShutterMm: atShutter.distanceMm,\n ...(budget.budgetPixels === undefined ? {} : { blurBudgetPixels: budget.budgetPixels, blurAtShutterPixels: atShutter.distancePixels! }),\n projectionDistanceMm: v,\n residualMotionFactor: r,\n angularVelocityRadPerSec: { yaw, pitch },\n stabilizationStopsEquivalent: stops\n }, \"camera-shake-blur-budget-slowest-shutter\", AUTO_ISO_SLOWEST_SHUTTER_MODEL_VERSION, [\n \"The slowest shutter is the exposure time at which estimateCameraShakeBlur()'s stabilized on-axis blur equals the blur budget\",\n \"The hand turns the camera at a constant angular velocity during the exposure (a caller-supplied profile)\",\n \"Stabilization attenuates the angular motion by 2^-stops (educational model, not a CIPA DC-011 rating)\",\n \"The whole-stop bias multiplies the derived time by 2^-fasterByStops, after the derivation\",\n \"Snapping to the shutter grid never lengthens the time, so the blur stays within the budget unless held at the body's fastest shutter\",\n \"The 1/focal-length reciprocal rule is a rule of thumb and is not used\",\n \"Roll, translation, off-axis field dependence and subject motion are not modelled\"\n ]);\n}\n"]}
|