@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 +163 -50
- package/dist/index.d.ts +102 -152
- package/dist/index.js +284 -207
- package/dist/index.js.map +1 -1
- package/package.json +8 -5
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
|
|
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 [OpenCV
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
63
|
-
|
|
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
|
|
153
|
+
Creates a new Fisheye undistortion instance.
|
|
74
154
|
|
|
75
|
-
|
|
155
|
+
#### OpenCV Fisheye Model Parameters
|
|
76
156
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
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
|
-
**
|
|
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
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
### `
|
|
193
|
+
### `undistort(frame: VideoFrame): Promise<VideoFrame>`
|
|
98
194
|
|
|
99
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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",
|
|
242
|
+
format: "NV12", // YUV format
|
|
130
243
|
width: 1920,
|
|
131
244
|
height: 1080,
|
|
132
|
-
timestamp: performance.now() * 1000,
|
|
245
|
+
timestamp: performance.now() * 1000, // microseconds
|
|
133
246
|
});
|
|
134
247
|
|
|
135
|
-
const
|
|
136
|
-
frame.close();
|
|
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
|
|
160
|
-
|
|
161
|
-
| `I420`
|
|
162
|
-
| `NV12`
|
|
163
|
-
| `I420A` | YUV 4:2:0 planar with alpha
|
|
164
|
-
| `I422`
|
|
165
|
-
| `I444`
|
|
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);
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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
|
-
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
313
|
-
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
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
|
-
|
|
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
|