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,238 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
//
|
|
3
|
+
// OpenCVStitcher.h
|
|
4
|
+
//
|
|
5
|
+
// Objective-C interface to the OpenCV stitcher. All C++ types
|
|
6
|
+
// (`cv::Stitcher`, `cv::Mat`, `std::vector`) are confined to the
|
|
7
|
+
// implementation file (`.mm`) so this header can be imported from
|
|
8
|
+
// pure Swift without dragging in the C++ standard library.
|
|
9
|
+
//
|
|
10
|
+
// Why the layered design (ObjC interface ↔ ObjC++ impl ↔ C++ lib)?
|
|
11
|
+
// The Swift importer does not understand C++. Without this layer
|
|
12
|
+
// we'd have to write a much heavier @objc shim using opaque void*
|
|
13
|
+
// pointers. Letting Objective-C own the boundary types gives us
|
|
14
|
+
// automatic memory management for NSString/NSError/NSDictionary
|
|
15
|
+
// and zero copy on the boundary — the .mm only does the C++→C++
|
|
16
|
+
// work, never marshalling.
|
|
17
|
+
//
|
|
18
|
+
|
|
19
|
+
#import <Foundation/Foundation.h>
|
|
20
|
+
|
|
21
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
22
|
+
|
|
23
|
+
/// NSError domain raised by OpenCVStitcher errors. Codes match the
|
|
24
|
+
/// `cv::Stitcher::Status` enum values so callers can branch on
|
|
25
|
+
/// "needs more images" vs. "homography failed".
|
|
26
|
+
extern NSString *const RNImageStitcherErrorDomain;
|
|
27
|
+
|
|
28
|
+
/// Result of a successful stitch — pixel dimensions of the panorama
|
|
29
|
+
/// plus the path it was written to (host app passed it in).
|
|
30
|
+
@interface RNStitchResult : NSObject
|
|
31
|
+
@property (nonatomic, copy, readonly) NSString *outputPath;
|
|
32
|
+
@property (nonatomic, assign, readonly) NSInteger width;
|
|
33
|
+
@property (nonatomic, assign, readonly) NSInteger height;
|
|
34
|
+
@property (nonatomic, assign, readonly) double durationMs;
|
|
35
|
+
/// 2026-05-16 (Issue 5) — C+D progressive-confidence retry telemetry
|
|
36
|
+
/// sourced from `retailens::StitchResult`. Surface in the JS finalize
|
|
37
|
+
/// dict so the host can render a debug toast on retry.
|
|
38
|
+
///
|
|
39
|
+
/// framesRequested: number of keyframes handed to the stitcher
|
|
40
|
+
/// framesIncluded: number retained after leaveBiggestComponent
|
|
41
|
+
/// finalConfidenceThresh: threshold the successful attempt used
|
|
42
|
+
/// (1.0 / 0.5 / 0.3); -1.0 when the
|
|
43
|
+
/// retry path didn't run (rare error paths)
|
|
44
|
+
@property (nonatomic, assign, readonly) NSInteger framesRequested;
|
|
45
|
+
@property (nonatomic, assign, readonly) NSInteger framesIncluded;
|
|
46
|
+
@property (nonatomic, assign, readonly) double finalConfidenceThresh;
|
|
47
|
+
- (instancetype)initWithOutputPath:(NSString *)outputPath
|
|
48
|
+
width:(NSInteger)width
|
|
49
|
+
height:(NSInteger)height
|
|
50
|
+
durationMs:(double)durationMs
|
|
51
|
+
framesRequested:(NSInteger)framesRequested
|
|
52
|
+
framesIncluded:(NSInteger)framesIncluded
|
|
53
|
+
finalConfidenceThresh:(double)finalConfidenceThresh NS_DESIGNATED_INITIALIZER;
|
|
54
|
+
/// Convenience initializer for paths that don't carry C+D retry
|
|
55
|
+
/// telemetry (e.g. stitchVideoAtPath / stitchKeyframePaths). Sets
|
|
56
|
+
/// the telemetry fields to sentinel values (-1) so JS callers can
|
|
57
|
+
/// detect "no retry data available" cleanly.
|
|
58
|
+
- (instancetype)initWithOutputPath:(NSString *)outputPath
|
|
59
|
+
width:(NSInteger)width
|
|
60
|
+
height:(NSInteger)height
|
|
61
|
+
durationMs:(double)durationMs;
|
|
62
|
+
- (instancetype)init NS_UNAVAILABLE;
|
|
63
|
+
@end
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@interface OpenCVStitcher : NSObject
|
|
67
|
+
|
|
68
|
+
/// Stitch the images at `framePaths` into a single panoramic JPEG
|
|
69
|
+
/// at `outputPath`.
|
|
70
|
+
///
|
|
71
|
+
/// `quality`: JPEG quality [0..100]. Caller-clamped; 0 / 101 will
|
|
72
|
+
/// be coerced into range by the impl.
|
|
73
|
+
///
|
|
74
|
+
/// On success returns the result object; on failure populates
|
|
75
|
+
/// `error` (NSError, RNImageStitcherErrorDomain) and returns nil.
|
|
76
|
+
/// `warperType`: one of @"plane" / @"cylindrical" / @"spherical".
|
|
77
|
+
/// Pass nil/empty for the default (@"plane"). Different
|
|
78
|
+
/// projections suit different gestures — see the field A/B
|
|
79
|
+
/// testing settings UI for guidance.
|
|
80
|
+
/// `blenderType`: one of @"multiband" / @"feather". Pass nil for
|
|
81
|
+
/// the default (@"multiband").
|
|
82
|
+
/// `seamFinderType`: one of @"graphcut" / @"skip". Pass nil for
|
|
83
|
+
/// the default (@"graphcut").
|
|
84
|
+
/// - "graphcut" runs cv::detail::GraphCutSeamFinder over all
|
|
85
|
+
/// warped frames before blending — produces clean seams,
|
|
86
|
+
/// pairs well with MultiBandBlender, but holds all warped
|
|
87
|
+
/// frames in memory simultaneously (higher peak).
|
|
88
|
+
/// - "skip" streams warp+feed in a single pass and never holds
|
|
89
|
+
/// more than one warped frame. Lower peak memory. Use on
|
|
90
|
+
/// low-RAM devices or for fastest path with FeatherBlender.
|
|
91
|
+
/// AR-STITCHING-TWO-MODES — see memory/ar-stitching-two-modes.md
|
|
92
|
+
///
|
|
93
|
+
/// - `captureOrientation` ("portrait" | "portrait-upside-down" |
|
|
94
|
+
/// "landscape-left" | "landscape-right"): physical phone hold at
|
|
95
|
+
/// capture start, sourced from the JS-side accelerometer hook.
|
|
96
|
+
/// Drives the OUTPUT panorama's bake-rotation per the two
|
|
97
|
+
/// supported capture modes:
|
|
98
|
+
/// portrait → no bake-rotation
|
|
99
|
+
/// portrait-upside-down → bake ROTATE_180
|
|
100
|
+
/// landscape-left → bake ROTATE_90_COUNTERCLOCKWISE
|
|
101
|
+
/// landscape-right → bake ROTATE_90_CLOCKWISE
|
|
102
|
+
/// Output JPEG is always written with EXIF=1 (no metadata
|
|
103
|
+
/// rotation) since the rotation is baked into the pixels.
|
|
104
|
+
///
|
|
105
|
+
/// - **Maximum-inscribed-rectangle crop** instead of bounding-
|
|
106
|
+
/// rectangle. cv::Stitcher's output has irregular black corners
|
|
107
|
+
/// where the projection didn't fill; bbox crop still included
|
|
108
|
+
/// them. With `useInscribedRectCrop:YES` we find the largest
|
|
109
|
+
/// axis-aligned rectangle entirely inside the non-zero region
|
|
110
|
+
/// and crop to that — clean output with no black corners.
|
|
111
|
+
+ (nullable RNStitchResult *)stitchFramePaths:(NSArray<NSString *> *)framePaths
|
|
112
|
+
outputPath:(NSString *)outputPath
|
|
113
|
+
jpegQuality:(NSInteger)quality
|
|
114
|
+
warperType:(nullable NSString *)warperType
|
|
115
|
+
blenderType:(nullable NSString *)blenderType
|
|
116
|
+
seamFinderType:(nullable NSString *)seamFinderType
|
|
117
|
+
captureOrientation:(nullable NSString *)captureOrientation
|
|
118
|
+
useInscribedRectCrop:(BOOL)useInscribedRectCrop
|
|
119
|
+
error:(NSError **)error;
|
|
120
|
+
|
|
121
|
+
/// Extract `maxFrames` evenly-spaced frames from the video at
|
|
122
|
+
/// `videoPath`, write each as a JPEG into `outputDir`, return the
|
|
123
|
+
/// list of file paths in capture order.
|
|
124
|
+
///
|
|
125
|
+
/// Used as the first half of the panorama pipeline: the host app
|
|
126
|
+
/// records video while the user holds the shutter, then we sample
|
|
127
|
+
/// it down to N still frames the stitcher can consume. cv::Stitcher
|
|
128
|
+
/// works best with 5-15 well-spaced frames — much more is redundant
|
|
129
|
+
/// and slow; much less risks gaps in the seam.
|
|
130
|
+
///
|
|
131
|
+
/// Implementation uses AVAssetImageGenerator (Foundation), not
|
|
132
|
+
/// OpenCV, so no C++ touches this path; cheap enough that we can
|
|
133
|
+
/// expose it as a separate primitive too.
|
|
134
|
+
+ (nullable NSArray<NSString *> *)extractFramesFromVideoAtPath:(NSString *)videoPath
|
|
135
|
+
outputDir:(NSString *)outputDir
|
|
136
|
+
maxFrames:(NSInteger)maxFrames
|
|
137
|
+
jpegQuality:(NSInteger)quality
|
|
138
|
+
error:(NSError **)error;
|
|
139
|
+
|
|
140
|
+
/// One-shot helper: extract frames from `videoPath`, stitch them
|
|
141
|
+
/// into a panorama at `outputPath`, delete the temporary frames,
|
|
142
|
+
/// return the result. This is what the JS shutter-hold flow calls;
|
|
143
|
+
/// callers don't have to manage their own tmp directory or clean
|
|
144
|
+
/// up partial state on failure.
|
|
145
|
+
+ (nullable RNStitchResult *)stitchVideoAtPath:(NSString *)videoPath
|
|
146
|
+
outputPath:(NSString *)outputPath
|
|
147
|
+
maxFrames:(NSInteger)maxFrames
|
|
148
|
+
jpegQuality:(NSInteger)quality
|
|
149
|
+
warperType:(nullable NSString *)warperType
|
|
150
|
+
blenderType:(nullable NSString *)blenderType
|
|
151
|
+
seamFinderType:(nullable NSString *)seamFinderType
|
|
152
|
+
error:(NSError **)error;
|
|
153
|
+
|
|
154
|
+
/// Phase 5: pose-driven stitch. Same end-to-end shape as
|
|
155
|
+
/// `stitchVideoAtPath` but consumes pre-computed camera poses
|
|
156
|
+
/// (from ARKit/ARCore via RNSARSession) and skips the
|
|
157
|
+
/// brittle features → matching → BundleAdjuster steps. Internally:
|
|
158
|
+
///
|
|
159
|
+
/// 1. Extract maxFrames evenly-spaced frames from the video.
|
|
160
|
+
/// 2. Compute each frame's timestamp (fraction × totalSeconds).
|
|
161
|
+
/// 3. Match each frame to the nearest pose in `poses` (within
|
|
162
|
+
/// a 100 ms tolerance).
|
|
163
|
+
/// 4. Build cv::detail::CameraParams directly from the pose's
|
|
164
|
+
/// quaternion + intrinsics — flips coordinate conventions
|
|
165
|
+
/// between ARKit (Y-up, -Z forward) and OpenCV (Y-down,
|
|
166
|
+
/// +Z forward).
|
|
167
|
+
/// 5. Hand cameras to the existing warp + seam + blend pipeline.
|
|
168
|
+
///
|
|
169
|
+
/// `poses` is an NSArray of NSDictionary; each entry has the keys
|
|
170
|
+
/// matching `RNSARFramePose.asDictionary()`:
|
|
171
|
+
/// tx, ty, tz, qx, qy, qz, qw, fx, fy, cx, cy,
|
|
172
|
+
/// imageWidth, imageHeight, timestampMs, trackingState
|
|
173
|
+
/// Frames whose closest pose is missing or beyond tolerance fall
|
|
174
|
+
/// back to the feature-matched path frame-by-frame (degraded but
|
|
175
|
+
/// functional). When ALL poses are missing the method returns
|
|
176
|
+
/// the same NSError code (1030) so the host can opt to retry via
|
|
177
|
+
/// the non-pose path.
|
|
178
|
+
+ (nullable RNStitchResult *)stitchVideoAtPath:(NSString *)videoPath
|
|
179
|
+
outputPath:(NSString *)outputPath
|
|
180
|
+
maxFrames:(NSInteger)maxFrames
|
|
181
|
+
jpegQuality:(NSInteger)quality
|
|
182
|
+
warperType:(nullable NSString *)warperType
|
|
183
|
+
blenderType:(nullable NSString *)blenderType
|
|
184
|
+
seamFinderType:(nullable NSString *)seamFinderType
|
|
185
|
+
poses:(NSArray<NSDictionary *> *)poses
|
|
186
|
+
error:(NSError **)error;
|
|
187
|
+
|
|
188
|
+
/// V16 Phase 1: pose-driven stitch over an explicit list of frame
|
|
189
|
+
/// paths. Sibling of `stitchVideoAtPath:withPoses:` — same compose
|
|
190
|
+
/// stage, but the caller supplies frames as already-on-disk JPEGs
|
|
191
|
+
/// + a 1:1 pose array, so the video extraction + timestamp matching
|
|
192
|
+
/// steps are skipped entirely.
|
|
193
|
+
///
|
|
194
|
+
/// This is the hot path for the "batch-on-AR-keyframes" flow: the
|
|
195
|
+
/// Swift `KeyframeGate` accepts ≤6 frames per capture, each saved
|
|
196
|
+
/// to disk with a known pose; on shutter release we feed those
|
|
197
|
+
/// straight into the same `BundleAdjuster + GraphCutSeamFinder +
|
|
198
|
+
/// MultiBandBlender` pipeline that the video-driven path uses.
|
|
199
|
+
///
|
|
200
|
+
/// `framePaths.count` MUST equal `poses.count` (1:1 mapping; any
|
|
201
|
+
/// downstream filtering happens inside this method). `framePaths`
|
|
202
|
+
/// must be at least 2 entries. Pose dictionaries follow the same
|
|
203
|
+
/// shape as `RNSARFramePose.asDictionary()`.
|
|
204
|
+
+ (nullable RNStitchResult *)stitchKeyframePaths:(NSArray<NSString *> *)framePaths
|
|
205
|
+
outputPath:(NSString *)outputPath
|
|
206
|
+
jpegQuality:(NSInteger)quality
|
|
207
|
+
warperType:(nullable NSString *)warperType
|
|
208
|
+
blenderType:(nullable NSString *)blenderType
|
|
209
|
+
seamFinderType:(nullable NSString *)seamFinderType
|
|
210
|
+
poses:(NSArray<NSDictionary *> *)poses
|
|
211
|
+
error:(NSError **)error;
|
|
212
|
+
|
|
213
|
+
/// Normalise the EXIF orientation of `imagePath` in place.
|
|
214
|
+
///
|
|
215
|
+
/// vision-camera writes photos with the camera-sensor's native
|
|
216
|
+
/// landscape pixels and an EXIF Orientation tag describing how to
|
|
217
|
+
/// rotate them for display. Most consumers (iOS UIImage, RN's
|
|
218
|
+
/// <Image>) honour the tag, but Sentry breadcrumbs, share sheets,
|
|
219
|
+
/// downstream image-manipulation libs, and the cv::Stitcher all
|
|
220
|
+
/// read raw pixels and end up sideways.
|
|
221
|
+
///
|
|
222
|
+
/// This method round-trips the file through cv::imread (which
|
|
223
|
+
/// honours EXIF and gives us the post-rotation pixel buffer) and
|
|
224
|
+
/// cv::imwrite (which writes a plain JPEG with NO EXIF), so the
|
|
225
|
+
/// saved file ends up with rotation baked into pixels and no
|
|
226
|
+
/// orientation metadata. Idempotent on already-normalised images.
|
|
227
|
+
///
|
|
228
|
+
/// Returns `@{ @"width": NSNumber, @"height": NSNumber }` post
|
|
229
|
+
/// rotation so the caller can update its CaptureResult dimensions
|
|
230
|
+
/// to match what's now on disk. NSDictionary is used (rather than
|
|
231
|
+
/// CGSize) because Swift can't translate `(CGSize) + (NSError**)`
|
|
232
|
+
/// into a throwing API — a nullable reference type is required.
|
|
233
|
+
+ (nullable NSDictionary<NSString *, NSNumber *> *)normaliseImageAtPath:(NSString *)imagePath
|
|
234
|
+
error:(NSError **)error;
|
|
235
|
+
|
|
236
|
+
@end
|
|
237
|
+
|
|
238
|
+
NS_ASSUME_NONNULL_END
|