drawdown-arcore-depth 0.1.5 → 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 CHANGED
@@ -190,6 +190,11 @@ No ArUco marker is required by the native ARCore workflow. The existing RFID/NFC
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
191
 
192
192
 
193
- ## 0.1.5 diagnostic sweep
193
+ ## 0.1.6 diagnostic sweep
194
194
 
195
- Version 0.1.5 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.
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.
@@ -47,11 +47,12 @@ final class DepthObservationSampler {
47
47
  boolean rawPatchReached = false;
48
48
  boolean smoothDepthMatchReached = false;
49
49
  boolean boundaryConsensusReached = false;
50
+ RawAnchorDiagnostics rawAnchorDiagnostics = null;
50
51
 
51
52
  if (camera.getTrackingState() != TrackingState.TRACKING) {
52
53
  return DepthSampleResult.failure(
53
54
  "tracking_not_tracking",
54
- false, false, false, false, false, false, false);
55
+ false, false, false, false, false, false, false, rawAnchorDiagnostics);
55
56
  }
56
57
  trackingFrame = true;
57
58
 
@@ -65,7 +66,7 @@ final class DepthObservationSampler {
65
66
  } catch (NotYetAvailableException e) {
66
67
  return DepthSampleResult.failure(
67
68
  "raw_depth_not_yet_available",
68
- trackingFrame, false, false, false, false, false, false);
69
+ trackingFrame, false, false, false, false, false, false, rawAnchorDiagnostics);
69
70
  }
70
71
 
71
72
  try {
@@ -73,7 +74,7 @@ final class DepthObservationSampler {
73
74
  } catch (NotYetAvailableException e) {
74
75
  return DepthSampleResult.failure(
75
76
  "raw_confidence_not_available",
76
- trackingFrame, rawDepthFrame, false, false, false, false, false);
77
+ trackingFrame, rawDepthFrame, false, false, false, false, false, rawAnchorDiagnostics);
77
78
  }
78
79
 
79
80
  try {
@@ -81,7 +82,7 @@ final class DepthObservationSampler {
81
82
  } catch (NotYetAvailableException e) {
82
83
  return DepthSampleResult.failure(
83
84
  "smooth_depth_not_available",
84
- trackingFrame, rawDepthFrame, false, false, false, false, false);
85
+ trackingFrame, rawDepthFrame, false, false, false, false, false, rawAnchorDiagnostics);
85
86
  }
86
87
 
87
88
  CameraIntrinsics intrinsics = camera.getImageIntrinsics();
@@ -90,18 +91,18 @@ final class DepthObservationSampler {
90
91
  if (focal == null || focal.length < 2 || imageDimensions == null || imageDimensions.length < 2) {
91
92
  return DepthSampleResult.failure(
92
93
  "image_intrinsics_missing",
93
- trackingFrame, rawDepthFrame, false, false, false, false, false);
94
+ trackingFrame, rawDepthFrame, false, false, false, false, false, rawAnchorDiagnostics);
94
95
  }
95
96
 
96
- // Keep the 0.1.4 geometry assumptions unchanged; 0.1.5 only makes
97
- // failures observable instead of silently returning null.
97
+ // Keep the 0.1.5 measurement geometry/thresholds unchanged; 0.1.6 only adds
98
+ // Raw Depth anchor diagnostics around the mapped crosshair.
98
99
  if (rawDepth.getWidth() != rawConfidence.getWidth()
99
100
  || rawDepth.getHeight() != rawConfidence.getHeight()
100
101
  || rawDepth.getWidth() != smoothDepth.getWidth()
101
102
  || rawDepth.getHeight() != smoothDepth.getHeight()) {
102
103
  return DepthSampleResult.failure(
103
104
  "image_size_mismatch",
104
- trackingFrame, rawDepthFrame, false, false, false, false, false);
105
+ trackingFrame, rawDepthFrame, false, false, false, false, false, rawAnchorDiagnostics);
105
106
  }
106
107
 
107
108
  float[] viewCenter = new float[] { viewWidth / 2f, viewHeight / 2f };
@@ -116,15 +117,17 @@ final class DepthObservationSampler {
116
117
  if (mappedCenter == null) {
117
118
  return DepthSampleResult.failure(
118
119
  "image_to_depth_mapping_failed",
119
- trackingFrame, rawDepthFrame, false, false, false, false, false);
120
+ trackingFrame, rawDepthFrame, false, false, false, false, false, rawAnchorDiagnostics);
120
121
  }
121
122
  mappedCenterReached = true;
