react-native-image-stitcher 0.1.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 +96 -0
- package/LICENSE +201 -0
- package/NOTICE +21 -0
- package/README.md +189 -0
- package/RNImageStitcher.podspec +76 -0
- package/android/build.gradle +224 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/cpp/CMakeLists.txt +124 -0
- package/android/src/main/cpp/image_stitcher_jni.cpp +145 -0
- package/android/src/main/cpp/keyframe_gate_jni.cpp +204 -0
- package/android/src/main/java/io/imagestitcher/rn/BatchStitcher.kt +426 -0
- package/android/src/main/java/io/imagestitcher/rn/IncrementalFirstwinsEngine.kt +960 -0
- package/android/src/main/java/io/imagestitcher/rn/IncrementalStitcher.kt +2371 -0
- package/android/src/main/java/io/imagestitcher/rn/KeyframeGate.kt +256 -0
- package/android/src/main/java/io/imagestitcher/rn/QualityChecker.kt +167 -0
- package/android/src/main/java/io/imagestitcher/rn/RNImageStitcherPackage.kt +39 -0
- package/android/src/main/java/io/imagestitcher/rn/RNSARCameraView.kt +558 -0
- package/android/src/main/java/io/imagestitcher/rn/RNSARCameraViewManager.kt +35 -0
- package/android/src/main/java/io/imagestitcher/rn/RNSARSession.kt +784 -0
- package/android/src/main/java/io/imagestitcher/rn/ar/BackgroundRenderer.kt +176 -0
- package/android/src/main/java/io/imagestitcher/rn/ar/ShaderUtil.kt +67 -0
- package/android/src/main/java/io/imagestitcher/rn/ar/YuvImageConverter.kt +201 -0
- package/cpp/ar_frame_pose.h +63 -0
- package/cpp/keyframe_gate.cpp +927 -0
- package/cpp/keyframe_gate.hpp +240 -0
- package/cpp/stitcher.cpp +2207 -0
- package/cpp/stitcher.hpp +275 -0
- package/dist/ar/useARSession.d.ts +102 -0
- package/dist/ar/useARSession.js +133 -0
- package/dist/camera/ARCameraView.d.ts +93 -0
- package/dist/camera/ARCameraView.js +170 -0
- package/dist/camera/Camera.d.ts +134 -0
- package/dist/camera/Camera.js +688 -0
- package/dist/camera/CameraShutter.d.ts +80 -0
- package/dist/camera/CameraShutter.js +237 -0
- package/dist/camera/CameraView.d.ts +65 -0
- package/dist/camera/CameraView.js +117 -0
- package/dist/camera/CaptureControlsBar.d.ts +87 -0
- package/dist/camera/CaptureControlsBar.js +82 -0
- package/dist/camera/CaptureHeader.d.ts +62 -0
- package/dist/camera/CaptureHeader.js +81 -0
- package/dist/camera/CapturePreview.d.ts +70 -0
- package/dist/camera/CapturePreview.js +188 -0
- package/dist/camera/CaptureStatusOverlay.d.ts +75 -0
- package/dist/camera/CaptureStatusOverlay.js +326 -0
- package/dist/camera/CaptureThumbnailStrip.d.ts +87 -0
- package/dist/camera/CaptureThumbnailStrip.js +177 -0
- package/dist/camera/IncrementalPanGuide.d.ts +83 -0
- package/dist/camera/IncrementalPanGuide.js +267 -0
- package/dist/camera/PanoramaBandOverlay.d.ts +107 -0
- package/dist/camera/PanoramaBandOverlay.js +399 -0
- package/dist/camera/PanoramaConfirmModal.d.ts +57 -0
- package/dist/camera/PanoramaConfirmModal.js +128 -0
- package/dist/camera/PanoramaGuidance.d.ts +79 -0
- package/dist/camera/PanoramaGuidance.js +246 -0
- package/dist/camera/PanoramaSettingsModal.d.ts +311 -0
- package/dist/camera/PanoramaSettingsModal.js +611 -0
- package/dist/camera/ViewportCropOverlay.d.ts +46 -0
- package/dist/camera/ViewportCropOverlay.js +67 -0
- package/dist/camera/useCapture.d.ts +111 -0
- package/dist/camera/useCapture.js +160 -0
- package/dist/camera/useDeviceOrientation.d.ts +48 -0
- package/dist/camera/useDeviceOrientation.js +131 -0
- package/dist/camera/useVideoCapture.d.ts +79 -0
- package/dist/camera/useVideoCapture.js +151 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +39 -0
- package/dist/quality/normaliseOrientation.d.ts +36 -0
- package/dist/quality/normaliseOrientation.js +62 -0
- package/dist/quality/runQualityCheck.d.ts +41 -0
- package/dist/quality/runQualityCheck.js +98 -0
- package/dist/sensors/useIMUTranslationGate.d.ts +70 -0
- package/dist/sensors/useIMUTranslationGate.js +235 -0
- package/dist/stitching/IncrementalStitcherView.d.ts +41 -0
- package/dist/stitching/IncrementalStitcherView.js +157 -0
- package/dist/stitching/incremental.d.ts +930 -0
- package/dist/stitching/incremental.js +133 -0
- package/dist/stitching/stitchFrames.d.ts +55 -0
- package/dist/stitching/stitchFrames.js +56 -0
- package/dist/stitching/stitchVideo.d.ts +119 -0
- package/dist/stitching/stitchVideo.js +57 -0
- package/dist/stitching/useIncrementalJSDriver.d.ts +74 -0
- package/dist/stitching/useIncrementalJSDriver.js +199 -0
- package/dist/stitching/useIncrementalStitcher.d.ts +58 -0
- package/dist/stitching/useIncrementalStitcher.js +172 -0
- package/dist/types.d.ts +58 -0
- package/dist/types.js +15 -0
- package/ios/Package.swift +72 -0
- package/ios/Sources/RNImageStitcher/ARCameraViewManager.m +33 -0
- package/ios/Sources/RNImageStitcher/ARCameraViewManager.swift +40 -0
- package/ios/Sources/RNImageStitcher/ARSessionBridge.m +55 -0
- package/ios/Sources/RNImageStitcher/ARSessionBridge.swift +149 -0
- package/ios/Sources/RNImageStitcher/IncrementalStitcher.swift +2727 -0
- package/ios/Sources/RNImageStitcher/IncrementalStitcherBridge.m +85 -0
- package/ios/Sources/RNImageStitcher/IncrementalStitcherBridge.swift +625 -0
- package/ios/Sources/RNImageStitcher/KeyframeGate.swift +328 -0
- package/ios/Sources/RNImageStitcher/KeyframeGateBridge.h +141 -0
- package/ios/Sources/RNImageStitcher/KeyframeGateBridge.mm +278 -0
- package/ios/Sources/RNImageStitcher/OpenCVIncrementalStitcher.h +473 -0
- package/ios/Sources/RNImageStitcher/OpenCVIncrementalStitcher.mm +1326 -0
- package/ios/Sources/RNImageStitcher/OpenCVKeyframeCollector.h +97 -0
- package/ios/Sources/RNImageStitcher/OpenCVKeyframeCollector.mm +296 -0
- package/ios/Sources/RNImageStitcher/OpenCVSlitScanStitcher.h +103 -0
- package/ios/Sources/RNImageStitcher/OpenCVSlitScanStitcher.mm +3285 -0
- package/ios/Sources/RNImageStitcher/OpenCVStitcher.h +238 -0
- package/ios/Sources/RNImageStitcher/OpenCVStitcher.mm +1880 -0
- package/ios/Sources/RNImageStitcher/QualityChecker.swift +252 -0
- package/ios/Sources/RNImageStitcher/QualityCheckerBridge.m +26 -0
- package/ios/Sources/RNImageStitcher/QualityCheckerBridge.swift +72 -0
- package/ios/Sources/RNImageStitcher/RNSARCameraView.swift +114 -0
- package/ios/Sources/RNImageStitcher/RNSARSession.swift +1111 -0
- package/ios/Sources/RNImageStitcher/Stitcher.swift +243 -0
- package/ios/Sources/RNImageStitcher/StitcherBridge.m +28 -0
- package/ios/Sources/RNImageStitcher/StitcherBridge.swift +246 -0
- package/package.json +73 -0
- package/react-native.config.js +34 -0
- package/scripts/opencv-version.txt +1 -0
- package/scripts/postinstall-fetch-binaries.js +286 -0
- package/src/ar/useARSession.ts +210 -0
- package/src/camera/.gitkeep +0 -0
- package/src/camera/ARCameraView.tsx +256 -0
- package/src/camera/Camera.tsx +1053 -0
- package/src/camera/CameraShutter.tsx +292 -0
- package/src/camera/CameraView.tsx +157 -0
- package/src/camera/CaptureControlsBar.tsx +204 -0
- package/src/camera/CaptureHeader.tsx +184 -0
- package/src/camera/CapturePreview.tsx +318 -0
- package/src/camera/CaptureStatusOverlay.tsx +391 -0
- package/src/camera/CaptureThumbnailStrip.tsx +277 -0
- package/src/camera/IncrementalPanGuide.tsx +328 -0
- package/src/camera/PanoramaBandOverlay.tsx +498 -0
- package/src/camera/PanoramaConfirmModal.tsx +206 -0
- package/src/camera/PanoramaGuidance.tsx +327 -0
- package/src/camera/PanoramaSettingsModal.tsx +1357 -0
- package/src/camera/ViewportCropOverlay.tsx +81 -0
- package/src/camera/useCapture.ts +279 -0
- package/src/camera/useDeviceOrientation.ts +140 -0
- package/src/camera/useVideoCapture.ts +236 -0
- package/src/index.ts +53 -0
- package/src/quality/.gitkeep +0 -0
- package/src/quality/normaliseOrientation.ts +79 -0
- package/src/quality/runQualityCheck.ts +131 -0
- package/src/sensors/useIMUTranslationGate.ts +347 -0
- package/src/stitching/.gitkeep +0 -0
- package/src/stitching/IncrementalStitcherView.tsx +198 -0
- package/src/stitching/incremental.ts +1021 -0
- package/src/stitching/stitchFrames.ts +88 -0
- package/src/stitching/stitchVideo.ts +153 -0
- package/src/stitching/useIncrementalJSDriver.ts +273 -0
- package/src/stitching/useIncrementalStitcher.ts +252 -0
- package/src/types.ts +78 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
/**
|
|
3
|
+
* ARCameraView — AR-backed alternative to ``<CameraView>`` for
|
|
4
|
+
* audits that need pose-aware capture (panorama mode, packet
|
|
5
|
+
* detection). Renders the ARKit camera feed via the native
|
|
6
|
+
* `RNSARCameraView` UIView; the underlying ARSession is the
|
|
7
|
+
* SDK singleton (`RNSARSession.shared`), shared between the
|
|
8
|
+
* preview and the pose log that feeds Phase 5 stitching + Phase 6
|
|
9
|
+
* measurement.
|
|
10
|
+
*
|
|
11
|
+
* Why a separate component (vs. a polymorphic CameraView)?
|
|
12
|
+
* 1. **Different imperative API.** The vision-camera-backed
|
|
13
|
+
* CameraView exposes `takePhoto / startRecording` via its ref
|
|
14
|
+
* (Phase 5 will add equivalents to this component, but they
|
|
15
|
+
* route through ARFrame.capturedImage + AVAssetWriter rather
|
|
16
|
+
* than vision-camera's APIs).
|
|
17
|
+
* 2. **Camera-access conflict.** ARKit and AVCaptureSession
|
|
18
|
+
* can't share the camera. Forcing the host to pick one
|
|
19
|
+
* component over the other (instead of toggling a prop on a
|
|
20
|
+
* shared component) makes the conflict impossible to misuse —
|
|
21
|
+
* you can't accidentally mount both at the same time.
|
|
22
|
+
* 3. **Lifecycle clarity.** The native side starts the AR
|
|
23
|
+
* session in `didMoveToWindow`. Mount = start, unmount =
|
|
24
|
+
* stop. No flag-twiddling.
|
|
25
|
+
*
|
|
26
|
+
* This component is preview-only in Phase 4.4. Photo + video
|
|
27
|
+
* capture come in Phase 5 (Step 5 of the AR design plan). Until
|
|
28
|
+
* then, the host's panorama capture flow continues to use
|
|
29
|
+
* vision-camera; ARCameraView is opt-in via a settings flag for
|
|
30
|
+
* developer verification.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import React, { forwardRef, useImperativeHandle, useRef } from 'react';
|
|
34
|
+
import {
|
|
35
|
+
NativeModules,
|
|
36
|
+
Platform,
|
|
37
|
+
StyleSheet,
|
|
38
|
+
Text,
|
|
39
|
+
View,
|
|
40
|
+
requireNativeComponent,
|
|
41
|
+
type ViewStyle,
|
|
42
|
+
} from 'react-native';
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
// React Native looks up the component by its NATIVE name.
|
|
46
|
+
// iOS: comes from `ARCameraViewManager.m`'s
|
|
47
|
+
// `RCT_EXTERN_MODULE(RNSARCameraViewManager, RCTViewManager)`.
|
|
48
|
+
// Android: comes from `RNSARCameraViewManager.kt`'s
|
|
49
|
+
// `getName() = "RNSARCameraView"`.
|
|
50
|
+
// Both expose the same name; same JS lookup works on both platforms.
|
|
51
|
+
const NativeARCameraView =
|
|
52
|
+
Platform.OS === 'ios' || Platform.OS === 'android'
|
|
53
|
+
? requireNativeComponent<{ style?: ViewStyle }>('RNSARCameraView')
|
|
54
|
+
: null;
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
export interface ARCameraViewProps {
|
|
58
|
+
/** Layout style, typically `StyleSheet.absoluteFill` or `flex: 1`. */
|
|
59
|
+
style?: ViewStyle;
|
|
60
|
+
/**
|
|
61
|
+
* Optional themed guidance banner shown over the preview at the
|
|
62
|
+
* top, mirrors the `<CameraView>` prop so host apps can swap
|
|
63
|
+
* components without rewriting their guidance text plumbing.
|
|
64
|
+
*/
|
|
65
|
+
guidance?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Imperative handle exposed via the ref — shape mirrors the subset
|
|
71
|
+
* of vision-camera's `Camera` ref methods that the host's
|
|
72
|
+
* `useCapture` / `useVideoCapture` hooks call. Hosts can pass the
|
|
73
|
+
* SAME ref to those hooks as they do for the vision-camera path,
|
|
74
|
+
* with no branching required.
|
|
75
|
+
*
|
|
76
|
+
* Note we do NOT exhaustively mirror vision-camera's API surface —
|
|
77
|
+
* only the methods the panorama capture flow uses today. As the
|
|
78
|
+
* SDK grows AR-aware features, methods are added here.
|
|
79
|
+
*/
|
|
80
|
+
export interface ARCameraViewHandle {
|
|
81
|
+
/**
|
|
82
|
+
* Capture the latest ARFrame as a JPEG. Resolves with a
|
|
83
|
+
* vision-camera-compatible PhotoFile (`{ path, width, height,
|
|
84
|
+
* isMirrored, isRawPhoto }`). Native generates a temp path —
|
|
85
|
+
* caller does NOT need to construct one.
|
|
86
|
+
*/
|
|
87
|
+
takePhoto: (options?: { quality?: number }) => Promise<{
|
|
88
|
+
path: string;
|
|
89
|
+
width: number;
|
|
90
|
+
height: number;
|
|
91
|
+
isMirrored: boolean;
|
|
92
|
+
isRawPhoto: boolean;
|
|
93
|
+
}>;
|
|
94
|
+
/**
|
|
95
|
+
* Begin recording AR frames into an mp4. Mirrors vision-camera's
|
|
96
|
+
* callback-based API: takes `onRecordingFinished` /
|
|
97
|
+
* `onRecordingError` handlers; the actual VideoFile is delivered
|
|
98
|
+
* via `onRecordingFinished` AFTER the host calls `stopRecording`.
|
|
99
|
+
*
|
|
100
|
+
* Synchronous return (void) — useVideoCapture wraps it in a
|
|
101
|
+
* Promise on top of the callbacks.
|
|
102
|
+
*/
|
|
103
|
+
startRecording: (options: {
|
|
104
|
+
onRecordingFinished?: (video: {
|
|
105
|
+
path: string;
|
|
106
|
+
duration: number;
|
|
107
|
+
size: number;
|
|
108
|
+
width: number;
|
|
109
|
+
height: number;
|
|
110
|
+
}) => void;
|
|
111
|
+
onRecordingError?: (err: Error) => void;
|
|
112
|
+
}) => void;
|
|
113
|
+
/** Finalise the in-progress recording. */
|
|
114
|
+
stopRecording: () => Promise<void>;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
type RecordingCallbacks = {
|
|
119
|
+
onRecordingFinished?: (video: {
|
|
120
|
+
path: string;
|
|
121
|
+
duration: number;
|
|
122
|
+
size: number;
|
|
123
|
+
width: number;
|
|
124
|
+
height: number;
|
|
125
|
+
}) => void;
|
|
126
|
+
onRecordingError?: (err: Error) => void;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
export const ARCameraView = forwardRef<ARCameraViewHandle, ARCameraViewProps>(
|
|
131
|
+
function ARCameraView(
|
|
132
|
+
{ style, guidance },
|
|
133
|
+
ref,
|
|
134
|
+
): React.JSX.Element {
|
|
135
|
+
// Held across the start→stop lifecycle so stopRecording's
|
|
136
|
+
// resolved VideoFile can be delivered via the same callback
|
|
137
|
+
// pair vision-camera uses.
|
|
138
|
+
const recordingCallbacksRef = useRef<RecordingCallbacks | null>(null);
|
|
139
|
+
|
|
140
|
+
useImperativeHandle(ref, () => ({
|
|
141
|
+
takePhoto: async (options = {}) => {
|
|
142
|
+
const native: any =
|
|
143
|
+
(NativeModules as Record<string, unknown>).RNSARSession;
|
|
144
|
+
if (!native?.takePhoto) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
'ARCameraView.takePhoto: native RNSARSession module not registered',
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
return native.takePhoto({
|
|
150
|
+
path: '',
|
|
151
|
+
quality: options.quality ?? 90,
|
|
152
|
+
});
|
|
153
|
+
},
|
|
154
|
+
startRecording: (options) => {
|
|
155
|
+
const native: any =
|
|
156
|
+
(NativeModules as Record<string, unknown>).RNSARSession;
|
|
157
|
+
if (!native?.startRecording) {
|
|
158
|
+
options.onRecordingError?.(new Error(
|
|
159
|
+
'ARCameraView.startRecording: native RNSARSession module not registered',
|
|
160
|
+
));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (recordingCallbacksRef.current !== null) {
|
|
164
|
+
options.onRecordingError?.(new Error(
|
|
165
|
+
'ARCameraView.startRecording: a recording is already in progress',
|
|
166
|
+
));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
recordingCallbacksRef.current = options;
|
|
170
|
+
native.startRecording({ path: '' })
|
|
171
|
+
.catch((err: Error) => {
|
|
172
|
+
recordingCallbacksRef.current = null;
|
|
173
|
+
options.onRecordingError?.(err);
|
|
174
|
+
});
|
|
175
|
+
},
|
|
176
|
+
stopRecording: async () => {
|
|
177
|
+
const native: any =
|
|
178
|
+
(NativeModules as Record<string, unknown>).RNSARSession;
|
|
179
|
+
const callbacks = recordingCallbacksRef.current;
|
|
180
|
+
recordingCallbacksRef.current = null;
|
|
181
|
+
if (!native?.stopRecording || !callbacks) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
try {
|
|
185
|
+
const video = await native.stopRecording();
|
|
186
|
+
callbacks.onRecordingFinished?.(video);
|
|
187
|
+
} catch (err) {
|
|
188
|
+
callbacks.onRecordingError?.(err as Error);
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
}), []);
|
|
192
|
+
|
|
193
|
+
if (!NativeARCameraView
|
|
194
|
+
|| (Platform.OS !== 'ios' && Platform.OS !== 'android')) {
|
|
195
|
+
// Web / unsupported platforms get a clear "not available here"
|
|
196
|
+
// placeholder instead of a silent black rectangle. iOS +
|
|
197
|
+
// Android both ship the native component now.
|
|
198
|
+
return (
|
|
199
|
+
<View style={[styles.placeholder, style]} accessibilityLabel="AR camera unavailable">
|
|
200
|
+
<Text style={styles.placeholderText}>
|
|
201
|
+
AR camera is not available on this platform.
|
|
202
|
+
</Text>
|
|
203
|
+
</View>
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
return (
|
|
208
|
+
<View style={[styles.root, style]}>
|
|
209
|
+
<NativeARCameraView style={StyleSheet.absoluteFill} />
|
|
210
|
+
{guidance ? (
|
|
211
|
+
<View
|
|
212
|
+
style={styles.guidance}
|
|
213
|
+
pointerEvents="none"
|
|
214
|
+
accessible
|
|
215
|
+
accessibilityRole="text"
|
|
216
|
+
>
|
|
217
|
+
<Text style={styles.guidanceText} numberOfLines={2}>
|
|
218
|
+
{guidance}
|
|
219
|
+
</Text>
|
|
220
|
+
</View>
|
|
221
|
+
) : null}
|
|
222
|
+
</View>
|
|
223
|
+
);
|
|
224
|
+
},
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
const styles = StyleSheet.create({
|
|
229
|
+
root: {
|
|
230
|
+
flex: 1,
|
|
231
|
+
overflow: 'hidden',
|
|
232
|
+
},
|
|
233
|
+
placeholder: {
|
|
234
|
+
flex: 1,
|
|
235
|
+
alignItems: 'center',
|
|
236
|
+
justifyContent: 'center',
|
|
237
|
+
backgroundColor: '#000',
|
|
238
|
+
},
|
|
239
|
+
placeholderText: {
|
|
240
|
+
color: '#ffffff',
|
|
241
|
+
fontSize: 14,
|
|
242
|
+
},
|
|
243
|
+
guidance: {
|
|
244
|
+
position: 'absolute',
|
|
245
|
+
top: 0,
|
|
246
|
+
left: 0,
|
|
247
|
+
right: 0,
|
|
248
|
+
paddingHorizontal: 16,
|
|
249
|
+
paddingVertical: 10,
|
|
250
|
+
backgroundColor: 'rgba(0, 0, 0, 0.55)',
|
|
251
|
+
},
|
|
252
|
+
guidanceText: {
|
|
253
|
+
color: '#ffffff',
|
|
254
|
+
fontSize: 13,
|
|
255
|
+
},
|
|
256
|
+
});
|