Skip to content

feat(addons/objects3d): reusable 3D object detection (2D detect + depth into oriented boxes)#417

Open
salmanmkc wants to merge 30 commits into
google:mainfrom
salmanmkc:feat/objects3d
Open

feat(addons/objects3d): reusable 3D object detection (2D detect + depth into oriented boxes)#417
salmanmkc wants to merge 30 commits into
google:mainfrom
salmanmkc:feat/objects3d

Conversation

@salmanmkc

Copy link
Copy Markdown
Contributor

adds an objects3d addon that turns 2D detection plus the depth mesh into oriented 3D bounding boxes you can query in world space. it's the pipeline that lived inline in the objects_3d demo, pulled out into a reusable Script so apps and other addons can just ask for detected 3D objects.

Object3DDetector runs the whole thing: snap the camera and depth mesh, run 2D detection, get a per-object segmentation mask, raycast the masked depth samples into world space, fit a yaw-aligned oriented box, fuse across views, and optionally draw debug wireframe boxes. each result is a Detected3DObject with a nearest-surface query, so you can ask for the closest point on an object, which is handy for pointing at it or placing something against it.

the backends are pluggable:

  • 2D detection: gemini (open-vocabulary, needs a key) or mediapipe (on-device COCO, no key, fixed class set).
  • masks: SAM / slimsam (via @huggingface/transformers) or the mediapipe segmenter.

it also exports the pure helpers that are useful on their own: uvToNdc (the snapshot-vs-camera aspect correction), box2dIoU / unionDetections / snapBoxToFloor (2D fusion and floor snapping), and the label categorize helpers (flat / surface / tiny-flat / light buckets).

the heavy deps (@huggingface/transformers, mediapipe) stay external; the one rollup change just marks transformers external so it isn't bundled.

this is split out of the agenthands branch (#416), which originally grounded its pointing through this pipeline before I switched it to a lighter depth raycast. the addon ships standalone here with colocated vitest specs (label categorization, depth sampling, fusion, and the nearest-surface query); the objects_3d demo can move onto it as a follow-up. lint, tests and build are clean.

@ruofeidu
ruofeidu requested a review from nsalminen June 26, 2026 22:16
ruofeidu and others added 4 commits June 26, 2026 18:16
segmenterMaskFromSnapshot resolved with the raw MediaPipe categoryMask,
whose backing buffer is only valid for the duration of the segment()
callback. The consumer reads it (getAsUint8Array) in a later microtask,
after the buffer is freed, giving garbage masks or a crash with the
mediapipe backend. Copy the data out inside the callback and resolve a
plain snapshot, mirroring the SAM path.
_detectInFlight was set before an unguarded setup block (canvas 2d
context, toDataURL, camera clone, depth-mesh snapshot). A throw there
left the guard set and wedged the detector for the page lifetime. Wrap
the whole detect() body in try/finally so the guard is always cleared.
@ruofeidu ruofeidu added the algorithm spatial algorithm label Jun 29, 2026

@ruofeidu ruofeidu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments in line.

High-level: after tested on device --- Nels: can you give it a try?

consider runDetection({snapshot, backend}) override in ObjectDetector,
or before this is stable, it can live in addons and enable only when reqeusted.

Comment thread src/addons/objects3d/geometry/Fusion.ts Outdated
export function fuseIntoBoxes(
records: FusionRecord[],
newObb: InternalObb,
cat: string

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: category instead of cat

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

renamed, thanks!

Comment thread src/addons/objects3d/geometry/Fusion.ts Outdated
): FusionRecord | null {
const half = (s: THREE.Vector3) => (s.x + s.y + s.z) / 6;
for (const rec of records) {
if (rec.category !== cat) continue;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: will a chair and a sofa near each other (both furniture) fuse into one object?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, previously they could fuse because matching used only the broad category and center distance. I changed this so same-label observations retain the existing proximity matching, while differently labelled observations fuse only when both OBB centers lie inside each other's box. So tested it, it still handles one object changing from armchair to sofa across views, but keeps merely adjacent furniture separate. I added regression tests for both cases.


const c = obb.center;
const farFromRoom =
Math.abs(c.x) > 6 || Math.abs(c.z) > 6 || c.y < -1 || c.y > 5;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: declare magic numbers

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, pulled these into named constants, split by purpose: depth-sampling settings, room bounds, minimum samples per category, and fitted-box size and degeneracy limits.

@salmanmkc

salmanmkc commented Jul 14, 2026

Copy link
Copy Markdown
Contributor Author

Some comments in line.

High-level: after tested on device --- Nels: can you give it a try?

consider runDetection({snapshot, backend}) override in ObjectDetector, or before this is stable, it can live in addons and enable only when reqeusted.

runDetection({snapshot, backend}) override in ObjectDetector as a follow up?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

algorithm spatial algorithm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants