@gyeonghokim/fisheye.js 1.1.0 → 1.1.1

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,48 +45,45 @@ in your code,
45
45
  ```ts
46
46
  import { Fisheye } from "@gyeonghokim/fisheye.js";
47
47
 
48
- const dewarper = new Fisheye({
48
+ const fisheye = new Fisheye({
49
+ // OpenCV fisheye distortion coefficients (from calibration)
49
50
  k1: 0.5,
50
51
  k2: 0.0,
51
52
  k3: 0.0,
52
53
  k4: 0.0,
54
+
55
+ // Output configuration
53
56
  width: 1920,
54
57
  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
58
+
59
+ // Optional: OpenCV camera matrix parameters
60
+ fx: 1000, // focal length x (pixels)
61
+ fy: 1000, // focal length y (pixels)
62
+ cx: 960, // principal point x (pixels)
63
+ cy: 540, // principal point y (pixels)
64
+
65
+ // Optional: Advanced OpenCV parameters
66
+ balance: 0.0, // 0.0 = all pixels, 1.0 = original FOV
67
+ fovScale: 1.0, // >1.0 = zoom out, <1.0 = zoom in
68
+
69
+ // Optional: Additional features (not part of OpenCV)
70
+ projection: "rectilinear", // "rectilinear" | "equirectangular"
71
+ mount: "ceiling", // "ceiling" | "wall" | "desk"
59
72
  });
60
73
 
61
- const renderLoop = (timestamp: DOMHighResTimestamp) => {
62
- // your render logic
63
- const dewarpedVideoFrame: VideoFrame = await dewarper.dewarp(yourVideoFrame);
64
- yourYUVPlayer.draw(dewarpedVideoFrame);
74
+ const renderLoop = async (timestamp: DOMHighResTimestamp) => {
75
+ // Undistort the fisheye frame
76
+ const undistorted: VideoFrame = await fisheye.undistort(yourVideoFrame);
77
+ yourYUVPlayer.draw(undistorted);
65
78
  requestAnimationFrame(renderLoop);
66
79
  };
67
80
  ```
68
81
 
69
- ## API
82
+ ## Architecture & Design
70
83
 
71
- ### `new Fisheye(options?: FisheyeOptions)`
84
+ ### Distortion Model: OpenCV Fisheye (Kannala-Brandt)
72
85
 
73
- Creates a new Fisheye dewarper instance.
74
-
75
- **Options:**
76
-
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`
87
-
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:
86
+ This library uses the **OpenCV fisheye model** (Kannala-Brandt, 2006) for undistortion:
90
87
 
91
88
  ```
92
89
  theta = atan(r)
@@ -94,23 +91,123 @@ theta_d = theta * (1 + k1*theta^2 + k2*theta^4 + k3*theta^6 + k4*theta^8)
94
91
  r_d = tan(theta_d)
