react-native-image-stitcher 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +200 -8
- package/android/src/main/java/io/imagestitcher/rn/CvFlowGateFrameProcessor.kt +2 -2
- package/android/src/main/java/io/imagestitcher/rn/IncrementalFirstwinsEngine.kt +120 -0
- package/android/src/main/java/io/imagestitcher/rn/IncrementalStitcher.kt +266 -385
- package/android/src/main/java/io/imagestitcher/rn/KeyframeGate.kt +6 -3
- package/android/src/main/java/io/imagestitcher/rn/RNSARCameraView.kt +17 -30
- package/dist/camera/Camera.d.ts +29 -27
- package/dist/camera/Camera.js +48 -79
- package/dist/index.d.ts +0 -2
- package/dist/index.js +4 -6
- package/dist/stitching/incremental.d.ts +10 -11
- package/dist/stitching/useFrameProcessorDriver.d.ts +7 -6
- package/dist/stitching/useFrameProcessorDriver.js +12 -11
- package/ios/Package.swift +35 -21
- package/ios/Sources/RNImageStitcher/IncrementalStitcher.swift +85 -206
- package/ios/Sources/RNImageStitcher/IncrementalStitcherBridge.m +0 -8
- package/ios/Sources/RNImageStitcher/IncrementalStitcherBridge.swift +6 -126
- package/ios/Sources/RNImageStitcher/KeyframeGateFrameProcessor.mm +6 -6
- package/package.json +1 -1
- package/src/camera/Camera.tsx +83 -107
- package/src/index.ts +3 -8
- package/src/stitching/incremental.ts +10 -11
- package/src/stitching/useFrameProcessorDriver.ts +12 -11
- package/dist/stitching/useIncrementalJSDriver.d.ts +0 -74
- package/dist/stitching/useIncrementalJSDriver.js +0 -220
- package/src/stitching/useIncrementalJSDriver.ts +0 -297
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,177 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
16
16
|
|
|
17
17
|
## [Unreleased]
|
|
18
18
|
|
|
19
|
+
## [0.6.0] — 2026-05-25
|
|
20
|
+
|
|
21
|
+
> [!WARNING]
|
|
22
|
+
> **Breaking changes.** v0.6.0 retires the deprecated JS-driver
|
|
23
|
+
> non-AR path that was marked for removal in v0.5.0's *Deprecated*
|
|
24
|
+
> section. Hosts using the default `<Camera>` flow (`legacyDriver`
|
|
25
|
+
> unset) are not affected — they were already on
|
|
26
|
+
> `useFrameProcessorDriver`. Hosts that opted into the legacy
|
|
27
|
+
> driver (`legacyDriver={true}` on `<Camera>`, or a direct
|
|
28
|
+
> `useIncrementalJSDriver()` consumer) MUST migrate to the Frame
|
|
29
|
+
> Processor driver — see *Migration from 0.5.x* below.
|
|
30
|
+
|
|
31
|
+
### Removed (breaking)
|
|
32
|
+
|
|
33
|
+
- **`useIncrementalJSDriver` hook** + its `UseIncrementalJSDriverOptions`
|
|
34
|
+
/ `IncrementalJSDriverHandle` types. Deprecated in v0.5.0; the
|
|
35
|
+
v0.5 deprecation warning has now been replaced by deletion.
|
|
36
|
+
- **`legacyDriver?: boolean` prop on `<Camera>`**. The escape hatch
|
|
37
|
+
back to the JS driver is gone. Hosts that set this prop will
|
|
38
|
+
get a TS-level error; at runtime the prop is silently ignored.
|
|
39
|
+
- **`frameSourceMode: 'jsDriver'`** enum value in
|
|
40
|
+
`IncrementalStartOptions`. The TS type is now narrowed to
|
|
41
|
+
`'arSession' | 'frameProcessor'`. Passing `'jsDriver'` is a
|
|
42
|
+
compile error; at the native bridge layer the value falls through
|
|
43
|
+
to the default (now `'arSession'`).
|
|
44
|
+
- **`IncrementalStitcher.processFrameAtPath` native method** on both
|
|
45
|
+
iOS and Android. The only JS caller was `useIncrementalJSDriver`,
|
|
46
|
+
also deleted. Hosts calling
|
|
47
|
+
`NativeModules.IncrementalStitcher.processFrameAtPath(...)` via
|
|
48
|
+
raw `NativeModules` access will get a runtime "method does not
|
|
49
|
+
exist" error. Use the Frame Processor driver instead.
|
|
50
|
+
|
|
51
|
+
### Changed (breaking)
|
|
52
|
+
|
|
53
|
+
- **Android `frameSourceMode` default switched from `"jsDriver"` to
|
|
54
|
+
`"arSession"`** for parity with iOS. Raw `NativeModules` callers
|
|
55
|
+
that omitted `frameSourceMode` were previously getting an inert
|
|
56
|
+
capture (the "jsDriver" branch dropped all engine input on
|
|
57
|
+
Android since v0.5.0); they now get AR-mode behaviour, matching
|
|
58
|
+
iOS. The production `<Camera>` is unaffected — it always passes
|
|
59
|
+
`frameSourceMode: 'arSession'` explicitly for AR captures.
|
|
60
|
+
|
|
61
|
+
### Changed (non-breaking)
|
|
62
|
+
|
|
63
|
+
- **`RNSARCameraView` (AR mode) no longer eager-encodes a JPEG per
|
|
64
|
+
ARCore frame.** Migrated to the pixel-data path introduced for
|
|
65
|
+
the Frame Processor in v0.5.1's F8.6 work. AR-mode captures now
|
|
66
|
+
pass `nv21PixelData` / `nv21PixelWidth` / `nv21PixelHeight`
|
|
67
|
+
through `ingestFromARCameraView`; `legacyJpegPath` is always null
|
|
68
|
+
on this path. Expected gain on Galaxy A35: ~30-50 ms per
|
|
69
|
+
accepted frame, with the dominant savings on rejected frames
|
|
70
|
+
(no JPEG encode → no imread round-trip). Closes the v0.5.0
|
|
71
|
+
follow-up.
|
|
72
|
+
|
|
73
|
+
### Removed (internal cleanup; no external API impact)
|
|
74
|
+
|
|
75
|
+
- **F8.6 perf-diagnostic logs** (`F8.6-route`, `F8.6-perf`)
|
|
76
|
+
introduced in v0.5.1 stripped from `IncrementalStitcher` +
|
|
77
|
+
`IncrementalFirstwinsEngine` — F8.6 is now baked in for
|
|
78
|
+
production and the diagnostic spam is no longer informative.
|
|
79
|
+
- **Orphaned native helpers** dropped after `processFrameAtPath`
|
|
80
|
+
removal:
|
|
81
|
+
- iOS: `addBatchKeyframePath(path:pose:)`, `isBatchKeyframeMode`
|
|
82
|
+
getter, `decodeJpegToGrayscalePixelBuffer` (only callers were
|
|
83
|
+
`processFrameAtPath`).
|
|
84
|
+
- Android: `decodeJpegToGrayscale` + `GrayscaleFrame` data class,
|
|
85
|
+
`isBatchKeyframeMode` getter (only callers were
|
|
86
|
+
`processFrameAtPath` and the AR-mode eager-encode branch).
|
|
87
|
+
- **Stale comments** referencing removed code paths swept across
|
|
88
|
+
Kotlin/Swift/Obj-C/TS. Historical "removed in v0.6" markers
|
|
89
|
+
retained; comments that described live code in terms of the
|
|
90
|
+
removed names rewritten to describe current behaviour.
|
|
91
|
+
|
|
92
|
+
### Migration from 0.5.x
|
|
93
|
+
|
|
94
|
+
**Default `<Camera>` hosts (no `legacyDriver` prop set):** no
|
|
95
|
+
action required. `<Camera>` already used `useFrameProcessorDriver`
|
|
96
|
+
in non-AR mode and `RNSARSession` in AR mode since v0.5.0.
|
|
97
|
+
|
|
98
|
+
**Hosts with `legacyDriver={true}` on `<Camera>`:** remove the
|
|
99
|
+
prop. `<Camera>` will use the Frame Processor driver, which has
|
|
100
|
+
been the default since v0.5.0 and the only path since this release.
|
|
101
|
+
|
|
102
|
+
```tsx
|
|
103
|
+
// Before (v0.5.x)
|
|
104
|
+
<Camera legacyDriver={true} ... />
|
|
105
|
+
|
|
106
|
+
// After (v0.6.0)
|
|
107
|
+
<Camera ... />
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**Hosts directly using `useIncrementalJSDriver`:** migrate to
|
|
111
|
+
`useFrameProcessorDriver`. The handle shape (`{ start, stop,
|
|
112
|
+
frameProcessor, isRunning }`) is preserved, but the new hook is a
|
|
113
|
+
Frame Processor + gyro driver instead of a `takeSnapshot` + JS
|
|
114
|
+
interval driver. See
|
|
115
|
+
[`src/stitching/useFrameProcessorDriver.ts`](src/stitching/useFrameProcessorDriver.ts)
|
|
116
|
+
for the migration mapping; the gyro pose synthesis convention
|
|
117
|
+
(`q = q_yaw * q_pitch * q_roll`) is identical, so existing pose
|
|
118
|
+
math at call sites continues to work.
|
|
119
|
+
|
|
120
|
+
**Hosts passing `frameSourceMode: 'jsDriver'` to
|
|
121
|
+
`incremental.start(...)`:** change to `'frameProcessor'`. The
|
|
122
|
+
TypeScript type now rejects `'jsDriver'` at compile time.
|
|
123
|
+
|
|
124
|
+
## [0.5.1] — 2026-05-25
|
|
125
|
+
|
|
126
|
+
### Added — F8.6 Android pixel-buffer engine parity
|
|
127
|
+
|
|
128
|
+
Closes the v0.5.0 follow-up tracked in the [0.5.0] section.
|
|
129
|
+
|
|
130
|
+
**Live engine ingest no longer requires a JPEG round-trip.**
|
|
131
|
+
The `IncrementalFirstwinsEngine` (slit-scan / first-wins) and the
|
|
132
|
+
hybrid `IncrementalEngine` both gained a new
|
|
133
|
+
`addFramePixelData(nv21, w, h, ...)` method. It builds the BGR
|
|
134
|
+
`cv::Mat` in-process via
|
|
135
|
+
`Imgproc.cvtColor(yuv, COLOR_YUV2BGR_NV21)`, then delegates to a
|
|
136
|
+
newly-extracted shared `addFrameMat` helper that runs the original
|
|
137
|
+
engine pipeline verbatim. The legacy `addFrameAtPath(path, ...)`
|
|
138
|
+
is now a thin wrapper: `imread → downsample → addFrameMat`.
|
|
139
|
+
|
|
140
|
+
**Routing.** `IncrementalStitcher.ingestFromARCameraView` got
|
|
141
|
+
three optional parameters — `nv21PixelData: ByteArray?`,
|
|
142
|
+
`nv21PixelWidth: Int`, `nv21PixelHeight: Int`. When supplied (and
|
|
143
|
+
`batchKeyframeMode == false`), the live engine ingests via
|
|
144
|
+
`addFramePixelData`; otherwise falls back to `addFrameAtPath` with
|
|
145
|
+
`legacyJpegPath`. Backwards-compatible — all-null defaults
|
|
146
|
+
preserve every existing caller.
|
|
147
|
+
|
|
148
|
+
**Frame Processor wiring.** `consumeFrameFromPlugin` now packs the
|
|
149
|
+
incoming `Image` NV21 once at the top (was twice — gate consumed
|
|
150
|
+
Y only, then the `onAccept` lambda re-packed for JPEG encode) and
|
|
151
|
+
threads the bytes through to both the gate (which reads only the
|
|
152
|
+
Y subset) AND the new `nv21PixelData` parameter. Net: single
|
|
153
|
+
`packNV21` per producer-thread frame.
|
|
154
|
+
|
|
155
|
+
**Measured on Galaxy A35, `engine: 'firstwins-rectilinear'`,
|
|
156
|
+
non-AR Frame Processor capture:**
|
|
157
|
+
|
|
158
|
+
| Outcome | F8.6 pixel-data | Legacy JPEG path (estimated) |
|
|
159
|
+
|---|---|---|
|
|
160
|
+
| `AcceptedHigh` (first-frame init) | 7–11 ms | 50–70 ms |
|
|
161
|
+
| `SkippedTooClose` (gate bail) | 0.5–2 ms | 50–60 ms (imread is unconditional) |
|
|
162
|
+
|
|
163
|
+
`SkippedTooClose` dominates the producer-thread frame budget
|
|
164
|
+
(~95% of frames at 30 fps with a slow pan). Eliminating the
|
|
165
|
+
imread on those frames is the bulk of the F8.6 win.
|
|
166
|
+
|
|
167
|
+
### Added
|
|
168
|
+
|
|
169
|
+
* New `<Camera engine={...}>` prop exposes the live engine
|
|
170
|
+
selection (`'batch-keyframe'` (default) / `'firstwins-rectilinear'`
|
|
171
|
+
/ `'hybrid'` / `'slitscan-*'`). Lets hosts opt into in-flight
|
|
172
|
+
stitching for low-latency previews; previously the choice was
|
|
173
|
+
hardcoded.
|
|
174
|
+
|
|
175
|
+
### Changed
|
|
176
|
+
|
|
177
|
+
* `New: F8.6 perf-diagnostic logs` (`F8.6-route`, `F8.6-perf`) fire
|
|
178
|
+
in live-engine mode only — inert under the default
|
|
179
|
+
`batch-keyframe`. Will be removed in v0.6 once F8.6 is baked in
|
|
180
|
+
production.
|
|
181
|
+
|
|
182
|
+
### Fixed
|
|
183
|
+
|
|
184
|
+
* In `IncrementalStitcher.consumeFrameFromPlugin`, the `onAccept`
|
|
185
|
+
lambda was re-packing the live `Image` instead of reusing the
|
|
186
|
+
already-packed NV21 from the outer scope. Now it reuses the
|
|
187
|
+
outer `packed` — saves a redundant `packNV21` call on every
|
|
188
|
+
accepted frame.
|
|
189
|
+
|
|
19
190
|
## [0.5.0] — 2026-05-25
|
|
20
191
|
|
|
21
192
|
### Added — F8 Frame Processor port
|
|
@@ -91,13 +262,29 @@ Frame Processor** on the camera producer thread instead of the
|
|
|
91
262
|
|
|
92
263
|
### Tracking — known follow-ups (don't gate this release)
|
|
93
264
|
|
|
94
|
-
- **F8.6** — Android engine refactor for pixel-buffer-
|
|
95
|
-
ingest (true zero-copy parity with iOS).
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
`
|
|
99
|
-
|
|
100
|
-
|
|
265
|
+
- **F8.6 (v0.5.1)** — Android engine refactor for pixel-buffer-
|
|
266
|
+
direct ingest (true zero-copy parity with iOS). Would extract
|
|
267
|
+
an `addFrameMat` helper from `IncrementalFirstwinsEngine` and
|
|
268
|
+
`IncrementalEngine`'s `addFrameAtPath`, add a parallel
|
|
269
|
+
`addFramePixelData` that constructs the BGR `cv::Mat` from NV21
|
|
270
|
+
bytes via `cvtColor`, and rewire `RNSARCameraView` to skip the
|
|
271
|
+
per-frame JPEG encode. Expected gain: ~30–50 ms per accepted
|
|
272
|
+
frame. Deferred because the engine bodies are 400+ lines of
|
|
273
|
+
complex AR-mode code; needs A35 device verification before
|
|
274
|
+
merge, which the v0.5.0 prep session didn't have.
|
|
275
|
+
|
|
276
|
+
- **F8.3-followup-roll** — resolved in v0.5.0.
|
|
277
|
+
|
|
278
|
+
- **F8.3.H2-target** — RESOLVED in v0.5.0 via a different
|
|
279
|
+
mechanism than originally planned. The selector pin is now a
|
|
280
|
+
compile-time `#selector(...)` reference inside
|
|
281
|
+
`IncrementalStitcher.swift` plus a dev-build runtime assert in
|
|
282
|
+
`IncrementalStitcher.init()` — both fire if the Swift method
|
|
283
|
+
signature drifts from what `KeyframeGateFrameProcessor.mm`
|
|
284
|
+
expects. The obsolete test file was deleted. `swift test` now
|
|
285
|
+
runs the (8-test) `QualityCheckerTests` suite cleanly because
|
|
286
|
+
`Package.swift` switched from an exclude list (broke every time
|
|
287
|
+
a new `.mm` landed) to an explicit `sources` allowlist.
|
|
101
288
|
|
|
102
289
|
## [0.4.1] — 2026-05-23
|
|
103
290
|
|
|
@@ -974,7 +1161,12 @@ Native module names also changed:
|
|
|
974
1161
|
- iOS pod: `RetaiLensCaptureSDK` → `RNImageStitcher`
|
|
975
1162
|
- iOS xcframework: shipped as `opencv2.xcframework` (linked from `RNImageStitcher.podspec`)
|
|
976
1163
|
|
|
977
|
-
[Unreleased]: https://github.com/bhargavkanda/react-native-image-stitcher/compare/v0.
|
|
1164
|
+
[Unreleased]: https://github.com/bhargavkanda/react-native-image-stitcher/compare/v0.6.0...HEAD
|
|
1165
|
+
[0.6.0]: https://github.com/bhargavkanda/react-native-image-stitcher/compare/v0.5.1...v0.6.0
|
|
1166
|
+
[0.5.1]: https://github.com/bhargavkanda/react-native-image-stitcher/compare/v0.5.0...v0.5.1
|
|
1167
|
+
[0.5.0]: https://github.com/bhargavkanda/react-native-image-stitcher/compare/v0.4.1...v0.5.0
|
|
1168
|
+
[0.4.1]: https://github.com/bhargavkanda/react-native-image-stitcher/compare/v0.4.0...v0.4.1
|
|
1169
|
+
[0.4.0]: https://github.com/bhargavkanda/react-native-image-stitcher/compare/v0.3.0...v0.4.0
|
|
978
1170
|
[0.3.0]: https://github.com/bhargavkanda/react-native-image-stitcher/compare/v0.2.1...v0.3.0
|
|
979
1171
|
[0.2.1]: https://github.com/bhargavkanda/react-native-image-stitcher/compare/v0.2.0...v0.2.1
|
|
980
1172
|
[0.2.0]: https://github.com/bhargavkanda/react-native-image-stitcher/compare/v0.1.3...v0.2.0
|
|
@@ -29,8 +29,8 @@ import com.mrousavy.camera.frameprocessors.VisionCameraProxy
|
|
|
29
29
|
* 3. Call `IncrementalStitcher.consumeFrameFromPlugin(image, …)`
|
|
30
30
|
* which:
|
|
31
31
|
* - Drops the call if `frameSourceMode != "frameProcessor"`
|
|
32
|
-
* (prevents double-feeding the engine alongside the
|
|
33
|
-
* `
|
|
32
|
+
* (prevents double-feeding the engine alongside the
|
|
33
|
+
* AR-mode `ingestFromARCameraView` path).
|
|
34
34
|
* - Otherwise: extracts the Y plane, evaluates the keyframe
|
|
35
35
|
* gate via `KeyframeGate.evaluateWithFrame`, encodes the
|
|
36
36
|
* accepted frame to JPEG synchronously, and hands the path
|
|
@@ -197,7 +197,127 @@ internal class IncrementalFirstwinsEngine(
|
|
|
197
197
|
}
|
|
198
198
|
val frameBGR = downsampleToCompose(srcRaw)
|
|
199
199
|
if (frameBGR !== srcRaw) srcRaw.release()
|
|
200
|
+
return addFrameMat(
|
|
201
|
+
frameBGR,
|
|
202
|
+
qx, qy, qz, qw,
|
|
203
|
+
fx, fy, cx, cy,
|
|
204
|
+
imageWidth, imageHeight,
|
|
205
|
+
yaw, pitch,
|
|
206
|
+
fovHorizDegrees, fovVertDegrees,
|
|
207
|
+
t0,
|
|
208
|
+
)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* F8.6 — pixel-data twin of [addFrameAtPath]. Accepts the
|
|
213
|
+
* camera frame as an NV21 byte buffer instead of a JPEG file
|
|
214
|
+
* path; skips the JPEG decode round-trip (~30–50 ms per
|
|
215
|
+
* accepted frame on a mid-tier device).
|
|
216
|
+
*
|
|
217
|
+
* Use this from the vision-camera Frame Processor path (where
|
|
218
|
+
* we already have direct producer-thread access to YUV bytes)
|
|
219
|
+
* and from the ARCore path (where the previous
|
|
220
|
+
* JPEG-encode-on-every-frame in `RNSARCameraView` was a
|
|
221
|
+
* measurable hot-spot).
|
|
222
|
+
*
|
|
223
|
+
* The body matches [addFrameAtPath] one-for-one except for the
|
|
224
|
+
* Mat construction: an `Mat(h*3/2, w, CV_8UC1)` wraps the
|
|
225
|
+
* NV21 bytes, then `Imgproc.cvtColor` produces the BGR Mat the
|
|
226
|
+
* engine pipeline already expects. Everything downstream is
|
|
227
|
+
* the shared [addFrameMat] helper.
|
|
228
|
+
*
|
|
229
|
+
* `nv21Width`/`nv21Height` describe the buffer's actual
|
|
230
|
+
* dimensions. `imageWidth`/`imageHeight` describe the
|
|
231
|
+
* camera's reported sensor dims used for intrinsics scaling —
|
|
232
|
+
* these can differ when the camera is downsampling for Frame
|
|
233
|
+
* Processor output.
|
|
234
|
+
*/
|
|
235
|
+
fun addFramePixelData(
|
|
236
|
+
nv21: ByteArray,
|
|
237
|
+
nv21Width: Int,
|
|
238
|
+
nv21Height: Int,
|
|
239
|
+
qx: Double, qy: Double, qz: Double, qw: Double,
|
|
240
|
+
fx: Double, fy: Double, cx: Double, cy: Double,
|
|
241
|
+
imageWidth: Int, imageHeight: Int,
|
|
242
|
+
yaw: Double, pitch: Double,
|
|
243
|
+
fovHorizDegrees: Double, fovVertDegrees: Double,
|
|
244
|
+
trackingPoor: Boolean,
|
|
245
|
+
): FrameTelemetry {
|
|
246
|
+
val t0 = System.nanoTime()
|
|
247
|
+
if (trackingPoor) {
|
|
248
|
+
return FrameTelemetry(
|
|
249
|
+
FrameOutcome.SkippedTrackingPoor, -1.0, 0, yaw, pitch,
|
|
250
|
+
msSince(t0),
|
|
251
|
+
isLandscape = isLandscape,
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
// NV21 layout: Y plane (w*h bytes) + interleaved VU
|
|
255
|
+
// (w*h/2 bytes). A single CV_8UC1 Mat of height h*3/2
|
|
256
|
+
// packs the whole thing; cvtColor with COLOR_YUV2BGR_NV21
|
|
257
|
+
// does the planar-aware decode in one call.
|
|
258
|
+
//
|
|
259
|
+
// F8.6 IS-1 — length guard. If the caller supplied a
|
|
260
|
+
// short buffer, `yuv.put(0,0,nv21)` would copy only
|
|
261
|
+
// `nv21.size` bytes and leave the rest zero-init; cvtColor
|
|
262
|
+
// would then read stale/zero UV and produce silently
|
|
263
|
+
// corrupt colour. Fail fast instead.
|
|
264
|
+
val expectedBytes = nv21Width * nv21Height * 3 / 2
|
|
265
|
+
require(nv21.size >= expectedBytes) {
|
|
266
|
+
"addFramePixelData: nv21 buffer too small " +
|
|
267
|
+
"(${nv21.size} bytes < $expectedBytes for " +
|
|
268
|
+
"${nv21Width}x${nv21Height})"
|
|
269
|
+
}
|
|
270
|
+
val yuv = Mat(nv21Height + nv21Height / 2, nv21Width, CvType.CV_8UC1)
|
|
271
|
+
yuv.put(0, 0, nv21)
|
|
272
|
+
val srcRaw = Mat()
|
|
273
|
+
Imgproc.cvtColor(yuv, srcRaw, Imgproc.COLOR_YUV2BGR_NV21)
|
|
274
|
+
yuv.release()
|
|
275
|
+
if (srcRaw.empty()) {
|
|
276
|
+
return FrameTelemetry(
|
|
277
|
+
FrameOutcome.RejectedAlignmentLost, -1.0, 0, yaw, pitch,
|
|
278
|
+
msSince(t0),
|
|
279
|
+
isLandscape = isLandscape,
|
|
280
|
+
)
|
|
281
|
+
}
|
|
282
|
+
val frameBGR = downsampleToCompose(srcRaw)
|
|
283
|
+
if (frameBGR !== srcRaw) srcRaw.release()
|
|
284
|
+
return addFrameMat(
|
|
285
|
+
frameBGR,
|
|
286
|
+
qx, qy, qz, qw,
|
|
287
|
+
fx, fy, cx, cy,
|
|
288
|
+
imageWidth, imageHeight,
|
|
289
|
+
yaw, pitch,
|
|
290
|
+
fovHorizDegrees, fovVertDegrees,
|
|
291
|
+
t0,
|
|
292
|
+
)
|
|
293
|
+
}
|
|
200
294
|
|
|
295
|
+
/**
|
|
296
|
+
* F8.6 — the body extracted from [addFrameAtPath]. Takes a
|
|
297
|
+
* BGR `Mat` (downsampled to compose dims) and runs the full
|
|
298
|
+
* engine pipeline: first-frame init or subsequent-frame paste
|
|
299
|
+
* via either rectilinear or cylindrical warp.
|
|
300
|
+
*
|
|
301
|
+
* Behaviour is identical to the pre-F8.6 `addFrameAtPath`
|
|
302
|
+
* (the body is a verbatim move). Both `addFrameAtPath` and
|
|
303
|
+
* `addFramePixelData` delegate here after their respective
|
|
304
|
+
* Mat constructions.
|
|
305
|
+
*/
|
|
306
|
+
private fun addFrameMat(
|
|
307
|
+
frameBGR: Mat,
|
|
308
|
+
qx: Double, qy: Double, qz: Double, qw: Double,
|
|
309
|
+
fx: Double, fy: Double, cx: Double, cy: Double,
|
|
310
|
+
imageWidth: Int, imageHeight: Int,
|
|
311
|
+
yaw: Double, pitch: Double,
|
|
312
|
+
// FOV params kept for symmetry with `IncrementalEngine.addFrameMat`
|
|
313
|
+
// (the hybrid engine, which uses them in `computeOverlapPct`).
|
|
314
|
+
// The firstwins/slit-scan engine here doesn't consume them —
|
|
315
|
+
// its paste decision is driven by pose-projected pixel
|
|
316
|
+
// displacement, not FoV-overlap percent.
|
|
317
|
+
@Suppress("UNUSED_PARAMETER") fovHorizDegrees: Double,
|
|
318
|
+
@Suppress("UNUSED_PARAMETER") fovVertDegrees: Double,
|
|
319
|
+
t0: Long,
|
|
320
|
+
): FrameTelemetry {
|
|
201
321
|
val rNew = quaternionToRotationMat(qx, qy, qz, qw)
|
|
202
322
|
|
|
203
323
|
if (!hasFirstFrame) {
|