123
+ rawAnchorDiagnostics = collectRawAnchorDiagnostics(
124
+ rawDepth, rawConfidence, mappedCenter[0], mappedCenter[1]);
122
125
 
123
126
  RawAnchor anchor = findRawAnchor(rawDepth, rawConfidence, mappedCenter[0], mappedCenter[1]);
124
127
  if (anchor == null) {
125
128
  return DepthSampleResult.failure(
126
129
  "no_confident_raw_anchor",
127
- trackingFrame, rawDepthFrame, mappedCenterReached, false, false, false, false);
130
+ trackingFrame, rawDepthFrame, mappedCenterReached, false, false, false, false, rawAnchorDiagnostics);
128
131
  }
129
132
  rawAnchorReached = true;
130
133
 
@@ -140,7 +143,7 @@ final class DepthObservationSampler {
140
143
  if (rawPatch.size() < MIN_RAW_PATCH_SAMPLES) {
141
144
  return DepthSampleResult.failure(
142
145
  "insufficient_raw_patch_samples",
143
- trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, false, false, false);
146
+ trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, false, false, false, rawAnchorDiagnostics);
144
147
  }
145
148
  rawPatchReached = true;
146
149
 
@@ -149,7 +152,7 @@ final class DepthObservationSampler {
149
152
  if (centerDepthMm < MIN_TARGET_DEPTH_MM || centerDepthMm > MAX_TARGET_DEPTH_MM) {
150
153
  return DepthSampleResult.failure(
151
154
  "target_depth_out_of_range",
152
- trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, false, false);
155
+ trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, false, false, rawAnchorDiagnostics);
153
156
  }
154
157
 
155
158
  // Locate the foreground surface in the smoothed depth image, but only
@@ -164,7 +167,7 @@ final class DepthObservationSampler {
164
167
  if (centerX < 0) {
165
168
  return DepthSampleResult.failure(
166
169
  "no_smooth_depth_match",
167
- trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, false, false);
170
+ trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, false, false, rawAnchorDiagnostics);
168
171
  }
169
172
  smoothDepthMatchReached = true;
170
173
 
@@ -178,7 +181,7 @@ final class DepthObservationSampler {
178
181
  if (boundary == null || boundary.rightX - boundary.leftX < 3) {
179
182
  return DepthSampleResult.failure(
180
183
  "boundary_no_consensus",
181
- trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, smoothDepthMatchReached, false);
184
+ trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, smoothDepthMatchReached, false, rawAnchorDiagnostics);
182
185
  }
183
186
  boundaryConsensusReached = true;
184
187
 
@@ -188,12 +191,12 @@ final class DepthObservationSampler {
188
191
  || boundary.rightX >= depthWidth - 1 - EDGE_MARGIN_PX) {
189
192
  return DepthSampleResult.failure(
190
193
  "boundary_touches_edge",
191
- trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, smoothDepthMatchReached, boundaryConsensusReached);
194
+ trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, smoothDepthMatchReached, boundaryConsensusReached, rawAnchorDiagnostics);
192
195
  }
193
196
  if (boundaryWidth > depthWidth * MAX_TRUNK_WIDTH_FRACTION) {
194
197
  return DepthSampleResult.failure(
195
198
  "boundary_too_wide",
196
- trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, smoothDepthMatchReached, boundaryConsensusReached);
199
+ trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, smoothDepthMatchReached, boundaryConsensusReached, rawAnchorDiagnostics);
197
200
  }
198
201
 
199
202
  float[] leftImage = depthToImage(frame, smoothDepth, boundary.leftX, anchor.y);
@@ -201,7 +204,7 @@ final class DepthObservationSampler {
201
204
  if (leftImage == null || rightImage == null) {
202
205
  return DepthSampleResult.failure(
203
206
  "depth_to_image_mapping_failed",
204
- trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, smoothDepthMatchReached, boundaryConsensusReached);
207
+ trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, smoothDepthMatchReached, boundaryConsensusReached, rawAnchorDiagnostics);
205
208
  }
206
209
 
207
210
  double dx = rightImage[0] - leftImage[0];
@@ -210,7 +213,7 @@ final class DepthObservationSampler {
210
213
  if (!Double.isFinite(imageWidthPx) || imageWidthPx < 8.0) {
211
214
  return DepthSampleResult.failure(
212
215
  "image_width_too_small",
213
- trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, smoothDepthMatchReached, boundaryConsensusReached);
216
+ trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, smoothDepthMatchReached, boundaryConsensusReached, rawAnchorDiagnostics);
214
217
  }
215
218
 