95
92
  ```
96
93
 
97
- ### `dewarp(frame: VideoFrame): Promise<VideoFrame>`
94
+ This is the same model as [OpenCV's fisheye module](https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html).
95
+
96
+ **Important:** OpenCV's `fisheye.undistortImage()` always outputs **rectilinear (perspective) projection only**. It does not provide panoramic or other projection modes.
97
+
98
+ ### Additional Features Beyond OpenCV
99
+
100
+ For **WebGPU efficiency**, we integrate additional transformations in a **single GPU pass** rather than CPU-side post-processing:
101
+
102
+ #### 1. **Projection Mode** (`projection`)
103
+
104
+ - **Purpose**: Controls output coordinate mapping after undistortion
105
+ - **Values**:
106
+ - `"rectilinear"`: Standard perspective projection (same as OpenCV output)
107
+ - `"equirectangular"`: Cylindrical/spherical panoramic projection
108
+ - **Implementation**: Applied in GPU shader after undistortion step
109
+ - **OpenCV equivalent**: Would require separate `cv2.warpPerspective()` or custom remapping after `fisheye.undistortImage()`
110
+
111
+ #### 2. **Mount Orientation** (`mount`)
112
+
113
+ - **Purpose**: Compensates for camera mounting angle
114
+ - **Values**:
115
+ - `"ceiling"`: No rotation (0°)
116
+ - `"wall"`: 90° rotation
117
+ - `"desk"`: 180° or -90° rotation
118
+ - **Implementation**: Applied as rotation transformation in GPU shader
119
+ - **OpenCV equivalent**: Would require `cv2.getRotationMatrix2D()` + `cv2.warpAffine()` after undistortion
120
+
121
+ ### Why Unified GPU Pipeline?
122
+
123
+ Traditional OpenCV approach:
124
+
125
+ ```python
126
+ # Step 1: Undistort (GPU/CPU)
127
+ undistorted = cv2.fisheye.undistortImage(img, K, D, Knew)
128
+
129
+ # Step 2: Projection transform (CPU)
130
+ panorama = cv2.remap(undistorted, custom_map_x, custom_map_y, cv2.INTER_LINEAR)
131
+
132
+ # Step 3: Rotation (CPU)
133
+ rotated = cv2.warpAffine(panorama, rotation_matrix, size)
134
+ ```
135
+
136
+ **Our approach (single GPU compute shader):**
137
+
138
+ ```typescript
139
+ // All in one GPU pass: undistortion + projection + rotation
140
+ const undistorted = await fisheye.undistort(input);
141
+ ```
142
+
143
+ ## API
144
+
145
+ ### `new Fisheye(options?: FisheyeOptions)`
146
+
147
+ Creates a new Fisheye undistortion instance.
148
+
149
+ #### OpenCV Fisheye Model Parameters
150
+
151
+ | Parameter | Type | Default | Description |
152
+ | ---------- | --------- | ---------- | -------------------------------------------------------- |
153
+ | `fx` | `number?` | auto | Camera matrix K: focal length in x-axis (pixels) |
154
+ | `fy` | `number?` | auto | Camera matrix K: focal length in y-axis (pixels) |
155
+ | `cx` | `number?` | `width/2` | Camera matrix K: principal point x-coordinate (pixels) |
156
+ | `cy` | `number?` | `height/2` | Camera matrix K: principal point y-coordinate (pixels) |
157
+ | `k1` | `number?` | `0` | Distortion coefficient k1 (Kannala-Brandt) |
158
+ | `k2` | `number?` | `0` | Distortion coefficient k2 (Kannala-Brandt) |
159
+ | `k3` | `number?` | `0` | Distortion coefficient k3 (Kannala-Brandt) |
160
+ | `k4` | `number?` | `0` | Distortion coefficient k4 (Kannala-Brandt) |
161
+ | `width` | `number?` | `300` | Output image width (OpenCV `new_size.width`) |
162
+ | `height` | `number?` | `150` | Output image height (OpenCV `new_size.height`) |
163
+ | `balance` | `number?` | `0.0` | Balance parameter (0.0 = all pixels, 1.0 = original FOV) |
164
+ | `fovScale` | `number?` | `1.0` | FOV scale divisor (>1.0 = zoom out, <1.0 = zoom in) |
98
165
 
99
- Dewarps a VideoFrame with fisheye distortion.
166
+ **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()`.
167
+
168
+ #### Additional Features (not part of OpenCV)
169
+
170
+ | Parameter | Type | Default | Description |
171
+ | ------------ | -------------------------------------- | --------------- | ------------------------------------------------------------------------------------------------------- |
172
+ | `projection` | `"rectilinear"` \| `"equirectangular"` | `"rectilinear"` | Output projection mode. `"rectilinear"` = perspective (same as OpenCV), `"equirectangular"` = panoramic |
173
+ | `mount` | `"ceiling"` \| `"wall"` \| `"desk"` | `"ceiling"` | Camera mount orientation for rotation compensation |
174
+
175
+ ### `undistort(frame: VideoFrame): Promise<VideoFrame>`
176
+
177
+ Undistorts a VideoFrame with fisheye distortion.
100
178
 
101
179
  **Parameters:**
102
180
 
103
181
  - `frame`: Input VideoFrame with fisheye distortion
104
182
 
