@gyeonghokim/fisheye.js 1.1.0 → 2.0.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/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # fisheye.js
2
2
 
3
- DEMO: https://gyeonghokim.github.io/fisheye.js/
3
+ DEMO: <https://gyeonghokim.github.io/fisheye.js/>
4
4
 
5
- > Modern fisheye dewarping library for the web using **WebGPU** (general-purpose GPU compute)
5
+ > Modern fisheye undistortion library for the web using **WebGPU** (general-purpose GPU compute)
6
6
 
7
- fisheye.js processes [VideoFrame](https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame)s with **WebGPU compute shaders**—no canvas 2D—and corrects fisheye lens distortion using the **OpenCV fisheye model** (Kannala–Brandt–style polynomial in angle θ with coefficients k1–k4). This is the same model as in [OpenCVs fisheye module](https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html), not UCM (Unified Camera Model) or a simple radial model.
7
+ fisheye.js processes [VideoFrame](https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame)s with **WebGPU compute shaders**—no canvas 2D—and corrects fisheye lens distortion using the **OpenCV fisheye model** (Kannala–Brandt–style polynomial in angle θ with coefficients k1–k4). This is the same model as in [OpenCV's fisheye module](https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html).
8
8
 
9
9
  ## Learning Docs
10
10
 
@@ -12,7 +12,7 @@ See the full educational curriculum in `doc/index.md`.
12
12
 
13
13
  ## Features
14
14
 
15
- - **WebGPU GPGPU**: Compute-shader pipeline via [TypeGPU](https://www.npmjs.com/package/typegpu); input/output as textures and readback to VideoFrame—no canvas element for dewarping
15
+ - **WebGPU GPGPU**: Compute-shader pipeline via [TypeGPU](https://www.npmjs.com/package/typegpu); input/output as textures and readback to VideoFrame—no canvas element for undistortion
16
16
  - **OpenCV fisheye (Kannala–Brandt) model**: Distortion model `θ_d = θ × (1 + k1·θ² + k2·θ⁴ + k3·θ⁶ + k4·θ⁸)` for accurate calibration
17
17
  - **WebCodecs**: Built on the [VideoFrame](https://developer.mozilla.org/en-US/docs/Web/API/VideoFrame) API
18
18
  - **ESM**: `import { Fisheye } from "@gyeonghokim/fisheye.js"`
@@ -37,7 +37,7 @@ if you installed `@webgpu/types`,
37
37
  ```
38
38
 
39
39
  > Why should I install webgpu types?
40
- > This library does not render your binary, it just dewarp the VideoFrame.
40
+ > This library does not render your binary, it just undistorts the VideoFrame.
41
41
  > You should make **your own YUV renderer**, or you can install `@gyeonghokim/yuv-player`.
42
42
 
43
43
  in your code,
@@ -45,72 +45,185 @@ in your code,
45
45
  ```ts
46
46
  import { Fisheye } from "@gyeonghokim/fisheye.js";
47
47
 
48
- const dewarper = new Fisheye({
48
+ // Option 1: Flat style (simple)
49
+ const fisheye = new Fisheye({
50
+ // OpenCV fisheye distortion coefficients D = [k1, k2, k3, k4]
49
51
  k1: 0.5,
50
52
  k2: 0.0,
51
53
  k3: 0.0,
52
54
  k4: 0.0,
55
+
56
+ // Output size
57
+ width: 1920,
58
+ height: 1080,
59
+
60
+ // Optional: Camera matrix K parameters
61
+ fx: 1000, // focal length x (pixels)
62
+ fy: 1000, // focal length y (pixels)
63
+ cx: 960, // principal point x (pixels)
64
+ cy: 540, // principal point y (pixels)
65
+
66
+ // Optional: New camera matrix P estimation parameters
67
+ balance: 0.0, // 0.0 = no black edges (zoom in), 1.0 = keep original FOV (may have black edges)
68
+ fovScale: 1.0, // >1.0 = widen FOV, <1.0 = narrow FOV
69
+
70
+ // Optional: Projection mode
71
+ projection: { kind: "rectilinear" }, // or "equirectangular", "cylindrical", "original"
72
+ });
73
+
74
+ // Option 2: Grouped style (OpenCV-like)
75
+ const fisheyeGrouped = new Fisheye({
76
+ K: { fx: 1000, fy: 1000, cx: 960, cy: 540 },
77
+ D: { k1: 0.5, k2: 0, k3: 0, k4: 0 },
78
+ size: { width: 1920, height: 1080 },
79
+ balance: 0.0,
80
+ fovScale: 1.0,
81
+ projection: { kind: "rectilinear" },
82
+ });
83
+
84
+ // Option 3: Manual rectilinear with explicit P matrix
85
+ const fisheyeManual = new Fisheye({
86
+ k1: 0.5, k2: 0, k3: 0, k4: 0,
53
87
  width: 1920,
54
88
  height: 1080,
55
- fov: 180, // Field of view in degrees
56
- centerX: 0, // X offset of lens center (-1.0 to 1.0)
57
- centerY: 0, // Y offset of lens center (-1.0 to 1.0)
58
- zoom: 1.0, // Zoom factor
89
+ projection: { kind: "rectilinear", mode: "manual", newFx: 800, newFy: 800 },
59
90
  });
60
91
 
61
- const renderLoop = (timestamp: DOMHighResTimestamp) => {
62
- // your render logic
63
- const dewarpedVideoFrame: VideoFrame = await dewarper.dewarp(yourVideoFrame);
64
- yourYUVPlayer.draw(dewarpedVideoFrame);
92
+ const renderLoop = async (timestamp: DOMHighResTimestamp) => {
93
+ const undistorted: VideoFrame = await fisheye.undistort(yourVideoFrame);
94
+ yourYUVPlayer.draw(undistorted);
65
95
  requestAnimationFrame(renderLoop);
66
96
  };
67
97
  ```
68
98
 
99
+ ## Architecture & Design
100
+
101
+ ### Distortion Model: OpenCV Fisheye (Kannala-Brandt)
102
+
103
+ This library uses the **OpenCV fisheye model** (Kannala-Brandt, 2006) for undistortion:
104
+
105
+ ```
106
+ # Normalized coordinates (a, b) where a = X/Z, b = Y/Z
107
+ r = sqrt(a² + b²)
108
+ θ = atan(r) # incidence angle
109
+ θ_d = θ × (1 + k₁θ² + k₂θ⁴ + k₃θ⁶ + k₄θ⁸) # distorted angle
110
+ x' = (θ_d / r) × a, y' = (θ_d / r) × b # distorted coords
111
+ u = fx(x' + αy') + cx, v = fy × y' + cy # pixel coords
112
+ ```
113
+
114
+ This is the same model as [OpenCV's fisheye module](https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html).
115
+
116
+ **Important:** OpenCV's `fisheye.undistortImage()` always outputs **rectilinear (perspective) projection only**. It does not provide panoramic or other projection modes.
117
+
118
+ ### Projection Modes
119
+
120
+ For **WebGPU efficiency**, projection transformation is applied in a **single GPU pass**:
121
+
122
+ | Mode | Description |
123
+ |------|-------------|
124
+ | `rectilinear` | Standard perspective projection (same as OpenCV) |
125
+ | `rectilinear` + `mode: "manual"` | Explicit P matrix with `newFx`, `newFy`, `newCx?`, `newCy?` |
126
+ | `equirectangular` | Panoramic equirectangular projection |
127
+ | `cylindrical` | Panoramic cylindrical projection |
128
+ | `original` | Pass-through (no undistortion) |
129
+
130
+ ### Why Unified GPU Pipeline?
131
+
132
+ Traditional OpenCV approach:
133
+
134
+ ```python
135
+ # Step 1: Undistort (GPU/CPU)
136
+ undistorted = cv2.fisheye.undistortImage(img, K, D, Knew)
137
+
138
+ # Step 2: Projection transform (CPU) - if panoramic needed
139
+ panorama = cv2.remap(undistorted, custom_map_x, custom_map_y, cv2.INTER_LINEAR)
140
+ ```
141
+
142
+ **Our approach (single GPU compute shader):**
143
+
144
+ ```typescript
145
+ // All in one GPU pass: undistortion + projection
146
+ const undistorted = await fisheye.undistort(input);
147
+ ```
148
+
69
149
  ## API
70
150
 
71
151
  ### `new Fisheye(options?: FisheyeOptions)`
72
152
 
73
- Creates a new Fisheye dewarper instance.
153
+ Creates a new Fisheye undistortion instance.
74
154
 
75
- **Options:**
155
+ #### OpenCV Fisheye Model Parameters
76
156
 
77
- - `k1` (number, optional): Fisheye distortion coefficient k1. Typical range: -1.0 to 1.0. Default: `0`.
78
- - `k2` (number, optional): Fisheye distortion coefficient k2. Default: `0`.
79
- - `k3` (number, optional): Fisheye distortion coefficient k3. Default: `0`.
80
- - `k4` (number, optional): Fisheye distortion coefficient k4. Default: `0`.
81
- - `width` (number, optional): Output frame width. Default: `300`
82
- - `height` (number, optional): Output frame height. Default: `150`
83
- - `fov` (number, optional): Field of view in degrees. Default: `180`
84
- - `centerX` (number, optional): X offset of the lens center (normalized, -1.0 to 1.0). Default: `0`
85
- - `centerY` (number, optional): Y offset of the lens center (normalized, -1.0 to 1.0). Default: `0`
86
- - `zoom` (number, optional): Zoom factor. Default: `1.0`
157
+ | Parameter | Type | Default | Description |
158
+ | ---------- | --------- | ---------- | -------------------------------------------------------- |
159
+ | `fx` | `number?` | auto | Camera matrix K: focal length in x-axis (pixels) |
160
+ | `fy` | `number?` | auto | Camera matrix K: focal length in y-axis (pixels) |
161
+ | `cx` | `number?` | `width/2` | Camera matrix K: principal point x-coordinate (pixels) |
162
+ | `cy` | `number?` | `height/2` | Camera matrix K: principal point y-coordinate (pixels) |
163
+ | `k1` | `number?` | `0` | Distortion coefficient k1 (Kannala-Brandt) |
164
+ | `k2` | `number?` | `0` | Distortion coefficient k2 (Kannala-Brandt) |
165
+ | `k3` | `number?` | `0` | Distortion coefficient k3 (Kannala-Brandt) |
166
+ | `k4` | `number?` | `0` | Distortion coefficient k4 (Kannala-Brandt) |
167
+ | `width` | `number?` | `300` | Output image width (OpenCV `new_size.width`) |
168
+ | `height` | `number?` | `150` | Output image height (OpenCV `new_size.height`) |
169
+ | `balance` | `number?` | `0.0` | Balance (0.0 = no black edges/zoom in, 1.0 = keep original FOV) |
170
+ | `fovScale` | `number?` | `1.0` | FOV scale (>1.0 = widen FOV, <1.0 = narrow FOV) |
87
171
 
88
- **Fisheye model (OpenCV fisheye / Kannala–Brandt):**
89
- We use the same model as OpenCV’s [fisheye module](https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html) (cited there as the “generic camera model” from Kannala & Brandt, 2006). It is a polynomial-in-θ model, not UCM:
172
+ **Note:** These parameters exactly match [OpenCV fisheye API](https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html). Use values from `cv2.fisheye.calibrate()` or `cv2.fisheye.estimateNewCameraMatrixForUndistortRectify()`.
90
173
 
91
- ```
92
- theta = atan(r)
93
- theta_d = theta * (1 + k1*theta^2 + k2*theta^4 + k3*theta^6 + k4*theta^8)
94
- r_d = tan(theta_d)
174
+ #### Projection Options
175
+
176
+ | Parameter | Type | Default | Description |
177
+ | ------------ | -------------------- | -------------------------- | -------------------------------------------------------------- |
178
+ | `projection` | `FisheyeProjection` | `{ kind: "rectilinear" }` | Output projection mode (see Projection Modes above) |
179
+
180
+ **FisheyeProjection types:**
181
+
182
+ ```typescript
183
+ // Auto: P matrix computed from balance/fovScale
184
+ { kind: "rectilinear" }
185
+ { kind: "equirectangular" }
186
+ { kind: "cylindrical" }
187
+ { kind: "original" }
188
+
189
+ // Manual: Explicit P matrix
190
+ { kind: "rectilinear", mode: "manual", newFx: number, newFy: number, newCx?: number, newCy?: number }
95
191
  ```
96
192
 
97
- ### `dewarp(frame: VideoFrame): Promise<VideoFrame>`
193
+ ### `undistort(frame: VideoFrame): Promise<VideoFrame>`
98
194
 
99
- Dewarps a VideoFrame with fisheye distortion.
195
+ Undistorts a VideoFrame with fisheye distortion.
100
196
 
101
197
  **Parameters:**
102
198
 
103
199
  - `frame`: Input VideoFrame with fisheye distortion
104
200
 
105
- **Returns:** Promise that resolves to a dewarped VideoFrame
201
+ **Returns:** Promise that resolves to an undistorted VideoFrame
202
+
203
+ **Differences from OpenCV:**
204
+
205
+ Unlike OpenCV's `fisheye.undistortImage()` which only outputs rectilinear (perspective) projection, this method performs **all transformations in a single GPU pass** for WebGPU efficiency:
206
+
207
+ 1. **Undistortion** (OpenCV Kannala-Brandt fisheye model)
208
+ 2. **Projection** (rectilinear, equirectangular, cylindrical, or original)
209
+
210
+ OpenCV equivalent for non-rectilinear projections would require 2 separate operations:
211
+ ```python
212
+ # OpenCV: 2 CPU/GPU roundtrips for panoramic projection
213
+ undistorted = cv2.fisheye.undistortImage(img, K, D, Knew)
214
+ panorama = cv2.remap(undistorted, map_x, map_y, cv2.INTER_LINEAR)
215
+
216
+ # fisheye.js: 1 GPU pass - undistortion + projection
217
+ undistorted = await fisheye.undistort(img)
218
+ ```
106
219
 
107
220
  ### `updateConfig(options: Partial<FisheyeOptions>): void`
108
221
 
109
- Updates the dewarper configuration. You can update any subset of the original options.
222
+ Updates the configuration. You can update any subset of the original options.
110
223
 
111
224
  ### `destroy(): void`
112
225
 
113
- Cleans up GPU resources. Call this when you're done using the dewarper.
226
+ Cleans up GPU resources. Call this when you're done using the instance.
114
227
 
115
228
  ## Working with YUV Binary Data
116
229
 
@@ -119,21 +232,21 @@ If you receive raw YUV binary data from a camera or server, you can use the `cre
119
232
  ```ts
120
233
  import { Fisheye, createVideoFrameFromYUV } from "@gyeonghokim/fisheye.js";
121
234
 
122
- const dewarper = new Fisheye({ k1: 0.5, width: 1920, height: 1080 });
235
+ const fisheye = new Fisheye({ k1: 0.5, width: 1920, height: 1080 });
123
236
 
124
237
  // Example: Receiving NV12 data from a server
125
238
  const response = await fetch("/api/camera/frame");
126
239
  const yuvBuffer = await response.arrayBuffer();
127
240
 
128
241
  const frame = createVideoFrameFromYUV(new Uint8Array(yuvBuffer), {
129
- format: "NV12", // YUV format
242
+ format: "NV12", // YUV format
130
243
  width: 1920,
131
244
  height: 1080,
132
- timestamp: performance.now() * 1000, // microseconds
245
+ timestamp: performance.now() * 1000, // microseconds
133
246
  });
134
247
 
135
- const dewarpedFrame = await dewarper.dewarp(frame);
136
- frame.close(); // Don't forget to close the original frame
248
+ const undistorted = await fisheye.undistort(frame);
249
+ frame.close(); // Don't forget to close the original frame
137
250
  ```
138
251
 
139
252
  ### `createVideoFrameFromYUV(data, options)`
@@ -156,13 +269,13 @@ Creates a VideoFrame from YUV binary data.
156
269
 
157
270
  **Supported YUV Formats:**
158
271
 
159
- | Format | Description | Data Size |
160
- |--------|-------------|-----------|
161
- | `I420` | YUV 4:2:0 planar (Y, U, V planes) | width × height × 1.5 |
162
- | `NV12` | YUV 4:2:0 semi-planar (Y plane, interleaved UV) | width × height × 1.5 |
163
- | `I420A` | YUV 4:2:0 planar with alpha | width × height × 2.5 |
164
- | `I422` | YUV 4:2:2 planar | width × height × 2 |
165
- | `I444` | YUV 4:4:4 planar | width × height × 3 |
272
+ | Format | Description | Data Size |
273
+ | ------- | ----------------------------------------------- | -------------------- |
274
+ | `I420` | YUV 4:2:0 planar (Y, U, V planes) | width × height × 1.5 |
275
+ | `NV12` | YUV 4:2:0 semi-planar (Y plane, interleaved UV) | width × height × 1.5 |
276
+ | `I420A` | YUV 4:2:0 planar with alpha | width × height × 2.5 |
277
+ | `I422` | YUV 4:2:2 planar | width × height × 2 |
278
+ | `I444` | YUV 4:4:4 planar | width × height × 3 |
166
279
 
167
280
  ### `calculateYUVDataSize(format, width, height)`
168
281
 
@@ -171,7 +284,7 @@ Calculates the expected byte size for YUV data.
171
284
  ```ts
172
285
  import { calculateYUVDataSize } from "@gyeonghokim/fisheye.js";
173
286
 
174
- const size = calculateYUVDataSize("NV12", 1920, 1080); // 3110400 bytes
287
+ const size = calculateYUVDataSize("NV12", 1920, 1080); // 3110400 bytes
175
288
  ```
176
289
 
177
290
  ## Development
package/dist/index.d.ts CHANGED
@@ -1,3 +1,7 @@
1
+ declare type AllOrNothing<T extends object> = T | {
2
+ [K in keyof T]?: undefined;
3
+ };
4
+
1
5
  /**
2
6
  * Calculate the expected byte size for YUV data based on format and dimensions
3
7
  *
@@ -8,6 +12,9 @@
8
12
  */
9
13
  export declare function calculateYUVDataSize(format: YUVFormat, width: number, height: number): number;
10
14
 
15
+ /** Flat K matrix input - fx/fy both required or both omitted */
16
+ export declare type CameraIntrinsics = AllOrNothing<Pick<KMatrix, "fx" | "fy">> & Pick<KMatrix, "cx" | "cy" | "alpha">;
17
+
11
18
  /**
12
19
  * Convert RGBA image data to YUV format (I420 by default)
13
20
  *
@@ -120,20 +127,17 @@ export declare interface CreateVideoFrameOptions {
120
127
  transfer?: boolean;
121
128
  }
122
129
 
130
+ /** D vector - distortion coefficients for `θ_d = θ(1 + k₁θ² + k₂θ⁴ + k₃θ⁶ + k₄θ⁸)` */
131
+ export declare interface DVector {
132
+ k1: number;
133
+ k2: number;
134
+ k3: number;
135
+ k4: number;
136
+ }
137
+
123
138
  /**
124
- * Fisheye dewarper using WebGPU via TypeGPU (Pure GPGPU)
125
- *
126
- * @example
127
- * ```ts
128
- * const dewarper = new Fisheye({
129
- * width: 1920,
130
- * height: 1080,
131
- * fov: 180,
132
- * projection: "rectilinear",
133
- * });
134
- *
135
- * const dewarpedFrame = await dewarper.dewarp(inputVideoFrame);
136
- * ```
139
+ * Fisheye undistortion using WebGPU via TypeGPU.
140
+ * @see {@link https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html}
137
141
  */
138
142
  export declare class Fisheye {
139
143
  private config;
@@ -142,7 +146,7 @@ export declare class Fisheye {
142
146
  private inputTexture;
143
147
  private outputTexture;
144
148
  private bindGroup;
145
- private dewarpPipeline;
149
+ private pipeline;
146
150
  private readbackBuffers;
147
151
  private readbackIndex;
148
152
  private readbackHasData;
@@ -153,169 +157,115 @@ export declare class Fisheye {
153
157
  private outputTextureSize;
154
158
  private uniformInputWidth;
155
159
  private uniformInputHeight;
160
+ private cachedNewCameraMatrix;
156
161
  constructor(options?: FisheyeOptions);
157
- /**
158
- * Apply default values to options
159
- */
160
162
  private applyDefaults;
161
- /**
162
- * Initialize TypeGPU root and resources
163
- */
163
+ /** Undistort a point using Newton's method (OpenCV fisheye inverse). */
164
+ private undistortPointNormalized;
165
+ private undistortPixelToNormalized;
166
+ /** @see {@link https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html#ga384940fdf04c03e362e94b6eb9b673c9|estimateNewCameraMatrixForUndistortRectify} */
167
+ private computeNewCameraMatrix;
164
168
  private initialize;
165
- /**
166
- * Get uniform data from current configuration
167
- */
168
169
  private getUniformData;
169
- /**
170
- * Update uniform buffer with current configuration
171
- */
172
170
  private updateUniforms;
173
171
  private readbackToVideoFrame;
174
- /**
175
- * Create input texture (TypeGPU; per official docs: sampled + render for .write(image/VideoFrame)).
176
- */
177
172
  private createInputTexture;
178
- /**
179
- * Create output storage texture (TypeGPU; type-safe with layout.$)
180
- */
181
173
  private createOutputTexture;
182
- /**
183
- * Calculate bytes per row with proper alignment (256-byte alignment for WebGPU)
184
- */
185
174
  private calculateBytesPerRow;
186
- /**
187
- * Create or recreate readback buffer for GPU to CPU data transfer
188
- */
189
175
  private createReadbackBuffer;
190
- /**
191
- * Dewarp a VideoFrame
192
- *
193
- * @param frame - Input VideoFrame with fisheye distortion
194
- * @returns Dewarped VideoFrame
195
- */
196
- dewarp(frame: VideoFrame): Promise<VideoFrame>;
197
- /**
198
- * Update configuration
199
- */
176
+ /** Undistort a VideoFrame. */
177
+ undistort(frame: VideoFrame): Promise<VideoFrame>;
178
+ /** Update configuration. */
200
179
  updateConfig(options: Partial<FisheyeOptions>): void;
201
- /**
202
- * Clean up GPU resources
203
- */
180
+ /** Clean up GPU resources. */
204
181
  destroy(): void;
205
182
  }
206
183
 
207
- /**
208
- * Internal configuration after applying defaults
209
- */
210
- export declare interface FisheyeConfig extends Required<FisheyeOptions> {
184
+ /** Normalized configuration with grouped K, D, size. */
185
+ export declare interface FisheyeConfig {
186
+ K?: KMatrix;
187
+ D: DVector;
188
+ size: ImageSize;
189
+ balance: number;
190
+ fovScale: number;
191
+ projection: FisheyeProjection;
211
192
  }
212
193
 
213
- /**
214
- * Camera mount position. Affects which view range is meaningful (e.g. ceiling → 360° azimuth).
215
- * Used for defaults or validation when combined with projection.
216
- */
217
- export declare type FisheyeMount = "ceiling" | "wall" | "desk";
194
+ /** Flat D vector input - all required or all omitted */
195
+ export declare type FisheyeDistortionCoeffs = AllOrNothing<DVector>;
218
196
 
219
- /**
220
- * **Presets for UI:** Combine `mount` + `projection` (+ `fov`) and expose them to end users
221
- * as preset names instead of raw options. Example mapping:
222
- *
223
- * | User-facing name | mount | projection | fov |
224
- * |-------------------------|---------|----------------|----------------------|
225
- * | Panoramic 180 | any | equirectangular| 180 |
226
- * | 360° Panorama | ceiling | equirectangular| 360 |
227
- * | Normal / PTZ view | any | rectilinear | e.g. 90 |
228
- * | Quad (4 panes) | ceiling | rectilinear ×4 | 90 per pane (layout) |
229
- *
230
- * The library does not define preset names; the app chooses labels and maps them to
231
- * `FisheyeOptions` (and, for Quad, to multiple Fisheye instances or a layout pipeline).
232
- */
233
- /**
234
- * Options for configuring the Fisheye dewarper
235
- */
236
- export declare interface FisheyeOptions {
237
- /**
238
- * Fisheye distortion coefficient k1 (OpenCV fisheye / Kannala–Brandt).
239
- * From calibration; omit or 0 for ideal equidistant.
240
- * @default 0
241
- */
242
- k1?: number;
243
- /**
244
- * Fisheye distortion coefficient k2.
245
- * @default 0
246
- */
247
- k2?: number;
248
- /**
249
- * Fisheye distortion coefficient k3.
250
- * @default 0
251
- */
252
- k3?: number;
253
- /**
254
- * Fisheye distortion coefficient k4.
255
- * @default 0
256
- */
257
- k4?: number;
258
- /**
259
- * Output image width in pixels.
260
- * @default 300
261
- */
262
- width?: number;
263
- /**
264
- * Output image height in pixels.
265
- * @default 150
266
- */
267
- height?: number;
268
- /**
269
- * Field of view in degrees.
270
- * For rectilinear: angular diameter of the output. For equirectangular: horizontal span.
271
- * @default 180
272
- */
273
- fov?: number;
274
- /**
275
- * Projection used to map corrected angles to output pixels.
276
- * @default "rectilinear"
277
- */
197
+ /** Union of flat options and grouped config for API flexibility. */
198
+ export declare type FisheyeOptions = FisheyeOptionsStrict | FisheyeConfig;
199
+
200
+ /** Flat options for user-facing API */
201
+ export declare type FisheyeOptionsStrict = CameraIntrinsics & FisheyeDistortionCoeffs & OutputSize & {
202
+ balance?: number;
203
+ fovScale?: number;
278
204
  projection?: FisheyeProjection;
279
- /**
280
- * Camera mount position. Optional; can affect default FOV or valid range when projection is equirectangular.
281
- * @default "ceiling"
282
- */
283
- mount?: FisheyeMount;
284
- /**
285
- * X offset of the lens center (normalized, -1.0 to 1.0).
286
- * @default 0
287
- */
288
- centerX?: number;
289
- /**
290
- * Y offset of the lens center (normalized, -1.0 to 1.0).
291
- * @default 0
292
- */
293
- centerY?: number;
294
- /**
295
- * Zoom factor applied after distortion correction.
296
- * @default 1.0
297
- */
298
- zoom?: number;
205
+ };
206
+
207
+ export declare type FisheyeProjection = RectilinearProjection | {
208
+ readonly kind: "equirectangular";
209
+ } | {
210
+ readonly kind: "original";
211
+ } | {
212
+ readonly kind: "cylindrical";
213
+ };
214
+
215
+ /** Output image size */
216
+ export declare interface ImageSize {
217
+ width: number;
218
+ height: number;
299
219
  }
300
220
 
301
221
  /**
302
- * How the corrected ray directions are mapped to the output image.
222
+ * OpenCV Fisheye Camera Model (Kannala-Brandt) type definitions.
223
+ *
224
+ * **Distortion**: `θ_d = θ(1 + k₁θ² + k₂θ⁴ + k₃θ⁶ + k₄θ⁸)` where `θ = atan(r)`, `r² = a² + b²`
303
225
  *
304
- * - **rectilinear**: Standard perspective (like a normal camera). Straight lines in the
305
- * scene stay straight; the center is natural, edges are compressed. With wide FOV (e.g.
306
- * 180°) the edges look very squished. One "window" view.
226
+ * **Pixel coords**: `u = fx(x' + αy') + cx`, `v = fy·y' + cy`
307
227
  *
308
- * - **equirectangular**: Horizontal axis = azimuth (longitude), vertical = elevation
309
- * (latitude). The full horizontal span (e.g. 180°) maps linearly to the width, so you
310
- * get a wide panoramic strip: left = one side, right = the other. Classic panorama look.
228
+ * **K matrix**: `[[fx, 0, cx], [0, fy, cy], [0, 0, 1]]`
311
229
  *
312
- * Commercial equivalents:
313
- * - "Panoramic 180" / "180° Panorama" → equirectangular with 180° horizontal.
314
- * - "Panoramic" / "360° Panorama" → equirectangular with 360° horizontal (ceiling mount).
315
- * - "Normal" / "PTZ view" / single window → rectilinear (often with ~90° FOV per pane).
316
- * - "Panoramic with 4 panes" / "Quad" → layout of four rectilinear ~90° views, not one projection.
230
+ * **D vector**: `[k₁, k₂, k₃, k₄]`
231
+ *
232
+ * @see {@link https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html}
233
+ * @module
317
234
  */
318
- export declare type FisheyeProjection = "rectilinear" | "equirectangular";
235
+ /** K matrix - camera intrinsics */
236
+ export declare interface KMatrix {
237
+ fx: number;
238
+ fy: number;
239
+ cx?: number;
240
+ cy?: number;
241
+ alpha?: number;
242
+ }
243
+
244
+ /**
245
+ * New camera matrix P (Knew).
246
+ * @see {@link https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html#ga167df4b1c2e30f6c46a2af7fa6d4cfff|initUndistortRectifyMap}
247
+ */
248
+ export declare interface NewCameraMatrix {
249
+ readonly newFx: number;
250
+ readonly newFy: number;
251
+ readonly newCx?: number;
252
+ readonly newCy?: number;
253
+ }
254
+
255
+ /** Flat size input - both required or both omitted */
256
+ export declare type OutputSize = AllOrNothing<ImageSize>;
257
+
258
+ declare interface RectilinearAuto {
259
+ readonly kind: "rectilinear";
260
+ readonly mode?: undefined;
261
+ }
262
+
263
+ declare interface RectilinearManual extends NewCameraMatrix {
264
+ readonly kind: "rectilinear";
265
+ readonly mode: "manual";
266
+ }
267
+
268
+ declare type RectilinearProjection = RectilinearAuto | RectilinearManual;
319
269
 
320
270
  /**
321
271
  * Supported YUV pixel formats for VideoFrame creation