@photivra/engine 0.1.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/CITATION.cff +23 -0
- package/LICENSE +201 -0
- package/NOTICE +9 -0
- package/README.md +235 -0
- package/dist/camera/field-of-view.d.ts +37 -0
- package/dist/camera/field-of-view.d.ts.map +1 -0
- package/dist/camera/field-of-view.js +47 -0
- package/dist/camera/field-of-view.js.map +1 -0
- package/dist/camera/projected-object-size.d.ts +34 -0
- package/dist/camera/projected-object-size.d.ts.map +1 -0
- package/dist/camera/projected-object-size.js +57 -0
- package/dist/camera/projected-object-size.js.map +1 -0
- package/dist/core/calculation-result.d.ts +125 -0
- package/dist/core/calculation-result.d.ts.map +1 -0
- package/dist/core/calculation-result.js +175 -0
- package/dist/core/calculation-result.js.map +1 -0
- package/dist/core/validation.d.ts +30 -0
- package/dist/core/validation.d.ts.map +1 -0
- package/dist/core/validation.js +45 -0
- package/dist/core/validation.js.map +1 -0
- package/dist/exposure/exposure.d.ts +50 -0
- package/dist/exposure/exposure.d.ts.map +1 -0
- package/dist/exposure/exposure.js +62 -0
- package/dist/exposure/exposure.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/motion/projected-motion.d.ts +41 -0
- package/dist/motion/projected-motion.d.ts.map +1 -0
- package/dist/motion/projected-motion.js +84 -0
- package/dist/motion/projected-motion.js.map +1 -0
- package/dist/optics/aperture.d.ts +39 -0
- package/dist/optics/aperture.d.ts.map +1 -0
- package/dist/optics/aperture.js +62 -0
- package/dist/optics/aperture.js.map +1 -0
- package/dist/optics/circle-of-confusion.d.ts +23 -0
- package/dist/optics/circle-of-confusion.d.ts.map +1 -0
- package/dist/optics/circle-of-confusion.js +32 -0
- package/dist/optics/circle-of-confusion.js.map +1 -0
- package/dist/optics/depth-of-field.d.ts +53 -0
- package/dist/optics/depth-of-field.d.ts.map +1 -0
- package/dist/optics/depth-of-field.js +81 -0
- package/dist/optics/depth-of-field.js.map +1 -0
- package/dist/optics/diffraction.d.ts +26 -0
- package/dist/optics/diffraction.d.ts.map +1 -0
- package/dist/optics/diffraction.js +21 -0
- package/dist/optics/diffraction.js.map +1 -0
- package/dist/optics/thin-lens.d.ts +30 -0
- package/dist/optics/thin-lens.d.ts.map +1 -0
- package/dist/optics/thin-lens.js +33 -0
- package/dist/optics/thin-lens.js.map +1 -0
- package/dist/output/crop.d.ts +27 -0
- package/dist/output/crop.d.ts.map +1 -0
- package/dist/output/crop.js +26 -0
- package/dist/output/crop.js.map +1 -0
- package/dist/output/subject-framing-crop.d.ts +36 -0
- package/dist/output/subject-framing-crop.d.ts.map +1 -0
- package/dist/output/subject-framing-crop.js +42 -0
- package/dist/output/subject-framing-crop.js.map +1 -0
- package/dist/schema/camera.d.ts +34 -0
- package/dist/schema/camera.d.ts.map +1 -0
- package/dist/schema/camera.js +3 -0
- package/dist/schema/camera.js.map +1 -0
- package/dist/schema/scene.d.ts +69 -0
- package/dist/schema/scene.d.ts.map +1 -0
- package/dist/schema/scene.js +3 -0
- package/dist/schema/scene.js.map +1 -0
- package/dist/schema/validation.d.ts +25 -0
- package/dist/schema/validation.d.ts.map +1 -0
- package/dist/schema/validation.js +180 -0
- package/dist/schema/validation.js.map +1 -0
- package/dist/sensor/pixel-pitch.d.ts +21 -0
- package/dist/sensor/pixel-pitch.d.ts.map +1 -0
- package/dist/sensor/pixel-pitch.js +19 -0
- package/dist/sensor/pixel-pitch.js.map +1 -0
- package/dist/sensor/signal-noise.d.ts +37 -0
- package/dist/sensor/signal-noise.d.ts.map +1 -0
- package/dist/sensor/signal-noise.js +53 -0
- package/dist/sensor/signal-noise.js.map +1 -0
- package/dist/simulation/poc-simulation.d.ts +264 -0
- package/dist/simulation/poc-simulation.d.ts.map +1 -0
- package/dist/simulation/poc-simulation.js +449 -0
- package/dist/simulation/poc-simulation.js.map +1 -0
- package/dist/stabilization/camera-shake.d.ts +41 -0
- package/dist/stabilization/camera-shake.d.ts.map +1 -0
- package/dist/stabilization/camera-shake.js +79 -0
- package/dist/stabilization/camera-shake.js.map +1 -0
- package/docs/API_STYLE.md +65 -0
- package/docs/ARCHITECTURE.md +51 -0
- package/docs/MOTION_AND_SIGNAL.md +53 -0
- package/docs/PHYSICS_FOUNDATION.md +92 -0
- package/docs/POC_API.md +210 -0
- package/docs/PROVENANCE.md +87 -0
- package/docs/STABILIZATION.md +78 -0
- package/docs/USAGE.md +538 -0
- package/package.json +91 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Public API Style
|
|
2
|
+
|
|
3
|
+
The open engine should feel like one coherent library.
|
|
4
|
+
|
|
5
|
+
## Rules
|
|
6
|
+
|
|
7
|
+
- Use descriptive `camelCase` function names, normally beginning with a verb such as `calculate`, `estimate`, `parse`, `validate`, or `simulate`.
|
|
8
|
+
- For calculations with more than one meaningful input, accept a single typed/config object rather than positional arguments.
|
|
9
|
+
- Include units in property names when a quantity is not represented by a dedicated unit type: `focalLengthMm`, `distanceM`, `shutterSeconds`.
|
|
10
|
+
- Never accept unitless ambiguous physical quantities.
|
|
11
|
+
- Use the same property name for the same physical concept across modules.
|
|
12
|
+
- Return plain serializable data from core calculations.
|
|
13
|
+
- Public calculation envelopes must not contain `NaN` or numeric infinities; non-finite computed results fail with `InvalidScientificResultError` rather than relying on JSON coercion.
|
|
14
|
+
- Keep deterministic functions synchronous. Use async only for actual asynchronous work.
|
|
15
|
+
- Throw typed/domain errors for invalid configurations; do not silently coerce scientifically invalid values.
|
|
16
|
+
- Public calculation functions and meaningful public contracts require concise JSDoc covering purpose, units, assumptions, and return meaning.
|
|
17
|
+
- Do not expose renderer, framework, or UI-specific types through calculation APIs.
|
|
18
|
+
- Public configuration interfaces that are expected to cross JSON/untrusted boundaries should have corresponding runtime parsers; TypeScript types alone are not a validation boundary.
|
|
19
|
+
- Preserve backward compatibility after 1.0 unless a documented major version changes the contract.
|
|
20
|
+
|
|
21
|
+
## Preferred shape
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
calculateFieldOfView({
|
|
25
|
+
focalLengthMm,
|
|
26
|
+
sensorDimensionMm,
|
|
27
|
+
focusDistanceM
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
calculateDepthOfField({
|
|
31
|
+
focalLengthMm,
|
|
32
|
+
aperture,
|
|
33
|
+
focusDistanceM,
|
|
34
|
+
circleOfConfusionMm
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Avoid one-off positional signatures and inconsistent abbreviations.
|
|
39
|
+
|
|
40
|
+
## Result metadata
|
|
41
|
+
|
|
42
|
+
Primitive public scientific calculation functions return `CalculationResult<T>`, which carries model provenance and optional quality metadata.
|
|
43
|
+
|
|
44
|
+
Not every public API returns a `CalculationResult<T>`. Parsers return validated configuration values, and the composed `simulatePocCamera()` response exposes component and aggregate provenance directly because it combines many calculation results.
|
|
45
|
+
|
|
46
|
+
Optional `quality` metadata is used only when there is defensible accuracy/uncertainty information to report; deterministic analytical results should omit it rather than inventing error bars.
|
|
47
|
+
|
|
48
|
+
Quality metadata supports:
|
|
49
|
+
|
|
50
|
+
- absolute uncertainty with an explicit unit;
|
|
51
|
+
- relative uncertainty as a fractional magnitude;
|
|
52
|
+
- uncertainty source classification: measurement, model approximation, or calibration;
|
|
53
|
+
- optional confidence/coverage level only when its basis can be stated;
|
|
54
|
+
- documented model/calibration valid ranges;
|
|
55
|
+
- concise quality notes.
|
|
56
|
+
|
|
57
|
+
Uncertainty entries identify the affected result with a JSON-style `quantityPath` such as `value.distanceM`. Valid ranges identify the relevant parameter with a path such as `input.focusDistanceM`.
|
|
58
|
+
|
|
59
|
+
Do **not** automatically sum, average, add in quadrature, or otherwise collapse multiple uncertainty components. Combination is permitted only when the implementing model documents the mathematical method plus independence/correlation assumptions. Aggregate simulations should preserve component uncertainty or explicitly state that no combined uncertainty has been calculated.
|
|
60
|
+
|
|
61
|
+
## Versioning
|
|
62
|
+
|
|
63
|
+
`ENGINE_API_VERSION` describes the composed engine API contract and is independent of the npm/package version.
|
|
64
|
+
|
|
65
|
+
Before 1.0, public APIs may evolve with documented changes. After 1.0, breaking public-contract changes require an appropriate major-version transition.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
Photivra separates a reusable scientific core from optional composition and transport layers.
|
|
4
|
+
|
|
5
|
+
## Current root package
|
|
6
|
+
|
|
7
|
+
The browser-safe root package currently owns:
|
|
8
|
+
|
|
9
|
+
- unit-explicit camera, sensor, lens, exposure, focus, scene, and motion contracts;
|
|
10
|
+
- deterministic camera/optics/exposure/crop calculations;
|
|
11
|
+
- the explicitly approximate stabilization-equivalent camera-shake model;
|
|
12
|
+
- sensor signal/noise primitives;
|
|
13
|
+
- schema/runtime validation;
|
|
14
|
+
- provenance and optional uncertainty/quality contracts;
|
|
15
|
+
- the composed `simulatePocCamera()` proof-of-concept calculation.
|
|
16
|
+
|
|
17
|
+
The root package has no runtime npm dependencies. CI walks the root import graph and fails if Node-only modules or the `src/api` transport layer become reachable from it.
|
|
18
|
+
|
|
19
|
+
Comparison, optimization, real-camera calibration databases, renderer effects, and full optical simulation are **not** current root-package capabilities.
|
|
20
|
+
|
|
21
|
+
## Repository-local Node POC transport
|
|
22
|
+
|
|
23
|
+
The repository contains a minimal Node-only HTTP transport under `src/api` for contributor integration testing.
|
|
24
|
+
|
|
25
|
+
It wraps `simulatePocCamera()` with an unauthenticated localhost server. The HTTP layer adds structural request validation and transport behavior; it does not define new camera-science equations.
|
|
26
|
+
|
|
27
|
+
This transport is intentionally **not** part of the public `@photivra/engine` package surface and is excluded from the npm tarball. The POC server is not the intended production architecture.
|
|
28
|
+
|
|
29
|
+
## Optional/future capability modules
|
|
30
|
+
|
|
31
|
+
Future capabilities should compose onto the scientific core rather than silently widening unrelated models. Examples include:
|
|
32
|
+
|
|
33
|
+
- more complete camera-rotation and translation models;
|
|
34
|
+
- panning and rolling/global shutter;
|
|
35
|
+
- flash;
|
|
36
|
+
- spectral/color modeling;
|
|
37
|
+
- macro/high-magnification calibration;
|
|
38
|
+
- non-circular diffraction PSFs;
|
|
39
|
+
- computational capture;
|
|
40
|
+
- lens calibration profiles;
|
|
41
|
+
- advanced/calibrated sensor profiles;
|
|
42
|
+
- comparison and optimization systems;
|
|
43
|
+
- renderer-specific effects.
|
|
44
|
+
|
|
45
|
+
Capabilities should remain discoverable and explicitly versioned rather than being assumed present for every scene or runtime.
|
|
46
|
+
|
|
47
|
+
## Repository scope
|
|
48
|
+
|
|
49
|
+
This repository contains the Apache-2.0 scientific/business-logic package together with its tests, documentation, schemas, validation, and contributor tooling.
|
|
50
|
+
|
|
51
|
+
The published npm package intentionally exposes only the browser-safe scientific root surface; repository-local development tooling such as the Node POC transport remains outside the published package.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Motion and Signal Foundation
|
|
2
|
+
|
|
3
|
+
## Projected subject motion
|
|
4
|
+
|
|
5
|
+
Subject-motion blur is modeled from image-plane displacement rather than broad labels such as "fast sport" or "slow subject."
|
|
6
|
+
|
|
7
|
+
`calculateProjectedMotionBlur()` projects one representative scene point at shutter open and shutter close after applying constant world-space linear velocity. The difference is the sensor-plane displacement.
|
|
8
|
+
|
|
9
|
+
Projection has two explicit modes:
|
|
10
|
+
|
|
11
|
+
- when `focusDistanceM` is omitted, nominal focal length is used as the backwards-compatible infinity-focus/pinhole projection distance;
|
|
12
|
+
- when `focusDistanceM` is supplied, the ideal Gaussian thin-lens image distance for that focus plane is used.
|
|
13
|
+
|
|
14
|
+
Projected displacement therefore depends on:
|
|
15
|
+
|
|
16
|
+
- focal length;
|
|
17
|
+
- optional focus-plane extension;
|
|
18
|
+
- shutter duration;
|
|
19
|
+
- starting object position/distance;
|
|
20
|
+
- motion direction;
|
|
21
|
+
- object velocity;
|
|
22
|
+
- optional sensor pixel pitch for pixel-domain reporting.
|
|
23
|
+
|
|
24
|
+
The model can represent a representative point moving laterally and/or along the optical axis. It does **not** yet model an extended object's changing apparent scale across the exposure, rotational subject motion, acceleration, deformation, or occlusion changes. A large object moving significantly in depth can therefore exhibit real scale blur that this point-displacement primitive does not describe.
|
|
25
|
+
|
|
26
|
+
Camera motion is not folded into subject motion. Camera shake is modeled separately by `estimateCameraShakeBlur()`; panning detection/intent is not currently modeled.
|
|
27
|
+
|
|
28
|
+
## Exposure relations
|
|
29
|
+
|
|
30
|
+
`calculateExposureValue100()` implements EV100 from aperture and shutter duration.
|
|
31
|
+
|
|
32
|
+
`calculateRelativeOpticalExposure()` compares the proportional image-plane exposure relation `t / N²`.
|
|
33
|
+
|
|
34
|
+
These calculations do not imply a photon count because they do not include scene radiance, lens transmission/T-stop, vignetting, spectral response, or sensor calibration.
|
|
35
|
+
|
|
36
|
+
`calculateEquivalentIso()` treats ISO as nominal gain/brightness compensation needed to preserve rendered exposure after an aperture/shutter change. It is not used as a shortcut for noise or photon creation.
|
|
37
|
+
|
|
38
|
+
## Signal/noise primitives
|
|
39
|
+
|
|
40
|
+
The root package also exports low-level deterministic sensor primitives:
|
|
41
|
+
|
|
42
|
+
- `calculatePhotoelectrons()`: caller-supplied mean incident photons × caller-supplied quantum efficiency;
|
|
43
|
+
- `calculateSignalToNoise()`: Poisson shot-noise standard deviation plus independent caller-supplied RMS read noise, reported as linear and dB SNR.
|
|
44
|
+
|
|
45
|
+
These functions do **not** derive incident photon counts from scene imagery.
|
|
46
|
+
|
|
47
|
+
They do not currently model dark current, fixed-pattern noise, PRNU/DSNU, ADC quantization, clipping/full-well behavior, CFA/demosaic, downstream denoising, or a particular commercial sensor.
|
|
48
|
+
|
|
49
|
+
The composed POC intentionally does not expose photon/noise output until there is a defensible calibrated radiometric path including the necessary scene, spectral, optical-transmission, pixel-area, quantum-efficiency, and calibration assumptions.
|
|
50
|
+
|
|
51
|
+
## Provenance
|
|
52
|
+
|
|
53
|
+
The equations in this slice are independently implemented from established projective geometry, exposure relations, and elementary photon/statistical noise models. No third-party source code or calibration dataset is incorporated by these modules.
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Physics Foundation
|
|
2
|
+
|
|
3
|
+
Photivra starts with analytical models that can be independently tested and whose assumptions can be stated explicitly.
|
|
4
|
+
|
|
5
|
+
## Implemented models
|
|
6
|
+
|
|
7
|
+
### Rectilinear field of view
|
|
8
|
+
|
|
9
|
+
The rectilinear relation is:
|
|
10
|
+
|
|
11
|
+
`FOV = 2 atan(d / (2v))`
|
|
12
|
+
|
|
13
|
+
where `d` is the relevant sensor dimension and `v` is the projection/image-plane distance.
|
|
14
|
+
|
|
15
|
+
When no focus distance is supplied, `v = f` is the infinity-focus/pinhole compatibility approximation. When focus distance is supplied, `v` is the ideal Gaussian thin-lens image distance.
|
|
16
|
+
|
|
17
|
+
### Pixel pitch and crop
|
|
18
|
+
|
|
19
|
+
Pixel pitch is derived directly from active sensor width and horizontal active pixel count.
|
|
20
|
+
|
|
21
|
+
Centered crop dimensions are derived from a linear crop factor while preserving the source aspect ratio, subject to integer pixel flooring.
|
|
22
|
+
|
|
23
|
+
Subject-height framing crop computes the additional same-aspect crop needed to target a requested subject-height fraction. It assumes the crop can be positioned around the subject and does not check subject position against image edges.
|
|
24
|
+
|
|
25
|
+
### Projection, focus extension, object size, and motion
|
|
26
|
+
|
|
27
|
+
Field of view, projected fronto-parallel object size, projected representative-point subject motion, and camera-shake projection support two explicit projection modes:
|
|
28
|
+
|
|
29
|
+
- **pinhole/infinity-focus compatibility mode** when no focus distance is supplied: nominal focal length is used as the projection distance;
|
|
30
|
+
- **focus-aware thin-lens mode** when a focus distance is supplied: sensor-plane projection distance is calculated from the Gaussian thin-lens relation `1/f = 1/s + 1/v`.
|
|
31
|
+
|
|
32
|
+
There is no hidden distance threshold or discontinuous transition between the modes. The caller chooses the model by supplying or omitting focus distance.
|
|
33
|
+
|
|
34
|
+
At a focus distance of at least 100 focal lengths, the ideal thin-lens projection-distance scale differs from the infinity-focus approximation by no more than about 1.011%. For example, 200 mm focused at 22 m (110 focal lengths) changes projected scale by about 0.92%.
|
|
35
|
+
|
|
36
|
+
The focus-aware model improves internal consistency with thin-lens DOF/defocus calculations, but it is still an ideal paraxial model. It does not model real-lens focus breathing, pupil magnification, principal-plane movement, distortion, aberrations, or lens-specific macro behavior.
|
|
37
|
+
|
|
38
|
+
Projected subject motion is a representative-point displacement model with constant linear world velocity. It does not yet describe scale blur of an extended object whose magnification changes materially during the exposure.
|
|
39
|
+
|
|
40
|
+
### Thin-lens focus and depth of field
|
|
41
|
+
|
|
42
|
+
The geometric depth-of-field model uses the conventional hyperfocal/near/far equations with a caller-supplied acceptable circle of confusion.
|
|
43
|
+
|
|
44
|
+
The circle-of-confusion criterion is a viewing/acceptability input, not a physical sensor threshold. The optional equivalent-viewing helper scales a caller-supplied reference criterion by sensor-diagonal ratio and is explicitly labeled an approximation.
|
|
45
|
+
|
|
46
|
+
The model is not a macro calibration model and does not include diffraction, pupil magnification, aberrations, focus breathing, or lens-specific principal-plane behavior in its DOF criterion.
|
|
47
|
+
|
|
48
|
+
### Defocus circle
|
|
49
|
+
|
|
50
|
+
Defocus is calculated geometrically by comparing the selected sensor plane for the focus distance with the ideal image plane for the subject distance and projecting an ideal circular entrance-pupil cone to the sensor.
|
|
51
|
+
|
|
52
|
+
### Diffraction
|
|
53
|
+
|
|
54
|
+
For an ideal circular aperture, first-zero Airy diameter is:
|
|
55
|
+
|
|
56
|
+
`d = 2.44 λ N`
|
|
57
|
+
|
|
58
|
+
where `λ` is wavelength and `N` is f-number.
|
|
59
|
+
|
|
60
|
+
This is a monochromatic, ideal-circular-pupil diagnostic.
|
|
61
|
+
|
|
62
|
+
It is deliberately separate from regular-polygon aperture geometry used for bokeh/sunstar exploration. Supplying a polygon blade count does not convert the circular Airy result into a polygon-aperture diffraction PSF; a physically consistent non-circular diffraction model remains future work.
|
|
63
|
+
|
|
64
|
+
### Ideal diaphragm geometry and sunstar symmetry
|
|
65
|
+
|
|
66
|
+
For a regular straight-edged diaphragm, the engine calculates normalized polygon vertices and idealized sunstar direction symmetry perpendicular to blade edges.
|
|
67
|
+
|
|
68
|
+
For even blade counts, opposite parallel edges produce overlapping opposite directions, yielding the same number of unique directions as blades.
|
|
69
|
+
|
|
70
|
+
For odd blade counts, there are no parallel opposite-edge pairs, yielding twice as many directions as blades.
|
|
71
|
+
|
|
72
|
+
This is a geometry/orientation model only. It does not calculate diffraction intensity, wavelength-dependent star length, blade curvature, coatings, internal reflections, lens aberrations, sensor blooming, or a complete point-spread function.
|
|
73
|
+
|
|
74
|
+
### Controlled camera shake
|
|
75
|
+
|
|
76
|
+
Yaw and pitch angular velocity are integrated over shutter duration. Their angular displacement is projected to the image plane using the selected projection distance.
|
|
77
|
+
|
|
78
|
+
The current model returns one global image-plane vector. It does not compute the spatially varying optical flow that a real camera rotation produces away from the optical axis.
|
|
79
|
+
|
|
80
|
+
Equivalent stabilization stops attenuate angular displacement by `2^-stops`; that attenuation is explicitly an educational approximation rather than a real IBIS/OIS or CIPA performance model.
|
|
81
|
+
|
|
82
|
+
## Source provenance
|
|
83
|
+
|
|
84
|
+
These implementations were written independently from established mathematical/physical relations. No third-party source code, calibration dataset, table, or model weight is incorporated by these scientific modules.
|
|
85
|
+
|
|
86
|
+
Public technical references may be used to understand or validate concepts, but their code, protected prose, tables, figures, or restricted measurement data are not copied unless separately licensed for that use. See [Scientific and Source Provenance](PROVENANCE.md).
|
|
87
|
+
|
|
88
|
+
## Scope
|
|
89
|
+
|
|
90
|
+
These models produce numerical indicators, not a universal "sharpness score." Sampling, defocus, diffraction, subject motion, and camera shake describe different physical effects and should not be directly added together without a justified image-formation model.
|
|
91
|
+
|
|
92
|
+
Rendering and analysis clients should consume validated numerical outputs rather than silently introducing conflicting camera science.
|
package/docs/POC_API.md
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# Local POC Simulation API
|
|
2
|
+
|
|
3
|
+
Photivra includes a minimal Node HTTP transport for proof-of-concept development and integration testing.
|
|
4
|
+
|
|
5
|
+
It is **not** the recommended production runtime. Browser applications can consume the root `@photivra/engine` scientific surface directly; the production Photivra architecture is intended to keep the simulation math client-side rather than expose this unauthenticated POC server to the public internet.
|
|
6
|
+
|
|
7
|
+
## Repository and package boundary
|
|
8
|
+
|
|
9
|
+
`@photivra/engine` publishes only the runtime-neutral scientific/calculation surface.
|
|
10
|
+
|
|
11
|
+
The Node HTTP transport remains in this GitHub repository under `src/api` as contributor/development tooling. It is **not** exported by the package and `dist/api` is explicitly excluded from the npm tarball.
|
|
12
|
+
|
|
13
|
+
The HTTP layer adds request parsing, CORS response headers, routing, and serialization. It does not add camera-science equations.
|
|
14
|
+
|
|
15
|
+
## Build and run from a repository checkout
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm ci
|
|
19
|
+
npm run build
|
|
20
|
+
node dist/api/poc-api-cli.js
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This command is a repository-development workflow. Installing `@photivra/engine` from npm does not install or expose the POC HTTP server.
|
|
24
|
+
|
|
25
|
+
Defaults:
|
|
26
|
+
|
|
27
|
+
- bind host: `localhost` (loopback only);
|
|
28
|
+
- API URL: `http://localhost:8787`;
|
|
29
|
+
- CORS allowed origin value: `http://localhost:5173`.
|
|
30
|
+
|
|
31
|
+
Overrides:
|
|
32
|
+
|
|
33
|
+
- `PHOTIVRA_API_HOST`;
|
|
34
|
+
- `PHOTIVRA_API_PORT`;
|
|
35
|
+
- `PHOTIVRA_ALLOWED_ORIGIN`.
|
|
36
|
+
|
|
37
|
+
The server is unauthenticated and therefore binds only to loopback by default. Setting `PHOTIVRA_API_HOST` to a non-loopback address exposes it beyond the local machine and is outside the security model supplied by this repository.
|
|
38
|
+
|
|
39
|
+
CORS is a browser policy, **not** authentication or authorization. Non-browser clients can call an exposed endpoint regardless of its CORS header.
|
|
40
|
+
|
|
41
|
+
The server accepts request bodies up to 64 KiB. It does not provide production rate limiting, authentication, quotas, reverse-proxy hardening, or abuse controls.
|
|
42
|
+
|
|
43
|
+
## Health
|
|
44
|
+
|
|
45
|
+
`GET /health`
|
|
46
|
+
|
|
47
|
+
Example:
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"ok": true,
|
|
52
|
+
"service": "photivra-engine",
|
|
53
|
+
"apiVersion": "0.17.0"
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Simulate
|
|
58
|
+
|
|
59
|
+
`POST /v1/poc/simulate`
|
|
60
|
+
|
|
61
|
+
Requests must use `Content-Type: application/json`.
|
|
62
|
+
|
|
63
|
+
All physical quantities use explicit units in their property names.
|
|
64
|
+
|
|
65
|
+
API 0.17 composes projection using the selected focus plane. The response includes a `projection` block with ideal thin-lens image distance, scale relative to the infinity-focus approximation, and model provenance. Full-sensor field of view, crop field of view, object sampling, subject motion, and camera-shake projection use that same selected projection plane.
|
|
66
|
+
|
|
67
|
+
### Required request groups
|
|
68
|
+
|
|
69
|
+
The request includes:
|
|
70
|
+
|
|
71
|
+
- sensor dimensions and resolution;
|
|
72
|
+
- focal length and aperture;
|
|
73
|
+
- shutter duration and ISO;
|
|
74
|
+
- focus distance;
|
|
75
|
+
- exactly one circle-of-confusion criterion:
|
|
76
|
+
- explicit target `circleOfConfusionMm`, or
|
|
77
|
+
- an equivalent-viewing reference sensor/CoC convention;
|
|
78
|
+
- crop factor;
|
|
79
|
+
- diffraction wavelength;
|
|
80
|
+
- one representative object position and velocity.
|
|
81
|
+
|
|
82
|
+
### Optional request groups
|
|
83
|
+
|
|
84
|
+
The request may also include:
|
|
85
|
+
|
|
86
|
+
- physical subject dimensions/distance for pixels-on-subject calculation;
|
|
87
|
+
- named scene-plane distances for defocus sampling;
|
|
88
|
+
- named physical objects for projected pixel sampling;
|
|
89
|
+
- named object motion samples with explicit position/velocity vectors;
|
|
90
|
+
- target subject-height fraction for framing crop;
|
|
91
|
+
- controlled yaw/pitch camera-shake profile plus equivalent stabilization-stop attenuation;
|
|
92
|
+
- ideal regular-polygon aperture blade count/orientation;
|
|
93
|
+
- primary-subject diagnostics selecting a named motion sample.
|
|
94
|
+
|
|
95
|
+
Named sample IDs must be non-empty and unique within their respective arrays.
|
|
96
|
+
|
|
97
|
+
## Response semantics
|
|
98
|
+
|
|
99
|
+
### Projection and field of view
|
|
100
|
+
|
|
101
|
+
The top-level `fieldOfView` always describes the full sensor before digital cropping.
|
|
102
|
+
|
|
103
|
+
`projection` reports:
|
|
104
|
+
|
|
105
|
+
- selected focus distance;
|
|
106
|
+
- ideal Gaussian thin-lens image distance;
|
|
107
|
+
- scale relative to the infinity-focus `f` projection;
|
|
108
|
+
- calculated model provenance.
|
|
109
|
+
|
|
110
|
+
This remains ideal paraxial geometry, not a real-lens focus-breathing calibration.
|
|
111
|
+
|
|
112
|
+
### Crop
|
|
113
|
+
|
|
114
|
+
`crop` reports the requested fixed crop factor, retained pixel dimensions/area, megapixels, and its effective field of view.
|
|
115
|
+
|
|
116
|
+
If `subjectCrop` is requested, subject framing is composed **after** the fixed crop. The response distinguishes:
|
|
117
|
+
|
|
118
|
+
- `additionalCropFactor`: framing crop relative to the fixed crop;
|
|
119
|
+
- `totalCropFactor`: total linear crop relative to the full sensor.
|
|
120
|
+
|
|
121
|
+
`subjectHeightFraction` is not clamped to 1. A value above 1 with `subjectClipped: true` means the projected subject is taller than the output frame.
|
|
122
|
+
|
|
123
|
+
The framing calculation assumes the crop can be positioned around the subject; it does not validate subject position against source-image edges.
|
|
124
|
+
|
|
125
|
+
### Subject/object sampling
|
|
126
|
+
|
|
127
|
+
When physical subject/object dimensions are supplied, projected dimensions are returned in sensor millimetres and pixels using the focus-aware projection plane.
|
|
128
|
+
|
|
129
|
+
These values can drive pre-authored asset/detail selection without moving projection math into a renderer.
|
|
130
|
+
|
|
131
|
+
### Defocus
|
|
132
|
+
|
|
133
|
+
Named defocus samples return ideal geometric blur-circle diameters in sensor millimetres and pixels.
|
|
134
|
+
|
|
135
|
+
### Subject motion
|
|
136
|
+
|
|
137
|
+
Primary and named motion samples return representative-point sensor-plane displacement in millimetres and pixels, including signed x/y components.
|
|
138
|
+
|
|
139
|
+
The model assumes constant world-space linear velocity. It can represent depth-direction motion of the representative point but does not model extended-object scale blur, rotation, acceleration, deformation, or occlusion changes.
|
|
140
|
+
|
|
141
|
+
### Camera shake
|
|
142
|
+
|
|
143
|
+
When supplied, camera shake returns unstabilized and stabilized global image-plane vectors.
|
|
144
|
+
|
|
145
|
+
Yaw/pitch projection uses the selected focus-aware projection distance. Equivalent stabilization stops attenuate angular displacement by `2^-stops` and are explicitly labeled an approximation.
|
|
146
|
+
|
|
147
|
+
The result is not a CIPA DC-011 measurement, real-camera stabilization rating, or spatially varying rotational optical-flow model.
|
|
148
|
+
|
|
149
|
+
### Aperture and diffraction
|
|
150
|
+
|
|
151
|
+
The optional aperture-shape response contains regular-polygon vertices plus idealized sunstar direction symmetry. Blade count is bounded to 1024.
|
|
152
|
+
|
|
153
|
+
The separate diffraction result remains the ideal **circular-pupil** first-zero Airy diagnostic and reports `pupilModel: "ideal-circular"`.
|
|
154
|
+
|
|
155
|
+
Polygon aperture geometry does not convert the circular Airy result into a polygon-aperture diffraction PSF.
|
|
156
|
+
|
|
157
|
+
### Focus criterion
|
|
158
|
+
|
|
159
|
+
For cross-format comparisons, the equivalent-viewing helper scales a caller-supplied reference CoC by sensor-diagonal ratio.
|
|
160
|
+
|
|
161
|
+
That is explicitly an approximation based on equivalent final viewing assumptions; it is not a physical lens/sensor blur threshold.
|
|
162
|
+
|
|
163
|
+
### Primary-subject diagnostics
|
|
164
|
+
|
|
165
|
+
When requested with the needed subject inputs, diagnostics report sampling, defocus, circular-pupil diffraction, optional named subject-motion path, and optional stabilized camera-shake path side by side.
|
|
166
|
+
|
|
167
|
+
They are intentionally **not** collapsed into one sharpness score because these quantities describe different physical effects.
|
|
168
|
+
|
|
169
|
+
### Provenance
|
|
170
|
+
|
|
171
|
+
Aggregate response provenance is:
|
|
172
|
+
|
|
173
|
+
- `calculated` when all included components are calculated models;
|
|
174
|
+
- `mixed` when an equivalent-viewing focus criterion and/or camera-shake approximation is present.
|
|
175
|
+
|
|
176
|
+
Component provenance identifies which parts are calculated versus approximate.
|
|
177
|
+
|
|
178
|
+
## Validation and errors
|
|
179
|
+
|
|
180
|
+
Decoded HTTP JSON is structurally checked for required object/array/string/finite-number/vector shapes before simulation.
|
|
181
|
+
|
|
182
|
+
That parser does not duplicate every scientific domain/range rule. Scientific modules remain responsible for constraints such as positive dimensions, physically valid distances, blade-count bounds, and crop limits.
|
|
183
|
+
|
|
184
|
+
Current transport responses include:
|
|
185
|
+
|
|
186
|
+
- `200`: successful simulation;
|
|
187
|
+
- `204`: CORS preflight;
|
|
188
|
+
- `400`: malformed JSON/shape or invalid scientific request;
|
|
189
|
+
- `404`: unknown route;
|
|
190
|
+
- `415`: simulation POST without `application/json`.
|
|
191
|
+
|
|
192
|
+
Error messages are intended for local development diagnostics and should not be treated as a hardened public error contract.
|
|
193
|
+
|
|
194
|
+
## Reusable repository module
|
|
195
|
+
|
|
196
|
+
Within this repository, `src/api/server.ts` exports an unbound `createPocApiServer` constructor for tests and trusted development composition.
|
|
197
|
+
|
|
198
|
+
It is repository-internal development tooling rather than a supported npm subpath. Importing the module in repository tests does not start a listener; the CLI entrypoint is responsible for binding the configured host/port.
|
|
199
|
+
|
|
200
|
+
## Important limitations
|
|
201
|
+
|
|
202
|
+
- Local POC transport only.
|
|
203
|
+
- No authentication or authorization.
|
|
204
|
+
- No rate limiting or public-service abuse protection.
|
|
205
|
+
- No persistence.
|
|
206
|
+
- No scene uploads.
|
|
207
|
+
- No calibration database.
|
|
208
|
+
- No real-camera/lens claims.
|
|
209
|
+
- ISO is an exposure/gain setting, not a noise shortcut.
|
|
210
|
+
- Photon/noise primitives exist in the root library, but calibrated photon/noise output is intentionally absent from the composed POC until the radiometric/spectral/optical/sensor prerequisites are defensible.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# Scientific and Source Provenance
|
|
2
|
+
|
|
3
|
+
Every scientific or calculation module in this repository must have a traceable, legally clean origin.
|
|
4
|
+
|
|
5
|
+
## Allowed foundations
|
|
6
|
+
|
|
7
|
+
An implementation may be based on:
|
|
8
|
+
|
|
9
|
+
1. Public-domain facts and mathematics.
|
|
10
|
+
2. Established mathematical identities and physical laws.
|
|
11
|
+
3. Scientific methods described in public technical literature, standards, patents, or papers, provided protected text/code/tables/figures are not copied unless separately licensed for that use and any applicable third-party patent rights are separately considered.
|
|
12
|
+
4. Third-party code, datasets, model weights, or calibration material only when the applicable license explicitly permits the intended copying, modification, redistribution, and commercial use and is compatible with this project.
|
|
13
|
+
|
|
14
|
+
"Available on the internet" is not sufficient permission.
|
|
15
|
+
|
|
16
|
+
A paper, standard, or patent publication may explain a method without granting copyright permission to copy its expression and without granting a patent license. Independent implementation addresses source-code/prose provenance; it does not by itself resolve active third-party patent claims.
|
|
17
|
+
|
|
18
|
+
## Independent implementation
|
|
19
|
+
|
|
20
|
+
When a reference explains a method but does not license its implementation or text for reuse:
|
|
21
|
+
|
|
22
|
+
- understand the mathematical/scientific method;
|
|
23
|
+
- write a fresh implementation from the method, not from proprietary source structure;
|
|
24
|
+
- do not translate, mechanically rewrite, or adapt proprietary source code;
|
|
25
|
+
- do not copy protected tables, diagrams, explanatory prose, calibration datasets, waveform files, or model weights;
|
|
26
|
+
- add tests based on independently derived analytical cases or clearly reusable data;
|
|
27
|
+
- record material references and any known licensing/patent constraints.
|
|
28
|
+
|
|
29
|
+
## AI-assisted work
|
|
30
|
+
|
|
31
|
+
AI-assisted code, documentation, tests, or research notes are treated as untrusted draft material until substantively reviewed by a human contributor.
|
|
32
|
+
|
|
33
|
+
A contributor using an AI system remains responsible for:
|
|
34
|
+
|
|
35
|
+
- confirming that the submitted expression is appropriate to distribute under Apache-2.0;
|
|
36
|
+
- checking for suspiciously copied/source-like output;
|
|
37
|
+
- validating scientific and technical correctness;
|
|
38
|
+
- ensuring no confidential or restricted third-party material was supplied to the tool;
|
|
39
|
+
- making the DCO certification for the final submitted contribution.
|
|
40
|
+
|
|
41
|
+
An AI system's assertion about originality, copyrightability, patents, or license compatibility is not accepted as provenance evidence.
|
|
42
|
+
|
|
43
|
+
## Module record
|
|
44
|
+
|
|
45
|
+
Meaningful scientific modules should document, in JSDoc or an adjacent reference document:
|
|
46
|
+
|
|
47
|
+
- mathematical/physical basis;
|
|
48
|
+
- assumptions and valid range;
|
|
49
|
+
- units and coordinate conventions;
|
|
50
|
+
- whether the implementation is independent;
|
|
51
|
+
- public references used to understand or validate the method;
|
|
52
|
+
- third-party code/data incorporated, if any;
|
|
53
|
+
- the license for any incorporated third-party material;
|
|
54
|
+
- uncertainty or approximation status where applicable.
|
|
55
|
+
|
|
56
|
+
## Provenance labels
|
|
57
|
+
|
|
58
|
+
Scientific calculation envelopes use these labels:
|
|
59
|
+
|
|
60
|
+
- `calculated`: directly computed from the documented model.
|
|
61
|
+
- `calibrated`: uses measurement/calibration data with documented reuse rights.
|
|
62
|
+
- `estimated`: an input or result is inferred rather than known exactly.
|
|
63
|
+
- `approximation`: a deliberately simplified model is used.
|
|
64
|
+
|
|
65
|
+
The composed `simulatePocCamera()` response is not itself a `CalculationResult<T>`; it reports aggregate/component provenance separately and uses `mixed` when calculated components are combined with explicitly approximate components.
|
|
66
|
+
|
|
67
|
+
## Uncertainty and quality metadata
|
|
68
|
+
|
|
69
|
+
Provenance answers **how a result was produced**; uncertainty answers **what quantitative accuracy/variability is defensibly known**. They are related but must not be conflated.
|
|
70
|
+
|
|
71
|
+
When available, `CalculationResult.quality` records uncertainty components by affected quantity and source:
|
|
72
|
+
|
|
73
|
+
- `measurement`: variability/uncertainty originating in measured inputs or observations;
|
|
74
|
+
- `calibration`: uncertainty originating in a calibration or calibrated reference;
|
|
75
|
+
- `model-approximation`: quantified error attributed to a simplified/inexact model.
|
|
76
|
+
|
|
77
|
+
An approximation label does not by itself justify a numeric model-error estimate. If no defensible bound exists, retain the approximation provenance and assumptions without fabricating quality metadata.
|
|
78
|
+
|
|
79
|
+
Confidence/coverage levels are optional and must include a stated basis. They are not subjective confidence scores.
|
|
80
|
+
|
|
81
|
+
Valid-range metadata describes where a model/calibration is considered applicable. It is not an uncertainty interval.
|
|
82
|
+
|
|
83
|
+
When composing models, preserve uncertainty components separately unless a documented model provides a defensible combination rule. Do not imply statistical independence, add errors in quadrature, or publish a single aggregate confidence score without evidence.
|
|
84
|
+
|
|
85
|
+
## Release gate
|
|
86
|
+
|
|
87
|
+
Ambiguous licensing, unresolved material patent risk, uncertain provenance, unexplained copied material, fabricated uncertainty, undocumented uncertainty combination, or unsupported scientific claims block release until resolved.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Camera Shake and Stabilization
|
|
2
|
+
|
|
3
|
+
Photivra separates deterministic projection geometry from a deliberately simplified educational stabilization model.
|
|
4
|
+
|
|
5
|
+
## Controlled camera shake
|
|
6
|
+
|
|
7
|
+
`estimateCameraShakeBlur()` accepts constant camera angular velocity around two axes:
|
|
8
|
+
|
|
9
|
+
- yaw, in radians per second;
|
|
10
|
+
- pitch, in radians per second.
|
|
11
|
+
|
|
12
|
+
For shutter-open duration `t`, each angular displacement is:
|
|
13
|
+
|
|
14
|
+
`theta = angularVelocity * t`
|
|
15
|
+
|
|
16
|
+
The image-plane component is projected as:
|
|
17
|
+
|
|
18
|
+
`d = v * tan(theta)`
|
|
19
|
+
|
|
20
|
+
where `v` is the projection distance in millimetres.
|
|
21
|
+
|
|
22
|
+
Projection has two modes:
|
|
23
|
+
|
|
24
|
+
- without `focusDistanceM`, `v` is nominal focal length (the infinity-focus compatibility approximation);
|
|
25
|
+
- with `focusDistanceM`, `v` is the ideal Gaussian thin-lens image distance for the selected focus plane.
|
|
26
|
+
|
|
27
|
+
When pixel pitch is supplied, the same displacement is also reported in sensor pixels.
|
|
28
|
+
|
|
29
|
+
The supplied shake profile is synthetic input. The model is not an empirical statement about how much a particular photographer shakes.
|
|
30
|
+
|
|
31
|
+
### Spatial limitation
|
|
32
|
+
|
|
33
|
+
The current result is one global image-plane yaw/pitch vector. Real camera rotation produces position-dependent optical flow away from the optical axis; that spatial variation is not yet modeled. Roll and translational camera motion are also omitted.
|
|
34
|
+
|
|
35
|
+
## Equivalent stabilization stops
|
|
36
|
+
|
|
37
|
+
The model accepts `stabilizationStopsEquivalent`.
|
|
38
|
+
|
|
39
|
+
For educational exploration, the supplied angular displacement is attenuated by:
|
|
40
|
+
|
|
41
|
+
`residualMotionFactor = 2 ^ (-stops)`
|
|
42
|
+
|
|
43
|
+
For example, 3 equivalent stops produces a residual factor of 1/8 for the controlled synthetic shake profile.
|
|
44
|
+
|
|
45
|
+
This attenuation is an **approximation**. It is useful for demonstrating the effect of reducing camera motion, but it is not a model of a particular IBIS/OIS implementation and does not imply that a real stabilization system deterministically reduces every motion path by that factor.
|
|
46
|
+
|
|
47
|
+
Because the returned result combines this attenuation assumption with the geometric projection, the calculation envelope uses `provenance.kind = "approximation"`.
|
|
48
|
+
|
|
49
|
+
## CIPA reference
|
|
50
|
+
|
|
51
|
+
CIPA publishes **DC-011-2024** for measurement and description of image-stabilization performance.
|
|
52
|
+
|
|
53
|
+
Photivra is not affiliated with, sponsored by, endorsed by, certified by, or claimed to comply with CIPA. The current educational approximation does not implement the CIPA test method.
|
|
54
|
+
|
|
55
|
+
Reference:
|
|
56
|
+
|
|
57
|
+
- CIPA DC-011-2024 overview: https://www.cipa.jp/e/std/image-stabilization.html
|
|
58
|
+
|
|
59
|
+
Only the public standard identifier and high-level terminology are referenced here; CIPA logos and protected standard content are not incorporated into this project.
|
|
60
|
+
|
|
61
|
+
## Not yet modeled
|
|
62
|
+
|
|
63
|
+
The current model intentionally omits:
|
|
64
|
+
|
|
65
|
+
- roll;
|
|
66
|
+
- translational camera shake;
|
|
67
|
+
- spatially varying rotational optical flow;
|
|
68
|
+
- frequency-dependent shake spectra;
|
|
69
|
+
- photographer-to-photographer variation;
|
|
70
|
+
- shutter-button impulse;
|
|
71
|
+
- tripod/support interactions;
|
|
72
|
+
- sensor/lens stabilization axis limits;
|
|
73
|
+
- IBIS/OIS coordination;
|
|
74
|
+
- focal-length-dependent stabilization effectiveness beyond projection geometry;
|
|
75
|
+
- rolling shutter;
|
|
76
|
+
- panning detection/modes.
|
|
77
|
+
|
|
78
|
+
Those capabilities should be added as separate, documented models rather than being silently folded into the current approximation.
|