105
- **Returns:** Promise that resolves to a dewarped VideoFrame
183
+ **Returns:** Promise that resolves to an undistorted VideoFrame
184
+
185
+ **Differences from OpenCV:**
186
+
187
+ Unlike OpenCV's `fisheye.undistortImage()` which only outputs rectilinear (perspective) projection, this method performs **all transformations in a single GPU pass** for WebGPU efficiency:
188
+
189
+ 1. **Undistortion** (OpenCV fisheye model)
190
+ 2. **Projection** (rectilinear or equirectangular, based on `projection` config)
191
+ 3. **Mount rotation** (based on `mount` config)
192
+
193
+ OpenCV equivalent would require 3 separate operations:
194
+ ```python
195
+ # OpenCV: 3 CPU/GPU roundtrips
196
+ undistorted = cv2.fisheye.undistortImage(img, K, D, Knew)
197
+ panorama = cv2.remap(undistorted, map_x, map_y, cv2.INTER_LINEAR) # if equirectangular
198
+ rotated = cv2.warpAffine(panorama, rotation_matrix, size) # if ceiling/wall/desk
199
+
200
+ # fisheye.js: 1 GPU pass - undistortion + projection + rotation
201
+ undistorted = await fisheye.undistort(img)
202
+ ```
106
203
 
107
204
  ### `updateConfig(options: Partial<FisheyeOptions>): void`
108
205
 
109
- Updates the dewarper configuration. You can update any subset of the original options.
206
+ Updates the configuration. You can update any subset of the original options.
110
207
 
111
208
  ### `destroy(): void`
112
209
 
113
- Cleans up GPU resources. Call this when you're done using the dewarper.
210
+ Cleans up GPU resources. Call this when you're done using the instance.
114
211
 
115
212
  ## Working with YUV Binary Data
116
213
 
@@ -119,21 +216,21 @@ If you receive raw YUV binary data from a camera or server, you can use the `cre
119
216
  ```ts
120
217
  import { Fisheye, createVideoFrameFromYUV } from "@gyeonghokim/fisheye.js";
121
218
 
122
- const dewarper = new Fisheye({ k1: 0.5, width: 1920, height: 1080 });
219
+ const fisheye = new Fisheye({ k1: 0.5, width: 1920, height: 1080 });
123
220
 
124
221
  // Example: Receiving NV12 data from a server
125
222
  const response = await fetch("/api/camera/frame");
126
223
  const yuvBuffer = await response.arrayBuffer();
127
224
 
128
225
  const frame = createVideoFrameFromYUV(new Uint8Array(yuvBuffer), {
129
- format: "NV12", // YUV format
226
+ format: "NV12", // YUV format
130
227
  width: 1920,
131
228
  height: 1080,
132
- timestamp: performance.now() * 1000, // microseconds
229
+ timestamp: performance.now() * 1000, // microseconds
133
230
  });
134
231
 
135
- const dewarpedFrame = await dewarper.dewarp(frame);
136
- frame.close(); // Don't forget to close the original frame
232
+ const undistorted = await fisheye.undistort(frame);
233
+ frame.close(); // Don't forget to close the original frame
137
234
  ```
138
235
 
139
236
  ### `createVideoFrameFromYUV(data, options)`
@@ -156,13 +253,13 @@ Creates a VideoFrame from YUV binary data.
156
253
 
157
254
  **Supported YUV Formats:**
158
255
 
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 |
256
+ | Format | Description | Data Size |
257
+ | ------- | ----------------------------------------------- | -------------------- |
258
+ | `I420` | YUV 4:2:0 planar (Y, U, V planes) | width × height × 1.5 |
259
+ | `NV12` | YUV 4:2:0 semi-planar (Y plane, interleaved UV) | width × height × 1.5 |
260
+ | `I420A` | YUV 4:2:0 planar with alpha | width × height × 2.5 |
261
+ | `I422` | YUV 4:2:2 planar | width × height × 2 |
262
+ | `I444` | YUV 4:4:4 planar | width × height × 3 |
166
263
 
167
264
  ### `calculateYUVDataSize(format, width, height)`
168
265
 
@@ -171,7 +268,7 @@ Calculates the expected byte size for YUV data.
171
268
  ```ts
172
269
  import { calculateYUVDataSize } from "@gyeonghokim/fisheye.js";
173
270
 
