Skip to content

feat(#818 B2): category-aware mesh segmentation + #788 retrain verdict - #879

Merged
fernandotonon merged 6 commits into
masterfrom
feat/meshseg-categories-818
Jul 13, 2026
Merged

feat(#818 B2): category-aware mesh segmentation + #788 retrain verdict#879
fernandotonon merged 6 commits into
masterfrom
feat/meshseg-categories-818

Conversation

@fernandotonon

@fernandotonon fernandotonon commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

Implements #818 Track B2 (segmentation for any mesh category) and resolves #788 (meshseg retrain with the capsule-cap fix) with a data-backed verdict.

#818 B2 — one label vocabulary, per-category specialist models

  • MeshSegmenter::Part grows vegetation (trunk/branch/foliage/root/flower), vehicle (vehicle_body/wheel/window/wing/rotor) and building (wall/roof/window/door/chimney/foundation) labels; window is one global label shared by vehicle+building. The body model's 7-channel meshseg.onnx wire contract is untouched.
  • Options::category {Auto, Body, Vegetation, Vehicle, Building}; per-category ONNX files resolved/downloaded under the same QTMESH_SEGMENT_* overrides; local channels map to global parts via categoryChannelMap().
  • Auto dispatch via a tiny (~0.1 MB) point-cloud category classifier (meshseg_category.onnx, 99.1% 4-way val) through resolveCategoryBlocking(); classifier unavailable/offline → Body (exact pre-B2 behaviour). Decision record (why several specialists + classifier, not one big softmax; why SmolVLM was rejected as the dispatcher and kept as a future GUI identify/name assist): docs/MESH_SEGMENTATION_STRATEGY.md.
  • Geometric fallback is category-aware; rig bone-proximity hints apply to Body only.
  • Surfaces: CLI qtmesh segment --category …, MCP segment_mesh category arg, GUI Select-by-Part resolves Auto transparently in its worker.
  • Training: export-meshseg-onnx.py --category vegetation|vehicle|building|classifier with new procedural surface samplers (exact by-construction labels, 100% ours — no third-party data). Held-out synthetic val: vegetation 93.8%, vehicle 93.5%, building 86.9%, classifier 99.1%.
  • Models are live on HF: aggregate QtMeshEditor-models/segment/ + dedicated repos QtMeshEditor-mesh-segmentation-{vegetation,vehicle,building,category} with model cards; sync-hf-model-repos.sh gained the four mirrors. Verified end-to-end with real first-use downloads: bare qtmesh segment house.obj → classifier download → building-model download → correct wall/roof/chimney split; Auto classified tree/car/house/human test meshes correctly.

#788 — retrain verdict: published v2 weights stay

Six full-recipe retrains with the fixed script (3 seeds plain #787 fix, 3 with an additional junction-cap suppression fix) all missed the issue's ≥94.7% rig-truth bar (best 92.7%; distribution mean ≈89.5%), while ALL of them beat v2 on the 6-file held-out real set (97.2–97.9% vs 97.0%). Diagnosis + findings table in docs/MESH_SEGMENTATION_STRATEGY.md: the exterior-cap fix concentrates limb attachment-end cap points exactly on the limb–torso boundary; the new capsule_surf(cap0/cap1) suppression removes those junction caps entirely (a limb has no real surface there) and recovers torso recall, but v2's 94.7% on the tiny (effectively 2-character) OOD eval is partly a fortunate draw. Script↔weights correspondence is deliberately NOT restored; follow-up: widen the OOD eval set before any republish.

Testing

  • 26/26 MeshSegmenter* unit tests pass on an ONNX-enabled build (12 new category tests).
  • CLI end-to-end on procedural tree/car/house/human meshes: explicit categories, Auto dispatch, geometric fallbacks, and live HF first-use downloads all verified.
  • Remaining local full-suite failures are pre-existing macOS-only issues (Ogre singleton leaks between suites, an Assimp OBJ-export crash in an optimize coverage test) — untouched by this diff; Linux CI is authoritative.

Closes #788

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added category-aware AI mesh segmentation for bodies, vegetation, vehicles, and buildings.
    • Added automatic category detection, manual category selection, and category-specific part labels.
    • Added category controls to the Inspector, CLI, and mesh-segmentation tool.
    • Added JSON and human-readable output reporting the selected category.
    • Added deterministic geometric fallback segmentation when AI models are unavailable.
  • Documentation

    • Updated CLI, model distribution, architecture, and segmentation strategy documentation.
    • Documented category-specific models, classifier behavior, fallbacks, and implementation status.

fernandotonon and others added 5 commits July 11, 2026 18:19
…-category models

- MeshSegmenter::Part grows vegetation (trunk/branch/foliage/root/flower),
  vehicle (vehicle_body/wheel/window/wing/rotor) and building
  (wall/roof/window/door/chimney/foundation) labels; 'window' is one global
  label shared by vehicle + building. Body keeps the original 7-channel
  meshseg.onnx wire contract.
- Options::category {Auto, Body, Vegetation, Vehicle, Building}; per-category
  ONNX files (meshseg_<cat>.onnx) resolved/downloaded via the same
  QTMESH_SEGMENT_* overrides; local channel → global Part via
  categoryChannelMap().
- Auto dispatch: tiny point-cloud category classifier
  (meshseg_category.onnx) via resolveCategoryBlocking(); classifier
  unavailable/offline → Body (pre-B2 behaviour). SmolVLM rejected as the
  dispatcher (render + 500MB VLM vs ~0.1MB headless classifier); kept as a
  follow-up GUI identify/name assist — decision in
  docs/MESH_SEGMENTATION_STRATEGY.md.
- Geometric fallback is category-aware; bone-proximity hints apply to Body
  only. CLI --category, MCP segment_mesh category arg, GUI Select-by-Part
  resolves Auto transparently. 12 new unit tests.
- export-meshseg-onnx.py: --category selects body (unchanged recipe) /
  vegetation / vehicle / building generators or the classifier; procedural
  tree/vehicle/building surface samplers with exact by-construction labels.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…dels

sync-hf-model-repos.sh gains segveg/segveh/segbld/segcls →
QtMeshEditor-mesh-segmentation-{vegetation,vehicle,building,category};
hosting note updated in THIRD_PARTY_AI_MODELS.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The #787 exterior-cap fix concentrated every attachment-end cap point on
the junction-facing hemisphere — limb-labelled points exactly on the
torso boundary — teaching the model to over-claim shoulders/hips (three
retrain seeds scored 92.6/89.0/87.0 vs the 94.7 rig-truth bar, torso
recall 0.80 → 0.50-0.68). A real character has no surface at the limb-
torso junction, so capsule_surf gains cap0/cap1 toggles and every body
plan suppresses the attachment-end cap (arm shoulder end, leg hip end,
neck both ends). Same-label junctions (tail-torso, elbow, wrist) keep
caps — they cannot bias a boundary.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Mesh segmentation now supports Body, Vegetation, Vehicle, and Building categories through shared labels, specialized ONNX models, automatic classification, category-aware fallbacks, CLI/MCP/UI controls, updated training exports, and expanded tests and documentation.

Changes

Category-aware segmentation

Layer / File(s) Summary
Segmentation contract and runtime
src/MeshSegmenter.h, src/MeshSegmenter.cpp
Adds category-specific labels, models, classifier resolution, normalized inference, and geometric fallback behavior.
Category training and model distribution
scripts/export-meshseg-onnx.py, scripts/sync-hf-model-repos.sh, THIRD_PARTY_AI_MODELS.md
Adds synthetic category datasets, classifier training, dynamic model channels, and distribution mappings for the expanded ONNX model family.
CLI, MCP, controller, and UI wiring
src/CLIPipeline.cpp, src/MCPServer.cpp, src/EditModeController.*, qml/PropertiesPanel.qml
Propagates category selection through command-line, MCP, edit-mode, and inspector flows, including category reporting.
Behavior validation and published status
src/MeshSegmenter_test.cpp, docs/MESH_SEGMENTATION_STRATEGY.md, CLAUDE.md
Tests category mappings, fallback behavior, geometric labels, and classifier degradation while updating strategy and usage documentation.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the category-aware segmentation change and the #788 retrain verdict.
Description check ✅ Passed The description covers the main summary, technical changes, testing, and the #788 verdict, with only template headings partially mismatched.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/meshseg-categories-818

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ef8e92f6cf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/MeshSegmenter.cpp
Comment on lines +772 to 775
const std::vector<int> chanMap = categoryChannelMap(cat);
const int C = static_cast<int>(chanMap.size());
if (elems < static_cast<size_t>(N))
return fallback(QStringLiteral("Segmentation output too small — used the geometric fallback."));

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Validate ONNX channel count before indexing logits

When the cached/downloaded model does not match the selected category (for example a stale prototype or a QTMESH_SEGMENT_MODEL_BASE_URL that serves the classifier/body model for meshseg_vehicle.onnx), this now derives C from the category map but only checks elems >= N. If the tensor has fewer than C channels, channelsLast becomes false and the loop below reads d[c * N + i] past the output buffer instead of falling back, so an invalid or mismatched model can crash segmentation rather than reporting model failure.

Useful? React with 👍 / 👎.

…lect by Part

Real-world failure (user report): a car with DETACHED wheels (separate
nodes dropped below the hull — common in exports) classified as 'body';
the floating wheels stretch the normalised box so the hull sits in the
upper half like a torso. Verified + fixed on two fronts:

- Training: new detach_parts augmentation (minority-label clusters offset
  downward/outward) for the category segmenters and, stronger, for the
  classifier; classifier clouds are additionally spun by a FULL random yaw
  (category is a yaw-invariant question — it had inherited the segmenters'
  near-canonical facing augmentation). Retrained classifier probe: detached
  wheels 18→20/20, detached+45° 5→19/20, detached+90° 3→20/20; no
  category regression at random yaw. Published as meshseg_category v1.1.
- GUI: Category dropdown (Auto/Body/Vegetation/Vehicle/Building) above
  Edit Mode's 'Select by Part (AI)' — the manual escape hatch, matching
  CLI --category / MCP category. An explicit non-body category also skips
  the rig-prior fast path (rig labels are body parts).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@fernandotonon

Copy link
Copy Markdown
Owner Author

Follow-up (real-world bug report during testing): detached-wheel cars classified as body — fixed in 11e61d0.

A real car mesh with wheels exported as detached nodes below the hull auto-classified as body (everything selected as right_leg). Reproduced synthetically and root-caused: floating parts stretch the normalised unit box, so the hull lands in the upper half like a torso; additionally the classifier had inherited the segmenters' near-canonical yaw augmentation even though category is a yaw-invariant question.

Fixes:

  1. detach_parts training augmentation (minority-label clusters offset downward/outward — the dropped-wheels export pattern) for the category segmenters + classifier, and full-random yaw on every classifier cloud. Retrained classifier probe (20 trials/case): detached wheels 18→20/20, detached+45° 5→19/20, detached+90° 3→20/20, no category regression at random yaw. Published as meshseg_category v1.1 (aggregate + dedicated repo, card updated). Verified end-to-end: a detached+45°-rotated car OBJ through qtmesh segment now auto-classifies vehicle and segments body/wheels/windows correctly.
  2. GUI manual override: Category dropdown (Auto/Body/Vegetation/Vehicle/Building) above Edit Mode's "Select by Part (AI)", matching CLI --category / MCP category. An explicit non-body category also skips the rig-prior fast path.

The vehicle segmenter is also retraining with the same augmentation (so detached wheels still label as wheel); it will be swapped on the hub when it beats v1 on val.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/MESH_SEGMENTATION_STRATEGY.md`:
- Line 86: Update the paragraph beginning with “#788” so the issue reference is
escaped from Markdown heading parsing, preferably by wrapping “#788” in
backticks. Preserve the existing text and references to `#787`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: e327b68f-2169-46d4-80fa-238242fa1bb2

📥 Commits

Reviewing files that changed from the base of the PR and between ed2b35d and 11e61d0.

📒 Files selected for processing (13)
  • CLAUDE.md
  • THIRD_PARTY_AI_MODELS.md
  • docs/MESH_SEGMENTATION_STRATEGY.md
  • qml/PropertiesPanel.qml
  • scripts/export-meshseg-onnx.py
  • scripts/sync-hf-model-repos.sh
  • src/CLIPipeline.cpp
  • src/EditModeController.cpp
  • src/EditModeController.h
  • src/MCPServer.cpp
  • src/MeshSegmenter.cpp
  • src/MeshSegmenter.h
  • src/MeshSegmenter_test.cpp


The current C++ contract is one 7-class body-centric label set
(`MeshSegmenter::Part`). That already covers **humanoids** and stretches to
#788 asked for a retrain with the #787 exterior-capsule-cap data fix,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Escape the leading #788 to avoid the markdownlint MD018 warning.

The paragraph starts with #788, which markdownlint parses as a heading missing a space after #. Wrapping the reference in backticks (or rewording) clears the warning.

📝 Proposed fix
-#788 asked for a retrain with the `#787` exterior-capsule-cap data fix,
+Issue `#788` asked for a retrain with the `#787` exterior-capsule-cap data fix,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#788 asked for a retrain with the #787 exterior-capsule-cap data fix,
Issue `#788` asked for a retrain with the `#787` exterior-capsule-cap data fix,
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)

[warning] 86-86: No space after hash on atx style heading

(MD018, no-missing-space-atx)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/MESH_SEGMENTATION_STRATEGY.md` at line 86, Update the paragraph
beginning with “#788” so the issue reference is escaped from Markdown heading
parsing, preferably by wrapping “#788” in backticks. Preserve the existing text
and references to `#787`.

Source: Linters/SAST tools

@sonarqubecloud

Copy link
Copy Markdown

@fernandotonon
fernandotonon merged commit 5415810 into master Jul 13, 2026
21 checks passed
@fernandotonon
fernandotonon deleted the feat/meshseg-categories-818 branch July 13, 2026 02:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Retrain meshseg.onnx with the exterior-capsule-cap data fix (#787 follow-up)

1 participant