216
219
  double normalizedAngularWidth = Math.sqrt(
@@ -221,7 +224,7 @@ final class DepthObservationSampler {
221
224
  || normalizedAngularWidth > 0.95) {
222
225
  return DepthSampleResult.failure(
223
226
  "angular_width_invalid",
224
- trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, smoothDepthMatchReached, boundaryConsensusReached);
227
+ trackingFrame, rawDepthFrame, mappedCenterReached, rawAnchorReached, rawPatchReached, smoothDepthMatchReached, boundaryConsensusReached, rawAnchorDiagnostics);
225
228
  }
226
229
 
227
230
  double effectiveFocalPx = imageWidthPx / normalizedAngularWidth;
@@ -256,7 +259,7 @@ final class DepthObservationSampler {
256
259
  boundaryConfidence,
257
260
  centerMadMm / 1000.0,
258
261
  System.currentTimeMillis());
259
- return DepthSampleResult.success(observation);
262
+ return DepthSampleResult.success(observation, rawAnchorDiagnostics);
260
263
  } catch (Exception e) {
261
264
  return DepthSampleResult.failure(
262
265
  "unexpected_exception",
@@ -266,7 +269,8 @@ final class DepthObservationSampler {
266
269
  rawAnchorReached,
267
270
  rawPatchReached,
268
271
  smoothDepthMatchReached,
269
- boundaryConsensusReached);
272
+ boundaryConsensusReached,
273
+ rawAnchorDiagnostics);
270
274
  } finally {
271
275
  if (smoothDepth != null) smoothDepth.close();
272
276
  if (rawConfidence != null) rawConfidence.close();
@@ -274,6 +278,77 @@ final class DepthObservationSampler {
274
278
  }
275
279
  }