174
- const size = calculateYUVDataSize("NV12", 1920, 1080); // 3110400 bytes
271
+ const size = calculateYUVDataSize("NV12", 1920, 1080); // 3110400 bytes
175
272
  ```
176
273
 
177
274
  ## Development
package/dist/index.d.ts CHANGED
@@ -121,19 +121,8 @@ export declare interface CreateVideoFrameOptions {
121
121
  }
122
122
 
123
123
  /**
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
- * ```
124
+ * Fisheye undistortion using WebGPU via TypeGPU.
125
+ * Implements OpenCV fisheye model (Kannala-Brandt).
137
126
  */
138
127
  export declare class Fisheye {
139
128
  private config;
@@ -142,7 +131,7 @@ export declare class Fisheye {
142
131
  private inputTexture;
143
132
  private outputTexture;
144
133
  private bindGroup;
145
- private dewarpPipeline;
134
+ private pipeline;
146
135
  private readbackBuffers;
147
136
  private readbackIndex;
148
137
  private readbackHasData;
@@ -153,169 +142,163 @@ export declare class Fisheye {
153
142
  private outputTextureSize;
154
143
  private uniformInputWidth;
155
144
  private uniformInputHeight;
145
+ private cachedNewCameraMatrix;
156
146
  constructor(options?: FisheyeOptions);
157
- /**
158
- * Apply default values to options
159
- */
160
147
  private applyDefaults;
161
- /**
162
- * Initialize TypeGPU root and resources
163
- */
148
+ /** Undistort a point using Newton's method (OpenCV fisheye inverse). */
149
+ private undistortPointNormalized;
150
+ private undistortPixelToNormalized;
151
+ /** Compute new camera matrix (OpenCV estimateNewCameraMatrixForUndistortRectify). */
152
+ private computeNewCameraMatrix;
164
153
  private initialize;
165
- /**
166
- * Get uniform data from current configuration
167
- */
168
154
  private getUniformData;
169
- /**
170
- * Update uniform buffer with current configuration
171
- */
172
155
  private updateUniforms;
173
156
  private readbackToVideoFrame;
174
- /**
175
- * Create input texture (TypeGPU; per official docs: sampled + render for .write(image/VideoFrame)).
176
- */
177
157
  private createInputTexture;
178
- /**
179
- * Create output storage texture (TypeGPU; type-safe with layout.$)
180
- */
181
158
  private createOutputTexture;
182
- /**
183
- * Calculate bytes per row with proper alignment (256-byte alignment for WebGPU)
184
- */
185
159
  private calculateBytesPerRow;
186
- /**
187
- * Create or recreate readback buffer for GPU to CPU data transfer
188
- */
189
160
  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
- */
161
+ /** Undistort a VideoFrame. */
162
+ undistort(frame: VideoFrame): Promise<VideoFrame>;
163
+ /** Update configuration. */
200
164
  updateConfig(options: Partial<FisheyeOptions>): void;
201
- /**
202
- * Clean up GPU resources
203
- */
165
+ /** Clean up GPU resources. */
204
166
  destroy(): void;
205
167
  }
206
168
 
207
169
  /**
208
- * Internal configuration after applying defaults
170
+ * Internal configuration with all defaults applied.
171
+ *
172
+ * Note: fx, fy, cx, cy are optional because they default to input image dimensions
173
+ * which are not known until undistort() is called.
209
174
  */
210
- export declare interface FisheyeConfig extends Required<FisheyeOptions> {
175
+ export declare interface FisheyeConfig {
176
+ /** Camera matrix K: fx. Defaults to input width if not specified. */
177
+ fx: number | undefined;
178
+ /** Camera matrix K: fy. Defaults to input width if not specified. */
179
+ fy: number | undefined;
180
+ /** Camera matrix K: cx. Defaults to input width / 2 if not specified. */
181
+ cx: number | undefined;
182
+ /** Camera matrix K: cy. Defaults to input height / 2 if not specified. */
183
+ cy: number | undefined;
184
+ /** Distortion coefficient k1. */
185
+ k1: number;
186
+ /** Distortion coefficient k2. */
187
+ k2: number;
188
+ /** Distortion coefficient k3. */
189
+ k3: number;
190
+ /** Distortion coefficient k4. */
191
+ k4: number;
192
+ /** Output width. */
193
+ width: number;
194
+ /** Output height. */
195
+ height: number;
196
+ /** Balance parameter. */
197
+ balance: number;
198
+ /** FOV scale parameter. */
199
+ fovScale: number;
200
+ /** Output projection mode. */
201
+ projection: FisheyeProjection;
202
+ /** Camera mount position. */
203
+ mount: FisheyeMount;
211
204
  }
212
205
 
213
206
  /**
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.
207
+ * Camera mount position.
216
208
  */
217
209
  export declare type FisheyeMount = "ceiling" | "wall" | "desk";
218
210
 
219
211
  /**
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) |
212
+ * Fisheye undistortion configuration.
229
213
  *
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
214
+ * Based on OpenCV fisheye camera model (Kannala-Brandt).
215
+ * @see https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html
235
216
  */
236
217
  export declare interface FisheyeOptions {
237
218
  /**
238
- * Fisheye distortion coefficient k1 (OpenCV fisheye / Kannala–Brandt).
239
- * From calibration; omit or 0 for ideal equidistant.
219
+ * Camera matrix K: focal length x (pixels).
220
+ * @see https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html
221
+ */
222
+ fx?: number;
223
+ /**
224
+ * Camera matrix K: focal length y (pixels).
225
+ * @see https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html
226
+ */
227
+ fy?: number;
228
+ /**
229
+ * Camera matrix K: principal point x (pixels).
230
+ * @see https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html
231
+ */
232
+ cx?: number;
233
+ /**
234
+ * Camera matrix K: principal point y (pixels).
235
+ * @see https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html
236
+ */
237
+ cy?: number;
238
+ /**
239
+ * Distortion coefficient k1 (Kannala-Brandt).
240
240
  * @default 0
241
+ * @see https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html
241
242
  */
242
243
  k1?: number;
243
244
  /**
244
- * Fisheye distortion coefficient k2.
245
+ * Distortion coefficient k2 (Kannala-Brandt).
245
246
  * @default 0
247
+ * @see https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html
246
248
  */
247
249
  k2?: number;
248
250
  /**
249
- * Fisheye distortion coefficient k3.
251
+ * Distortion coefficient k3 (Kannala-Brandt).
250
252
  * @default 0
253
+ * @see https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html
251
254
  */
252
255
  k3?: number;
253
256
  /**
254
- * Fisheye distortion coefficient k4.
257
+ * Distortion coefficient k4 (Kannala-Brandt).
255
258
  * @default 0
259
+ * @see https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html
256
260
  */
257
261
  k4?: number;
258
262
  /**
259
- * Output image width in pixels.
263
+ * Output image width (pixels).
260
264
  * @default 300
265
+ * @see https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html
261
266
  */
262
267
  width?: number;
263
268
  /**
264
- * Output image height in pixels.
269
+ * Output image height (pixels).
265
270
  * @default 150
271
+ * @see https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html
266
272
  */
267
273
  height?: number;
268
274
  /**
269
- * Field of view in degrees.
270
- * For rectilinear: angular diameter of the output. For equirectangular: horizontal span.
271
- * @default 180
275
+ * Balance between all pixels vs original FOV (0.0-1.0).
276
+ * @default 0.0
277
+ * @see https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html
272
278
  */
273
- fov?: number;
279
+ balance?: number;
274
280
  /**
275
- * Projection used to map corrected angles to output pixels.
281
+ * FOV scale divisor (>1.0 = zoom out, <1.0 = zoom in).
282
+ * @default 1.0
283
+ * @see https://docs.opencv.org/4.x/db/d58/group__calib3d__fisheye.html
284
+ */
285
+ fovScale?: number;
286
+ /**
287
+ * Output projection mode.
276
288
  * @default "rectilinear"
277
289
  */
278
290
  projection?: FisheyeProjection;
279
291
  /**
280
- * Camera mount position. Optional; can affect default FOV or valid range when projection is equirectangular.
292
+ * Camera mount position.
281
293
  * @default "ceiling"
282
294
  */
283
295
  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;
299
296
  }
300
297
 
301
298
  /**
302
- * How the corrected ray directions are mapped to the output image.
303
- *
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.
307
- *
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.
311
- *
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.
299
+ * Output projection mode.
317
300
  */
318
- export declare type FisheyeProjection = "rectilinear" | "equirectangular";
301
+ export declare type FisheyeProjection = "rectilinear" | "equirectangular" | "original";
319
302
 
320
303
  /**
321
304
  * Supported YUV pixel formats for VideoFrame creation