drawdown-arcore-depth 0.1.4 → 0.1.6
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/README.md +10 -0
- package/android/src/main/java/org/drawdown/arcoredepth/DepthObservationSampler.java +180 -25
- package/android/src/main/java/org/drawdown/arcoredepth/DepthSampleResult.java +69 -0
- package/android/src/main/java/org/drawdown/arcoredepth/DrawDownArCoreDepthPlugin.java +189 -9
- package/android/src/main/java/org/drawdown/arcoredepth/RawAnchorDiagnostics.java +106 -0
- package/dist/esm/definitions.d.ts +113 -38
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -188,3 +188,13 @@ No ArUco marker is required by the native ARCore workflow. The existing RFID/NFC
|
|
|
188
188
|
## v0.1.4 Raw Depth field fix
|
|
189
189
|
|
|
190
190
|
The DBH sampler now anchors front-surface range with ARCore Raw Depth plus the matching confidence image (confidence >= 128). Smoothed Depth is used only after a high-confidence foreground tree range has been established, to fill the trunk surface for multi-row boundary estimation. This prevents distant outdoor background depth from being mistaken for the trunk.
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
## 0.1.6 diagnostic sweep
|
|
194
|
+
|
|
195
|
+
Version 0.1.6 keeps the 0.1.4 Raw Depth DBH acceptance geometry unchanged and adds compact native sweep diagnostics. `captureDbhSweep()` now reports per-stage frame counts and `nativeRejectionCounts` so zero-observation field failures can be diagnosed without retaining camera or depth frames.
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
## 0.1.6 Raw Depth anchor diagnostics
|
|
199
|
+
|
|
200
|
+
0.1.6 keeps the 0.1.5 DBH acceptance algorithm unchanged and adds compact numeric diagnostics around the mapped crosshair at 10, 20 and 30 pixel radii. It reports non-zero/in-range Raw Depth counts, confidence threshold counts (64/96/128/160), the highest confidence in the eligible 0.5–5 m range and the associated depth. These diagnostics do not influence the accepted DBH result.
|
|
@@ -39,29 +39,70 @@ final class DepthObservationSampler {
|
|
|
39
39
|
private static final double MIN_TARGET_DEPTH_MM = 500.0;
|
|
40
40
|
private static final double MAX_TARGET_DEPTH_MM = 5000.0;
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
DepthSampleResult sample(Frame frame, Camera camera, int viewWidth, int viewHeight) {
|
|
43
|
+
boolean trackingFrame = false;
|
|
44
|
+
boolean rawDepthFrame = false;
|
|
45
|
+
boolean mappedCenterReached = false;
|
|
46
|
+
boolean rawAnchorReached = false;
|
|
47
|
+
boolean rawPatchReached = false;
|
|
48
|
+
boolean smoothDepthMatchReached = false;
|
|
49
|
+
boolean boundaryConsensusReached = false;
|
|
50
|
+
RawAnchorDiagnostics rawAnchorDiagnostics = null;
|
|
51
|
+
|
|
43
52
|
if (camera.getTrackingState() != TrackingState.TRACKING) {
|
|
44
|
-
return
|
|
53
|
+
return DepthSampleResult.failure(
|
|
54
|
+
"tracking_not_tracking",
|
|
55
|
+
false, false, false, false, false, false, false, rawAnchorDiagnostics);
|
|
45
56
|
}
|
|
57
|
+
trackingFrame = true;
|
|
58
|
+
|
|
59
|
+
Image rawDepth = null;
|
|
60
|
+
Image rawConfidence = null;
|
|
61
|
+
Image smoothDepth = null;
|
|
62
|
+
try {
|
|
63
|
+
try {
|
|
64
|
+
rawDepth = frame.acquireRawDepthImage16Bits();
|
|
65
|
+
rawDepthFrame = true;
|
|
66
|
+
} catch (NotYetAvailableException e) {
|
|
67
|
+
return DepthSampleResult.failure(
|
|
68
|
+
"raw_depth_not_yet_available",
|
|
69
|
+
trackingFrame, false, false, false, false, false, false, rawAnchorDiagnostics);
|
|
70
|
+
}
|
|
46
71
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
72
|
+
try {
|
|
73
|
+
rawConfidence = frame.acquireRawDepthConfidenceImage();
|
|
74
|
+
} catch (NotYetAvailableException e) {
|
|
75
|
+
return DepthSampleResult.failure(
|
|
76
|
+
"raw_confidence_not_available",
|
|
77
|
+
trackingFrame, rawDepthFrame, false, false, false, false, false, rawAnchorDiagnostics);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
smoothDepth = frame.acquireDepthImage16Bits();
|
|
82
|
+
} catch (NotYetAvailableException e) {
|
|
83
|
+
return DepthSampleResult.failure(
|
|
84
|
+
"smooth_depth_not_available",
|
|
85
|
+
trackingFrame, rawDepthFrame, false, false, false, false, false, rawAnchorDiagnostics);
|
|
86
|
+
}
|
|
51
87
|
|
|
52
88
|
CameraIntrinsics intrinsics = camera.getImageIntrinsics();
|
|
53
89
|
float[] focal = intrinsics.getFocalLength();
|
|
54
90
|
int[] imageDimensions = intrinsics.getImageDimensions();
|
|
55
91
|
if (focal == null || focal.length < 2 || imageDimensions == null || imageDimensions.length < 2) {
|
|
56
|
-
return
|
|
92
|
+
return DepthSampleResult.failure(
|
|
93
|
+
"image_intrinsics_missing",
|
|
94
|
+
trackingFrame, rawDepthFrame, false, false, false, false, false, rawAnchorDiagnostics);
|
|
57
95
|
}
|
|
58
96
|
|
|
59
|
-
//
|
|
97
|
+
// Keep the 0.1.5 measurement geometry/thresholds unchanged; 0.1.6 only adds
|
|
98
|
+
// Raw Depth anchor diagnostics around the mapped crosshair.
|
|
60
99
|
if (rawDepth.getWidth() != rawConfidence.getWidth()
|
|
61
100
|
|| rawDepth.getHeight() != rawConfidence.getHeight()
|
|
62
101
|
|| rawDepth.getWidth() != smoothDepth.getWidth()
|
|
63
102
|
|| rawDepth.getHeight() != smoothDepth.getHeight()) {
|
|
64
|
-
return
|
|
103
|
+
return DepthSampleResult.failure(
|
|
104
|
+
"image_size_mismatch",
|
|
105
|
+
trackingFrame, rawDepthFrame, false, false, false, false, false, rawAnchorDiagnostics);
|
|
65
106
|
}
|
|
66
107
|
|
|
67
108
|
float[] viewCenter = new float[] { viewWidth / 2f, viewHeight / 2f };
|
|
@@ -74,13 +115,21 @@ final class DepthObservationSampler {
|
|
|
74
115
|
|
|
75
116
|
int[] mappedCenter = imageToDepth(frame, rawDepth, imageCenter[0], imageCenter[1]);
|
|
76
117
|
if (mappedCenter == null) {
|
|
77
|
-
return
|
|
118
|
+
return DepthSampleResult.failure(
|
|
119
|
+
"image_to_depth_mapping_failed",
|
|
120
|
+
trackingFrame, rawDepthFrame, false, false, false, false, false, rawAnchorDiagnostics);
|
|
78
121
|
}
|
|
122
|
+
mappedCenterReached = true;
|
|
123
|
+
rawAnchorDiagnostics = collectRawAnchorDiagnostics(
|
|
124
|
+
rawDepth, rawConfidence, mappedCenter[0], mappedCenter[1]);
|
|
79
125
|
|
|
80
126
|
RawAnchor anchor = findRawAnchor(rawDepth, rawConfidence, mappedCenter[0], mappedCenter[1]);
|
|
81
127
|
if (anchor == null) {
|
|
82
|
-
return
|
|
128
|
+
return DepthSampleResult.failure(
|
|
129
|
+
"no_confident_raw_anchor",
|
|
130
|
+
trackingFrame, rawDepthFrame, mappedCenterReached, false, false, false, false, rawAnchorDiagnostics);
|
|
83
131
|
}
|
|
132
|
+
rawAnchorReached = true;
|
|
84
133
|
|
|
85
134
|
double rawToleranceMm = clamp(anchor.depthMm * 0.10, 100.0, 250.0);
|
|
86
135
|
List<Integer> rawPatch = collectConfidentRawPatch(
|
|
@@ -92,13 +141,18 @@ final class DepthObservationSampler {
|
|
|
92
141
|
rawToleranceMm,
|
|
93
142
|
anchor.depthMm);
|
|
94
143
|
if (rawPatch.size() < MIN_RAW_PATCH_SAMPLES) {
|
|
95
|
-
return
|
|
144
|
+
return DepthSampleResult.failure(
|
|
145
|
+
"insufficient_raw_patch_samples",
|
|
146
|
+
trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, false, false, false, rawAnchorDiagnostics);
|
|
96
147
|
}
|
|
148
|
+
rawPatchReached = true;
|
|
97
149
|
|
|
98
150
|
double centerDepthMm = median(rawPatch);
|
|
99
151
|
double centerMadMm = mad(rawPatch, centerDepthMm);
|
|
100
152
|
if (centerDepthMm < MIN_TARGET_DEPTH_MM || centerDepthMm > MAX_TARGET_DEPTH_MM) {
|
|
101
|
-
return
|
|
153
|
+
return DepthSampleResult.failure(
|
|
154
|
+
"target_depth_out_of_range",
|
|
155
|
+
trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, false, false, rawAnchorDiagnostics);
|
|
102
156
|
}
|
|
103
157
|
|
|
104
158
|
// Locate the foreground surface in the smoothed depth image, but only
|
|
@@ -111,8 +165,11 @@ final class DepthObservationSampler {
|
|
|
111
165
|
centerDepthMm,
|
|
112
166
|
clamp(centerDepthMm * 0.10, 100.0, 250.0));
|
|
113
167
|
if (centerX < 0) {
|
|
114
|
-
return
|
|
168
|
+
return DepthSampleResult.failure(
|
|
169
|
+
"no_smooth_depth_match",
|
|
170
|
+
trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, false, false, rawAnchorDiagnostics);
|
|
115
171
|
}
|
|
172
|
+
smoothDepthMatchReached = true;
|
|
116
173
|
|
|
117
174
|
double toleranceMm = clamp(centerDepthMm * 0.08, 80.0, 220.0);
|
|
118
175
|
Boundary boundary = findConsensusBoundary(
|
|
@@ -122,28 +179,41 @@ final class DepthObservationSampler {
|
|
|
122
179
|
centerDepthMm,
|
|
123
180
|
toleranceMm);
|
|
124
181
|
if (boundary == null || boundary.rightX - boundary.leftX < 3) {
|
|
125
|
-
return
|
|
182
|
+
return DepthSampleResult.failure(
|
|
183
|
+
"boundary_no_consensus",
|
|
184
|
+
trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, smoothDepthMatchReached, false, rawAnchorDiagnostics);
|
|
126
185
|
}
|
|
186
|
+
boundaryConsensusReached = true;
|
|
127
187
|
|
|
128
188
|
int depthWidth = smoothDepth.getWidth();
|
|
129
189
|
int boundaryWidth = boundary.rightX - boundary.leftX;
|
|
130
190
|
if (boundary.leftX <= EDGE_MARGIN_PX
|
|
131
|
-
|| boundary.rightX >= depthWidth - 1 - EDGE_MARGIN_PX
|
|
132
|
-
|
|
133
|
-
|
|
191
|
+
|| boundary.rightX >= depthWidth - 1 - EDGE_MARGIN_PX) {
|
|
192
|
+
return DepthSampleResult.failure(
|
|
193
|
+
"boundary_touches_edge",
|
|
194
|
+
trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, smoothDepthMatchReached, boundaryConsensusReached, rawAnchorDiagnostics);
|
|
195
|
+
}
|
|
196
|
+
if (boundaryWidth > depthWidth * MAX_TRUNK_WIDTH_FRACTION) {
|
|
197
|
+
return DepthSampleResult.failure(
|
|
198
|
+
"boundary_too_wide",
|
|
199
|
+
trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, smoothDepthMatchReached, boundaryConsensusReached, rawAnchorDiagnostics);
|
|
134
200
|
}
|
|
135
201
|
|
|
136
202
|
float[] leftImage = depthToImage(frame, smoothDepth, boundary.leftX, anchor.y);
|
|
137
203
|
float[] rightImage = depthToImage(frame, smoothDepth, boundary.rightX, anchor.y);
|
|
138
204
|
if (leftImage == null || rightImage == null) {
|
|
139
|
-
return
|
|
205
|
+
return DepthSampleResult.failure(
|
|
206
|
+
"depth_to_image_mapping_failed",
|
|
207
|
+
trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, smoothDepthMatchReached, boundaryConsensusReached, rawAnchorDiagnostics);
|
|
140
208
|
}
|
|
141
209
|
|
|
142
210
|
double dx = rightImage[0] - leftImage[0];
|
|
143
211
|
double dy = rightImage[1] - leftImage[1];
|
|
144
212
|
double imageWidthPx = Math.hypot(dx, dy);
|
|
145
213
|
if (!Double.isFinite(imageWidthPx) || imageWidthPx < 8.0) {
|
|
146
|
-
return
|
|
214
|
+
return DepthSampleResult.failure(
|
|
215
|
+
"image_width_too_small",
|
|
216
|
+
trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, smoothDepthMatchReached, boundaryConsensusReached, rawAnchorDiagnostics);
|
|
147
217
|
}
|
|
148
218
|
|
|
149
219
|
double normalizedAngularWidth = Math.sqrt(
|
|
@@ -152,7 +222,9 @@ final class DepthObservationSampler {
|
|
|
152
222
|
if (!Double.isFinite(normalizedAngularWidth)
|
|
153
223
|
|| normalizedAngularWidth <= 0.0
|
|
154
224
|
|| normalizedAngularWidth > 0.95) {
|
|
155
|
-
return
|
|
225
|
+
return DepthSampleResult.failure(
|
|
226
|
+
"angular_width_invalid",
|
|
227
|
+
trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, smoothDepthMatchReached, boundaryConsensusReached, rawAnchorDiagnostics);
|
|
156
228
|
}
|
|
157
229
|
|
|
158
230
|
double effectiveFocalPx = imageWidthPx / normalizedAngularWidth;
|
|
@@ -178,7 +250,7 @@ final class DepthObservationSampler {
|
|
|
178
250
|
boundaryConfidence = "low";
|
|
179
251
|
}
|
|
180
252
|
|
|
181
|
-
|
|
253
|
+
DepthObservation observation = new DepthObservation(
|
|
182
254
|
centerDepthMm / 1000.0,
|
|
183
255
|
effectiveFocalPx,
|
|
184
256
|
imageWidthPx,
|
|
@@ -187,11 +259,94 @@ final class DepthObservationSampler {
|
|
|
187
259
|
boundaryConfidence,
|
|
188
260
|
centerMadMm / 1000.0,
|
|
189
261
|
System.currentTimeMillis());
|
|
190
|
-
|
|
191
|
-
return null;
|
|
262
|
+
return DepthSampleResult.success(observation, rawAnchorDiagnostics);
|
|
192
263
|
} catch (Exception e) {
|
|
193
|
-
return
|
|
264
|
+
return DepthSampleResult.failure(
|
|
265
|
+
"unexpected_exception",
|
|
266
|
+
trackingFrame,
|
|
267
|
+
rawDepthFrame,
|
|
268
|
+
mappedCenterReached,
|
|
269
|
+
rawAnchorReached,
|
|
270
|
+
rawPatchReached,
|
|
271
|
+
smoothDepthMatchReached,
|
|
272
|
+
boundaryConsensusReached,
|
|
273
|
+
rawAnchorDiagnostics);
|
|
274
|
+
} finally {
|
|
275
|
+
if (smoothDepth != null) smoothDepth.close();
|
|
276
|
+
if (rawConfidence != null) rawConfidence.close();
|
|
277
|
+
if (rawDepth != null) rawDepth.close();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
private static RawAnchorDiagnostics collectRawAnchorDiagnostics(
|
|
282
|
+
Image rawDepth,
|
|
283
|
+
Image confidence,
|
|
284
|
+
int centerX,
|
|
285
|
+
int centerY) {
|
|
286
|
+
RawAnchorDiagnostics.RadiusDiagnostics radius10 = scanRawRadius(rawDepth, confidence, centerX, centerY, 10);
|
|
287
|
+
RawAnchorDiagnostics.RadiusDiagnostics radius20 = scanRawRadius(rawDepth, confidence, centerX, centerY, 20);
|
|
288
|
+
RawAnchorDiagnostics.RadiusDiagnostics radius30 = scanRawRadius(rawDepth, confidence, centerX, centerY, 30);
|
|
289
|
+
return new RawAnchorDiagnostics(
|
|
290
|
+
centerX,
|
|
291
|
+
centerY,
|
|
292
|
+
rawDepth.getWidth(),
|
|
293
|
+
rawDepth.getHeight(),
|
|
294
|
+
radius10,
|
|
295
|
+
radius20,
|
|
296
|
+
radius30);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private static RawAnchorDiagnostics.RadiusDiagnostics scanRawRadius(
|
|
300
|
+
Image rawDepth,
|
|
301
|
+
Image confidence,
|
|
302
|
+
int centerX,
|
|
303
|
+
int centerY,
|
|
304
|
+
int radius) {
|
|
305
|
+
int sampled = 0;
|
|
306
|
+
int nonZero = 0;
|
|
307
|
+
int inRange = 0;
|
|
308
|
+
int atLeast64 = 0;
|
|
309
|
+
int atLeast96 = 0;
|
|
310
|
+
int atLeast128 = 0;
|
|
311
|
+
int atLeast160 = 0;
|
|
312
|
+
int maxConfidenceInRange = 0;
|
|
313
|
+
int depthAtMaxConfidence = 0;
|
|
314
|
+
|
|
315
|
+
for (int y = Math.max(0, centerY - radius);
|
|
316
|
+
y <= Math.min(rawDepth.getHeight() - 1, centerY + radius);
|
|
317
|
+
y++) {
|
|
318
|
+
for (int x = Math.max(0, centerX - radius);
|
|
319
|
+
x <= Math.min(rawDepth.getWidth() - 1, centerX + radius);
|
|
320
|
+
x++) {
|
|
321
|
+
sampled += 1;
|
|
322
|
+
int depthMm = getDepthMm(rawDepth, x, y);
|
|
323
|
+
if (depthMm <= 0) continue;
|
|
324
|
+
nonZero += 1;
|
|
325
|
+
if (depthMm < MIN_TARGET_DEPTH_MM || depthMm > MAX_TARGET_DEPTH_MM) continue;
|
|
326
|
+
inRange += 1;
|
|
327
|
+
int conf = getConfidence(confidence, x, y);
|
|
328
|
+
if (conf >= 64) atLeast64 += 1;
|
|
329
|
+
if (conf >= 96) atLeast96 += 1;
|
|
330
|
+
if (conf >= 128) atLeast128 += 1;
|
|
331
|
+
if (conf >= 160) atLeast160 += 1;
|
|
332
|
+
if (conf > maxConfidenceInRange) {
|
|
333
|
+
maxConfidenceInRange = conf;
|
|
334
|
+
depthAtMaxConfidence = depthMm;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
194
337
|
}
|
|
338
|
+
|
|
339
|
+
return new RawAnchorDiagnostics.RadiusDiagnostics(
|
|
340
|
+
radius,
|
|
341
|
+
sampled,
|
|
342
|
+
nonZero,
|
|
343
|
+
inRange,
|
|
344
|
+
atLeast64,
|
|
345
|
+
atLeast96,
|
|
346
|
+
atLeast128,
|
|
347
|
+
atLeast160,
|
|
348
|
+
maxConfidenceInRange,
|
|
349
|
+
depthAtMaxConfidence);
|
|
195
350
|
}
|
|
196
351
|
|
|
197
352
|
private static RawAnchor findRawAnchor(Image rawDepth, Image confidence, int centerX, int centerY) {
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
package org.drawdown.arcoredepth;
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* One attempted native ARCore sample. A failed sample carries a stable reason
|
|
5
|
+
* plus milestone flags so the sweep can explain where frames are being lost
|
|
6
|
+
* without persisting camera/depth frames.
|
|
7
|
+
*/
|
|
8
|
+
final class DepthSampleResult {
|
|
9
|
+
final DepthObservation observation;
|
|
10
|
+
final String rejectionReason;
|
|
11
|
+
final boolean trackingFrame;
|
|
12
|
+
final boolean rawDepthFrame;
|
|
13
|
+
final boolean mappedCenter;
|
|
14
|
+
final boolean rawAnchor;
|
|
15
|
+
final boolean rawPatch;
|
|
16
|
+
final boolean smoothDepthMatch;
|
|
17
|
+
final boolean boundaryConsensus;
|
|
18
|
+
final RawAnchorDiagnostics rawAnchorDiagnostics;
|
|
19
|
+
|
|
20
|
+
DepthSampleResult(
|
|
21
|
+
DepthObservation observation,
|
|
22
|
+
String rejectionReason,
|
|
23
|
+
boolean trackingFrame,
|
|
24
|
+
boolean rawDepthFrame,
|
|
25
|
+
boolean mappedCenter,
|
|
26
|
+
boolean rawAnchor,
|
|
27
|
+
boolean rawPatch,
|
|
28
|
+
boolean smoothDepthMatch,
|
|
29
|
+
boolean boundaryConsensus,
|
|
30
|
+
RawAnchorDiagnostics rawAnchorDiagnostics) {
|
|
31
|
+
this.observation = observation;
|
|
32
|
+
this.rejectionReason = rejectionReason;
|
|
33
|
+
this.trackingFrame = trackingFrame;
|
|
34
|
+
this.rawDepthFrame = rawDepthFrame;
|
|
35
|
+
this.mappedCenter = mappedCenter;
|
|
36
|
+
this.rawAnchor = rawAnchor;
|
|
37
|
+
this.rawPatch = rawPatch;
|
|
38
|
+
this.smoothDepthMatch = smoothDepthMatch;
|
|
39
|
+
this.boundaryConsensus = boundaryConsensus;
|
|
40
|
+
this.rawAnchorDiagnostics = rawAnchorDiagnostics;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static DepthSampleResult failure(
|
|
44
|
+
String reason,
|
|
45
|
+
boolean trackingFrame,
|
|
46
|
+
boolean rawDepthFrame,
|
|
47
|
+
boolean mappedCenter,
|
|
48
|
+
boolean rawAnchor,
|
|
49
|
+
boolean rawPatch,
|
|
50
|
+
boolean smoothDepthMatch,
|
|
51
|
+
boolean boundaryConsensus,
|
|
52
|
+
RawAnchorDiagnostics rawAnchorDiagnostics) {
|
|
53
|
+
return new DepthSampleResult(
|
|
54
|
+
null,
|
|
55
|
+
reason,
|
|
56
|
+
trackingFrame,
|
|
57
|
+
rawDepthFrame,
|
|
58
|
+
mappedCenter,
|
|
59
|
+
rawAnchor,
|
|
60
|
+
rawPatch,
|
|
61
|
+
smoothDepthMatch,
|
|
62
|
+
boundaryConsensus,
|
|
63
|
+
rawAnchorDiagnostics);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static DepthSampleResult success(DepthObservation observation, RawAnchorDiagnostics rawAnchorDiagnostics) {
|
|
67
|
+
return new DepthSampleResult(observation, null, true, true, true, true, true, true, true, rawAnchorDiagnostics);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -36,7 +36,9 @@ import com.google.ar.core.exceptions.UnavailableUserDeclinedInstallationExceptio
|
|
|
36
36
|
import android.opengl.GLSurfaceView;
|
|
37
37
|
|
|
38
38
|
import java.util.ArrayList;
|
|
39
|
+
import java.util.LinkedHashMap;
|
|
39
40
|
import java.util.List;
|
|
41
|
+
import java.util.Map;
|
|
40
42
|
|
|
41
43
|
@CapacitorPlugin(
|
|
42
44
|
name = "DrawDownArCoreDepth",
|
|
@@ -44,7 +46,8 @@ import java.util.List;
|
|
|
44
46
|
@Permission(alias = "camera", strings = { Manifest.permission.CAMERA })
|
|
45
47
|
})
|
|
46
48
|
public class DrawDownArCoreDepthPlugin extends Plugin {
|
|
47
|
-
private static final String PLUGIN_VERSION = "0.1.
|
|
49
|
+
private static final String PLUGIN_VERSION = "0.1.6";
|
|
50
|
+
private static final String METHOD_VERSION = "arcore-raw-depth-cylinder-v5-anchor-diagnostics";
|
|
48
51
|
private static final long WARMUP_MS = 1200L;
|
|
49
52
|
|
|
50
53
|
private final Handler mainHandler = new Handler(Looper.getMainLooper());
|
|
@@ -68,6 +71,16 @@ public class DrawDownArCoreDepthPlugin extends Plugin {
|
|
|
68
71
|
private int maxObservations = 10;
|
|
69
72
|
private int minObservations = 5;
|
|
70
73
|
private final List<DepthObservation> sweepObservations = new ArrayList<>();
|
|
74
|
+
private final Map<String, Integer> sweepRejectionCounts = new LinkedHashMap<>();
|
|
75
|
+
private final List<RawAnchorDiagnostics> sweepRawAnchorDiagnostics = new ArrayList<>();
|
|
76
|
+
private int totalFrameCount = 0;
|
|
77
|
+
private int trackingFrameCount = 0;
|
|
78
|
+
private int rawDepthFrameCount = 0;
|
|
79
|
+
private int mappedCenterCount = 0;
|
|
80
|
+
private int rawAnchorCount = 0;
|
|
81
|
+
private int rawPatchCount = 0;
|
|
82
|
+
private int smoothDepthMatchCount = 0;
|
|
83
|
+
private int boundaryConsensusCount = 0;
|
|
71
84
|
|
|
72
85
|
@PluginMethod
|
|
73
86
|
public void checkSupport(PluginCall call) {
|
|
@@ -251,6 +264,16 @@ public class DrawDownArCoreDepthPlugin extends Plugin {
|
|
|
251
264
|
|
|
252
265
|
pendingSweepCall = call;
|
|
253
266
|
sweepObservations.clear();
|
|
267
|
+
sweepRejectionCounts.clear();
|
|
268
|
+
sweepRawAnchorDiagnostics.clear();
|
|
269
|
+
totalFrameCount = 0;
|
|
270
|
+
trackingFrameCount = 0;
|
|
271
|
+
rawDepthFrameCount = 0;
|
|
272
|
+
mappedCenterCount = 0;
|
|
273
|
+
rawAnchorCount = 0;
|
|
274
|
+
rawPatchCount = 0;
|
|
275
|
+
smoothDepthMatchCount = 0;
|
|
276
|
+
boundaryConsensusCount = 0;
|
|
254
277
|
minObservations = requestedMin;
|
|
255
278
|
maxObservations = requestedMax;
|
|
256
279
|
sampleIntervalMs = Math.max(100L, durationMs / Math.max(1, requestedMax * 2));
|
|
@@ -372,32 +395,65 @@ public class DrawDownArCoreDepthPlugin extends Plugin {
|
|
|
372
395
|
|
|
373
396
|
try {
|
|
374
397
|
Camera camera = frame.getCamera();
|
|
375
|
-
|
|
376
|
-
if (observation == null) {
|
|
377
|
-
return;
|
|
378
|
-
}
|
|
398
|
+
DepthSampleResult sampleResult = sampler.sample(frame, camera, viewWidth, viewHeight);
|
|
379
399
|
|
|
380
400
|
boolean reachedMax = false;
|
|
381
401
|
synchronized (sweepLock) {
|
|
382
402
|
if (!sweepActive || pendingSweepCall == null) {
|
|
383
403
|
return;
|
|
384
404
|
}
|
|
385
|
-
|
|
386
|
-
|
|
405
|
+
totalFrameCount += 1;
|
|
406
|
+
if (sampleResult.trackingFrame) trackingFrameCount += 1;
|
|
407
|
+
if (sampleResult.rawDepthFrame) rawDepthFrameCount += 1;
|
|
408
|
+
if (sampleResult.mappedCenter) mappedCenterCount += 1;
|
|
409
|
+
if (sampleResult.rawAnchor) rawAnchorCount += 1;
|
|
410
|
+
if (sampleResult.rawPatch) rawPatchCount += 1;
|
|
411
|
+
if (sampleResult.smoothDepthMatch) smoothDepthMatchCount += 1;
|
|
412
|
+
if (sampleResult.boundaryConsensus) boundaryConsensusCount += 1;
|
|
413
|
+
if (sampleResult.rawAnchorDiagnostics != null) {
|
|
414
|
+
sweepRawAnchorDiagnostics.add(sampleResult.rawAnchorDiagnostics);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (sampleResult.rejectionReason != null) {
|
|
418
|
+
int current = sweepRejectionCounts.containsKey(sampleResult.rejectionReason)
|
|
419
|
+
? sweepRejectionCounts.get(sampleResult.rejectionReason)
|
|
420
|
+
: 0;
|
|
421
|
+
sweepRejectionCounts.put(sampleResult.rejectionReason, current + 1);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
if (sampleResult.observation != null) {
|
|
425
|
+
sweepObservations.add(sampleResult.observation);
|
|
426
|
+
reachedMax = sweepObservations.size() >= maxObservations;
|
|
427
|
+
}
|
|
387
428
|
}
|
|
388
429
|
|
|
389
430
|
if (reachedMax) {
|
|
390
431
|
mainHandler.post(() -> finishSweep(false));
|
|
391
432
|
}
|
|
392
433
|
} catch (Exception ignored) {
|
|
393
|
-
|
|
394
|
-
|
|
434
|
+
synchronized (sweepLock) {
|
|
435
|
+
totalFrameCount += 1;
|
|
436
|
+
int current = sweepRejectionCounts.containsKey("unexpected_exception")
|
|
437
|
+
? sweepRejectionCounts.get("unexpected_exception")
|
|
438
|
+
: 0;
|
|
439
|
+
sweepRejectionCounts.put("unexpected_exception", current + 1);
|
|
440
|
+
}
|
|
395
441
|
}
|
|
396
442
|
}
|
|
397
443
|
|
|
398
444
|
private void finishSweep(boolean stoppedByCaller) {
|
|
399
445
|
PluginCall sweepCall;
|
|
400
446
|
List<DepthObservation> observations;
|
|
447
|
+
Map<String, Integer> rejectionCounts;
|
|
448
|
+
List<RawAnchorDiagnostics> rawAnchorDiagnostics;
|
|
449
|
+
int framesTotal;
|
|
450
|
+
int framesTracking;
|
|
451
|
+
int framesRawDepth;
|
|
452
|
+
int centersMapped;
|
|
453
|
+
int anchorsRaw;
|
|
454
|
+
int patchesRaw;
|
|
455
|
+
int matchesSmooth;
|
|
456
|
+
int boundariesConsensus;
|
|
401
457
|
synchronized (sweepLock) {
|
|
402
458
|
sweepCall = pendingSweepCall;
|
|
403
459
|
if (sweepCall == null) {
|
|
@@ -407,7 +463,19 @@ public class DrawDownArCoreDepthPlugin extends Plugin {
|
|
|
407
463
|
pendingSweepCall = null;
|
|
408
464
|
sweepActive = false;
|
|
409
465
|
observations = new ArrayList<>(sweepObservations);
|
|
466
|
+
rejectionCounts = new LinkedHashMap<>(sweepRejectionCounts);
|
|
467
|
+
rawAnchorDiagnostics = new ArrayList<>(sweepRawAnchorDiagnostics);
|
|
468
|
+
framesTotal = totalFrameCount;
|
|
469
|
+
framesTracking = trackingFrameCount;
|
|
470
|
+
framesRawDepth = rawDepthFrameCount;
|
|
471
|
+
centersMapped = mappedCenterCount;
|
|
472
|
+
anchorsRaw = rawAnchorCount;
|
|
473
|
+
patchesRaw = rawPatchCount;
|
|
474
|
+
matchesSmooth = smoothDepthMatchCount;
|
|
475
|
+
boundariesConsensus = boundaryConsensusCount;
|
|
410
476
|
sweepObservations.clear();
|
|
477
|
+
sweepRejectionCounts.clear();
|
|
478
|
+
sweepRawAnchorDiagnostics.clear();
|
|
411
479
|
}
|
|
412
480
|
|
|
413
481
|
JSArray observationArray = new JSArray();
|
|
@@ -415,12 +483,37 @@ public class DrawDownArCoreDepthPlugin extends Plugin {
|
|
|
415
483
|
observationArray.put(observation.toJsObject());
|
|
416
484
|
}
|
|
417
485
|
|
|
486
|
+
JSObject rejectionObject = new JSObject();
|
|
487
|
+
for (Map.Entry<String, Integer> entry : rejectionCounts.entrySet()) {
|
|
488
|
+
rejectionObject.put(entry.getKey(), entry.getValue());
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
JSArray rawDiagnosticArray = new JSArray();
|
|
492
|
+
for (RawAnchorDiagnostics diagnostics : rawAnchorDiagnostics) {
|
|
493
|
+
rawDiagnosticArray.put(diagnostics.toJsObject());
|
|
494
|
+
}
|
|
495
|
+
JSObject rawDiagnosticSummary = buildRawAnchorDiagnosticSummary(rawAnchorDiagnostics);
|
|
496
|
+
|
|
418
497
|
JSObject result = new JSObject();
|
|
419
498
|
result.put("observations", observationArray);
|
|
420
499
|
result.put("deviceModel", Build.MANUFACTURER + " " + Build.MODEL);
|
|
421
500
|
result.put("arcoreVersion", installedArcoreVersion());
|
|
501
|
+
result.put("pluginVersion", PLUGIN_VERSION);
|
|
502
|
+
result.put("methodVersion", METHOD_VERSION);
|
|
422
503
|
result.put("minimumRequested", minObservations);
|
|
423
504
|
result.put("stoppedByCaller", stoppedByCaller);
|
|
505
|
+
result.put("totalFrameCount", framesTotal);
|
|
506
|
+
result.put("trackingFrameCount", framesTracking);
|
|
507
|
+
result.put("rawDepthFrameCount", framesRawDepth);
|
|
508
|
+
result.put("mappedCenterCount", centersMapped);
|
|
509
|
+
result.put("rawAnchorCount", anchorsRaw);
|
|
510
|
+
result.put("rawPatchCount", patchesRaw);
|
|
511
|
+
result.put("smoothDepthMatchCount", matchesSmooth);
|
|
512
|
+
result.put("boundaryConsensusCount", boundariesConsensus);
|
|
513
|
+
result.put("nativeValidObservationCount", observations.size());
|
|
514
|
+
result.put("nativeRejectionCounts", rejectionObject);
|
|
515
|
+
result.put("rawAnchorDiagnostics", rawDiagnosticArray);
|
|
516
|
+
result.put("rawAnchorDiagnosticSummary", rawDiagnosticSummary);
|
|
424
517
|
sweepCall.resolve(result);
|
|
425
518
|
|
|
426
519
|
if (!stoppedByCaller) {
|
|
@@ -430,6 +523,93 @@ public class DrawDownArCoreDepthPlugin extends Plugin {
|
|
|
430
523
|
}
|
|
431
524
|
}
|
|
432
525
|
|
|
526
|
+
private static JSObject buildRawAnchorDiagnosticSummary(List<RawAnchorDiagnostics> diagnostics) {
|
|
527
|
+
JSObject summary = new JSObject();
|
|
528
|
+
summary.put("frameCount", diagnostics.size());
|
|
529
|
+
if (diagnostics.isEmpty()) {
|
|
530
|
+
return summary;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
int minX = Integer.MAX_VALUE;
|
|
534
|
+
int maxX = Integer.MIN_VALUE;
|
|
535
|
+
int minY = Integer.MAX_VALUE;
|
|
536
|
+
int maxY = Integer.MIN_VALUE;
|
|
537
|
+
int rawWidth = 0;
|
|
538
|
+
int rawHeight = 0;
|
|
539
|
+
|
|
540
|
+
for (RawAnchorDiagnostics item : diagnostics) {
|
|
541
|
+
minX = Math.min(minX, item.centerX);
|
|
542
|
+
maxX = Math.max(maxX, item.centerX);
|
|
543
|
+
minY = Math.min(minY, item.centerY);
|
|
544
|
+
maxY = Math.max(maxY, item.centerY);
|
|
545
|
+
rawWidth = item.imageWidth;
|
|
546
|
+
rawHeight = item.imageHeight;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
summary.put("mappedCenterMinX", minX);
|
|
550
|
+
summary.put("mappedCenterMaxX", maxX);
|
|
551
|
+
summary.put("mappedCenterMinY", minY);
|
|
552
|
+
summary.put("mappedCenterMaxY", maxY);
|
|
553
|
+
summary.put("rawDepthWidth", rawWidth);
|
|
554
|
+
summary.put("rawDepthHeight", rawHeight);
|
|
555
|
+
summary.put("radius10", summarizeRawRadius(diagnostics, 10));
|
|
556
|
+
summary.put("radius20", summarizeRawRadius(diagnostics, 20));
|
|
557
|
+
summary.put("radius30", summarizeRawRadius(diagnostics, 30));
|
|
558
|
+
return summary;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
private static JSObject summarizeRawRadius(List<RawAnchorDiagnostics> diagnostics, int radius) {
|
|
562
|
+
long totalSampled = 0L;
|
|
563
|
+
long totalNonZero = 0L;
|
|
564
|
+
long totalInRange = 0L;
|
|
565
|
+
long total64 = 0L;
|
|
566
|
+
long total96 = 0L;
|
|
567
|
+
long total128 = 0L;
|
|
568
|
+
long total160 = 0L;
|
|
569
|
+
int frames64 = 0;
|
|
570
|
+
int frames96 = 0;
|
|
571
|
+
int frames128 = 0;
|
|
572
|
+
int frames160 = 0;
|
|
573
|
+
int maxConfidence = 0;
|
|
574
|
+
int depthAtMaxConfidence = 0;
|
|
575
|
+
|
|
576
|
+
for (RawAnchorDiagnostics item : diagnostics) {
|
|
577
|
+
RawAnchorDiagnostics.RadiusDiagnostics r = item.forRadius(radius);
|
|
578
|
+
totalSampled += r.sampledPixelCount;
|
|
579
|
+
totalNonZero += r.nonZeroDepthCount;
|
|
580
|
+
totalInRange += r.inRangeDepthCount;
|
|
581
|
+
total64 += r.confidenceAtLeast64Count;
|
|
582
|
+
total96 += r.confidenceAtLeast96Count;
|
|
583
|
+
total128 += r.confidenceAtLeast128Count;
|
|
584
|
+
total160 += r.confidenceAtLeast160Count;
|
|
585
|
+
if (r.confidenceAtLeast64Count > 0) frames64 += 1;
|
|
586
|
+
if (r.confidenceAtLeast96Count > 0) frames96 += 1;
|
|
587
|
+
if (r.confidenceAtLeast128Count > 0) frames128 += 1;
|
|
588
|
+
if (r.confidenceAtLeast160Count > 0) frames160 += 1;
|
|
589
|
+
if (r.maxConfidenceInRange > maxConfidence) {
|
|
590
|
+
maxConfidence = r.maxConfidenceInRange;
|
|
591
|
+
depthAtMaxConfidence = r.depthMmAtMaxConfidenceInRange;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
JSObject obj = new JSObject();
|
|
596
|
+
obj.put("radiusPx", radius);
|
|
597
|
+
obj.put("totalSampledPixelCount", totalSampled);
|
|
598
|
+
obj.put("totalNonZeroDepthCount", totalNonZero);
|
|
599
|
+
obj.put("totalInRangeDepthCount", totalInRange);
|
|
600
|
+
obj.put("totalConfidenceAtLeast64Count", total64);
|
|
601
|
+
obj.put("totalConfidenceAtLeast96Count", total96);
|
|
602
|
+
obj.put("totalConfidenceAtLeast128Count", total128);
|
|
603
|
+
obj.put("totalConfidenceAtLeast160Count", total160);
|
|
604
|
+
obj.put("framesWithConfidenceAtLeast64", frames64);
|
|
605
|
+
obj.put("framesWithConfidenceAtLeast96", frames96);
|
|
606
|
+
obj.put("framesWithConfidenceAtLeast128", frames128);
|
|
607
|
+
obj.put("framesWithConfidenceAtLeast160", frames160);
|
|
608
|
+
obj.put("maxConfidenceInRange", maxConfidence);
|
|
609
|
+
obj.put("depthMmAtMaxConfidenceInRange", depthAtMaxConfidence);
|
|
610
|
+
return obj;
|
|
611
|
+
}
|
|
612
|
+
|
|
433
613
|
private void cleanupSession() {
|
|
434
614
|
sweepActive = false;
|
|
435
615
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
package org.drawdown.arcoredepth;
|
|
2
|
+
|
|
3
|
+
import com.getcapacitor.JSObject;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Compact numeric diagnostics describing Raw Depth around the mapped crosshair.
|
|
7
|
+
* These values never influence the accepted DBH measurement; they exist only
|
|
8
|
+
* to distinguish confidence-threshold failures from coordinate/search failures.
|
|
9
|
+
*/
|
|
10
|
+
final class RawAnchorDiagnostics {
|
|
11
|
+
static final int[] RADII = new int[] { 10, 20, 30 };
|
|
12
|
+
|
|
13
|
+
final int centerX;
|
|
14
|
+
final int centerY;
|
|
15
|
+
final int imageWidth;
|
|
16
|
+
final int imageHeight;
|
|
17
|
+
final RadiusDiagnostics radius10;
|
|
18
|
+
final RadiusDiagnostics radius20;
|
|
19
|
+
final RadiusDiagnostics radius30;
|
|
20
|
+
|
|
21
|
+
RawAnchorDiagnostics(
|
|
22
|
+
int centerX,
|
|
23
|
+
int centerY,
|
|
24
|
+
int imageWidth,
|
|
25
|
+
int imageHeight,
|
|
26
|
+
RadiusDiagnostics radius10,
|
|
27
|
+
RadiusDiagnostics radius20,
|
|
28
|
+
RadiusDiagnostics radius30) {
|
|
29
|
+
this.centerX = centerX;
|
|
30
|
+
this.centerY = centerY;
|
|
31
|
+
this.imageWidth = imageWidth;
|
|
32
|
+
this.imageHeight = imageHeight;
|
|
33
|
+
this.radius10 = radius10;
|
|
34
|
+
this.radius20 = radius20;
|
|
35
|
+
this.radius30 = radius30;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
RadiusDiagnostics forRadius(int radius) {
|
|
39
|
+
if (radius == 10) return radius10;
|
|
40
|
+
if (radius == 20) return radius20;
|
|
41
|
+
return radius30;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
JSObject toJsObject() {
|
|
45
|
+
JSObject obj = new JSObject();
|
|
46
|
+
obj.put("mappedCenterX", centerX);
|
|
47
|
+
obj.put("mappedCenterY", centerY);
|
|
48
|
+
obj.put("rawDepthWidth", imageWidth);
|
|
49
|
+
obj.put("rawDepthHeight", imageHeight);
|
|
50
|
+
obj.put("radius10", radius10.toJsObject());
|
|
51
|
+
obj.put("radius20", radius20.toJsObject());
|
|
52
|
+
obj.put("radius30", radius30.toJsObject());
|
|
53
|
+
return obj;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
static final class RadiusDiagnostics {
|
|
57
|
+
final int radiusPx;
|
|
58
|
+
final int sampledPixelCount;
|
|
59
|
+
final int nonZeroDepthCount;
|
|
60
|
+
final int inRangeDepthCount;
|
|
61
|
+
final int confidenceAtLeast64Count;
|
|
62
|
+
final int confidenceAtLeast96Count;
|
|
63
|
+
final int confidenceAtLeast128Count;
|
|
64
|
+
final int confidenceAtLeast160Count;
|
|
65
|
+
final int maxConfidenceInRange;
|
|
66
|
+
final int depthMmAtMaxConfidenceInRange;
|
|
67
|
+
|
|
68
|
+
RadiusDiagnostics(
|
|
69
|
+
int radiusPx,
|
|
70
|
+
int sampledPixelCount,
|
|
71
|
+
int nonZeroDepthCount,
|
|
72
|
+
int inRangeDepthCount,
|
|
73
|
+
int confidenceAtLeast64Count,
|
|
74
|
+
int confidenceAtLeast96Count,
|
|
75
|
+
int confidenceAtLeast128Count,
|
|
76
|
+
int confidenceAtLeast160Count,
|
|
77
|
+
int maxConfidenceInRange,
|
|
78
|
+
int depthMmAtMaxConfidenceInRange) {
|
|
79
|
+
this.radiusPx = radiusPx;
|
|
80
|
+
this.sampledPixelCount = sampledPixelCount;
|
|
81
|
+
this.nonZeroDepthCount = nonZeroDepthCount;
|
|
82
|
+
this.inRangeDepthCount = inRangeDepthCount;
|
|
83
|
+
this.confidenceAtLeast64Count = confidenceAtLeast64Count;
|
|
84
|
+
this.confidenceAtLeast96Count = confidenceAtLeast96Count;
|
|
85
|
+
this.confidenceAtLeast128Count = confidenceAtLeast128Count;
|
|
86
|
+
this.confidenceAtLeast160Count = confidenceAtLeast160Count;
|
|
87
|
+
this.maxConfidenceInRange = maxConfidenceInRange;
|
|
88
|
+
this.depthMmAtMaxConfidenceInRange = depthMmAtMaxConfidenceInRange;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
JSObject toJsObject() {
|
|
92
|
+
JSObject obj = new JSObject();
|
|
93
|
+
obj.put("radiusPx", radiusPx);
|
|
94
|
+
obj.put("sampledPixelCount", sampledPixelCount);
|
|
95
|
+
obj.put("nonZeroDepthCount", nonZeroDepthCount);
|
|
96
|
+
obj.put("inRangeDepthCount", inRangeDepthCount);
|
|
97
|
+
obj.put("confidenceAtLeast64Count", confidenceAtLeast64Count);
|
|
98
|
+
obj.put("confidenceAtLeast96Count", confidenceAtLeast96Count);
|
|
99
|
+
obj.put("confidenceAtLeast128Count", confidenceAtLeast128Count);
|
|
100
|
+
obj.put("confidenceAtLeast160Count", confidenceAtLeast160Count);
|
|
101
|
+
obj.put("maxConfidenceInRange", maxConfidenceInRange);
|
|
102
|
+
obj.put("depthMmAtMaxConfidenceInRange", depthMmAtMaxConfidenceInRange);
|
|
103
|
+
return obj;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -1,54 +1,129 @@
|
|
|
1
1
|
export type ArcoreRangeSource = 'arcore_depth16' | 'arcore_raw_depth' | 'arcore_surface_hit';
|
|
2
2
|
export type BoundaryConfidence = 'high' | 'medium' | 'low';
|
|
3
|
+
|
|
3
4
|
export interface SupportResult {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
isSupported: boolean;
|
|
6
|
+
hasDepthApi: boolean;
|
|
7
|
+
pluginVersion?: string;
|
|
8
|
+
message?: string;
|
|
8
9
|
}
|
|
10
|
+
|
|
9
11
|
export interface StartDbhSessionOptions {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
/** RFID/NFC tree tag is the field anchor that identifies the 1.3 m DBH plane. */
|
|
13
|
+
anchorType?: 'rfid_tag';
|
|
14
|
+
targetHeightM: 1.3;
|
|
15
|
+
/** @deprecated Ignored from v0.1.2; retained only for backward compatibility. */
|
|
16
|
+
markerDictionary?: 'DICT_6X6_250';
|
|
17
|
+
/** @deprecated Ignored from v0.1.2; retained only for backward compatibility. */
|
|
18
|
+
markerId?: 0;
|
|
19
|
+
/** @deprecated Ignored from v0.1.2; retained only for backward compatibility. */
|
|
20
|
+
markerSizeCm?: 10;
|
|
19
21
|
}
|
|
22
|
+
|
|
20
23
|
export interface StartDbhSessionResult {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
started: boolean;
|
|
25
|
+
installationRequested?: boolean;
|
|
26
|
+
message?: string;
|
|
24
27
|
}
|
|
28
|
+
|
|
25
29
|
export interface CaptureDbhSweepOptions {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
+
durationMs: number;
|
|
31
|
+
minObservations: number;
|
|
32
|
+
maxObservations: number;
|
|
33
|
+
retainFrames: false;
|
|
30
34
|
}
|
|
35
|
+
|
|
31
36
|
export interface DbhObservation {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
frontSurfaceDepthM: number;
|
|
38
|
+
/** Effective focal length in pixels along the measured trunk-width axis. */
|
|
39
|
+
focalLengthXPx: number;
|
|
40
|
+
/** Trunk tangent-to-tangent apparent width in corresponding image pixels. */
|
|
41
|
+
trunkWidthPx: number;
|
|
42
|
+
rangeSource: ArcoreRangeSource;
|
|
43
|
+
trackingConfidence?: number;
|
|
44
|
+
boundaryConfidence?: BoundaryConfidence;
|
|
45
|
+
depthMadM?: number;
|
|
46
|
+
timestampMs?: number;
|
|
42
47
|
}
|
|
48
|
+
|
|
49
|
+
export interface RawAnchorRadiusDiagnostics {
|
|
50
|
+
radiusPx: number;
|
|
51
|
+
sampledPixelCount: number;
|
|
52
|
+
nonZeroDepthCount: number;
|
|
53
|
+
inRangeDepthCount: number;
|
|
54
|
+
confidenceAtLeast64Count: number;
|
|
55
|
+
confidenceAtLeast96Count: number;
|
|
56
|
+
confidenceAtLeast128Count: number;
|
|
57
|
+
confidenceAtLeast160Count: number;
|
|
58
|
+
maxConfidenceInRange: number;
|
|
59
|
+
depthMmAtMaxConfidenceInRange: number;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export interface RawAnchorFrameDiagnostics {
|
|
63
|
+
mappedCenterX: number;
|
|
64
|
+
mappedCenterY: number;
|
|
65
|
+
rawDepthWidth: number;
|
|
66
|
+
rawDepthHeight: number;
|
|
67
|
+
radius10: RawAnchorRadiusDiagnostics;
|
|
68
|
+
radius20: RawAnchorRadiusDiagnostics;
|
|
69
|
+
radius30: RawAnchorRadiusDiagnostics;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface RawAnchorRadiusSummary {
|
|
73
|
+
radiusPx: number;
|
|
74
|
+
totalSampledPixelCount: number;
|
|
75
|
+
totalNonZeroDepthCount: number;
|
|
76
|
+
totalInRangeDepthCount: number;
|
|
77
|
+
totalConfidenceAtLeast64Count: number;
|
|
78
|
+
totalConfidenceAtLeast96Count: number;
|
|
79
|
+
totalConfidenceAtLeast128Count: number;
|
|
80
|
+
totalConfidenceAtLeast160Count: number;
|
|
81
|
+
framesWithConfidenceAtLeast64: number;
|
|
82
|
+
framesWithConfidenceAtLeast96: number;
|
|
83
|
+
framesWithConfidenceAtLeast128: number;
|
|
84
|
+
framesWithConfidenceAtLeast160: number;
|
|
85
|
+
maxConfidenceInRange: number;
|
|
86
|
+
depthMmAtMaxConfidenceInRange: number;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface RawAnchorDiagnosticSummary {
|
|
90
|
+
frameCount: number;
|
|
91
|
+
mappedCenterMinX?: number;
|
|
92
|
+
mappedCenterMaxX?: number;
|
|
93
|
+
mappedCenterMinY?: number;
|
|
94
|
+
mappedCenterMaxY?: number;
|
|
95
|
+
rawDepthWidth?: number;
|
|
96
|
+
rawDepthHeight?: number;
|
|
97
|
+
radius10?: RawAnchorRadiusSummary;
|
|
98
|
+
radius20?: RawAnchorRadiusSummary;
|
|
99
|
+
radius30?: RawAnchorRadiusSummary;
|
|
100
|
+
}
|
|
101
|
+
|
|
43
102
|
export interface CaptureDbhSweepResult {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
103
|
+
observations: DbhObservation[];
|
|
104
|
+
deviceModel?: string;
|
|
105
|
+
arcoreVersion?: string;
|
|
106
|
+
pluginVersion?: string;
|
|
107
|
+
methodVersion?: string;
|
|
108
|
+
totalFrameCount?: number;
|
|
109
|
+
trackingFrameCount?: number;
|
|
110
|
+
rawDepthFrameCount?: number;
|
|
111
|
+
mappedCenterCount?: number;
|
|
112
|
+
rawAnchorCount?: number;
|
|
113
|
+
rawPatchCount?: number;
|
|
114
|
+
smoothDepthMatchCount?: number;
|
|
115
|
+
boundaryConsensusCount?: number;
|
|
116
|
+
nativeValidObservationCount?: number;
|
|
117
|
+
nativeRejectionCounts?: Record<string, number>;
|
|
118
|
+
rawAnchorDiagnostics?: RawAnchorFrameDiagnostics[];
|
|
119
|
+
rawAnchorDiagnosticSummary?: RawAnchorDiagnosticSummary;
|
|
47
120
|
}
|
|
121
|
+
|
|
48
122
|
export interface DrawDownArCoreDepthPlugin {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
123
|
+
checkSupport(): Promise<SupportResult>;
|
|
124
|
+
startDbhSession(options: StartDbhSessionOptions): Promise<StartDbhSessionResult>;
|
|
125
|
+
captureDbhSweep(options: CaptureDbhSweepOptions): Promise<CaptureDbhSweepResult>;
|
|
126
|
+
stopDbhSession(): Promise<void>;
|
|
53
127
|
}
|
|
54
|
-
|
|
128
|
+
|
|
129
|
+
//# sourceMappingURL=definitions.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drawdown-arcore-depth",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Small Android-only Capacitor 8 bridge for ARCore Depth DBH observations.",
|
|
3
|
+
"version": "0.1.6",
|
|
4
|
+
"description": "Small Android-only Capacitor 8 bridge for ARCore Raw Depth DBH observations with native Raw Depth anchor diagnostics.",
|
|
5
5
|
"main": "dist/plugin.cjs.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|
|
7
7
|
"types": "dist/esm/index.d.ts",
|