react-native-image-stitcher 0.2.1 → 0.4.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 +511 -1
- package/README.md +1 -1
- package/android/src/main/cpp/keyframe_gate_jni.cpp +138 -0
- package/android/src/main/java/io/imagestitcher/rn/IncrementalStitcher.kt +412 -40
- package/android/src/main/java/io/imagestitcher/rn/KeyframeGate.kt +128 -0
- package/android/src/main/java/io/imagestitcher/rn/RNSARCameraView.kt +87 -45
- package/android/src/main/java/io/imagestitcher/rn/RNSARSession.kt +46 -4
- package/cpp/stitcher.cpp +101 -1
- package/cpp/stitcher.hpp +8 -0
- package/dist/camera/Camera.d.ts +9 -0
- package/dist/camera/Camera.js +165 -43
- package/dist/camera/CaptureDebugOverlay.d.ts +45 -0
- package/dist/camera/CaptureDebugOverlay.js +146 -0
- package/dist/camera/CaptureKeyframePill.d.ts +28 -0
- package/dist/camera/CaptureKeyframePill.js +60 -0
- package/dist/camera/CaptureMemoryPill.d.ts +28 -0
- package/dist/camera/CaptureMemoryPill.js +109 -0
- package/dist/camera/CaptureOrientationPill.d.ts +22 -0
- package/dist/camera/CaptureOrientationPill.js +44 -0
- package/dist/camera/CaptureStitchStatsToast.d.ts +45 -0
- package/dist/camera/CaptureStitchStatsToast.js +133 -0
- package/dist/camera/PanoramaSettings.d.ts +478 -0
- package/dist/camera/PanoramaSettings.js +120 -0
- package/dist/camera/PanoramaSettingsBridge.d.ts +84 -0
- package/dist/camera/PanoramaSettingsBridge.js +208 -0
- package/dist/camera/PanoramaSettingsModal.d.ts +50 -298
- package/dist/camera/PanoramaSettingsModal.js +189 -354
- package/dist/camera/buildPanoramaInitialSettings.d.ts +70 -0
- package/dist/camera/buildPanoramaInitialSettings.js +97 -0
- package/dist/camera/lowMemDevice.d.ts +24 -0
- package/dist/camera/lowMemDevice.js +69 -0
- package/dist/index.d.ts +16 -2
- package/dist/index.js +37 -2
- package/dist/sensors/useIMUTranslationGate.d.ts +26 -0
- package/dist/sensors/useIMUTranslationGate.js +83 -1
- package/dist/stitching/incremental.d.ts +25 -0
- package/dist/stitching/useIncrementalStitcher.d.ts +12 -1
- package/dist/stitching/useIncrementalStitcher.js +7 -1
- package/ios/Sources/RNImageStitcher/IncrementalStitcher.swift +321 -7
- package/ios/Sources/RNImageStitcher/IncrementalStitcherBridge.swift +8 -0
- package/ios/Sources/RNImageStitcher/KeyframeGate.swift +12 -0
- package/ios/Sources/RNImageStitcher/KeyframeGateBridge.h +13 -0
- package/ios/Sources/RNImageStitcher/KeyframeGateBridge.mm +15 -0
- package/ios/Sources/RNImageStitcher/OpenCVStitcher.h +1 -0
- package/ios/Sources/RNImageStitcher/OpenCVStitcher.mm +17 -4
- package/ios/Sources/RNImageStitcher/Stitcher.swift +6 -1
- package/package.json +6 -2
- package/src/camera/Camera.tsx +220 -54
- package/src/camera/CaptureDebugOverlay.tsx +180 -0
- package/src/camera/CaptureKeyframePill.tsx +77 -0
- package/src/camera/CaptureMemoryPill.tsx +96 -0
- package/src/camera/CaptureOrientationPill.tsx +57 -0
- package/src/camera/CaptureStitchStatsToast.tsx +155 -0
- package/src/camera/PanoramaSettings.ts +605 -0
- package/src/camera/PanoramaSettingsBridge.ts +238 -0
- package/src/camera/PanoramaSettingsModal.tsx +296 -988
- package/src/camera/__tests__/PanoramaSettingsBridge.test.ts +375 -0
- package/src/camera/__tests__/buildPanoramaInitialSettings.test.ts +119 -0
- package/src/camera/__tests__/lowMemDevice.test.ts +52 -0
- package/src/camera/buildPanoramaInitialSettings.ts +139 -0
- package/src/camera/lowMemDevice.ts +71 -0
- package/src/index.ts +61 -3
- package/src/sensors/useIMUTranslationGate.ts +112 -1
- package/src/stitching/incremental.ts +25 -0
- package/src/stitching/useIncrementalStitcher.ts +18 -0
|
@@ -120,6 +120,46 @@ Java_io_imagestitcher_rn_KeyframeGate_nativeSetFlowNoveltyPercentile(
|
|
|
120
120
|
gate(handle)->setFlowNoveltyPercentile(static_cast<double>(percentile));
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
// 2026-05-22 (audit F5) — Android JNI parity for the Shi-Tomasi
|
|
124
|
+
// corner tunables. Pre-audit, iOS bridges these via KeyframeGateBridge
|
|
125
|
+
// but Android had no equivalent — JS Settings sliders for
|
|
126
|
+
// flowMaxCorners / flowQualityLevel / flowMinDistance were no-ops on
|
|
127
|
+
// Android. See setFlowMaxCorners / setFlowQualityLevel /
|
|
128
|
+
// setFlowMinDistance docs in keyframe_gate.hpp.
|
|
129
|
+
JNIEXPORT void JNICALL
|
|
130
|
+
Java_io_imagestitcher_rn_KeyframeGate_nativeSetFlowMaxCorners(
|
|
131
|
+
JNIEnv*, jclass, jlong handle, jint maxCorners)
|
|
132
|
+
{
|
|
133
|
+
gate(handle)->setFlowMaxCorners(static_cast<int32_t>(maxCorners));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
JNIEXPORT void JNICALL
|
|
137
|
+
Java_io_imagestitcher_rn_KeyframeGate_nativeSetFlowQualityLevel(
|
|
138
|
+
JNIEnv*, jclass, jlong handle, jdouble quality)
|
|
139
|
+
{
|
|
140
|
+
gate(handle)->setFlowQualityLevel(static_cast<double>(quality));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
JNIEXPORT void JNICALL
|
|
144
|
+
Java_io_imagestitcher_rn_KeyframeGate_nativeSetFlowMinDistance(
|
|
145
|
+
JNIEnv*, jclass, jlong handle, jdouble minDistance)
|
|
146
|
+
{
|
|
147
|
+
gate(handle)->setFlowMinDistance(static_cast<double>(minDistance));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 2026-05-22 (audit F6) — gate strategy selector. Maps the Kotlin
|
|
151
|
+
// enum's int value back to the C++ GateStrategy enum. Pre-audit
|
|
152
|
+
// Android had no way to flip strategy → was stuck on the C++ default
|
|
153
|
+
// (Pose), making `frameSelectionMode = 'flow-based'` a silent no-op
|
|
154
|
+
// on Android.
|
|
155
|
+
JNIEXPORT void JNICALL
|
|
156
|
+
Java_io_imagestitcher_rn_KeyframeGate_nativeSetStrategy(
|
|
157
|
+
JNIEnv*, jclass, jlong handle, jint strategyInt)
|
|
158
|
+
{
|
|
159
|
+
auto strategy = static_cast<retailens::GateStrategy>(strategyInt);
|
|
160
|
+
gate(handle)->setStrategy(strategy);
|
|
161
|
+
}
|
|
162
|
+
|
|
123
163
|
JNIEXPORT void JNICALL
|
|
124
164
|
Java_io_imagestitcher_rn_KeyframeGate_nativeReset(
|
|
125
165
|
JNIEnv*, jclass, jlong handle)
|
|
@@ -201,4 +241,102 @@ Java_io_imagestitcher_rn_KeyframeGate_nativeEvaluate(
|
|
|
201
241
|
return out;
|
|
202
242
|
}
|
|
203
243
|
|
|
244
|
+
// ── Per-frame evaluate WITH PIXEL DATA ──────────────────────────
|
|
245
|
+
//
|
|
246
|
+
// 2026-05-21 (v0.3) — pixel-aware Flow-strategy entry point. The
|
|
247
|
+
// `nativeEvaluate` above hands the gate pose + plane only, which
|
|
248
|
+
// forces the C++ side to silently fall back from Flow strategy to
|
|
249
|
+
// Pose strategy in cpp/keyframe_gate.cpp's evaluateWithFrame()
|
|
250
|
+
// (defensive fallback at the grayData==nullptr branch). This thunk
|
|
251
|
+
// is the proper Flow-strategy entry point: the caller supplies the
|
|
252
|
+
// frame's grayscale plane (Y plane for YUV camera images, or a
|
|
253
|
+
// JPEG-decode result for the JS-driver path), and the C++ Flow
|
|
254
|
+
// path actually runs feature tracking on it.
|
|
255
|
+
//
|
|
256
|
+
// grayBytes: Java byte[] holding the grayscale plane. Accessed via
|
|
257
|
+
// GetPrimitiveArrayCritical (no copy, pins GC briefly for
|
|
258
|
+
// the duration of the gate.evaluateWithFrame call —
|
|
259
|
+
// evaluation is ~1-5 ms so the pin window is tight).
|
|
260
|
+
// width: grayscale image width in pixels.
|
|
261
|
+
// height: grayscale image height in pixels.
|
|
262
|
+
// stride: bytes per row. May exceed width when the plane has
|
|
263
|
+
// padding (ARCore's Image.Plane.getRowStride() can pad).
|
|
264
|
+
//
|
|
265
|
+
// plane16OrNull: same as nativeEvaluate — column-major 4×4 plane
|
|
266
|
+
// transform, or null for angular-delta fallback.
|
|
267
|
+
//
|
|
268
|
+
// Returns DoubleArray[5] identical to nativeEvaluate.
|
|
269
|
+
JNIEXPORT jdoubleArray JNICALL
|
|
270
|
+
Java_io_imagestitcher_rn_KeyframeGate_nativeEvaluateWithFrame(
|
|
271
|
+
JNIEnv* env, jclass, jlong handle,
|
|
272
|
+
jfloat tx, jfloat ty, jfloat tz,
|
|
273
|
+
jfloat qx, jfloat qy, jfloat qz, jfloat qw,
|
|
274
|
+
jfloat fx, jfloat fy, jfloat cx, jfloat cy,
|
|
275
|
+
jint imageWidth, jint imageHeight,
|
|
276
|
+
jfloatArray plane16OrNull,
|
|
277
|
+
jbyteArray grayBytes,
|
|
278
|
+
jint grayWidth, jint grayHeight, jint grayStride)
|
|
279
|
+
{
|
|
280
|
+
retailens::Pose pose;
|
|
281
|
+
pose.tx = tx; pose.ty = ty; pose.tz = tz;
|
|
282
|
+
pose.qx = qx; pose.qy = qy; pose.qz = qz; pose.qw = qw;
|
|
283
|
+
pose.fx = fx; pose.fy = fy; pose.cx = cx; pose.cy = cy;
|
|
284
|
+
pose.imageWidth = static_cast<int32_t>(imageWidth);
|
|
285
|
+
pose.imageHeight = static_cast<int32_t>(imageHeight);
|
|
286
|
+
|
|
287
|
+
retailens::PlaneTransform planeStorage;
|
|
288
|
+
const retailens::PlaneTransform* planePtr = nullptr;
|
|
289
|
+
if (plane16OrNull) {
|
|
290
|
+
jsize len = env->GetArrayLength(plane16OrNull);
|
|
291
|
+
if (len == 16) {
|
|
292
|
+
jfloat* src = env->GetFloatArrayElements(plane16OrNull, nullptr);
|
|
293
|
+
if (src) {
|
|
294
|
+
std::memcpy(planeStorage.m, src, sizeof(float) * 16);
|
|
295
|
+
env->ReleaseFloatArrayElements(plane16OrNull, src, JNI_ABORT);
|
|
296
|
+
planePtr = &planeStorage;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Pin the byte[] for the duration of the gate evaluate. Use
|
|
302
|
+
// GetPrimitiveArrayCritical (zero-copy, JVM pins the GC) over
|
|
303
|
+
// GetByteArrayElements (may copy on some VMs) because at 30-60
|
|
304
|
+
// Hz of 2 MB Y-planes, the copy cost adds up. Evaluate is
|
|
305
|
+
// ~1-5 ms so the pin window is short. Always paired with
|
|
306
|
+
// ReleasePrimitiveArrayCritical even on the error paths below.
|
|
307
|
+
retailens::KeyframeGateDecision d;
|
|
308
|
+
if (grayBytes && grayWidth > 0 && grayHeight > 0 && grayStride >= grayWidth) {
|
|
309
|
+
void* raw = env->GetPrimitiveArrayCritical(grayBytes, nullptr);
|
|
310
|
+
if (raw) {
|
|
311
|
+
d = gate(handle)->evaluateWithFrame(
|
|
312
|
+
pose, planePtr,
|
|
313
|
+
static_cast<const uint8_t*>(raw),
|
|
314
|
+
static_cast<int32_t>(grayWidth),
|
|
315
|
+
static_cast<int32_t>(grayHeight),
|
|
316
|
+
static_cast<int32_t>(grayStride));
|
|
317
|
+
env->ReleasePrimitiveArrayCritical(grayBytes, raw, JNI_ABORT);
|
|
318
|
+
} else {
|
|
319
|
+
// GetPrimitiveArrayCritical failed (rare, but defensive).
|
|
320
|
+
// Fall back to pose-only path so we degrade gracefully
|
|
321
|
+
// rather than crashing the whole capture pipeline.
|
|
322
|
+
d = gate(handle)->evaluate(pose, planePtr);
|
|
323
|
+
}
|
|
324
|
+
} else {
|
|
325
|
+
// Caller passed null / invalid dims — defensive fall-through
|
|
326
|
+
// to pose-only path (matches the C++ side's own defensive
|
|
327
|
+
// fallback in evaluateWithFrame when grayData == nullptr).
|
|
328
|
+
d = gate(handle)->evaluate(pose, planePtr);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
jdoubleArray out = env->NewDoubleArray(5);
|
|
332
|
+
jdouble values[5];
|
|
333
|
+
values[0] = d.accept ? 1.0 : 0.0;
|
|
334
|
+
values[1] = static_cast<jdouble>(static_cast<int32_t>(d.reason));
|
|
335
|
+
values[2] = d.newContentFraction;
|
|
336
|
+
values[3] = static_cast<jdouble>(d.acceptedCount);
|
|
337
|
+
values[4] = static_cast<jdouble>(d.maxCount);
|
|
338
|
+
env->SetDoubleArrayRegion(out, 0, 5, values);
|
|
339
|
+
return out;
|
|
340
|
+
}
|
|
341
|
+
|
|
204
342
|
} // extern "C"
|