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,278 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
//
|
|
3
|
+
// KeyframeGateBridge.mm — Obj-C++ glue between Swift and the shared
|
|
4
|
+
// C++ KeyframeGate. See header for design rationale.
|
|
5
|
+
|
|
6
|
+
#import "KeyframeGateBridge.h"
|
|
7
|
+
#import "keyframe_gate.hpp" // ../../../cpp/keyframe_gate.hpp via header search path
|
|
8
|
+
#import "ar_frame_pose.h"
|
|
9
|
+
|
|
10
|
+
// V16 A2 — OpenCV needed for the BGRA → grayscale conversion fallback
|
|
11
|
+
// in the new pixel-buffer evaluate path. ARKit's native YUV format
|
|
12
|
+
// (the dominant case) skips OpenCV entirely — Y plane is read
|
|
13
|
+
// directly as the grayscale source. We only pay the cvtColor cost
|
|
14
|
+
// for non-YUV buffers (e.g., vision-camera BGRA capture flows).
|
|
15
|
+
#import <opencv2/core.hpp>
|
|
16
|
+
#import <opencv2/imgproc.hpp>
|
|
17
|
+
|
|
18
|
+
// Single source of truth for the reason-code → string mapping. These
|
|
19
|
+
// strings MUST stay 1:1 with the labels emitted by the original
|
|
20
|
+
// KeyframeGate.swift (and read by the JS telemetry layer in
|
|
21
|
+
// retailens-capture-sdk/src/stitching/incremental.ts). Drift will
|
|
22
|
+
// silently break the JS UI's pill text.
|
|
23
|
+
static NSString *kReasonStringFor(retailens::KeyframeGateDecisionReason r) {
|
|
24
|
+
using R = retailens::KeyframeGateDecisionReason;
|
|
25
|
+
switch (r) {
|
|
26
|
+
case R::AcceptDisabled: return @"gate-disabled";
|
|
27
|
+
case R::AcceptForceLast: return @"force-last";
|
|
28
|
+
case R::AcceptFirstOnPlane: return @"first-anchored-on-plane";
|
|
29
|
+
case R::AcceptFirstNoPlane: return @"first-no-plane";
|
|
30
|
+
case R::AcceptOk: return @"ok";
|
|
31
|
+
case R::AcceptOkAngular: return @"ok-angular";
|
|
32
|
+
case R::AcceptProjectionDegenerate: return @"projection-degenerate";
|
|
33
|
+
case R::AcceptCurrentAreaZero: return @"current-area-zero";
|
|
34
|
+
case R::AcceptNoPoseYet: return @"no-pose-yet";
|
|
35
|
+
case R::RejectMaxReached: return @"max-reached";
|
|
36
|
+
case R::RejectOverlapTooHigh: return @"overlap-too-high";
|
|
37
|
+
case R::RejectOverlapTooHighAngular: return @"overlap-too-high (angular)";
|
|
38
|
+
// V16 A2 — flow strategy reason codes
|
|
39
|
+
case R::AcceptOkFlow: return @"ok-flow";
|
|
40
|
+
case R::AcceptFirstFlow: return @"first-flow";
|
|
41
|
+
case R::RejectOverlapTooHighFlow: return @"overlap-too-high (flow)";
|
|
42
|
+
// V16 — translation-budget force-accept
|
|
43
|
+
case R::AcceptFlowTranslation: return @"ok-flow-translation";
|
|
44
|
+
}
|
|
45
|
+
return @"unknown";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ── KGBDecision impl ─────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
@interface KGBDecision ()
|
|
51
|
+
@property (nonatomic, readwrite) BOOL accept;
|
|
52
|
+
@property (nonatomic, readwrite) NSInteger reasonCode;
|
|
53
|
+
@property (nonatomic, readwrite) NSString *reasonString;
|
|
54
|
+
@property (nonatomic, readwrite) double newContentFraction;
|
|
55
|
+
@property (nonatomic, readwrite) NSInteger acceptedCount;
|
|
56
|
+
@property (nonatomic, readwrite) NSInteger maxCount;
|
|
57
|
+
@end
|
|
58
|
+
|
|
59
|
+
@implementation KGBDecision
|
|
60
|
+
@end
|
|
61
|
+
|
|
62
|
+
// ── KeyframeGateBridge impl ──────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
@implementation KeyframeGateBridge {
|
|
65
|
+
retailens::KeyframeGate _gate;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
- (instancetype)init {
|
|
69
|
+
self = [super init];
|
|
70
|
+
// No init needed — KeyframeGate's default state is exactly right
|
|
71
|
+
// (enabled=false, threshold=0.4, maxCount=6, acceptedCount=0).
|
|
72
|
+
return self;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
- (void)setEnabled:(BOOL)enabled {
|
|
76
|
+
_gate.setEnabled(static_cast<bool>(enabled));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
- (void)setOverlapThreshold:(double)threshold {
|
|
80
|
+
_gate.setOverlapThreshold(threshold);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
- (void)setMaxCount:(NSInteger)maxCount {
|
|
84
|
+
_gate.setMaxCount(static_cast<int32_t>(maxCount));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
- (void)markNextFrameAsLast {
|
|
88
|
+
_gate.markNextFrameAsLast();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
- (void)reset {
|
|
92
|
+
_gate.reset();
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
- (BOOL)isEnabled { return static_cast<BOOL>(_gate.isEnabled()); }
|
|
96
|
+
- (NSInteger)acceptedCount { return static_cast<NSInteger>(_gate.getAcceptedCount()); }
|
|
97
|
+
- (NSInteger)maxCount { return static_cast<NSInteger>(_gate.getMaxCount()); }
|
|
98
|
+
|
|
99
|
+
// ── V16 A2 — strategy + flow tunables ───────────────────────────
|
|
100
|
+
|
|
101
|
+
- (void)setStrategy:(KGBStrategy)strategy {
|
|
102
|
+
_gate.setStrategy(static_cast<retailens::GateStrategy>(strategy));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
- (KGBStrategy)strategy {
|
|
106
|
+
return static_cast<KGBStrategy>(_gate.getStrategy());
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
- (void)setFlowMaxCorners:(NSInteger)maxCorners {
|
|
110
|
+
_gate.setFlowMaxCorners(static_cast<int32_t>(maxCorners));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
- (void)setFlowQualityLevel:(double)quality {
|
|
114
|
+
_gate.setFlowQualityLevel(quality);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
- (void)setFlowMinDistance:(double)minDistance {
|
|
118
|
+
_gate.setFlowMinDistance(minDistance);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
- (void)setFlowMaxTranslationM:(double)metres {
|
|
122
|
+
_gate.setFlowMaxTranslationM(metres);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
- (void)setFlowNoveltyPercentile:(double)percentile {
|
|
126
|
+
_gate.setFlowNoveltyPercentile(percentile);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
- (KGBDecision *)evaluateWithTx:(float)tx ty:(float)ty tz:(float)tz
|
|
130
|
+
qx:(float)qx qy:(float)qy qz:(float)qz qw:(float)qw
|
|
131
|
+
fx:(float)fx fy:(float)fy cx:(float)cx cy:(float)cy
|
|
132
|
+
imageWidth:(int32_t)imageWidth
|
|
133
|
+
imageHeight:(int32_t)imageHeight
|
|
134
|
+
plane16:(nullable NSArray<NSNumber *> *)plane16
|
|
135
|
+
{
|
|
136
|
+
retailens::Pose pose;
|
|
137
|
+
pose.tx = tx; pose.ty = ty; pose.tz = tz;
|
|
138
|
+
pose.qx = qx; pose.qy = qy; pose.qz = qz; pose.qw = qw;
|
|
139
|
+
pose.fx = fx; pose.fy = fy; pose.cx = cx; pose.cy = cy;
|
|
140
|
+
pose.imageWidth = imageWidth;
|
|
141
|
+
pose.imageHeight = imageHeight;
|
|
142
|
+
|
|
143
|
+
retailens::PlaneTransform planeStorage;
|
|
144
|
+
const retailens::PlaneTransform *planePtr = nullptr;
|
|
145
|
+
if (plane16 != nil && plane16.count == 16) {
|
|
146
|
+
for (NSUInteger i = 0; i < 16; ++i) {
|
|
147
|
+
planeStorage.m[i] = static_cast<float>(plane16[i].doubleValue);
|
|
148
|
+
}
|
|
149
|
+
planePtr = &planeStorage;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
retailens::KeyframeGateDecision d = _gate.evaluate(pose, planePtr);
|
|
153
|
+
|
|
154
|
+
KGBDecision *out = [[KGBDecision alloc] init];
|
|
155
|
+
out.accept = static_cast<BOOL>(d.accept);
|
|
156
|
+
out.reasonCode = static_cast<NSInteger>(d.reason);
|
|
157
|
+
out.reasonString = kReasonStringFor(d.reason);
|
|
158
|
+
out.newContentFraction = d.newContentFraction;
|
|
159
|
+
out.acceptedCount = static_cast<NSInteger>(d.acceptedCount);
|
|
160
|
+
out.maxCount = static_cast<NSInteger>(d.maxCount);
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// V16 A2 — strategy-aware evaluate that also accepts the frame's
|
|
165
|
+
// pixel buffer. See header for format support + cost notes.
|
|
166
|
+
//
|
|
167
|
+
// Internal flow:
|
|
168
|
+
// 1. Build the retailens::Pose + optional plane (same as
|
|
169
|
+
// evaluateWith…plane16:).
|
|
170
|
+
// 2. Lock the pixel buffer (read-only — we never write back).
|
|
171
|
+
// 3. Get a grayscale view of the frame: Y-plane direct read for
|
|
172
|
+
// YUV 4:2:0, cv::cvtColor for BGRA. Other formats → fall
|
|
173
|
+
// through to the pose-only path (defensive).
|
|
174
|
+
// 4. Hand grayscale data pointer + dims + stride to the C++ gate
|
|
175
|
+
// via evaluateWithFrame(...).
|
|
176
|
+
// 5. Unlock the pixel buffer.
|
|
177
|
+
// 6. Marshal the decision back to KGBDecision.
|
|
178
|
+
//
|
|
179
|
+
// The grayscale data MUST stay alive for the duration of the C++
|
|
180
|
+
// call: for YUV that's guaranteed by the lock; for BGRA we hold
|
|
181
|
+
// the cvtColor result in a stack-local cv::Mat (`bgraToGrayHolder`)
|
|
182
|
+
// whose buffer outlives the gate call.
|
|
183
|
+
- (KGBDecision *)evaluatePixelBuffer:(CVPixelBufferRef)pixelBuffer
|
|
184
|
+
tx:(float)tx ty:(float)ty tz:(float)tz
|
|
185
|
+
qx:(float)qx qy:(float)qy qz:(float)qz qw:(float)qw
|
|
186
|
+
fx:(float)fx fy:(float)fy cx:(float)cx cy:(float)cy
|
|
187
|
+
imageWidth:(int32_t)imageWidth
|
|
188
|
+
imageHeight:(int32_t)imageHeight
|
|
189
|
+
plane16:(nullable NSArray<NSNumber *> *)plane16
|
|
190
|
+
{
|
|
191
|
+
// Fast path: Pose strategy doesn't need the pixel buffer; route
|
|
192
|
+
// straight to the pose-only evaluate so we don't pay the
|
|
193
|
+
// CVPixelBuffer lock/unlock cost (~10 µs but at 60 fps = 600 µs/s
|
|
194
|
+
// wasted) for every Pose-strategy frame.
|
|
195
|
+
if (_gate.getStrategy() == retailens::GateStrategy::Pose) {
|
|
196
|
+
return [self evaluateWithTx:tx ty:ty tz:tz
|
|
197
|
+
qx:qx qy:qy qz:qz qw:qw
|
|
198
|
+
fx:fx fy:fy cx:cx cy:cy
|
|
199
|
+
imageWidth:imageWidth
|
|
200
|
+
imageHeight:imageHeight
|
|
201
|
+
plane16:plane16];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
retailens::Pose pose;
|
|
205
|
+
pose.tx = tx; pose.ty = ty; pose.tz = tz;
|
|
206
|
+
pose.qx = qx; pose.qy = qy; pose.qz = qz; pose.qw = qw;
|
|
207
|
+
pose.fx = fx; pose.fy = fy; pose.cx = cx; pose.cy = cy;
|
|
208
|
+
pose.imageWidth = imageWidth;
|
|
209
|
+
pose.imageHeight = imageHeight;
|
|
210
|
+
|
|
211
|
+
retailens::PlaneTransform planeStorage;
|
|
212
|
+
const retailens::PlaneTransform *planePtr = nullptr;
|
|
213
|
+
if (plane16 != nil && plane16.count == 16) {
|
|
214
|
+
for (NSUInteger i = 0; i < 16; ++i) {
|
|
215
|
+
planeStorage.m[i] = static_cast<float>(plane16[i].doubleValue);
|
|
216
|
+
}
|
|
217
|
+
planePtr = &planeStorage;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Lock the buffer for read-only access. Caller (the engine's AR
|
|
221
|
+
// delegate) might want to use the same buffer for its own
|
|
222
|
+
// processing — the lock is reentrant within the same thread, so
|
|
223
|
+
// this is safe.
|
|
224
|
+
CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
|
|
225
|
+
|
|
226
|
+
const OSType format = CVPixelBufferGetPixelFormatType(pixelBuffer);
|
|
227
|
+
const uint8_t *grayData = nullptr;
|
|
228
|
+
int32_t grayWidth = 0;
|
|
229
|
+
int32_t grayHeight = 0;
|
|
230
|
+
int32_t grayStride = 0;
|
|
231
|
+
cv::Mat bgraToGrayHolder; // owns the converted buffer for BGRA path
|
|
232
|
+
|
|
233
|
+
if (format == kCVPixelFormatType_420YpCbCr8BiPlanarFullRange ||
|
|
234
|
+
format == kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange)
|
|
235
|
+
{
|
|
236
|
+
// YUV 4:2:0 biplanar — Y plane IS our grayscale. Zero
|
|
237
|
+
// conversion cost. ARKit's native format; the dominant
|
|
238
|
+
// production path.
|
|
239
|
+
grayData = static_cast<const uint8_t *>(
|
|
240
|
+
CVPixelBufferGetBaseAddressOfPlane(pixelBuffer, 0));
|
|
241
|
+
grayWidth = static_cast<int32_t>(CVPixelBufferGetWidthOfPlane(pixelBuffer, 0));
|
|
242
|
+
grayHeight = static_cast<int32_t>(CVPixelBufferGetHeightOfPlane(pixelBuffer, 0));
|
|
243
|
+
grayStride = static_cast<int32_t>(CVPixelBufferGetBytesPerRowOfPlane(pixelBuffer, 0));
|
|
244
|
+
} else if (format == kCVPixelFormatType_32BGRA) {
|
|
245
|
+
// BGRA — convert to grayscale via OpenCV. ~2-3 ms at
|
|
246
|
+
// 1920×1440 on iPhone 13 Pro.
|
|
247
|
+
const int32_t w = static_cast<int32_t>(CVPixelBufferGetWidth(pixelBuffer));
|
|
248
|
+
const int32_t h = static_cast<int32_t>(CVPixelBufferGetHeight(pixelBuffer));
|
|
249
|
+
const int32_t bgraStride = static_cast<int32_t>(CVPixelBufferGetBytesPerRow(pixelBuffer));
|
|
250
|
+
uint8_t *bgraPtr = static_cast<uint8_t *>(CVPixelBufferGetBaseAddress(pixelBuffer));
|
|
251
|
+
cv::Mat bgra(h, w, CV_8UC4, bgraPtr, bgraStride);
|
|
252
|
+
cv::cvtColor(bgra, bgraToGrayHolder, cv::COLOR_BGRA2GRAY);
|
|
253
|
+
grayData = bgraToGrayHolder.data;
|
|
254
|
+
grayWidth = static_cast<int32_t>(bgraToGrayHolder.cols);
|
|
255
|
+
grayHeight = static_cast<int32_t>(bgraToGrayHolder.rows);
|
|
256
|
+
grayStride = static_cast<int32_t>(bgraToGrayHolder.step);
|
|
257
|
+
}
|
|
258
|
+
// else: grayData stays nullptr. The C++ gate detects this and
|
|
259
|
+
// falls back to the pose-only path inside evaluateWithFrame —
|
|
260
|
+
// graceful degradation for unsupported pixel formats.
|
|
261
|
+
|
|
262
|
+
retailens::KeyframeGateDecision d = _gate.evaluateWithFrame(
|
|
263
|
+
pose, planePtr,
|
|
264
|
+
grayData, grayWidth, grayHeight, grayStride);
|
|
265
|
+
|
|
266
|
+
CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
|
|
267
|
+
|
|
268
|
+
KGBDecision *out = [[KGBDecision alloc] init];
|
|
269
|
+
out.accept = static_cast<BOOL>(d.accept);
|
|
270
|
+
out.reasonCode = static_cast<NSInteger>(d.reason);
|
|
271
|
+
out.reasonString = kReasonStringFor(d.reason);
|
|
272
|
+
out.newContentFraction = d.newContentFraction;
|
|
273
|
+
out.acceptedCount = static_cast<NSInteger>(d.acceptedCount);
|
|
274
|
+
out.maxCount = static_cast<NSInteger>(d.maxCount);
|
|
275
|
+
return out;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
@end
|