276
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
+ }
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);
350
+ }
351
+
277
352
  private static RawAnchor findRawAnchor(Image rawDepth, Image confidence, int centerX, int centerY) {
278
353
  RawAnchor best = null;
279
354
  double bestScore = Double.POSITIVE_INFINITY;
@@ -15,6 +15,7 @@ final class DepthSampleResult {
15
15
  final boolean rawPatch;
16
16
  final boolean smoothDepthMatch;
17
17
  final boolean boundaryConsensus;
18
+ final RawAnchorDiagnostics rawAnchorDiagnostics;
18
19
 
19
20
  DepthSampleResult(
20
21
  DepthObservation observation,
@@ -25,7 +26,8 @@ final class DepthSampleResult {
25
26
  boolean rawAnchor,
26
27
  boolean rawPatch,
27
28
  boolean smoothDepthMatch,
28
- boolean boundaryConsensus) {
29
+ boolean boundaryConsensus,
30
+ RawAnchorDiagnostics rawAnchorDiagnostics) {
29
31
  this.observation = observation;
30
32
  this.rejectionReason = rejectionReason;
31
33
  this.trackingFrame = trackingFrame;
@@ -35,6 +37,7 @@ final class DepthSampleResult {
35
37
  this.rawPatch = rawPatch;
36
38
  this.smoothDepthMatch = smoothDepthMatch;
37
39
  this.boundaryConsensus = boundaryConsensus;
40
+ this.rawAnchorDiagnostics = rawAnchorDiagnostics;
38
41
  }
39
42
 
40
43
  static DepthSampleResult failure(
@@ -45,7 +48,8 @@ final class DepthSampleResult {
45
48
  boolean rawAnchor,
46
49
  boolean rawPatch,
47
50
  boolean smoothDepthMatch,
48
- boolean boundaryConsensus) {
51
+ boolean boundaryConsensus,
52
+ RawAnchorDiagnostics rawAnchorDiagnostics) {
49
53
  return new DepthSampleResult(
50
54
  null,
51
55
  reason,
@@ -55,10 +59,11 @@ final class DepthSampleResult {
55
59
  rawAnchor,
56
60
  rawPatch,
57
61
  smoothDepthMatch,
58
- boundaryConsensus);
62
+ boundaryConsensus,
63
+ rawAnchorDiagnostics);
59
64
  }
60
65
 
61
- static DepthSampleResult success(DepthObservation observation) {
62
- return new DepthSampleResult(observation, null, true, true, true, true, true, true, true);
66
+ static DepthSampleResult success(DepthObservation observation, RawAnchorDiagnostics rawAnchorDiagnostics) {
67
+ return new DepthSampleResult(observation, null, true, true, true, true, true, true, true, rawAnchorDiagnostics);
63
68
  }
64
69
  }
@@ -46,8 +46,8 @@ import java.util.Map;
46
46
  @Permission(alias = "camera", strings = { Manifest.permission.CAMERA })
47
47
  })
48
48
  public class DrawDownArCoreDepthPlugin extends Plugin {
49
- private static final String PLUGIN_VERSION = "0.1.5";
50
- private static final String METHOD_VERSION = "arcore-raw-depth-cylinder-v4-diagnostics";
49
+ private static final String PLUGIN_VERSION = "0.1.6";
50
+ private static final String METHOD_VERSION = "arcore-raw-depth-cylinder-v5-anchor-diagnostics";
51
51
  private static final long WARMUP_MS = 1200L;
52
52
 
53
53
  private final Handler mainHandler = new Handler(Looper.getMainLooper());
@@ -72,6 +72,7 @@ public class DrawDownArCoreDepthPlugin extends Plugin {
72
72
  private int minObservations = 5;
73
73
  private final List<DepthObservation> sweepObservations = new ArrayList<>();
74
74
  private final Map<String, Integer> sweepRejectionCounts = new LinkedHashMap<>();
75
+ private final List<RawAnchorDiagnostics> sweepRawAnchorDiagnostics = new ArrayList<>();
75
76
  private int totalFrameCount = 0;
76
77
  private int trackingFrameCount = 0;
77
78
  private int rawDepthFrameCount = 0;
@@ -264,6 +265,7 @@ public class DrawDownArCoreDepthPlugin extends Plugin {
264
265
  pendingSweepCall = call;
265
266
  sweepObservations.clear();
266
267
  sweepRejectionCounts.clear();
268
+ sweepRawAnchorDiagnostics.clear();
267
269
  totalFrameCount = 0;
268
270
  trackingFrameCount = 0;
269
271
  rawDepthFrameCount = 0;
@@ -408,6 +410,9 @@ public class DrawDownArCoreDepthPlugin extends Plugin {
408
410
  if (sampleResult.rawPatch) rawPatchCount += 1;
409
411
  if (sampleResult.smoothDepthMatch) smoothDepthMatchCount += 1;
410
412
  if (sampleResult.boundaryConsensus) boundaryConsensusCount += 1;
413
+ if (sampleResult.rawAnchorDiagnostics != null) {
414
+ sweepRawAnchorDiagnostics.add(sampleResult.rawAnchorDiagnostics);
415
+ }
411
416
 
412
417
  if (sampleResult.rejectionReason != null) {
413
418
  int current = sweepRejectionCounts.containsKey(sampleResult.rejectionReason)
@@ -440,6 +445,7 @@ public class DrawDownArCoreDepthPlugin extends Plugin {
440
445
  PluginCall sweepCall;
441
446
  List<DepthObservation> observations;
442
447
  Map<String, Integer> rejectionCounts;
448
+ List<RawAnchorDiagnostics> rawAnchorDiagnostics;
443
449
  int framesTotal;
444
450
  int framesTracking;
445
451
  int framesRawDepth;
@@ -458,6 +464,7 @@ public class DrawDownArCoreDepthPlugin extends Plugin {
458
464
  sweepActive = false;
459
465
  observations = new ArrayList<>(sweepObservations);
460
466
  rejectionCounts = new LinkedHashMap<>(sweepRejectionCounts);
467
+ rawAnchorDiagnostics = new ArrayList<>(sweepRawAnchorDiagnostics);
461
468
  framesTotal = totalFrameCount;
462
469
  framesTracking = trackingFrameCount;
463
470
  framesRawDepth = rawDepthFrameCount;
@@ -468,6 +475,7 @@ public class DrawDownArCoreDepthPlugin extends Plugin {
468
475
  boundariesConsensus = boundaryConsensusCount;
469
476
  sweepObservations.clear();
470
477
  sweepRejectionCounts.clear();
478
+ sweepRawAnchorDiagnostics.clear();
471
479
  }
472
480
 
473
481
  JSArray observationArray = new JSArray();
@@ -480,6 +488,12 @@ public class DrawDownArCoreDepthPlugin extends Plugin {
480
488
  rejectionObject.put(entry.getKey(), entry.getValue());
481
489
  }
482
490
 
491
+ JSArray rawDiagnosticArray = new JSArray();
492
+ for (RawAnchorDiagnostics diagnostics : rawAnchorDiagnostics) {
493
+ rawDiagnosticArray.put(diagnostics.toJsObject());
494
+ }
495
+ JSObject rawDiagnosticSummary = buildRawAnchorDiagnosticSummary(rawAnchorDiagnostics);
496
+
483
497
  JSObject result = new JSObject();
484
498
  result.put("observations", observationArray);
485
499
  result.put("deviceModel", Build.MANUFACTURER + " " + Build.MODEL);
@@ -498,6 +512,8 @@ public class DrawDownArCoreDepthPlugin extends Plugin {
498
512
  result.put("boundaryConsensusCount", boundariesConsensus);
499
513
  result.put("nativeValidObservationCount", observations.size());
500
514
  result.put("nativeRejectionCounts", rejectionObject);
515
+ result.put("rawAnchorDiagnostics", rawDiagnosticArray);
516
+ result.put("rawAnchorDiagnosticSummary", rawDiagnosticSummary);
501
517
  sweepCall.resolve(result);
502
518
 
503
519
  if (!stoppedByCaller) {
@@ -507,6 +523,93 @@ public class DrawDownArCoreDepthPlugin extends Plugin {
507
523
  }
508
524
  }
509
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
+
510
613
  private void cleanupSession() {
511
614
  sweepActive = false;
512
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,66 +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
- isSupported: boolean;
5
- hasDepthApi: boolean;
6
- pluginVersion?: string;
7
- message?: string;
5
+ isSupported: boolean;
6
+ hasDepthApi: boolean;
7
+ pluginVersion?: string;
8
+ message?: string;
8
9
  }
10
+
9
11
  export interface StartDbhSessionOptions {
10
- /** RFID/NFC tree tag is the field anchor that identifies the 1.3 m DBH plane. */
11
- anchorType?: 'rfid_tag';
12
- targetHeightM: 1.3;
13
- /** @deprecated Ignored from v0.1.2; retained only for backward compatibility. */
14
- markerDictionary?: 'DICT_6X6_250';
15
- /** @deprecated Ignored from v0.1.2; retained only for backward compatibility. */
16
- markerId?: 0;
17
- /** @deprecated Ignored from v0.1.2; retained only for backward compatibility. */
18
- markerSizeCm?: 10;
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
- started: boolean;
22
- installationRequested?: boolean;
23
- message?: string;
24
+ started: boolean;
25
+ installationRequested?: boolean;
26
+ message?: string;
24
27
  }
28
+
25
29
  export interface CaptureDbhSweepOptions {
26
- durationMs: number;
27
- minObservations: number;
28
- maxObservations: number;
29
- retainFrames: false;
30
+ durationMs: number;
31
+ minObservations: number;
32
+ maxObservations: number;
33
+ retainFrames: false;
30
34
  }
35
+
31
36
  export interface DbhObservation {
32
- frontSurfaceDepthM: number;
33
- /** Effective focal length in pixels along the measured trunk-width axis. */
34
- focalLengthXPx: number;
35
- /** Trunk tangent-to-tangent apparent width in corresponding image pixels. */
36
- trunkWidthPx: number;
37
- rangeSource: ArcoreRangeSource;
38
- trackingConfidence?: number;
39
- boundaryConfidence?: BoundaryConfidence;
40
- depthMadM?: number;
41
- timestampMs?: number;
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
- observations: DbhObservation[];
45
- deviceModel?: string;
46
- arcoreVersion?: string;
47
- pluginVersion?: string;
48
- methodVersion?: string;
49
- totalFrameCount?: number;
50
- trackingFrameCount?: number;
51
- rawDepthFrameCount?: number;
52
- mappedCenterCount?: number;
53
- rawAnchorCount?: number;
54
- rawPatchCount?: number;
55
- smoothDepthMatchCount?: number;
56
- boundaryConsensusCount?: number;
57
- nativeValidObservationCount?: number;
58
- nativeRejectionCounts?: Record<string, number>;
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;
59
120
  }
121
+
60
122
  export interface DrawDownArCoreDepthPlugin {
61
- checkSupport(): Promise<SupportResult>;
62
- startDbhSession(options: StartDbhSessionOptions): Promise<StartDbhSessionResult>;
63
- captureDbhSweep(options: CaptureDbhSweepOptions): Promise<CaptureDbhSweepResult>;
64
- stopDbhSession(): Promise<void>;
123
+ checkSupport(): Promise<SupportResult>;
124
+ startDbhSession(options: StartDbhSessionOptions): Promise<StartDbhSessionResult>;
125
+ captureDbhSweep(options: CaptureDbhSweepOptions): Promise<CaptureDbhSweepResult>;
126
+ stopDbhSession(): Promise<void>;
65
127
  }
66
- //# sourceMappingURL=definitions.d.ts.map
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.5",
4
- "description": "Small Android-only Capacitor 8 bridge for ARCore Raw Depth DBH observations with native sweep diagnostics.",
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",